use hmac::{Hmac, KeyInit, Mac};
use p256::elliptic_curve::PrimeField;
use p256::{FieldBytes, Scalar};
use sha2::Sha256;
type HmacSha256 = Hmac<Sha256>;
pub fn derive_nonce_share(
secret_share: &Scalar,
message_hash: &[u8; 32],
party_idx: u32,
) -> Scalar {
let mut mac =
HmacSha256::new_from_slice(&secret_share.to_repr()).expect("HMAC accepts any key length");
mac.update(message_hash);
mac.update(&party_idx.to_be_bytes());
let result = mac.finalize().into_bytes();
let mut nonce_bytes = [0u8; 32];
nonce_bytes.copy_from_slice(&result);
reduce_mod_order(&nonce_bytes)
}
pub fn sum_nonce_shares(shares: &[Scalar]) -> Scalar {
shares.iter().fold(Scalar::ZERO, |acc, s| acc + s)
}
pub fn derive_full_nonce(
secret_shares: &[Scalar],
message_hash: &[u8; 32],
party_indices: &[u32],
) -> Scalar {
assert_eq!(secret_shares.len(), party_indices.len());
let shares: Vec<Scalar> = secret_shares
.iter()
.zip(party_indices.iter())
.map(|(s, &idx)| derive_nonce_share(s, message_hash, idx))
.collect();
sum_nonce_shares(&shares)
}
fn reduce_mod_order(bytes: &[u8; 32]) -> Scalar {
let fb = FieldBytes::from(*bytes);
let ct = Scalar::from_repr(fb);
Option::<Scalar>::from(ct).unwrap_or_else(|| {
let mut adjusted = *bytes;
adjusted[0] = adjusted[0].wrapping_sub(1);
let fb2 = FieldBytes::from(adjusted);
let ct2 = Scalar::from_repr(fb2);
Option::<Scalar>::from(ct2).unwrap_or(Scalar::ONE)
})
}
#[cfg(test)]
mod tests {
use super::*;
use p256::elliptic_curve::Field;
use p256::elliptic_curve::rand_core::UnwrapErr;
fn random_scalar() -> Scalar {
Scalar::random(&mut UnwrapErr(getrandom::SysRng))
}
#[test]
fn derivation_is_deterministic() {
let secret = random_scalar();
let msg = [0x42u8; 32];
let n1 = derive_nonce_share(&secret, &msg, 1);
let n2 = derive_nonce_share(&secret, &msg, 1);
assert_eq!(n1, n2);
}
#[test]
fn different_party_indices_differ() {
let secret = random_scalar();
let msg = [0x42u8; 32];
let n1 = derive_nonce_share(&secret, &msg, 1);
let n2 = derive_nonce_share(&secret, &msg, 2);
assert_ne!(n1, n2);
}
#[test]
fn different_messages_differ() {
let secret = random_scalar();
let m1 = [0x01u8; 32];
let m2 = [0x02u8; 32];
let n1 = derive_nonce_share(&secret, &m1, 1);
let n2 = derive_nonce_share(&secret, &m2, 1);
assert_ne!(n1, n2);
}
#[test]
fn different_secrets_differ() {
let s1 = random_scalar();
let s2 = random_scalar();
let msg = [0x42u8; 32];
let n1 = derive_nonce_share(&s1, &msg, 1);
let n2 = derive_nonce_share(&s2, &msg, 1);
assert_ne!(n1, n2);
}
#[test]
fn nonce_share_is_nonzero() {
let secret = random_scalar();
let msg = [0x42u8; 32];
let nonce = derive_nonce_share(&secret, &msg, 1);
assert_ne!(nonce, Scalar::ZERO);
}
#[test]
fn sum_nonce_shares_works() {
let s1 = random_scalar();
let s2 = random_scalar();
let expected = s1 + s2;
assert_eq!(sum_nonce_shares(&[s1, s2]), expected);
}
#[test]
fn sum_empty_is_zero() {
assert_eq!(sum_nonce_shares(&[]), Scalar::ZERO);
}
#[test]
fn full_nonce_is_sum_of_shares() {
let secrets = vec![random_scalar(), random_scalar(), random_scalar()];
let indices = vec![1u32, 2, 3];
let msg = [0xAAu8; 32];
let shares: Vec<Scalar> = secrets
.iter()
.zip(indices.iter())
.map(|(s, &i)| derive_nonce_share(s, &msg, i))
.collect();
let expected = sum_nonce_shares(&shares);
let actual = derive_full_nonce(&secrets, &msg, &indices);
assert_eq!(actual, expected);
}
#[test]
fn nonce_is_valid_scalar() {
let secret = random_scalar();
let msg = [0xFFu8; 32];
let nonce = derive_nonce_share(&secret, &msg, 1);
let _bytes = nonce.to_repr();
}
#[test]
fn reduce_mod_order_handles_large_input() {
let max_bytes = [0xFFu8; 32];
let result = reduce_mod_order(&max_bytes);
assert_ne!(result, Scalar::ZERO);
}
}