Skip to content

Instantly share code, notes, and snippets.

@LLFourn
Last active March 15, 2021 18:31
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save LLFourn/ee73d67b3f4645e4c19bd853e1b17062 to your computer and use it in GitHub Desktop.
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
//! 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