#![forbid(unsafe_code)]
use curve25519_dalek::{EdwardsPoint, Scalar};
use oxicrypto_core::CryptoError;
use super::keygen::KeyPackage;
use super::round1::{SigningCommitments, SigningNonces};
use super::{
binding_factor_for_participant, compute_binding_factors, compute_challenge,
compute_group_commitment, derive_interpolating_value, deserialize_scalar, scalar_base_mult,
serialize_scalar, Identifier, SCALAR_LEN,
};
use oxicrypto_core::Vec;
#[derive(Clone, Copy, Debug)]
pub struct SignatureShare {
identifier: Identifier,
value: Scalar,
}
impl SignatureShare {
#[must_use]
pub fn new(identifier: Identifier, value: Scalar) -> Self {
Self { identifier, value }
}
#[must_use = "result must be checked"]
pub fn from_bytes(identifier: Identifier, value_bytes: &[u8]) -> Result<Self, CryptoError> {
let value = deserialize_scalar(value_bytes).map_err(|_| CryptoError::InvalidTag)?;
Ok(Self { identifier, value })
}
#[must_use]
pub fn identifier(&self) -> Identifier {
self.identifier
}
#[must_use]
pub fn value(&self) -> Scalar {
self.value
}
#[must_use]
pub fn to_bytes(&self) -> [u8; SCALAR_LEN] {
serialize_scalar(&self.value)
}
}
#[must_use]
fn participants_from_commitment_list(commitment_list: &[SigningCommitments]) -> Vec<Identifier> {
commitment_list.iter().map(|c| c.identifier()).collect()
}
#[must_use = "the signature share must be sent to the coordinator"]
pub fn sign(
key_package: &KeyPackage,
nonces: &SigningNonces,
msg: &[u8],
commitment_list: &[SigningCommitments],
) -> Result<SignatureShare, CryptoError> {
let identifier = key_package.identifier();
if nonces.identifier() != identifier {
return Err(CryptoError::BadInput);
}
if !commitment_list.iter().any(|c| c.identifier() == identifier) {
return Err(CryptoError::BadInput);
}
let group_public_key = key_package.group_public_key();
let binding_factor_list = compute_binding_factors(&group_public_key, commitment_list, msg)?;
let binding_factor = binding_factor_for_participant(&binding_factor_list, identifier)?;
let group_commitment = compute_group_commitment(commitment_list, &binding_factor_list)?;
let participant_list = participants_from_commitment_list(commitment_list);
let lambda_i = derive_interpolating_value(&participant_list, identifier)?;
let challenge = compute_challenge(&group_commitment, &group_public_key, msg)?;
let s_i = key_package.secret_share().value();
let sig_share = nonces.hiding_nonce()
+ (nonces.binding_nonce() * binding_factor)
+ (lambda_i * s_i * challenge);
Ok(SignatureShare::new(identifier, sig_share))
}
#[must_use = "verification result must be checked"]
pub fn verify_signature_share(
public_share: &EdwardsPoint,
comm_i: &SigningCommitments,
sig_share_i: &SignatureShare,
commitment_list: &[SigningCommitments],
group_public_key: &EdwardsPoint,
msg: &[u8],
) -> Result<(), CryptoError> {
let identifier = sig_share_i.identifier();
let binding_factor_list = compute_binding_factors(group_public_key, commitment_list, msg)?;
let binding_factor = binding_factor_for_participant(&binding_factor_list, identifier)?;
let group_commitment = compute_group_commitment(commitment_list, &binding_factor_list)?;
let comm_share = comm_i.hiding() + (comm_i.binding() * binding_factor);
let challenge = compute_challenge(&group_commitment, group_public_key, msg)?;
let participant_list = participants_from_commitment_list(commitment_list);
let lambda_i = derive_interpolating_value(&participant_list, identifier)?;
let l = scalar_base_mult(&sig_share_i.value());
let r = comm_share + (*public_share * (challenge * lambda_i));
if l == r {
Ok(())
} else {
Err(CryptoError::Sign)
}
}