use k256::elliptic_curve::group::Group;
use k256::elliptic_curve::ops::Reduce;
use k256::elliptic_curve::sec1::ToEncodedPoint;
use k256::{NonZeroScalar, ProjectivePoint, Scalar, U256};
use rand::rngs::OsRng;
use rand::CryptoRng;
use rand::RngCore;
use sha2::{Digest, Sha256};
use subtle::{ConditionallySelectable, ConstantTimeEq};
use crate::types::{hex_to_scalar, Error, RingSignature, RingSignatureBinary};
use crate::utils::{hex_to_point, random_non_zero_scalar};
const GENERATOR: ProjectivePoint = ProjectivePoint::GENERATOR;
pub fn sign(
message: &[u8],
private_key_hex: &str,
ring_pubkeys_hex: &[String],
) -> Result<RingSignature, Error> {
let private_key = hex_to_scalar(private_key_hex)?;
let ring_pubkeys: Vec<ProjectivePoint> = ring_pubkeys_hex
.iter()
.map(|pubkey_str| hex_to_point(pubkey_str))
.collect::<Result<_, _>>()?;
let binary_signature = sign_binary(message, &private_key, &ring_pubkeys, OsRng)?;
Ok(RingSignature::from(&binary_signature))
}
pub fn verify(
signature: &RingSignature,
message: &[u8],
ring_pubkeys_hex: &[String],
) -> Result<bool, Error> {
let binary_signature = RingSignatureBinary::try_from(signature)?;
let ring_pubkeys: Vec<ProjectivePoint> = ring_pubkeys_hex
.iter()
.map(|pubkey_str| hex_to_point(pubkey_str))
.collect::<Result<_, _>>()?;
verify_binary(&binary_signature, message, &ring_pubkeys)
}
pub fn sign_binary(
message: &[u8],
private_key: &Scalar,
ring_pubkeys: &[ProjectivePoint],
mut rng: impl RngCore + CryptoRng,
) -> Result<RingSignatureBinary, Error> {
let ring_size = ring_pubkeys.len();
if ring_size < 2 {
return Err(Error::RingTooSmall(ring_size));
}
if *private_key == Scalar::ZERO {
return Err(Error::PrivateKeyFormat(
"Private key scalar cannot be zero".into(),
));
}
let d = *private_key;
let _d_nonzero =
NonZeroScalar::new(d).expect("d was checked non-zero, NonZeroScalar::new must succeed");
let my_point = GENERATOR * d;
let flipped_d = d.negate();
let flipped_point = GENERATOR * flipped_d;
let mut signer_index: Option<usize> = None;
let mut used_d = d;
for (i, p) in ring_pubkeys.iter().enumerate() {
if p == &my_point {
signer_index = Some(i);
used_d = d;
break;
}
if p == &flipped_point {
signer_index = Some(i);
used_d = flipped_d;
break;
}
}
let signer_index = signer_index.ok_or(Error::SignerNotInRing)?;
let mut r_scalars = vec![Scalar::ZERO; ring_size];
let mut c_scalars = vec![Scalar::ZERO; ring_size];
let alpha_nonzero = random_non_zero_scalar(&mut rng);
let alpha = *alpha_nonzero.as_ref();
let alpha_g = GENERATOR * alpha;
let start_index = (signer_index + 1) % ring_size;
c_scalars[start_index] = hash_to_scalar(message, ring_pubkeys, &alpha_g)?;
let mut current_index = start_index;
while current_index != signer_index {
let r_nonzero = random_non_zero_scalar(&mut rng);
r_scalars[current_index] = *r_nonzero.as_ref();
let xi = (GENERATOR * r_scalars[current_index])
+ (ring_pubkeys[current_index] * c_scalars[current_index]);
let next_index = (current_index + 1) % ring_size;
c_scalars[next_index] = hash_to_scalar(message, ring_pubkeys, &xi)?;
current_index = next_index;
}
r_scalars[signer_index] = alpha - (c_scalars[signer_index] * used_d);
Ok(RingSignatureBinary {
c0: c_scalars[0],
s: r_scalars,
})
}
pub fn verify_binary(
signature: &RingSignatureBinary,
message: &[u8],
ring_pubkeys: &[ProjectivePoint],
) -> Result<bool, Error> {
let ring_size = ring_pubkeys.len();
if ring_size == 0 {
return Ok(false);
}
if signature.s.len() != ring_size {
return Err(Error::InvalidSignatureFormat);
}
let c0_scalar = signature.c0;
let r_scalars = &signature.s;
let mut current_c = c0_scalar;
for i in 0..ring_size {
let xi = (GENERATOR * r_scalars[i]) + (ring_pubkeys[i] * current_c);
current_c = hash_to_scalar(message, ring_pubkeys, &xi)?;
}
let is_valid = current_c.ct_eq(&c0_scalar);
Ok(is_valid.into())
}
fn hash_to_scalar(
message: &[u8],
ring_pubkeys: &[ProjectivePoint], ephemeral_point: &ProjectivePoint,
) -> Result<Scalar, Error> {
let mut hasher = Sha256::new();
hasher.update(message);
for pk_point in ring_pubkeys {
if pk_point.is_identity().into() {
return Err(Error::PublicKeyFormat(
"Cannot hash identity point in ring".into(),
));
}
let pk_bytes = pk_point.to_encoded_point(true);
hasher.update(pk_bytes.as_bytes());
}
if ephemeral_point.is_identity().into() {
return Err(Error::PublicKeyFormat(
"Cannot hash identity ephemeral point".into(),
));
}
let ephemeral_compressed = ephemeral_point.to_encoded_point(true);
hasher.update(ephemeral_compressed.as_bytes());
let hash_result = hasher.finalize();
let hash_uint = U256::from_be_slice(&hash_result);
let scalar = Scalar::reduce(hash_uint);
let is_zero = scalar.ct_eq(&Scalar::ZERO);
Ok(Scalar::conditional_select(&scalar, &Scalar::ONE, is_zero))
}