Last active
March 15, 2021 18:31
-
-
Save LLFourn/ee73d67b3f4645e4c19bd853e1b17062 to your computer and use it in GitHub Desktop.
rust code for anticipating signatures for Discreet Log Contract oracles with the standard rust-secp256k1 library
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//! PoC style code for anticipating BIP-340 style signatures for PoC DLC stuff. | |
//! | |
//! WARNING this doesn't check that: | |
//! 1. Public keys have an even Y coordinate | |
//! 2. Public nonces have a square Y coordinate | |
//! | |
//! If the public keys are in the right form, then this should produce valid | |
//! BIP-340 signatures (to be fully compatible then the tag needs to be set | |
//! correctly). | |
//! Add to Cargo.toml: | |
//! [dependencies] | |
//! sha2 = "0.8" | |
//! secp256k1 = "0.17" | |
use secp256k1::{PublicKey, SecretKey}; | |
use sha2::digest::Digest; | |
pub fn tagged_hash(tag: &[u8]) -> sha2::Sha256 { | |
let hashed_tag = { | |
let mut hash = sha2::Sha256::default(); | |
hash.input(tag); | |
hash.result() | |
}; | |
let mut tagged_hash = sha2::Sha256::default(); | |
tagged_hash.input(hashed_tag); | |
tagged_hash.input(hashed_tag); | |
tagged_hash | |
} | |
pub fn anticipate_signature<C: secp256k1::Verification>( | |
secp: &secp256k1::Secp256k1<C>, | |
signing_key: &PublicKey, | |
nonce: &PublicKey, | |
message: &[u8], | |
challenge_hash: sha2::Sha256, | |
) -> PublicKey { | |
let e = challenge_hash | |
// note we drop the first bit which is the y even/odd tiebreaker and | |
// only hash the x-coordinate | |
.chain(&nonce.serialize()[1..]) | |
.chain(&signing_key.serialize()[1..]) | |
.chain(message) | |
.result(); | |
let mut res = signing_key.clone(); | |
res.mul_assign(&secp, e.as_ref()).unwrap(); | |
PublicKey::combine(nonce, &res).unwrap() | |
} | |
pub fn reveal_signature( | |
signing_key: (&SecretKey, &PublicKey), | |
nonce: (&SecretKey, &PublicKey), | |
message: &[u8], | |
challenge_hash: sha2::Sha256, | |
) -> SecretKey { | |
let e = challenge_hash | |
.chain(&nonce.1.serialize()[1..]) | |
.chain(&signing_key.1.serialize()[1..]) | |
.chain(message) | |
.result(); | |
let mut res = signing_key.0.clone(); | |
res.mul_assign(e.as_ref()).unwrap(); | |
res.add_assign(&nonce.0[..]).unwrap(); | |
res | |
} | |
#[cfg(test)] | |
mod tests { | |
use super::*; | |
use std::str::FromStr; | |
// This test case is derived from my own library that use parity's secp256k1 | |
#[test] | |
fn anticpate_signature() { | |
let secp = secp256k1::Secp256k1::default(); | |
let challenge_hash = tagged_hash(b"oracle/challenge"); | |
let secret_key = SecretKey::from_slice(b"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx").unwrap(); | |
let public_key = PublicKey::from_secret_key(&secp, &secret_key); | |
let secret_nonce = | |
SecretKey::from_str("e6dbb82375570e799f7847adc7ea98dccbb915fd672795f84c0a5d367959c9b1") | |
.unwrap(); | |
let public_nonce = PublicKey::from_secret_key(&secp, &secret_nonce); | |
let anticipated_signature = anticipate_signature( | |
&secp, | |
&public_key, | |
&public_nonce, | |
b"message", | |
challenge_hash.clone(), | |
); | |
assert_eq!(&anticipated_signature, &PublicKey::from_str("04c6c663064de55c72b303c096ca87eab552ab47f4256d20e32a89818688559b4a9e34d7e026748fdcc79110f89bbb2533614dd0dc1682e258098e7ee898ae770a").unwrap()); | |
let revealed_signature = reveal_signature( | |
(&secret_key, &public_key), | |
(&secret_nonce, &public_nonce), | |
b"message", | |
challenge_hash.clone(), | |
); | |
assert_eq!( | |
revealed_signature, | |
SecretKey::from_str("5e754d3776cc0960d83486d76a6949d2fd8fe620a8a1ff03501b8748585d4190") | |
.unwrap() | |
); | |
assert_eq!( | |
PublicKey::from_secret_key(&secp, &revealed_signature), | |
anticipated_signature, | |
); | |
// [schnorr_fun/src/lib.rs:74] &r = Scalar<Secret,NonZero>(e6dbb82375570e799f7847adc7ea98dccbb915fd672795f84c0a5d367959c9b1) | |
// [schnorr_fun/src/lib.rs:171] &e = Scalar<Public,Zero>(26459148a91654096fb7e8a8c86d1974697df1770fe67747bc97133a483c1728) | |
// [schnorr_fun/src/lib.rs:172] e * X = Point<Jacobian,Public,Zero>(b32d259094009a8a55f58bce2f6a7a0e49fd9388a971990a4500a48c32ce5a45b4ca84a1a53dc8414288c2accd0f1e2c16d73c3a4bea045973095cf7839ac0f2) | |
// [schnorr_fun/src/lib.rs:247] &keypair = KeyPair { | |
// sk: Scalar<Secret,NonZero>(7878787878787878787878787878787878787878787878787878787878787878), | |
// pk: XOnly<EvenY>(17142f69535e4dad0dc7060df645c55a174cc1bfa5b9eb2e59aad2ae96072dfc), | |
// } | |
// [schnorr_fun/src/lib.rs:247] &anticipated_signature = Point<Jacobian,Public,Zero>(c6c663064de55c72b303c096ca87eab552ab47f4256d20e32a89818688559b4a9e34d7e026748fdcc79110f89bbb2533614dd0dc1682e258098e7ee898ae770a) | |
// [schnorr_fun/src/lib.rs:247] &signature.s = Scalar<Public,Zero>(5e754d3776cc0960d83486d76a6949d2fd8fe620a8a1ff03501b8748585d4190) | |
// [schnorr_fun/src/lib.rs:247] &signature.R.to_point() = Point<SquareY,Public,NonZero>(925af36f0e5a662afb98deefaac177d52695bfba4e33428fcf6ccf73d6606a27c172a32219f42f003fdb29eb9ec47748c0477661414a8dd7067157024b7b4716) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment