1use crate::client::datatype::{
67 split, Address, Certificate, CloseReason, LinkProtocol, RelayCommand, Size, ZERO,
68};
69use crate::Error;
70use chrono::{DateTime, TimeZone, Utc};
71
72pub const FIXED_PAYLOAD_LEN: usize = 509;
77
78pub const AUTH_CHALLENGE_SIZE: usize = 32;
80
81pub const HASH_LEN: usize = 20;
85
86pub const CELL_TYPE_SIZE: Size = Size::Char;
88
89pub const PAYLOAD_LEN_SIZE: Size = Size::Short;
91
92pub const RELAY_DIGEST_SIZE: Size = Size::Long;
94
95pub const STREAM_ID_REQUIRED: &[RelayCommand] = &[
100 RelayCommand::Begin,
101 RelayCommand::Data,
102 RelayCommand::End,
103 RelayCommand::Connected,
104 RelayCommand::Resolve,
105 RelayCommand::Resolved,
106 RelayCommand::BeginDir,
107];
108
109pub const STREAM_ID_DISALLOWED: &[RelayCommand] = &[
114 RelayCommand::Extend,
115 RelayCommand::Extended,
116 RelayCommand::Truncate,
117 RelayCommand::Truncated,
118 RelayCommand::Drop,
119 RelayCommand::Extend2,
120 RelayCommand::Extended2,
121];
122
123#[derive(Debug, Clone, Copy, PartialEq, Eq)]
156pub enum CellType {
157 Padding,
159 Create,
161 Created,
163 Relay,
165 Destroy,
167 CreateFast,
169 CreatedFast,
171 Versions,
173 Netinfo,
175 RelayEarly,
177 Create2,
179 Created2,
181 PaddingNegotiate,
183 VPadding,
185 Certs,
187 AuthChallenge,
189 Authenticate,
191 Authorize,
193}
194
195impl CellType {
196 pub fn name(&self) -> &'static str {
200 match self {
201 CellType::Padding => "PADDING",
202 CellType::Create => "CREATE",
203 CellType::Created => "CREATED",
204 CellType::Relay => "RELAY",
205 CellType::Destroy => "DESTROY",
206 CellType::CreateFast => "CREATE_FAST",
207 CellType::CreatedFast => "CREATED_FAST",
208 CellType::Versions => "VERSIONS",
209 CellType::Netinfo => "NETINFO",
210 CellType::RelayEarly => "RELAY_EARLY",
211 CellType::Create2 => "CREATE2",
212 CellType::Created2 => "CREATED2",
213 CellType::PaddingNegotiate => "PADDING_NEGOTIATE",
214 CellType::VPadding => "VPADDING",
215 CellType::Certs => "CERTS",
216 CellType::AuthChallenge => "AUTH_CHALLENGE",
217 CellType::Authenticate => "AUTHENTICATE",
218 CellType::Authorize => "AUTHORIZE",
219 }
220 }
221
222 pub fn value(&self) -> u8 {
226 match self {
227 CellType::Padding => 0,
228 CellType::Create => 1,
229 CellType::Created => 2,
230 CellType::Relay => 3,
231 CellType::Destroy => 4,
232 CellType::CreateFast => 5,
233 CellType::CreatedFast => 6,
234 CellType::Versions => 7,
235 CellType::Netinfo => 8,
236 CellType::RelayEarly => 9,
237 CellType::Create2 => 10,
238 CellType::Created2 => 11,
239 CellType::PaddingNegotiate => 12,
240 CellType::VPadding => 128,
241 CellType::Certs => 129,
242 CellType::AuthChallenge => 130,
243 CellType::Authenticate => 131,
244 CellType::Authorize => 132,
245 }
246 }
247
248 pub fn is_fixed_size(&self) -> bool {
253 !matches!(
254 self,
255 CellType::Versions
256 | CellType::VPadding
257 | CellType::Certs
258 | CellType::AuthChallenge
259 | CellType::Authenticate
260 | CellType::Authorize
261 )
262 }
263}
264
265pub fn cell_by_name(name: &str) -> Result<CellType, Error> {
289 match name {
290 "PADDING" => Ok(CellType::Padding),
291 "CREATE" => Ok(CellType::Create),
292 "CREATED" => Ok(CellType::Created),
293 "RELAY" => Ok(CellType::Relay),
294 "DESTROY" => Ok(CellType::Destroy),
295 "CREATE_FAST" => Ok(CellType::CreateFast),
296 "CREATED_FAST" => Ok(CellType::CreatedFast),
297 "VERSIONS" => Ok(CellType::Versions),
298 "NETINFO" => Ok(CellType::Netinfo),
299 "RELAY_EARLY" => Ok(CellType::RelayEarly),
300 "CREATE2" => Ok(CellType::Create2),
301 "CREATED2" => Ok(CellType::Created2),
302 "PADDING_NEGOTIATE" => Ok(CellType::PaddingNegotiate),
303 "VPADDING" => Ok(CellType::VPadding),
304 "CERTS" => Ok(CellType::Certs),
305 "AUTH_CHALLENGE" => Ok(CellType::AuthChallenge),
306 "AUTHENTICATE" => Ok(CellType::Authenticate),
307 "AUTHORIZE" => Ok(CellType::Authorize),
308 _ => Err(Error::Protocol(format!(
309 "'{}' isn't a valid cell type",
310 name
311 ))),
312 }
313}
314
315pub fn cell_by_value(value: u8) -> Result<CellType, Error> {
338 match value {
339 0 => Ok(CellType::Padding),
340 1 => Ok(CellType::Create),
341 2 => Ok(CellType::Created),
342 3 => Ok(CellType::Relay),
343 4 => Ok(CellType::Destroy),
344 5 => Ok(CellType::CreateFast),
345 6 => Ok(CellType::CreatedFast),
346 7 => Ok(CellType::Versions),
347 8 => Ok(CellType::Netinfo),
348 9 => Ok(CellType::RelayEarly),
349 10 => Ok(CellType::Create2),
350 11 => Ok(CellType::Created2),
351 12 => Ok(CellType::PaddingNegotiate),
352 128 => Ok(CellType::VPadding),
353 129 => Ok(CellType::Certs),
354 130 => Ok(CellType::AuthChallenge),
355 131 => Ok(CellType::Authenticate),
356 132 => Ok(CellType::Authorize),
357 _ => Err(Error::Protocol(format!(
358 "'{}' isn't a valid cell value",
359 value
360 ))),
361 }
362}
363
364#[derive(Debug, Clone, PartialEq)]
397pub enum Cell {
398 Padding(PaddingCell),
400 Versions(VersionsCell),
402 Netinfo(NetinfoCell),
404 CreateFast(CreateFastCell),
406 CreatedFast(CreatedFastCell),
408 Relay(RelayCell),
410 Destroy(DestroyCell),
412 VPadding(VPaddingCell),
414 Certs(CertsCell),
416 AuthChallenge(AuthChallengeCell),
418}
419
420impl Cell {
421 pub fn pack(&self, link_protocol: u32) -> Vec<u8> {
429 let lp = LinkProtocol::new(link_protocol);
430 match self {
431 Cell::Padding(c) => c.pack(&lp),
432 Cell::Versions(c) => c.pack(&lp),
433 Cell::Netinfo(c) => c.pack(&lp),
434 Cell::CreateFast(c) => c.pack(&lp),
435 Cell::CreatedFast(c) => c.pack(&lp),
436 Cell::Relay(c) => c.pack(&lp),
437 Cell::Destroy(c) => c.pack(&lp),
438 Cell::VPadding(c) => c.pack(&lp),
439 Cell::Certs(c) => c.pack(&lp),
440 Cell::AuthChallenge(c) => c.pack(&lp),
441 }
442 }
443
444 pub fn pop(content: &[u8], link_protocol: u32) -> Result<(Cell, Vec<u8>), Error> {
479 let lp = LinkProtocol::new(link_protocol);
480 let circ_id_size = lp.circ_id_size.size();
481
482 if content.len() < circ_id_size + CELL_TYPE_SIZE.size() {
483 return Err(Error::Protocol(
484 "Cell content too short for header".to_string(),
485 ));
486 }
487
488 let (circ_id, rest) = lp.circ_id_size.pop(content)?;
489 let (command, rest) = CELL_TYPE_SIZE.pop(rest)?;
490 let cell_type = cell_by_value(command as u8)?;
491
492 let payload_len = if cell_type.is_fixed_size() {
493 FIXED_PAYLOAD_LEN
494 } else {
495 if rest.len() < PAYLOAD_LEN_SIZE.size() {
496 return Err(Error::Protocol(
497 "Cell content too short for payload length".to_string(),
498 ));
499 }
500 let (len, _) = PAYLOAD_LEN_SIZE.pop(rest)?;
501 len as usize
502 };
503
504 let header_size = if cell_type.is_fixed_size() {
505 circ_id_size + CELL_TYPE_SIZE.size()
506 } else {
507 circ_id_size + CELL_TYPE_SIZE.size() + PAYLOAD_LEN_SIZE.size()
508 };
509
510 let total_size = header_size + payload_len;
511 if content.len() < total_size {
512 return Err(Error::Protocol(format!(
513 "{} cell should have a payload of {} bytes, but only had {}",
514 cell_type.name(),
515 payload_len,
516 content.len() - header_size
517 )));
518 }
519
520 let (_, payload_start) = split(content, header_size);
521 let (payload, _) = split(payload_start, payload_len);
522 let remainder = content[total_size..].to_vec();
523
524 let cell = match cell_type {
525 CellType::Padding => Cell::Padding(PaddingCell::unpack(payload)?),
526 CellType::Versions => Cell::Versions(VersionsCell::unpack(payload)?),
527 CellType::Netinfo => Cell::Netinfo(NetinfoCell::unpack(payload)?),
528 CellType::CreateFast => {
529 Cell::CreateFast(CreateFastCell::unpack(payload, circ_id as u32)?)
530 }
531 CellType::CreatedFast => {
532 Cell::CreatedFast(CreatedFastCell::unpack(payload, circ_id as u32)?)
533 }
534 CellType::Relay => Cell::Relay(RelayCell::unpack(payload, circ_id as u32)?),
535 CellType::Destroy => Cell::Destroy(DestroyCell::unpack(payload, circ_id as u32)?),
536 CellType::VPadding => Cell::VPadding(VPaddingCell::unpack(payload)?),
537 CellType::Certs => Cell::Certs(CertsCell::unpack(payload)?),
538 CellType::AuthChallenge => Cell::AuthChallenge(AuthChallengeCell::unpack(payload)?),
539 _ => {
540 return Err(Error::Protocol(format!(
541 "Unpacking not yet implemented for {} cells",
542 cell_type.name()
543 )))
544 }
545 };
546
547 Ok((cell, remainder))
548 }
549
550 pub fn unpack_all(content: &[u8], link_protocol: u32) -> Result<Vec<Cell>, Error> {
563 let mut cells = Vec::new();
564 let mut remaining = content.to_vec();
565 while !remaining.is_empty() {
566 let (cell, rest) = Cell::pop(&remaining, link_protocol)?;
567 cells.push(cell);
568 remaining = rest;
569 }
570 Ok(cells)
571 }
572}
573
574#[derive(Debug, Clone, PartialEq)]
600pub struct PaddingCell {
601 pub payload: Vec<u8>,
603}
604
605impl PaddingCell {
606 pub fn new() -> Self {
610 let mut payload = vec![0u8; FIXED_PAYLOAD_LEN];
611 use rand::RngCore;
612 let mut rng = rand::rng();
613 rng.fill_bytes(&mut payload);
614 PaddingCell { payload }
615 }
616
617 pub fn with_payload(payload: Vec<u8>) -> Result<Self, Error> {
627 if payload.len() != FIXED_PAYLOAD_LEN {
628 return Err(Error::Protocol(format!(
629 "Padding payload should be {} bytes, but was {}",
630 FIXED_PAYLOAD_LEN,
631 payload.len()
632 )));
633 }
634 Ok(PaddingCell { payload })
635 }
636
637 pub fn pack(&self, link_protocol: &LinkProtocol) -> Vec<u8> {
639 pack_fixed_cell(
640 link_protocol,
641 CellType::Padding.value(),
642 &self.payload,
643 None,
644 )
645 }
646
647 pub fn unpack(payload: &[u8]) -> Result<Self, Error> {
649 Ok(PaddingCell {
650 payload: payload.to_vec(),
651 })
652 }
653}
654
655impl Default for PaddingCell {
656 fn default() -> Self {
657 Self::new()
658 }
659}
660
661#[derive(Debug, Clone, PartialEq)]
690pub struct VersionsCell {
691 pub versions: Vec<u32>,
693}
694
695impl VersionsCell {
696 pub fn new(versions: Vec<u32>) -> Self {
702 VersionsCell { versions }
703 }
704
705 pub fn pack(&self, link_protocol: &LinkProtocol) -> Vec<u8> {
707 let payload: Vec<u8> = self
708 .versions
709 .iter()
710 .flat_map(|v| Size::Short.pack(*v as u64))
711 .collect();
712 pack_variable_cell(link_protocol, CellType::Versions.value(), &payload, None)
713 }
714
715 pub fn unpack(payload: &[u8]) -> Result<Self, Error> {
717 let mut versions = Vec::new();
718 let mut content = payload;
719 while !content.is_empty() {
720 let (version, rest) = Size::Short.pop(content)?;
721 versions.push(version as u32);
722 content = rest;
723 }
724 Ok(VersionsCell { versions })
725 }
726}
727
728#[derive(Debug, Clone, PartialEq)]
751pub struct NetinfoCell {
752 pub timestamp: DateTime<Utc>,
754 pub receiver_address: Address,
756 pub sender_addresses: Vec<Address>,
758 pub unused: Vec<u8>,
760}
761
762impl NetinfoCell {
763 pub fn new(
771 receiver_address: Address,
772 sender_addresses: Vec<Address>,
773 timestamp: Option<DateTime<Utc>>,
774 ) -> Self {
775 NetinfoCell {
776 timestamp: timestamp.unwrap_or_else(Utc::now),
777 receiver_address,
778 sender_addresses,
779 unused: Vec::new(),
780 }
781 }
782
783 pub fn pack(&self, link_protocol: &LinkProtocol) -> Vec<u8> {
785 let mut payload = Vec::new();
786 payload.extend_from_slice(&Size::Long.pack(self.timestamp.timestamp() as u64));
787 payload.extend_from_slice(&self.receiver_address.pack());
788 payload.push(self.sender_addresses.len() as u8);
789 for addr in &self.sender_addresses {
790 payload.extend_from_slice(&addr.pack());
791 }
792 pack_fixed_cell(
793 link_protocol,
794 CellType::Netinfo.value(),
795 &payload,
796 Some(&self.unused),
797 )
798 }
799
800 pub fn unpack(payload: &[u8]) -> Result<Self, Error> {
802 let (timestamp, content) = Size::Long.pop(payload)?;
803 let (receiver_address, content) = Address::pop(content)?;
804 let (sender_addr_count, mut content) = Size::Char.pop(content)?;
805
806 let mut sender_addresses = Vec::new();
807 for _ in 0..sender_addr_count {
808 let (addr, rest) = Address::pop(content)?;
809 sender_addresses.push(addr);
810 content = rest;
811 }
812
813 Ok(NetinfoCell {
814 timestamp: Utc.timestamp_opt(timestamp as i64, 0).unwrap(),
815 receiver_address,
816 sender_addresses,
817 unused: content.to_vec(),
818 })
819 }
820}
821
822#[derive(Debug, Clone, PartialEq)]
849pub struct CreateFastCell {
850 pub circ_id: u32,
852 pub key_material: [u8; HASH_LEN],
854 pub unused: Vec<u8>,
856}
857
858impl CreateFastCell {
859 pub fn new(circ_id: u32) -> Self {
865 let mut key_material = [0u8; HASH_LEN];
866 use rand::RngCore;
867 let mut rng = rand::rng();
868 rng.fill_bytes(&mut key_material);
869 CreateFastCell {
870 circ_id,
871 key_material,
872 unused: Vec::new(),
873 }
874 }
875
876 pub fn with_key_material(circ_id: u32, key_material: [u8; HASH_LEN]) -> Self {
883 CreateFastCell {
884 circ_id,
885 key_material,
886 unused: Vec::new(),
887 }
888 }
889
890 pub fn pack(&self, link_protocol: &LinkProtocol) -> Vec<u8> {
892 pack_fixed_cell(
893 link_protocol,
894 CellType::CreateFast.value(),
895 &self.key_material,
896 Some(&self.unused),
897 )
898 .iter()
899 .enumerate()
900 .map(|(i, &b)| {
901 if i < link_protocol.circ_id_size.size() {
902 let circ_id_bytes = if link_protocol.circ_id_size == Size::Long {
903 self.circ_id.to_be_bytes().to_vec()
904 } else {
905 (self.circ_id as u16).to_be_bytes().to_vec()
906 };
907 circ_id_bytes.get(i).copied().unwrap_or(b)
908 } else {
909 b
910 }
911 })
912 .collect()
913 }
914
915 pub fn unpack(payload: &[u8], circ_id: u32) -> Result<Self, Error> {
921 if payload.len() < HASH_LEN {
922 return Err(Error::Protocol(format!(
923 "Key material should be {} bytes, but was {}",
924 HASH_LEN,
925 payload.len()
926 )));
927 }
928 let (key_material_slice, unused) = split(payload, HASH_LEN);
929 let mut key_material = [0u8; HASH_LEN];
930 key_material.copy_from_slice(key_material_slice);
931
932 Ok(CreateFastCell {
933 circ_id,
934 key_material,
935 unused: unused.to_vec(),
936 })
937 }
938}
939
940#[derive(Debug, Clone, PartialEq)]
957pub struct CreatedFastCell {
958 pub circ_id: u32,
960 pub key_material: [u8; HASH_LEN],
962 pub derivative_key: [u8; HASH_LEN],
964 pub unused: Vec<u8>,
966}
967
968impl CreatedFastCell {
969 pub fn new(circ_id: u32, derivative_key: [u8; HASH_LEN]) -> Self {
976 let mut key_material = [0u8; HASH_LEN];
977 use rand::RngCore;
978 let mut rng = rand::rng();
979 rng.fill_bytes(&mut key_material);
980 CreatedFastCell {
981 circ_id,
982 key_material,
983 derivative_key,
984 unused: Vec::new(),
985 }
986 }
987
988 pub fn with_key_material(
996 circ_id: u32,
997 key_material: [u8; HASH_LEN],
998 derivative_key: [u8; HASH_LEN],
999 ) -> Self {
1000 CreatedFastCell {
1001 circ_id,
1002 key_material,
1003 derivative_key,
1004 unused: Vec::new(),
1005 }
1006 }
1007
1008 pub fn pack(&self, link_protocol: &LinkProtocol) -> Vec<u8> {
1010 let mut payload = Vec::new();
1011 payload.extend_from_slice(&self.key_material);
1012 payload.extend_from_slice(&self.derivative_key);
1013 let mut cell = pack_fixed_cell(
1014 link_protocol,
1015 CellType::CreatedFast.value(),
1016 &payload,
1017 Some(&self.unused),
1018 );
1019 let circ_id_bytes = if link_protocol.circ_id_size == Size::Long {
1020 self.circ_id.to_be_bytes().to_vec()
1021 } else {
1022 (self.circ_id as u16).to_be_bytes().to_vec()
1023 };
1024 for (i, &b) in circ_id_bytes.iter().enumerate() {
1025 cell[i] = b;
1026 }
1027 cell
1028 }
1029
1030 pub fn unpack(payload: &[u8], circ_id: u32) -> Result<Self, Error> {
1036 if payload.len() < HASH_LEN * 2 {
1037 return Err(Error::Protocol(format!(
1038 "Key material and derivative key should be {} bytes, but was {}",
1039 HASH_LEN * 2,
1040 payload.len()
1041 )));
1042 }
1043 let (key_material_slice, rest) = split(payload, HASH_LEN);
1044 let (derivative_key_slice, unused) = split(rest, HASH_LEN);
1045
1046 let mut key_material = [0u8; HASH_LEN];
1047 let mut derivative_key = [0u8; HASH_LEN];
1048 key_material.copy_from_slice(key_material_slice);
1049 derivative_key.copy_from_slice(derivative_key_slice);
1050
1051 Ok(CreatedFastCell {
1052 circ_id,
1053 key_material,
1054 derivative_key,
1055 unused: unused.to_vec(),
1056 })
1057 }
1058}
1059
1060#[derive(Debug, Clone, PartialEq)]
1086pub struct RelayCell {
1087 pub circ_id: u32,
1089 pub command: RelayCommand,
1091 pub command_int: u8,
1093 pub recognized: u16,
1095 pub stream_id: u16,
1097 pub digest: u32,
1099 pub data: Vec<u8>,
1101 pub unused: Vec<u8>,
1103}
1104
1105impl RelayCell {
1106 pub fn new(
1122 circ_id: u32,
1123 command: RelayCommand,
1124 data: Vec<u8>,
1125 digest: u32,
1126 stream_id: u16,
1127 ) -> Result<Self, Error> {
1128 if digest == 0 {
1129 if stream_id == 0 && STREAM_ID_REQUIRED.contains(&command) {
1130 return Err(Error::Protocol(format!(
1131 "{} relay cells require a stream id",
1132 command
1133 )));
1134 }
1135 if stream_id != 0 && STREAM_ID_DISALLOWED.contains(&command) {
1136 return Err(Error::Protocol(format!(
1137 "{} relay cells concern the circuit itself and cannot have a stream id",
1138 command
1139 )));
1140 }
1141 }
1142
1143 Ok(RelayCell {
1144 circ_id,
1145 command_int: command.value(),
1146 command,
1147 recognized: 0,
1148 stream_id,
1149 digest,
1150 data,
1151 unused: Vec::new(),
1152 })
1153 }
1154
1155 pub fn pack(&self, link_protocol: &LinkProtocol) -> Vec<u8> {
1157 let mut payload = Vec::new();
1158 payload.push(self.command_int);
1159 payload.extend_from_slice(&self.recognized.to_be_bytes());
1160 payload.extend_from_slice(&self.stream_id.to_be_bytes());
1161 payload.extend_from_slice(&self.digest.to_be_bytes());
1162 payload.extend_from_slice(&(self.data.len() as u16).to_be_bytes());
1163 payload.extend_from_slice(&self.data);
1164
1165 let mut cell = pack_fixed_cell(
1166 link_protocol,
1167 CellType::Relay.value(),
1168 &payload,
1169 Some(&self.unused),
1170 );
1171 let circ_id_bytes = if link_protocol.circ_id_size == Size::Long {
1172 self.circ_id.to_be_bytes().to_vec()
1173 } else {
1174 (self.circ_id as u16).to_be_bytes().to_vec()
1175 };
1176 for (i, &b) in circ_id_bytes.iter().enumerate() {
1177 cell[i] = b;
1178 }
1179 cell
1180 }
1181
1182 pub fn unpack(payload: &[u8], circ_id: u32) -> Result<Self, Error> {
1188 let (command, content) = Size::Char.pop(payload)?;
1189 let (recognized, content) = Size::Short.pop(content)?;
1190 let (stream_id, content) = Size::Short.pop(content)?;
1191 let (digest, content) = Size::Long.pop(content)?;
1192 let (data_len, content) = Size::Short.pop(content)?;
1193 let data_len = data_len as usize;
1194
1195 if content.len() < data_len {
1196 return Err(Error::Protocol(format!(
1197 "RELAY cell said it had {} bytes of data, but only had {}",
1198 data_len,
1199 content.len()
1200 )));
1201 }
1202
1203 let (data, unused) = split(content, data_len);
1204 let (cmd, cmd_int) = RelayCommand::get(command as u8);
1205
1206 Ok(RelayCell {
1207 circ_id,
1208 command: cmd,
1209 command_int: cmd_int,
1210 recognized: recognized as u16,
1211 stream_id: stream_id as u16,
1212 digest: digest as u32,
1213 data: data.to_vec(),
1214 unused: unused.to_vec(),
1215 })
1216 }
1217}
1218
1219#[derive(Debug, Clone, PartialEq)]
1239pub struct DestroyCell {
1240 pub circ_id: u32,
1242 pub reason: CloseReason,
1244 pub reason_int: u8,
1246 pub unused: Vec<u8>,
1248}
1249
1250impl DestroyCell {
1251 pub fn new(circ_id: u32, reason: CloseReason) -> Self {
1258 DestroyCell {
1259 circ_id,
1260 reason_int: reason.value(),
1261 reason,
1262 unused: Vec::new(),
1263 }
1264 }
1265
1266 pub fn pack(&self, link_protocol: &LinkProtocol) -> Vec<u8> {
1268 let payload = vec![self.reason_int];
1269 let mut cell = pack_fixed_cell(
1270 link_protocol,
1271 CellType::Destroy.value(),
1272 &payload,
1273 Some(&self.unused),
1274 );
1275 let circ_id_bytes = if link_protocol.circ_id_size == Size::Long {
1276 self.circ_id.to_be_bytes().to_vec()
1277 } else {
1278 (self.circ_id as u16).to_be_bytes().to_vec()
1279 };
1280 for (i, &b) in circ_id_bytes.iter().enumerate() {
1281 cell[i] = b;
1282 }
1283 cell
1284 }
1285
1286 pub fn unpack(payload: &[u8], circ_id: u32) -> Result<Self, Error> {
1288 let (reason, unused) = Size::Char.pop(payload)?;
1289 let (close_reason, reason_int) = CloseReason::get(reason as u8);
1290
1291 Ok(DestroyCell {
1292 circ_id,
1293 reason: close_reason,
1294 reason_int,
1295 unused: unused.to_vec(),
1296 })
1297 }
1298}
1299
1300#[derive(Debug, Clone, PartialEq)]
1313pub struct VPaddingCell {
1314 pub payload: Vec<u8>,
1316}
1317
1318impl VPaddingCell {
1319 pub fn new(size: usize) -> Self {
1325 let mut payload = vec![0u8; size];
1326 if size > 0 {
1327 use rand::RngCore;
1328 let mut rng = rand::rng();
1329 rng.fill_bytes(&mut payload);
1330 }
1331 VPaddingCell { payload }
1332 }
1333
1334 pub fn with_payload(payload: Vec<u8>) -> Self {
1340 VPaddingCell { payload }
1341 }
1342
1343 pub fn pack(&self, link_protocol: &LinkProtocol) -> Vec<u8> {
1345 pack_variable_cell(
1346 link_protocol,
1347 CellType::VPadding.value(),
1348 &self.payload,
1349 None,
1350 )
1351 }
1352
1353 pub fn unpack(payload: &[u8]) -> Result<Self, Error> {
1355 Ok(VPaddingCell {
1356 payload: payload.to_vec(),
1357 })
1358 }
1359}
1360
1361#[derive(Debug, Clone, PartialEq)]
1378pub struct CertsCell {
1379 pub certificates: Vec<Certificate>,
1381 pub unused: Vec<u8>,
1383}
1384
1385impl CertsCell {
1386 pub fn new(certificates: Vec<Certificate>) -> Self {
1392 CertsCell {
1393 certificates,
1394 unused: Vec::new(),
1395 }
1396 }
1397
1398 pub fn pack(&self, link_protocol: &LinkProtocol) -> Vec<u8> {
1400 let mut payload = Vec::new();
1401 payload.push(self.certificates.len() as u8);
1402 for cert in &self.certificates {
1403 payload.extend_from_slice(&cert.pack());
1404 }
1405 payload.extend_from_slice(&self.unused);
1406 pack_variable_cell(link_protocol, CellType::Certs.value(), &payload, None)
1407 }
1408
1409 pub fn unpack(payload: &[u8]) -> Result<Self, Error> {
1416 if payload.is_empty() {
1417 return Ok(CertsCell {
1418 certificates: Vec::new(),
1419 unused: Vec::new(),
1420 });
1421 }
1422
1423 let (cert_count, mut content) = Size::Char.pop(payload)?;
1424 let mut certificates = Vec::new();
1425
1426 for _ in 0..cert_count {
1427 if content.is_empty() {
1428 return Err(Error::Protocol(format!(
1429 "CERTS cell indicates it should have {} certificates, but only contained {}",
1430 cert_count,
1431 certificates.len()
1432 )));
1433 }
1434 let (cert, rest) = Certificate::pop(content)?;
1435 certificates.push(cert);
1436 content = rest;
1437 }
1438
1439 Ok(CertsCell {
1440 certificates,
1441 unused: content.to_vec(),
1442 })
1443 }
1444}
1445
1446#[derive(Debug, Clone, PartialEq)]
1460pub struct AuthChallengeCell {
1461 pub challenge: [u8; AUTH_CHALLENGE_SIZE],
1463 pub methods: Vec<u16>,
1465 pub unused: Vec<u8>,
1467}
1468
1469impl AuthChallengeCell {
1470 pub fn new(methods: Vec<u16>) -> Self {
1476 let mut challenge = [0u8; AUTH_CHALLENGE_SIZE];
1477 use rand::RngCore;
1478 let mut rng = rand::rng();
1479 rng.fill_bytes(&mut challenge);
1480 AuthChallengeCell {
1481 challenge,
1482 methods,
1483 unused: Vec::new(),
1484 }
1485 }
1486
1487 pub fn with_challenge(challenge: [u8; AUTH_CHALLENGE_SIZE], methods: Vec<u16>) -> Self {
1494 AuthChallengeCell {
1495 challenge,
1496 methods,
1497 unused: Vec::new(),
1498 }
1499 }
1500
1501 pub fn pack(&self, link_protocol: &LinkProtocol) -> Vec<u8> {
1503 let mut payload = Vec::new();
1504 payload.extend_from_slice(&self.challenge);
1505 payload.extend_from_slice(&(self.methods.len() as u16).to_be_bytes());
1506 for method in &self.methods {
1507 payload.extend_from_slice(&method.to_be_bytes());
1508 }
1509 payload.extend_from_slice(&self.unused);
1510 pack_variable_cell(
1511 link_protocol,
1512 CellType::AuthChallenge.value(),
1513 &payload,
1514 None,
1515 )
1516 }
1517
1518 pub fn unpack(payload: &[u8]) -> Result<Self, Error> {
1525 let min_size = AUTH_CHALLENGE_SIZE + Size::Short.size();
1526 if payload.len() < min_size {
1527 return Err(Error::Protocol(format!(
1528 "AUTH_CHALLENGE payload should be at least {} bytes, but was {}",
1529 min_size,
1530 payload.len()
1531 )));
1532 }
1533
1534 let (challenge_slice, content) = split(payload, AUTH_CHALLENGE_SIZE);
1535 let (method_count, mut content) = Size::Short.pop(content)?;
1536
1537 if content.len() < (method_count as usize) * Size::Short.size() {
1538 return Err(Error::Protocol(format!(
1539 "AUTH_CHALLENGE should have {} methods, but only had {} bytes for it",
1540 method_count,
1541 content.len()
1542 )));
1543 }
1544
1545 let mut methods = Vec::new();
1546 for _ in 0..method_count {
1547 let (method, rest) = Size::Short.pop(content)?;
1548 methods.push(method as u16);
1549 content = rest;
1550 }
1551
1552 let mut challenge = [0u8; AUTH_CHALLENGE_SIZE];
1553 challenge.copy_from_slice(challenge_slice);
1554
1555 Ok(AuthChallengeCell {
1556 challenge,
1557 methods,
1558 unused: content.to_vec(),
1559 })
1560 }
1561}
1562
1563fn pack_fixed_cell(
1574 link_protocol: &LinkProtocol,
1575 command: u8,
1576 payload: &[u8],
1577 unused: Option<&[u8]>,
1578) -> Vec<u8> {
1579 let mut cell = Vec::new();
1580
1581 cell.extend_from_slice(&vec![0u8; link_protocol.circ_id_size.size()]);
1582
1583 cell.push(command);
1584
1585 cell.extend_from_slice(payload);
1586
1587 if let Some(unused_bytes) = unused {
1588 cell.extend_from_slice(unused_bytes);
1589 }
1590
1591 let padding_needed = link_protocol.fixed_cell_length.saturating_sub(cell.len());
1592 cell.extend(std::iter::repeat_n(ZERO, padding_needed));
1593
1594 cell
1595}
1596
1597fn pack_variable_cell(
1608 link_protocol: &LinkProtocol,
1609 command: u8,
1610 payload: &[u8],
1611 _unused: Option<&[u8]>,
1612) -> Vec<u8> {
1613 let mut cell = Vec::new();
1614
1615 cell.extend_from_slice(&vec![0u8; link_protocol.circ_id_size.size()]);
1616
1617 cell.push(command);
1618
1619 cell.extend_from_slice(&(payload.len() as u16).to_be_bytes());
1620
1621 cell.extend_from_slice(payload);
1622
1623 cell
1624}
1625
1626#[cfg(test)]
1627mod tests {
1628 use super::*;
1629
1630 #[test]
1631 fn test_cell_by_name() {
1632 let cell_type = cell_by_name("NETINFO").unwrap();
1633 assert_eq!("NETINFO", cell_type.name());
1634 assert_eq!(8, cell_type.value());
1635 assert!(cell_type.is_fixed_size());
1636
1637 assert!(cell_by_name("NOPE").is_err());
1638 }
1639
1640 #[test]
1641 fn test_cell_by_value() {
1642 let cell_type = cell_by_value(8).unwrap();
1643 assert_eq!("NETINFO", cell_type.name());
1644 assert_eq!(8, cell_type.value());
1645 assert!(cell_type.is_fixed_size());
1646
1647 assert!(cell_by_value(85).is_err());
1648 }
1649
1650 #[test]
1651 fn test_versions_cell() {
1652 let versions = vec![1, 2, 3];
1653 let cell = VersionsCell::new(versions.clone());
1654 let packed = cell.pack(&LinkProtocol::new(2));
1655
1656 let expected = b"\x00\x00\x07\x00\x06\x00\x01\x00\x02\x00\x03";
1657 assert_eq!(expected.to_vec(), packed);
1658
1659 let (unpacked, _) = Cell::pop(&packed, 2).unwrap();
1660 match unpacked {
1661 Cell::Versions(v) => assert_eq!(versions, v.versions),
1662 _ => panic!("Expected VersionsCell"),
1663 }
1664 }
1665
1666 #[test]
1667 fn test_versions_cell_empty() {
1668 let cell = VersionsCell::new(vec![]);
1669 let packed = cell.pack(&LinkProtocol::new(2));
1670
1671 let expected = b"\x00\x00\x07\x00\x00";
1672 assert_eq!(expected.to_vec(), packed);
1673 }
1674
1675 #[test]
1676 fn test_vpadding_cell() {
1677 let cell = VPaddingCell::with_payload(vec![]);
1678 let packed = cell.pack(&LinkProtocol::new(2));
1679
1680 let expected = b"\x00\x00\x80\x00\x00";
1681 assert_eq!(expected.to_vec(), packed);
1682
1683 let (unpacked, _) = Cell::pop(&packed, 2).unwrap();
1684 match unpacked {
1685 Cell::VPadding(v) => assert!(v.payload.is_empty()),
1686 _ => panic!("Expected VPaddingCell"),
1687 }
1688 }
1689
1690 #[test]
1691 fn test_vpadding_cell_with_data() {
1692 let cell = VPaddingCell::with_payload(vec![0x08, 0x11]);
1693 let packed = cell.pack(&LinkProtocol::new(2));
1694
1695 let expected = b"\x00\x00\x80\x00\x02\x08\x11";
1696 assert_eq!(expected.to_vec(), packed);
1697 }
1698
1699 #[test]
1700 fn test_destroy_cell() {
1701 let cell = DestroyCell::new(2147483648, CloseReason::None);
1702 let packed = cell.pack(&LinkProtocol::new(5));
1703
1704 assert_eq!(0x80, packed[0]);
1705 assert_eq!(0x00, packed[1]);
1706 assert_eq!(0x00, packed[2]);
1707 assert_eq!(0x00, packed[3]);
1708 assert_eq!(4, packed[4]);
1709 assert_eq!(0, packed[5]);
1710
1711 let (unpacked, _) = Cell::pop(&packed, 5).unwrap();
1712 match unpacked {
1713 Cell::Destroy(d) => {
1714 assert_eq!(2147483648, d.circ_id);
1715 assert_eq!(CloseReason::None, d.reason);
1716 }
1717 _ => panic!("Expected DestroyCell"),
1718 }
1719 }
1720
1721 #[test]
1722 fn test_create_fast_cell() {
1723 let key_material: [u8; HASH_LEN] = [
1724 0x92, 0x4f, 0x0c, 0xcb, 0xa8, 0xac, 0xfb, 0xc9, 0x7f, 0xd0, 0x0d, 0x7a, 0x1a, 0x03,
1725 0x75, 0x91, 0xce, 0x61, 0x73, 0xce,
1726 ];
1727 let cell = CreateFastCell::with_key_material(2147483648, key_material);
1728 let packed = cell.pack(&LinkProtocol::new(5));
1729
1730 assert_eq!(0x80, packed[0]);
1731 assert_eq!(0x00, packed[1]);
1732 assert_eq!(0x00, packed[2]);
1733 assert_eq!(0x00, packed[3]);
1734 assert_eq!(5, packed[4]);
1735
1736 let (unpacked, _) = Cell::pop(&packed, 5).unwrap();
1737 match unpacked {
1738 Cell::CreateFast(c) => {
1739 assert_eq!(2147483648, c.circ_id);
1740 assert_eq!(key_material, c.key_material);
1741 }
1742 _ => panic!("Expected CreateFastCell"),
1743 }
1744 }
1745
1746 #[test]
1747 fn test_created_fast_cell() {
1748 let key_material: [u8; HASH_LEN] = [
1749 0x92, 0x4f, 0x0c, 0xcb, 0xa8, 0xac, 0xfb, 0xc9, 0x7f, 0xd0, 0x0d, 0x7a, 0x1a, 0x03,
1750 0x75, 0x91, 0xce, 0x61, 0x73, 0xce,
1751 ];
1752 let derivative_key: [u8; HASH_LEN] = [
1753 0x13, 0x5a, 0x99, 0xb2, 0x1e, 0xb6, 0x05, 0x85, 0x17, 0xfc, 0x1c, 0x00, 0x7b, 0xa9,
1754 0xae, 0x83, 0x5e, 0x4b, 0x99, 0xb2,
1755 ];
1756 let cell = CreatedFastCell::with_key_material(2147483648, key_material, derivative_key);
1757 let packed = cell.pack(&LinkProtocol::new(5));
1758
1759 let (unpacked, _) = Cell::pop(&packed, 5).unwrap();
1760 match unpacked {
1761 Cell::CreatedFast(c) => {
1762 assert_eq!(2147483648, c.circ_id);
1763 assert_eq!(key_material, c.key_material);
1764 assert_eq!(derivative_key, c.derivative_key);
1765 }
1766 _ => panic!("Expected CreatedFastCell"),
1767 }
1768 }
1769
1770 #[test]
1771 fn test_relay_cell() {
1772 let cell = RelayCell::new(1, RelayCommand::BeginDir, vec![], 564346860, 1).unwrap();
1773 let packed = cell.pack(&LinkProtocol::new(2));
1774
1775 let (unpacked, _) = Cell::pop(&packed, 2).unwrap();
1776 match unpacked {
1777 Cell::Relay(r) => {
1778 assert_eq!(1, r.circ_id);
1779 assert_eq!(RelayCommand::BeginDir, r.command);
1780 assert_eq!(564346860, r.digest);
1781 assert_eq!(1, r.stream_id);
1782 }
1783 _ => panic!("Expected RelayCell"),
1784 }
1785 }
1786
1787 #[test]
1788 fn test_certs_cell_empty() {
1789 let cell = CertsCell::new(vec![]);
1790 let packed = cell.pack(&LinkProtocol::new(2));
1791
1792 let expected = b"\x00\x00\x81\x00\x01\x00";
1793 assert_eq!(expected.to_vec(), packed);
1794 }
1795
1796 #[test]
1797 fn test_certs_cell_with_cert() {
1798 let cert = Certificate::from_int(1, vec![]);
1799 let cell = CertsCell::new(vec![cert]);
1800 let packed = cell.pack(&LinkProtocol::new(2));
1801
1802 let expected = b"\x00\x00\x81\x00\x04\x01\x01\x00\x00";
1803 assert_eq!(expected.to_vec(), packed);
1804 }
1805
1806 #[test]
1807 fn test_auth_challenge_cell() {
1808 let challenge: [u8; AUTH_CHALLENGE_SIZE] = [
1809 0x89, 0x59, 0x09, 0x99, 0xb2, 0x1e, 0xd9, 0x2a, 0x56, 0xb6, 0x1b, 0x6e, 0x0a, 0x05,
1810 0xd8, 0x2f, 0xe3, 0x51, 0x48, 0x85, 0x13, 0x5a, 0x17, 0xfc, 0x1c, 0x00, 0x7b, 0xa9,
1811 0xae, 0x83, 0x5e, 0x4b,
1812 ];
1813 let cell = AuthChallengeCell::with_challenge(challenge, vec![1, 3]);
1814 let packed = cell.pack(&LinkProtocol::new(2));
1815
1816 let (unpacked, _) = Cell::pop(&packed, 2).unwrap();
1817 match unpacked {
1818 Cell::AuthChallenge(a) => {
1819 assert_eq!(challenge, a.challenge);
1820 assert_eq!(vec![1, 3], a.methods);
1821 }
1822 _ => panic!("Expected AuthChallengeCell"),
1823 }
1824 }
1825
1826 #[test]
1827 fn test_netinfo_cell() {
1828 use chrono::TimeZone;
1829 let timestamp = Utc.with_ymd_and_hms(2018, 1, 14, 1, 46, 56).unwrap();
1830 let receiver = Address::new("127.0.0.1").unwrap();
1831 let sender = Address::new("97.113.15.2").unwrap();
1832
1833 let cell = NetinfoCell::new(receiver.clone(), vec![sender.clone()], Some(timestamp));
1834 let packed = cell.pack(&LinkProtocol::new(2));
1835
1836 let (unpacked, _) = Cell::pop(&packed, 2).unwrap();
1837 match unpacked {
1838 Cell::Netinfo(n) => {
1839 assert_eq!(timestamp, n.timestamp);
1840 assert_eq!(receiver, n.receiver_address);
1841 assert_eq!(vec![sender], n.sender_addresses);
1842 }
1843 _ => panic!("Expected NetinfoCell"),
1844 }
1845 }
1846
1847 #[test]
1848 fn test_relay_cell_with_data() {
1849 let data = b"GET /tor/server/authority HTTP/1.0\r\n\r\n";
1850 let cell = RelayCell::new(1, RelayCommand::Data, data.to_vec(), 356150752, 1).unwrap();
1851 let packed = cell.pack(&LinkProtocol::new(2));
1852
1853 let (unpacked, _) = Cell::pop(&packed, 2).unwrap();
1854 match unpacked {
1855 Cell::Relay(r) => {
1856 assert_eq!(1, r.circ_id);
1857 assert_eq!(RelayCommand::Data, r.command);
1858 assert_eq!(2, r.command_int);
1859 assert_eq!(data.to_vec(), r.data);
1860 assert_eq!(356150752, r.digest);
1861 assert_eq!(1, r.stream_id);
1862 }
1863 _ => panic!("Expected RelayCell"),
1864 }
1865 }
1866
1867 #[test]
1868 fn test_relay_cell_stream_id_required() {
1869 let result = RelayCell::new(1, RelayCommand::BeginDir, vec![], 0, 0);
1870 assert!(result.is_err());
1871 }
1872
1873 #[test]
1874 fn test_relay_cell_stream_id_disallowed() {
1875 let result = RelayCell::new(1, RelayCommand::Extend, vec![], 0, 1);
1876 assert!(result.is_err());
1877 }
1878
1879 #[test]
1880 fn test_relay_cell_mismatched_data_length() {
1881 let mismatched_data = [
1882 0x00, 0x01, 0x03, 0x02, 0x00, 0x00, 0x00, 0x01, 0x15, 0x3a, 0x6d, 0xe0, 0xFF, 0xFF,
1883 ];
1884 let mut cell_bytes = mismatched_data.to_vec();
1885 cell_bytes.extend(vec![0u8; 498]);
1886
1887 let result = Cell::pop(&cell_bytes, 2);
1888 assert!(result.is_err());
1889 }
1890
1891 #[test]
1892 fn test_versions_cell_protocol_4() {
1893 let versions = vec![1, 2, 3, 4];
1894 let cell = VersionsCell::new(versions.clone());
1895 let packed = cell.pack(&LinkProtocol::new(4));
1896
1897 let expected = b"\x00\x00\x00\x00\x07\x00\x08\x00\x01\x00\x02\x00\x03\x00\x04";
1898 assert_eq!(expected.to_vec(), packed);
1899
1900 let (unpacked, _) = Cell::pop(&packed, 4).unwrap();
1901 match unpacked {
1902 Cell::Versions(v) => assert_eq!(versions, v.versions),
1903 _ => panic!("Expected VersionsCell"),
1904 }
1905 }
1906
1907 #[test]
1908 fn test_certs_cell_truncated() {
1909 let truncated = b"\x00\x00\x81\x00\x05\x02\x01\x00\x01\x08";
1910 let result = Cell::pop(truncated, 2);
1911 assert!(result.is_err());
1912 }
1913
1914 #[test]
1915 fn test_certs_cell_cert_too_short() {
1916 let short_cert = b"\x00\x00\x81\x00\x05\x01\x01\x00\x03\x08";
1917 let result = Cell::pop(short_cert, 2);
1918 assert!(result.is_err());
1919 }
1920
1921 #[test]
1922 fn test_auth_challenge_cell_truncated() {
1923 let challenge: [u8; 32] = [
1924 0x89, 0x59, 0x09, 0x99, 0xb2, 0x1e, 0xd9, 0x2a, 0x56, 0xb6, 0x1b, 0x6e, 0x0a, 0x05,
1925 0xd8, 0x2f, 0xe3, 0x51, 0x48, 0x85, 0x13, 0x5a, 0x17, 0xfc, 0x1c, 0x00, 0x7b, 0xa9,
1926 0xae, 0x83, 0x5e, 0x4b,
1927 ];
1928 let mut truncated = vec![0x00, 0x00, 0x82, 0x00, 0x26];
1929 truncated.extend_from_slice(&challenge[..10]);
1930 truncated.extend_from_slice(&[0x00, 0x02, 0x00, 0x01, 0x00, 0x03]);
1931
1932 let result = Cell::pop(&truncated, 2);
1933 assert!(result.is_err());
1934 }
1935
1936 #[test]
1937 fn test_auth_challenge_cell_methods_truncated() {
1938 let challenge: [u8; 32] = [
1939 0x89, 0x59, 0x09, 0x99, 0xb2, 0x1e, 0xd9, 0x2a, 0x56, 0xb6, 0x1b, 0x6e, 0x0a, 0x05,
1940 0xd8, 0x2f, 0xe3, 0x51, 0x48, 0x85, 0x13, 0x5a, 0x17, 0xfc, 0x1c, 0x00, 0x7b, 0xa9,
1941 0xae, 0x83, 0x5e, 0x4b,
1942 ];
1943 let mut truncated = vec![0x00, 0x00, 0x82, 0x00, 0x26];
1944 truncated.extend_from_slice(&challenge);
1945 truncated.extend_from_slice(&[0x00, 0x03, 0x00, 0x01, 0x00, 0x03]);
1946
1947 let result = Cell::pop(&truncated, 2);
1948 assert!(result.is_err());
1949 }
1950
1951 #[test]
1952 fn test_padding_cell_roundtrip() {
1953 let payload = vec![0x42u8; FIXED_PAYLOAD_LEN];
1954 let cell = PaddingCell::with_payload(payload.clone()).unwrap();
1955 let packed = cell.pack(&LinkProtocol::new(2));
1956
1957 let (unpacked, _) = Cell::pop(&packed, 2).unwrap();
1958 match unpacked {
1959 Cell::Padding(p) => {
1960 assert_eq!(payload, p.payload);
1961 }
1962 _ => panic!("Expected PaddingCell"),
1963 }
1964 }
1965
1966 #[test]
1967 fn test_padding_cell_wrong_size() {
1968 let result = PaddingCell::with_payload(vec![0x42u8; 100]);
1969 assert!(result.is_err());
1970 }
1971
1972 #[test]
1973 fn test_cell_unpack_all() {
1974 let versions = VersionsCell::new(vec![3, 4, 5]);
1975 let vpadding = VPaddingCell::with_payload(vec![0x08, 0x11]);
1976
1977 let mut combined = versions.pack(&LinkProtocol::new(2));
1978 combined.extend(vpadding.pack(&LinkProtocol::new(2)));
1979
1980 let cells = Cell::unpack_all(&combined, 2).unwrap();
1981 assert_eq!(2, cells.len());
1982
1983 match &cells[0] {
1984 Cell::Versions(v) => assert_eq!(vec![3, 4, 5], v.versions),
1985 _ => panic!("Expected VersionsCell"),
1986 }
1987
1988 match &cells[1] {
1989 Cell::VPadding(v) => assert_eq!(vec![0x08, 0x11], v.payload),
1990 _ => panic!("Expected VPaddingCell"),
1991 }
1992 }
1993
1994 #[test]
1995 fn test_destroy_cell_reasons() {
1996 let reasons = [
1997 (CloseReason::None, 0),
1998 (CloseReason::Protocol, 1),
1999 (CloseReason::Requested, 3),
2000 (CloseReason::Finished, 9),
2001 ];
2002
2003 for (reason, reason_int) in reasons {
2004 let cell = DestroyCell::new(1, reason);
2005 let packed = cell.pack(&LinkProtocol::new(5));
2006
2007 let (unpacked, _) = Cell::pop(&packed, 5).unwrap();
2008 match unpacked {
2009 Cell::Destroy(d) => {
2010 assert_eq!(1, d.circ_id);
2011 assert_eq!(reason, d.reason);
2012 assert_eq!(reason_int, d.reason_int);
2013 }
2014 _ => panic!("Expected DestroyCell"),
2015 }
2016 }
2017 }
2018}