1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327
//! Algorithms for Schnorr "adaptor signature" signature encryption.
//!
//! Adaptor signatures are a kind of signature encryption. Just as you would expect this means you
//! can't get the signature from the encrypted signature unless you know the decryption key. As you
//! might not necessarily expect, this encryption is _one-time_ in that anyone who knows the
//! encrypted signature can recover the decryption key from the decrypted signature.
//!
//! This weird leaking of the decryption key is incredibly useful has numerous
//! applications in Bitcoin and cryptography more generally.
//!
//! # Synopsis
//! ```
//! use rand::rngs::ThreadRng;
//! use schnorr_fun::{
//! adaptor::{Adaptor, EncryptedSign},
//! fun::{marker::*, nonce, Scalar},
//! Message, Schnorr,
//! };
//! use sha2::Sha256;
//! let nonce_gen = nonce::Synthetic::<Sha256, nonce::GlobalRng<ThreadRng>>::default();
//! let schnorr = Schnorr::<Sha256, _>::new(nonce_gen);
//! let signing_keypair = schnorr.new_keypair(Scalar::random(&mut rand::thread_rng()));
//! let verification_key = signing_keypair.public_key();
//! let decryption_key = Scalar::random(&mut rand::thread_rng());
//! let encryption_key = schnorr.encryption_key_for(&decryption_key);
//! let message = Message::<Public>::plain("text-bitcoin", b"send 1 BTC to Bob");
//!
//! // Alice knows: signing_keypair, encryption_key
//! // Bob knows: decryption_key, verification_key
//!
//! // Alice creates an encrypted signature and sends it to Bob
//! let encrypted_signature = schnorr.encrypted_sign(&signing_keypair, &encryption_key, message);
//!
//! // Bob verifies it and decrypts it
//! assert!(schnorr.verify_encrypted_signature(
//! &verification_key,
//! &encryption_key,
//! message,
//! &encrypted_signature
//! ));
//! let signature = schnorr.decrypt_signature(decryption_key, encrypted_signature.clone());
//!
//! // Bob then broadcasts the signature to the public.
//! // Once Alice sees it she can recover Bob's secret decryption key
//! match schnorr.recover_decryption_key(&encryption_key, &encrypted_signature, &signature) {
//! Some(decryption_key) => println!("Alice got the decryption key {}", decryption_key),
//! None => eprintln!("signature is not the decryption of our original encrypted signature"),
//! }
//! ```
use crate::{
fun::{
derive_nonce,
digest::{generic_array::typenum::U32, Digest},
g,
marker::*,
nonce, s, KeyPair, Point, Scalar, G,
},
Message, Schnorr, Signature,
};
mod encrypted_signature;
pub use encrypted_signature::EncryptedSignature;
/// Extension trait for [`Schnorr`] to add the encrypted signing algorithm.
///
/// [`Schnorr`]: crate::Schnorr
pub trait EncryptedSign {
/// Create a signature on a message encrypted under `encryption_key`.
///
/// See the [synopsis] for usage.
///
/// [synopsis]: crate::adaptor#synopsis
fn encrypted_sign(
&self,
signing_keypair: &KeyPair<EvenY>,
encryption_key: &Point<Normal, impl Secrecy>,
message: Message<'_, impl Secrecy>,
) -> EncryptedSignature;
}
impl<NG, CH> EncryptedSign for Schnorr<CH, NG>
where
CH: Digest<OutputSize = U32> + Clone,
NG: nonce::NonceGen,
{
fn encrypted_sign(
&self,
signing_key: &KeyPair<EvenY>,
encryption_key: &Point<Normal, impl Secrecy>,
message: Message<'_, impl Secrecy>,
) -> 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)
// R_hat = r * G is sampled pseudorandomly for every Y which means R_hat + Y is also
// be pseudoranodm and therefore will not be zero.
// NOTE: Crucially we add Y to the nonce derivation to ensure this is true.
.non_zero()
.expect("computationally unreachable");
let (R, needs_negation) = R.into_point_with_even_y();
// We correct r here but we can't correct the decryption key (y) so we
// store in "needs_negation" whether the decryptor needs to negate their
// key before decrypting it
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,
}
}
}
/// Extension trait adding the algorithms for the adaptor signature scheme to instances of [`Schnorr`].
pub trait Adaptor {
/// Derives the public encryption key corresponding to a secret decryption key.
///
/// # Example
/// ```
/// # use schnorr_fun::{adaptor::Adaptor, fun::Scalar, Schnorr};
/// # let schnorr = schnorr_fun::test_instance!();
/// let decryption_key = Scalar::random(&mut rand::thread_rng());
/// let encryption_key = schnorr.encryption_key_for(&decryption_key);
fn encryption_key_for(&self, decryption_key: &Scalar) -> Point;
/// Verifies an encrypted signature is valid i.e. if it is decrypted it will yield a signature
/// on `message` under `verification_key`.
///
/// See [synopsis] for usage.
///
/// [synopsis]: crate::adaptor#synopsis
#[must_use]
fn verify_encrypted_signature(
&self,
verification_key: &Point<EvenY, impl Secrecy>,
encryption_key: &Point<impl PointType, impl Secrecy>,
message: Message<'_, impl Secrecy>,
encrypted_signature: &EncryptedSignature<impl Secrecy>,
) -> bool;
/// Decrypts an encrypted signature yielding the signature.
///
/// There are two crucial things to understand when calling this:
///
/// 1. You should be certain that the encrypted signature is what you think it is by calling
/// [`verify_encrypted_signature`] on it first.
/// 2. Once you give the decrypted signature to anyone who has seen `encrypted_signature` they will be
/// able to learn `decryption_key` by calling [`recover_decryption_key`].
///
/// See [synopsis] for an example
///
/// [`verify_encrypted_signature`]: Adaptor::verify_encrypted_signature
/// [`recover_decryption_key`]: Adaptor::recover_decryption_key
/// [synopsis]: crate::adaptor#synopsis
fn decrypt_signature(
&self,
decryption_key: Scalar<impl Secrecy>,
encrypted_signature: EncryptedSignature<impl Secrecy>,
) -> Signature;
/// Recovers the decryption key given an encrypted signature and the signature that was decrypted from it.
///
/// If the `signature` was **not** the one decrypted from the `encrypted_signature` then this function
/// returns `None`. If it returns `Some(decryption_key)`, then `signature` is the unique
/// signature obtained by decrypting `encrypted_signature` with the `decryption_key` corresponding to
/// `encryption_key`. In other words, if you already know that `encrypted_signature` is valid you do not
/// have to call [`Schnorr::verify`] on `signature` before calling this function because this function returning
/// `Some` implies it.
///
/// See [synopsis] for an example
///
/// [synopsis]: crate::adaptor#synopsis
fn recover_decryption_key(
&self,
encryption_key: &Point<impl Normalized, impl Secrecy>,
encrypted_signature: &EncryptedSignature<impl Secrecy>,
signature: &Signature<impl Secrecy>,
) -> Option<Scalar>;
}
impl<CH, NG> Adaptor for Schnorr<CH, NG>
where
CH: Digest<OutputSize = U32> + Clone,
{
fn encryption_key_for(&self, decryption_key: &Scalar) -> Point {
g!(decryption_key * G).normalize()
}
#[must_use]
fn verify_encrypted_signature(
&self,
verification_key: &Point<EvenY, impl Secrecy>,
encryption_key: &Point<impl PointType, impl Secrecy>,
message: Message<'_, impl Secrecy>,
encrypted_signature: &EncryptedSignature<impl Secrecy>,
) -> bool {
let EncryptedSignature {
R,
s_hat,
needs_negation,
} = encrypted_signature;
let X = verification_key;
let Y = encryption_key;
// needs_negation => R_hat = R + Y
// !needs_negation => R_hat = R - Y
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<impl Secrecy>,
) -> 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::<Public>::plain("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);
}
}