mixnet/core/sphinx/
peel.rs1use super::{build::SurbPayloadEncryptionKeys, crypto::*, delay::Delay, packet::*, target::Target};
24use arrayref::{array_mut_ref, array_ref, array_refs};
25use subtle::ConstantTimeEq;
26
27pub fn kx_public(packet: &Packet) -> &KxPublic {
29 array_ref![packet, 0, KX_PUBLIC_SIZE]
30}
31
32#[derive(Debug, PartialEq, Eq)]
34pub enum Action {
35 ForwardTo { target: Target, delay: Delay },
37 DeliverRequest,
39 DeliverReply { surb_id: SurbId },
42 DeliverCover { cover_id: Option<CoverId> },
44}
45
46#[derive(Debug, thiserror::Error, PartialEq, Eq)]
47pub enum PeelErr {
48 #[error("Bad MAC in header")]
49 Mac,
50 #[error("Bad action in header")]
51 Action,
52 #[error("Bad payload tag")]
53 PayloadTag,
54}
55
56fn check_payload_tag(tag: &PayloadTag) -> Result<(), PeelErr> {
57 let tag_ok: bool = tag.ct_eq(&PAYLOAD_TAG).into();
58 if tag_ok {
59 Ok(())
60 } else {
61 Err(PeelErr::PayloadTag)
62 }
63}
64
65pub fn peel(
68 out: &mut Packet,
69 packet: &Packet,
70 kx_shared_secret: &SharedSecret,
71) -> Result<Action, PeelErr> {
72 let (kx_public, mac, actions, payload) =
75 array_refs![packet, KX_PUBLIC_SIZE, MAC_SIZE, ACTIONS_SIZE, PAYLOAD_SIZE];
76
77 let sds = SmallDerivedSecrets::new(kx_shared_secret);
78
79 if !mac_ok(mac, actions, sds.mac_key()) {
81 return Err(PeelErr::Mac)
82 }
83
84 let decrypted_actions =
91 array_mut_ref![out, KX_PUBLIC_SIZE - RAW_ACTION_SIZE, ACTIONS_SIZE + MAX_ACTIONS_PAD_SIZE];
92 *array_mut_ref![decrypted_actions, 0, ACTIONS_SIZE] = *actions;
93 *array_mut_ref![decrypted_actions, ACTIONS_SIZE, MAX_ACTIONS_PAD_SIZE] =
94 [0; MAX_ACTIONS_PAD_SIZE]; apply_actions_encryption_keystream(decrypted_actions, sds.actions_encryption_key());
96
97 let raw_action = RawAction::from_le_bytes(*array_ref![decrypted_actions, 0, RAW_ACTION_SIZE]);
98 Ok(match raw_action {
99 RAW_ACTION_DELIVER_REQUEST => {
100 let out = array_mut_ref![out, 0, PAYLOAD_SIZE];
102 *out = *payload;
103 decrypt_payload(out, &derive_payload_encryption_key(kx_shared_secret));
104
105 check_payload_tag(array_ref![out, PAYLOAD_DATA_SIZE, PAYLOAD_TAG_SIZE])?;
106
107 Action::DeliverRequest
108 },
109 RAW_ACTION_DELIVER_REPLY => {
110 let surb_id = *array_ref![decrypted_actions, RAW_ACTION_SIZE, SURB_ID_SIZE];
112
113 *array_mut_ref![out, 0, PAYLOAD_SIZE] = *payload;
116
117 Action::DeliverReply { surb_id }
118 },
119 RAW_ACTION_DELIVER_COVER => Action::DeliverCover { cover_id: None },
120 RAW_ACTION_DELIVER_COVER_WITH_ID => {
121 let cover_id = *array_ref![decrypted_actions, RAW_ACTION_SIZE, COVER_ID_SIZE];
123
124 Action::DeliverCover { cover_id: Some(cover_id) }
125 },
126 _ => {
127 let target = if raw_action == RAW_ACTION_FORWARD_TO_PEER_ID {
129 let peer_id = *array_ref![decrypted_actions, RAW_ACTION_SIZE, PEER_ID_SIZE];
131 decrypted_actions.copy_within(
132 RAW_ACTION_SIZE + PEER_ID_SIZE..
133 RAW_ACTION_SIZE + PEER_ID_SIZE + MAC_SIZE + ACTIONS_SIZE,
134 RAW_ACTION_SIZE,
135 );
136 Target::PeerId(peer_id)
137 } else {
138 Target::MixnodeIndex(raw_action.try_into().map_err(|_| PeelErr::Action)?)
139 };
140
141 let delay = Delay::exp(sds.delay_seed());
143
144 *array_mut_ref![out, 0, KX_PUBLIC_SIZE] = blind_kx_public(kx_public, kx_shared_secret);
146
147 let out_payload = array_mut_ref![out, HEADER_SIZE, PAYLOAD_SIZE];
151 *out_payload = *payload;
152 decrypt_payload(out_payload, &derive_payload_encryption_key(kx_shared_secret));
153
154 Action::ForwardTo { target, delay }
155 },
156 })
157}
158
159pub fn decrypt_reply_payload(
162 payload: &mut Payload,
163 keys: &SurbPayloadEncryptionKeys,
164) -> Result<(), PeelErr> {
165 for key in keys.iter().rev() {
166 encrypt_payload(payload, key);
167 }
168
169 check_payload_tag(array_ref![payload, PAYLOAD_DATA_SIZE, PAYLOAD_TAG_SIZE])
170}