--------
Security is the primary focus of `rust-lightning`; disclosure of security
-vulnerabilites helps prevent user loss of funds. If you believe a vulnerability
+vulnerabilities helps prevent user loss of funds. If you believe a vulnerability
may affect other Lightning implementations, please inform them.
You can find further information on submitting (possible) vulnerabilities in the
use lightning::ln::functional_test_utils::*;
use lightning::offers::invoice::{BlindedPayInfo, UnsignedBolt12Invoice};
use lightning::offers::invoice_request::UnsignedInvoiceRequest;
-use lightning::onion_message::{Destination, MessageRouter, OnionMessagePath};
+use lightning::onion_message::messenger::{Destination, MessageRouter, OnionMessagePath};
use lightning::util::test_channel_signer::{TestChannelSigner, EnforcementState};
use lightning::util::errors::APIError;
use lightning::util::logger::Logger;
},
0x89 => { fee_est_c.ret_val.store(253, atomic::Ordering::Release); nodes[2].maybe_update_chan_fees(); },
+ 0xf0 => {
+ let pending_updates = monitor_a.chain_monitor.list_pending_monitor_updates().remove(&chan_1_funding).unwrap();
+ if let Some(id) = pending_updates.get(0) {
+ monitor_a.chain_monitor.channel_monitor_updated(chan_1_funding, *id).unwrap();
+ }
+ nodes[0].process_monitor_events();
+ }
+ 0xf1 => {
+ let pending_updates = monitor_a.chain_monitor.list_pending_monitor_updates().remove(&chan_1_funding).unwrap();
+ if let Some(id) = pending_updates.get(1) {
+ monitor_a.chain_monitor.channel_monitor_updated(chan_1_funding, *id).unwrap();
+ }
+ nodes[0].process_monitor_events();
+ }
+ 0xf2 => {
+ let pending_updates = monitor_a.chain_monitor.list_pending_monitor_updates().remove(&chan_1_funding).unwrap();
+ if let Some(id) = pending_updates.last() {
+ monitor_a.chain_monitor.channel_monitor_updated(chan_1_funding, *id).unwrap();
+ }
+ nodes[0].process_monitor_events();
+ }
+
+ 0xf4 => {
+ let pending_updates = monitor_b.chain_monitor.list_pending_monitor_updates().remove(&chan_1_funding).unwrap();
+ if let Some(id) = pending_updates.get(0) {
+ monitor_b.chain_monitor.channel_monitor_updated(chan_1_funding, *id).unwrap();
+ }
+ nodes[1].process_monitor_events();
+ }
+ 0xf5 => {
+ let pending_updates = monitor_b.chain_monitor.list_pending_monitor_updates().remove(&chan_1_funding).unwrap();
+ if let Some(id) = pending_updates.get(1) {
+ monitor_b.chain_monitor.channel_monitor_updated(chan_1_funding, *id).unwrap();
+ }
+ nodes[1].process_monitor_events();
+ }
+ 0xf6 => {
+ let pending_updates = monitor_b.chain_monitor.list_pending_monitor_updates().remove(&chan_1_funding).unwrap();
+ if let Some(id) = pending_updates.last() {
+ monitor_b.chain_monitor.channel_monitor_updated(chan_1_funding, *id).unwrap();
+ }
+ nodes[1].process_monitor_events();
+ }
+
+ 0xf8 => {
+ let pending_updates = monitor_b.chain_monitor.list_pending_monitor_updates().remove(&chan_2_funding).unwrap();
+ if let Some(id) = pending_updates.get(0) {
+ monitor_b.chain_monitor.channel_monitor_updated(chan_2_funding, *id).unwrap();
+ }
+ nodes[1].process_monitor_events();
+ }
+ 0xf9 => {
+ let pending_updates = monitor_b.chain_monitor.list_pending_monitor_updates().remove(&chan_2_funding).unwrap();
+ if let Some(id) = pending_updates.get(1) {
+ monitor_b.chain_monitor.channel_monitor_updated(chan_2_funding, *id).unwrap();
+ }
+ nodes[1].process_monitor_events();
+ }
+ 0xfa => {
+ let pending_updates = monitor_b.chain_monitor.list_pending_monitor_updates().remove(&chan_2_funding).unwrap();
+ if let Some(id) = pending_updates.last() {
+ monitor_b.chain_monitor.channel_monitor_updated(chan_2_funding, *id).unwrap();
+ }
+ nodes[1].process_monitor_events();
+ }
+
+ 0xfc => {
+ let pending_updates = monitor_c.chain_monitor.list_pending_monitor_updates().remove(&chan_2_funding).unwrap();
+ if let Some(id) = pending_updates.get(0) {
+ monitor_c.chain_monitor.channel_monitor_updated(chan_2_funding, *id).unwrap();
+ }
+ nodes[2].process_monitor_events();
+ }
+ 0xfd => {
+ let pending_updates = monitor_c.chain_monitor.list_pending_monitor_updates().remove(&chan_2_funding).unwrap();
+ if let Some(id) = pending_updates.get(1) {
+ monitor_c.chain_monitor.channel_monitor_updated(chan_2_funding, *id).unwrap();
+ }
+ nodes[2].process_monitor_events();
+ }
+ 0xfe => {
+ let pending_updates = monitor_c.chain_monitor.list_pending_monitor_updates().remove(&chan_2_funding).unwrap();
+ if let Some(id) = pending_updates.last() {
+ monitor_c.chain_monitor.channel_monitor_updated(chan_2_funding, *id).unwrap();
+ }
+ nodes[2].process_monitor_events();
+ }
+
0xff => {
// Test that no channel is in a stuck state where neither party can send funds even
// after we resolve all pending events.
use lightning::ln::functional_test_utils::*;
use lightning::offers::invoice::{BlindedPayInfo, UnsignedBolt12Invoice};
use lightning::offers::invoice_request::UnsignedInvoiceRequest;
-use lightning::onion_message::{Destination, MessageRouter, OnionMessagePath};
+use lightning::onion_message::messenger::{Destination, MessageRouter, OnionMessagePath};
use lightning::routing::gossip::{P2PGossipSync, NetworkGraph};
use lightning::routing::utxo::UtxoLookup;
use lightning::routing::router::{InFlightHtlcs, PaymentParameters, Route, RouteParameters, Router};
use lightning::util::test_channel_signer::TestChannelSigner;
use lightning::util::logger::Logger;
use lightning::util::ser::{Readable, Writeable, Writer};
-use lightning::onion_message::{CustomOnionMessageHandler, Destination, MessageRouter, OffersMessage, OffersMessageHandler, OnionMessageContents, OnionMessagePath, OnionMessenger, PendingOnionMessage};
+use lightning::onion_message::messenger::{CustomOnionMessageHandler, Destination, MessageRouter, OnionMessagePath, OnionMessenger, PendingOnionMessage};
+use lightning::onion_message::offers::{OffersMessage, OffersMessageHandler};
+use lightning::onion_message::packet::OnionMessageContents;
use crate::utils::test_logger;
pub struct GossipVerifier<S: FutureSpawner,
Blocks: Deref + Send + Sync + 'static + Clone,
L: Deref + Send + Sync + 'static,
- APM: Deref + Send + Sync + 'static + Clone,
> where
Blocks::Target: UtxoSource,
L::Target: Logger,
- APM::Target: APeerManager,
{
source: Blocks,
- peer_manager: APM,
+ peer_manager_wake: Arc<dyn Fn() + Send + Sync>,
gossiper: Arc<P2PGossipSync<Arc<NetworkGraph<L>>, Self, L>>,
spawn: S,
block_cache: Arc<Mutex<VecDeque<(u32, Block)>>>,
impl<S: FutureSpawner,
Blocks: Deref + Send + Sync + Clone,
L: Deref + Send + Sync,
- APM: Deref + Send + Sync + Clone,
-> GossipVerifier<S, Blocks, L, APM> where
+> GossipVerifier<S, Blocks, L> where
Blocks::Target: UtxoSource,
L::Target: Logger,
- APM::Target: APeerManager,
{
/// Constructs a new [`GossipVerifier`].
///
/// This is expected to be given to a [`P2PGossipSync`] (initially constructed with `None` for
/// the UTXO lookup) via [`P2PGossipSync::add_utxo_lookup`].
- pub fn new(source: Blocks, spawn: S, gossiper: Arc<P2PGossipSync<Arc<NetworkGraph<L>>, Self, L>>, peer_manager: APM) -> Self {
+ pub fn new<APM: Deref + Send + Sync + Clone + 'static>(
+ source: Blocks, spawn: S, gossiper: Arc<P2PGossipSync<Arc<NetworkGraph<L>>, Self, L>>, peer_manager: APM
+ ) -> Self where APM::Target: APeerManager {
+ let peer_manager_wake = Arc::new(move || peer_manager.as_ref().process_events());
Self {
- source, spawn, gossiper, peer_manager,
+ source, spawn, gossiper, peer_manager_wake,
block_cache: Arc::new(Mutex::new(VecDeque::with_capacity(BLOCK_CACHE_SIZE))),
}
}
impl<S: FutureSpawner,
Blocks: Deref + Send + Sync + Clone,
L: Deref + Send + Sync,
- APM: Deref + Send + Sync + Clone,
-> Deref for GossipVerifier<S, Blocks, L, APM> where
+> Deref for GossipVerifier<S, Blocks, L> where
Blocks::Target: UtxoSource,
L::Target: Logger,
- APM::Target: APeerManager,
{
type Target = Self;
fn deref(&self) -> &Self { self }
impl<S: FutureSpawner,
Blocks: Deref + Send + Sync + Clone,
L: Deref + Send + Sync,
- APM: Deref + Send + Sync + Clone,
-> UtxoLookup for GossipVerifier<S, Blocks, L, APM> where
+> UtxoLookup for GossipVerifier<S, Blocks, L> where
Blocks::Target: UtxoSource,
L::Target: Logger,
- APM::Target: APeerManager,
{
fn get_utxo(&self, _chain_hash: &ChainHash, short_channel_id: u64) -> UtxoResult {
let res = UtxoFuture::new();
let source = self.source.clone();
let gossiper = Arc::clone(&self.gossiper);
let block_cache = Arc::clone(&self.block_cache);
- let pm = self.peer_manager.clone();
+ let pmw = Arc::clone(&self.peer_manager_wake);
self.spawn.spawn(async move {
let res = Self::retrieve_utxo(source, block_cache, short_channel_id).await;
fut.resolve(gossiper.network_graph(), &*gossiper, res);
- pm.as_ref().process_events();
+ (pmw)();
});
UtxoResult::Async(res)
}
}
// Combine all bits from buffer with enough bits from this rounds byte so that they fill
- // a u5. Save reamining bits from byte to buffer.
+ // a u5. Save remaining bits from byte to buffer.
let from_buffer = self.buffer >> 3;
let from_byte = byte >> (3 + self.buffer_bits); // buffer_bits <= 4
written_len += res;
if written_len == data.len() { return written_len; }
},
+ Err(ref e) if e.kind() == std::io::ErrorKind::WouldBlock => {
+ continue;
+ }
Err(_) => return written_len,
}
},
use crate::io;
use crate::io::Cursor;
use crate::ln::onion_utils;
-use crate::onion_message::ControlTlvs;
+use crate::onion_message::packet::ControlTlvs;
use crate::prelude::*;
use crate::sign::{NodeSigner, Recipient};
-use crate::util::chacha20poly1305rfc::ChaChaPolyReadAdapter;
+use crate::crypto::streams::ChaChaPolyReadAdapter;
use crate::util::ser::{FixedLengthReader, LengthReadableArgs, Writeable, Writer};
use core::mem;
pub htlc_minimum_msat: u64,
}
-impl From<CounterpartyForwardingInfo> for PaymentRelay {
- fn from(info: CounterpartyForwardingInfo) -> Self {
+impl TryFrom<CounterpartyForwardingInfo> for PaymentRelay {
+ type Error = ();
+
+ fn try_from(info: CounterpartyForwardingInfo) -> Result<Self, ()> {
let CounterpartyForwardingInfo {
fee_base_msat, fee_proportional_millionths, cltv_expiry_delta
} = info;
- Self { cltv_expiry_delta, fee_proportional_millionths, fee_base_msat }
+
+ // Avoid exposing esoteric CLTV expiry deltas
+ let cltv_expiry_delta = match cltv_expiry_delta {
+ 0..=40 => 40,
+ 41..=80 => 80,
+ 81..=144 => 144,
+ 145..=216 => 216,
+ _ => return Err(()),
+ };
+
+ Ok(Self { cltv_expiry_delta, fee_proportional_millionths, fee_base_msat })
}
}
use super::{BlindedHop, BlindedPath};
use crate::ln::msgs::DecodeError;
use crate::ln::onion_utils;
-use crate::onion_message::Destination;
-use crate::util::chacha20poly1305rfc::ChaChaPolyWriteAdapter;
+use crate::onion_message::messenger::Destination;
+use crate::crypto::streams::ChaChaPolyWriteAdapter;
use crate::util::ser::{Readable, Writeable};
use crate::io;
preimages_slice_to_htlcs!($preimages_slice).into_iter().map(|(htlc, _)| (htlc, None)).collect()
}
}
- let dummy_sig = crate::util::crypto::sign(&secp_ctx,
+ let dummy_sig = crate::crypto::utils::sign(&secp_ctx,
&bitcoin::secp256k1::Message::from_slice(&[42; 32]).unwrap(),
&SecretKey::from_slice(&[42; 32]).unwrap());
--- /dev/null
+// This file was stolen from rust-crypto.
+// Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// This file is licensed under the Apache License, Version 2.0 <LICENSE-APACHE
+// or http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your option.
+// You may not use this file except in accordance with one or both of these
+// licenses.
+
+#[cfg(not(fuzzing))]
+mod real_chacha {
+ use core::cmp;
+ use core::convert::TryInto;
+
+ #[derive(Clone, Copy, PartialEq, Eq)]
+ #[allow(non_camel_case_types)]
+ struct u32x4(pub u32, pub u32, pub u32, pub u32);
+ impl ::core::ops::Add for u32x4 {
+ type Output = u32x4;
+ #[inline]
+ fn add(self, rhs: u32x4) -> u32x4 {
+ u32x4(self.0.wrapping_add(rhs.0),
+ self.1.wrapping_add(rhs.1),
+ self.2.wrapping_add(rhs.2),
+ self.3.wrapping_add(rhs.3))
+ }
+ }
+ impl ::core::ops::Sub for u32x4 {
+ type Output = u32x4;
+ #[inline]
+ fn sub(self, rhs: u32x4) -> u32x4 {
+ u32x4(self.0.wrapping_sub(rhs.0),
+ self.1.wrapping_sub(rhs.1),
+ self.2.wrapping_sub(rhs.2),
+ self.3.wrapping_sub(rhs.3))
+ }
+ }
+ impl ::core::ops::BitXor for u32x4 {
+ type Output = u32x4;
+ #[inline]
+ fn bitxor(self, rhs: u32x4) -> u32x4 {
+ u32x4(self.0 ^ rhs.0, self.1 ^ rhs.1, self.2 ^ rhs.2, self.3 ^ rhs.3)
+ }
+ }
+ impl ::core::ops::Shr<u8> for u32x4 {
+ type Output = u32x4;
+ #[inline]
+ fn shr(self, shr: u8) -> u32x4 {
+ u32x4(self.0 >> shr, self.1 >> shr, self.2 >> shr, self.3 >> shr)
+ }
+ }
+ impl ::core::ops::Shl<u8> for u32x4 {
+ type Output = u32x4;
+ #[inline]
+ fn shl(self, shl: u8) -> u32x4 {
+ u32x4(self.0 << shl, self.1 << shl, self.2 << shl, self.3 << shl)
+ }
+ }
+ impl u32x4 {
+ #[inline]
+ fn from_bytes(bytes: &[u8]) -> Self {
+ assert_eq!(bytes.len(), 4*4);
+ Self (
+ u32::from_le_bytes(bytes[0*4..1*4].try_into().expect("len is 4")),
+ u32::from_le_bytes(bytes[1*4..2*4].try_into().expect("len is 4")),
+ u32::from_le_bytes(bytes[2*4..3*4].try_into().expect("len is 4")),
+ u32::from_le_bytes(bytes[3*4..4*4].try_into().expect("len is 4")),
+ )
+ }
+ }
+
+ const BLOCK_SIZE: usize = 64;
+
+ #[derive(Clone,Copy)]
+ struct ChaChaState {
+ a: u32x4,
+ b: u32x4,
+ c: u32x4,
+ d: u32x4
+ }
+
+ #[derive(Copy)]
+ pub struct ChaCha20 {
+ state : ChaChaState,
+ output : [u8; BLOCK_SIZE],
+ offset : usize,
+ }
+
+ impl Clone for ChaCha20 { fn clone(&self) -> ChaCha20 { *self } }
+
+ macro_rules! swizzle {
+ ($b: expr, $c: expr, $d: expr) => {{
+ let u32x4(b10, b11, b12, b13) = $b;
+ $b = u32x4(b11, b12, b13, b10);
+ let u32x4(c10, c11, c12, c13) = $c;
+ $c = u32x4(c12, c13,c10, c11);
+ let u32x4(d10, d11, d12, d13) = $d;
+ $d = u32x4(d13, d10, d11, d12);
+ }}
+ }
+
+ macro_rules! state_to_buffer {
+ ($state: expr, $output: expr) => {{
+ let u32x4(a1, a2, a3, a4) = $state.a;
+ let u32x4(b1, b2, b3, b4) = $state.b;
+ let u32x4(c1, c2, c3, c4) = $state.c;
+ let u32x4(d1, d2, d3, d4) = $state.d;
+ let lens = [
+ a1,a2,a3,a4,
+ b1,b2,b3,b4,
+ c1,c2,c3,c4,
+ d1,d2,d3,d4
+ ];
+ for i in 0..lens.len() {
+ $output[i*4..(i+1)*4].copy_from_slice(&lens[i].to_le_bytes());
+ }
+ }}
+ }
+
+ macro_rules! round{
+ ($state: expr) => {{
+ $state.a = $state.a + $state.b;
+ rotate!($state.d, $state.a, 16);
+ $state.c = $state.c + $state.d;
+ rotate!($state.b, $state.c, 12);
+ $state.a = $state.a + $state.b;
+ rotate!($state.d, $state.a, 8);
+ $state.c = $state.c + $state.d;
+ rotate!($state.b, $state.c, 7);
+ }}
+ }
+
+ macro_rules! rotate {
+ ($a: expr, $b: expr, $rot: expr) => {{
+ let v = $a ^ $b;
+ let r = 32 - $rot;
+ let right = v >> r;
+ $a = (v << $rot) ^ right
+ }}
+ }
+
+ impl ChaCha20 {
+ pub fn new(key: &[u8], nonce: &[u8]) -> ChaCha20 {
+ assert!(key.len() == 16 || key.len() == 32);
+ assert!(nonce.len() == 8 || nonce.len() == 12);
+
+ ChaCha20{ state: ChaCha20::expand(key, nonce), output: [0u8; BLOCK_SIZE], offset: 64 }
+ }
+
+ /// Get one block from a ChaCha stream.
+ pub fn get_single_block(key: &[u8; 32], nonce: &[u8; 16]) -> [u8; 32] {
+ let mut chacha = ChaCha20 { state: ChaCha20::expand(key, nonce), output: [0u8; BLOCK_SIZE], offset: 64 };
+ let mut chacha_bytes = [0; 32];
+ chacha.process_in_place(&mut chacha_bytes);
+ chacha_bytes
+ }
+
+ /// Encrypts `src` into `dest` using a single block from a ChaCha stream. Passing `dest` as
+ /// `src` in a second call will decrypt it.
+ pub fn encrypt_single_block(
+ key: &[u8; 32], nonce: &[u8; 16], dest: &mut [u8], src: &[u8]
+ ) {
+ debug_assert_eq!(dest.len(), src.len());
+ debug_assert!(dest.len() <= 32);
+
+ let block = ChaCha20::get_single_block(key, nonce);
+ for i in 0..dest.len() {
+ dest[i] = block[i] ^ src[i];
+ }
+ }
+
+ /// Same as `encrypt_single_block` only operates on a fixed-size input in-place.
+ pub fn encrypt_single_block_in_place(
+ key: &[u8; 32], nonce: &[u8; 16], bytes: &mut [u8; 32]
+ ) {
+ let block = ChaCha20::get_single_block(key, nonce);
+ for i in 0..bytes.len() {
+ bytes[i] = block[i] ^ bytes[i];
+ }
+ }
+
+ fn expand(key: &[u8], nonce: &[u8]) -> ChaChaState {
+ let constant = match key.len() {
+ 16 => b"expand 16-byte k",
+ 32 => b"expand 32-byte k",
+ _ => unreachable!(),
+ };
+ ChaChaState {
+ a: u32x4::from_bytes(&constant[0..16]),
+ b: u32x4::from_bytes(&key[0..16]),
+ c: if key.len() == 16 {
+ u32x4::from_bytes(&key[0..16])
+ } else {
+ u32x4::from_bytes(&key[16..32])
+ },
+ d: if nonce.len() == 16 {
+ u32x4::from_bytes(&nonce[0..16])
+ } else if nonce.len() == 12 {
+ let mut nonce4 = [0; 4*4];
+ nonce4[4..].copy_from_slice(nonce);
+ u32x4::from_bytes(&nonce4)
+ } else {
+ let mut nonce4 = [0; 4*4];
+ nonce4[8..].copy_from_slice(nonce);
+ u32x4::from_bytes(&nonce4)
+ }
+ }
+ }
+
+ // put the the next BLOCK_SIZE keystream bytes into self.output
+ fn update(&mut self) {
+ let mut state = self.state;
+
+ for _ in 0..10 {
+ round!(state);
+ swizzle!(state.b, state.c, state.d);
+ round!(state);
+ swizzle!(state.d, state.c, state.b);
+ }
+ state.a = state.a + self.state.a;
+ state.b = state.b + self.state.b;
+ state.c = state.c + self.state.c;
+ state.d = state.d + self.state.d;
+
+ state_to_buffer!(state, self.output);
+
+ self.state.d = self.state.d + u32x4(1, 0, 0, 0);
+ let u32x4(c12, _, _, _) = self.state.d;
+ if c12 == 0 {
+ // we could increment the other counter word with an 8 byte nonce
+ // but other implementations like boringssl have this same
+ // limitation
+ panic!("counter is exhausted");
+ }
+
+ self.offset = 0;
+ }
+
+ #[inline] // Useful cause input may be 0s on stack that should be optimized out
+ pub fn process(&mut self, input: &[u8], output: &mut [u8]) {
+ assert!(input.len() == output.len());
+ let len = input.len();
+ let mut i = 0;
+ while i < len {
+ // If there is no keystream available in the output buffer,
+ // generate the next block.
+ if self.offset == BLOCK_SIZE {
+ self.update();
+ }
+
+ // Process the min(available keystream, remaining input length).
+ let count = cmp::min(BLOCK_SIZE - self.offset, len - i);
+ // explicitly assert lengths to avoid bounds checks:
+ assert!(output.len() >= i + count);
+ assert!(input.len() >= i + count);
+ assert!(self.output.len() >= self.offset + count);
+ for j in 0..count {
+ output[i + j] = input[i + j] ^ self.output[self.offset + j];
+ }
+ i += count;
+ self.offset += count;
+ }
+ }
+
+ pub fn process_in_place(&mut self, input_output: &mut [u8]) {
+ let len = input_output.len();
+ let mut i = 0;
+ while i < len {
+ // If there is no keystream available in the output buffer,
+ // generate the next block.
+ if self.offset == BLOCK_SIZE {
+ self.update();
+ }
+
+ // Process the min(available keystream, remaining input length).
+ let count = cmp::min(BLOCK_SIZE - self.offset, len - i);
+ // explicitly assert lengths to avoid bounds checks:
+ assert!(input_output.len() >= i + count);
+ assert!(self.output.len() >= self.offset + count);
+ for j in 0..count {
+ input_output[i + j] ^= self.output[self.offset + j];
+ }
+ i += count;
+ self.offset += count;
+ }
+ }
+
+ #[cfg(test)]
+ pub fn seek_to_block(&mut self, block_offset: u32) {
+ self.state.d.0 = block_offset;
+ self.update();
+ }
+ }
+}
+#[cfg(not(fuzzing))]
+pub use self::real_chacha::ChaCha20;
+
+#[cfg(fuzzing)]
+mod fuzzy_chacha {
+ pub struct ChaCha20 {}
+
+ impl ChaCha20 {
+ pub fn new(key: &[u8], nonce: &[u8]) -> ChaCha20 {
+ assert!(key.len() == 16 || key.len() == 32);
+ assert!(nonce.len() == 8 || nonce.len() == 12);
+ Self {}
+ }
+
+ pub fn get_single_block(_key: &[u8; 32], _nonce: &[u8; 16]) -> [u8; 32] {
+ [0; 32]
+ }
+
+ pub fn encrypt_single_block(
+ _key: &[u8; 32], _nonce: &[u8; 16], dest: &mut [u8], src: &[u8]
+ ) {
+ debug_assert_eq!(dest.len(), src.len());
+ debug_assert!(dest.len() <= 32);
+ }
+
+ pub fn encrypt_single_block_in_place(
+ _key: &[u8; 32], _nonce: &[u8; 16], _bytes: &mut [u8; 32]
+ ) {}
+
+ pub fn process(&mut self, input: &[u8], output: &mut [u8]) {
+ output.copy_from_slice(input);
+ }
+
+ pub fn process_in_place(&mut self, _input_output: &mut [u8]) {}
+ }
+}
+#[cfg(fuzzing)]
+pub use self::fuzzy_chacha::ChaCha20;
+
+#[cfg(test)]
+mod test {
+ use alloc::vec;
+ use alloc::vec::{Vec};
+ use core::convert::TryInto;
+ use core::iter::repeat;
+
+ use super::ChaCha20;
+
+ #[test]
+ fn test_chacha20_256_tls_vectors() {
+ struct TestVector {
+ key: [u8; 32],
+ nonce: [u8; 8],
+ keystream: Vec<u8>,
+ }
+ // taken from http://tools.ietf.org/html/draft-agl-tls-chacha20poly1305-04
+ let test_vectors = vec!(
+ TestVector{
+ key: [
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ ],
+ nonce: [ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ],
+ keystream: vec!(
+ 0x76, 0xb8, 0xe0, 0xad, 0xa0, 0xf1, 0x3d, 0x90,
+ 0x40, 0x5d, 0x6a, 0xe5, 0x53, 0x86, 0xbd, 0x28,
+ 0xbd, 0xd2, 0x19, 0xb8, 0xa0, 0x8d, 0xed, 0x1a,
+ 0xa8, 0x36, 0xef, 0xcc, 0x8b, 0x77, 0x0d, 0xc7,
+ 0xda, 0x41, 0x59, 0x7c, 0x51, 0x57, 0x48, 0x8d,
+ 0x77, 0x24, 0xe0, 0x3f, 0xb8, 0xd8, 0x4a, 0x37,
+ 0x6a, 0x43, 0xb8, 0xf4, 0x15, 0x18, 0xa1, 0x1c,
+ 0xc3, 0x87, 0xb6, 0x69, 0xb2, 0xee, 0x65, 0x86,
+ ),
+ }, TestVector{
+ key: [
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+ ],
+ nonce: [ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ],
+ keystream: vec!(
+ 0x45, 0x40, 0xf0, 0x5a, 0x9f, 0x1f, 0xb2, 0x96,
+ 0xd7, 0x73, 0x6e, 0x7b, 0x20, 0x8e, 0x3c, 0x96,
+ 0xeb, 0x4f, 0xe1, 0x83, 0x46, 0x88, 0xd2, 0x60,
+ 0x4f, 0x45, 0x09, 0x52, 0xed, 0x43, 0x2d, 0x41,
+ 0xbb, 0xe2, 0xa0, 0xb6, 0xea, 0x75, 0x66, 0xd2,
+ 0xa5, 0xd1, 0xe7, 0xe2, 0x0d, 0x42, 0xaf, 0x2c,
+ 0x53, 0xd7, 0x92, 0xb1, 0xc4, 0x3f, 0xea, 0x81,
+ 0x7e, 0x9a, 0xd2, 0x75, 0xae, 0x54, 0x69, 0x63,
+ ),
+ }, TestVector{
+ key: [
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ ],
+ nonce: [ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 ],
+ keystream: vec!(
+ 0xde, 0x9c, 0xba, 0x7b, 0xf3, 0xd6, 0x9e, 0xf5,
+ 0xe7, 0x86, 0xdc, 0x63, 0x97, 0x3f, 0x65, 0x3a,
+ 0x0b, 0x49, 0xe0, 0x15, 0xad, 0xbf, 0xf7, 0x13,
+ 0x4f, 0xcb, 0x7d, 0xf1, 0x37, 0x82, 0x10, 0x31,
+ 0xe8, 0x5a, 0x05, 0x02, 0x78, 0xa7, 0x08, 0x45,
+ 0x27, 0x21, 0x4f, 0x73, 0xef, 0xc7, 0xfa, 0x5b,
+ 0x52, 0x77, 0x06, 0x2e, 0xb7, 0xa0, 0x43, 0x3e,
+ 0x44, 0x5f, 0x41, 0xe3,
+ ),
+ }, TestVector{
+ key: [
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ ],
+ nonce: [ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ],
+ keystream: vec!(
+ 0xef, 0x3f, 0xdf, 0xd6, 0xc6, 0x15, 0x78, 0xfb,
+ 0xf5, 0xcf, 0x35, 0xbd, 0x3d, 0xd3, 0x3b, 0x80,
+ 0x09, 0x63, 0x16, 0x34, 0xd2, 0x1e, 0x42, 0xac,
+ 0x33, 0x96, 0x0b, 0xd1, 0x38, 0xe5, 0x0d, 0x32,
+ 0x11, 0x1e, 0x4c, 0xaf, 0x23, 0x7e, 0xe5, 0x3c,
+ 0xa8, 0xad, 0x64, 0x26, 0x19, 0x4a, 0x88, 0x54,
+ 0x5d, 0xdc, 0x49, 0x7a, 0x0b, 0x46, 0x6e, 0x7d,
+ 0x6b, 0xbd, 0xb0, 0x04, 0x1b, 0x2f, 0x58, 0x6b,
+ ),
+ }, TestVector{
+ key: [
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
+ ],
+ nonce: [ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07 ],
+ keystream: vec!(
+ 0xf7, 0x98, 0xa1, 0x89, 0xf1, 0x95, 0xe6, 0x69,
+ 0x82, 0x10, 0x5f, 0xfb, 0x64, 0x0b, 0xb7, 0x75,
+ 0x7f, 0x57, 0x9d, 0xa3, 0x16, 0x02, 0xfc, 0x93,
+ 0xec, 0x01, 0xac, 0x56, 0xf8, 0x5a, 0xc3, 0xc1,
+ 0x34, 0xa4, 0x54, 0x7b, 0x73, 0x3b, 0x46, 0x41,
+ 0x30, 0x42, 0xc9, 0x44, 0x00, 0x49, 0x17, 0x69,
+ 0x05, 0xd3, 0xbe, 0x59, 0xea, 0x1c, 0x53, 0xf1,
+ 0x59, 0x16, 0x15, 0x5c, 0x2b, 0xe8, 0x24, 0x1a,
+ 0x38, 0x00, 0x8b, 0x9a, 0x26, 0xbc, 0x35, 0x94,
+ 0x1e, 0x24, 0x44, 0x17, 0x7c, 0x8a, 0xde, 0x66,
+ 0x89, 0xde, 0x95, 0x26, 0x49, 0x86, 0xd9, 0x58,
+ 0x89, 0xfb, 0x60, 0xe8, 0x46, 0x29, 0xc9, 0xbd,
+ 0x9a, 0x5a, 0xcb, 0x1c, 0xc1, 0x18, 0xbe, 0x56,
+ 0x3e, 0xb9, 0xb3, 0xa4, 0xa4, 0x72, 0xf8, 0x2e,
+ 0x09, 0xa7, 0xe7, 0x78, 0x49, 0x2b, 0x56, 0x2e,
+ 0xf7, 0x13, 0x0e, 0x88, 0xdf, 0xe0, 0x31, 0xc7,
+ 0x9d, 0xb9, 0xd4, 0xf7, 0xc7, 0xa8, 0x99, 0x15,
+ 0x1b, 0x9a, 0x47, 0x50, 0x32, 0xb6, 0x3f, 0xc3,
+ 0x85, 0x24, 0x5f, 0xe0, 0x54, 0xe3, 0xdd, 0x5a,
+ 0x97, 0xa5, 0xf5, 0x76, 0xfe, 0x06, 0x40, 0x25,
+ 0xd3, 0xce, 0x04, 0x2c, 0x56, 0x6a, 0xb2, 0xc5,
+ 0x07, 0xb1, 0x38, 0xdb, 0x85, 0x3e, 0x3d, 0x69,
+ 0x59, 0x66, 0x09, 0x96, 0x54, 0x6c, 0xc9, 0xc4,
+ 0xa6, 0xea, 0xfd, 0xc7, 0x77, 0xc0, 0x40, 0xd7,
+ 0x0e, 0xaf, 0x46, 0xf7, 0x6d, 0xad, 0x39, 0x79,
+ 0xe5, 0xc5, 0x36, 0x0c, 0x33, 0x17, 0x16, 0x6a,
+ 0x1c, 0x89, 0x4c, 0x94, 0xa3, 0x71, 0x87, 0x6a,
+ 0x94, 0xdf, 0x76, 0x28, 0xfe, 0x4e, 0xaa, 0xf2,
+ 0xcc, 0xb2, 0x7d, 0x5a, 0xaa, 0xe0, 0xad, 0x7a,
+ 0xd0, 0xf9, 0xd4, 0xb6, 0xad, 0x3b, 0x54, 0x09,
+ 0x87, 0x46, 0xd4, 0x52, 0x4d, 0x38, 0x40, 0x7a,
+ 0x6d, 0xeb, 0x3a, 0xb7, 0x8f, 0xab, 0x78, 0xc9,
+ ),
+ },
+ );
+
+ for tv in test_vectors.iter() {
+ let mut c = ChaCha20::new(&tv.key, &tv.nonce);
+ let input: Vec<u8> = repeat(0).take(tv.keystream.len()).collect();
+ let mut output: Vec<u8> = repeat(0).take(input.len()).collect();
+ c.process(&input[..], &mut output[..]);
+ assert_eq!(output, tv.keystream);
+ }
+ }
+
+ #[test]
+ fn test_chacha20_256_tls_vectors_96_nonce() {
+ struct TestVector {
+ key: [u8; 32],
+ nonce: [u8; 12],
+ keystream: Vec<u8>,
+ }
+ // taken from http://tools.ietf.org/html/draft-agl-tls-chacha20poly1305-04
+ let test_vectors = vec!(
+ TestVector{
+ key: [
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ ],
+ nonce: [ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ],
+ keystream: vec!(
+ 0x76, 0xb8, 0xe0, 0xad, 0xa0, 0xf1, 0x3d, 0x90,
+ 0x40, 0x5d, 0x6a, 0xe5, 0x53, 0x86, 0xbd, 0x28,
+ 0xbd, 0xd2, 0x19, 0xb8, 0xa0, 0x8d, 0xed, 0x1a,
+ 0xa8, 0x36, 0xef, 0xcc, 0x8b, 0x77, 0x0d, 0xc7,
+ 0xda, 0x41, 0x59, 0x7c, 0x51, 0x57, 0x48, 0x8d,
+ 0x77, 0x24, 0xe0, 0x3f, 0xb8, 0xd8, 0x4a, 0x37,
+ 0x6a, 0x43, 0xb8, 0xf4, 0x15, 0x18, 0xa1, 0x1c,
+ 0xc3, 0x87, 0xb6, 0x69, 0xb2, 0xee, 0x65, 0x86,
+ ),
+ }, TestVector{
+ key: [
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+ ],
+ nonce: [ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ],
+ keystream: vec!(
+ 0x45, 0x40, 0xf0, 0x5a, 0x9f, 0x1f, 0xb2, 0x96,
+ 0xd7, 0x73, 0x6e, 0x7b, 0x20, 0x8e, 0x3c, 0x96,
+ 0xeb, 0x4f, 0xe1, 0x83, 0x46, 0x88, 0xd2, 0x60,
+ 0x4f, 0x45, 0x09, 0x52, 0xed, 0x43, 0x2d, 0x41,
+ 0xbb, 0xe2, 0xa0, 0xb6, 0xea, 0x75, 0x66, 0xd2,
+ 0xa5, 0xd1, 0xe7, 0xe2, 0x0d, 0x42, 0xaf, 0x2c,
+ 0x53, 0xd7, 0x92, 0xb1, 0xc4, 0x3f, 0xea, 0x81,
+ 0x7e, 0x9a, 0xd2, 0x75, 0xae, 0x54, 0x69, 0x63,
+ ),
+ }, TestVector{
+ key: [
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ ],
+ nonce: [ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 ],
+ keystream: vec!(
+ 0xde, 0x9c, 0xba, 0x7b, 0xf3, 0xd6, 0x9e, 0xf5,
+ 0xe7, 0x86, 0xdc, 0x63, 0x97, 0x3f, 0x65, 0x3a,
+ 0x0b, 0x49, 0xe0, 0x15, 0xad, 0xbf, 0xf7, 0x13,
+ 0x4f, 0xcb, 0x7d, 0xf1, 0x37, 0x82, 0x10, 0x31,
+ 0xe8, 0x5a, 0x05, 0x02, 0x78, 0xa7, 0x08, 0x45,
+ 0x27, 0x21, 0x4f, 0x73, 0xef, 0xc7, 0xfa, 0x5b,
+ 0x52, 0x77, 0x06, 0x2e, 0xb7, 0xa0, 0x43, 0x3e,
+ 0x44, 0x5f, 0x41, 0xe3,
+ ),
+ }, TestVector{
+ key: [
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ ],
+ nonce: [ 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ],
+ keystream: vec!(
+ 0xef, 0x3f, 0xdf, 0xd6, 0xc6, 0x15, 0x78, 0xfb,
+ 0xf5, 0xcf, 0x35, 0xbd, 0x3d, 0xd3, 0x3b, 0x80,
+ 0x09, 0x63, 0x16, 0x34, 0xd2, 0x1e, 0x42, 0xac,
+ 0x33, 0x96, 0x0b, 0xd1, 0x38, 0xe5, 0x0d, 0x32,
+ 0x11, 0x1e, 0x4c, 0xaf, 0x23, 0x7e, 0xe5, 0x3c,
+ 0xa8, 0xad, 0x64, 0x26, 0x19, 0x4a, 0x88, 0x54,
+ 0x5d, 0xdc, 0x49, 0x7a, 0x0b, 0x46, 0x6e, 0x7d,
+ 0x6b, 0xbd, 0xb0, 0x04, 0x1b, 0x2f, 0x58, 0x6b,
+ ),
+ }, TestVector{
+ key: [
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
+ ],
+ nonce: [0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07 ],
+ keystream: vec!(
+ 0xf7, 0x98, 0xa1, 0x89, 0xf1, 0x95, 0xe6, 0x69,
+ 0x82, 0x10, 0x5f, 0xfb, 0x64, 0x0b, 0xb7, 0x75,
+ 0x7f, 0x57, 0x9d, 0xa3, 0x16, 0x02, 0xfc, 0x93,
+ 0xec, 0x01, 0xac, 0x56, 0xf8, 0x5a, 0xc3, 0xc1,
+ 0x34, 0xa4, 0x54, 0x7b, 0x73, 0x3b, 0x46, 0x41,
+ 0x30, 0x42, 0xc9, 0x44, 0x00, 0x49, 0x17, 0x69,
+ 0x05, 0xd3, 0xbe, 0x59, 0xea, 0x1c, 0x53, 0xf1,
+ 0x59, 0x16, 0x15, 0x5c, 0x2b, 0xe8, 0x24, 0x1a,
+ 0x38, 0x00, 0x8b, 0x9a, 0x26, 0xbc, 0x35, 0x94,
+ 0x1e, 0x24, 0x44, 0x17, 0x7c, 0x8a, 0xde, 0x66,
+ 0x89, 0xde, 0x95, 0x26, 0x49, 0x86, 0xd9, 0x58,
+ 0x89, 0xfb, 0x60, 0xe8, 0x46, 0x29, 0xc9, 0xbd,
+ 0x9a, 0x5a, 0xcb, 0x1c, 0xc1, 0x18, 0xbe, 0x56,
+ 0x3e, 0xb9, 0xb3, 0xa4, 0xa4, 0x72, 0xf8, 0x2e,
+ 0x09, 0xa7, 0xe7, 0x78, 0x49, 0x2b, 0x56, 0x2e,
+ 0xf7, 0x13, 0x0e, 0x88, 0xdf, 0xe0, 0x31, 0xc7,
+ 0x9d, 0xb9, 0xd4, 0xf7, 0xc7, 0xa8, 0x99, 0x15,
+ 0x1b, 0x9a, 0x47, 0x50, 0x32, 0xb6, 0x3f, 0xc3,
+ 0x85, 0x24, 0x5f, 0xe0, 0x54, 0xe3, 0xdd, 0x5a,
+ 0x97, 0xa5, 0xf5, 0x76, 0xfe, 0x06, 0x40, 0x25,
+ 0xd3, 0xce, 0x04, 0x2c, 0x56, 0x6a, 0xb2, 0xc5,
+ 0x07, 0xb1, 0x38, 0xdb, 0x85, 0x3e, 0x3d, 0x69,
+ 0x59, 0x66, 0x09, 0x96, 0x54, 0x6c, 0xc9, 0xc4,
+ 0xa6, 0xea, 0xfd, 0xc7, 0x77, 0xc0, 0x40, 0xd7,
+ 0x0e, 0xaf, 0x46, 0xf7, 0x6d, 0xad, 0x39, 0x79,
+ 0xe5, 0xc5, 0x36, 0x0c, 0x33, 0x17, 0x16, 0x6a,
+ 0x1c, 0x89, 0x4c, 0x94, 0xa3, 0x71, 0x87, 0x6a,
+ 0x94, 0xdf, 0x76, 0x28, 0xfe, 0x4e, 0xaa, 0xf2,
+ 0xcc, 0xb2, 0x7d, 0x5a, 0xaa, 0xe0, 0xad, 0x7a,
+ 0xd0, 0xf9, 0xd4, 0xb6, 0xad, 0x3b, 0x54, 0x09,
+ 0x87, 0x46, 0xd4, 0x52, 0x4d, 0x38, 0x40, 0x7a,
+ 0x6d, 0xeb, 0x3a, 0xb7, 0x8f, 0xab, 0x78, 0xc9,
+ ),
+ },
+ );
+
+ for tv in test_vectors.iter() {
+ let mut c = ChaCha20::new(&tv.key, &tv.nonce);
+ let input: Vec<u8> = repeat(0).take(tv.keystream.len()).collect();
+ let mut output: Vec<u8> = repeat(0).take(input.len()).collect();
+ c.process(&input[..], &mut output[..]);
+ assert_eq!(output, tv.keystream);
+ }
+ }
+
+ #[test]
+ fn get_single_block() {
+ // Test that `get_single_block` (which takes a 16-byte nonce) is equivalent to getting a block
+ // using a 12-byte nonce, with the block starting at the counter offset given by the remaining 4
+ // bytes.
+ let key = [
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
+ ];
+ let nonce_16bytes = [
+ 0x00, 0x01, 0x02, 0x03, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b
+ ];
+ let counter_pos = &nonce_16bytes[..4];
+ let nonce_12bytes = &nonce_16bytes[4..];
+
+ // Initialize a ChaCha20 instance with its counter starting at 0.
+ let mut chacha20 = ChaCha20::new(&key, nonce_12bytes);
+ // Seek its counter to the block at counter_pos.
+ chacha20.seek_to_block(u32::from_le_bytes(counter_pos.try_into().unwrap()));
+ let mut block_bytes = [0; 32];
+ chacha20.process_in_place(&mut block_bytes);
+
+ assert_eq!(ChaCha20::get_single_block(&key, &nonce_16bytes), block_bytes);
+ }
+
+ #[test]
+ fn encrypt_single_block() {
+ let key = [
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
+ ];
+ let nonce = [
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
+ 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ ];
+ let bytes = [1; 32];
+
+ let mut encrypted_bytes = [0; 32];
+ ChaCha20::encrypt_single_block(&key, &nonce, &mut encrypted_bytes, &bytes);
+
+ let mut decrypted_bytes = [0; 32];
+ ChaCha20::encrypt_single_block(&key, &nonce, &mut decrypted_bytes, &encrypted_bytes);
+
+ assert_eq!(bytes, decrypted_bytes);
+ }
+
+ #[test]
+ fn encrypt_single_block_in_place() {
+ let key = [
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
+ ];
+ let nonce = [
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
+ 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ ];
+ let unencrypted_bytes = [1; 32];
+ let mut bytes = unencrypted_bytes;
+
+ ChaCha20::encrypt_single_block_in_place(&key, &nonce, &mut bytes);
+ assert_ne!(bytes, unencrypted_bytes);
+
+ ChaCha20::encrypt_single_block_in_place(&key, &nonce, &mut bytes);
+ assert_eq!(bytes, unencrypted_bytes);
+ }
+}
--- /dev/null
+// ring has a garbage API so its use is avoided, but rust-crypto doesn't have RFC-variant poly1305
+// Instead, we steal rust-crypto's implementation and tweak it to match the RFC.
+//
+// This file is licensed under the Apache License, Version 2.0 <LICENSE-APACHE
+// or http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your option.
+// You may not use this file except in accordance with one or both of these
+// licenses.
+//
+// This is a port of Andrew Moons poly1305-donna
+// https://github.com/floodyberry/poly1305-donna
+
+#[cfg(not(fuzzing))]
+mod real_chachapoly {
+ use super::super::chacha20::ChaCha20;
+ use super::super::poly1305::Poly1305;
+ use super::super::fixed_time_eq;
+
+ #[derive(Clone, Copy)]
+ pub struct ChaCha20Poly1305RFC {
+ cipher: ChaCha20,
+ mac: Poly1305,
+ finished: bool,
+ data_len: usize,
+ aad_len: u64,
+ }
+
+ impl ChaCha20Poly1305RFC {
+ #[inline]
+ fn pad_mac_16(mac: &mut Poly1305, len: usize) {
+ if len % 16 != 0 {
+ mac.input(&[0; 16][0..16 - (len % 16)]);
+ }
+ }
+ pub fn new(key: &[u8], nonce: &[u8], aad: &[u8]) -> ChaCha20Poly1305RFC {
+ assert!(key.len() == 16 || key.len() == 32);
+ assert!(nonce.len() == 12);
+
+ // Ehh, I'm too lazy to *also* tweak ChaCha20 to make it RFC-compliant
+ assert!(nonce[0] == 0 && nonce[1] == 0 && nonce[2] == 0 && nonce[3] == 0);
+
+ let mut cipher = ChaCha20::new(key, &nonce[4..]);
+ let mut mac_key = [0u8; 64];
+ let zero_key = [0u8; 64];
+ cipher.process(&zero_key, &mut mac_key);
+
+ let mut mac = Poly1305::new(&mac_key[..32]);
+ mac.input(aad);
+ ChaCha20Poly1305RFC::pad_mac_16(&mut mac, aad.len());
+
+ ChaCha20Poly1305RFC {
+ cipher,
+ mac,
+ finished: false,
+ data_len: 0,
+ aad_len: aad.len() as u64,
+ }
+ }
+
+ pub fn encrypt(&mut self, input: &[u8], output: &mut [u8], out_tag: &mut [u8]) {
+ assert!(input.len() == output.len());
+ assert!(self.finished == false);
+ self.cipher.process(input, output);
+ self.data_len += input.len();
+ self.mac.input(output);
+ ChaCha20Poly1305RFC::pad_mac_16(&mut self.mac, self.data_len);
+ self.finished = true;
+ self.mac.input(&self.aad_len.to_le_bytes());
+ self.mac.input(&(self.data_len as u64).to_le_bytes());
+ self.mac.raw_result(out_tag);
+ }
+
+ pub fn encrypt_full_message_in_place(&mut self, input_output: &mut [u8], out_tag: &mut [u8]) {
+ self.encrypt_in_place(input_output);
+ self.finish_and_get_tag(out_tag);
+ }
+
+ // Encrypt `input_output` in-place. To finish and calculate the tag, use `finish_and_get_tag`
+ // below.
+ pub(in super::super) fn encrypt_in_place(&mut self, input_output: &mut [u8]) {
+ debug_assert!(self.finished == false);
+ self.cipher.process_in_place(input_output);
+ self.data_len += input_output.len();
+ self.mac.input(input_output);
+ }
+
+ // If we were previously encrypting with `encrypt_in_place`, this method can be used to finish
+ // encrypting and calculate the tag.
+ pub(in super::super) fn finish_and_get_tag(&mut self, out_tag: &mut [u8]) {
+ debug_assert!(self.finished == false);
+ ChaCha20Poly1305RFC::pad_mac_16(&mut self.mac, self.data_len);
+ self.finished = true;
+ self.mac.input(&self.aad_len.to_le_bytes());
+ self.mac.input(&(self.data_len as u64).to_le_bytes());
+ self.mac.raw_result(out_tag);
+ }
+
+ /// Decrypt the `input`, checking the given `tag` prior to writing the decrypted contents
+ /// into `output`. Note that, because `output` is not touched until the `tag` is checked,
+ /// this decryption is *variable time*.
+ pub fn variable_time_decrypt(&mut self, input: &[u8], output: &mut [u8], tag: &[u8]) -> Result<(), ()> {
+ assert!(input.len() == output.len());
+ assert!(self.finished == false);
+
+ self.finished = true;
+
+ self.mac.input(input);
+
+ self.data_len += input.len();
+ ChaCha20Poly1305RFC::pad_mac_16(&mut self.mac, self.data_len);
+ self.mac.input(&self.aad_len.to_le_bytes());
+ self.mac.input(&(self.data_len as u64).to_le_bytes());
+
+ let mut calc_tag = [0u8; 16];
+ self.mac.raw_result(&mut calc_tag);
+ if fixed_time_eq(&calc_tag, tag) {
+ self.cipher.process(input, output);
+ Ok(())
+ } else {
+ Err(())
+ }
+ }
+
+ pub fn check_decrypt_in_place(&mut self, input_output: &mut [u8], tag: &[u8]) -> Result<(), ()> {
+ self.decrypt_in_place(input_output);
+ if self.finish_and_check_tag(tag) { Ok(()) } else { Err(()) }
+ }
+
+ /// Decrypt in place, without checking the tag. Use `finish_and_check_tag` to check it
+ /// later when decryption finishes.
+ ///
+ /// Should never be `pub` because the public API should always enforce tag checking.
+ pub(in super::super) fn decrypt_in_place(&mut self, input_output: &mut [u8]) {
+ debug_assert!(self.finished == false);
+ self.mac.input(input_output);
+ self.data_len += input_output.len();
+ self.cipher.process_in_place(input_output);
+ }
+
+ /// If we were previously decrypting with `just_decrypt_in_place`, this method must be used
+ /// to check the tag. Returns whether or not the tag is valid.
+ pub(in super::super) fn finish_and_check_tag(&mut self, tag: &[u8]) -> bool {
+ debug_assert!(self.finished == false);
+ self.finished = true;
+ ChaCha20Poly1305RFC::pad_mac_16(&mut self.mac, self.data_len);
+ self.mac.input(&self.aad_len.to_le_bytes());
+ self.mac.input(&(self.data_len as u64).to_le_bytes());
+
+ let mut calc_tag = [0u8; 16];
+ self.mac.raw_result(&mut calc_tag);
+ if fixed_time_eq(&calc_tag, tag) {
+ true
+ } else {
+ false
+ }
+ }
+ }
+}
+#[cfg(not(fuzzing))]
+pub use self::real_chachapoly::ChaCha20Poly1305RFC;
+
+#[cfg(fuzzing)]
+mod fuzzy_chachapoly {
+ #[derive(Clone, Copy)]
+ pub struct ChaCha20Poly1305RFC {
+ tag: [u8; 16],
+ finished: bool,
+ }
+ impl ChaCha20Poly1305RFC {
+ pub fn new(key: &[u8], nonce: &[u8], _aad: &[u8]) -> ChaCha20Poly1305RFC {
+ assert!(key.len() == 16 || key.len() == 32);
+ assert!(nonce.len() == 12);
+
+ // Ehh, I'm too lazy to *also* tweak ChaCha20 to make it RFC-compliant
+ assert!(nonce[0] == 0 && nonce[1] == 0 && nonce[2] == 0 && nonce[3] == 0);
+
+ let mut tag = [0; 16];
+ tag.copy_from_slice(&key[0..16]);
+
+ ChaCha20Poly1305RFC {
+ tag,
+ finished: false,
+ }
+ }
+
+ pub fn encrypt(&mut self, input: &[u8], output: &mut [u8], out_tag: &mut [u8]) {
+ assert!(input.len() == output.len());
+ assert!(self.finished == false);
+
+ output.copy_from_slice(&input);
+ out_tag.copy_from_slice(&self.tag);
+ self.finished = true;
+ }
+
+ pub fn encrypt_full_message_in_place(&mut self, input_output: &mut [u8], out_tag: &mut [u8]) {
+ self.encrypt_in_place(input_output);
+ self.finish_and_get_tag(out_tag);
+ }
+
+ pub(in super::super) fn encrypt_in_place(&mut self, _input_output: &mut [u8]) {
+ assert!(self.finished == false);
+ }
+
+ pub(in super::super) fn finish_and_get_tag(&mut self, out_tag: &mut [u8]) {
+ assert!(self.finished == false);
+ out_tag.copy_from_slice(&self.tag);
+ self.finished = true;
+ }
+
+ pub fn variable_time_decrypt(&mut self, input: &[u8], output: &mut [u8], tag: &[u8]) -> Result<(), ()> {
+ assert!(input.len() == output.len());
+ assert!(self.finished == false);
+
+ if tag[..] != self.tag[..] { return Err(()); }
+ output.copy_from_slice(input);
+ self.finished = true;
+ Ok(())
+ }
+
+ pub fn check_decrypt_in_place(&mut self, input_output: &mut [u8], tag: &[u8]) -> Result<(), ()> {
+ self.decrypt_in_place(input_output);
+ if self.finish_and_check_tag(tag) { Ok(()) } else { Err(()) }
+ }
+
+ pub(in super::super) fn decrypt_in_place(&mut self, _input: &mut [u8]) {
+ assert!(self.finished == false);
+ }
+
+ pub(in super::super) fn finish_and_check_tag(&mut self, tag: &[u8]) -> bool {
+ if tag[..] != self.tag[..] { return false; }
+ self.finished = true;
+ true
+ }
+ }
+}
+#[cfg(fuzzing)]
+pub use self::fuzzy_chachapoly::ChaCha20Poly1305RFC;
--- /dev/null
+use bitcoin::hashes::cmp::fixed_time_eq;
+
+pub(crate) mod chacha20;
+#[cfg(not(fuzzing))]
+pub(crate) mod poly1305;
+pub(crate) mod chacha20poly1305rfc;
+pub(crate) mod streams;
+pub(crate) mod utils;
--- /dev/null
+// This file is licensed under the Apache License, Version 2.0 <LICENSE-APACHE
+// or http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your option.
+// You may not use this file except in accordance with one or both of these
+// licenses.
+
+// This is a port of Andrew Moons poly1305-donna
+// https://github.com/floodyberry/poly1305-donna
+
+use core::cmp::min;
+use core::convert::TryInto;
+
+#[derive(Clone, Copy)]
+pub struct Poly1305 {
+ r : [u32; 5],
+ h : [u32; 5],
+ pad : [u32; 4],
+ leftover : usize,
+ buffer : [u8; 16],
+ finalized : bool,
+}
+
+impl Poly1305 {
+ pub fn new(key: &[u8]) -> Poly1305 {
+ assert!(key.len() == 32);
+ let mut poly = Poly1305{ r: [0u32; 5], h: [0u32; 5], pad: [0u32; 4], leftover: 0, buffer: [0u8; 16], finalized: false };
+
+ // r &= 0xffffffc0ffffffc0ffffffc0fffffff
+ poly.r[0] = (u32::from_le_bytes(key[ 0.. 4].try_into().expect("len is 4")) ) & 0x3ffffff;
+ poly.r[1] = (u32::from_le_bytes(key[ 3.. 7].try_into().expect("len is 4")) >> 2) & 0x3ffff03;
+ poly.r[2] = (u32::from_le_bytes(key[ 6..10].try_into().expect("len is 4")) >> 4) & 0x3ffc0ff;
+ poly.r[3] = (u32::from_le_bytes(key[ 9..13].try_into().expect("len is 4")) >> 6) & 0x3f03fff;
+ poly.r[4] = (u32::from_le_bytes(key[12..16].try_into().expect("len is 4")) >> 8) & 0x00fffff;
+
+ poly.pad[0] = u32::from_le_bytes(key[16..20].try_into().expect("len is 4"));
+ poly.pad[1] = u32::from_le_bytes(key[20..24].try_into().expect("len is 4"));
+ poly.pad[2] = u32::from_le_bytes(key[24..28].try_into().expect("len is 4"));
+ poly.pad[3] = u32::from_le_bytes(key[28..32].try_into().expect("len is 4"));
+
+ poly
+ }
+
+ fn block(&mut self, m: &[u8]) {
+ let hibit : u32 = if self.finalized { 0 } else { 1 << 24 };
+
+ let r0 = self.r[0];
+ let r1 = self.r[1];
+ let r2 = self.r[2];
+ let r3 = self.r[3];
+ let r4 = self.r[4];
+
+ let s1 = r1 * 5;
+ let s2 = r2 * 5;
+ let s3 = r3 * 5;
+ let s4 = r4 * 5;
+
+ let mut h0 = self.h[0];
+ let mut h1 = self.h[1];
+ let mut h2 = self.h[2];
+ let mut h3 = self.h[3];
+ let mut h4 = self.h[4];
+
+ // h += m
+ h0 += (u32::from_le_bytes(m[ 0.. 4].try_into().expect("len is 4")) ) & 0x3ffffff;
+ h1 += (u32::from_le_bytes(m[ 3.. 7].try_into().expect("len is 4")) >> 2) & 0x3ffffff;
+ h2 += (u32::from_le_bytes(m[ 6..10].try_into().expect("len is 4")) >> 4) & 0x3ffffff;
+ h3 += (u32::from_le_bytes(m[ 9..13].try_into().expect("len is 4")) >> 6) & 0x3ffffff;
+ h4 += (u32::from_le_bytes(m[12..16].try_into().expect("len is 4")) >> 8) | hibit;
+
+ // h *= r
+ let d0 = (h0 as u64 * r0 as u64) + (h1 as u64 * s4 as u64) + (h2 as u64 * s3 as u64) + (h3 as u64 * s2 as u64) + (h4 as u64 * s1 as u64);
+ let mut d1 = (h0 as u64 * r1 as u64) + (h1 as u64 * r0 as u64) + (h2 as u64 * s4 as u64) + (h3 as u64 * s3 as u64) + (h4 as u64 * s2 as u64);
+ let mut d2 = (h0 as u64 * r2 as u64) + (h1 as u64 * r1 as u64) + (h2 as u64 * r0 as u64) + (h3 as u64 * s4 as u64) + (h4 as u64 * s3 as u64);
+ let mut d3 = (h0 as u64 * r3 as u64) + (h1 as u64 * r2 as u64) + (h2 as u64 * r1 as u64) + (h3 as u64 * r0 as u64) + (h4 as u64 * s4 as u64);
+ let mut d4 = (h0 as u64 * r4 as u64) + (h1 as u64 * r3 as u64) + (h2 as u64 * r2 as u64) + (h3 as u64 * r1 as u64) + (h4 as u64 * r0 as u64);
+
+ // (partial) h %= p
+ let mut c : u32;
+ c = (d0 >> 26) as u32; h0 = d0 as u32 & 0x3ffffff;
+ d1 += c as u64; c = (d1 >> 26) as u32; h1 = d1 as u32 & 0x3ffffff;
+ d2 += c as u64; c = (d2 >> 26) as u32; h2 = d2 as u32 & 0x3ffffff;
+ d3 += c as u64; c = (d3 >> 26) as u32; h3 = d3 as u32 & 0x3ffffff;
+ d4 += c as u64; c = (d4 >> 26) as u32; h4 = d4 as u32 & 0x3ffffff;
+ h0 += c * 5; c = h0 >> 26; h0 = h0 & 0x3ffffff;
+ h1 += c;
+
+ self.h[0] = h0;
+ self.h[1] = h1;
+ self.h[2] = h2;
+ self.h[3] = h3;
+ self.h[4] = h4;
+ }
+
+ pub fn finish(&mut self) {
+ if self.leftover > 0 {
+ self.buffer[self.leftover] = 1;
+ for i in self.leftover+1..16 {
+ self.buffer[i] = 0;
+ }
+ self.finalized = true;
+ let tmp = self.buffer;
+ self.block(&tmp);
+ }
+
+ // fully carry h
+ let mut h0 = self.h[0];
+ let mut h1 = self.h[1];
+ let mut h2 = self.h[2];
+ let mut h3 = self.h[3];
+ let mut h4 = self.h[4];
+
+ let mut c : u32;
+ c = h1 >> 26; h1 = h1 & 0x3ffffff;
+ h2 += c; c = h2 >> 26; h2 = h2 & 0x3ffffff;
+ h3 += c; c = h3 >> 26; h3 = h3 & 0x3ffffff;
+ h4 += c; c = h4 >> 26; h4 = h4 & 0x3ffffff;
+ h0 += c * 5; c = h0 >> 26; h0 = h0 & 0x3ffffff;
+ h1 += c;
+
+ // compute h + -p
+ let mut g0 = h0.wrapping_add(5); c = g0 >> 26; g0 &= 0x3ffffff;
+ let mut g1 = h1.wrapping_add(c); c = g1 >> 26; g1 &= 0x3ffffff;
+ let mut g2 = h2.wrapping_add(c); c = g2 >> 26; g2 &= 0x3ffffff;
+ let mut g3 = h3.wrapping_add(c); c = g3 >> 26; g3 &= 0x3ffffff;
+ let mut g4 = h4.wrapping_add(c).wrapping_sub(1 << 26);
+
+ // select h if h < p, or h + -p if h >= p
+ let mut mask = (g4 >> (32 - 1)).wrapping_sub(1);
+ g0 &= mask;
+ g1 &= mask;
+ g2 &= mask;
+ g3 &= mask;
+ g4 &= mask;
+ mask = !mask;
+ h0 = (h0 & mask) | g0;
+ h1 = (h1 & mask) | g1;
+ h2 = (h2 & mask) | g2;
+ h3 = (h3 & mask) | g3;
+ h4 = (h4 & mask) | g4;
+
+ // h = h % (2^128)
+ h0 = ((h0 ) | (h1 << 26)) & 0xffffffff;
+ h1 = ((h1 >> 6) | (h2 << 20)) & 0xffffffff;
+ h2 = ((h2 >> 12) | (h3 << 14)) & 0xffffffff;
+ h3 = ((h3 >> 18) | (h4 << 8)) & 0xffffffff;
+
+ // h = mac = (h + pad) % (2^128)
+ let mut f : u64;
+ f = h0 as u64 + self.pad[0] as u64 ; h0 = f as u32;
+ f = h1 as u64 + self.pad[1] as u64 + (f >> 32); h1 = f as u32;
+ f = h2 as u64 + self.pad[2] as u64 + (f >> 32); h2 = f as u32;
+ f = h3 as u64 + self.pad[3] as u64 + (f >> 32); h3 = f as u32;
+
+ self.h[0] = h0;
+ self.h[1] = h1;
+ self.h[2] = h2;
+ self.h[3] = h3;
+ }
+
+ pub fn input(&mut self, data: &[u8]) {
+ assert!(!self.finalized);
+ let mut m = data;
+
+ if self.leftover > 0 {
+ let want = min(16 - self.leftover, m.len());
+ for i in 0..want {
+ self.buffer[self.leftover+i] = m[i];
+ }
+ m = &m[want..];
+ self.leftover += want;
+
+ if self.leftover < 16 {
+ return;
+ }
+
+ // self.block(self.buffer[..]);
+ let tmp = self.buffer;
+ self.block(&tmp);
+
+ self.leftover = 0;
+ }
+
+ while m.len() >= 16 {
+ self.block(&m[0..16]);
+ m = &m[16..];
+ }
+
+ for i in 0..m.len() {
+ self.buffer[i] = m[i];
+ }
+ self.leftover = m.len();
+ }
+
+ pub fn raw_result(&mut self, output: &mut [u8]) {
+ assert!(output.len() >= 16);
+ if !self.finalized{
+ self.finish();
+ }
+ output[0..4].copy_from_slice(&self.h[0].to_le_bytes());
+ output[4..8].copy_from_slice(&self.h[1].to_le_bytes());
+ output[8..12].copy_from_slice(&self.h[2].to_le_bytes());
+ output[12..16].copy_from_slice(&self.h[3].to_le_bytes());
+ }
+}
+
+#[cfg(test)]
+mod test {
+ use core::iter::repeat;
+ use alloc::vec::Vec;
+
+ use super::Poly1305;
+
+ fn poly1305(key: &[u8], msg: &[u8], mac: &mut [u8]) {
+ let mut poly = Poly1305::new(key);
+ poly.input(msg);
+ poly.raw_result(mac);
+ }
+
+ #[test]
+ fn test_nacl_vector() {
+ let key = [
+ 0xee,0xa6,0xa7,0x25,0x1c,0x1e,0x72,0x91,
+ 0x6d,0x11,0xc2,0xcb,0x21,0x4d,0x3c,0x25,
+ 0x25,0x39,0x12,0x1d,0x8e,0x23,0x4e,0x65,
+ 0x2d,0x65,0x1f,0xa4,0xc8,0xcf,0xf8,0x80,
+ ];
+
+ let msg = [
+ 0x8e,0x99,0x3b,0x9f,0x48,0x68,0x12,0x73,
+ 0xc2,0x96,0x50,0xba,0x32,0xfc,0x76,0xce,
+ 0x48,0x33,0x2e,0xa7,0x16,0x4d,0x96,0xa4,
+ 0x47,0x6f,0xb8,0xc5,0x31,0xa1,0x18,0x6a,
+ 0xc0,0xdf,0xc1,0x7c,0x98,0xdc,0xe8,0x7b,
+ 0x4d,0xa7,0xf0,0x11,0xec,0x48,0xc9,0x72,
+ 0x71,0xd2,0xc2,0x0f,0x9b,0x92,0x8f,0xe2,
+ 0x27,0x0d,0x6f,0xb8,0x63,0xd5,0x17,0x38,
+ 0xb4,0x8e,0xee,0xe3,0x14,0xa7,0xcc,0x8a,
+ 0xb9,0x32,0x16,0x45,0x48,0xe5,0x26,0xae,
+ 0x90,0x22,0x43,0x68,0x51,0x7a,0xcf,0xea,
+ 0xbd,0x6b,0xb3,0x73,0x2b,0xc0,0xe9,0xda,
+ 0x99,0x83,0x2b,0x61,0xca,0x01,0xb6,0xde,
+ 0x56,0x24,0x4a,0x9e,0x88,0xd5,0xf9,0xb3,
+ 0x79,0x73,0xf6,0x22,0xa4,0x3d,0x14,0xa6,
+ 0x59,0x9b,0x1f,0x65,0x4c,0xb4,0x5a,0x74,
+ 0xe3,0x55,0xa5,
+ ];
+
+ let expected = [
+ 0xf3,0xff,0xc7,0x70,0x3f,0x94,0x00,0xe5,
+ 0x2a,0x7d,0xfb,0x4b,0x3d,0x33,0x05,0xd9,
+ ];
+
+ let mut mac = [0u8; 16];
+ poly1305(&key, &msg, &mut mac);
+ assert_eq!(&mac[..], &expected[..]);
+
+ let mut poly = Poly1305::new(&key);
+ poly.input(&msg[0..32]);
+ poly.input(&msg[32..96]);
+ poly.input(&msg[96..112]);
+ poly.input(&msg[112..120]);
+ poly.input(&msg[120..124]);
+ poly.input(&msg[124..126]);
+ poly.input(&msg[126..127]);
+ poly.input(&msg[127..128]);
+ poly.input(&msg[128..129]);
+ poly.input(&msg[129..130]);
+ poly.input(&msg[130..131]);
+ poly.raw_result(&mut mac);
+ assert_eq!(&mac[..], &expected[..]);
+ }
+
+ #[test]
+ fn donna_self_test() {
+ let wrap_key = [
+ 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ ];
+
+ let wrap_msg = [
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ ];
+
+ let wrap_mac = [
+ 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ ];
+
+ let mut mac = [0u8; 16];
+ poly1305(&wrap_key, &wrap_msg, &mut mac);
+ assert_eq!(&mac[..], &wrap_mac[..]);
+
+ let total_key = [
+ 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0xff,
+ 0xfe, 0xfd, 0xfc, 0xfb, 0xfa, 0xf9, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00,
+ ];
+
+ let total_mac = [
+ 0x64, 0xaf, 0xe2, 0xe8, 0xd6, 0xad, 0x7b, 0xbd,
+ 0xd2, 0x87, 0xf9, 0x7c, 0x44, 0x62, 0x3d, 0x39,
+ ];
+
+ let mut tpoly = Poly1305::new(&total_key);
+ for i in 0..256 {
+ let key: Vec<u8> = repeat(i as u8).take(32).collect();
+ let msg: Vec<u8> = repeat(i as u8).take(256).collect();
+ let mut mac = [0u8; 16];
+ poly1305(&key[..], &msg[0..i], &mut mac);
+ tpoly.input(&mac);
+ }
+ tpoly.raw_result(&mut mac);
+ assert_eq!(&mac[..], &total_mac[..]);
+ }
+
+ #[test]
+ fn test_tls_vectors() {
+ // from http://tools.ietf.org/html/draft-agl-tls-chacha20poly1305-04
+ let key = b"this is 32-byte key for Poly1305";
+ let msg = [0u8; 32];
+ let expected = [
+ 0x49, 0xec, 0x78, 0x09, 0x0e, 0x48, 0x1e, 0xc6,
+ 0xc2, 0x6b, 0x33, 0xb9, 0x1c, 0xcc, 0x03, 0x07,
+ ];
+ let mut mac = [0u8; 16];
+ poly1305(key, &msg, &mut mac);
+ assert_eq!(&mac[..], &expected[..]);
+
+ let msg = b"Hello world!";
+ let expected= [
+ 0xa6, 0xf7, 0x45, 0x00, 0x8f, 0x81, 0xc9, 0x16,
+ 0xa2, 0x0d, 0xcc, 0x74, 0xee, 0xf2, 0xb2, 0xf0,
+ ];
+ poly1305(key, msg, &mut mac);
+ assert_eq!(&mac[..], &expected[..]);
+ }
+}
--- /dev/null
+use crate::crypto::chacha20::ChaCha20;
+use crate::crypto::chacha20poly1305rfc::ChaCha20Poly1305RFC;
+
+use crate::ln::msgs::DecodeError;
+use crate::util::ser::{FixedLengthReader, LengthRead, LengthReadableArgs, Readable, Writeable, Writer};
+use crate::io::{self, Read, Write};
+
+pub(crate) struct ChaChaReader<'a, R: io::Read> {
+ pub chacha: &'a mut ChaCha20,
+ pub read: R,
+}
+impl<'a, R: io::Read> io::Read for ChaChaReader<'a, R> {
+ fn read(&mut self, dest: &mut [u8]) -> Result<usize, io::Error> {
+ let res = self.read.read(dest)?;
+ if res > 0 {
+ self.chacha.process_in_place(&mut dest[0..res]);
+ }
+ Ok(res)
+ }
+}
+
+/// Enables the use of the serialization macros for objects that need to be simultaneously encrypted and
+/// serialized. This allows us to avoid an intermediate Vec allocation.
+pub(crate) struct ChaChaPolyWriteAdapter<'a, W: Writeable> {
+ pub rho: [u8; 32],
+ pub writeable: &'a W,
+}
+
+impl<'a, W: Writeable> ChaChaPolyWriteAdapter<'a, W> {
+ #[allow(unused)] // This will be used for onion messages soon
+ pub fn new(rho: [u8; 32], writeable: &'a W) -> ChaChaPolyWriteAdapter<'a, W> {
+ Self { rho, writeable }
+ }
+}
+
+impl<'a, T: Writeable> Writeable for ChaChaPolyWriteAdapter<'a, T> {
+ // Simultaneously write and encrypt Self::writeable.
+ fn write<W: Writer>(&self, w: &mut W) -> Result<(), io::Error> {
+ let mut chacha = ChaCha20Poly1305RFC::new(&self.rho, &[0; 12], &[]);
+ let mut chacha_stream = ChaChaPolyWriter { chacha: &mut chacha, write: w };
+ self.writeable.write(&mut chacha_stream)?;
+ let mut tag = [0 as u8; 16];
+ chacha.finish_and_get_tag(&mut tag);
+ tag.write(w)?;
+
+ Ok(())
+ }
+}
+
+/// Enables the use of the serialization macros for objects that need to be simultaneously decrypted and
+/// deserialized. This allows us to avoid an intermediate Vec allocation.
+pub(crate) struct ChaChaPolyReadAdapter<R: Readable> {
+ pub readable: R,
+}
+
+impl<T: Readable> LengthReadableArgs<[u8; 32]> for ChaChaPolyReadAdapter<T> {
+ // Simultaneously read and decrypt an object from a LengthRead, storing it in Self::readable.
+ // LengthRead must be used instead of std::io::Read because we need the total length to separate
+ // out the tag at the end.
+ fn read<R: LengthRead>(mut r: &mut R, secret: [u8; 32]) -> Result<Self, DecodeError> {
+ if r.total_bytes() < 16 { return Err(DecodeError::InvalidValue) }
+
+ let mut chacha = ChaCha20Poly1305RFC::new(&secret, &[0; 12], &[]);
+ let decrypted_len = r.total_bytes() - 16;
+ let s = FixedLengthReader::new(&mut r, decrypted_len);
+ let mut chacha_stream = ChaChaPolyReader { chacha: &mut chacha, read: s };
+ let readable: T = Readable::read(&mut chacha_stream)?;
+ chacha_stream.read.eat_remaining()?;
+
+ let mut tag = [0 as u8; 16];
+ r.read_exact(&mut tag)?;
+ if !chacha.finish_and_check_tag(&tag) {
+ return Err(DecodeError::InvalidValue)
+ }
+
+ Ok(Self { readable })
+ }
+}
+
+
+/// Enables simultaneously reading and decrypting a ChaCha20Poly1305RFC stream from a std::io::Read.
+struct ChaChaPolyReader<'a, R: Read> {
+ pub chacha: &'a mut ChaCha20Poly1305RFC,
+ pub read: R,
+}
+
+impl<'a, R: Read> Read for ChaChaPolyReader<'a, R> {
+ // Decrypt bytes from Self::read into `dest`.
+ // `ChaCha20Poly1305RFC::finish_and_check_tag` must be called to check the tag after all reads
+ // complete.
+ fn read(&mut self, dest: &mut [u8]) -> Result<usize, io::Error> {
+ let res = self.read.read(dest)?;
+ if res > 0 {
+ self.chacha.decrypt_in_place(&mut dest[0..res]);
+ }
+ Ok(res)
+ }
+}
+
+/// Enables simultaneously writing and encrypting a byte stream into a Writer.
+struct ChaChaPolyWriter<'a, W: Writer> {
+ pub chacha: &'a mut ChaCha20Poly1305RFC,
+ pub write: &'a mut W,
+}
+
+impl<'a, W: Writer> Writer for ChaChaPolyWriter<'a, W> {
+ // Encrypt then write bytes from `src` into Self::write.
+ // `ChaCha20Poly1305RFC::finish_and_get_tag` can be called to retrieve the tag after all writes
+ // complete.
+ fn write_all(&mut self, src: &[u8]) -> Result<(), io::Error> {
+ let mut src_idx = 0;
+ while src_idx < src.len() {
+ let mut write_buffer = [0; 8192];
+ let bytes_written = (&mut write_buffer[..]).write(&src[src_idx..]).expect("In-memory writes can't fail");
+ self.chacha.encrypt_in_place(&mut write_buffer[..bytes_written]);
+ self.write.write_all(&write_buffer[..bytes_written])?;
+ src_idx += bytes_written;
+ }
+ Ok(())
+ }
+}
+
+
+#[cfg(test)]
+mod tests {
+ use crate::ln::msgs::DecodeError;
+ use super::{ChaChaPolyReadAdapter, ChaChaPolyWriteAdapter};
+ use crate::util::ser::{self, FixedLengthReader, LengthReadableArgs, Writeable};
+
+ // Used for for testing various lengths of serialization.
+ #[derive(Debug, PartialEq, Eq)]
+ struct TestWriteable {
+ field1: Vec<u8>,
+ field2: Vec<u8>,
+ field3: Vec<u8>,
+ }
+ impl_writeable_tlv_based!(TestWriteable, {
+ (1, field1, required_vec),
+ (2, field2, required_vec),
+ (3, field3, required_vec),
+ });
+
+ #[test]
+ fn test_chacha_stream_adapters() {
+ // Check that ChaChaPolyReadAdapter and ChaChaPolyWriteAdapter correctly encode and decode an
+ // encrypted object.
+ macro_rules! check_object_read_write {
+ ($obj: expr) => {
+ // First, serialize the object, encrypted with ChaCha20Poly1305.
+ let rho = [42; 32];
+ let writeable_len = $obj.serialized_length() as u64 + 16;
+ let write_adapter = ChaChaPolyWriteAdapter::new(rho, &$obj);
+ let encrypted_writeable_bytes = write_adapter.encode();
+ let encrypted_writeable = &encrypted_writeable_bytes[..];
+
+ // Now deserialize the object back and make sure it matches the original.
+ let mut rd = FixedLengthReader::new(encrypted_writeable, writeable_len);
+ let read_adapter = <ChaChaPolyReadAdapter<TestWriteable>>::read(&mut rd, rho).unwrap();
+ assert_eq!($obj, read_adapter.readable);
+ };
+ }
+
+ // Try a big object that will require multiple write buffers.
+ let big_writeable = TestWriteable {
+ field1: vec![43],
+ field2: vec![44; 4192],
+ field3: vec![45; 4192 + 1],
+ };
+ check_object_read_write!(big_writeable);
+
+ // Try a small object that fits into one write buffer.
+ let small_writeable = TestWriteable {
+ field1: vec![43],
+ field2: vec![44],
+ field3: vec![45],
+ };
+ check_object_read_write!(small_writeable);
+ }
+
+ fn do_chacha_stream_adapters_ser_macros() -> Result<(), DecodeError> {
+ let writeable = TestWriteable {
+ field1: vec![43],
+ field2: vec![44; 4192],
+ field3: vec![45; 4192 + 1],
+ };
+
+ // First, serialize the object into a TLV stream, encrypted with ChaCha20Poly1305.
+ let rho = [42; 32];
+ let write_adapter = ChaChaPolyWriteAdapter::new(rho, &writeable);
+ let mut writer = ser::VecWriter(Vec::new());
+ encode_tlv_stream!(&mut writer, {
+ (1, write_adapter, required),
+ });
+
+ // Now deserialize the object back and make sure it matches the original.
+ let mut read_adapter: Option<ChaChaPolyReadAdapter<TestWriteable>> = None;
+ decode_tlv_stream!(&writer.0[..], {
+ (1, read_adapter, (option: LengthReadableArgs, rho)),
+ });
+ assert_eq!(writeable, read_adapter.unwrap().readable);
+
+ Ok(())
+ }
+
+ #[test]
+ fn chacha_stream_adapters_ser_macros() {
+ // Test that our stream adapters work as expected with the TLV macros.
+ // This also serves to test the `option: $trait` variant of the `_decode_tlv` ser macro.
+ do_chacha_stream_adapters_ser_macros().unwrap()
+ }
+}
--- /dev/null
+use bitcoin::hashes::{Hash, HashEngine};
+use bitcoin::hashes::hmac::{Hmac, HmacEngine};
+use bitcoin::hashes::sha256::Hash as Sha256;
+use bitcoin::secp256k1::{Message, Secp256k1, SecretKey, ecdsa::Signature, Signing};
+
+use crate::sign::EntropySource;
+
+use core::ops::Deref;
+
+macro_rules! hkdf_extract_expand {
+ ($salt: expr, $ikm: expr) => {{
+ let mut hmac = HmacEngine::<Sha256>::new($salt);
+ hmac.input($ikm);
+ let prk = Hmac::from_engine(hmac).to_byte_array();
+ let mut hmac = HmacEngine::<Sha256>::new(&prk[..]);
+ hmac.input(&[1; 1]);
+ let t1 = Hmac::from_engine(hmac).to_byte_array();
+ let mut hmac = HmacEngine::<Sha256>::new(&prk[..]);
+ hmac.input(&t1);
+ hmac.input(&[2; 1]);
+ (t1, Hmac::from_engine(hmac).to_byte_array(), prk)
+ }};
+ ($salt: expr, $ikm: expr, 2) => {{
+ let (k1, k2, _) = hkdf_extract_expand!($salt, $ikm);
+ (k1, k2)
+ }};
+ ($salt: expr, $ikm: expr, 5) => {{
+ let (k1, k2, prk) = hkdf_extract_expand!($salt, $ikm);
+
+ let mut hmac = HmacEngine::<Sha256>::new(&prk[..]);
+ hmac.input(&k2);
+ hmac.input(&[3; 1]);
+ let k3 = Hmac::from_engine(hmac).to_byte_array();
+
+ let mut hmac = HmacEngine::<Sha256>::new(&prk[..]);
+ hmac.input(&k3);
+ hmac.input(&[4; 1]);
+ let k4 = Hmac::from_engine(hmac).to_byte_array();
+
+ let mut hmac = HmacEngine::<Sha256>::new(&prk[..]);
+ hmac.input(&k4);
+ hmac.input(&[5; 1]);
+ let k5 = Hmac::from_engine(hmac).to_byte_array();
+
+ (k1, k2, k3, k4, k5)
+ }}
+}
+
+pub fn hkdf_extract_expand_twice(salt: &[u8], ikm: &[u8]) -> ([u8; 32], [u8; 32]) {
+ hkdf_extract_expand!(salt, ikm, 2)
+}
+
+pub fn hkdf_extract_expand_5x(salt: &[u8], ikm: &[u8]) -> ([u8; 32], [u8; 32], [u8; 32], [u8; 32], [u8; 32]) {
+ hkdf_extract_expand!(salt, ikm, 5)
+}
+
+#[inline]
+pub fn sign<C: Signing>(ctx: &Secp256k1<C>, msg: &Message, sk: &SecretKey) -> Signature {
+ #[cfg(feature = "grind_signatures")]
+ let sig = ctx.sign_ecdsa_low_r(msg, sk);
+ #[cfg(not(feature = "grind_signatures"))]
+ let sig = ctx.sign_ecdsa(msg, sk);
+ sig
+}
+
+#[inline]
+#[allow(unused_variables)]
+pub fn sign_with_aux_rand<C: Signing, ES: Deref>(
+ ctx: &Secp256k1<C>, msg: &Message, sk: &SecretKey, entropy_source: &ES
+) -> Signature where ES::Target: EntropySource {
+ #[cfg(feature = "grind_signatures")]
+ let sig = loop {
+ let sig = ctx.sign_ecdsa_with_noncedata(msg, sk, &entropy_source.get_secure_random_bytes());
+ if sig.serialize_compact()[0] < 0x80 {
+ break sig;
+ }
+ };
+ #[cfg(all(not(feature = "grind_signatures"), not(feature = "_test_vectors")))]
+ let sig = ctx.sign_ecdsa_with_noncedata(msg, sk, &entropy_source.get_secure_random_bytes());
+ #[cfg(all(not(feature = "grind_signatures"), feature = "_test_vectors"))]
+ let sig = sign(ctx, msg, sk);
+ sig
+}
use crate::ln::features::ChannelTypeFeatures;
use crate::ln::msgs;
use crate::ln::{ChannelId, PaymentPreimage, PaymentHash, PaymentSecret};
+use crate::chain::transaction;
use crate::routing::gossip::NetworkUpdate;
use crate::util::errors::APIError;
use crate::util::ser::{BigSize, FixedLengthReader, Writeable, Writer, MaybeReadable, Readable, RequiredWrapper, UpgradableRequired, WithoutLength};
/// replies. Handlers should connect to the node otherwise any buffered messages may be lost.
///
/// [`OnionMessage`]: msgs::OnionMessage
- /// [`MessageRouter`]: crate::onion_message::MessageRouter
- /// [`Destination`]: crate::onion_message::Destination
+ /// [`MessageRouter`]: crate::onion_message::messenger::MessageRouter
+ /// [`Destination`]: crate::onion_message::messenger::Destination
/// [`OnionMessageHandler`]: crate::ln::msgs::OnionMessageHandler
ConnectionNeeded {
/// The node id for the node needing a connection.
///
/// [`ChannelManager::accept_inbound_channel`]: crate::ln::channelmanager::ChannelManager::accept_inbound_channel
/// [`UserConfig::manually_accept_inbound_channels`]: crate::util::config::UserConfig::manually_accept_inbound_channels
- ChannelClosed {
+ ChannelClosed {
/// The `channel_id` of the channel which has been closed. Note that on-chain transactions
/// resolving the channel are likely still awaiting confirmation.
channel_id: ChannelId,
///
/// This field will be `None` for objects serialized prior to LDK 0.0.117.
channel_capacity_sats: Option<u64>,
+ /// The original channel funding TXO; this helps checking for the existence and confirmation
+ /// status of the closing tx.
+ /// Note that for instances serialized in v0.0.119 or prior this will be missing (None).
+ channel_funding_txo: Option<transaction::OutPoint>,
},
/// Used to indicate to the user that they can abandon the funding transaction and recycle the
/// inputs for another purpose.
});
},
&Event::ChannelClosed { ref channel_id, ref user_channel_id, ref reason,
- ref counterparty_node_id, ref channel_capacity_sats
+ ref counterparty_node_id, ref channel_capacity_sats, ref channel_funding_txo
} => {
9u8.write(writer)?;
// `user_channel_id` used to be a single u64 value. In order to remain backwards
(3, user_channel_id_high, required),
(5, counterparty_node_id, option),
(7, channel_capacity_sats, option),
+ (9, channel_funding_txo, option),
});
},
&Event::DiscardFunding { ref channel_id, ref transaction } => {
let mut user_channel_id_high_opt: Option<u64> = None;
let mut counterparty_node_id = None;
let mut channel_capacity_sats = None;
+ let mut channel_funding_txo = None;
read_tlv_fields!(reader, {
(0, channel_id, required),
(1, user_channel_id_low_opt, option),
(3, user_channel_id_high_opt, option),
(5, counterparty_node_id, option),
(7, channel_capacity_sats, option),
+ (9, channel_funding_txo, option),
});
// `user_channel_id` used to be a single u64 value. In order to remain
((user_channel_id_high_opt.unwrap_or(0) as u128) << 64);
Ok(Some(Event::ChannelClosed { channel_id, user_channel_id, reason: _init_tlv_based_struct_field!(reason, upgradable_required),
- counterparty_node_id, channel_capacity_sats }))
+ counterparty_node_id, channel_capacity_sats, channel_funding_txo }))
};
f()
},
pub mod blinded_path;
pub mod events;
+pub(crate) mod crypto;
+
#[cfg(feature = "std")]
/// Re-export of either `core2::io` or `std::io`, depending on the `std` feature flag.
pub use std::io;
claim_payment(&nodes[0], &[&nodes[1], &nodes[2]], payment_preimage);
}
+#[test]
+fn three_hop_blinded_path_success() {
+ let chanmon_cfgs = create_chanmon_cfgs(5);
+ let node_cfgs = create_node_cfgs(5, &chanmon_cfgs);
+ let node_chanmgrs = create_node_chanmgrs(5, &node_cfgs, &[None, None, None, None, None]);
+ let mut nodes = create_network(5, &node_cfgs, &node_chanmgrs);
+ create_announced_chan_between_nodes_with_value(&nodes, 0, 1, 1_000_000, 0);
+ create_announced_chan_between_nodes_with_value(&nodes, 1, 2, 1_000_000, 0);
+ let chan_upd_2_3 = create_announced_chan_between_nodes_with_value(&nodes, 2, 3, 1_000_000, 0).0.contents;
+ let chan_upd_3_4 = create_announced_chan_between_nodes_with_value(&nodes, 3, 4, 1_000_000, 0).0.contents;
+
+ let amt_msat = 5000;
+ let (payment_preimage, payment_hash, payment_secret) = get_payment_preimage_hash(&nodes[4], Some(amt_msat), None);
+ let route_params = get_blinded_route_parameters(amt_msat, payment_secret,
+ nodes.iter().skip(2).map(|n| n.node.get_our_node_id()).collect(),
+ &[&chan_upd_2_3, &chan_upd_3_4], &chanmon_cfgs[4].keys_manager);
+
+ nodes[0].node.send_payment(payment_hash, RecipientOnionFields::spontaneous_empty(), PaymentId(payment_hash.0), route_params, Retry::Attempts(0)).unwrap();
+ check_added_monitors(&nodes[0], 1);
+ pass_along_route(&nodes[0], &[&[&nodes[1], &nodes[2], &nodes[3], &nodes[4]]], amt_msat, payment_hash, payment_secret);
+ claim_payment(&nodes[0], &[&nodes[1], &nodes[2], &nodes[3], &nodes[4]], payment_preimage);
+}
+
#[derive(PartialEq)]
enum ReceiveCheckFail {
// The recipient fails the payment upon `PaymentClaimable`.
};
let amt_msat = 5000;
- let final_cltv_delta = if check == ReceiveCheckFail::ProcessPendingHTLCsCheck {
+ let excess_final_cltv_delta_opt = if check == ReceiveCheckFail::ProcessPendingHTLCsCheck {
// Set the final CLTV expiry too low to trigger the failure in process_pending_htlc_forwards.
Some(TEST_FINAL_CLTV as u16 - 2)
} else { None };
- let (_, payment_hash, payment_secret) = get_payment_preimage_hash(&nodes[2], Some(amt_msat), final_cltv_delta);
+ let (_, payment_hash, payment_secret) = get_payment_preimage_hash(&nodes[2], Some(amt_msat), excess_final_cltv_delta_opt);
let mut route_params = get_blinded_route_parameters(amt_msat, payment_secret,
nodes.iter().skip(1).map(|n| n.node.get_our_node_id()).collect(), &[&chan_upd_1_2],
&chanmon_cfgs[2].keys_manager);
let route = if check == ReceiveCheckFail::ProcessPendingHTLCsCheck {
let mut route = get_route(&nodes[0], &route_params).unwrap();
// Set the final CLTV expiry too low to trigger the failure in process_pending_htlc_forwards.
- route.paths[0].blinded_tail.as_mut().map(|bt| bt.excess_final_cltv_expiry_delta = TEST_FINAL_CLTV - 2);
+ route.paths[0].hops.last_mut().map(|h| h.cltv_expiry_delta += excess_final_cltv_delta_opt.unwrap() as u32);
+ route.paths[0].blinded_tail.as_mut().map(|bt| bt.excess_final_cltv_expiry_delta = excess_final_cltv_delta_opt.unwrap() as u32);
route
} else if check == ReceiveCheckFail::PaymentConstraints {
// Create a blinded path where the receiver's encrypted payload has an htlc_minimum_msat that is
commitment_signed_dance!(nodes[2], nodes[1], (), false, true, false, false);
},
ReceiveCheckFail::ProcessPendingHTLCsCheck => {
+ assert_eq!(payment_event_1_2.msgs[0].cltv_expiry, nodes[0].best_block_info().1 + 1 + excess_final_cltv_delta_opt.unwrap() as u32);
nodes[2].node.handle_update_add_htlc(&nodes[1].node.get_our_node_id(), &payment_event_1_2.msgs[0]);
check_added_monitors!(nodes[2], 0);
do_commitment_signed_dance(&nodes[2], &nodes[1], &payment_event_1_2.commitment_msg, true, true);
use core::ops::Deref;
use crate::chain;
use crate::ln::features::ChannelTypeFeatures;
-use crate::util::crypto::{sign, sign_with_aux_rand};
+use crate::crypto::utils::{sign, sign_with_aux_rand};
use super::channel_keys::{DelayedPaymentBasepoint, DelayedPaymentKey, HtlcKey, HtlcBasepoint, RevocationKey, RevocationBasepoint};
/// Maximum number of one-way in-flight HTLC (protocol-level value).
/// The result of a shutdown that should be handled.
#[must_use]
pub(crate) struct ShutdownResult {
+ pub(crate) closure_reason: ClosureReason,
/// A channel monitor update to apply.
pub(crate) monitor_update: Option<(PublicKey, OutPoint, ChannelMonitorUpdate)>,
/// A list of dropped outbound HTLCs that can safely be failed backwards immediately.
/// propagated to the remainder of the batch.
pub(crate) unbroadcasted_batch_funding_txid: Option<Txid>,
pub(crate) channel_id: ChannelId,
+ pub(crate) user_channel_id: u128,
+ pub(crate) channel_capacity_satoshis: u64,
pub(crate) counterparty_node_id: PublicKey,
+ pub(crate) unbroadcasted_funding_tx: Option<Transaction>,
+ pub(crate) channel_funding_txo: Option<OutPoint>,
}
/// If the majority of the channels funds are to the fundee and the initiator holds only just
/// will sign and send to our counterparty.
/// If an Err is returned, it is a ChannelError::Close (for get_funding_created)
fn build_remote_transaction_keys(&self) -> TxCreationKeys {
- //TODO: Ensure that the payment_key derived here ends up in the library users' wallet as we
- //may see payments to it!
let revocation_basepoint = &self.get_holder_pubkeys().revocation_basepoint;
let htlc_basepoint = &self.get_holder_pubkeys().htlc_basepoint;
let counterparty_pubkeys = self.get_counterparty_pubkeys();
res
}
- fn if_unbroadcasted_funding<F, O>(&self, f: F) -> Option<O>
- where F: Fn() -> Option<O> {
+ fn if_unbroadcasted_funding<F, O>(&self, f: F) -> Option<O> where F: Fn() -> Option<O> {
match self.channel_state {
ChannelState::FundingNegotiated => f(),
- ChannelState::AwaitingChannelReady(flags) => if flags.is_set(AwaitingChannelReadyFlags::WAITING_FOR_BATCH) {
- f()
- } else {
- None
- },
+ ChannelState::AwaitingChannelReady(flags) =>
+ if flags.is_set(AwaitingChannelReadyFlags::WAITING_FOR_BATCH) ||
+ flags.is_set(FundedStateFlags::MONITOR_UPDATE_IN_PROGRESS.into())
+ {
+ f()
+ } else {
+ None
+ },
_ => None,
}
}
/// those explicitly stated to be allowed after shutdown completes, eg some simple getters).
/// Also returns the list of payment_hashes for channels which we can safely fail backwards
/// immediately (others we will have to allow to time out).
- pub fn force_shutdown(&mut self, should_broadcast: bool) -> ShutdownResult {
+ pub fn force_shutdown(&mut self, should_broadcast: bool, closure_reason: ClosureReason) -> ShutdownResult {
// Note that we MUST only generate a monitor update that indicates force-closure - we're
// called during initialization prior to the chain_monitor in the encompassing ChannelManager
// being fully configured in some cases. Thus, its likely any monitor events we generate will
} else { None }
} else { None };
let unbroadcasted_batch_funding_txid = self.unbroadcasted_batch_funding_txid();
+ let unbroadcasted_funding_tx = self.unbroadcasted_funding();
self.channel_state = ChannelState::ShutdownComplete;
self.update_time_counter += 1;
ShutdownResult {
+ closure_reason,
monitor_update,
dropped_outbound_htlcs,
unbroadcasted_batch_funding_txid,
channel_id: self.channel_id,
+ user_channel_id: self.user_id,
+ channel_capacity_satoshis: self.channel_value_satoshis,
counterparty_node_id: self.counterparty_node_id,
+ unbroadcasted_funding_tx,
+ channel_funding_txo: self.get_funding_txo(),
}
}
HTLCUpdateAwaitingACK::FailHTLC { htlc_id, err_packet: self }
}
}
-impl FailHTLCContents for (u16, [u8; 32]) {
- type Message = msgs::UpdateFailMalformedHTLC; // (failure_code, sha256_of_onion)
+impl FailHTLCContents for ([u8; 32], u16) {
+ type Message = msgs::UpdateFailMalformedHTLC;
fn to_message(self, htlc_id: u64, channel_id: ChannelId) -> Self::Message {
msgs::UpdateFailMalformedHTLC {
htlc_id,
channel_id,
- failure_code: self.0,
- sha256_of_onion: self.1
+ sha256_of_onion: self.0,
+ failure_code: self.1
}
}
fn to_inbound_htlc_state(self) -> InboundHTLCState {
- InboundHTLCState::LocalRemoved(
- InboundHTLCRemovalReason::FailMalformed((self.1, self.0))
- )
+ InboundHTLCState::LocalRemoved(InboundHTLCRemovalReason::FailMalformed(self))
}
fn to_htlc_update_awaiting_ack(self, htlc_id: u64) -> HTLCUpdateAwaitingACK {
HTLCUpdateAwaitingACK::FailMalformedHTLC {
htlc_id,
- failure_code: self.0,
- sha256_of_onion: self.1
+ sha256_of_onion: self.0,
+ failure_code: self.1
}
}
}
pub fn queue_fail_malformed_htlc<L: Deref>(
&mut self, htlc_id_arg: u64, failure_code: u16, sha256_of_onion: [u8; 32], logger: &L
) -> Result<(), ChannelError> where L::Target: Logger {
- self.fail_htlc(htlc_id_arg, (failure_code, sha256_of_onion), true, logger)
+ self.fail_htlc(htlc_id_arg, (sha256_of_onion, failure_code), true, logger)
.map(|msg_opt| assert!(msg_opt.is_none(), "We forced holding cell?"))
}
/// return `Ok(_)` if preconditions are met. In any case, `Err`s will only be
/// [`ChannelError::Ignore`].
fn fail_htlc<L: Deref, E: FailHTLCContents + Clone>(
- &mut self, htlc_id_arg: u64, err_packet: E, mut force_holding_cell: bool,
+ &mut self, htlc_id_arg: u64, err_contents: E, mut force_holding_cell: bool,
logger: &L
) -> Result<Option<E::Message>, ChannelError> where L::Target: Logger {
if !matches!(self.context.channel_state, ChannelState::ChannelReady(_)) {
}
}
log_trace!(logger, "Placing failure for HTLC ID {} in holding cell in channel {}.", htlc_id_arg, &self.context.channel_id());
- self.context.holding_cell_htlc_updates.push(err_packet.to_htlc_update_awaiting_ack(htlc_id_arg));
+ self.context.holding_cell_htlc_updates.push(err_contents.to_htlc_update_awaiting_ack(htlc_id_arg));
return Ok(None);
}
E::Message::name(), &self.context.channel_id());
{
let htlc = &mut self.context.pending_inbound_htlcs[pending_idx];
- htlc.state = err_packet.clone().to_inbound_htlc_state();
+ htlc.state = err_contents.clone().to_inbound_htlc_state();
}
- Ok(Some(err_packet.to_message(htlc_id_arg, self.context.channel_id())))
+ Ok(Some(err_contents.to_message(htlc_id_arg, self.context.channel_id())))
}
// Message handlers:
// the limit. In case it's less rare than I anticipate, we may want to revisit
// handling this case better and maybe fulfilling some of the HTLCs while attempting
// to rebalance channels.
- match &htlc_update {
+ let fail_htlc_res = match &htlc_update {
&HTLCUpdateAwaitingACK::AddHTLC {
amount_msat, cltv_expiry, ref payment_hash, ref source, ref onion_routing_packet,
skimmed_fee_msat, blinding_point, ..
}
}
}
+ None
},
&HTLCUpdateAwaitingACK::ClaimHTLC { ref payment_preimage, htlc_id, .. } => {
// If an HTLC claim was previously added to the holding cell (via
{ monitor_update } else { unreachable!() };
update_fulfill_count += 1;
monitor_update.updates.append(&mut additional_monitor_update.updates);
+ None
},
&HTLCUpdateAwaitingACK::FailHTLC { htlc_id, ref err_packet } => {
- match self.fail_htlc(htlc_id, err_packet.clone(), false, logger) {
- Ok(update_fail_msg_option) => {
- // If an HTLC failure was previously added to the holding cell (via
- // `queue_fail_htlc`) then generating the fail message itself must
- // not fail - we should never end up in a state where we double-fail
- // an HTLC or fail-then-claim an HTLC as it indicates we didn't wait
- // for a full revocation before failing.
- debug_assert!(update_fail_msg_option.is_some());
- update_fail_count += 1;
- },
- Err(e) => {
- if let ChannelError::Ignore(_) = e {}
- else {
- panic!("Got a non-IgnoreError action trying to fail holding cell HTLC");
- }
- }
- }
+ Some(self.fail_htlc(htlc_id, err_packet.clone(), false, logger)
+ .map(|fail_msg_opt| fail_msg_opt.map(|_| ())))
},
&HTLCUpdateAwaitingACK::FailMalformedHTLC { htlc_id, failure_code, sha256_of_onion } => {
- match self.fail_htlc(htlc_id, (failure_code, sha256_of_onion), false, logger) {
- Ok(update_fail_malformed_opt) => {
- debug_assert!(update_fail_malformed_opt.is_some()); // See above comment
- update_fail_count += 1;
- },
- Err(e) => {
- if let ChannelError::Ignore(_) = e {}
- else {
- panic!("Got a non-IgnoreError action trying to fail holding cell HTLC");
- }
- }
- }
- },
+ Some(self.fail_htlc(htlc_id, (sha256_of_onion, failure_code), false, logger)
+ .map(|fail_msg_opt| fail_msg_opt.map(|_| ())))
+ }
+ };
+ if let Some(res) = fail_htlc_res {
+ match res {
+ Ok(fail_msg_opt) => {
+ // If an HTLC failure was previously added to the holding cell (via
+ // `queue_fail_{malformed_}htlc`) then generating the fail message itself must
+ // not fail - we should never end up in a state where we double-fail
+ // an HTLC or fail-then-claim an HTLC as it indicates we didn't wait
+ // for a full revocation before failing.
+ debug_assert!(fail_msg_opt.is_some());
+ update_fail_count += 1;
+ },
+ Err(ChannelError::Ignore(_)) => {},
+ Err(_) => {
+ panic!("Got a non-IgnoreError action trying to fail holding cell HTLC");
+ },
+ }
}
}
if update_add_count == 0 && update_fulfill_count == 0 && update_fail_count == 0 && self.context.holding_cell_update_fee.is_none() {
if let Some((last_fee, sig)) = self.context.last_sent_closing_fee {
if last_fee == msg.fee_satoshis {
let shutdown_result = ShutdownResult {
+ closure_reason: ClosureReason::CooperativeClosure,
monitor_update: None,
dropped_outbound_htlcs: Vec::new(),
unbroadcasted_batch_funding_txid: self.context.unbroadcasted_batch_funding_txid(),
channel_id: self.context.channel_id,
+ user_channel_id: self.context.user_id,
+ channel_capacity_satoshis: self.context.channel_value_satoshis,
counterparty_node_id: self.context.counterparty_node_id,
+ unbroadcasted_funding_tx: self.context.unbroadcasted_funding(),
+ channel_funding_txo: self.context.get_funding_txo(),
};
let tx = self.build_signed_closing_transaction(&mut closing_tx, &msg.signature, &sig);
self.context.channel_state = ChannelState::ShutdownComplete;
.map_err(|_| ChannelError::Close("External signer refused to sign closing transaction".to_owned()))?;
let (signed_tx, shutdown_result) = if $new_fee == msg.fee_satoshis {
let shutdown_result = ShutdownResult {
+ closure_reason: ClosureReason::CooperativeClosure,
monitor_update: None,
dropped_outbound_htlcs: Vec::new(),
unbroadcasted_batch_funding_txid: self.context.unbroadcasted_batch_funding_txid(),
channel_id: self.context.channel_id,
+ user_channel_id: self.context.user_id,
+ channel_capacity_satoshis: self.context.channel_value_satoshis,
counterparty_node_id: self.context.counterparty_node_id,
+ unbroadcasted_funding_tx: self.context.unbroadcasted_funding(),
+ channel_funding_txo: self.context.get_funding_txo(),
};
self.context.channel_state = ChannelState::ShutdownComplete;
self.context.update_time_counter += 1;
use bitcoin::hashes::sha256::Hash as Sha256;
macro_rules! doc_comment {
- ($x:expr, $($tt:tt)*) => {
- #[doc = $x]
- $($tt)*
- };
+ ($x:expr, $($tt:tt)*) => {
+ #[doc = $x]
+ $($tt)*
+ };
}
macro_rules! basepoint_impl {
- ($BasepointT:ty) => {
- impl $BasepointT {
- /// Get inner Public Key
- pub fn to_public_key(&self) -> PublicKey {
- self.0
- }
- }
-
- impl From<PublicKey> for $BasepointT {
- fn from(value: PublicKey) -> Self {
- Self(value)
- }
- }
-
- }
+ ($BasepointT:ty) => {
+ impl $BasepointT {
+ /// Get inner Public Key
+ pub fn to_public_key(&self) -> PublicKey {
+ self.0
+ }
+ }
+
+ impl From<PublicKey> for $BasepointT {
+ fn from(value: PublicKey) -> Self {
+ Self(value)
+ }
+ }
+
+ }
}
macro_rules! key_impl {
- ($BasepointT:ty, $KeyName:expr) => {
- doc_comment! {
- concat!("Generate ", $KeyName, " using per_commitment_point"),
- pub fn from_basepoint<T: secp256k1::Signing>(
- secp_ctx: &Secp256k1<T>,
- basepoint: &$BasepointT,
- per_commitment_point: &PublicKey,
- ) -> Self {
- Self(derive_public_key(secp_ctx, per_commitment_point, &basepoint.0))
- }
- }
-
- doc_comment! {
- concat!("Generate ", $KeyName, " from privkey"),
- pub fn from_secret_key<T: secp256k1::Signing>(secp_ctx: &Secp256k1<T>, sk: &SecretKey) -> Self {
- Self(PublicKey::from_secret_key(&secp_ctx, &sk))
- }
- }
-
- /// Get inner Public Key
- pub fn to_public_key(&self) -> PublicKey {
- self.0
- }
- }
+ ($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))
+ }
+ }
+
+ /// Get inner Public Key
+ 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))
- }
- }
- }
+ ($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))
+ }
+ }
+ }
}
-/// Master key used in conjunction with per_commitment_point to generate [`local_delayedpubkey`](https://github.com/lightning/bolts/blob/master/03-transactions.md#key-derivation) for the latest state of a channel.
-/// A watcher can be given a [DelayedPaymentBasepoint] to generate per commitment [DelayedPaymentKey] to create justice transactions.
+/// Base key used in conjunction with a `per_commitment_point` to generate a [`DelayedPaymentKey`].
+///
+/// The delayed payment key is used to pay the commitment state broadcaster their
+/// non-HTLC-encumbered funds after a delay to give their counterparty a chance to punish if the
+/// state broadcasted was previously revoked.
#[derive(PartialEq, Eq, Clone, Copy, Debug, Hash)]
pub struct DelayedPaymentBasepoint(pub PublicKey);
basepoint_impl!(DelayedPaymentBasepoint);
key_read_write!(DelayedPaymentBasepoint);
-/// [delayedpubkey](https://github.com/lightning/bolts/blob/master/03-transactions.md#localpubkey-local_htlcpubkey-remote_htlcpubkey-local_delayedpubkey-and-remote_delayedpubkey-derivation)
-/// To allow a counterparty to contest a channel state published by a node, Lightning protocol sets delays for some of the outputs, before can be spend.
-/// For example a commitment transaction has to_local output encumbered by a delay, negotiated at the channel establishment flow.
-/// To spend from such output a node has to generate a script using, among others, a local delayed payment key.
+
+/// A derived key built from a [`DelayedPaymentBasepoint`] and `per_commitment_point`.
+///
+/// The delayed payment key is used to pay the commitment state broadcaster their
+/// non-HTLC-encumbered funds after a delay. This delay gives their counterparty a chance to
+/// punish and claim all the channel funds if the state broadcasted was previously revoked.
+///
+/// [See the BOLT specs]
+/// (https://github.com/lightning/bolts/blob/master/03-transactions.md#localpubkey-local_htlcpubkey-remote_htlcpubkey-local_delayedpubkey-and-remote_delayedpubkey-derivation)
+/// for more information on key derivation details.
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
pub struct DelayedPaymentKey(pub PublicKey);
impl DelayedPaymentKey {
- key_impl!(DelayedPaymentBasepoint, "delayedpubkey");
+ key_impl!(DelayedPaymentBasepoint, "delayedpubkey");
}
key_read_write!(DelayedPaymentKey);
-/// Master key used in conjunction with per_commitment_point to generate a [localpubkey](https://github.com/lightning/bolts/blob/master/03-transactions.md#key-derivation) for the latest state of a channel.
-/// Also used to generate a commitment number in a commitment transaction or as a Payment Key for a remote node (not us) in an anchor output if `option_static_remotekey` is enabled.
-/// Shared by both nodes in a channel establishment message flow.
-#[derive(PartialEq, Eq, Clone, Copy, Debug, Hash)]
-pub struct PaymentBasepoint(pub PublicKey);
-basepoint_impl!(PaymentBasepoint);
-key_read_write!(PaymentBasepoint);
-
-
-/// [localpubkey](https://github.com/lightning/bolts/blob/master/03-transactions.md#localpubkey-local_htlcpubkey-remote_htlcpubkey-local_delayedpubkey-and-remote_delayedpubkey-derivation) is a child key of a payment basepoint,
-/// that enables a secure hash-lock for off-chain payments without risk of funds getting stuck or stolen. A payment key is normally shared with a counterparty so that it can generate
-/// a commitment transaction's to_remote ouput, which our node can claim in case the counterparty force closes the channel.
-#[derive(PartialEq, Eq, Clone, Copy, Debug)]
-pub struct PaymentKey(pub PublicKey);
-
-impl PaymentKey {
- key_impl!(PaymentBasepoint, "localpubkey");
-}
-key_read_write!(PaymentKey);
-
-/// Master key used in conjunction with per_commitment_point to generate [htlcpubkey](https://github.com/lightning/bolts/blob/master/03-transactions.md#key-derivation) for the latest state of a channel.
+/// Base key used in conjunction with a `per_commitment_point` to generate an [`HtlcKey`].
+///
+/// HTLC keys are used to ensure only the recipient of an HTLC can claim it on-chain with the HTLC
+/// preimage and that only the sender of an HTLC can claim it on-chain after it has timed out.
+/// Thus, both channel counterparties' HTLC keys will appears in each HTLC output's script.
#[derive(PartialEq, Eq, Clone, Copy, Debug, Hash)]
pub struct HtlcBasepoint(pub PublicKey);
basepoint_impl!(HtlcBasepoint);
key_read_write!(HtlcBasepoint);
-
-/// [htlcpubkey](https://github.com/lightning/bolts/blob/master/03-transactions.md#localpubkey-local_htlcpubkey-remote_htlcpubkey-local_delayedpubkey-and-remote_delayedpubkey-derivation) is a child key of an htlc basepoint,
-/// that enables secure routing of payments in onion scheme without a risk of them getting stuck or diverted. It is used to claim the funds in successful or timed out htlc outputs.
+/// A derived key built from a [`HtlcBasepoint`] and `per_commitment_point`.
+///
+/// HTLC keys are used to ensure only the recipient of an HTLC can claim it on-chain with the HTLC
+/// preimage and that only the sender of an HTLC can claim it on-chain after it has timed out.
+/// Thus, both channel counterparties' HTLC keys will appears in each HTLC output's script.
+///
+/// [See the BOLT specs]
+/// (https://github.com/lightning/bolts/blob/master/03-transactions.md#localpubkey-local_htlcpubkey-remote_htlcpubkey-local_delayedpubkey-and-remote_delayedpubkey-derivation)
+/// for more information on key derivation details.
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
pub struct HtlcKey(pub PublicKey);
impl HtlcKey {
- key_impl!(HtlcBasepoint, "htlcpubkey");
+ key_impl!(HtlcBasepoint, "htlcpubkey");
}
key_read_write!(HtlcKey);
sha.input(&per_commitment_point.serialize());
sha.input(&base_point.serialize());
let res = Sha256::from_engine(sha).to_byte_array();
-
let hashkey = PublicKey::from_secret_key(&secp_ctx,
&SecretKey::from_slice(&res).expect("Hashes should always be valid keys unless SHA-256 is broken"));
key_read_write!(RevocationBasepoint);
-/// [htlcpubkey](https://github.com/lightning/bolts/blob/master/03-transactions.md#localpubkey-local_htlcpubkey-remote_htlcpubkey-local_delayedpubkey-and-remote_delayedpubkey-derivation) is a child key of a revocation basepoint,
-/// that enables a node to create a justice transaction punishing a counterparty for an attempt to steal funds. Used to in generation of commitment and htlc outputs.
+/// The revocation key is used to allow a channel party to revoke their state - giving their
+/// counterparty the required material to claim all of their funds if they broadcast that state.
+///
+/// Each commitment transaction has a revocation key based on the basepoint and
+/// per_commitment_point which is used in both commitment and HTLC transactions.
+///
+/// See [the BOLT spec for derivation details]
+/// (https://github.com/lightning/bolts/blob/master/03-transactions.md#revocationpubkey-derivation)
#[derive(PartialEq, Eq, Clone, Copy, Debug, Hash)]
pub struct RevocationKey(pub PublicKey);
impl RevocationKey {
- /// Derives a per-commitment-transaction revocation public key from its constituent parts. This is
- /// the public equivalend of derive_private_revocation_key - using only public keys to derive a
- /// public key instead of private keys.
- ///
- /// Only the cheating participant owns a valid witness to propagate a revoked
- /// commitment transaction, thus per_commitment_point always come from cheater
- /// and revocation_base_point always come from punisher, which is the broadcaster
- /// of the transaction spending with this key knowledge.
- ///
- /// Note that this is infallible iff we trust that at least one of the two input keys are randomly
- /// generated (ie our own).
- pub fn from_basepoint<T: secp256k1::Verification>(
- secp_ctx: &Secp256k1<T>,
- basepoint: &RevocationBasepoint,
- per_commitment_point: &PublicKey,
- ) -> Self {
- let rev_append_commit_hash_key = {
- let mut sha = Sha256::engine();
- sha.input(&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(&basepoint.to_public_key().serialize());
-
- Sha256::from_engine(sha).to_byte_array()
- };
-
- let countersignatory_contrib = 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)
- }
-
- /// Get inner Public Key
- pub fn to_public_key(&self) -> PublicKey {
- self.0
- }
+ /// Derives a per-commitment-transaction revocation public key from one party's per-commitment
+ /// point and the other party's [`RevocationBasepoint`]. This is the public equivalent of
+ /// [`chan_utils::derive_private_revocation_key`] - using only public keys to derive a public
+ /// key instead of private keys.
+ ///
+ /// Note that this is infallible iff we trust that at least one of the two input keys are randomly
+ /// generated (ie our own).
+ ///
+ /// [`chan_utils::derive_private_revocation_key`]: crate::ln::chan_utils::derive_private_revocation_key
+ 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)
+ }
+
+ /// Get inner Public Key
+ pub fn to_public_key(&self) -> PublicKey {
+ self.0
+ }
}
key_read_write!(RevocationKey);
-
#[cfg(test)]
mod test {
- use bitcoin::secp256k1::{Secp256k1, SecretKey, PublicKey};
- use bitcoin::hashes::hex::FromHex;
- use super::derive_public_key;
+ use bitcoin::secp256k1::{Secp256k1, SecretKey, PublicKey};
+ use bitcoin::hashes::hex::FromHex;
+ use super::derive_public_key;
- #[test]
+ #[test]
fn test_key_derivation() {
// Test vectors from BOLT 3 Appendix E:
let secp_ctx = Secp256k1::new();
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()[..]);
+ <Vec<u8>>::from_hex("0235f2dbfaa89b57ec7b055afe29849ef7ddfeb1cefdb9ebdc43f5494984db29e5").unwrap()[..]);
}
}
#[cfg(any(feature = "_test_utils", test))]
use crate::ln::features::Bolt11InvoiceFeatures;
use crate::routing::router::{BlindedTail, InFlightHtlcs, Path, Payee, PaymentParameters, Route, RouteParameters, Router};
-use crate::ln::onion_payment::{check_incoming_htlc_cltv, create_recv_pending_htlc_info, create_fwd_pending_htlc_info, decode_incoming_update_add_htlc_onion, InboundOnionErr, NextPacketDetails};
+use crate::ln::onion_payment::{check_incoming_htlc_cltv, create_recv_pending_htlc_info, create_fwd_pending_htlc_info, decode_incoming_update_add_htlc_onion, InboundHTLCErr, NextPacketDetails};
use crate::ln::msgs;
use crate::ln::onion_utils;
use crate::ln::onion_utils::{HTLCFailReason, INVALID_ONION_BLINDING};
use crate::offers::offer::{DerivedMetadata, Offer, OfferBuilder};
use crate::offers::parse::Bolt12SemanticError;
use crate::offers::refund::{Refund, RefundBuilder};
-use crate::onion_message::{Destination, MessageRouter, OffersMessage, OffersMessageHandler, PendingOnionMessage, new_pending_onion_message};
+use crate::onion_message::messenger::{Destination, MessageRouter, PendingOnionMessage, new_pending_onion_message};
+use crate::onion_message::offers::{OffersMessage, OffersMessageHandler};
use crate::sign::{EntropySource, NodeSigner, Recipient, SignerProvider};
use crate::sign::ecdsa::WriteableEcdsaChannelSigner;
use crate::util::config::{UserConfig, ChannelConfig, ChannelConfigUpdate};
/// onion payload if we're the introduction node. Useful for calculating the next hop's
/// [`msgs::UpdateAddHTLC::blinding_point`].
pub inbound_blinding_point: PublicKey,
- // Another field will be added here when we support forwarding as a non-intro node.
+ /// If needed, this determines how this HTLC should be failed backwards, based on whether we are
+ /// the introduction node.
+ pub failure: BlindedFailure,
}
impl PendingHTLCRouting {
// Used to override the onion failure code and data if the HTLC is blinded.
fn blinded_failure(&self) -> Option<BlindedFailure> {
- // TODO: needs update when we support forwarding blinded HTLCs as non-intro node
match self {
- Self::Forward { blinded: Some(_), .. } => Some(BlindedFailure::FromIntroductionNode),
+ Self::Forward { blinded: Some(BlindedForward { failure, .. }), .. } => Some(*failure),
Self::Receive { requires_blinded_error: true, .. } => Some(BlindedFailure::FromBlindedNode),
_ => None,
}
},
}
-// Used for failing blinded HTLCs backwards correctly.
+/// Whether this blinded HTLC is being failed backwards by the introduction node or a blinded node,
+/// which determines the failure message that should be used.
#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq)]
-enum BlindedFailure {
+pub enum BlindedFailure {
+ /// This HTLC is being failed backwards by the introduction node, and thus should be failed with
+ /// [`msgs::UpdateFailHTLC`] and error code `0x8000|0x4000|24`.
FromIntroductionNode,
+ /// This HTLC is being failed backwards by a blinded node within the path, and thus should be
+ /// failed with [`msgs::UpdateFailMalformedHTLC`] and error code `0x8000|0x4000|24`.
FromBlindedNode,
}
struct MsgHandleErrInternal {
err: msgs::LightningError,
- chan_id: Option<(ChannelId, u128)>, // If Some a channel of ours has been closed
+ closes_channel: bool,
shutdown_finish: Option<(ShutdownResult, Option<msgs::ChannelUpdate>)>,
- channel_capacity: Option<u64>,
}
impl MsgHandleErrInternal {
#[inline]
},
},
},
- chan_id: None,
+ closes_channel: false,
shutdown_finish: None,
- channel_capacity: None,
}
}
#[inline]
fn from_no_close(err: msgs::LightningError) -> Self {
- Self { err, chan_id: None, shutdown_finish: None, channel_capacity: None }
+ Self { err, closes_channel: false, shutdown_finish: None }
}
#[inline]
- fn from_finish_shutdown(err: String, channel_id: ChannelId, user_channel_id: u128, shutdown_res: ShutdownResult, channel_update: Option<msgs::ChannelUpdate>, channel_capacity: u64) -> Self {
+ fn from_finish_shutdown(err: String, channel_id: ChannelId, shutdown_res: ShutdownResult, channel_update: Option<msgs::ChannelUpdate>) -> Self {
let err_msg = msgs::ErrorMessage { channel_id, data: err.clone() };
let action = if shutdown_res.monitor_update.is_some() {
// We have a closing `ChannelMonitorUpdate`, which means the channel was funded and we
};
Self {
err: LightningError { err, action },
- chan_id: Some((channel_id, user_channel_id)),
+ closes_channel: true,
shutdown_finish: Some((shutdown_res, channel_update)),
- channel_capacity: Some(channel_capacity)
}
}
#[inline]
},
},
},
- chan_id: None,
+ closes_channel: false,
shutdown_finish: None,
- channel_capacity: None,
}
}
fn closes_channel(&self) -> bool {
- self.chan_id.is_some()
+ self.closes_channel
}
}
match $internal {
Ok(msg) => Ok(msg),
- Err(MsgHandleErrInternal { err, chan_id, shutdown_finish, channel_capacity }) => {
+ Err(MsgHandleErrInternal { err, shutdown_finish, .. }) => {
let mut msg_events = Vec::with_capacity(2);
if let Some((shutdown_res, update_option)) = shutdown_finish {
+ let counterparty_node_id = shutdown_res.counterparty_node_id;
+ let channel_id = shutdown_res.channel_id;
+ let logger = WithContext::from(
+ &$self.logger, Some(counterparty_node_id), Some(channel_id),
+ );
+ log_error!(logger, "Force-closing channel: {}", err.err);
+
$self.finish_close_channel(shutdown_res);
if let Some(update) = update_option {
msg_events.push(events::MessageSendEvent::BroadcastChannelUpdate {
msg: update
});
}
- if let Some((channel_id, user_channel_id)) = chan_id {
- $self.pending_events.lock().unwrap().push_back((events::Event::ChannelClosed {
- channel_id, user_channel_id,
- reason: ClosureReason::ProcessingError { err: err.err.clone() },
- counterparty_node_id: Some($counterparty_node_id),
- channel_capacity_sats: channel_capacity,
- }, None));
- }
+ } else {
+ log_error!($self.logger, "Got non-closing error: {}", err.err);
}
- let logger = WithContext::from(
- &$self.logger, Some($counterparty_node_id), chan_id.map(|(chan_id, _)| chan_id)
- );
- log_error!(logger, "{}", err.err);
if let msgs::ErrorAction::IgnoreError = err.action {
} else {
msg_events.push(events::MessageSendEvent::HandleError {
let logger = WithChannelContext::from(&$self.logger, &$channel.context);
log_error!(logger, "Closing channel {} due to close-required error: {}", $channel_id, msg);
update_maps_on_chan_removal!($self, $channel.context);
- let shutdown_res = $channel.context.force_shutdown(true);
- let user_id = $channel.context.get_user_id();
- let channel_capacity_satoshis = $channel.context.get_value_satoshis();
-
- (true, MsgHandleErrInternal::from_finish_shutdown(msg, *$channel_id, user_id,
- shutdown_res, $channel_update, channel_capacity_satoshis))
+ let reason = ClosureReason::ProcessingError { err: msg.clone() };
+ let shutdown_res = $channel.context.force_shutdown(true, reason);
+ let err =
+ MsgHandleErrInternal::from_finish_shutdown(msg, *$channel_id, shutdown_res, $channel_update);
+ (true, err)
},
}
};
.collect()
}
- /// Helper function that issues the channel close events
- fn issue_channel_close_events(&self, context: &ChannelContext<SP>, closure_reason: ClosureReason) {
- let mut pending_events_lock = self.pending_events.lock().unwrap();
- match context.unbroadcasted_funding() {
- Some(transaction) => {
- pending_events_lock.push_back((events::Event::DiscardFunding {
- channel_id: context.channel_id(), transaction
- }, None));
- },
- None => {},
- }
- pending_events_lock.push_back((events::Event::ChannelClosed {
- channel_id: context.channel_id(),
- user_channel_id: context.get_user_id(),
- reason: closure_reason,
- counterparty_node_id: Some(context.get_counterparty_node_id()),
- channel_capacity_sats: Some(context.get_value_satoshis()),
- }, None));
- }
-
fn close_channel_internal(&self, channel_id: &ChannelId, counterparty_node_id: &PublicKey, target_feerate_sats_per_1000_weight: Option<u32>, override_shutdown_script: Option<ShutdownScript>) -> Result<(), APIError> {
let _persistence_guard = PersistenceNotifierGuard::notify_on_drop(self);
peer_state_lock, peer_state, per_peer_state, chan);
}
} else {
- self.issue_channel_close_events(chan_phase_entry.get().context(), ClosureReason::HolderForceClosed);
let mut chan_phase = remove_channel_phase!(self, chan_phase_entry);
- shutdown_result = Some(chan_phase.context_mut().force_shutdown(false));
+ shutdown_result = Some(chan_phase.context_mut().force_shutdown(false, ClosureReason::HolderForceClosed));
}
},
hash_map::Entry::Vacant(_) => {
let logger = WithContext::from(
&self.logger, Some(shutdown_res.counterparty_node_id), Some(shutdown_res.channel_id),
);
- log_debug!(logger, "Finishing closure of channel with {} HTLCs to fail", shutdown_res.dropped_outbound_htlcs.len());
+
+ log_debug!(logger, "Finishing closure of channel due to {} with {} HTLCs to fail",
+ shutdown_res.closure_reason, shutdown_res.dropped_outbound_htlcs.len());
for htlc_source in shutdown_res.dropped_outbound_htlcs.drain(..) {
let (source, payment_hash, counterparty_node_id, channel_id) = htlc_source;
let reason = HTLCFailReason::from_failure_code(0x4000 | 8);
let mut peer_state = peer_state_mutex.lock().unwrap();
if let Some(mut chan) = peer_state.channel_by_id.remove(&channel_id) {
update_maps_on_chan_removal!(self, &chan.context());
- self.issue_channel_close_events(&chan.context(), ClosureReason::FundingBatchClosure);
- shutdown_results.push(chan.context_mut().force_shutdown(false));
+ shutdown_results.push(chan.context_mut().force_shutdown(false, ClosureReason::FundingBatchClosure));
}
}
has_uncompleted_channel = Some(has_uncompleted_channel.map_or(!state, |v| v || !state));
"Closing a batch where all channels have completed initial monitor update",
);
}
+
+ {
+ let mut pending_events = self.pending_events.lock().unwrap();
+ pending_events.push_back((events::Event::ChannelClosed {
+ channel_id: shutdown_res.channel_id,
+ user_channel_id: shutdown_res.user_channel_id,
+ reason: shutdown_res.closure_reason,
+ counterparty_node_id: Some(shutdown_res.counterparty_node_id),
+ channel_capacity_sats: Some(shutdown_res.channel_capacity_satoshis),
+ channel_funding_txo: shutdown_res.channel_funding_txo,
+ }, None));
+
+ if let Some(transaction) = shutdown_res.unbroadcasted_funding_tx {
+ pending_events.push_back((events::Event::DiscardFunding {
+ channel_id: shutdown_res.channel_id, transaction
+ }, None));
+ }
+ }
for shutdown_result in shutdown_results.drain(..) {
self.finish_close_channel(shutdown_result);
}
let logger = WithContext::from(&self.logger, Some(*peer_node_id), Some(*channel_id));
if let hash_map::Entry::Occupied(chan_phase_entry) = peer_state.channel_by_id.entry(channel_id.clone()) {
log_error!(logger, "Force-closing channel {}", channel_id);
- self.issue_channel_close_events(&chan_phase_entry.get().context(), closure_reason);
let mut chan_phase = remove_channel_phase!(self, chan_phase_entry);
mem::drop(peer_state);
mem::drop(per_peer_state);
match chan_phase {
ChannelPhase::Funded(mut chan) => {
- self.finish_close_channel(chan.context.force_shutdown(broadcast));
+ self.finish_close_channel(chan.context.force_shutdown(broadcast, closure_reason));
(self.get_channel_update_for_broadcast(&chan).ok(), chan.context.get_counterparty_node_id())
},
ChannelPhase::UnfundedOutboundV1(_) | ChannelPhase::UnfundedInboundV1(_) => {
- self.finish_close_channel(chan_phase.context_mut().force_shutdown(false));
+ self.finish_close_channel(chan_phase.context_mut().force_shutdown(false, closure_reason));
// Unfunded channel has no update
(None, chan_phase.context().get_counterparty_node_id())
},
let is_intro_node_forward = match next_hop {
onion_utils::Hop::Forward {
- // TODO: update this when we support blinded forwarding as non-intro node
- next_hop_data: msgs::InboundOnionPayload::BlindedForward { .. }, ..
+ next_hop_data: msgs::InboundOnionPayload::BlindedForward {
+ intro_node_blinding_point: Some(_), ..
+ }, ..
} => true,
_ => false,
};
// delay) once they've send us a commitment_signed!
PendingHTLCStatus::Forward(info)
},
- Err(InboundOnionErr { err_code, err_data, msg }) => return_err!(msg, err_code, &err_data)
+ Err(InboundHTLCErr { err_code, err_data, msg }) => return_err!(msg, err_code, &err_data)
}
},
onion_utils::Hop::Forward { next_hop_data, next_hop_hmac, new_packet_bytes } => {
match create_fwd_pending_htlc_info(msg, next_hop_data, next_hop_hmac,
new_packet_bytes, shared_secret, next_packet_pubkey_opt) {
Ok(info) => PendingHTLCStatus::Forward(info),
- Err(InboundOnionErr { err_code, err_data, msg }) => return_err!(msg, err_code, &err_data)
+ Err(InboundHTLCErr { err_code, err_data, msg }) => return_err!(msg, err_code, &err_data)
}
}
}
let mut peer_state_lock = peer_state_mutex.lock().unwrap();
let peer_state = &mut *peer_state_lock;
let funding_txo;
- let (chan, msg_opt) = match peer_state.channel_by_id.remove(temporary_channel_id) {
+ let (mut chan, msg_opt) = match peer_state.channel_by_id.remove(temporary_channel_id) {
Some(ChannelPhase::UnfundedOutboundV1(mut chan)) => {
funding_txo = find_funding_output(&chan, &funding_transaction)?;
let funding_res = chan.get_funding_created(funding_transaction, funding_txo, is_batch_funding, &&logger)
.map_err(|(mut chan, e)| if let ChannelError::Close(msg) = e {
let channel_id = chan.context.channel_id();
- let user_id = chan.context.get_user_id();
- let shutdown_res = chan.context.force_shutdown(false);
- let channel_capacity = chan.context.get_value_satoshis();
- (chan, MsgHandleErrInternal::from_finish_shutdown(msg, channel_id, user_id, shutdown_res, None, channel_capacity))
+ let reason = ClosureReason::ProcessingError { err: msg.clone() };
+ let shutdown_res = chan.context.force_shutdown(false, reason);
+ (chan, MsgHandleErrInternal::from_finish_shutdown(msg, channel_id, shutdown_res, None))
} else { unreachable!(); });
match funding_res {
Ok(funding_msg) => (chan, funding_msg),
},
hash_map::Entry::Vacant(e) => {
let mut outpoint_to_peer = self.outpoint_to_peer.lock().unwrap();
- if outpoint_to_peer.insert(funding_txo, chan.context.get_counterparty_node_id()).is_some() {
- panic!("outpoint_to_peer map already contained funding outpoint, which shouldn't be possible");
+ match outpoint_to_peer.entry(funding_txo) {
+ hash_map::Entry::Vacant(e) => { e.insert(chan.context.get_counterparty_node_id()); },
+ hash_map::Entry::Occupied(o) => {
+ let err = format!(
+ "An existing channel using outpoint {} is open with peer {}",
+ funding_txo, o.get()
+ );
+ mem::drop(outpoint_to_peer);
+ mem::drop(peer_state_lock);
+ mem::drop(per_peer_state);
+ let reason = ClosureReason::ProcessingError { err: err.clone() };
+ self.finish_close_channel(chan.context.force_shutdown(true, reason));
+ return Err(APIError::ChannelUnavailable { err });
+ }
}
e.insert(ChannelPhase::UnfundedOutboundV1(chan));
}
.and_then(|mut peer_state| peer_state.channel_by_id.remove(&channel_id))
.map(|mut chan| {
update_maps_on_chan_removal!(self, &chan.context());
- self.issue_channel_close_events(&chan.context(), ClosureReason::ProcessingError { err: e.clone() });
- shutdown_results.push(chan.context_mut().force_shutdown(false));
+ let closure_reason = ClosureReason::ProcessingError { err: e.clone() };
+ shutdown_results.push(chan.context_mut().force_shutdown(false, closure_reason));
});
}
}
current_height, self.default_configuration.accept_mpp_keysend)
{
Ok(info) => phantom_receives.push((prev_short_channel_id, prev_funding_outpoint, prev_user_channel_id, vec![(info, prev_htlc_id)])),
- Err(InboundOnionErr { err_code, err_data, msg }) => failed_payment!(msg, err_code, err_data, Some(phantom_shared_secret))
+ Err(InboundHTLCErr { err_code, err_data, msg }) => failed_payment!(msg, err_code, err_data, Some(phantom_shared_secret))
}
},
_ => panic!(),
if let Some(ChannelPhase::Funded(ref mut chan)) = peer_state.channel_by_id.get_mut(&forward_chan_id) {
let logger = WithChannelContext::from(&self.logger, &chan.context);
for forward_info in pending_forwards.drain(..) {
- match forward_info {
+ let queue_fail_htlc_res = match forward_info {
HTLCForwardInfo::AddHTLC(PendingAddHTLCInfo {
prev_short_channel_id, prev_htlc_id, prev_funding_outpoint, prev_user_channel_id,
forward_info: PendingHTLCInfo {
incoming_packet_shared_secret: incoming_shared_secret,
// Phantom payments are only PendingHTLCRouting::Receive.
phantom_shared_secret: None,
- blinded_failure: blinded.map(|_| BlindedFailure::FromIntroductionNode),
+ blinded_failure: blinded.map(|b| b.failure),
});
let next_blinding_point = blinded.and_then(|b| {
let encrypted_tlvs_ss = self.node_signer.ecdh(
));
continue;
}
+ None
},
HTLCForwardInfo::AddHTLC { .. } => {
panic!("short_channel_id != 0 should imply any pending_forward entries are of type Forward");
},
HTLCForwardInfo::FailHTLC { htlc_id, err_packet } => {
log_trace!(logger, "Failing HTLC back to channel with short id {} (backward HTLC ID {}) after delay", short_chan_id, htlc_id);
- if let Err(e) = chan.queue_fail_htlc(
- htlc_id, err_packet, &&logger
- ) {
- if let ChannelError::Ignore(msg) = e {
- log_trace!(logger, "Failed to fail HTLC with ID {} backwards to short_id {}: {}", htlc_id, short_chan_id, msg);
- } else {
- panic!("Stated return value requirements in queue_fail_htlc() were not met");
- }
- // fail-backs are best-effort, we probably already have one
- // pending, and if not that's OK, if not, the channel is on
- // the chain and sending the HTLC-Timeout is their problem.
- continue;
- }
+ Some((chan.queue_fail_htlc(htlc_id, err_packet, &&logger), htlc_id))
},
HTLCForwardInfo::FailMalformedHTLC { htlc_id, failure_code, sha256_of_onion } => {
- log_trace!(self.logger, "Failing malformed HTLC back to channel with short id {} (backward HTLC ID {}) after delay", short_chan_id, htlc_id);
- if let Err(e) = chan.queue_fail_malformed_htlc(htlc_id, failure_code, sha256_of_onion, &self.logger) {
- if let ChannelError::Ignore(msg) = e {
- log_trace!(self.logger, "Failed to fail HTLC with ID {} backwards to short_id {}: {}", htlc_id, short_chan_id, msg);
- } else {
- panic!("Stated return value requirements in queue_fail_malformed_htlc() were not met");
- }
- // fail-backs are best-effort, we probably already have one
- // pending, and if not that's OK, if not, the channel is on
- // the chain and sending the HTLC-Timeout is their problem.
- continue;
- }
+ log_trace!(logger, "Failing malformed HTLC back to channel with short id {} (backward HTLC ID {}) after delay", short_chan_id, htlc_id);
+ let res = chan.queue_fail_malformed_htlc(
+ htlc_id, failure_code, sha256_of_onion, &&logger
+ );
+ Some((res, htlc_id))
},
+ };
+ if let Some((queue_fail_htlc_res, htlc_id)) = queue_fail_htlc_res {
+ if let Err(e) = queue_fail_htlc_res {
+ if let ChannelError::Ignore(msg) = e {
+ log_trace!(logger, "Failed to fail HTLC with ID {} backwards to short_id {}: {}", htlc_id, short_chan_id, msg);
+ } else {
+ panic!("Stated return value requirements in queue_fail_{{malformed_}}htlc() were not met");
+ }
+ // fail-backs are best-effort, we probably already have one
+ // pending, and if not that's OK, if not, the channel is on
+ // the chain and sending the HTLC-Timeout is their problem.
+ continue;
+ }
}
}
} else {
log_error!(logger,
"Force-closing pending channel with ID {} for not establishing in a timely manner", chan_id);
update_maps_on_chan_removal!(self, &context);
- self.issue_channel_close_events(&context, ClosureReason::HolderForceClosed);
- shutdown_channels.push(context.force_shutdown(false));
+ shutdown_channels.push(context.force_shutdown(false, ClosureReason::HolderForceClosed));
pending_msg_events.push(MessageSendEvent::HandleError {
node_id: counterparty_node_id,
action: msgs::ErrorAction::SendErrorMessage {
}
fn do_accept_inbound_channel(&self, temporary_channel_id: &ChannelId, counterparty_node_id: &PublicKey, accept_0conf: bool, user_channel_id: u128) -> Result<(), APIError> {
+
+ let logger = WithContext::from(&self.logger, Some(*counterparty_node_id), Some(*temporary_channel_id));
let _persistence_guard = PersistenceNotifierGuard::notify_on_drop(self);
let peers_without_funded_channels =
self.peers_without_funded_channels(|peer| { peer.total_channel_count() > 0 });
let per_peer_state = self.per_peer_state.read().unwrap();
let peer_state_mutex = per_peer_state.get(counterparty_node_id)
- .ok_or_else(|| APIError::ChannelUnavailable { err: format!("Can't find a peer matching the passed counterparty node_id {}", counterparty_node_id) })?;
+ .ok_or_else(|| {
+ let err_str = format!("Can't find a peer matching the passed counterparty node_id {}", counterparty_node_id);
+ log_error!(logger, "{}", err_str);
+
+ APIError::ChannelUnavailable { err: err_str }
+ })?;
let mut peer_state_lock = peer_state_mutex.lock().unwrap();
let peer_state = &mut *peer_state_lock;
let is_only_peer_channel = peer_state.total_channel_count() == 1;
InboundV1Channel::new(&self.fee_estimator, &self.entropy_source, &self.signer_provider,
counterparty_node_id.clone(), &self.channel_type_features(), &peer_state.latest_features,
&unaccepted_channel.open_channel_msg, user_channel_id, &self.default_configuration, best_block_height,
- &self.logger, accept_0conf).map_err(|e| APIError::ChannelUnavailable { err: e.to_string() })
+ &self.logger, accept_0conf).map_err(|e| {
+ let err_str = e.to_string();
+ log_error!(logger, "{}", err_str);
+
+ APIError::ChannelUnavailable { err: err_str }
+ })
+ }
+ _ => {
+ let err_str = "No such channel awaiting to be accepted.".to_owned();
+ log_error!(logger, "{}", err_str);
+
+ Err(APIError::APIMisuseError { err: err_str })
}
- _ => Err(APIError::APIMisuseError { err: "No such channel awaiting to be accepted.".to_owned() })
}?;
if accept_0conf {
}
};
peer_state.pending_msg_events.push(send_msg_err_event);
- return Err(APIError::APIMisuseError { err: "Please use accept_inbound_channel_from_trusted_peer_0conf to accept channels with zero confirmations.".to_owned() });
+ let err_str = "Please use accept_inbound_channel_from_trusted_peer_0conf to accept channels with zero confirmations.".to_owned();
+ log_error!(logger, "{}", err_str);
+
+ return Err(APIError::APIMisuseError { err: err_str });
} else {
// If this peer already has some channels, a new channel won't increase our number of peers
// with unfunded channels, so as long as we aren't over the maximum number of unfunded
}
};
peer_state.pending_msg_events.push(send_msg_err_event);
- return Err(APIError::APIMisuseError { err: "Too many peers with unfunded channels, refusing to accept new ones".to_owned() });
+ let err_str = "Too many peers with unfunded channels, refusing to accept new ones".to_owned();
+ log_error!(logger, "{}", err_str);
+
+ return Err(APIError::APIMisuseError { err: err_str });
}
}
let res =
chan.funding_signed(&msg, best_block, &self.signer_provider, &&logger);
match res {
- Ok((chan, monitor)) => {
+ Ok((mut chan, monitor)) => {
if let Ok(persist_status) = self.chain_monitor.watch_channel(chan.context.get_funding_txo().unwrap(), monitor) {
// We really should be able to insert here without doing a second
// lookup, but sadly rust stdlib doesn't currently allow keeping
Ok(())
} else {
let e = ChannelError::Close("Channel funding outpoint was a duplicate".to_owned());
+ // We weren't able to watch the channel to begin with, so no
+ // updates should be made on it. Previously, full_stack_target
+ // found an (unreachable) panic when the monitor update contained
+ // within `shutdown_finish` was applied.
+ chan.unset_funding_info(msg.channel_id);
return Err(convert_chan_phase_err!(self, e, &mut ChannelPhase::Funded(chan), &msg.channel_id).1);
}
},
let context = phase.context_mut();
let logger = WithChannelContext::from(&self.logger, context);
log_error!(logger, "Immediately closing unfunded channel {} as peer asked to cooperatively shut it down (which is unnecessary)", &msg.channel_id);
- self.issue_channel_close_events(&context, ClosureReason::CounterpartyCoopClosedUnfundedChannel);
let mut chan = remove_channel_phase!(self, chan_phase_entry);
- finish_shutdown = Some(chan.context_mut().force_shutdown(false));
+ finish_shutdown = Some(chan.context_mut().force_shutdown(false, ClosureReason::CounterpartyCoopClosedUnfundedChannel));
},
}
} else {
msg: update
});
}
- self.issue_channel_close_events(&chan.context, ClosureReason::CooperativeClosure);
}
mem::drop(per_peer_state);
if let Some(shutdown_result) = shutdown_result {
let pending_msg_events = &mut peer_state.pending_msg_events;
if let hash_map::Entry::Occupied(chan_phase_entry) = peer_state.channel_by_id.entry(funding_outpoint.to_channel_id()) {
if let ChannelPhase::Funded(mut chan) = remove_channel_phase!(self, chan_phase_entry) {
- failed_channels.push(chan.context.force_shutdown(false));
+ failed_channels.push(chan.context.force_shutdown(false, ClosureReason::HolderForceClosed));
if let Ok(update) = self.get_channel_update_for_broadcast(&chan) {
pending_msg_events.push(events::MessageSendEvent::BroadcastChannelUpdate {
msg: update
});
}
- self.issue_channel_close_events(&chan.context, ClosureReason::HolderForceClosed);
pending_msg_events.push(events::MessageSendEvent::HandleError {
node_id: chan.context.get_counterparty_node_id(),
action: msgs::ErrorAction::DisconnectPeer {
});
}
- self.issue_channel_close_events(&chan.context, ClosureReason::CooperativeClosure);
-
log_info!(logger, "Broadcasting {}", log_tx!(tx));
self.tx_broadcaster.broadcast_transactions(&[&tx]);
update_maps_on_chan_removal!(self, &chan.context);
update_maps_on_chan_removal!(self, &channel.context);
// It looks like our counterparty went on-chain or funding transaction was
// reorged out of the main chain. Close the channel.
- failed_channels.push(channel.context.force_shutdown(true));
+ let reason_message = format!("{}", reason);
+ failed_channels.push(channel.context.force_shutdown(true, reason));
if let Ok(update) = self.get_channel_update_for_broadcast(&channel) {
pending_msg_events.push(events::MessageSendEvent::BroadcastChannelUpdate {
msg: update
});
}
- let reason_message = format!("{}", reason);
- self.issue_channel_close_events(&channel.context, reason);
pending_msg_events.push(events::MessageSendEvent::HandleError {
node_id: channel.context.get_counterparty_node_id(),
action: msgs::ErrorAction::DisconnectPeer {
};
// Clean up for removal.
update_maps_on_chan_removal!(self, &context);
- self.issue_channel_close_events(&context, ClosureReason::DisconnectedPeer);
- failed_channels.push(context.force_shutdown(false));
+ failed_channels.push(context.force_shutdown(false, ClosureReason::DisconnectedPeer));
false
});
// Note that we don't bother generating any events for pre-accept channels -
features.set_channel_type_optional();
features.set_scid_privacy_optional();
features.set_zero_conf_optional();
+ features.set_route_blinding_optional();
if config.channel_handshake_config.negotiate_anchors_zero_fee_htlc_tx {
features.set_anchors_zero_fee_htlc_tx_optional();
}
impl_writeable_tlv_based!(BlindedForward, {
(0, inbound_blinding_point, required),
+ (1, failure, (default_value, BlindedFailure::FromIntroductionNode)),
});
impl_writeable_tlv_based_enum!(PendingHTLCRouting,
log_error!(logger, " The ChannelMonitor for channel {} is at counterparty commitment transaction number {} but the ChannelManager is at counterparty commitment transaction number {}.",
&channel.context.channel_id(), monitor.get_cur_counterparty_commitment_number(), channel.get_cur_counterparty_commitment_transaction_number());
}
- let mut shutdown_result = channel.context.force_shutdown(true);
+ let mut shutdown_result = channel.context.force_shutdown(true, ClosureReason::OutdatedChannelManager);
if shutdown_result.unbroadcasted_batch_funding_txid.is_some() {
return Err(DecodeError::InvalidValue);
}
reason: ClosureReason::OutdatedChannelManager,
counterparty_node_id: Some(channel.context.get_counterparty_node_id()),
channel_capacity_sats: Some(channel.context.get_value_satoshis()),
+ channel_funding_txo: channel.context.get_funding_txo(),
}, None));
for (channel_htlc_source, payment_hash) in channel.inflight_htlc_sources() {
let mut found_htlc = false;
// If we were persisted and shut down while the initial ChannelMonitor persistence
// was in-progress, we never broadcasted the funding transaction and can still
// safely discard the channel.
- let _ = channel.context.force_shutdown(false);
+ let _ = channel.context.force_shutdown(false, ClosureReason::DisconnectedPeer);
channel_closures.push_back((events::Event::ChannelClosed {
channel_id: channel.context.channel_id(),
user_channel_id: channel.context.get_user_id(),
reason: ClosureReason::DisconnectedPeer,
counterparty_node_id: Some(channel.context.get_counterparty_node_id()),
channel_capacity_sats: Some(channel.context.get_value_satoshis()),
+ channel_funding_txo: channel.context.get_funding_txo(),
}, None));
} else {
log_error!(logger, "Missing ChannelMonitor for channel {} needed by ChannelManager.", &channel.context.channel_id());
let sender_intended_amt_msat = 100;
let extra_fee_msat = 10;
let hop_data = msgs::InboundOnionPayload::Receive {
- amt_msat: 100,
- outgoing_cltv_value: 42,
+ sender_intended_htlc_amt_msat: 100,
+ cltv_expiry_height: 42,
payment_metadata: None,
keysend_preimage: None,
payment_data: Some(msgs::FinalOnionHopData {
// Check that if the amount we received + the penultimate hop extra fee is less than the sender
// intended amount, we fail the payment.
let current_height: u32 = node[0].node.best_block.read().unwrap().height();
- if let Err(crate::ln::channelmanager::InboundOnionErr { err_code, .. }) =
+ if let Err(crate::ln::channelmanager::InboundHTLCErr { err_code, .. }) =
create_recv_pending_htlc_info(hop_data, [0; 32], PaymentHash([0; 32]),
sender_intended_amt_msat - extra_fee_msat - 1, 42, None, true, Some(extra_fee_msat),
current_height, node[0].node.default_configuration.accept_mpp_keysend)
// If amt_received + extra_fee is equal to the sender intended amount, we're fine.
let hop_data = msgs::InboundOnionPayload::Receive { // This is the same payload as above, InboundOnionPayload doesn't implement Clone
- amt_msat: 100,
- outgoing_cltv_value: 42,
+ sender_intended_htlc_amt_msat: 100,
+ cltv_expiry_height: 42,
payment_metadata: None,
keysend_preimage: None,
payment_data: Some(msgs::FinalOnionHopData {
let current_height: u32 = node[0].node.best_block.read().unwrap().height();
let result = create_recv_pending_htlc_info(msgs::InboundOnionPayload::Receive {
- amt_msat: 100,
- outgoing_cltv_value: 22,
+ sender_intended_htlc_amt_msat: 100,
+ cltv_expiry_height: 22,
payment_metadata: None,
keysend_preimage: None,
payment_data: Some(msgs::FinalOnionHopData {
(tx, as_channel_ready.channel_id)
}
-pub fn create_chan_between_nodes_with_value_init<'a, 'b, 'c>(node_a: &Node<'a, 'b, 'c>, node_b: &Node<'a, 'b, 'c>, channel_value: u64, push_msat: u64) -> Transaction {
+pub fn exchange_open_accept_chan<'a, 'b, 'c>(node_a: &Node<'a, 'b, 'c>, node_b: &Node<'a, 'b, 'c>, channel_value: u64, push_msat: u64) -> ChannelId {
let create_chan_id = node_a.node.create_channel(node_b.node.get_our_node_id(), channel_value, push_msat, 42, None, None).unwrap();
let open_channel_msg = get_event_msg!(node_a, MessageSendEvent::SendOpenChannel, node_b.node.get_our_node_id());
assert_eq!(open_channel_msg.temporary_channel_id, create_chan_id);
node_a.node.handle_accept_channel(&node_b.node.get_our_node_id(), &accept_channel_msg);
assert_ne!(node_b.node.list_channels().iter().find(|channel| channel.channel_id == create_chan_id).unwrap().user_channel_id, 0);
+ create_chan_id
+}
+
+pub fn create_chan_between_nodes_with_value_init<'a, 'b, 'c>(node_a: &Node<'a, 'b, 'c>, node_b: &Node<'a, 'b, 'c>, channel_value: u64, push_msat: u64) -> Transaction {
+ let create_chan_id = exchange_open_accept_chan(node_a, node_b, channel_value, push_msat);
sign_funding_transaction(node_a, node_b, channel_value, create_chan_id)
}
pub counterparty_node_id: Option<PublicKey>,
pub discard_funding: bool,
pub reason: Option<ClosureReason>,
+ pub channel_funding_txo: Option<OutPoint>,
+ pub user_channel_id: Option<u128>,
}
impl ExpectedCloseEvent {
counterparty_node_id: None,
discard_funding,
reason: Some(reason),
+ channel_funding_txo: None,
+ user_channel_id: None,
}
}
}
reason,
counterparty_node_id,
channel_capacity_sats,
+ channel_funding_txo,
+ user_channel_id,
..
} if (
expected_event.channel_id.map(|expected| *channel_id == expected).unwrap_or(true) &&
expected_event.reason.as_ref().map(|expected| reason == expected).unwrap_or(true) &&
- expected_event.counterparty_node_id.map(|expected| *counterparty_node_id == Some(expected)).unwrap_or(true) &&
- expected_event.channel_capacity_sats.map(|expected| *channel_capacity_sats == Some(expected)).unwrap_or(true)
+ expected_event.
+ counterparty_node_id.map(|expected| *counterparty_node_id == Some(expected)).unwrap_or(true) &&
+ expected_event.channel_capacity_sats
+ .map(|expected| *channel_capacity_sats == Some(expected)).unwrap_or(true) &&
+ expected_event.channel_funding_txo
+ .map(|expected| *channel_funding_txo == Some(expected)).unwrap_or(true) &&
+ expected_event.user_channel_id
+ .map(|expected| *user_channel_id == expected).unwrap_or(true)
)
)));
}
counterparty_node_id: Some(*node_id),
discard_funding: is_check_discard_funding,
reason: Some(expected_reason.clone()),
+ channel_funding_txo: None,
+ user_channel_id: None,
}).collect::<Vec<_>>();
check_closed_events(node, expected_close_events.as_slice());
}
//check to see if the funder, who sent the update_fee request, can afford the new fee (funder_balance >= fee+channel_reserve)
//Should produce and error.
nodes[1].node.handle_commitment_signed(&nodes[0].node.get_our_node_id(), &commit_signed_msg);
- nodes[1].logger.assert_log("lightning::ln::channelmanager", "Funding remote cannot afford proposed new fee".to_string(), 1);
+ nodes[1].logger.assert_log_contains("lightning::ln::channelmanager", "Funding remote cannot afford proposed new fee", 3);
check_added_monitors!(nodes[1], 1);
check_closed_broadcast!(nodes[1], true);
check_closed_event!(nodes[1], 1, ClosureReason::ProcessingError { err: String::from("Funding remote cannot afford proposed new fee") },
nodes[0].node.handle_update_add_htlc(&nodes[1].node.get_our_node_id(), &msg);
// Check that the payment failed and the channel is closed in response to the malicious UpdateAdd.
- nodes[0].logger.assert_log("lightning::ln::channelmanager", "Cannot accept HTLC that would put our balance under counterparty-announced channel reserve value".to_string(), 1);
+ nodes[0].logger.assert_log_contains("lightning::ln::channelmanager", "Cannot accept HTLC that would put our balance under counterparty-announced channel reserve value", 3);
assert_eq!(nodes[0].node.list_channels().len(), 0);
let err_msg = check_closed_broadcast!(nodes[0], true).unwrap();
assert_eq!(err_msg.data, "Cannot accept HTLC that would put our balance under counterparty-announced channel reserve value");
nodes[1].node.handle_update_add_htlc(&nodes[0].node.get_our_node_id(), &msg);
// Check that the payment failed and the channel is closed in response to the malicious UpdateAdd.
- nodes[1].logger.assert_log("lightning::ln::channelmanager", "Remote HTLC add would put them under remote reserve value".to_string(), 1);
+ nodes[1].logger.assert_log_contains("lightning::ln::channelmanager", "Remote HTLC add would put them under remote reserve value", 3);
assert_eq!(nodes[1].node.list_channels().len(), 1);
let err_msg = check_closed_broadcast!(nodes[1], true).unwrap();
assert_eq!(err_msg.data, "Remote HTLC add would put them under remote reserve value");
let events = nodes[1].node.get_and_clear_pending_events();
assert_eq!(events.len(), if deliver_bs_raa { 3 + nodes.len() - 1 } else { 4 + nodes.len() });
- match events[0] {
- Event::ChannelClosed { reason: ClosureReason::CommitmentTxConfirmed, .. } => { },
- _ => panic!("Unexepected event"),
- }
- match events[1] {
- Event::PaymentPathFailed { ref payment_hash, .. } => {
- assert_eq!(*payment_hash, fourth_payment_hash);
- },
- _ => panic!("Unexpected event"),
- }
- match events[2] {
- Event::PaymentFailed { ref payment_hash, .. } => {
- assert_eq!(*payment_hash, fourth_payment_hash);
- },
- _ => panic!("Unexpected event"),
- }
+ assert!(events.iter().any(|ev| matches!(
+ ev,
+ Event::ChannelClosed { reason: ClosureReason::CommitmentTxConfirmed, .. }
+ )));
+ assert!(events.iter().any(|ev| matches!(
+ ev,
+ Event::PaymentPathFailed { ref payment_hash, .. } if *payment_hash == fourth_payment_hash
+ )));
+ assert!(events.iter().any(|ev| matches!(
+ ev,
+ Event::PaymentFailed { ref payment_hash, .. } if *payment_hash == fourth_payment_hash
+ )));
nodes[1].node.process_pending_htlc_forwards();
check_added_monitors!(nodes[1], 1);
updates.update_add_htlcs[0].amount_msat = 0;
nodes[1].node.handle_update_add_htlc(&nodes[0].node.get_our_node_id(), &updates.update_add_htlcs[0]);
- nodes[1].logger.assert_log("lightning::ln::channelmanager", "Remote side tried to send a 0-msat HTLC".to_string(), 1);
+ nodes[1].logger.assert_log_contains("lightning::ln::channelmanager", "Remote side tried to send a 0-msat HTLC", 3);
check_closed_broadcast!(nodes[1], true).unwrap();
check_added_monitors!(nodes[1], 1);
check_closed_event!(nodes[1], 1, ClosureReason::ProcessingError { err: "Remote side tried to send a 0-msat HTLC".to_string() },
RecipientOnionFields::secret_only(our_payment_secret), height + 1, &None).unwrap();
// Edit amt_to_forward to simulate the sender having set
// the final amount and the routing node taking less fee
- if let msgs::OutboundOnionPayload::Receive { ref mut amt_msat, .. } = onion_payloads[1] {
- *amt_msat = 99_000;
+ if let msgs::OutboundOnionPayload::Receive {
+ ref mut sender_intended_htlc_amt_msat, ..
+ } = onion_payloads[1] {
+ *sender_intended_htlc_amt_msat = 99_000;
} else { panic!() }
let new_onion_packet = onion_utils::construct_onion_packet(onion_payloads, onion_keys, [0; 32], &our_payment_hash).unwrap();
payment_event.msgs[0].onion_routing_packet = new_onion_packet;
}
}
+#[test]
+fn test_peer_funding_sidechannel() {
+ // Test that if a peer somehow learns which txid we'll use for our channel funding before we
+ // receive `funding_transaction_generated` the peer cannot cause us to crash. We'd previously
+ // assumed that LDK would receive `funding_transaction_generated` prior to our peer learning
+ // the txid and panicked if the peer tried to open a redundant channel to us with the same
+ // funding outpoint.
+ //
+ // While this assumption is generally safe, some users may have out-of-band protocols where
+ // they notify their LSP about a funding outpoint first, or this may be violated in the future
+ // with collaborative transaction construction protocols, i.e. dual-funding.
+ let chanmon_cfgs = create_chanmon_cfgs(3);
+ let node_cfgs = create_node_cfgs(3, &chanmon_cfgs);
+ let node_chanmgrs = create_node_chanmgrs(3, &node_cfgs, &[None, None, None]);
+ let nodes = create_network(3, &node_cfgs, &node_chanmgrs);
+
+ let temp_chan_id_ab = exchange_open_accept_chan(&nodes[0], &nodes[1], 1_000_000, 0);
+ let temp_chan_id_ca = exchange_open_accept_chan(&nodes[2], &nodes[0], 1_000_000, 0);
+
+ let (_, tx, funding_output) =
+ create_funding_transaction(&nodes[0], &nodes[1].node.get_our_node_id(), 1_000_000, 42);
+
+ let cs_funding_events = nodes[2].node.get_and_clear_pending_events();
+ assert_eq!(cs_funding_events.len(), 1);
+ match cs_funding_events[0] {
+ Event::FundingGenerationReady { .. } => {}
+ _ => panic!("Unexpected event {:?}", cs_funding_events),
+ }
+
+ nodes[2].node.funding_transaction_generated_unchecked(&temp_chan_id_ca, &nodes[0].node.get_our_node_id(), tx.clone(), funding_output.index).unwrap();
+ let funding_created_msg = get_event_msg!(nodes[2], MessageSendEvent::SendFundingCreated, nodes[0].node.get_our_node_id());
+ nodes[0].node.handle_funding_created(&nodes[2].node.get_our_node_id(), &funding_created_msg);
+ get_event_msg!(nodes[0], MessageSendEvent::SendFundingSigned, nodes[2].node.get_our_node_id());
+ expect_channel_pending_event(&nodes[0], &nodes[2].node.get_our_node_id());
+ check_added_monitors!(nodes[0], 1);
+
+ let res = nodes[0].node.funding_transaction_generated(&temp_chan_id_ab, &nodes[1].node.get_our_node_id(), tx.clone());
+ let err_msg = format!("{:?}", res.unwrap_err());
+ assert!(err_msg.contains("An existing channel using outpoint "));
+ assert!(err_msg.contains(" is open with peer"));
+ // Even though the last funding_transaction_generated errored, it still generated a
+ // SendFundingCreated. However, when the peer responds with a funding_signed it will send the
+ // appropriate error message.
+ let as_funding_created = get_event_msg!(nodes[0], MessageSendEvent::SendFundingCreated, nodes[1].node.get_our_node_id());
+ nodes[1].node.handle_funding_created(&nodes[0].node.get_our_node_id(), &as_funding_created);
+ check_added_monitors!(nodes[1], 1);
+ expect_channel_pending_event(&nodes[1], &nodes[0].node.get_our_node_id());
+ let reason = ClosureReason::ProcessingError { err: format!("An existing channel using outpoint {} is open with peer {}", funding_output, nodes[2].node.get_our_node_id()), };
+ check_closed_events(&nodes[0], &[ExpectedCloseEvent::from_id_reason(funding_output.to_channel_id(), true, reason)]);
+
+ let funding_signed = get_event_msg!(nodes[1], MessageSendEvent::SendFundingSigned, nodes[0].node.get_our_node_id());
+ nodes[0].node.handle_funding_signed(&nodes[1].node.get_our_node_id(), &funding_signed);
+ get_err_msg(&nodes[0], &nodes[1].node.get_our_node_id());
+}
+
+#[test]
+fn test_duplicate_conflicting_funding_from_second_peer() {
+ // Test that if a user tries to fund a channel with a funding outpoint they'd previously used
+ // we don't try to remove the previous ChannelMonitor. This is largely a test to ensure we
+ // don't regress in the fuzzer, as such funding getting passed our outpoint-matches checks
+ // implies the user (and our counterparty) has reused cryptographic keys across channels, which
+ // we require the user not do.
+ let chanmon_cfgs = create_chanmon_cfgs(4);
+ let node_cfgs = create_node_cfgs(4, &chanmon_cfgs);
+ let node_chanmgrs = create_node_chanmgrs(4, &node_cfgs, &[None, None, None, None]);
+ let nodes = create_network(4, &node_cfgs, &node_chanmgrs);
+
+ let temp_chan_id = exchange_open_accept_chan(&nodes[0], &nodes[1], 1_000_000, 0);
+
+ let (_, tx, funding_output) =
+ create_funding_transaction(&nodes[0], &nodes[1].node.get_our_node_id(), 1_000_000, 42);
+
+ // Now that we have a funding outpoint, create a dummy `ChannelMonitor` and insert it into
+ // nodes[0]'s ChainMonitor so that the initial `ChannelMonitor` write fails.
+ let dummy_chan_id = create_chan_between_nodes(&nodes[2], &nodes[3]).3;
+ let dummy_monitor = get_monitor!(nodes[2], dummy_chan_id).clone();
+ nodes[0].chain_monitor.chain_monitor.watch_channel(funding_output, dummy_monitor).unwrap();
+
+ nodes[0].node.funding_transaction_generated(&temp_chan_id, &nodes[1].node.get_our_node_id(), tx.clone()).unwrap();
+
+ let mut funding_created_msg = get_event_msg!(nodes[0], MessageSendEvent::SendFundingCreated, nodes[1].node.get_our_node_id());
+ nodes[1].node.handle_funding_created(&nodes[0].node.get_our_node_id(), &funding_created_msg);
+ let funding_signed_msg = get_event_msg!(nodes[1], MessageSendEvent::SendFundingSigned, nodes[0].node.get_our_node_id());
+ check_added_monitors!(nodes[1], 1);
+ expect_channel_pending_event(&nodes[1], &nodes[0].node.get_our_node_id());
+
+ nodes[0].node.handle_funding_signed(&nodes[1].node.get_our_node_id(), &funding_signed_msg);
+ // At this point, the channel should be closed, after having generated one monitor write (the
+ // watch_channel call which failed), but zero monitor updates.
+ check_added_monitors!(nodes[0], 1);
+ get_err_msg(&nodes[0], &nodes[1].node.get_our_node_id());
+ let err_reason = ClosureReason::ProcessingError { err: "Channel funding outpoint was a duplicate".to_owned() };
+ check_closed_events(&nodes[0], &[ExpectedCloseEvent::from_id_reason(funding_signed_msg.channel_id, true, err_reason)]);
+}
+
#[test]
fn test_duplicate_funding_err_in_funding() {
// Test that if we have a live channel with one peer, then another peer comes along and tries
chan.get_funding_created(tx.clone(), funding_outpoint, false, &&logger).map_err(|_| ()).unwrap()
},
_ => panic!("Unexpected ChannelPhase variant"),
- }
+ }.unwrap()
};
check_added_monitors!(nodes[0], 0);
- nodes[1].node.handle_funding_created(&nodes[0].node.get_our_node_id(), &funding_created.unwrap());
+ nodes[1].node.handle_funding_created(&nodes[0].node.get_our_node_id(), &funding_created);
// At this point we'll look up if the channel_id is present and immediately fail the channel
// without trying to persist the `ChannelMonitor`.
check_added_monitors!(nodes[1], 0);
check_closed_events(&nodes[1], &[
- ExpectedCloseEvent::from_id_reason(channel_id, false, ClosureReason::ProcessingError {
+ ExpectedCloseEvent::from_id_reason(funding_created.temporary_channel_id, false, ClosureReason::ProcessingError {
err: "Already had channel with the new channel_id".to_owned()
})
]);
nodes[0].node.peer_disconnected(&nodes[2].node.get_our_node_id());
// The channels in the batch will close immediately.
- let channel_id_1 = OutPoint { txid: tx.txid(), index: 0 }.to_channel_id();
- let channel_id_2 = OutPoint { txid: tx.txid(), index: 1 }.to_channel_id();
+ let funding_txo_1 = OutPoint { txid: tx.txid(), index: 0 };
+ let funding_txo_2 = OutPoint { txid: tx.txid(), index: 1 };
+ let channel_id_1 = funding_txo_1.to_channel_id();
+ let channel_id_2 = funding_txo_2.to_channel_id();
check_closed_events(&nodes[0], &[
ExpectedCloseEvent {
channel_id: Some(channel_id_1),
discard_funding: true,
+ channel_funding_txo: Some(funding_txo_1),
+ user_channel_id: Some(42),
..Default::default()
},
ExpectedCloseEvent {
channel_id: Some(channel_id_2),
discard_funding: true,
+ channel_funding_txo: Some(funding_txo_2),
+ user_channel_id: Some(43),
..Default::default()
},
]);
assert_eq!(nodes[0].tx_broadcaster.txn_broadcast().len(), 0);
// Force-close the channel for which we've completed the initial monitor.
- let channel_id_1 = OutPoint { txid: tx.txid(), index: 0 }.to_channel_id();
- let channel_id_2 = OutPoint { txid: tx.txid(), index: 1 }.to_channel_id();
+ let funding_txo_1 = OutPoint { txid: tx.txid(), index: 0 };
+ let funding_txo_2 = OutPoint { txid: tx.txid(), index: 1 };
+ let channel_id_1 = funding_txo_1.to_channel_id();
+ let channel_id_2 = funding_txo_2.to_channel_id();
nodes[0].node.force_close_broadcasting_latest_txn(&channel_id_1, &nodes[1].node.get_our_node_id()).unwrap();
check_added_monitors(&nodes[0], 2);
{
ExpectedCloseEvent {
channel_id: Some(channel_id_1),
discard_funding: true,
+ channel_funding_txo: Some(funding_txo_1),
+ user_channel_id: Some(42),
..Default::default()
},
ExpectedCloseEvent {
channel_id: Some(channel_id_2),
discard_funding: true,
+ channel_funding_txo: Some(funding_txo_2),
+ user_channel_id: Some(43),
..Default::default()
},
]);
use crate::ln::{PaymentHash, PaymentPreimage, PaymentSecret};
use crate::ln::msgs;
use crate::ln::msgs::MAX_VALUE_MSAT;
-use crate::util::chacha20::ChaCha20;
-use crate::util::crypto::hkdf_extract_expand_5x;
+use crate::crypto::chacha20::ChaCha20;
+use crate::crypto::utils::hkdf_extract_expand_5x;
use crate::util::errors::APIError;
use crate::util::logger::Logger;
use crate::ln::channelmanager::{BREAKDOWN_TIMEOUT, PaymentId, RecipientOnionFields};
use crate::ln::msgs::ChannelMessageHandler;
use crate::util::config::UserConfig;
-use crate::util::crypto::sign;
+use crate::crypto::utils::sign;
use crate::util::ser::Writeable;
use crate::util::scid_utils::block_from_scid;
use crate::util::test_utils;
use crate::io_extras::read_to_end;
use crate::events::{EventsProvider, MessageSendEventsProvider};
-use crate::util::chacha20poly1305rfc::ChaChaPolyReadAdapter;
+use crate::crypto::streams::ChaChaPolyReadAdapter;
use crate::util::logger;
use crate::util::ser::{LengthReadable, LengthReadableArgs, Readable, ReadableArgs, Writeable, Writer, WithoutLength, FixedLengthReader, HighZeroBytesDroppedBigSize, Hostname, TransactionU16LenLimited, BigSize};
use crate::util::base32;
/// Used in decrypting the onion packet's payload.
pub blinding_point: PublicKey,
/// The full onion packet including hop data, pubkey, and hmac
- pub onion_routing_packet: onion_message::Packet,
+ pub onion_routing_packet: onion_message::packet::Packet,
}
/// An [`update_fulfill_htlc`] message to be sent to or received from a peer.
payment_metadata: Option<Vec<u8>>,
keysend_preimage: Option<PaymentPreimage>,
custom_tlvs: Vec<(u64, Vec<u8>)>,
- amt_msat: u64,
- outgoing_cltv_value: u32,
+ sender_intended_htlc_amt_msat: u64,
+ cltv_expiry_height: u32,
},
BlindedForward {
short_channel_id: u64,
payment_relay: PaymentRelay,
payment_constraints: PaymentConstraints,
features: BlindedHopFeatures,
- intro_node_blinding_point: PublicKey,
+ intro_node_blinding_point: Option<PublicKey>,
},
BlindedReceive {
- amt_msat: u64,
+ sender_intended_htlc_amt_msat: u64,
total_msat: u64,
- outgoing_cltv_value: u32,
+ cltv_expiry_height: u32,
payment_secret: PaymentSecret,
payment_constraints: PaymentConstraints,
intro_node_blinding_point: Option<PublicKey>,
payment_metadata: Option<Vec<u8>>,
keysend_preimage: Option<PaymentPreimage>,
custom_tlvs: Vec<(u64, Vec<u8>)>,
- amt_msat: u64,
- outgoing_cltv_value: u32,
+ sender_intended_htlc_amt_msat: u64,
+ cltv_expiry_height: u32,
},
BlindedForward {
encrypted_tlvs: Vec<u8>,
intro_node_blinding_point: Option<PublicKey>,
},
BlindedReceive {
- amt_msat: u64,
+ sender_intended_htlc_amt_msat: u64,
total_msat: u64,
- outgoing_cltv_value: u32,
+ cltv_expiry_height: u32,
encrypted_tlvs: Vec<u8>,
intro_node_blinding_point: Option<PublicKey>, // Set if the introduction node of the blinded path is the final node
}
let blinding_point: PublicKey = Readable::read(r)?;
let len: u16 = Readable::read(r)?;
let mut packet_reader = FixedLengthReader::new(r, len as u64);
- let onion_routing_packet: onion_message::Packet = <onion_message::Packet as LengthReadable>::read(&mut packet_reader)?;
+ let onion_routing_packet: onion_message::packet::Packet =
+ <onion_message::packet::Packet as LengthReadable>::read(&mut packet_reader)?;
Ok(Self {
blinding_point,
onion_routing_packet,
});
},
Self::Receive {
- ref payment_data, ref payment_metadata, ref keysend_preimage, amt_msat,
- outgoing_cltv_value, ref custom_tlvs,
+ ref payment_data, ref payment_metadata, ref keysend_preimage, sender_intended_htlc_amt_msat,
+ cltv_expiry_height, ref custom_tlvs,
} => {
// We need to update [`ln::outbound_payment::RecipientOnionFields::with_custom_tlvs`]
// to reject any reserved types in the experimental range if new ones are ever
let mut custom_tlvs: Vec<&(u64, Vec<u8>)> = custom_tlvs.iter().chain(keysend_tlv.iter()).collect();
custom_tlvs.sort_unstable_by_key(|(typ, _)| *typ);
_encode_varint_length_prefixed_tlv!(w, {
- (2, HighZeroBytesDroppedBigSize(*amt_msat), required),
- (4, HighZeroBytesDroppedBigSize(*outgoing_cltv_value), required),
+ (2, HighZeroBytesDroppedBigSize(*sender_intended_htlc_amt_msat), required),
+ (4, HighZeroBytesDroppedBigSize(*cltv_expiry_height), required),
(8, payment_data, option),
(16, payment_metadata.as_ref().map(|m| WithoutLength(m)), option)
}, custom_tlvs.iter());
});
},
Self::BlindedReceive {
- amt_msat, total_msat, outgoing_cltv_value, encrypted_tlvs,
+ sender_intended_htlc_amt_msat, total_msat, cltv_expiry_height, encrypted_tlvs,
intro_node_blinding_point,
} => {
_encode_varint_length_prefixed_tlv!(w, {
- (2, HighZeroBytesDroppedBigSize(*amt_msat), required),
- (4, HighZeroBytesDroppedBigSize(*outgoing_cltv_value), required),
+ (2, HighZeroBytesDroppedBigSize(*sender_intended_htlc_amt_msat), required),
+ (4, HighZeroBytesDroppedBigSize(*cltv_expiry_height), required),
(10, *encrypted_tlvs, required_vec),
(12, intro_node_blinding_point, option),
(18, HighZeroBytesDroppedBigSize(*total_msat), required)
payment_relay,
payment_constraints,
features,
- intro_node_blinding_point: intro_node_blinding_point.ok_or(DecodeError::InvalidValue)?,
+ intro_node_blinding_point,
})
},
ChaChaPolyReadAdapter { readable: BlindedPaymentTlvs::Receive(ReceiveTlvs {
})} => {
if total_msat.unwrap_or(0) > MAX_VALUE_MSAT { return Err(DecodeError::InvalidValue) }
Ok(Self::BlindedReceive {
- amt_msat: amt.ok_or(DecodeError::InvalidValue)?,
+ sender_intended_htlc_amt_msat: amt.ok_or(DecodeError::InvalidValue)?,
total_msat: total_msat.ok_or(DecodeError::InvalidValue)?,
- outgoing_cltv_value: cltv_value.ok_or(DecodeError::InvalidValue)?,
+ cltv_expiry_height: cltv_value.ok_or(DecodeError::InvalidValue)?,
payment_secret,
payment_constraints,
intro_node_blinding_point,
payment_data,
payment_metadata: payment_metadata.map(|w| w.0),
keysend_preimage,
- amt_msat: amt.ok_or(DecodeError::InvalidValue)?,
- outgoing_cltv_value: cltv_value.ok_or(DecodeError::InvalidValue)?,
+ sender_intended_htlc_amt_msat: amt.ok_or(DecodeError::InvalidValue)?,
+ cltv_expiry_height: cltv_value.ok_or(DecodeError::InvalidValue)?,
custom_tlvs,
})
}
payment_data: None,
payment_metadata: None,
keysend_preimage: None,
- amt_msat: 0x0badf00d01020304,
- outgoing_cltv_value: 0xffffffff,
+ sender_intended_htlc_amt_msat: 0x0badf00d01020304,
+ cltv_expiry_height: 0xffffffff,
custom_tlvs: vec![],
};
let encoded_value = outbound_msg.encode();
let node_signer = test_utils::TestKeysInterface::new(&[42; 32], Network::Testnet);
let inbound_msg = ReadableArgs::read(&mut Cursor::new(&target_value[..]), (None, &&node_signer)).unwrap();
if let msgs::InboundOnionPayload::Receive {
- payment_data: None, amt_msat, outgoing_cltv_value, ..
+ payment_data: None, sender_intended_htlc_amt_msat, cltv_expiry_height, ..
} = inbound_msg {
- assert_eq!(amt_msat, 0x0badf00d01020304);
- assert_eq!(outgoing_cltv_value, 0xffffffff);
+ assert_eq!(sender_intended_htlc_amt_msat, 0x0badf00d01020304);
+ assert_eq!(cltv_expiry_height, 0xffffffff);
} else { panic!(); }
}
}),
payment_metadata: None,
keysend_preimage: None,
- amt_msat: 0x0badf00d01020304,
- outgoing_cltv_value: 0xffffffff,
+ sender_intended_htlc_amt_msat: 0x0badf00d01020304,
+ cltv_expiry_height: 0xffffffff,
custom_tlvs: vec![],
};
let encoded_value = outbound_msg.encode();
payment_secret,
total_msat: 0x1badca1f
}),
- amt_msat, outgoing_cltv_value,
+ sender_intended_htlc_amt_msat, cltv_expiry_height,
payment_metadata: None,
keysend_preimage: None,
custom_tlvs,
} = inbound_msg {
assert_eq!(payment_secret, expected_payment_secret);
- assert_eq!(amt_msat, 0x0badf00d01020304);
- assert_eq!(outgoing_cltv_value, 0xffffffff);
+ assert_eq!(sender_intended_htlc_amt_msat, 0x0badf00d01020304);
+ assert_eq!(cltv_expiry_height, 0xffffffff);
assert_eq!(custom_tlvs, vec![]);
} else { panic!(); }
}
payment_metadata: None,
keysend_preimage: None,
custom_tlvs: bad_type_range_tlvs,
- amt_msat: 0x0badf00d01020304,
- outgoing_cltv_value: 0xffffffff,
+ sender_intended_htlc_amt_msat: 0x0badf00d01020304,
+ cltv_expiry_height: 0xffffffff,
};
let encoded_value = msg.encode();
let node_signer = test_utils::TestKeysInterface::new(&[42; 32], Network::Testnet);
payment_metadata: None,
keysend_preimage: None,
custom_tlvs: expected_custom_tlvs.clone(),
- amt_msat: 0x0badf00d01020304,
- outgoing_cltv_value: 0xffffffff,
+ sender_intended_htlc_amt_msat: 0x0badf00d01020304,
+ cltv_expiry_height: 0xffffffff,
};
let encoded_value = msg.encode();
let target_value = <Vec<u8>>::from_hex("2e02080badf00d010203040404ffffffffff0000000146c6616b021234ff0000000146c6616f084242424242424242").unwrap();
payment_metadata: None,
keysend_preimage: None,
custom_tlvs,
- amt_msat,
- outgoing_cltv_value,
+ sender_intended_htlc_amt_msat,
+ cltv_expiry_height: outgoing_cltv_value,
..
} = inbound_msg {
assert_eq!(custom_tlvs, expected_custom_tlvs);
- assert_eq!(amt_msat, 0x0badf00d01020304);
+ assert_eq!(sender_intended_htlc_amt_msat, 0x0badf00d01020304);
assert_eq!(outgoing_cltv_value, 0xffffffff);
} else { panic!(); }
}
use crate::blinded_path::payment::{PaymentConstraints, PaymentRelay};
use crate::chain::channelmonitor::{HTLC_FAIL_BACK_BUFFER, LATENCY_GRACE_PERIOD_BLOCKS};
use crate::ln::PaymentHash;
-use crate::ln::channelmanager::{BlindedForward, CLTV_FAR_FAR_AWAY, HTLCFailureMsg, MIN_CLTV_EXPIRY_DELTA, PendingHTLCInfo, PendingHTLCRouting};
+use crate::ln::channelmanager::{BlindedFailure, BlindedForward, CLTV_FAR_FAR_AWAY, HTLCFailureMsg, MIN_CLTV_EXPIRY_DELTA, PendingHTLCInfo, PendingHTLCRouting};
use crate::ln::features::BlindedHopFeatures;
use crate::ln::msgs;
use crate::ln::onion_utils;
/// Invalid inbound onion payment.
#[derive(Debug)]
-pub struct InboundOnionErr {
+pub struct InboundHTLCErr {
/// BOLT 4 error code.
pub err_code: u16,
/// Data attached to this error.
msg: &msgs::UpdateAddHTLC, hop_data: msgs::InboundOnionPayload, hop_hmac: [u8; 32],
new_packet_bytes: [u8; onion_utils::ONION_DATA_LEN], shared_secret: [u8; 32],
next_packet_pubkey_opt: Option<Result<PublicKey, secp256k1::Error>>
-) -> Result<PendingHTLCInfo, InboundOnionErr> {
+) -> Result<PendingHTLCInfo, InboundHTLCErr> {
debug_assert!(next_packet_pubkey_opt.is_some());
let outgoing_packet = msgs::OnionPacket {
version: 0,
};
let (
- short_channel_id, amt_to_forward, outgoing_cltv_value, inbound_blinding_point
+ short_channel_id, amt_to_forward, outgoing_cltv_value, intro_node_blinding_point
) = match hop_data {
msgs::InboundOnionPayload::Forward { short_channel_id, amt_to_forward, outgoing_cltv_value } =>
(short_channel_id, amt_to_forward, outgoing_cltv_value, None),
).map_err(|()| {
// We should be returning malformed here if `msg.blinding_point` is set, but this is
// unreachable right now since we checked it in `decode_update_add_htlc_onion`.
- InboundOnionErr {
+ InboundHTLCErr {
msg: "Underflow calculating outbound amount or cltv value for blinded forward",
err_code: INVALID_ONION_BLINDING,
err_data: vec![0; 32],
}
})?;
- (short_channel_id, amt_to_forward, outgoing_cltv_value, Some(intro_node_blinding_point))
+ (short_channel_id, amt_to_forward, outgoing_cltv_value, intro_node_blinding_point)
},
msgs::InboundOnionPayload::Receive { .. } | msgs::InboundOnionPayload::BlindedReceive { .. } =>
- return Err(InboundOnionErr {
+ return Err(InboundHTLCErr {
msg: "Final Node OnionHopData provided for us as an intermediary node",
err_code: 0x4000 | 22,
err_data: Vec::new(),
routing: PendingHTLCRouting::Forward {
onion_packet: outgoing_packet,
short_channel_id,
- blinded: inbound_blinding_point.map(|bp| BlindedForward { inbound_blinding_point: bp }),
+ blinded: intro_node_blinding_point.or(msg.blinding_point)
+ .map(|bp| BlindedForward {
+ inbound_blinding_point: bp,
+ failure: intro_node_blinding_point
+ .map(|_| BlindedFailure::FromIntroductionNode)
+ .unwrap_or(BlindedFailure::FromBlindedNode),
+ }),
},
payment_hash: msg.payment_hash,
incoming_shared_secret: shared_secret,
hop_data: msgs::InboundOnionPayload, shared_secret: [u8; 32], payment_hash: PaymentHash,
amt_msat: u64, cltv_expiry: u32, phantom_shared_secret: Option<[u8; 32]>, allow_underpay: bool,
counterparty_skimmed_fee_msat: Option<u64>, current_height: u32, accept_mpp_keysend: bool,
-) -> Result<PendingHTLCInfo, InboundOnionErr> {
+) -> Result<PendingHTLCInfo, InboundHTLCErr> {
let (
- payment_data, keysend_preimage, custom_tlvs, onion_amt_msat, outgoing_cltv_value,
+ payment_data, keysend_preimage, custom_tlvs, onion_amt_msat, onion_cltv_expiry,
payment_metadata, requires_blinded_error
) = match hop_data {
msgs::InboundOnionPayload::Receive {
- payment_data, keysend_preimage, custom_tlvs, amt_msat, outgoing_cltv_value, payment_metadata, ..
+ payment_data, keysend_preimage, custom_tlvs, sender_intended_htlc_amt_msat,
+ cltv_expiry_height, payment_metadata, ..
} =>
- (payment_data, keysend_preimage, custom_tlvs, amt_msat, outgoing_cltv_value, payment_metadata,
- false),
+ (payment_data, keysend_preimage, custom_tlvs, sender_intended_htlc_amt_msat,
+ cltv_expiry_height, payment_metadata, false),
msgs::InboundOnionPayload::BlindedReceive {
- amt_msat, total_msat, outgoing_cltv_value, payment_secret, intro_node_blinding_point,
- payment_constraints, ..
+ sender_intended_htlc_amt_msat, total_msat, cltv_expiry_height, payment_secret,
+ intro_node_blinding_point, payment_constraints, ..
} => {
- check_blinded_payment_constraints(amt_msat, cltv_expiry, &payment_constraints)
+ check_blinded_payment_constraints(
+ sender_intended_htlc_amt_msat, cltv_expiry, &payment_constraints
+ )
.map_err(|()| {
- InboundOnionErr {
+ InboundHTLCErr {
err_code: INVALID_ONION_BLINDING,
err_data: vec![0; 32],
msg: "Amount or cltv_expiry violated blinded payment constraints",
}
})?;
let payment_data = msgs::FinalOnionHopData { payment_secret, total_msat };
- (Some(payment_data), None, Vec::new(), amt_msat, outgoing_cltv_value, None,
- intro_node_blinding_point.is_none())
+ (Some(payment_data), None, Vec::new(), sender_intended_htlc_amt_msat, cltv_expiry_height,
+ None, intro_node_blinding_point.is_none())
}
msgs::InboundOnionPayload::Forward { .. } => {
- return Err(InboundOnionErr {
+ return Err(InboundHTLCErr {
err_code: 0x4000|22,
err_data: Vec::new(),
msg: "Got non final data with an HMAC of 0",
})
},
msgs::InboundOnionPayload::BlindedForward { .. } => {
- return Err(InboundOnionErr {
+ return Err(InboundHTLCErr {
err_code: INVALID_ONION_BLINDING,
err_data: vec![0; 32],
msg: "Got blinded non final data with an HMAC of 0",
}
};
// final_incorrect_cltv_expiry
- if outgoing_cltv_value > cltv_expiry {
- return Err(InboundOnionErr {
+ if onion_cltv_expiry > cltv_expiry {
+ return Err(InboundHTLCErr {
msg: "Upstream node set CLTV to less than the CLTV set by the sender",
err_code: 18,
err_data: cltv_expiry.to_be_bytes().to_vec()
let mut err_data = Vec::with_capacity(12);
err_data.extend_from_slice(&amt_msat.to_be_bytes());
err_data.extend_from_slice(¤t_height.to_be_bytes());
- return Err(InboundOnionErr {
+ return Err(InboundHTLCErr {
err_code: 0x4000 | 15, err_data,
msg: "The final CLTV expiry is too soon to handle",
});
(allow_underpay && onion_amt_msat >
amt_msat.saturating_add(counterparty_skimmed_fee_msat.unwrap_or(0)))
{
- return Err(InboundOnionErr {
+ return Err(InboundHTLCErr {
err_code: 19,
err_data: amt_msat.to_be_bytes().to_vec(),
msg: "Upstream node sent less than we were supposed to receive in payment",
// time discrepancies due to a hash collision with X.
let hashed_preimage = PaymentHash(Sha256::hash(&payment_preimage.0).to_byte_array());
if hashed_preimage != payment_hash {
- return Err(InboundOnionErr {
+ return Err(InboundHTLCErr {
err_code: 0x4000|22,
err_data: Vec::new(),
msg: "Payment preimage didn't match payment hash",
});
}
if !accept_mpp_keysend && payment_data.is_some() {
- return Err(InboundOnionErr {
+ return Err(InboundHTLCErr {
err_code: 0x4000|22,
err_data: Vec::new(),
msg: "We don't support MPP keysend payments",
payment_data,
payment_preimage,
payment_metadata,
- incoming_cltv_expiry: outgoing_cltv_value,
+ incoming_cltv_expiry: onion_cltv_expiry,
custom_tlvs,
}
} else if let Some(data) = payment_data {
PendingHTLCRouting::Receive {
payment_data: data,
payment_metadata,
- incoming_cltv_expiry: outgoing_cltv_value,
+ incoming_cltv_expiry: onion_cltv_expiry,
phantom_shared_secret,
custom_tlvs,
requires_blinded_error,
}
} else {
- return Err(InboundOnionErr {
+ return Err(InboundHTLCErr {
err_code: 0x4000|0x2000|3,
err_data: Vec::new(),
msg: "We require payment_secrets",
incoming_shared_secret: shared_secret,
incoming_amt_msat: Some(amt_msat),
outgoing_amt_msat: onion_amt_msat,
- outgoing_cltv_value,
+ outgoing_cltv_value: onion_cltv_expiry,
skimmed_fee_msat: counterparty_skimmed_fee_msat,
})
}
pub fn peel_payment_onion<NS: Deref, L: Deref, T: secp256k1::Verification>(
msg: &msgs::UpdateAddHTLC, node_signer: &NS, logger: &L, secp_ctx: &Secp256k1<T>,
cur_height: u32, accept_mpp_keysend: bool, allow_skimmed_fees: bool,
-) -> Result<PendingHTLCInfo, InboundOnionErr>
+) -> Result<PendingHTLCInfo, InboundHTLCErr>
where
NS::Target: NodeSigner,
L::Target: Logger,
HTLCFailureMsg::Relay(r) => (0x4000 | 22, r.reason.data),
};
let msg = "Failed to decode update add htlc onion";
- InboundOnionErr { msg, err_code, err_data }
+ InboundHTLCErr { msg, err_code, err_data }
})?;
Ok(match hop {
onion_utils::Hop::Forward { next_hop_data, next_hop_hmac, new_packet_bytes } => {
} = match next_packet_details_opt {
Some(next_packet_details) => next_packet_details,
// Forward should always include the next hop details
- None => return Err(InboundOnionErr {
+ None => return Err(InboundHTLCErr {
msg: "Failed to decode update add htlc onion",
err_code: 0x4000 | 22,
err_data: Vec::new(),
if let Err((err_msg, code)) = check_incoming_htlc_cltv(
cur_height, outgoing_cltv_value, msg.cltv_expiry
) {
- return Err(InboundOnionErr {
+ return Err(InboundHTLCErr {
msg: err_msg,
err_code: code,
err_data: Vec::new(),
use crate::routing::gossip::NetworkUpdate;
use crate::routing::router::{BlindedTail, Path, RouteHop};
use crate::sign::NodeSigner;
-use crate::util::chacha20::{ChaCha20, ChaChaReader};
+use crate::crypto::chacha20::ChaCha20;
+use crate::crypto::streams::ChaChaReader;
use crate::util::errors::{self, APIError};
use crate::util::ser::{Readable, ReadableArgs, Writeable, Writer, LengthCalculatingWriter};
use crate::util::logger::Logger;
for (i, blinded_hop) in hops.iter().enumerate() {
if i == hops.len() - 1 {
cur_value_msat += final_value_msat;
- cur_cltv += excess_final_cltv_expiry_delta;
res.push(msgs::OutboundOnionPayload::BlindedReceive {
- amt_msat: *final_value_msat,
+ sender_intended_htlc_amt_msat: *final_value_msat,
total_msat,
- outgoing_cltv_value: cltv,
+ cltv_expiry_height: cur_cltv + excess_final_cltv_expiry_delta,
encrypted_tlvs: blinded_hop.encrypted_payload.clone(),
intro_node_blinding_point: blinding_point.take(),
});
payment_metadata: recipient_onion.payment_metadata.take(),
keysend_preimage: *keysend_preimage,
custom_tlvs: recipient_onion.custom_tlvs.clone(),
- amt_msat: value_msat,
- outgoing_cltv_value: cltv,
+ sender_intended_htlc_amt_msat: value_msat,
+ cltv_expiry_height: cltv,
});
}
} else {
use hex::DisplayHex;
-use crate::util::chacha20poly1305rfc::ChaCha20Poly1305RFC;
-use crate::util::crypto::hkdf_extract_expand_twice;
+use crate::crypto::chacha20poly1305rfc::ChaCha20Poly1305RFC;
+use crate::crypto::utils::hkdf_extract_expand_twice;
use crate::util::ser::VecWriter;
use core::ops::Deref;
nonce[4..].copy_from_slice(&n.to_le_bytes()[..]);
let mut chacha = ChaCha20Poly1305RFC::new(key, &nonce, h);
- if !chacha.decrypt(&cyphertext[0..cyphertext.len() - 16], res, &cyphertext[cyphertext.len() - 16..]) {
+ if chacha.variable_time_decrypt(&cyphertext[0..cyphertext.len() - 16], res, &cyphertext[cyphertext.len() - 16..]).is_err() {
return Err(LightningError{err: "Bad MAC".to_owned(), action: msgs::ErrorAction::DisconnectPeer{ msg: None }});
}
Ok(())
use crate::ln::wire;
use crate::ln::wire::{Encode, Type};
#[cfg(not(c_bindings))]
-use crate::onion_message::{SimpleArcOnionMessenger, SimpleRefOnionMessenger};
-use crate::onion_message::{CustomOnionMessageHandler, OffersMessage, OffersMessageHandler, OnionMessageContents, PendingOnionMessage};
+use crate::onion_message::messenger::{SimpleArcOnionMessenger, SimpleRefOnionMessenger};
+use crate::onion_message::messenger::{CustomOnionMessageHandler, PendingOnionMessage};
+use crate::onion_message::offers::{OffersMessage, OffersMessageHandler};
+use crate::onion_message::packet::OnionMessageContents;
use crate::routing::gossip::{NodeId, NodeAlias};
use crate::util::atomic_counter::AtomicCounter;
use crate::util::logger::{Logger, WithContext};
features.set_channel_type_optional();
features.set_scid_privacy_optional();
features.set_zero_conf_optional();
+ features.set_route_blinding_optional();
features
}
/// A message handler which handles onion messages. This should generally be an
/// [`OnionMessenger`], but can also be an [`IgnoringMessageHandler`].
///
- /// [`OnionMessenger`]: crate::onion_message::OnionMessenger
+ /// [`OnionMessenger`]: crate::onion_message::messenger::OnionMessenger
pub onion_message_handler: OM,
/// A message handler which handles custom messages. The only LDK-provided implementation is
}
if let wire::Message::GossipTimestampFilter(_msg) = message {
- // When supporting gossip messages, start inital gossip sync only after we receive
+ // When supporting gossip messages, start initial gossip sync only after we receive
// a GossipTimestampFilter
if peer_lock.their_features.as_ref().unwrap().supports_gossip_queries() &&
!peer_lock.sent_gossip_timestamp_filter {
log_pubkey!(node_id));
}
// We do not have the peers write lock, so we just store that we're
- // about to disconenct the peer and do it after we finish
+ // about to disconnect the peer and do it after we finish
// processing most messages.
let msg = msg.map(|msg| wire::Message::<<<CMH as core::ops::Deref>::Target as wire::CustomMessageReader>::CustomMessage>::Error(msg));
peers_to_disconnect.insert(node_id, msg);
log_trace!(logger, "Handling DisconnectPeer HandleError event in peer_handler for node {} with message {}",
log_pubkey!(node_id), msg.data);
// We do not have the peers write lock, so we just store that we're
- // about to disconenct the peer and do it after we finish
+ // about to disconnect the peer and do it after we finish
// processing most messages.
peers_to_disconnect.insert(node_id, Some(wire::Message::Warning(msg)));
},
use crate::sign::{EntropySource, NodeSigner, Recipient};
use crate::util::ser::{FixedLengthReader, LengthReadable, Writeable, Writer};
use crate::util::test_utils;
-use super::{CustomOnionMessageHandler, Destination, MessageRouter, OffersMessage, OffersMessageHandler, OnionMessageContents, OnionMessagePath, OnionMessenger, PendingOnionMessage, SendError};
+use super::messenger::{CustomOnionMessageHandler, Destination, MessageRouter, OnionMessagePath, OnionMessenger, PendingOnionMessage, SendError};
+use super::offers::{OffersMessage, OffersMessageHandler};
+use super::packet::{OnionMessageContents, Packet};
use bitcoin::network::constants::Network;
use bitcoin::hashes::hex::FromHex;
let sender_to_alice_packet_bytes_len = sender_to_alice_packet_bytes.len() as u64;
let mut reader = io::Cursor::new(sender_to_alice_packet_bytes);
let mut packet_reader = FixedLengthReader::new(&mut reader, sender_to_alice_packet_bytes_len);
- let sender_to_alice_packet: super::Packet =
- <super::Packet as LengthReadable>::read(&mut packet_reader).unwrap();
+ let sender_to_alice_packet: Packet =
+ <Packet as LengthReadable>::read(&mut packet_reader).unwrap();
let secp_ctx = Secp256k1::new();
let sender_to_alice_om = msgs::OnionMessage {
blinding_point: PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&<Vec<u8>>::from_hex("6363636363636363636363636363636363636363636363636363636363636363").unwrap()).unwrap()),
// You may not use this file except in accordance with one or both of these
// licenses.
-//! LDK sends, receives, and forwards onion messages via the [`OnionMessenger`]. See its docs for
-//! more information.
+//! LDK sends, receives, and forwards onion messages via this [`OnionMessenger`], which lives here,
+//! as well as various types, traits, and utilities that it uses.
use bitcoin::hashes::{Hash, HashEngine};
use bitcoin::hashes::hmac::{Hmac, HmacEngine};
use crate::ln::msgs::{self, OnionMessage, OnionMessageHandler, SocketAddress};
use crate::ln::onion_utils;
use crate::routing::gossip::{NetworkGraph, NodeId};
-pub use super::packet::OnionMessageContents;
+use super::packet::OnionMessageContents;
use super::packet::ParsedOnionMessageContents;
use super::offers::OffersMessageHandler;
use super::packet::{BIG_PACKET_HOP_DATA_LEN, ForwardControlTlvs, Packet, Payload, ReceiveControlTlvs, SMALL_PACKET_HOP_DATA_LEN};
/// # use lightning::blinded_path::BlindedPath;
/// # use lightning::sign::{EntropySource, KeysManager};
/// # use lightning::ln::peer_handler::IgnoringMessageHandler;
-/// # use lightning::onion_message::{OnionMessageContents, Destination, MessageRouter, OnionMessagePath, OnionMessenger};
+/// # use lightning::onion_message::messenger::{Destination, MessageRouter, OnionMessagePath, OnionMessenger};
+/// # use lightning::onion_message::packet::OnionMessageContents;
/// # use lightning::util::logger::{Logger, Record};
/// # use lightning::util::ser::{Writeable, Writer};
/// # use lightning::io;
//!
//! [offers]: <https://github.com/lightning/bolts/pull/798>
//! [blinded paths]: crate::blinded_path::BlindedPath
+//! [`OnionMessenger`]: self::messenger::OnionMessenger
-mod messenger;
-mod offers;
-mod packet;
+pub mod messenger;
+pub mod offers;
+pub mod packet;
#[cfg(test)]
mod functional_tests;
-
-// Re-export structs so they can be imported with just the `onion_message::` module prefix.
-pub use self::messenger::{CustomOnionMessageHandler, DefaultMessageRouter, Destination, MessageRouter, OnionMessageContents, OnionMessagePath, OnionMessenger, PeeledOnion, PendingOnionMessage, SendError};
-pub use self::messenger::{create_onion_message, peel_onion_message};
-#[cfg(not(c_bindings))]
-pub use self::messenger::{SimpleArcOnionMessenger, SimpleRefOnionMessenger};
-pub use self::offers::{OffersMessage, OffersMessageHandler};
-pub use self::packet::{Packet, ParsedOnionMessageContents};
-pub(crate) use self::packet::ControlTlvs;
-pub(crate) use self::messenger::new_pending_onion_message;
use crate::offers::invoice_request::InvoiceRequest;
use crate::offers::invoice::Bolt12Invoice;
use crate::offers::parse::Bolt12ParseError;
-use crate::onion_message::OnionMessageContents;
+use crate::onion_message::packet::OnionMessageContents;
use crate::util::logger::Logger;
use crate::util::ser::{Readable, ReadableArgs, Writeable, Writer};
#[cfg(not(c_bindings))]
///
/// The returned [`OffersMessage`], if any, is enqueued to be sent by [`OnionMessenger`].
///
- /// [`OnionMessenger`]: crate::onion_message::OnionMessenger
+ /// [`OnionMessenger`]: crate::onion_message::messenger::OnionMessenger
fn handle_message(&self, message: OffersMessage) -> Option<OffersMessage>;
/// Releases any [`OffersMessage`]s that need to be sent.
/// Typically, this is used for messages initiating a payment flow rather than in response to
/// another message. The latter should use the return value of [`Self::handle_message`].
#[cfg(c_bindings)]
- fn release_pending_messages(&self) -> Vec<(OffersMessage, crate::onion_message::Destination, Option<crate::blinded_path::BlindedPath>)> { vec![] }
+ fn release_pending_messages(&self) -> Vec<(OffersMessage, crate::onion_message::messenger::Destination, Option<crate::blinded_path::BlindedPath>)> { vec![] }
}
/// Possible BOLT 12 Offers messages sent and received via an [`OnionMessage`].
use crate::ln::onion_utils;
use super::messenger::CustomOnionMessageHandler;
use super::offers::OffersMessage;
-use crate::util::chacha20poly1305rfc::{ChaChaPolyReadAdapter, ChaChaPolyWriteAdapter};
+use crate::crypto::streams::{ChaChaPolyReadAdapter, ChaChaPolyWriteAdapter};
use crate::util::logger::Logger;
use crate::util::ser::{BigSize, FixedLengthReader, LengthRead, LengthReadable, LengthReadableArgs, Readable, ReadableArgs, Writeable, Writer};
use crate::ln::features::{BlindedHopFeatures, Bolt11InvoiceFeatures, Bolt12InvoiceFeatures, ChannelFeatures, NodeFeatures};
use crate::ln::msgs::{DecodeError, ErrorAction, LightningError, MAX_VALUE_MSAT};
use crate::offers::invoice::{BlindedPayInfo, Bolt12Invoice};
-use crate::onion_message::{DefaultMessageRouter, Destination, MessageRouter, OnionMessagePath};
+use crate::onion_message::messenger::{DefaultMessageRouter, Destination, MessageRouter, OnionMessagePath};
use crate::routing::gossip::{DirectedChannelInfo, EffectiveCapacity, ReadOnlyNetworkGraph, NetworkGraph, NodeId, RoutingFees};
use crate::routing::scoring::{ChannelUsage, LockableScore, ScoreLookUp};
use crate::sign::EntropySource;
use crate::util::ser::{Writeable, Readable, ReadableArgs, Writer};
use crate::util::logger::{Level, Logger};
-use crate::util::chacha20::ChaCha20;
+use crate::crypto::chacha20::ChaCha20;
use crate::io;
use crate::prelude::*;
None => return None,
};
let payment_relay: PaymentRelay = match details.counterparty.forwarding_info {
- Some(forwarding_info) => forwarding_info.into(),
+ Some(forwarding_info) => match forwarding_info.try_into() {
+ Ok(payment_relay) => payment_relay,
+ Err(()) => return None,
+ },
None => return None,
};
- // Avoid exposing esoteric CLTV expiry deltas
- let cltv_expiry_delta = match payment_relay.cltv_expiry_delta {
- 0..=40 => 40u32,
- 41..=80 => 80u32,
- 81..=144 => 144u32,
- 145..=216 => 216u32,
- _ => return None,
- };
-
+ let cltv_expiry_delta = payment_relay.cltv_expiry_delta as u32;
let payment_constraints = PaymentConstraints {
max_cltv_expiry: tlvs.payment_constraints.max_cltv_expiry + cltv_expiry_delta,
htlc_minimum_msat: details.inbound_htlc_minimum_msat.unwrap_or(0),
}
}
- // Means we succesfully traversed from the payer to the payee, now
+ // Means we successfully traversed from the payer to the payee, now
// save this path for the payment route. Also, update the liquidity
// remaining on the used hops, so that we take them into account
// while looking for more paths.
use crate::offers::invoice::BlindedPayInfo;
use crate::util::config::UserConfig;
use crate::util::test_utils as ln_test_utils;
- use crate::util::chacha20::ChaCha20;
+ use crate::crypto::chacha20::ChaCha20;
use crate::util::ser::{Readable, Writeable};
#[cfg(c_bindings)]
use crate::util::ser::Writer;
fn do_min_htlc_overpay_violates_max_htlc(blinded_payee: bool) {
// Test that if overpaying to meet a later hop's min_htlc and causes us to violate an earlier
// hop's max_htlc, we don't consider that candidate hop valid. Previously we would add this hop
- // to `targets` and build an invalid path with it, and subsquently hit a debug panic asserting
+ // to `targets` and build an invalid path with it, and subsequently hit a debug panic asserting
// that the used liquidity for a hop was less than its available liquidity limit.
let secp_ctx = Secp256k1::new();
let logger = Arc::new(ln_test_utils::TestLogger::new());
}
break;
}
- // If we couldn't find a path with a higer amount, reduce and try again.
+ // If we couldn't find a path with a higher amount, reduce and try again.
score_amt /= 100;
}
use bitcoin::{secp256k1, Sequence, Witness, Txid};
use crate::util::transaction_utils;
-use crate::util::crypto::{hkdf_extract_expand_twice, sign, sign_with_aux_rand};
+use crate::crypto::utils::{hkdf_extract_expand_twice, sign, sign_with_aux_rand};
use crate::util::ser::{Writeable, Writer, Readable, ReadableArgs};
use crate::chain::transaction::OutPoint;
use crate::ln::channel::ANCHOR_OUTPUT_VALUE_SATOSHI;
#[cfg(taproot)]
use crate::sign::taproot::TaprootChannelSigner;
use crate::util::atomic_counter::AtomicCounter;
-use crate::util::chacha20::ChaCha20;
+use crate::crypto::chacha20::ChaCha20;
use crate::util::invoice::construct_invoice_preimage;
pub(crate) mod type_resolver;
+++ /dev/null
-// This file was stolen from rust-crypto.
-// Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT
-// file at the top-level directory of this distribution and at
-// http://rust-lang.org/COPYRIGHT.
-//
-// This file is licensed under the Apache License, Version 2.0 <LICENSE-APACHE
-// or http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your option.
-// You may not use this file except in accordance with one or both of these
-// licenses.
-
-use crate::io;
-
-#[cfg(not(fuzzing))]
-mod real_chacha {
- use core::cmp;
- use core::convert::TryInto;
-
- #[derive(Clone, Copy, PartialEq, Eq)]
- #[allow(non_camel_case_types)]
- struct u32x4(pub u32, pub u32, pub u32, pub u32);
- impl ::core::ops::Add for u32x4 {
- type Output = u32x4;
- #[inline]
- fn add(self, rhs: u32x4) -> u32x4 {
- u32x4(self.0.wrapping_add(rhs.0),
- self.1.wrapping_add(rhs.1),
- self.2.wrapping_add(rhs.2),
- self.3.wrapping_add(rhs.3))
- }
- }
- impl ::core::ops::Sub for u32x4 {
- type Output = u32x4;
- #[inline]
- fn sub(self, rhs: u32x4) -> u32x4 {
- u32x4(self.0.wrapping_sub(rhs.0),
- self.1.wrapping_sub(rhs.1),
- self.2.wrapping_sub(rhs.2),
- self.3.wrapping_sub(rhs.3))
- }
- }
- impl ::core::ops::BitXor for u32x4 {
- type Output = u32x4;
- #[inline]
- fn bitxor(self, rhs: u32x4) -> u32x4 {
- u32x4(self.0 ^ rhs.0, self.1 ^ rhs.1, self.2 ^ rhs.2, self.3 ^ rhs.3)
- }
- }
- impl ::core::ops::Shr<u8> for u32x4 {
- type Output = u32x4;
- #[inline]
- fn shr(self, shr: u8) -> u32x4 {
- u32x4(self.0 >> shr, self.1 >> shr, self.2 >> shr, self.3 >> shr)
- }
- }
- impl ::core::ops::Shl<u8> for u32x4 {
- type Output = u32x4;
- #[inline]
- fn shl(self, shl: u8) -> u32x4 {
- u32x4(self.0 << shl, self.1 << shl, self.2 << shl, self.3 << shl)
- }
- }
- impl u32x4 {
- #[inline]
- fn from_bytes(bytes: &[u8]) -> Self {
- assert_eq!(bytes.len(), 4*4);
- Self (
- u32::from_le_bytes(bytes[0*4..1*4].try_into().expect("len is 4")),
- u32::from_le_bytes(bytes[1*4..2*4].try_into().expect("len is 4")),
- u32::from_le_bytes(bytes[2*4..3*4].try_into().expect("len is 4")),
- u32::from_le_bytes(bytes[3*4..4*4].try_into().expect("len is 4")),
- )
- }
- }
-
- const BLOCK_SIZE: usize = 64;
-
- #[derive(Clone,Copy)]
- struct ChaChaState {
- a: u32x4,
- b: u32x4,
- c: u32x4,
- d: u32x4
- }
-
- #[derive(Copy)]
- pub struct ChaCha20 {
- state : ChaChaState,
- output : [u8; BLOCK_SIZE],
- offset : usize,
- }
-
- impl Clone for ChaCha20 { fn clone(&self) -> ChaCha20 { *self } }
-
- macro_rules! swizzle {
- ($b: expr, $c: expr, $d: expr) => {{
- let u32x4(b10, b11, b12, b13) = $b;
- $b = u32x4(b11, b12, b13, b10);
- let u32x4(c10, c11, c12, c13) = $c;
- $c = u32x4(c12, c13,c10, c11);
- let u32x4(d10, d11, d12, d13) = $d;
- $d = u32x4(d13, d10, d11, d12);
- }}
- }
-
- macro_rules! state_to_buffer {
- ($state: expr, $output: expr) => {{
- let u32x4(a1, a2, a3, a4) = $state.a;
- let u32x4(b1, b2, b3, b4) = $state.b;
- let u32x4(c1, c2, c3, c4) = $state.c;
- let u32x4(d1, d2, d3, d4) = $state.d;
- let lens = [
- a1,a2,a3,a4,
- b1,b2,b3,b4,
- c1,c2,c3,c4,
- d1,d2,d3,d4
- ];
- for i in 0..lens.len() {
- $output[i*4..(i+1)*4].copy_from_slice(&lens[i].to_le_bytes());
- }
- }}
- }
-
- macro_rules! round{
- ($state: expr) => {{
- $state.a = $state.a + $state.b;
- rotate!($state.d, $state.a, 16);
- $state.c = $state.c + $state.d;
- rotate!($state.b, $state.c, 12);
- $state.a = $state.a + $state.b;
- rotate!($state.d, $state.a, 8);
- $state.c = $state.c + $state.d;
- rotate!($state.b, $state.c, 7);
- }}
- }
-
- macro_rules! rotate {
- ($a: expr, $b: expr, $rot: expr) => {{
- let v = $a ^ $b;
- let r = 32 - $rot;
- let right = v >> r;
- $a = (v << $rot) ^ right
- }}
- }
-
- impl ChaCha20 {
- pub fn new(key: &[u8], nonce: &[u8]) -> ChaCha20 {
- assert!(key.len() == 16 || key.len() == 32);
- assert!(nonce.len() == 8 || nonce.len() == 12);
-
- ChaCha20{ state: ChaCha20::expand(key, nonce), output: [0u8; BLOCK_SIZE], offset: 64 }
- }
-
- /// Get one block from a ChaCha stream.
- pub fn get_single_block(key: &[u8; 32], nonce: &[u8; 16]) -> [u8; 32] {
- let mut chacha = ChaCha20 { state: ChaCha20::expand(key, nonce), output: [0u8; BLOCK_SIZE], offset: 64 };
- let mut chacha_bytes = [0; 32];
- chacha.process_in_place(&mut chacha_bytes);
- chacha_bytes
- }
-
- /// Encrypts `src` into `dest` using a single block from a ChaCha stream. Passing `dest` as
- /// `src` in a second call will decrypt it.
- pub fn encrypt_single_block(
- key: &[u8; 32], nonce: &[u8; 16], dest: &mut [u8], src: &[u8]
- ) {
- debug_assert_eq!(dest.len(), src.len());
- debug_assert!(dest.len() <= 32);
-
- let block = ChaCha20::get_single_block(key, nonce);
- for i in 0..dest.len() {
- dest[i] = block[i] ^ src[i];
- }
- }
-
- /// Same as `encrypt_single_block` only operates on a fixed-size input in-place.
- pub fn encrypt_single_block_in_place(
- key: &[u8; 32], nonce: &[u8; 16], bytes: &mut [u8; 32]
- ) {
- let block = ChaCha20::get_single_block(key, nonce);
- for i in 0..bytes.len() {
- bytes[i] = block[i] ^ bytes[i];
- }
- }
-
- fn expand(key: &[u8], nonce: &[u8]) -> ChaChaState {
- let constant = match key.len() {
- 16 => b"expand 16-byte k",
- 32 => b"expand 32-byte k",
- _ => unreachable!(),
- };
- ChaChaState {
- a: u32x4::from_bytes(&constant[0..16]),
- b: u32x4::from_bytes(&key[0..16]),
- c: if key.len() == 16 {
- u32x4::from_bytes(&key[0..16])
- } else {
- u32x4::from_bytes(&key[16..32])
- },
- d: if nonce.len() == 16 {
- u32x4::from_bytes(&nonce[0..16])
- } else if nonce.len() == 12 {
- let mut nonce4 = [0; 4*4];
- nonce4[4..].copy_from_slice(nonce);
- u32x4::from_bytes(&nonce4)
- } else {
- let mut nonce4 = [0; 4*4];
- nonce4[8..].copy_from_slice(nonce);
- u32x4::from_bytes(&nonce4)
- }
- }
- }
-
- // put the the next BLOCK_SIZE keystream bytes into self.output
- fn update(&mut self) {
- let mut state = self.state;
-
- for _ in 0..10 {
- round!(state);
- swizzle!(state.b, state.c, state.d);
- round!(state);
- swizzle!(state.d, state.c, state.b);
- }
- state.a = state.a + self.state.a;
- state.b = state.b + self.state.b;
- state.c = state.c + self.state.c;
- state.d = state.d + self.state.d;
-
- state_to_buffer!(state, self.output);
-
- self.state.d = self.state.d + u32x4(1, 0, 0, 0);
- let u32x4(c12, _, _, _) = self.state.d;
- if c12 == 0 {
- // we could increment the other counter word with an 8 byte nonce
- // but other implementations like boringssl have this same
- // limitation
- panic!("counter is exhausted");
- }
-
- self.offset = 0;
- }
-
- #[inline] // Useful cause input may be 0s on stack that should be optimized out
- pub fn process(&mut self, input: &[u8], output: &mut [u8]) {
- assert!(input.len() == output.len());
- let len = input.len();
- let mut i = 0;
- while i < len {
- // If there is no keystream available in the output buffer,
- // generate the next block.
- if self.offset == BLOCK_SIZE {
- self.update();
- }
-
- // Process the min(available keystream, remaining input length).
- let count = cmp::min(BLOCK_SIZE - self.offset, len - i);
- // explicitly assert lengths to avoid bounds checks:
- assert!(output.len() >= i + count);
- assert!(input.len() >= i + count);
- assert!(self.output.len() >= self.offset + count);
- for j in 0..count {
- output[i + j] = input[i + j] ^ self.output[self.offset + j];
- }
- i += count;
- self.offset += count;
- }
- }
-
- pub fn process_in_place(&mut self, input_output: &mut [u8]) {
- let len = input_output.len();
- let mut i = 0;
- while i < len {
- // If there is no keystream available in the output buffer,
- // generate the next block.
- if self.offset == BLOCK_SIZE {
- self.update();
- }
-
- // Process the min(available keystream, remaining input length).
- let count = cmp::min(BLOCK_SIZE - self.offset, len - i);
- // explicitly assert lengths to avoid bounds checks:
- assert!(input_output.len() >= i + count);
- assert!(self.output.len() >= self.offset + count);
- for j in 0..count {
- input_output[i + j] ^= self.output[self.offset + j];
- }
- i += count;
- self.offset += count;
- }
- }
-
- #[cfg(test)]
- pub fn seek_to_block(&mut self, block_offset: u32) {
- self.state.d.0 = block_offset;
- self.update();
- }
- }
-}
-#[cfg(not(fuzzing))]
-pub use self::real_chacha::ChaCha20;
-
-#[cfg(fuzzing)]
-mod fuzzy_chacha {
- pub struct ChaCha20 {}
-
- impl ChaCha20 {
- pub fn new(key: &[u8], nonce: &[u8]) -> ChaCha20 {
- assert!(key.len() == 16 || key.len() == 32);
- assert!(nonce.len() == 8 || nonce.len() == 12);
- Self {}
- }
-
- pub fn get_single_block(_key: &[u8; 32], _nonce: &[u8; 16]) -> [u8; 32] {
- [0; 32]
- }
-
- pub fn encrypt_single_block(
- _key: &[u8; 32], _nonce: &[u8; 16], dest: &mut [u8], src: &[u8]
- ) {
- debug_assert_eq!(dest.len(), src.len());
- debug_assert!(dest.len() <= 32);
- }
-
- pub fn encrypt_single_block_in_place(
- _key: &[u8; 32], _nonce: &[u8; 16], _bytes: &mut [u8; 32]
- ) {}
-
- pub fn process(&mut self, input: &[u8], output: &mut [u8]) {
- output.copy_from_slice(input);
- }
-
- pub fn process_in_place(&mut self, _input_output: &mut [u8]) {}
- }
-}
-#[cfg(fuzzing)]
-pub use self::fuzzy_chacha::ChaCha20;
-
-pub(crate) struct ChaChaReader<'a, R: io::Read> {
- pub chacha: &'a mut ChaCha20,
- pub read: R,
-}
-impl<'a, R: io::Read> io::Read for ChaChaReader<'a, R> {
- fn read(&mut self, dest: &mut [u8]) -> Result<usize, io::Error> {
- let res = self.read.read(dest)?;
- if res > 0 {
- self.chacha.process_in_place(&mut dest[0..res]);
- }
- Ok(res)
- }
-}
-
-#[cfg(test)]
-mod test {
- use crate::prelude::*;
- use core::iter::repeat;
-
- use super::ChaCha20;
- use std::convert::TryInto;
-
- #[test]
- fn test_chacha20_256_tls_vectors() {
- struct TestVector {
- key: [u8; 32],
- nonce: [u8; 8],
- keystream: Vec<u8>,
- }
- // taken from http://tools.ietf.org/html/draft-agl-tls-chacha20poly1305-04
- let test_vectors = vec!(
- TestVector{
- key: [
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- ],
- nonce: [ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ],
- keystream: vec!(
- 0x76, 0xb8, 0xe0, 0xad, 0xa0, 0xf1, 0x3d, 0x90,
- 0x40, 0x5d, 0x6a, 0xe5, 0x53, 0x86, 0xbd, 0x28,
- 0xbd, 0xd2, 0x19, 0xb8, 0xa0, 0x8d, 0xed, 0x1a,
- 0xa8, 0x36, 0xef, 0xcc, 0x8b, 0x77, 0x0d, 0xc7,
- 0xda, 0x41, 0x59, 0x7c, 0x51, 0x57, 0x48, 0x8d,
- 0x77, 0x24, 0xe0, 0x3f, 0xb8, 0xd8, 0x4a, 0x37,
- 0x6a, 0x43, 0xb8, 0xf4, 0x15, 0x18, 0xa1, 0x1c,
- 0xc3, 0x87, 0xb6, 0x69, 0xb2, 0xee, 0x65, 0x86,
- ),
- }, TestVector{
- key: [
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
- ],
- nonce: [ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ],
- keystream: vec!(
- 0x45, 0x40, 0xf0, 0x5a, 0x9f, 0x1f, 0xb2, 0x96,
- 0xd7, 0x73, 0x6e, 0x7b, 0x20, 0x8e, 0x3c, 0x96,
- 0xeb, 0x4f, 0xe1, 0x83, 0x46, 0x88, 0xd2, 0x60,
- 0x4f, 0x45, 0x09, 0x52, 0xed, 0x43, 0x2d, 0x41,
- 0xbb, 0xe2, 0xa0, 0xb6, 0xea, 0x75, 0x66, 0xd2,
- 0xa5, 0xd1, 0xe7, 0xe2, 0x0d, 0x42, 0xaf, 0x2c,
- 0x53, 0xd7, 0x92, 0xb1, 0xc4, 0x3f, 0xea, 0x81,
- 0x7e, 0x9a, 0xd2, 0x75, 0xae, 0x54, 0x69, 0x63,
- ),
- }, TestVector{
- key: [
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- ],
- nonce: [ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 ],
- keystream: vec!(
- 0xde, 0x9c, 0xba, 0x7b, 0xf3, 0xd6, 0x9e, 0xf5,
- 0xe7, 0x86, 0xdc, 0x63, 0x97, 0x3f, 0x65, 0x3a,
- 0x0b, 0x49, 0xe0, 0x15, 0xad, 0xbf, 0xf7, 0x13,
- 0x4f, 0xcb, 0x7d, 0xf1, 0x37, 0x82, 0x10, 0x31,
- 0xe8, 0x5a, 0x05, 0x02, 0x78, 0xa7, 0x08, 0x45,
- 0x27, 0x21, 0x4f, 0x73, 0xef, 0xc7, 0xfa, 0x5b,
- 0x52, 0x77, 0x06, 0x2e, 0xb7, 0xa0, 0x43, 0x3e,
- 0x44, 0x5f, 0x41, 0xe3,
- ),
- }, TestVector{
- key: [
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- ],
- nonce: [ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ],
- keystream: vec!(
- 0xef, 0x3f, 0xdf, 0xd6, 0xc6, 0x15, 0x78, 0xfb,
- 0xf5, 0xcf, 0x35, 0xbd, 0x3d, 0xd3, 0x3b, 0x80,
- 0x09, 0x63, 0x16, 0x34, 0xd2, 0x1e, 0x42, 0xac,
- 0x33, 0x96, 0x0b, 0xd1, 0x38, 0xe5, 0x0d, 0x32,
- 0x11, 0x1e, 0x4c, 0xaf, 0x23, 0x7e, 0xe5, 0x3c,
- 0xa8, 0xad, 0x64, 0x26, 0x19, 0x4a, 0x88, 0x54,
- 0x5d, 0xdc, 0x49, 0x7a, 0x0b, 0x46, 0x6e, 0x7d,
- 0x6b, 0xbd, 0xb0, 0x04, 0x1b, 0x2f, 0x58, 0x6b,
- ),
- }, TestVector{
- key: [
- 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
- 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
- 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
- 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
- ],
- nonce: [ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07 ],
- keystream: vec!(
- 0xf7, 0x98, 0xa1, 0x89, 0xf1, 0x95, 0xe6, 0x69,
- 0x82, 0x10, 0x5f, 0xfb, 0x64, 0x0b, 0xb7, 0x75,
- 0x7f, 0x57, 0x9d, 0xa3, 0x16, 0x02, 0xfc, 0x93,
- 0xec, 0x01, 0xac, 0x56, 0xf8, 0x5a, 0xc3, 0xc1,
- 0x34, 0xa4, 0x54, 0x7b, 0x73, 0x3b, 0x46, 0x41,
- 0x30, 0x42, 0xc9, 0x44, 0x00, 0x49, 0x17, 0x69,
- 0x05, 0xd3, 0xbe, 0x59, 0xea, 0x1c, 0x53, 0xf1,
- 0x59, 0x16, 0x15, 0x5c, 0x2b, 0xe8, 0x24, 0x1a,
- 0x38, 0x00, 0x8b, 0x9a, 0x26, 0xbc, 0x35, 0x94,
- 0x1e, 0x24, 0x44, 0x17, 0x7c, 0x8a, 0xde, 0x66,
- 0x89, 0xde, 0x95, 0x26, 0x49, 0x86, 0xd9, 0x58,
- 0x89, 0xfb, 0x60, 0xe8, 0x46, 0x29, 0xc9, 0xbd,
- 0x9a, 0x5a, 0xcb, 0x1c, 0xc1, 0x18, 0xbe, 0x56,
- 0x3e, 0xb9, 0xb3, 0xa4, 0xa4, 0x72, 0xf8, 0x2e,
- 0x09, 0xa7, 0xe7, 0x78, 0x49, 0x2b, 0x56, 0x2e,
- 0xf7, 0x13, 0x0e, 0x88, 0xdf, 0xe0, 0x31, 0xc7,
- 0x9d, 0xb9, 0xd4, 0xf7, 0xc7, 0xa8, 0x99, 0x15,
- 0x1b, 0x9a, 0x47, 0x50, 0x32, 0xb6, 0x3f, 0xc3,
- 0x85, 0x24, 0x5f, 0xe0, 0x54, 0xe3, 0xdd, 0x5a,
- 0x97, 0xa5, 0xf5, 0x76, 0xfe, 0x06, 0x40, 0x25,
- 0xd3, 0xce, 0x04, 0x2c, 0x56, 0x6a, 0xb2, 0xc5,
- 0x07, 0xb1, 0x38, 0xdb, 0x85, 0x3e, 0x3d, 0x69,
- 0x59, 0x66, 0x09, 0x96, 0x54, 0x6c, 0xc9, 0xc4,
- 0xa6, 0xea, 0xfd, 0xc7, 0x77, 0xc0, 0x40, 0xd7,
- 0x0e, 0xaf, 0x46, 0xf7, 0x6d, 0xad, 0x39, 0x79,
- 0xe5, 0xc5, 0x36, 0x0c, 0x33, 0x17, 0x16, 0x6a,
- 0x1c, 0x89, 0x4c, 0x94, 0xa3, 0x71, 0x87, 0x6a,
- 0x94, 0xdf, 0x76, 0x28, 0xfe, 0x4e, 0xaa, 0xf2,
- 0xcc, 0xb2, 0x7d, 0x5a, 0xaa, 0xe0, 0xad, 0x7a,
- 0xd0, 0xf9, 0xd4, 0xb6, 0xad, 0x3b, 0x54, 0x09,
- 0x87, 0x46, 0xd4, 0x52, 0x4d, 0x38, 0x40, 0x7a,
- 0x6d, 0xeb, 0x3a, 0xb7, 0x8f, 0xab, 0x78, 0xc9,
- ),
- },
- );
-
- for tv in test_vectors.iter() {
- let mut c = ChaCha20::new(&tv.key, &tv.nonce);
- let input: Vec<u8> = repeat(0).take(tv.keystream.len()).collect();
- let mut output: Vec<u8> = repeat(0).take(input.len()).collect();
- c.process(&input[..], &mut output[..]);
- assert_eq!(output, tv.keystream);
- }
- }
-
- #[test]
- fn test_chacha20_256_tls_vectors_96_nonce() {
- struct TestVector {
- key: [u8; 32],
- nonce: [u8; 12],
- keystream: Vec<u8>,
- }
- // taken from http://tools.ietf.org/html/draft-agl-tls-chacha20poly1305-04
- let test_vectors = vec!(
- TestVector{
- key: [
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- ],
- nonce: [ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ],
- keystream: vec!(
- 0x76, 0xb8, 0xe0, 0xad, 0xa0, 0xf1, 0x3d, 0x90,
- 0x40, 0x5d, 0x6a, 0xe5, 0x53, 0x86, 0xbd, 0x28,
- 0xbd, 0xd2, 0x19, 0xb8, 0xa0, 0x8d, 0xed, 0x1a,
- 0xa8, 0x36, 0xef, 0xcc, 0x8b, 0x77, 0x0d, 0xc7,
- 0xda, 0x41, 0x59, 0x7c, 0x51, 0x57, 0x48, 0x8d,
- 0x77, 0x24, 0xe0, 0x3f, 0xb8, 0xd8, 0x4a, 0x37,
- 0x6a, 0x43, 0xb8, 0xf4, 0x15, 0x18, 0xa1, 0x1c,
- 0xc3, 0x87, 0xb6, 0x69, 0xb2, 0xee, 0x65, 0x86,
- ),
- }, TestVector{
- key: [
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
- ],
- nonce: [ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ],
- keystream: vec!(
- 0x45, 0x40, 0xf0, 0x5a, 0x9f, 0x1f, 0xb2, 0x96,
- 0xd7, 0x73, 0x6e, 0x7b, 0x20, 0x8e, 0x3c, 0x96,
- 0xeb, 0x4f, 0xe1, 0x83, 0x46, 0x88, 0xd2, 0x60,
- 0x4f, 0x45, 0x09, 0x52, 0xed, 0x43, 0x2d, 0x41,
- 0xbb, 0xe2, 0xa0, 0xb6, 0xea, 0x75, 0x66, 0xd2,
- 0xa5, 0xd1, 0xe7, 0xe2, 0x0d, 0x42, 0xaf, 0x2c,
- 0x53, 0xd7, 0x92, 0xb1, 0xc4, 0x3f, 0xea, 0x81,
- 0x7e, 0x9a, 0xd2, 0x75, 0xae, 0x54, 0x69, 0x63,
- ),
- }, TestVector{
- key: [
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- ],
- nonce: [ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 ],
- keystream: vec!(
- 0xde, 0x9c, 0xba, 0x7b, 0xf3, 0xd6, 0x9e, 0xf5,
- 0xe7, 0x86, 0xdc, 0x63, 0x97, 0x3f, 0x65, 0x3a,
- 0x0b, 0x49, 0xe0, 0x15, 0xad, 0xbf, 0xf7, 0x13,
- 0x4f, 0xcb, 0x7d, 0xf1, 0x37, 0x82, 0x10, 0x31,
- 0xe8, 0x5a, 0x05, 0x02, 0x78, 0xa7, 0x08, 0x45,
- 0x27, 0x21, 0x4f, 0x73, 0xef, 0xc7, 0xfa, 0x5b,
- 0x52, 0x77, 0x06, 0x2e, 0xb7, 0xa0, 0x43, 0x3e,
- 0x44, 0x5f, 0x41, 0xe3,
- ),
- }, TestVector{
- key: [
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- ],
- nonce: [ 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ],
- keystream: vec!(
- 0xef, 0x3f, 0xdf, 0xd6, 0xc6, 0x15, 0x78, 0xfb,
- 0xf5, 0xcf, 0x35, 0xbd, 0x3d, 0xd3, 0x3b, 0x80,
- 0x09, 0x63, 0x16, 0x34, 0xd2, 0x1e, 0x42, 0xac,
- 0x33, 0x96, 0x0b, 0xd1, 0x38, 0xe5, 0x0d, 0x32,
- 0x11, 0x1e, 0x4c, 0xaf, 0x23, 0x7e, 0xe5, 0x3c,
- 0xa8, 0xad, 0x64, 0x26, 0x19, 0x4a, 0x88, 0x54,
- 0x5d, 0xdc, 0x49, 0x7a, 0x0b, 0x46, 0x6e, 0x7d,
- 0x6b, 0xbd, 0xb0, 0x04, 0x1b, 0x2f, 0x58, 0x6b,
- ),
- }, TestVector{
- key: [
- 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
- 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
- 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
- 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
- ],
- nonce: [0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07 ],
- keystream: vec!(
- 0xf7, 0x98, 0xa1, 0x89, 0xf1, 0x95, 0xe6, 0x69,
- 0x82, 0x10, 0x5f, 0xfb, 0x64, 0x0b, 0xb7, 0x75,
- 0x7f, 0x57, 0x9d, 0xa3, 0x16, 0x02, 0xfc, 0x93,
- 0xec, 0x01, 0xac, 0x56, 0xf8, 0x5a, 0xc3, 0xc1,
- 0x34, 0xa4, 0x54, 0x7b, 0x73, 0x3b, 0x46, 0x41,
- 0x30, 0x42, 0xc9, 0x44, 0x00, 0x49, 0x17, 0x69,
- 0x05, 0xd3, 0xbe, 0x59, 0xea, 0x1c, 0x53, 0xf1,
- 0x59, 0x16, 0x15, 0x5c, 0x2b, 0xe8, 0x24, 0x1a,
- 0x38, 0x00, 0x8b, 0x9a, 0x26, 0xbc, 0x35, 0x94,
- 0x1e, 0x24, 0x44, 0x17, 0x7c, 0x8a, 0xde, 0x66,
- 0x89, 0xde, 0x95, 0x26, 0x49, 0x86, 0xd9, 0x58,
- 0x89, 0xfb, 0x60, 0xe8, 0x46, 0x29, 0xc9, 0xbd,
- 0x9a, 0x5a, 0xcb, 0x1c, 0xc1, 0x18, 0xbe, 0x56,
- 0x3e, 0xb9, 0xb3, 0xa4, 0xa4, 0x72, 0xf8, 0x2e,
- 0x09, 0xa7, 0xe7, 0x78, 0x49, 0x2b, 0x56, 0x2e,
- 0xf7, 0x13, 0x0e, 0x88, 0xdf, 0xe0, 0x31, 0xc7,
- 0x9d, 0xb9, 0xd4, 0xf7, 0xc7, 0xa8, 0x99, 0x15,
- 0x1b, 0x9a, 0x47, 0x50, 0x32, 0xb6, 0x3f, 0xc3,
- 0x85, 0x24, 0x5f, 0xe0, 0x54, 0xe3, 0xdd, 0x5a,
- 0x97, 0xa5, 0xf5, 0x76, 0xfe, 0x06, 0x40, 0x25,
- 0xd3, 0xce, 0x04, 0x2c, 0x56, 0x6a, 0xb2, 0xc5,
- 0x07, 0xb1, 0x38, 0xdb, 0x85, 0x3e, 0x3d, 0x69,
- 0x59, 0x66, 0x09, 0x96, 0x54, 0x6c, 0xc9, 0xc4,
- 0xa6, 0xea, 0xfd, 0xc7, 0x77, 0xc0, 0x40, 0xd7,
- 0x0e, 0xaf, 0x46, 0xf7, 0x6d, 0xad, 0x39, 0x79,
- 0xe5, 0xc5, 0x36, 0x0c, 0x33, 0x17, 0x16, 0x6a,
- 0x1c, 0x89, 0x4c, 0x94, 0xa3, 0x71, 0x87, 0x6a,
- 0x94, 0xdf, 0x76, 0x28, 0xfe, 0x4e, 0xaa, 0xf2,
- 0xcc, 0xb2, 0x7d, 0x5a, 0xaa, 0xe0, 0xad, 0x7a,
- 0xd0, 0xf9, 0xd4, 0xb6, 0xad, 0x3b, 0x54, 0x09,
- 0x87, 0x46, 0xd4, 0x52, 0x4d, 0x38, 0x40, 0x7a,
- 0x6d, 0xeb, 0x3a, 0xb7, 0x8f, 0xab, 0x78, 0xc9,
- ),
- },
- );
-
- for tv in test_vectors.iter() {
- let mut c = ChaCha20::new(&tv.key, &tv.nonce);
- let input: Vec<u8> = repeat(0).take(tv.keystream.len()).collect();
- let mut output: Vec<u8> = repeat(0).take(input.len()).collect();
- c.process(&input[..], &mut output[..]);
- assert_eq!(output, tv.keystream);
- }
- }
-
- #[test]
- fn get_single_block() {
- // Test that `get_single_block` (which takes a 16-byte nonce) is equivalent to getting a block
- // using a 12-byte nonce, with the block starting at the counter offset given by the remaining 4
- // bytes.
- let key = [
- 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
- 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
- 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
- 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
- ];
- let nonce_16bytes = [
- 0x00, 0x01, 0x02, 0x03, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b
- ];
- let counter_pos = &nonce_16bytes[..4];
- let nonce_12bytes = &nonce_16bytes[4..];
-
- // Initialize a ChaCha20 instance with its counter starting at 0.
- let mut chacha20 = ChaCha20::new(&key, nonce_12bytes);
- // Seek its counter to the block at counter_pos.
- chacha20.seek_to_block(u32::from_le_bytes(counter_pos.try_into().unwrap()));
- let mut block_bytes = [0; 32];
- chacha20.process_in_place(&mut block_bytes);
-
- assert_eq!(ChaCha20::get_single_block(&key, &nonce_16bytes), block_bytes);
- }
-
- #[test]
- fn encrypt_single_block() {
- let key = [
- 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
- 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
- 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
- 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
- ];
- let nonce = [
- 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
- 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
- ];
- let bytes = [1; 32];
-
- let mut encrypted_bytes = [0; 32];
- ChaCha20::encrypt_single_block(&key, &nonce, &mut encrypted_bytes, &bytes);
-
- let mut decrypted_bytes = [0; 32];
- ChaCha20::encrypt_single_block(&key, &nonce, &mut decrypted_bytes, &encrypted_bytes);
-
- assert_eq!(bytes, decrypted_bytes);
- }
-
- #[test]
- fn encrypt_single_block_in_place() {
- let key = [
- 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
- 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
- 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
- 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
- ];
- let nonce = [
- 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
- 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
- ];
- let unencrypted_bytes = [1; 32];
- let mut bytes = unencrypted_bytes;
-
- ChaCha20::encrypt_single_block_in_place(&key, &nonce, &mut bytes);
- assert_ne!(bytes, unencrypted_bytes);
-
- ChaCha20::encrypt_single_block_in_place(&key, &nonce, &mut bytes);
- assert_eq!(bytes, unencrypted_bytes);
- }
-}
+++ /dev/null
-// ring has a garbage API so its use is avoided, but rust-crypto doesn't have RFC-variant poly1305
-// Instead, we steal rust-crypto's implementation and tweak it to match the RFC.
-//
-// This file is licensed under the Apache License, Version 2.0 <LICENSE-APACHE
-// or http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your option.
-// You may not use this file except in accordance with one or both of these
-// licenses.
-//
-// This is a port of Andrew Moons poly1305-donna
-// https://github.com/floodyberry/poly1305-donna
-
-use crate::ln::msgs::DecodeError;
-use crate::util::ser::{FixedLengthReader, LengthRead, LengthReadableArgs, Readable, Writeable, Writer};
-use crate::io::{self, Read, Write};
-
-#[cfg(not(fuzzing))]
-mod real_chachapoly {
- use crate::util::chacha20::ChaCha20;
- use crate::util::poly1305::Poly1305;
- use bitcoin::hashes::cmp::fixed_time_eq;
-
- #[derive(Clone, Copy)]
- pub struct ChaCha20Poly1305RFC {
- cipher: ChaCha20,
- mac: Poly1305,
- finished: bool,
- data_len: usize,
- aad_len: u64,
- }
-
- impl ChaCha20Poly1305RFC {
- #[inline]
- fn pad_mac_16(mac: &mut Poly1305, len: usize) {
- if len % 16 != 0 {
- mac.input(&[0; 16][0..16 - (len % 16)]);
- }
- }
- pub fn new(key: &[u8], nonce: &[u8], aad: &[u8]) -> ChaCha20Poly1305RFC {
- assert!(key.len() == 16 || key.len() == 32);
- assert!(nonce.len() == 12);
-
- // Ehh, I'm too lazy to *also* tweak ChaCha20 to make it RFC-compliant
- assert!(nonce[0] == 0 && nonce[1] == 0 && nonce[2] == 0 && nonce[3] == 0);
-
- let mut cipher = ChaCha20::new(key, &nonce[4..]);
- let mut mac_key = [0u8; 64];
- let zero_key = [0u8; 64];
- cipher.process(&zero_key, &mut mac_key);
-
- let mut mac = Poly1305::new(&mac_key[..32]);
- mac.input(aad);
- ChaCha20Poly1305RFC::pad_mac_16(&mut mac, aad.len());
-
- ChaCha20Poly1305RFC {
- cipher,
- mac,
- finished: false,
- data_len: 0,
- aad_len: aad.len() as u64,
- }
- }
-
- pub fn encrypt(&mut self, input: &[u8], output: &mut [u8], out_tag: &mut [u8]) {
- assert!(input.len() == output.len());
- assert!(self.finished == false);
- self.cipher.process(input, output);
- self.data_len += input.len();
- self.mac.input(output);
- ChaCha20Poly1305RFC::pad_mac_16(&mut self.mac, self.data_len);
- self.finished = true;
- self.mac.input(&self.aad_len.to_le_bytes());
- self.mac.input(&(self.data_len as u64).to_le_bytes());
- self.mac.raw_result(out_tag);
- }
-
- pub fn encrypt_full_message_in_place(&mut self, input_output: &mut [u8], out_tag: &mut [u8]) {
- self.encrypt_in_place(input_output);
- self.finish_and_get_tag(out_tag);
- }
-
- // Encrypt `input_output` in-place. To finish and calculate the tag, use `finish_and_get_tag`
- // below.
- pub(super) fn encrypt_in_place(&mut self, input_output: &mut [u8]) {
- debug_assert!(self.finished == false);
- self.cipher.process_in_place(input_output);
- self.data_len += input_output.len();
- self.mac.input(input_output);
- }
-
- // If we were previously encrypting with `encrypt_in_place`, this method can be used to finish
- // encrypting and calculate the tag.
- pub(super) fn finish_and_get_tag(&mut self, out_tag: &mut [u8]) {
- debug_assert!(self.finished == false);
- ChaCha20Poly1305RFC::pad_mac_16(&mut self.mac, self.data_len);
- self.finished = true;
- self.mac.input(&self.aad_len.to_le_bytes());
- self.mac.input(&(self.data_len as u64).to_le_bytes());
- self.mac.raw_result(out_tag);
- }
-
- pub fn decrypt(&mut self, input: &[u8], output: &mut [u8], tag: &[u8]) -> bool {
- assert!(input.len() == output.len());
- assert!(self.finished == false);
-
- self.finished = true;
-
- self.mac.input(input);
-
- self.data_len += input.len();
- ChaCha20Poly1305RFC::pad_mac_16(&mut self.mac, self.data_len);
- self.mac.input(&self.aad_len.to_le_bytes());
- self.mac.input(&(self.data_len as u64).to_le_bytes());
-
- let mut calc_tag = [0u8; 16];
- self.mac.raw_result(&mut calc_tag);
- if fixed_time_eq(&calc_tag, tag) {
- self.cipher.process(input, output);
- true
- } else {
- false
- }
- }
-
- pub fn check_decrypt_in_place(&mut self, input_output: &mut [u8], tag: &[u8]) -> Result<(), ()> {
- self.decrypt_in_place(input_output);
- if self.finish_and_check_tag(tag) { Ok(()) } else { Err(()) }
- }
-
- /// Decrypt in place, without checking the tag. Use `finish_and_check_tag` to check it
- /// later when decryption finishes.
- ///
- /// Should never be `pub` because the public API should always enforce tag checking.
- pub(super) fn decrypt_in_place(&mut self, input_output: &mut [u8]) {
- debug_assert!(self.finished == false);
- self.mac.input(input_output);
- self.data_len += input_output.len();
- self.cipher.process_in_place(input_output);
- }
-
- /// If we were previously decrypting with `just_decrypt_in_place`, this method must be used
- /// to check the tag. Returns whether or not the tag is valid.
- pub(super) fn finish_and_check_tag(&mut self, tag: &[u8]) -> bool {
- debug_assert!(self.finished == false);
- self.finished = true;
- ChaCha20Poly1305RFC::pad_mac_16(&mut self.mac, self.data_len);
- self.mac.input(&self.aad_len.to_le_bytes());
- self.mac.input(&(self.data_len as u64).to_le_bytes());
-
- let mut calc_tag = [0u8; 16];
- self.mac.raw_result(&mut calc_tag);
- if fixed_time_eq(&calc_tag, tag) {
- true
- } else {
- false
- }
- }
- }
-}
-#[cfg(not(fuzzing))]
-pub use self::real_chachapoly::ChaCha20Poly1305RFC;
-
-/// Enables simultaneously reading and decrypting a ChaCha20Poly1305RFC stream from a std::io::Read.
-struct ChaChaPolyReader<'a, R: Read> {
- pub chacha: &'a mut ChaCha20Poly1305RFC,
- pub read: R,
-}
-
-impl<'a, R: Read> Read for ChaChaPolyReader<'a, R> {
- // Decrypt bytes from Self::read into `dest`.
- // `ChaCha20Poly1305RFC::finish_and_check_tag` must be called to check the tag after all reads
- // complete.
- fn read(&mut self, dest: &mut [u8]) -> Result<usize, io::Error> {
- let res = self.read.read(dest)?;
- if res > 0 {
- self.chacha.decrypt_in_place(&mut dest[0..res]);
- }
- Ok(res)
- }
-}
-
-/// Enables simultaneously writing and encrypting a byte stream into a Writer.
-struct ChaChaPolyWriter<'a, W: Writer> {
- pub chacha: &'a mut ChaCha20Poly1305RFC,
- pub write: &'a mut W,
-}
-
-impl<'a, W: Writer> Writer for ChaChaPolyWriter<'a, W> {
- // Encrypt then write bytes from `src` into Self::write.
- // `ChaCha20Poly1305RFC::finish_and_get_tag` can be called to retrieve the tag after all writes
- // complete.
- fn write_all(&mut self, src: &[u8]) -> Result<(), io::Error> {
- let mut src_idx = 0;
- while src_idx < src.len() {
- let mut write_buffer = [0; 8192];
- let bytes_written = (&mut write_buffer[..]).write(&src[src_idx..]).expect("In-memory writes can't fail");
- self.chacha.encrypt_in_place(&mut write_buffer[..bytes_written]);
- self.write.write_all(&write_buffer[..bytes_written])?;
- src_idx += bytes_written;
- }
- Ok(())
- }
-}
-
-/// Enables the use of the serialization macros for objects that need to be simultaneously encrypted and
-/// serialized. This allows us to avoid an intermediate Vec allocation.
-pub(crate) struct ChaChaPolyWriteAdapter<'a, W: Writeable> {
- pub rho: [u8; 32],
- pub writeable: &'a W,
-}
-
-impl<'a, W: Writeable> ChaChaPolyWriteAdapter<'a, W> {
- #[allow(unused)] // This will be used for onion messages soon
- pub fn new(rho: [u8; 32], writeable: &'a W) -> ChaChaPolyWriteAdapter<'a, W> {
- Self { rho, writeable }
- }
-}
-
-impl<'a, T: Writeable> Writeable for ChaChaPolyWriteAdapter<'a, T> {
- // Simultaneously write and encrypt Self::writeable.
- fn write<W: Writer>(&self, w: &mut W) -> Result<(), io::Error> {
- let mut chacha = ChaCha20Poly1305RFC::new(&self.rho, &[0; 12], &[]);
- let mut chacha_stream = ChaChaPolyWriter { chacha: &mut chacha, write: w };
- self.writeable.write(&mut chacha_stream)?;
- let mut tag = [0 as u8; 16];
- chacha.finish_and_get_tag(&mut tag);
- tag.write(w)?;
-
- Ok(())
- }
-}
-
-/// Enables the use of the serialization macros for objects that need to be simultaneously decrypted and
-/// deserialized. This allows us to avoid an intermediate Vec allocation.
-pub(crate) struct ChaChaPolyReadAdapter<R: Readable> {
- pub readable: R,
-}
-
-impl<T: Readable> LengthReadableArgs<[u8; 32]> for ChaChaPolyReadAdapter<T> {
- // Simultaneously read and decrypt an object from a LengthRead, storing it in Self::readable.
- // LengthRead must be used instead of std::io::Read because we need the total length to separate
- // out the tag at the end.
- fn read<R: LengthRead>(mut r: &mut R, secret: [u8; 32]) -> Result<Self, DecodeError> {
- if r.total_bytes() < 16 { return Err(DecodeError::InvalidValue) }
-
- let mut chacha = ChaCha20Poly1305RFC::new(&secret, &[0; 12], &[]);
- let decrypted_len = r.total_bytes() - 16;
- let s = FixedLengthReader::new(&mut r, decrypted_len);
- let mut chacha_stream = ChaChaPolyReader { chacha: &mut chacha, read: s };
- let readable: T = Readable::read(&mut chacha_stream)?;
- chacha_stream.read.eat_remaining()?;
-
- let mut tag = [0 as u8; 16];
- r.read_exact(&mut tag)?;
- if !chacha.finish_and_check_tag(&tag) {
- return Err(DecodeError::InvalidValue)
- }
-
- Ok(Self { readable })
- }
-}
-
-#[cfg(fuzzing)]
-mod fuzzy_chachapoly {
- #[derive(Clone, Copy)]
- pub struct ChaCha20Poly1305RFC {
- tag: [u8; 16],
- finished: bool,
- }
- impl ChaCha20Poly1305RFC {
- pub fn new(key: &[u8], nonce: &[u8], _aad: &[u8]) -> ChaCha20Poly1305RFC {
- assert!(key.len() == 16 || key.len() == 32);
- assert!(nonce.len() == 12);
-
- // Ehh, I'm too lazy to *also* tweak ChaCha20 to make it RFC-compliant
- assert!(nonce[0] == 0 && nonce[1] == 0 && nonce[2] == 0 && nonce[3] == 0);
-
- let mut tag = [0; 16];
- tag.copy_from_slice(&key[0..16]);
-
- ChaCha20Poly1305RFC {
- tag,
- finished: false,
- }
- }
-
- pub fn encrypt(&mut self, input: &[u8], output: &mut [u8], out_tag: &mut [u8]) {
- assert!(input.len() == output.len());
- assert!(self.finished == false);
-
- output.copy_from_slice(&input);
- out_tag.copy_from_slice(&self.tag);
- self.finished = true;
- }
-
- pub fn encrypt_full_message_in_place(&mut self, input_output: &mut [u8], out_tag: &mut [u8]) {
- self.encrypt_in_place(input_output);
- self.finish_and_get_tag(out_tag);
- }
-
- pub(super) fn encrypt_in_place(&mut self, _input_output: &mut [u8]) {
- assert!(self.finished == false);
- }
-
- pub(super) fn finish_and_get_tag(&mut self, out_tag: &mut [u8]) {
- assert!(self.finished == false);
- out_tag.copy_from_slice(&self.tag);
- self.finished = true;
- }
-
- pub fn decrypt(&mut self, input: &[u8], output: &mut [u8], tag: &[u8]) -> bool {
- assert!(input.len() == output.len());
- assert!(self.finished == false);
-
- if tag[..] != self.tag[..] { return false; }
- output.copy_from_slice(input);
- self.finished = true;
- true
- }
-
- pub fn check_decrypt_in_place(&mut self, input_output: &mut [u8], tag: &[u8]) -> Result<(), ()> {
- self.decrypt_in_place(input_output);
- if self.finish_and_check_tag(tag) { Ok(()) } else { Err(()) }
- }
-
- pub(super) fn decrypt_in_place(&mut self, _input: &mut [u8]) {
- assert!(self.finished == false);
- }
-
- pub(super) fn finish_and_check_tag(&mut self, tag: &[u8]) -> bool {
- if tag[..] != self.tag[..] { return false; }
- self.finished = true;
- true
- }
- }
-}
-#[cfg(fuzzing)]
-pub use self::fuzzy_chachapoly::ChaCha20Poly1305RFC;
-
-#[cfg(test)]
-mod tests {
- use crate::ln::msgs::DecodeError;
- use super::{ChaChaPolyReadAdapter, ChaChaPolyWriteAdapter};
- use crate::util::ser::{self, FixedLengthReader, LengthReadableArgs, Writeable};
-
- // Used for for testing various lengths of serialization.
- #[derive(Debug, PartialEq, Eq)]
- struct TestWriteable {
- field1: Vec<u8>,
- field2: Vec<u8>,
- field3: Vec<u8>,
- }
- impl_writeable_tlv_based!(TestWriteable, {
- (1, field1, required_vec),
- (2, field2, required_vec),
- (3, field3, required_vec),
- });
-
- #[test]
- fn test_chacha_stream_adapters() {
- // Check that ChaChaPolyReadAdapter and ChaChaPolyWriteAdapter correctly encode and decode an
- // encrypted object.
- macro_rules! check_object_read_write {
- ($obj: expr) => {
- // First, serialize the object, encrypted with ChaCha20Poly1305.
- let rho = [42; 32];
- let writeable_len = $obj.serialized_length() as u64 + 16;
- let write_adapter = ChaChaPolyWriteAdapter::new(rho, &$obj);
- let encrypted_writeable_bytes = write_adapter.encode();
- let encrypted_writeable = &encrypted_writeable_bytes[..];
-
- // Now deserialize the object back and make sure it matches the original.
- let mut rd = FixedLengthReader::new(encrypted_writeable, writeable_len);
- let read_adapter = <ChaChaPolyReadAdapter<TestWriteable>>::read(&mut rd, rho).unwrap();
- assert_eq!($obj, read_adapter.readable);
- };
- }
-
- // Try a big object that will require multiple write buffers.
- let big_writeable = TestWriteable {
- field1: vec![43],
- field2: vec![44; 4192],
- field3: vec![45; 4192 + 1],
- };
- check_object_read_write!(big_writeable);
-
- // Try a small object that fits into one write buffer.
- let small_writeable = TestWriteable {
- field1: vec![43],
- field2: vec![44],
- field3: vec![45],
- };
- check_object_read_write!(small_writeable);
- }
-
- fn do_chacha_stream_adapters_ser_macros() -> Result<(), DecodeError> {
- let writeable = TestWriteable {
- field1: vec![43],
- field2: vec![44; 4192],
- field3: vec![45; 4192 + 1],
- };
-
- // First, serialize the object into a TLV stream, encrypted with ChaCha20Poly1305.
- let rho = [42; 32];
- let write_adapter = ChaChaPolyWriteAdapter::new(rho, &writeable);
- let mut writer = ser::VecWriter(Vec::new());
- encode_tlv_stream!(&mut writer, {
- (1, write_adapter, required),
- });
-
- // Now deserialize the object back and make sure it matches the original.
- let mut read_adapter: Option<ChaChaPolyReadAdapter<TestWriteable>> = None;
- decode_tlv_stream!(&writer.0[..], {
- (1, read_adapter, (option: LengthReadableArgs, rho)),
- });
- assert_eq!(writeable, read_adapter.unwrap().readable);
-
- Ok(())
- }
-
- #[test]
- fn chacha_stream_adapters_ser_macros() {
- // Test that our stream adapters work as expected with the TLV macros.
- // This also serves to test the `option: $trait` variant of the `_decode_tlv` ser macro.
- do_chacha_stream_adapters_ser_macros().unwrap()
- }
-}
+++ /dev/null
-use bitcoin::hashes::{Hash, HashEngine};
-use bitcoin::hashes::hmac::{Hmac, HmacEngine};
-use bitcoin::hashes::sha256::Hash as Sha256;
-use bitcoin::secp256k1::{Message, Secp256k1, SecretKey, ecdsa::Signature, Signing};
-
-use crate::sign::EntropySource;
-
-use core::ops::Deref;
-
-macro_rules! hkdf_extract_expand {
- ($salt: expr, $ikm: expr) => {{
- let mut hmac = HmacEngine::<Sha256>::new($salt);
- hmac.input($ikm);
- let prk = Hmac::from_engine(hmac).to_byte_array();
- let mut hmac = HmacEngine::<Sha256>::new(&prk[..]);
- hmac.input(&[1; 1]);
- let t1 = Hmac::from_engine(hmac).to_byte_array();
- let mut hmac = HmacEngine::<Sha256>::new(&prk[..]);
- hmac.input(&t1);
- hmac.input(&[2; 1]);
- (t1, Hmac::from_engine(hmac).to_byte_array(), prk)
- }};
- ($salt: expr, $ikm: expr, 2) => {{
- let (k1, k2, _) = hkdf_extract_expand!($salt, $ikm);
- (k1, k2)
- }};
- ($salt: expr, $ikm: expr, 5) => {{
- let (k1, k2, prk) = hkdf_extract_expand!($salt, $ikm);
-
- let mut hmac = HmacEngine::<Sha256>::new(&prk[..]);
- hmac.input(&k2);
- hmac.input(&[3; 1]);
- let k3 = Hmac::from_engine(hmac).to_byte_array();
-
- let mut hmac = HmacEngine::<Sha256>::new(&prk[..]);
- hmac.input(&k3);
- hmac.input(&[4; 1]);
- let k4 = Hmac::from_engine(hmac).to_byte_array();
-
- let mut hmac = HmacEngine::<Sha256>::new(&prk[..]);
- hmac.input(&k4);
- hmac.input(&[5; 1]);
- let k5 = Hmac::from_engine(hmac).to_byte_array();
-
- (k1, k2, k3, k4, k5)
- }}
-}
-
-pub fn hkdf_extract_expand_twice(salt: &[u8], ikm: &[u8]) -> ([u8; 32], [u8; 32]) {
- hkdf_extract_expand!(salt, ikm, 2)
-}
-
-pub fn hkdf_extract_expand_5x(salt: &[u8], ikm: &[u8]) -> ([u8; 32], [u8; 32], [u8; 32], [u8; 32], [u8; 32]) {
- hkdf_extract_expand!(salt, ikm, 5)
-}
-
-#[inline]
-pub fn sign<C: Signing>(ctx: &Secp256k1<C>, msg: &Message, sk: &SecretKey) -> Signature {
- #[cfg(feature = "grind_signatures")]
- let sig = ctx.sign_ecdsa_low_r(msg, sk);
- #[cfg(not(feature = "grind_signatures"))]
- let sig = ctx.sign_ecdsa(msg, sk);
- sig
-}
-
-#[inline]
-#[allow(unused_variables)]
-pub fn sign_with_aux_rand<C: Signing, ES: Deref>(
- ctx: &Secp256k1<C>, msg: &Message, sk: &SecretKey, entropy_source: &ES
-) -> Signature where ES::Target: EntropySource {
- #[cfg(feature = "grind_signatures")]
- let sig = loop {
- let sig = ctx.sign_ecdsa_with_noncedata(msg, sk, &entropy_source.get_secure_random_bytes());
- if sig.serialize_compact()[0] < 0x80 {
- break sig;
- }
- };
- #[cfg(all(not(feature = "grind_signatures"), not(feature = "_test_vectors")))]
- let sig = ctx.sign_ecdsa_with_noncedata(msg, sk, &entropy_source.get_secure_random_bytes());
- #[cfg(all(not(feature = "grind_signatures"), feature = "_test_vectors"))]
- let sig = sign(ctx, msg, sk);
- sig
-}
pub(crate) mod atomic_counter;
pub(crate) mod byte_utils;
-pub(crate) mod chacha20;
-#[cfg(not(fuzzing))]
-pub(crate) mod poly1305;
-pub(crate) mod chacha20poly1305rfc;
pub(crate) mod transaction_utils;
pub(crate) mod scid_utils;
pub(crate) mod time;
#[macro_use]
pub(crate) mod macro_logger;
-/// Cryptography utilities.
-pub(crate) mod crypto;
-
// These have to come after macro_logger to build
pub mod logger;
pub mod config;
+++ /dev/null
-// This file is licensed under the Apache License, Version 2.0 <LICENSE-APACHE
-// or http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your option.
-// You may not use this file except in accordance with one or both of these
-// licenses.
-
-// This is a port of Andrew Moons poly1305-donna
-// https://github.com/floodyberry/poly1305-donna
-
-use core::cmp::min;
-use core::convert::TryInto;
-
-#[derive(Clone, Copy)]
-pub struct Poly1305 {
- r : [u32; 5],
- h : [u32; 5],
- pad : [u32; 4],
- leftover : usize,
- buffer : [u8; 16],
- finalized : bool,
-}
-
-impl Poly1305 {
- pub fn new(key: &[u8]) -> Poly1305 {
- assert!(key.len() == 32);
- let mut poly = Poly1305{ r: [0u32; 5], h: [0u32; 5], pad: [0u32; 4], leftover: 0, buffer: [0u8; 16], finalized: false };
-
- // r &= 0xffffffc0ffffffc0ffffffc0fffffff
- poly.r[0] = (u32::from_le_bytes(key[ 0.. 4].try_into().expect("len is 4")) ) & 0x3ffffff;
- poly.r[1] = (u32::from_le_bytes(key[ 3.. 7].try_into().expect("len is 4")) >> 2) & 0x3ffff03;
- poly.r[2] = (u32::from_le_bytes(key[ 6..10].try_into().expect("len is 4")) >> 4) & 0x3ffc0ff;
- poly.r[3] = (u32::from_le_bytes(key[ 9..13].try_into().expect("len is 4")) >> 6) & 0x3f03fff;
- poly.r[4] = (u32::from_le_bytes(key[12..16].try_into().expect("len is 4")) >> 8) & 0x00fffff;
-
- poly.pad[0] = u32::from_le_bytes(key[16..20].try_into().expect("len is 4"));
- poly.pad[1] = u32::from_le_bytes(key[20..24].try_into().expect("len is 4"));
- poly.pad[2] = u32::from_le_bytes(key[24..28].try_into().expect("len is 4"));
- poly.pad[3] = u32::from_le_bytes(key[28..32].try_into().expect("len is 4"));
-
- poly
- }
-
- fn block(&mut self, m: &[u8]) {
- let hibit : u32 = if self.finalized { 0 } else { 1 << 24 };
-
- let r0 = self.r[0];
- let r1 = self.r[1];
- let r2 = self.r[2];
- let r3 = self.r[3];
- let r4 = self.r[4];
-
- let s1 = r1 * 5;
- let s2 = r2 * 5;
- let s3 = r3 * 5;
- let s4 = r4 * 5;
-
- let mut h0 = self.h[0];
- let mut h1 = self.h[1];
- let mut h2 = self.h[2];
- let mut h3 = self.h[3];
- let mut h4 = self.h[4];
-
- // h += m
- h0 += (u32::from_le_bytes(m[ 0.. 4].try_into().expect("len is 4")) ) & 0x3ffffff;
- h1 += (u32::from_le_bytes(m[ 3.. 7].try_into().expect("len is 4")) >> 2) & 0x3ffffff;
- h2 += (u32::from_le_bytes(m[ 6..10].try_into().expect("len is 4")) >> 4) & 0x3ffffff;
- h3 += (u32::from_le_bytes(m[ 9..13].try_into().expect("len is 4")) >> 6) & 0x3ffffff;
- h4 += (u32::from_le_bytes(m[12..16].try_into().expect("len is 4")) >> 8) | hibit;
-
- // h *= r
- let d0 = (h0 as u64 * r0 as u64) + (h1 as u64 * s4 as u64) + (h2 as u64 * s3 as u64) + (h3 as u64 * s2 as u64) + (h4 as u64 * s1 as u64);
- let mut d1 = (h0 as u64 * r1 as u64) + (h1 as u64 * r0 as u64) + (h2 as u64 * s4 as u64) + (h3 as u64 * s3 as u64) + (h4 as u64 * s2 as u64);
- let mut d2 = (h0 as u64 * r2 as u64) + (h1 as u64 * r1 as u64) + (h2 as u64 * r0 as u64) + (h3 as u64 * s4 as u64) + (h4 as u64 * s3 as u64);
- let mut d3 = (h0 as u64 * r3 as u64) + (h1 as u64 * r2 as u64) + (h2 as u64 * r1 as u64) + (h3 as u64 * r0 as u64) + (h4 as u64 * s4 as u64);
- let mut d4 = (h0 as u64 * r4 as u64) + (h1 as u64 * r3 as u64) + (h2 as u64 * r2 as u64) + (h3 as u64 * r1 as u64) + (h4 as u64 * r0 as u64);
-
- // (partial) h %= p
- let mut c : u32;
- c = (d0 >> 26) as u32; h0 = d0 as u32 & 0x3ffffff;
- d1 += c as u64; c = (d1 >> 26) as u32; h1 = d1 as u32 & 0x3ffffff;
- d2 += c as u64; c = (d2 >> 26) as u32; h2 = d2 as u32 & 0x3ffffff;
- d3 += c as u64; c = (d3 >> 26) as u32; h3 = d3 as u32 & 0x3ffffff;
- d4 += c as u64; c = (d4 >> 26) as u32; h4 = d4 as u32 & 0x3ffffff;
- h0 += c * 5; c = h0 >> 26; h0 = h0 & 0x3ffffff;
- h1 += c;
-
- self.h[0] = h0;
- self.h[1] = h1;
- self.h[2] = h2;
- self.h[3] = h3;
- self.h[4] = h4;
- }
-
- pub fn finish(&mut self) {
- if self.leftover > 0 {
- self.buffer[self.leftover] = 1;
- for i in self.leftover+1..16 {
- self.buffer[i] = 0;
- }
- self.finalized = true;
- let tmp = self.buffer;
- self.block(&tmp);
- }
-
- // fully carry h
- let mut h0 = self.h[0];
- let mut h1 = self.h[1];
- let mut h2 = self.h[2];
- let mut h3 = self.h[3];
- let mut h4 = self.h[4];
-
- let mut c : u32;
- c = h1 >> 26; h1 = h1 & 0x3ffffff;
- h2 += c; c = h2 >> 26; h2 = h2 & 0x3ffffff;
- h3 += c; c = h3 >> 26; h3 = h3 & 0x3ffffff;
- h4 += c; c = h4 >> 26; h4 = h4 & 0x3ffffff;
- h0 += c * 5; c = h0 >> 26; h0 = h0 & 0x3ffffff;
- h1 += c;
-
- // compute h + -p
- let mut g0 = h0.wrapping_add(5); c = g0 >> 26; g0 &= 0x3ffffff;
- let mut g1 = h1.wrapping_add(c); c = g1 >> 26; g1 &= 0x3ffffff;
- let mut g2 = h2.wrapping_add(c); c = g2 >> 26; g2 &= 0x3ffffff;
- let mut g3 = h3.wrapping_add(c); c = g3 >> 26; g3 &= 0x3ffffff;
- let mut g4 = h4.wrapping_add(c).wrapping_sub(1 << 26);
-
- // select h if h < p, or h + -p if h >= p
- let mut mask = (g4 >> (32 - 1)).wrapping_sub(1);
- g0 &= mask;
- g1 &= mask;
- g2 &= mask;
- g3 &= mask;
- g4 &= mask;
- mask = !mask;
- h0 = (h0 & mask) | g0;
- h1 = (h1 & mask) | g1;
- h2 = (h2 & mask) | g2;
- h3 = (h3 & mask) | g3;
- h4 = (h4 & mask) | g4;
-
- // h = h % (2^128)
- h0 = ((h0 ) | (h1 << 26)) & 0xffffffff;
- h1 = ((h1 >> 6) | (h2 << 20)) & 0xffffffff;
- h2 = ((h2 >> 12) | (h3 << 14)) & 0xffffffff;
- h3 = ((h3 >> 18) | (h4 << 8)) & 0xffffffff;
-
- // h = mac = (h + pad) % (2^128)
- let mut f : u64;
- f = h0 as u64 + self.pad[0] as u64 ; h0 = f as u32;
- f = h1 as u64 + self.pad[1] as u64 + (f >> 32); h1 = f as u32;
- f = h2 as u64 + self.pad[2] as u64 + (f >> 32); h2 = f as u32;
- f = h3 as u64 + self.pad[3] as u64 + (f >> 32); h3 = f as u32;
-
- self.h[0] = h0;
- self.h[1] = h1;
- self.h[2] = h2;
- self.h[3] = h3;
- }
-
- pub fn input(&mut self, data: &[u8]) {
- assert!(!self.finalized);
- let mut m = data;
-
- if self.leftover > 0 {
- let want = min(16 - self.leftover, m.len());
- for i in 0..want {
- self.buffer[self.leftover+i] = m[i];
- }
- m = &m[want..];
- self.leftover += want;
-
- if self.leftover < 16 {
- return;
- }
-
- // self.block(self.buffer[..]);
- let tmp = self.buffer;
- self.block(&tmp);
-
- self.leftover = 0;
- }
-
- while m.len() >= 16 {
- self.block(&m[0..16]);
- m = &m[16..];
- }
-
- for i in 0..m.len() {
- self.buffer[i] = m[i];
- }
- self.leftover = m.len();
- }
-
- pub fn raw_result(&mut self, output: &mut [u8]) {
- assert!(output.len() >= 16);
- if !self.finalized{
- self.finish();
- }
- output[0..4].copy_from_slice(&self.h[0].to_le_bytes());
- output[4..8].copy_from_slice(&self.h[1].to_le_bytes());
- output[8..12].copy_from_slice(&self.h[2].to_le_bytes());
- output[12..16].copy_from_slice(&self.h[3].to_le_bytes());
- }
-}
-
-#[cfg(test)]
-mod test {
- use crate::prelude::*;
- use core::iter::repeat;
-
- use crate::util::poly1305::Poly1305;
-
- fn poly1305(key: &[u8], msg: &[u8], mac: &mut [u8]) {
- let mut poly = Poly1305::new(key);
- poly.input(msg);
- poly.raw_result(mac);
- }
-
- #[test]
- fn test_nacl_vector() {
- let key = [
- 0xee,0xa6,0xa7,0x25,0x1c,0x1e,0x72,0x91,
- 0x6d,0x11,0xc2,0xcb,0x21,0x4d,0x3c,0x25,
- 0x25,0x39,0x12,0x1d,0x8e,0x23,0x4e,0x65,
- 0x2d,0x65,0x1f,0xa4,0xc8,0xcf,0xf8,0x80,
- ];
-
- let msg = [
- 0x8e,0x99,0x3b,0x9f,0x48,0x68,0x12,0x73,
- 0xc2,0x96,0x50,0xba,0x32,0xfc,0x76,0xce,
- 0x48,0x33,0x2e,0xa7,0x16,0x4d,0x96,0xa4,
- 0x47,0x6f,0xb8,0xc5,0x31,0xa1,0x18,0x6a,
- 0xc0,0xdf,0xc1,0x7c,0x98,0xdc,0xe8,0x7b,
- 0x4d,0xa7,0xf0,0x11,0xec,0x48,0xc9,0x72,
- 0x71,0xd2,0xc2,0x0f,0x9b,0x92,0x8f,0xe2,
- 0x27,0x0d,0x6f,0xb8,0x63,0xd5,0x17,0x38,
- 0xb4,0x8e,0xee,0xe3,0x14,0xa7,0xcc,0x8a,
- 0xb9,0x32,0x16,0x45,0x48,0xe5,0x26,0xae,
- 0x90,0x22,0x43,0x68,0x51,0x7a,0xcf,0xea,
- 0xbd,0x6b,0xb3,0x73,0x2b,0xc0,0xe9,0xda,
- 0x99,0x83,0x2b,0x61,0xca,0x01,0xb6,0xde,
- 0x56,0x24,0x4a,0x9e,0x88,0xd5,0xf9,0xb3,
- 0x79,0x73,0xf6,0x22,0xa4,0x3d,0x14,0xa6,
- 0x59,0x9b,0x1f,0x65,0x4c,0xb4,0x5a,0x74,
- 0xe3,0x55,0xa5,
- ];
-
- let expected = [
- 0xf3,0xff,0xc7,0x70,0x3f,0x94,0x00,0xe5,
- 0x2a,0x7d,0xfb,0x4b,0x3d,0x33,0x05,0xd9,
- ];
-
- let mut mac = [0u8; 16];
- poly1305(&key, &msg, &mut mac);
- assert_eq!(&mac[..], &expected[..]);
-
- let mut poly = Poly1305::new(&key);
- poly.input(&msg[0..32]);
- poly.input(&msg[32..96]);
- poly.input(&msg[96..112]);
- poly.input(&msg[112..120]);
- poly.input(&msg[120..124]);
- poly.input(&msg[124..126]);
- poly.input(&msg[126..127]);
- poly.input(&msg[127..128]);
- poly.input(&msg[128..129]);
- poly.input(&msg[129..130]);
- poly.input(&msg[130..131]);
- poly.raw_result(&mut mac);
- assert_eq!(&mac[..], &expected[..]);
- }
-
- #[test]
- fn donna_self_test() {
- let wrap_key = [
- 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- ];
-
- let wrap_msg = [
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- ];
-
- let wrap_mac = [
- 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- ];
-
- let mut mac = [0u8; 16];
- poly1305(&wrap_key, &wrap_msg, &mut mac);
- assert_eq!(&mac[..], &wrap_mac[..]);
-
- let total_key = [
- 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0xff,
- 0xfe, 0xfd, 0xfc, 0xfb, 0xfa, 0xf9, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00,
- ];
-
- let total_mac = [
- 0x64, 0xaf, 0xe2, 0xe8, 0xd6, 0xad, 0x7b, 0xbd,
- 0xd2, 0x87, 0xf9, 0x7c, 0x44, 0x62, 0x3d, 0x39,
- ];
-
- let mut tpoly = Poly1305::new(&total_key);
- for i in 0..256 {
- let key: Vec<u8> = repeat(i as u8).take(32).collect();
- let msg: Vec<u8> = repeat(i as u8).take(256).collect();
- let mut mac = [0u8; 16];
- poly1305(&key[..], &msg[0..i], &mut mac);
- tpoly.input(&mac);
- }
- tpoly.raw_result(&mut mac);
- assert_eq!(&mac[..], &total_mac[..]);
- }
-
- #[test]
- fn test_tls_vectors() {
- // from http://tools.ietf.org/html/draft-agl-tls-chacha20poly1305-04
- let key = b"this is 32-byte key for Poly1305";
- let msg = [0u8; 32];
- let expected = [
- 0x49, 0xec, 0x78, 0x09, 0x0e, 0x48, 0x1e, 0xc6,
- 0xc2, 0x6b, 0x33, 0xb9, 0x1c, 0xcc, 0x03, 0x07,
- ];
- let mut mac = [0u8; 16];
- poly1305(key, &msg, &mut mac);
- assert_eq!(&mac[..], &expected[..]);
-
- let msg = b"Hello world!";
- let expected= [
- 0xa6, 0xf7, 0x45, 0x00, 0x8f, 0x81, 0xc9, 0x16,
- 0xa2, 0x0d, 0xcc, 0x74, 0xee, 0xf2, 0xb2, 0xf0,
- ];
- poly1305(key, msg, &mut mac);
- assert_eq!(&mac[..], &expected[..]);
- }
-}
use bitcoin::blockdata::constants::ChainHash;
use bitcoin::network::constants::Network;
use crate::sign::EntropySource;
- use crate::util::chacha20::ChaCha20;
+ use crate::crypto::chacha20::ChaCha20;
use crate::util::scid_utils;
use core::convert::TryInto;
use crate::ln::script::ShutdownScript;
use crate::offers::invoice::{BlindedPayInfo, UnsignedBolt12Invoice};
use crate::offers::invoice_request::UnsignedInvoiceRequest;
-use crate::onion_message::{Destination, MessageRouter, OnionMessagePath};
+use crate::onion_message::messenger::{Destination, MessageRouter, OnionMessagePath};
use crate::routing::gossip::{EffectiveCapacity, NetworkGraph, NodeId, RoutingFees};
use crate::routing::utxo::{UtxoLookup, UtxoLookupError, UtxoResult};
use crate::routing::router::{find_route, InFlightHtlcs, Path, Route, RouteParameters, RouteHintHop, Router, ScorerAccountingForInFlightHtlcs};
}
// Rather annoyingly, there's no safe way in Rust std to construct a Waker despite it being
- // totally possible to construct from a trait implementation (though somewhat less effecient
+ // totally possible to construct from a trait implementation (though somewhat less efficient
// compared to a raw VTable). Instead, we have to write out a lot of boilerplate to build a
// waker, which we do here with a trivial Arc<AtomicBool> data element to track woke-ness.
const WAKER_V_TABLE: RawWakerVTable = RawWakerVTable::new(waker_clone, wake, wake_by_ref, drop);