Bump rust-bitcoin to v0.30.2
[rust-lightning] / lightning / src / offers / signer.rs
index 2ee3d13afbb9c173c1c9bf03654d392e7fa5f450..e0abd82767f6c9fd51dc0a7e425f7236631a0f67 100644 (file)
@@ -16,15 +16,26 @@ use bitcoin::hashes::sha256::Hash as Sha256;
 use bitcoin::secp256k1::{KeyPair, PublicKey, Secp256k1, SecretKey, self};
 use core::convert::TryFrom;
 use core::fmt;
+use crate::ln::channelmanager::PaymentId;
 use crate::ln::inbound_payment::{ExpandedKey, IV_LEN, Nonce};
 use crate::offers::merkle::TlvRecord;
 use crate::util::ser::Writeable;
 
 use crate::prelude::*;
 
+// Use a different HMAC input for each derivation. Otherwise, an attacker could:
+// - take an Offer that has metadata consisting of a nonce and HMAC
+// - strip off the HMAC and replace the signing_pubkey where the privkey is the HMAC,
+// - generate and sign an invoice using the new signing_pubkey, and
+// - claim they paid it since they would know the preimage of the invoice's payment_hash
 const DERIVED_METADATA_HMAC_INPUT: &[u8; 16] = &[1; 16];
 const DERIVED_METADATA_AND_KEYS_HMAC_INPUT: &[u8; 16] = &[2; 16];
 
+// Additional HMAC inputs to distinguish use cases, either Offer or Refund/InvoiceRequest, where
+// metadata for the latter contain an encrypted PaymentId.
+const WITHOUT_ENCRYPTED_PAYMENT_ID_HMAC_INPUT: &[u8; 16] = &[3; 16];
+const WITH_ENCRYPTED_PAYMENT_ID_HMAC_INPUT: &[u8; 16] = &[4; 16];
+
 /// Message metadata which possibly is derived from [`MetadataMaterial`] such that it can be
 /// verified.
 #[derive(Clone)]
@@ -56,7 +67,20 @@ impl Metadata {
                }
        }
 
-       pub fn derives_keys(&self) -> bool {
+       pub fn derives_payer_keys(&self) -> bool {
+               match self {
+                       // Infer whether Metadata::derived_from was called on Metadata::DerivedSigningPubkey to
+                       // produce Metadata::Bytes. This is merely to determine which fields should be included
+                       // when verifying a message. It doesn't necessarily indicate that keys were in fact
+                       // derived, as wouldn't be the case if a Metadata::Bytes with length PaymentId::LENGTH +
+                       // Nonce::LENGTH had been set explicitly.
+                       Metadata::Bytes(bytes) => bytes.len() == PaymentId::LENGTH + Nonce::LENGTH,
+                       Metadata::Derived(_) => false,
+                       Metadata::DerivedSigningPubkey(_) => true,
+               }
+       }
+
+       pub fn derives_recipient_keys(&self) -> bool {
                match self {
                        // Infer whether Metadata::derived_from was called on Metadata::DerivedSigningPubkey to
                        // produce Metadata::Bytes. This is merely to determine which fields should be included
@@ -96,6 +120,12 @@ impl Metadata {
        }
 }
 
+impl Default for Metadata {
+       fn default() -> Self {
+               Metadata::Bytes(vec![])
+       }
+}
+
 impl fmt::Debug for Metadata {
        fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
                match self {
@@ -126,21 +156,34 @@ impl PartialEq for Metadata {
 pub(super) struct MetadataMaterial {
        nonce: Nonce,
        hmac: HmacEngine<Sha256>,
+       // Some for payer metadata and None for offer metadata
+       encrypted_payment_id: Option<[u8; PaymentId::LENGTH]>,
 }
 
 impl MetadataMaterial {
-       pub fn new(nonce: Nonce, expanded_key: &ExpandedKey, iv_bytes: &[u8; IV_LEN]) -> Self {
+       pub fn new(
+               nonce: Nonce, expanded_key: &ExpandedKey, iv_bytes: &[u8; IV_LEN],
+               payment_id: Option<PaymentId>
+       ) -> Self {
+               // Encrypt payment_id
+               let encrypted_payment_id = payment_id.map(|payment_id| {
+                       expanded_key.crypt_for_offer(payment_id.0, nonce)
+               });
+
                Self {
                        nonce,
                        hmac: expanded_key.hmac_for_offer(nonce, iv_bytes),
+                       encrypted_payment_id,
                }
        }
 
        fn derive_metadata(mut self) -> Vec<u8> {
                self.hmac.input(DERIVED_METADATA_HMAC_INPUT);
+               self.maybe_include_encrypted_payment_id();
 
-               let mut bytes = self.nonce.as_slice().to_vec();
-               bytes.extend_from_slice(&Hmac::from_engine(self.hmac).into_inner());
+               let mut bytes = self.encrypted_payment_id.map(|id| id.to_vec()).unwrap_or(vec![]);
+               bytes.extend_from_slice(self.nonce.as_slice());
+               bytes.extend_from_slice(Hmac::from_engine(self.hmac).as_byte_array());
                bytes
        }
 
@@ -148,12 +191,72 @@ impl MetadataMaterial {
                mut self, secp_ctx: &Secp256k1<T>
        ) -> (Vec<u8>, KeyPair) {
                self.hmac.input(DERIVED_METADATA_AND_KEYS_HMAC_INPUT);
+               self.maybe_include_encrypted_payment_id();
+
+               let mut bytes = self.encrypted_payment_id.map(|id| id.to_vec()).unwrap_or(vec![]);
+               bytes.extend_from_slice(self.nonce.as_slice());
 
                let hmac = Hmac::from_engine(self.hmac);
-               let privkey = SecretKey::from_slice(hmac.as_inner()).unwrap();
+               let privkey = SecretKey::from_slice(hmac.as_byte_array()).unwrap();
                let keys = KeyPair::from_secret_key(secp_ctx, &privkey);
-               (self.nonce.as_slice().to_vec(), keys)
+
+               (bytes, keys)
+       }
+
+       fn maybe_include_encrypted_payment_id(&mut self) {
+               match self.encrypted_payment_id {
+                       None => self.hmac.input(WITHOUT_ENCRYPTED_PAYMENT_ID_HMAC_INPUT),
+                       Some(encrypted_payment_id) => {
+                               self.hmac.input(WITH_ENCRYPTED_PAYMENT_ID_HMAC_INPUT);
+                               self.hmac.input(&encrypted_payment_id)
+                       },
+               }
+       }
+}
+
+pub(super) fn derive_keys(nonce: Nonce, expanded_key: &ExpandedKey) -> KeyPair {
+       const IV_BYTES: &[u8; IV_LEN] = b"LDK Invoice ~~~~";
+       let secp_ctx = Secp256k1::new();
+       let hmac = Hmac::from_engine(expanded_key.hmac_for_offer(nonce, IV_BYTES));
+       let privkey = SecretKey::from_slice(hmac.as_byte_array()).unwrap();
+       KeyPair::from_secret_key(&secp_ctx, &privkey)
+}
+
+/// Verifies data given in a TLV stream was used to produce the given metadata, consisting of:
+/// - a 256-bit [`PaymentId`],
+/// - a 128-bit [`Nonce`], and possibly
+/// - a [`Sha256`] hash of the nonce and the TLV records using the [`ExpandedKey`].
+///
+/// If the latter is not included in the metadata, the TLV stream is used to check if the given
+/// `signing_pubkey` can be derived from it.
+///
+/// Returns the [`PaymentId`] that should be used for sending the payment.
+pub(super) fn verify_payer_metadata<'a, T: secp256k1::Signing>(
+       metadata: &[u8], expanded_key: &ExpandedKey, iv_bytes: &[u8; IV_LEN],
+       signing_pubkey: PublicKey, tlv_stream: impl core::iter::Iterator<Item = TlvRecord<'a>>,
+       secp_ctx: &Secp256k1<T>
+) -> Result<PaymentId, ()> {
+       if metadata.len() < PaymentId::LENGTH {
+               return Err(());
        }
+
+       let mut encrypted_payment_id = [0u8; PaymentId::LENGTH];
+       encrypted_payment_id.copy_from_slice(&metadata[..PaymentId::LENGTH]);
+
+       let mut hmac = hmac_for_message(
+               &metadata[PaymentId::LENGTH..], expanded_key, iv_bytes, tlv_stream
+       )?;
+       hmac.input(WITH_ENCRYPTED_PAYMENT_ID_HMAC_INPUT);
+       hmac.input(&encrypted_payment_id);
+
+       verify_metadata(
+               &metadata[PaymentId::LENGTH..], Hmac::from_engine(hmac), signing_pubkey, secp_ctx
+       )?;
+
+       let nonce = Nonce::try_from(&metadata[PaymentId::LENGTH..][..Nonce::LENGTH]).unwrap();
+       let payment_id = expanded_key.crypt_for_offer(encrypted_payment_id, nonce);
+
+       Ok(PaymentId(payment_id))
 }
 
 /// Verifies data given in a TLV stream was used to produce the given metadata, consisting of:
@@ -162,18 +265,53 @@ impl MetadataMaterial {
 ///
 /// If the latter is not included in the metadata, the TLV stream is used to check if the given
 /// `signing_pubkey` can be derived from it.
-pub(super) fn verify_metadata<'a, T: secp256k1::Signing>(
-       metadata: &Vec<u8>, expanded_key: &ExpandedKey, iv_bytes: &[u8; IV_LEN],
+///
+/// Returns the [`KeyPair`] for signing the invoice, if it can be derived from the metadata.
+pub(super) fn verify_recipient_metadata<'a, T: secp256k1::Signing>(
+       metadata: &[u8], expanded_key: &ExpandedKey, iv_bytes: &[u8; IV_LEN],
        signing_pubkey: PublicKey, tlv_stream: impl core::iter::Iterator<Item = TlvRecord<'a>>,
        secp_ctx: &Secp256k1<T>
-) -> bool {
+) -> Result<Option<KeyPair>, ()> {
+       let mut hmac = hmac_for_message(metadata, expanded_key, iv_bytes, tlv_stream)?;
+       hmac.input(WITHOUT_ENCRYPTED_PAYMENT_ID_HMAC_INPUT);
+
+       verify_metadata(metadata, Hmac::from_engine(hmac), signing_pubkey, secp_ctx)
+}
+
+fn verify_metadata<T: secp256k1::Signing>(
+       metadata: &[u8], hmac: Hmac<Sha256>, signing_pubkey: PublicKey, secp_ctx: &Secp256k1<T>
+) -> Result<Option<KeyPair>, ()> {
+       if metadata.len() == Nonce::LENGTH {
+               let derived_keys = KeyPair::from_secret_key(
+                       secp_ctx, &SecretKey::from_slice(hmac.as_byte_array()).unwrap()
+               );
+               if fixed_time_eq(&signing_pubkey.serialize(), &derived_keys.public_key().serialize()) {
+                       Ok(Some(derived_keys))
+               } else {
+                       Err(())
+               }
+       } else if metadata[Nonce::LENGTH..].len() == Sha256::LEN {
+               if fixed_time_eq(&metadata[Nonce::LENGTH..], &hmac.to_byte_array()) {
+                       Ok(None)
+               } else {
+                       Err(())
+               }
+       } else {
+               Err(())
+       }
+}
+
+fn hmac_for_message<'a>(
+       metadata: &[u8], expanded_key: &ExpandedKey, iv_bytes: &[u8; IV_LEN],
+       tlv_stream: impl core::iter::Iterator<Item = TlvRecord<'a>>
+) -> Result<HmacEngine<Sha256>, ()> {
        if metadata.len() < Nonce::LENGTH {
-               return false;
+               return Err(());
        }
 
        let nonce = match Nonce::try_from(&metadata[..Nonce::LENGTH]) {
                Ok(nonce) => nonce,
-               Err(_) => return false,
+               Err(_) => return Err(()),
        };
        let mut hmac = expanded_key.hmac_for_offer(nonce, iv_bytes);
 
@@ -183,13 +321,9 @@ pub(super) fn verify_metadata<'a, T: secp256k1::Signing>(
 
        if metadata.len() == Nonce::LENGTH {
                hmac.input(DERIVED_METADATA_AND_KEYS_HMAC_INPUT);
-               let hmac = Hmac::from_engine(hmac);
-               let derived_pubkey = SecretKey::from_slice(hmac.as_inner()).unwrap().public_key(&secp_ctx);
-               fixed_time_eq(&signing_pubkey.serialize(), &derived_pubkey.serialize())
-       } else if metadata[Nonce::LENGTH..].len() == Sha256::LEN {
-               hmac.input(DERIVED_METADATA_HMAC_INPUT);
-               fixed_time_eq(&metadata[Nonce::LENGTH..], &Hmac::from_engine(hmac).into_inner())
        } else {
-               false
+               hmac.input(DERIVED_METADATA_HMAC_INPUT);
        }
+
+       Ok(hmac)
 }