Skip to content

Commit 21fab54

Browse files
committed
Prepare to auth blinded path contexts with a secret AAD in the MAC
When we receive an onion message, we often want to make sure it was sent through a blinded path we constructed. This protects us from various deanonymization attacks where someone can send a message to every node on the network until they find us, effectively unwrapping the blinded path and identifying its recipient. We generally do so by adding authentication tags to our `MessageContext` variants. Because the contexts themselves are encrypted (and MAC'd) to us, we only have to ensure that they cannot be forged, which is trivially accomplished with a simple nonce and a MAC covering it. This logic has ended up being repeated in nearly all of our onion message handlers, and has gotten quite repetitive. Instead, here, we simply authenticate the blinded path contexts using the MAC that's already there, but tweaking it with an additional secret as the AAD in Poly1305. This prevents forgery as the secret is now required to make the MAC check pass. Ultimately this means that no one can ever build a blinded path which terminates at an LDK node that we'll accept, but over time we've come to recognize this as a useful property, rather than something to fight. Here we finally break from the spec fully in our context encryption (not just the contents thereof). This will save a bit of space in some of our `MessageContext`s, though sadly not in the blinded path we include in `Bolt12Offer`s, so they're generally not in space-sensitive blinded paths. We can apply the same logic in our blinded payment paths as well, but we do not do so here. This commit only adds the required changes to the cryptography, for now it uses a constant key of `[41; 32]`.
1 parent 861497e commit 21fab54

File tree

7 files changed

+92
-39
lines changed

7 files changed

+92
-39
lines changed

lightning/src/blinded_path/message.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,7 @@ impl BlindedMessagePath {
9292
recipient_node_id,
9393
context,
9494
&blinding_secret,
95+
[41; 32], // TODO: Pass this in
9596
)
9697
.map_err(|_| ())?,
9798
}))
@@ -514,6 +515,7 @@ pub(crate) const MESSAGE_PADDING_ROUND_OFF: usize = 100;
514515
pub(super) fn blinded_hops<T: secp256k1::Signing + secp256k1::Verification>(
515516
secp_ctx: &Secp256k1<T>, intermediate_nodes: &[MessageForwardNode],
516517
recipient_node_id: PublicKey, context: MessageContext, session_priv: &SecretKey,
518+
local_node_receive_key: [u8; 32],
517519
) -> Result<Vec<BlindedHop>, secp256k1::Error> {
518520
let pks = intermediate_nodes
519521
.iter()
@@ -536,13 +538,13 @@ pub(super) fn blinded_hops<T: secp256k1::Signing + secp256k1::Verification>(
536538

537539
if is_compact {
538540
let path = pks.zip(tlvs);
539-
utils::construct_blinded_hops(secp_ctx, path, session_priv)
541+
utils::construct_blinded_hops(secp_ctx, path, session_priv, Some(local_node_receive_key))
540542
} else {
541543
let path =
542544
pks.zip(tlvs.map(|tlv| BlindedPathWithPadding {
543545
tlvs: tlv,
544546
round_off: MESSAGE_PADDING_ROUND_OFF,
545547
}));
546-
utils::construct_blinded_hops(secp_ctx, path, session_priv)
548+
utils::construct_blinded_hops(secp_ctx, path, session_priv, Some(local_node_receive_key))
547549
}
548550
}

lightning/src/blinded_path/payment.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -675,7 +675,7 @@ pub(super) fn blinded_hops<T: secp256k1::Signing + secp256k1::Verification>(
675675
tlvs.map(|tlv| BlindedPathWithPadding { tlvs: tlv, round_off: PAYMENT_PADDING_ROUND_OFF }),
676676
);
677677

678-
utils::construct_blinded_hops(secp_ctx, path, session_priv)
678+
utils::construct_blinded_hops(secp_ctx, path, session_priv, None)
679679
}
680680

681681
/// `None` if underflow occurs.

lightning/src/blinded_path/utils.rs

Lines changed: 33 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,8 @@ use bitcoin::secp256k1::{self, PublicKey, Scalar, Secp256k1, SecretKey};
1717

1818
use super::message::BlindedMessagePath;
1919
use super::{BlindedHop, BlindedPath};
20-
use crate::crypto::streams::ChaChaPolyWriteAdapter;
20+
use crate::crypto::chacha20poly1305rfc::ChaCha20Poly1305RFC;
21+
use crate::crypto::streams::chachapoly_encrypt_with_swapped_aad;
2122
use crate::io;
2223
use crate::ln::onion_utils;
2324
use crate::onion_message::messenger::Destination;
@@ -38,7 +39,7 @@ macro_rules! build_keys_helper {
3839
let mut onion_packet_pubkey = msg_blinding_point.clone();
3940

4041
macro_rules! build_keys {
41-
($hop: expr, $blinded: expr, $encrypted_payload: expr) => {{
42+
($hop: expr, $blinded: expr, $encrypted_payload: expr, $node_recv_key: expr) => {{
4243
let pk = *$hop.borrow();
4344
let encrypted_data_ss = SharedSecret::new(&pk, &msg_blinding_point_priv);
4445

@@ -65,16 +66,17 @@ macro_rules! build_keys_helper {
6566
onion_packet_pubkey,
6667
rho,
6768
unblinded_hop_opt,
69+
$node_recv_key,
6870
$encrypted_payload,
6971
);
7072
(encrypted_data_ss, onion_packet_ss)
7173
}};
7274
}
7375

7476
macro_rules! build_keys_in_loop {
75-
($pk: expr, $blinded: expr, $encrypted_payload: expr) => {
77+
($pk: expr, $blinded: expr, $encrypted_payload: expr, $node_recv_key: expr) => {
7678
let (encrypted_data_ss, onion_packet_ss) =
77-
build_keys!($pk, $blinded, $encrypted_payload);
79+
build_keys!($pk, $blinded, $encrypted_payload, $node_recv_key);
7880

7981
let msg_blinding_point_blinding_factor = {
8082
let mut sha = Sha256::engine();
@@ -105,7 +107,6 @@ macro_rules! build_keys_helper {
105107
};
106108
}
107109

108-
#[inline]
109110
pub(crate) fn construct_keys_for_onion_message<'a, T, I, F>(
110111
secp_ctx: &Secp256k1<T>, unblinded_path: I, destination: Destination, session_priv: &SecretKey,
111112
mut callback: F,
@@ -115,41 +116,43 @@ where
115116
I: Iterator<Item = PublicKey>,
116117
F: FnMut(SharedSecret, PublicKey, [u8; 32], Option<PublicKey>, Option<Vec<u8>>),
117118
{
118-
let mut callback_wrapper = |_, ss, pk, encrypted_payload_rho, unblinded_hop_data, encrypted_payload| {
119+
let mut callback_wrapper = |_, ss, pk, encrypted_payload_rho, unblinded_hop_data, _: Option<()>, encrypted_payload| {
119120
callback(ss, pk, encrypted_payload_rho, unblinded_hop_data, encrypted_payload);
120121
};
121122
build_keys_helper!(session_priv, secp_ctx, callback_wrapper);
122123

123124
for pk in unblinded_path {
124-
build_keys_in_loop!(pk, false, None);
125+
build_keys_in_loop!(pk, false, None, None);
125126
}
126127
match destination {
127128
Destination::Node(pk) => {
128-
build_keys!(pk, false, None);
129+
build_keys!(pk, false, None, None);
129130
},
130131
Destination::BlindedPath(BlindedMessagePath(BlindedPath { blinded_hops, .. })) => {
131132
for hop in blinded_hops {
132-
build_keys_in_loop!(hop.blinded_node_id, true, Some(hop.encrypted_payload));
133+
build_keys_in_loop!(hop.blinded_node_id, true, Some(hop.encrypted_payload), None);
133134
}
134135
},
135136
}
136137
Ok(())
137138
}
138139

139-
#[inline]
140-
pub(super) fn construct_keys_for_blinded_path<'a, T, I, F, H>(
141-
secp_ctx: &Secp256k1<T>, unblinded_path: I, session_priv: &SecretKey, mut callback: F,
140+
fn construct_keys_for_blinded_path<'a, T, I, F, H>(
141+
secp_ctx: &Secp256k1<T>, unblinded_path: I, session_priv: &SecretKey,
142+
mut last_hop_receive_key: Option<[u8; 32]>, mut callback: F,
142143
) -> Result<(), secp256k1::Error>
143144
where
144145
T: secp256k1::Signing + secp256k1::Verification,
145146
H: Borrow<PublicKey>,
146147
I: Iterator<Item = H>,
147-
F: FnMut(PublicKey, SharedSecret, PublicKey, [u8; 32], Option<H>, Option<Vec<u8>>),
148+
F: FnMut(PublicKey, SharedSecret, PublicKey, [u8; 32], Option<H>, Option<[u8; 32]>, Option<Vec<u8>>),
148149
{
149150
build_keys_helper!(session_priv, secp_ctx, callback);
150151

151-
for pk in unblinded_path {
152-
build_keys_in_loop!(pk, false, None);
152+
let mut iter = unblinded_path.peekable();
153+
while let Some(pk) = iter.next() {
154+
let receive_key = if iter.peek().is_none() { last_hop_receive_key.take() } else { None };
155+
build_keys_in_loop!(pk, false, None, receive_key);
153156
}
154157
Ok(())
155158
}
@@ -167,6 +170,7 @@ impl<W: Writeable> Borrow<PublicKey> for PublicKeyWithTlvs<W> {
167170

168171
pub(crate) fn construct_blinded_hops<'a, T, I, W>(
169172
secp_ctx: &Secp256k1<T>, unblinded_path: I, session_priv: &SecretKey,
173+
last_hop_receive_key: Option<[u8; 32]>,
170174
) -> Result<Vec<BlindedHop>, secp256k1::Error>
171175
where
172176
T: secp256k1::Signing + secp256k1::Verification,
@@ -178,12 +182,14 @@ where
178182
secp_ctx,
179183
unblinded_path.map(|(pubkey, tlvs)| PublicKeyWithTlvs { pubkey, tlvs }),
180184
session_priv,
181-
|blinded_node_id, _, _, encrypted_payload_rho, unblinded_hop_data, _| {
185+
last_hop_receive_key,
186+
|blinded_node_id, _, _, encrypted_payload_rho, unblinded_hop_data, hop_recv_key, _| {
182187
blinded_hops.push(BlindedHop {
183188
blinded_node_id,
184189
encrypted_payload: encrypt_payload(
185190
unblinded_hop_data.unwrap().tlvs,
186191
encrypted_payload_rho,
192+
hop_recv_key,
187193
),
188194
});
189195
},
@@ -192,9 +198,17 @@ where
192198
}
193199

194200
/// Encrypt TLV payload to be used as a [`crate::blinded_path::BlindedHop::encrypted_payload`].
195-
fn encrypt_payload<P: Writeable>(payload: P, encrypted_tlvs_rho: [u8; 32]) -> Vec<u8> {
196-
let write_adapter = ChaChaPolyWriteAdapter::new(encrypted_tlvs_rho, &payload);
197-
write_adapter.encode()
201+
fn encrypt_payload<P: Writeable>(payload: P, encrypted_tlvs_rho: [u8; 32], hop_recv_key: Option<[u8; 32]>) -> Vec<u8> {
202+
let mut payload_data = payload.encode();
203+
if let Some(hop_recv_key) = hop_recv_key {
204+
chachapoly_encrypt_with_swapped_aad(payload_data, encrypted_tlvs_rho, hop_recv_key)
205+
} else {
206+
let mut chacha = ChaCha20Poly1305RFC::new(&encrypted_tlvs_rho, &[0; 12], &[]);
207+
let mut tag = [0; 16];
208+
chacha.encrypt_full_message_in_place(&mut payload_data, &mut tag);
209+
payload_data.extend_from_slice(&tag);
210+
payload_data
211+
}
198212
}
199213

200214
/// A data structure used exclusively to pad blinded path payloads, ensuring they are of

lightning/src/crypto/streams.rs

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,31 @@ impl<'a, T: Writeable> Writeable for ChaChaPolyWriteAdapter<'a, T> {
5151
}
5252
}
5353

54+
/// Encrypts the provided plaintext with the given key using ChaCha20Poly1305 in the modified
55+
/// with-AAD form used in [`ChaChaDualPolyReadAdapter`].
56+
pub(crate) fn chachapoly_encrypt_with_swapped_aad(mut plaintext: Vec<u8>, key: [u8; 32], aad: [u8; 32]) -> Vec<u8> {
57+
let mut chacha = ChaCha20::new(&key[..], &[0; 12]);
58+
let mut mac_key = [0u8; 64];
59+
chacha.process_in_place(&mut mac_key);
60+
61+
let mut mac = Poly1305::new(&mac_key[..32]);
62+
chacha.process_in_place(&mut plaintext[..]);
63+
mac.input(&plaintext[..]);
64+
65+
if plaintext.len() % 16 != 0 {
66+
mac.input(&[0; 16][0..16 - (plaintext.len() % 16)]);
67+
}
68+
69+
mac.input(&aad[..]);
70+
// Note that we don't need to pad the AAD since its a multiple of 16 bytes
71+
72+
mac.input(&(plaintext.len() as u64).to_le_bytes());
73+
mac.input(&32u64.to_le_bytes());
74+
75+
plaintext.extend_from_slice(&mac.result());
76+
plaintext
77+
}
78+
5479
/// Enables the use of the serialization macros for objects that need to be simultaneously decrypted
5580
/// and deserialized. This allows us to avoid an intermediate Vec allocation.
5681
///

lightning/src/ln/blinded_payment_tests.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1558,15 +1558,15 @@ fn route_blinding_spec_test_vector() {
15581558
// Can't use the public API here as the encrypted payloads contain unknown TLVs.
15591559
let path = [(dave_node_id, WithoutLength(&dave_unblinded_tlvs)), (eve_node_id, WithoutLength(&eve_unblinded_tlvs))];
15601560
let mut dave_eve_blinded_hops = blinded_path::utils::construct_blinded_hops(
1561-
&secp_ctx, path.into_iter(), &dave_eve_session_priv
1561+
&secp_ctx, path.into_iter(), &dave_eve_session_priv, None,
15621562
).unwrap();
15631563

15641564
// Concatenate an additional Bob -> Carol blinded path to the Eve -> Dave blinded path.
15651565
let bob_carol_session_priv = secret_from_hex("0202020202020202020202020202020202020202020202020202020202020202");
15661566
let bob_blinding_point = PublicKey::from_secret_key(&secp_ctx, &bob_carol_session_priv);
15671567
let path = [(bob_node_id, WithoutLength(&bob_unblinded_tlvs)), (carol_node_id, WithoutLength(&carol_unblinded_tlvs))];
15681568
let bob_carol_blinded_hops = blinded_path::utils::construct_blinded_hops(
1569-
&secp_ctx, path.into_iter(), &bob_carol_session_priv
1569+
&secp_ctx, path.into_iter(), &bob_carol_session_priv, None,
15701570
).unwrap();
15711571

15721572
let mut blinded_hops = bob_carol_blinded_hops;
@@ -2028,7 +2028,7 @@ fn do_test_trampoline_single_hop_receive(success: bool) {
20282028

20292029
let path = [(carol_node_id, WithoutLength(&carol_unblinded_tlvs))];
20302030
blinded_path::utils::construct_blinded_hops(
2031-
&secp_ctx, path.into_iter(), &carol_alice_trampoline_session_priv
2031+
&secp_ctx, path.into_iter(), &carol_alice_trampoline_session_priv, None,
20322032
).unwrap()
20332033
} else {
20342034
let payee_tlvs = blinded_path::payment::TrampolineForwardTlvs {
@@ -2049,7 +2049,7 @@ fn do_test_trampoline_single_hop_receive(success: bool) {
20492049
let carol_unblinded_tlvs = payee_tlvs.encode();
20502050
let path = [(carol_node_id, WithoutLength(&carol_unblinded_tlvs))];
20512051
blinded_path::utils::construct_blinded_hops(
2052-
&secp_ctx, path.into_iter(), &carol_alice_trampoline_session_priv
2052+
&secp_ctx, path.into_iter(), &carol_alice_trampoline_session_priv, None,
20532053
).unwrap()
20542054
};
20552055

@@ -2253,7 +2253,7 @@ fn test_trampoline_unblinded_receive() {
22532253
let carol_alice_trampoline_session_priv = secret_from_hex("a0f4b8d7b6c2d0ffdfaf718f76e9decaef4d9fb38a8c4addb95c4007cc3eee03");
22542254
let carol_blinding_point = PublicKey::from_secret_key(&secp_ctx, &carol_alice_trampoline_session_priv);
22552255
let carol_blinded_hops = blinded_path::utils::construct_blinded_hops(
2256-
&secp_ctx, path.into_iter(), &carol_alice_trampoline_session_priv
2256+
&secp_ctx, path.into_iter(), &carol_alice_trampoline_session_priv, None,
22572257
).unwrap();
22582258

22592259
let route = Route {

lightning/src/onion_message/messenger.rs

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1068,18 +1068,20 @@ where
10681068
},
10691069
}
10701070
};
1071+
let receiving_context_auth_key = [41; 32]; // TODO: pass this in
10711072
let next_hop = onion_utils::decode_next_untagged_hop(
10721073
onion_decode_ss,
10731074
&msg.onion_routing_packet.hop_data[..],
10741075
msg.onion_routing_packet.hmac,
1075-
(control_tlvs_ss, custom_handler.deref(), logger.deref()),
1076+
(control_tlvs_ss, custom_handler.deref(), receiving_context_auth_key, logger.deref()),
10761077
);
10771078
match next_hop {
10781079
Ok((
10791080
Payload::Receive {
10801081
message,
10811082
control_tlvs: ReceiveControlTlvs::Unblinded(ReceiveTlvs { context }),
10821083
reply_path,
1084+
control_tlvs_authenticated,
10831085
},
10841086
None,
10851087
)) => match (message, context) {
@@ -1108,6 +1110,8 @@ where
11081110
Ok(PeeledOnion::DNSResolver(msg, None, reply_path))
11091111
},
11101112
_ => {
1113+
// Hide the "`control_tlvs_authenticated` is unused warning". We'll use it here soon
1114+
let _ = control_tlvs_authenticated;
11111115
log_trace!(
11121116
logger,
11131117
"Received message was sent on a blinded path with wrong or missing context."
@@ -2298,7 +2302,7 @@ fn packet_payloads_and_keys<
22982302

22992303
if let Some(control_tlvs) = final_control_tlvs {
23002304
payloads.push((
2301-
Payload::Receive { control_tlvs, reply_path: reply_path.take(), message },
2305+
Payload::Receive { control_tlvs, reply_path: reply_path.take(), message, control_tlvs_authenticated: false, },
23022306
prev_control_tlvs_ss.unwrap(),
23032307
));
23042308
} else {
@@ -2307,6 +2311,7 @@ fn packet_payloads_and_keys<
23072311
control_tlvs: ReceiveControlTlvs::Unblinded(ReceiveTlvs { context: None }),
23082312
reply_path: reply_path.take(),
23092313
message,
2314+
control_tlvs_authenticated: false,
23102315
},
23112316
prev_control_tlvs_ss.unwrap(),
23122317
));

lightning/src/onion_message/packet.rs

Lines changed: 17 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ use super::dns_resolution::DNSResolverMessage;
1818
use super::messenger::CustomOnionMessageHandler;
1919
use super::offers::OffersMessage;
2020
use crate::blinded_path::message::{BlindedMessagePath, ForwardTlvs, NextMessageHop, ReceiveTlvs};
21-
use crate::crypto::streams::{ChaChaPolyReadAdapter, ChaChaPolyWriteAdapter};
21+
use crate::crypto::streams::{ChaChaDualPolyReadAdapter, ChaChaPolyWriteAdapter};
2222
use crate::ln::msgs::DecodeError;
2323
use crate::ln::onion_utils;
2424
use crate::util::logger::Logger;
@@ -112,7 +112,11 @@ pub(super) enum Payload<T: OnionMessageContents> {
112112
/// This payload is for an intermediate hop.
113113
Forward(ForwardControlTlvs),
114114
/// This payload is for the final hop.
115-
Receive { control_tlvs: ReceiveControlTlvs, reply_path: Option<BlindedMessagePath>, message: T },
115+
Receive {
116+
/// The [`ReceiveControlTlvs`] were authenticated with the additional key which was
117+
/// provided to [`ReadableArgs::read`].
118+
control_tlvs_authenticated: bool,
119+
control_tlvs: ReceiveControlTlvs, reply_path: Option<BlindedMessagePath>, message: T },
116120
}
117121

118122
/// The contents of an [`OnionMessage`] as read from the wire.
@@ -223,6 +227,7 @@ impl<T: OnionMessageContents> Writeable for (Payload<T>, [u8; 32]) {
223227
control_tlvs: ReceiveControlTlvs::Blinded(encrypted_bytes),
224228
reply_path,
225229
message,
230+
control_tlvs_authenticated: _,
226231
} => {
227232
_encode_varint_length_prefixed_tlv!(w, {
228233
(2, reply_path, option),
@@ -238,6 +243,7 @@ impl<T: OnionMessageContents> Writeable for (Payload<T>, [u8; 32]) {
238243
control_tlvs: ReceiveControlTlvs::Unblinded(control_tlvs),
239244
reply_path,
240245
message,
246+
control_tlvs_authenticated: _,
241247
} => {
242248
let write_adapter = ChaChaPolyWriteAdapter::new(self.1, &control_tlvs);
243249
_encode_varint_length_prefixed_tlv!(w, {
@@ -252,22 +258,22 @@ impl<T: OnionMessageContents> Writeable for (Payload<T>, [u8; 32]) {
252258
}
253259

254260
// Uses the provided secret to simultaneously decode and decrypt the control TLVs and data TLV.
255-
impl<H: CustomOnionMessageHandler + ?Sized, L: Logger + ?Sized> ReadableArgs<(SharedSecret, &H, &L)>
261+
impl<H: CustomOnionMessageHandler + ?Sized, L: Logger + ?Sized> ReadableArgs<(SharedSecret, &H, [u8; 32], &L)>
256262
for Payload<ParsedOnionMessageContents<<H as CustomOnionMessageHandler>::CustomMessage>>
257263
{
258-
fn read<R: Read>(r: &mut R, args: (SharedSecret, &H, &L)) -> Result<Self, DecodeError> {
259-
let (encrypted_tlvs_ss, handler, logger) = args;
264+
fn read<R: Read>(r: &mut R, args: (SharedSecret, &H, [u8; 32], &L)) -> Result<Self, DecodeError> {
265+
let (encrypted_tlvs_ss, handler, receive_tlvs_key, logger) = args;
260266

261267
let v: BigSize = Readable::read(r)?;
262268
let mut rd = FixedLengthReader::new(r, v.0);
263269
let mut reply_path: Option<BlindedMessagePath> = None;
264-
let mut read_adapter: Option<ChaChaPolyReadAdapter<ControlTlvs>> = None;
270+
let mut read_adapter: Option<ChaChaDualPolyReadAdapter<ControlTlvs>> = None;
265271
let rho = onion_utils::gen_rho_from_shared_secret(&encrypted_tlvs_ss.secret_bytes());
266272
let mut message_type: Option<u64> = None;
267273
let mut message = None;
268274
decode_tlv_stream_with_custom_tlv_decode!(&mut rd, {
269275
(2, reply_path, option),
270-
(4, read_adapter, (option: LengthReadableArgs, rho)),
276+
(4, read_adapter, (option: LengthReadableArgs, (rho, receive_tlvs_key))),
271277
}, |msg_type, msg_reader| {
272278
if msg_type < 64 { return Ok(false) }
273279
// Don't allow reading more than one data TLV from an onion message.
@@ -304,17 +310,18 @@ impl<H: CustomOnionMessageHandler + ?Sized, L: Logger + ?Sized> ReadableArgs<(Sh
304310

305311
match read_adapter {
306312
None => return Err(DecodeError::InvalidValue),
307-
Some(ChaChaPolyReadAdapter { readable: ControlTlvs::Forward(tlvs) }) => {
308-
if message_type.is_some() {
313+
Some(ChaChaDualPolyReadAdapter { readable: ControlTlvs::Forward(tlvs), used_aad }) => {
314+
if used_aad || message_type.is_some() {
309315
return Err(DecodeError::InvalidValue);
310316
}
311317
Ok(Payload::Forward(ForwardControlTlvs::Unblinded(tlvs)))
312318
},
313-
Some(ChaChaPolyReadAdapter { readable: ControlTlvs::Receive(tlvs) }) => {
319+
Some(ChaChaDualPolyReadAdapter { readable: ControlTlvs::Receive(tlvs), used_aad }) => {
314320
Ok(Payload::Receive {
315321
control_tlvs: ReceiveControlTlvs::Unblinded(tlvs),
316322
reply_path,
317323
message: message.ok_or(DecodeError::InvalidValue)?,
324+
control_tlvs_authenticated: used_aad,
318325
})
319326
},
320327
}

0 commit comments

Comments
 (0)