use crate::io;
use crate::ln::msgs::DecodeError;
use crate::util::ser::Readable;
use crate::util::ser::Writeable;
use crate::util::ser::Writer;
use bitcoin::hashes::sha256::Hash as Sha256;
use bitcoin::hashes::Hash;
use bitcoin::hashes::HashEngine;
use bitcoin::secp256k1;
use bitcoin::secp256k1::PublicKey;
use bitcoin::secp256k1::Scalar;
use bitcoin::secp256k1::Secp256k1;
use bitcoin::secp256k1::SecretKey;
macro_rules! doc_comment {
($x:expr, $($tt:tt)*) => {
#[doc = $x]
$($tt)*
};
}
macro_rules! basepoint_impl {
($BasepointT:ty $(, $KeyName: expr)?) => {
impl $BasepointT {
pub fn to_public_key(&self) -> PublicKey {
self.0
}
$(doc_comment!(
concat!(
"Derives the \"tweak\" used in calculate [`", $KeyName, "::from_basepoint`].\n",
"\n",
"[`", $KeyName, "::from_basepoint`] calculates a private key as:\n",
"`privkey = basepoint_secret + SHA256(per_commitment_point || basepoint)`\n",
"\n",
"This calculates the hash part in the tweak derivation process, which is used to\n",
"ensure that each key is unique and cannot be guessed by an external party."
),
pub fn derive_add_tweak(&self, per_commitment_point: &PublicKey) -> Sha256 {
let mut sha = Sha256::engine();
sha.input(&per_commitment_point.serialize());
sha.input(&self.to_public_key().serialize());
Sha256::from_engine(sha)
});
)?
}
impl From<PublicKey> for $BasepointT {
fn from(value: PublicKey) -> Self {
Self(value)
}
}
};
}
macro_rules! key_impl {
($BasepointT:ty, $KeyName:expr) => {
doc_comment! {
concat!("Derive a public ", $KeyName, " using one node's `per_commitment_point` and its countersignatory's `basepoint`"),
pub fn from_basepoint<T: secp256k1::Signing>(
secp_ctx: &Secp256k1<T>,
countersignatory_basepoint: &$BasepointT,
per_commitment_point: &PublicKey,
) -> Self {
Self(derive_public_key(secp_ctx, per_commitment_point, &countersignatory_basepoint.0))
}
}
doc_comment! {
concat!("Build a ", $KeyName, " directly from an already-derived private key"),
pub fn from_secret_key<T: secp256k1::Signing>(secp_ctx: &Secp256k1<T>, sk: &SecretKey) -> Self {
Self(PublicKey::from_secret_key(&secp_ctx, &sk))
}
}
pub fn to_public_key(&self) -> PublicKey {
self.0
}
}
}
macro_rules! key_read_write {
($SelfT:ty) => {
impl Writeable for $SelfT {
fn write<W: Writer>(&self, w: &mut W) -> Result<(), io::Error> {
self.0.serialize().write(w)
}
}
impl Readable for $SelfT {
fn read<R: io::Read>(r: &mut R) -> Result<Self, DecodeError> {
let key: PublicKey = Readable::read(r)?;
Ok(Self(key))
}
}
};
}
#[derive(PartialEq, Eq, Clone, Copy, Debug, Hash)]
pub struct DelayedPaymentBasepoint(pub PublicKey);
basepoint_impl!(DelayedPaymentBasepoint, "DelayedPaymentKey");
key_read_write!(DelayedPaymentBasepoint);
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
pub struct DelayedPaymentKey(pub PublicKey);
impl DelayedPaymentKey {
key_impl!(DelayedPaymentBasepoint, "delayedpubkey");
}
key_read_write!(DelayedPaymentKey);
#[derive(PartialEq, Eq, Clone, Copy, Debug, Hash)]
pub struct HtlcBasepoint(pub PublicKey);
basepoint_impl!(HtlcBasepoint, "HtlcKey");
key_read_write!(HtlcBasepoint);
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
pub struct HtlcKey(pub PublicKey);
impl HtlcKey {
key_impl!(HtlcBasepoint, "htlcpubkey");
}
key_read_write!(HtlcKey);
fn derive_public_key<T: secp256k1::Signing>(
secp_ctx: &Secp256k1<T>, per_commitment_point: &PublicKey, base_point: &PublicKey,
) -> PublicKey {
let mut sha = Sha256::engine();
sha.input(&per_commitment_point.serialize());
sha.input(&base_point.serialize());
let res = Sha256::from_engine(sha);
add_public_key_tweak(secp_ctx, base_point, &res)
}
pub fn add_public_key_tweak<T: secp256k1::Signing>(
secp_ctx: &Secp256k1<T>, base_point: &PublicKey, tweak: &Sha256,
) -> PublicKey {
let hashkey = PublicKey::from_secret_key(
&secp_ctx,
&SecretKey::from_slice(tweak.as_byte_array())
.expect("Hashes should always be valid keys unless SHA-256 is broken"),
);
base_point.combine(&hashkey)
.expect("Addition only fails if the tweak is the inverse of the key. This is not possible when the tweak contains the hash of the key.")
}
#[derive(PartialEq, Eq, Clone, Copy, Debug, Hash)]
pub struct RevocationBasepoint(pub PublicKey);
basepoint_impl!(RevocationBasepoint);
key_read_write!(RevocationBasepoint);
#[derive(PartialEq, Eq, Clone, Copy, Debug, Hash)]
pub struct RevocationKey(pub PublicKey);
impl RevocationKey {
pub fn from_basepoint<T: secp256k1::Verification>(
secp_ctx: &Secp256k1<T>, countersignatory_basepoint: &RevocationBasepoint,
per_commitment_point: &PublicKey,
) -> Self {
let rev_append_commit_hash_key = {
let mut sha = Sha256::engine();
sha.input(&countersignatory_basepoint.to_public_key().serialize());
sha.input(&per_commitment_point.serialize());
Sha256::from_engine(sha).to_byte_array()
};
let commit_append_rev_hash_key = {
let mut sha = Sha256::engine();
sha.input(&per_commitment_point.serialize());
sha.input(&countersignatory_basepoint.to_public_key().serialize());
Sha256::from_engine(sha).to_byte_array()
};
let countersignatory_contrib = countersignatory_basepoint.to_public_key().mul_tweak(&secp_ctx, &Scalar::from_be_bytes(rev_append_commit_hash_key).unwrap())
.expect("Multiplying a valid public key by a hash is expected to never fail per secp256k1 docs");
let broadcaster_contrib = (&per_commitment_point).mul_tweak(&secp_ctx, &Scalar::from_be_bytes(commit_append_rev_hash_key).unwrap())
.expect("Multiplying a valid public key by a hash is expected to never fail per secp256k1 docs");
let pk = countersignatory_contrib.combine(&broadcaster_contrib)
.expect("Addition only fails if the tweak is the inverse of the key. This is not possible when the tweak commits to the key.");
Self(pk)
}
pub fn to_public_key(&self) -> PublicKey {
self.0
}
}
key_read_write!(RevocationKey);
#[cfg(test)]
mod test {
use super::derive_public_key;
use bitcoin::hex::FromHex;
use bitcoin::secp256k1::{PublicKey, Secp256k1, SecretKey};
#[test]
fn test_key_derivation() {
let secp_ctx = Secp256k1::new();
let base_secret = SecretKey::from_slice(
&<Vec<u8>>::from_hex(
"000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f",
)
.unwrap()[..],
)
.unwrap();
let per_commitment_secret = SecretKey::from_slice(
&<Vec<u8>>::from_hex(
"1f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100",
)
.unwrap()[..],
)
.unwrap();
let base_point = PublicKey::from_secret_key(&secp_ctx, &base_secret);
assert_eq!(
base_point.serialize()[..],
<Vec<u8>>::from_hex(
"036d6caac248af96f6afa7f904f550253a0f3ef3f5aa2fe6838a95b216691468e2"
)
.unwrap()[..]
);
let per_commitment_point = PublicKey::from_secret_key(&secp_ctx, &per_commitment_secret);
assert_eq!(
per_commitment_point.serialize()[..],
<Vec<u8>>::from_hex(
"025f7117a78150fe2ef97db7cfc83bd57b2e2c0d0dd25eaf467a4a1c2a45ce1486"
)
.unwrap()[..]
);
assert_eq!(
derive_public_key(&secp_ctx, &per_commitment_point, &base_point).serialize()[..],
<Vec<u8>>::from_hex(
"0235f2dbfaa89b57ec7b055afe29849ef7ddfeb1cefdb9ebdc43f5494984db29e5"
)
.unwrap()[..]
);
}
}