use crate::{ECDSA, Signature};
use secp256kfun::{
G, Point, Scalar, Tag, derive_nonce_rng,
digest::generic_array::typenum::U32,
g,
marker::*,
nonce::{NoNonces, NonceGen},
s,
};
pub use sigma_fun::HashTranscript;
use sigma_fun::{Eq, FiatShamir, ProverTranscript, Transcript, secp256k1};
mod encrypted_signature;
pub use encrypted_signature::*;
pub type DLEQ = Eq<secp256k1::DLG<U32>, secp256k1::DL<U32>>;
#[derive(Clone, Debug)]
pub struct Adaptor<T, NonceGen> {
pub ecdsa: ECDSA<NonceGen>,
pub dleq_proof_system: FiatShamir<DLEQ, T>,
}
impl<T, NG> Default for Adaptor<T, NG>
where
NG: Default + Tag,
T: Transcript<DLEQ> + Default,
{
fn default() -> Self {
Self::new(NG::default())
}
}
impl<T: Transcript<DLEQ> + Default, NG: Tag> Adaptor<T, NG> {
pub fn new(nonce_gen: NG) -> Self {
let sigma = DLEQ::default();
Self {
ecdsa: ECDSA::new(nonce_gen),
dleq_proof_system: FiatShamir::new(sigma, T::default(), Some("DLEQ")),
}
}
}
impl<T: Transcript<DLEQ> + Default> Adaptor<T, NoNonces> {
pub fn verify_only() -> Self {
Self::new(NoNonces)
}
}
impl<T, NG> Adaptor<T, NG>
where
T: Transcript<DLEQ>,
NG: NonceGen,
{
}
impl<T: Transcript<DLEQ>, NG> Adaptor<T, NG> {
pub fn encrypted_sign(
&self,
signing_key: &Scalar,
encryption_key: &Point,
message: &[u8; 32],
) -> EncryptedSignature
where
T: ProverTranscript<DLEQ>,
NG: NonceGen,
{
let x = signing_key;
let Y = encryption_key;
let m = Scalar::<Public, _>::from_bytes_mod_order(*message);
let mut rng = derive_nonce_rng!(
nonce_gen => self.ecdsa.nonce_gen,
secret => x,
public => [Y, &message[..]],
seedable_rng => rand_chacha::ChaCha20Rng
);
let r = Scalar::random(&mut rng);
let R_hat = g!(r * G).normalize();
let R = g!(r * Y).normalize();
let proof = self
.dleq_proof_system
.prove(&r, &(R_hat, (*Y, R)), Some(&mut rng));
let R_x = Scalar::<Public, _>::from_bytes_mod_order(R.to_xonly_bytes())
.non_zero()
.expect("computationally unreachable");
let s_hat = s!(r.invert() * (m + R_x * x))
.public()
.non_zero()
.expect("computationally unreachable");
EncryptedSignatureInternal {
R: PointNonce {
point: R,
x_scalar: R_x,
},
R_hat,
s_hat,
proof,
}
.into()
}
pub fn encryption_key_for(&self, decryption_key: &Scalar) -> Point {
g!(decryption_key * G).normalize()
}
#[must_use]
pub fn verify_encrypted_signature(
&self,
verification_key: &Point<impl PointType, impl Secrecy>,
encryption_key: &Point,
message_hash: &[u8; 32],
ciphertext: &EncryptedSignature,
) -> bool {
let X = verification_key;
let Y = encryption_key;
let m = Scalar::<Public, _>::from_bytes_mod_order(*message_hash);
let EncryptedSignature(EncryptedSignatureInternal {
R,
R_hat,
proof,
s_hat,
}) = ciphertext;
if !self
.dleq_proof_system
.verify(&(*R_hat, (*Y, R.point)), proof)
{
return false;
}
let s_hat_inv = s_hat.invert();
g!((s_hat_inv * m) * G + (s_hat_inv * R.x_scalar) * X) == *R_hat
}
pub fn decrypt_signature(
&self,
decryption_key: &Scalar<impl Secrecy, NonZero>,
ciphertext: EncryptedSignature,
) -> Signature {
let EncryptedSignature(EncryptedSignatureInternal { R, s_hat, .. }) = ciphertext;
let y = decryption_key;
let mut s = s!(s_hat * { y.invert() });
s.conditional_negate(s.is_high());
Signature {
R_x: R.x_scalar.public(),
s: s.public(),
}
}
pub fn recover_decryption_key(
&self,
encryption_key: &Point<impl Normalized, impl Secrecy>,
signature: &Signature,
ciphertext: &EncryptedSignature,
) -> Option<Scalar> {
let EncryptedSignature(EncryptedSignatureInternal { s_hat, R, .. }) = ciphertext;
if R.x_scalar != signature.R_x
|| (signature.s.is_high() && self.ecdsa.enforce_low_s)
{
return None;
}
let s = &signature.s;
let y = s!(s.invert() * s_hat);
let Y = g!(y * G);
if Y == *encryption_key {
Some(y)
} else if -Y == *encryption_key {
Some(-y)
} else {
None
}
}
}
#[cfg(test)]
mod test {
use super::*;
use crate::fun::nonce;
use rand::rngs::ThreadRng;
use rand_chacha::ChaCha20Rng;
use sha2::Sha256;
#[test]
fn end_to_end() {
let nonce_gen = nonce::Synthetic::<Sha256, nonce::GlobalRng<ThreadRng>>::default();
let ecdsa_adaptor = Adaptor::<HashTranscript<Sha256, ChaCha20Rng>, _>::new(nonce_gen);
for _ in 0..20 {
let msg = b"hello world you are beautiful!!!";
let signing_key = Scalar::random(&mut rand::thread_rng());
let verification_key = ecdsa_adaptor.ecdsa.verification_key_for(&signing_key);
let decryption_key = Scalar::random(&mut rand::thread_rng());
let encryption_key = ecdsa_adaptor.encryption_key_for(&decryption_key);
let ciphertext = ecdsa_adaptor.encrypted_sign(&signing_key, &encryption_key, msg);
assert!(ecdsa_adaptor.verify_encrypted_signature(
&verification_key,
&encryption_key,
msg,
&ciphertext,
));
let signature = ecdsa_adaptor.decrypt_signature(&decryption_key, ciphertext.clone());
assert!(
ecdsa_adaptor
.ecdsa
.verify(&verification_key, msg, &signature)
);
let recoverd_decryption_sk = ecdsa_adaptor
.recover_decryption_key(&encryption_key, &signature, &ciphertext)
.unwrap();
assert_eq!(recoverd_decryption_sk, decryption_key);
}
}
}