Skip to main content

ddk_dlc/
secp_utils.rs

1//! Crypto utilities providing necessary DLC specific functions not available in
2//! rust-secp256k1 or rust-secp256k1-zkp.
3
4use crate::Error;
5use bitcoin::hashes::{sha256t_hash_newtype, Hash};
6use core::ptr;
7use secp256k1_sys::{
8    types::{c_int, c_uchar, c_void, size_t},
9    CPtr, SchnorrSigExtraParams,
10};
11use secp256k1_zkp::{
12    schnorr::Signature as SchnorrSignature, Keypair, Message, PublicKey, Scalar, Secp256k1,
13    Signing, Verification, XOnlyPublicKey,
14};
15
16const BIP340_MIDSTATE: [u8; 32] = [
17    0x9c, 0xec, 0xba, 0x11, 0x23, 0x92, 0x53, 0x81, 0x11, 0x67, 0x91, 0x12, 0xd1, 0x62, 0x7e, 0x0f,
18    0x97, 0xc8, 0x75, 0x50, 0x00, 0x3c, 0xc7, 0x65, 0x90, 0xf6, 0x11, 0x64, 0x33, 0xe9, 0xb6, 0x6a,
19];
20
21sha256t_hash_newtype! {
22    /// BIP340 Hash Tag
23    pub struct BIP340HashTag = raw(BIP340_MIDSTATE, 64);
24
25    /// BIP340 Hash
26    #[hash_newtype(backward)]
27    pub struct BIP340Hash(_);
28}
29
30/// Create a Schnorr signature using the provided nonce instead of generating one.
31pub fn schnorrsig_sign_with_nonce<S: Signing>(
32    secp: &Secp256k1<S>,
33    msg: &Message,
34    keypair: &Keypair,
35    nonce: &[u8; 32],
36) -> SchnorrSignature {
37    unsafe {
38        let mut sig = [0u8; secp256k1_zkp::constants::SCHNORR_SIGNATURE_SIZE];
39        let extra_params =
40            SchnorrSigExtraParams::new(Some(constant_nonce_fn), nonce.as_c_ptr() as *const c_void);
41        assert_eq!(
42            1,
43            secp256k1_sys::secp256k1_schnorrsig_sign_custom(
44                secp.ctx().as_ref(),
45                sig.as_mut_c_ptr(),
46                msg.as_c_ptr(),
47                32_usize,
48                keypair.as_c_ptr(),
49                &extra_params,
50            )
51        );
52
53        SchnorrSignature::from_slice(&sig).unwrap()
54    }
55}
56
57/// Compute a signature point for the given public key, nonce and message.
58pub fn schnorrsig_compute_sig_point<C: Verification>(
59    secp: &Secp256k1<C>,
60    pubkey: &XOnlyPublicKey,
61    nonce: &XOnlyPublicKey,
62    message: &Message,
63) -> Result<PublicKey, Error> {
64    let hash = create_schnorr_hash(message, nonce, pubkey);
65    let pk = schnorr_pubkey_to_pubkey(pubkey)?;
66    let scalar = Scalar::from_be_bytes(hash).unwrap();
67    let tweaked = pk.mul_tweak(secp, &scalar)?;
68    let npk = schnorr_pubkey_to_pubkey(nonce)?;
69    Ok(npk.combine(&tweaked)?)
70}
71
72/// Decompose a bip340 signature into a nonce and a secret key (as byte array)
73pub fn schnorrsig_decompose(
74    signature: &SchnorrSignature,
75) -> Result<(XOnlyPublicKey, &[u8]), Error> {
76    let bytes = signature.as_ref();
77    Ok((XOnlyPublicKey::from_slice(&bytes[0..32])?, &bytes[32..64]))
78}
79
80extern "C" fn constant_nonce_fn(
81    nonce32: *mut c_uchar,
82    _msg32: *const c_uchar,
83    _msg_len: size_t,
84    _key32: *const c_uchar,
85    _xonly_pk32: *const c_uchar,
86    _algo16: *const c_uchar,
87    _algo_len: size_t,
88    data: *mut c_void,
89) -> c_int {
90    unsafe {
91        ptr::copy_nonoverlapping(data as *const c_uchar, nonce32, 32);
92    }
93    1
94}
95
96fn create_schnorr_hash(msg: &Message, nonce: &XOnlyPublicKey, pubkey: &XOnlyPublicKey) -> [u8; 32] {
97    let mut buf = Vec::<u8>::new();
98    buf.extend(nonce.serialize());
99    buf.extend(pubkey.serialize());
100    buf.extend(msg.as_ref().to_vec());
101    BIP340Hash::hash(&buf).to_byte_array()
102}
103
104fn schnorr_pubkey_to_pubkey(schnorr_pubkey: &XOnlyPublicKey) -> Result<PublicKey, Error> {
105    let mut buf = Vec::<u8>::with_capacity(33);
106    buf.push(0x02);
107    buf.extend(schnorr_pubkey.serialize());
108    Ok(PublicKey::from_slice(&buf)?)
109}