use super::{
CryptoRngCore, NonZeroScalar, PlumeSignature, PlumeSignatureV1Fields, ProjectivePoint,
SecretKey, DST,
};
use k256::{
elliptic_curve::{
hash2curve::{ExpandMsgXmd, GroupDigest},
point::NonIdentity,
sec1::ToEncodedPoint,
},
sha2::{Digest, Sha256},
Secp256k1,
};
use signature::{Error, RandomizedSigner};
pub struct PlumeSigner<'signing> {
secret_key: &'signing SecretKey,
pub v1: bool,
}
impl<'signing> PlumeSigner<'signing> {
pub fn new(secret_key: &SecretKey, v1: bool) -> PlumeSigner {
PlumeSigner { secret_key, v1 }
}
}
impl<'signing> RandomizedSigner<PlumeSignature> for PlumeSigner<'signing> {
fn try_sign_with_rng(
&self,
rng: &mut impl CryptoRngCore,
msg: &[u8],
) -> Result<PlumeSignature, Error> {
let r_scalar = SecretKey::random(rng);
let r_point = r_scalar.public_key();
let pk = self.secret_key.public_key();
let pk_bytes = pk.to_encoded_point(true).to_bytes();
let hashed_to_curve = NonIdentity::new(
Secp256k1::hash_from_bytes::<ExpandMsgXmd<Sha256>>(&[msg, &pk_bytes], &[DST])
.map_err(|_| Error::new())?,
)
.expect("something is drammatically wrong if the input hashed to the identity");
let r_scalar = r_scalar.to_nonzero_scalar();
let hashed_to_curve_r = hashed_to_curve * r_scalar;
let nullifier = hashed_to_curve * self.secret_key.to_nonzero_scalar();
let mut hasher = Sha256::new();
macro_rules! updhash {
($p:ident) => {
hasher.update($p.to_encoded_point(true).as_bytes())
};
}
if self.v1 {
hasher.update(ProjectivePoint::GENERATOR.to_encoded_point(true).as_bytes());
hasher.update(pk_bytes);
updhash!(hashed_to_curve);
}
updhash!(nullifier);
updhash!(r_point);
updhash!(hashed_to_curve_r);
let c = hasher.finalize();
let c_scalar = NonZeroScalar::from_repr(c)
.expect("it should be impossible to get the hash equal to zero");
let s_scalar = NonZeroScalar::new(*r_scalar + *(c_scalar * self.secret_key.to_nonzero_scalar()))
.expect("something is terribly wrong if the nonce is equal to negated product of the secret and the hash");
Ok(PlumeSignature {
message: msg.to_owned(),
pk: pk.into(),
nullifier: nullifier.to_point().to_affine(),
c: c_scalar,
s: s_scalar,
v1specific: if self.v1 {
Some(PlumeSignatureV1Fields {
r_point: r_point.into(),
hashed_to_curve_r: hashed_to_curve_r.to_point().to_affine(),
})
} else {
None
},
})
}
}