use crate::{
Message, Schnorr, Signature,
fun::{KeyPair, derive_nonce, nonce, prelude::*},
};
mod encrypted_signature;
pub use encrypted_signature::EncryptedSignature;
use secp256kfun::hash::Hash32;
pub trait EncryptedSign {
fn encrypted_sign(
&self,
signing_keypair: &KeyPair<EvenY>,
encryption_key: &Point<Normal, impl Secrecy>,
message: Message<'_>,
) -> EncryptedSignature;
}
impl<NG, CH> EncryptedSign for Schnorr<CH, NG>
where
CH: Hash32,
NG: nonce::NonceGen,
{
fn encrypted_sign(
&self,
signing_key: &KeyPair<EvenY>,
encryption_key: &Point<Normal, impl Secrecy>,
message: Message<'_>,
) -> EncryptedSignature {
let (x, X) = signing_key.as_tuple();
let Y = encryption_key;
let mut r = derive_nonce!(
nonce_gen => self.nonce_gen(),
secret => x,
public => [X, Y, message]
);
let R = g!(r * G + Y)
.non_zero()
.expect("computationally unreachable");
let (R, needs_negation) = R.into_point_with_even_y();
r.conditional_negate(needs_negation);
let c = self.challenge(&R, &X, message);
let s_hat = s!(r + c * x).public();
EncryptedSignature {
R,
s_hat,
needs_negation,
}
}
}
pub trait Adaptor {
fn encryption_key_for(&self, decryption_key: &Scalar) -> Point;
#[must_use]
fn verify_encrypted_signature(
&self,
verification_key: &Point<EvenY, impl Secrecy>,
encryption_key: &Point<impl PointType, impl Secrecy>,
message: Message<'_>,
encrypted_signature: &EncryptedSignature<impl Secrecy>,
) -> bool;
fn decrypt_signature(
&self,
decryption_key: Scalar<impl Secrecy>,
encrypted_signature: EncryptedSignature<impl Secrecy>,
) -> Signature;
fn recover_decryption_key(
&self,
encryption_key: &Point<impl Normalized, impl Secrecy>,
encrypted_signature: &EncryptedSignature<impl Secrecy>,
signature: &Signature,
) -> Option<Scalar>;
}
impl<CH, NG> Adaptor for Schnorr<CH, NG>
where
CH: Hash32,
{
fn encryption_key_for(&self, decryption_key: &Scalar) -> Point {
g!(decryption_key * G).normalize()
}
fn verify_encrypted_signature(
&self,
verification_key: &Point<EvenY, impl Secrecy>,
encryption_key: &Point<impl PointType, impl Secrecy>,
message: Message<'_>,
encrypted_signature: &EncryptedSignature<impl Secrecy>,
) -> bool {
let EncryptedSignature {
R,
s_hat,
needs_negation,
} = encrypted_signature;
let X = verification_key;
let Y = encryption_key;
let R_hat = g!(R + { Y.conditional_negate(!needs_negation) });
let c = self.challenge(R, X, message);
R_hat == g!(s_hat * G - c * X)
}
fn decrypt_signature(
&self,
decryption_key: Scalar<impl Secrecy>,
encrypted_signature: EncryptedSignature<impl Secrecy>,
) -> Signature {
let EncryptedSignature {
R,
s_hat,
needs_negation,
} = encrypted_signature;
let mut y = decryption_key;
y.conditional_negate(needs_negation);
let s = s!(s_hat + y).public();
Signature { s, R }
}
fn recover_decryption_key(
&self,
encryption_key: &Point<impl PointType, impl Secrecy>,
encrypted_signature: &EncryptedSignature<impl Secrecy>,
signature: &Signature,
) -> Option<Scalar> {
if signature.R != encrypted_signature.R {
return None;
}
let EncryptedSignature {
s_hat,
needs_negation,
..
} = encrypted_signature;
let s = &signature.s;
let mut y = s!(s - s_hat);
y.conditional_negate(*needs_negation);
let implied_encryption_key = g!(y * G);
if implied_encryption_key == *encryption_key {
Some(
y.non_zero()
.expect("unreachable - encryption_key is NonZero and y*G equals it"),
)
} else {
None
}
}
}
#[cfg(test)]
mod test {
use super::*;
use crate::nonce::{GlobalRng, Synthetic};
use rand::rngs::ThreadRng;
use secp256kfun::proptest::prelude::*;
use sha2::Sha256;
#[cfg(target_arch = "wasm32")]
use wasm_bindgen_test::wasm_bindgen_test as test;
proptest! {
#[test]
fn signing_tests_deterministic(secret_key in any::<Scalar>(), decryption_key in any::<Scalar>()) {
let schnorr = Schnorr::<Sha256, nonce::Deterministic<Sha256>>::default();
test_it(schnorr, secret_key, decryption_key);
}
#[test]
fn signing_tests_synthetic(secret_key in any::<Scalar>(), decryption_key in any::<Scalar>()) {
let schnorr = Schnorr::<Sha256, Synthetic<Sha256, GlobalRng<ThreadRng>>>::default();
test_it(schnorr, secret_key, decryption_key);
}
}
fn test_it<NG: nonce::NonceGen>(
schnorr: Schnorr<Sha256, NG>,
secret_key: Scalar,
decryption_key: Scalar,
) {
let signing_keypair = schnorr.new_keypair(secret_key);
let verification_key = signing_keypair.public_key();
let encryption_key = schnorr.encryption_key_for(&decryption_key);
let message = Message::new("test", b"give 100 coins to Bob".as_ref());
let encrypted_signature =
schnorr.encrypted_sign(&signing_keypair, &encryption_key, message);
assert!(schnorr.verify_encrypted_signature(
&verification_key,
&encryption_key,
message,
&encrypted_signature,
));
let decryption_key = decryption_key.public();
let signature = schnorr.decrypt_signature(decryption_key, encrypted_signature.clone());
assert!(schnorr.verify(&verification_key, message, &signature));
let rec_decryption_key = schnorr
.recover_decryption_key(&encryption_key, &encrypted_signature, &signature)
.expect("recovery works");
assert_eq!(rec_decryption_key, decryption_key);
}
}