1use std::{borrow::Cow, io::Cursor};
2
3use rand::{thread_rng, Rng};
4use serde::{Deserialize, Serialize};
5
6use crate::{
7 clock::NtpClock,
8 identifiers::ReferenceId,
9 io::NonBlockingWrite,
10 keyset::{DecodedServerCookie, KeySet},
11 system::SystemSnapshot,
12 time_types::{NtpDuration, NtpTimestamp, PollInterval},
13 NtpVersion,
14};
15
16use self::{error::ParsingError, extension_fields::ExtensionFieldData, mac::Mac};
17
18mod crypto;
19mod error;
20mod extension_fields;
21mod mac;
22
23pub mod v5;
24
25pub use crypto::{
26 AesSivCmac256, AesSivCmac512, Cipher, CipherHolder, CipherProvider, DecryptError,
27 EncryptResult, NoCipher,
28};
29pub use error::PacketParsingError;
30pub use extension_fields::{ExtensionField, ExtensionHeaderVersion};
31
32#[derive(Debug, Copy, Clone, PartialEq, Eq, Serialize, Deserialize)]
33pub enum NtpLeapIndicator {
34 NoWarning,
35 Leap61,
36 Leap59,
37 Unknown,
41 Unsynchronized,
42}
43
44impl NtpLeapIndicator {
45 fn from_bits(bits: u8) -> NtpLeapIndicator {
48 match bits {
49 0 => NtpLeapIndicator::NoWarning,
50 1 => NtpLeapIndicator::Leap61,
51 2 => NtpLeapIndicator::Leap59,
52 3 => NtpLeapIndicator::Unsynchronized,
53 _ => unreachable!(),
56 }
57 }
58
59 fn to_bits(self) -> u8 {
60 match self {
61 NtpLeapIndicator::NoWarning => 0,
62 NtpLeapIndicator::Leap61 => 1,
63 NtpLeapIndicator::Leap59 => 2,
64 NtpLeapIndicator::Unknown => 3,
65 NtpLeapIndicator::Unsynchronized => 3,
66 }
67 }
68
69 pub fn is_synchronized(&self) -> bool {
70 !matches!(self, Self::Unsynchronized)
71 }
72}
73
74#[derive(Debug, Copy, Clone, PartialEq, Eq)]
75pub enum NtpAssociationMode {
76 Reserved,
77 SymmetricActive,
78 SymmetricPassive,
79 Client,
80 Server,
81 Broadcast,
82 Control,
83 Private,
84}
85
86impl NtpAssociationMode {
87 fn from_bits(bits: u8) -> NtpAssociationMode {
90 match bits {
91 0 => NtpAssociationMode::Reserved,
92 1 => NtpAssociationMode::SymmetricActive,
93 2 => NtpAssociationMode::SymmetricPassive,
94 3 => NtpAssociationMode::Client,
95 4 => NtpAssociationMode::Server,
96 5 => NtpAssociationMode::Broadcast,
97 6 => NtpAssociationMode::Control,
98 7 => NtpAssociationMode::Private,
99 _ => unreachable!(),
102 }
103 }
104
105 fn to_bits(self) -> u8 {
106 match self {
107 NtpAssociationMode::Reserved => 0,
108 NtpAssociationMode::SymmetricActive => 1,
109 NtpAssociationMode::SymmetricPassive => 2,
110 NtpAssociationMode::Client => 3,
111 NtpAssociationMode::Server => 4,
112 NtpAssociationMode::Broadcast => 5,
113 NtpAssociationMode::Control => 6,
114 NtpAssociationMode::Private => 7,
115 }
116 }
117}
118
119#[derive(Debug, Clone, PartialEq, Eq)]
120pub struct NtpPacket<'a> {
121 header: NtpHeader,
122 efdata: ExtensionFieldData<'a>,
123 mac: Option<Mac<'a>>,
124}
125
126#[derive(Debug, Copy, Clone, PartialEq, Eq)]
127pub enum NtpHeader {
128 V3(NtpHeaderV3V4),
129 V4(NtpHeaderV3V4),
130 V5(v5::NtpHeaderV5),
131}
132
133#[derive(Debug, Copy, Clone, PartialEq, Eq)]
134pub struct NtpHeaderV3V4 {
135 leap: NtpLeapIndicator,
136 mode: NtpAssociationMode,
137 stratum: u8,
138 poll: PollInterval,
139 precision: i8,
140 root_delay: NtpDuration,
141 root_dispersion: NtpDuration,
142 reference_id: ReferenceId,
143 reference_timestamp: NtpTimestamp,
144 origin_timestamp: NtpTimestamp,
146 receive_timestamp: NtpTimestamp,
148 transmit_timestamp: NtpTimestamp,
150}
151
152#[derive(Debug, Copy, Clone, PartialEq, Eq)]
153pub struct RequestIdentifier {
154 expected_origin_timestamp: NtpTimestamp,
155 uid: Option<[u8; 32]>,
156}
157
158impl NtpHeaderV3V4 {
159 const WIRE_LENGTH: usize = 48;
160
161 fn new() -> Self {
163 Self {
164 leap: NtpLeapIndicator::NoWarning,
165 mode: NtpAssociationMode::Client,
166 stratum: 0,
167 poll: PollInterval::from_byte(0),
168 precision: 0,
169 root_delay: NtpDuration::default(),
170 root_dispersion: NtpDuration::default(),
171 reference_id: ReferenceId::from_int(0),
172 reference_timestamp: NtpTimestamp::default(),
173 origin_timestamp: NtpTimestamp::default(),
174 receive_timestamp: NtpTimestamp::default(),
175 transmit_timestamp: NtpTimestamp::default(),
176 }
177 }
178
179 fn deserialize(data: &[u8]) -> Result<(Self, usize), ParsingError<std::convert::Infallible>> {
180 if data.len() < Self::WIRE_LENGTH {
181 return Err(ParsingError::IncorrectLength);
182 }
183
184 Ok((
185 Self {
186 leap: NtpLeapIndicator::from_bits((data[0] & 0xC0) >> 6),
187 mode: NtpAssociationMode::from_bits(data[0] & 0x07),
188 stratum: data[1],
189 poll: PollInterval::from_byte(data[2]),
190 precision: data[3] as i8,
191 root_delay: NtpDuration::from_bits_short(data[4..8].try_into().unwrap()),
192 root_dispersion: NtpDuration::from_bits_short(data[8..12].try_into().unwrap()),
193 reference_id: ReferenceId::from_bytes(data[12..16].try_into().unwrap()),
194 reference_timestamp: NtpTimestamp::from_bits(data[16..24].try_into().unwrap()),
195 origin_timestamp: NtpTimestamp::from_bits(data[24..32].try_into().unwrap()),
196 receive_timestamp: NtpTimestamp::from_bits(data[32..40].try_into().unwrap()),
197 transmit_timestamp: NtpTimestamp::from_bits(data[40..48].try_into().unwrap()),
198 },
199 Self::WIRE_LENGTH,
200 ))
201 }
202
203 fn serialize(&self, mut w: impl NonBlockingWrite, version: u8) -> std::io::Result<()> {
204 w.write_all(&[(self.leap.to_bits() << 6) | (version << 3) | self.mode.to_bits()])?;
205 w.write_all(&[self.stratum, self.poll.as_byte(), self.precision as u8])?;
206 w.write_all(&self.root_delay.to_bits_short())?;
207 w.write_all(&self.root_dispersion.to_bits_short())?;
208 w.write_all(&self.reference_id.to_bytes())?;
209 w.write_all(&self.reference_timestamp.to_bits())?;
210 w.write_all(&self.origin_timestamp.to_bits())?;
211 w.write_all(&self.receive_timestamp.to_bits())?;
212 w.write_all(&self.transmit_timestamp.to_bits())?;
213 Ok(())
214 }
215
216 fn poll_message(poll_interval: PollInterval) -> (Self, RequestIdentifier) {
217 let mut packet = Self::new();
218 packet.poll = poll_interval;
219 packet.mode = NtpAssociationMode::Client;
220
221 let transmit_timestamp = thread_rng().gen();
226 packet.transmit_timestamp = transmit_timestamp;
227
228 (
229 packet,
230 RequestIdentifier {
231 expected_origin_timestamp: transmit_timestamp,
232 uid: None,
233 },
234 )
235 }
236
237 fn timestamp_response<C: NtpClock>(
238 system: &SystemSnapshot,
239 input: Self,
240 recv_timestamp: NtpTimestamp,
241 clock: &C,
242 ) -> Self {
243 Self {
244 mode: NtpAssociationMode::Server,
245 stratum: system.stratum,
246 origin_timestamp: input.transmit_timestamp,
247 receive_timestamp: recv_timestamp,
248 reference_id: system.reference_id,
249 poll: input.poll,
250 precision: system.time_snapshot.precision.log2(),
251 root_delay: system.time_snapshot.root_delay,
252 root_dispersion: system.time_snapshot.root_dispersion(recv_timestamp),
253 transmit_timestamp: clock.now().expect("Failed to read time"),
255 leap: system.time_snapshot.leap_indicator,
256 reference_timestamp: recv_timestamp.truncated_second_bits(7),
257 }
258 }
259
260 fn rate_limit_response(packet_from_client: Self) -> Self {
261 Self {
262 mode: NtpAssociationMode::Server,
263 stratum: 0, reference_id: ReferenceId::KISS_RATE,
265 origin_timestamp: packet_from_client.transmit_timestamp,
266 ..Self::new()
267 }
268 }
269
270 fn deny_response(packet_from_client: Self) -> Self {
271 Self {
272 mode: NtpAssociationMode::Server,
273 stratum: 0, reference_id: ReferenceId::KISS_DENY,
275 origin_timestamp: packet_from_client.transmit_timestamp,
276 ..Self::new()
277 }
278 }
279
280 fn nts_nak_response(packet_from_client: Self) -> Self {
281 Self {
282 mode: NtpAssociationMode::Server,
283 stratum: 0,
284 reference_id: ReferenceId::KISS_NTSN,
285 origin_timestamp: packet_from_client.transmit_timestamp,
286 ..Self::new()
287 }
288 }
289}
290
291impl<'a> NtpPacket<'a> {
292 pub fn into_owned(self) -> NtpPacket<'static> {
293 NtpPacket::<'static> {
294 header: self.header,
295 efdata: self.efdata.into_owned(),
296 mac: self.mac.map(|v| v.into_owned()),
297 }
298 }
299
300 #[allow(clippy::result_large_err)]
301 pub fn deserialize(
302 data: &'a [u8],
303 cipher: &(impl CipherProvider + ?Sized),
304 ) -> Result<(Self, Option<DecodedServerCookie>), PacketParsingError<'a>> {
305 if data.is_empty() {
306 return Err(PacketParsingError::IncorrectLength);
307 }
308
309 let version = (data[0] & 0b0011_1000) >> 3;
310
311 match version {
312 3 => {
313 let (header, header_size) =
314 NtpHeaderV3V4::deserialize(data).map_err(|e| e.generalize())?;
315 let mac = if header_size != data.len() {
316 Some(Mac::deserialize(&data[header_size..]).map_err(|e| e.generalize())?)
317 } else {
318 None
319 };
320 Ok((
321 NtpPacket {
322 header: NtpHeader::V3(header),
323 efdata: ExtensionFieldData::default(),
324 mac,
325 },
326 None,
327 ))
328 }
329 4 => {
330 let (header, header_size) =
331 NtpHeaderV3V4::deserialize(data).map_err(|e| e.generalize())?;
332
333 let construct_packet = |remaining_bytes: &'a [u8], efdata| {
334 let mac = if !remaining_bytes.is_empty() {
335 Some(Mac::deserialize(remaining_bytes)?)
336 } else {
337 None
338 };
339
340 let packet = NtpPacket {
341 header: NtpHeader::V4(header),
342 efdata,
343 mac,
344 };
345
346 Ok::<_, ParsingError<std::convert::Infallible>>(packet)
347 };
348
349 match ExtensionFieldData::deserialize(
350 data,
351 header_size,
352 cipher,
353 ExtensionHeaderVersion::V4,
354 ) {
355 Ok(decoded) => {
356 let packet = construct_packet(decoded.remaining_bytes, decoded.efdata)
357 .map_err(|e| e.generalize())?;
358
359 Ok((packet, decoded.cookie))
360 }
361 Err(e) => {
362 let invalid = e.get_decrypt_error()?;
364
365 let packet = construct_packet(invalid.remaining_bytes, invalid.efdata)
366 .map_err(|e| e.generalize())?;
367
368 Err(ParsingError::DecryptError(packet))
369 }
370 }
371 }
372 5 => {
373 let (header, header_size) =
374 v5::NtpHeaderV5::deserialize(data).map_err(|e| e.generalize())?;
375
376 let construct_packet = |remaining_bytes: &'a [u8], efdata| {
377 let mac = if !remaining_bytes.is_empty() {
378 Some(Mac::deserialize(remaining_bytes)?)
379 } else {
380 None
381 };
382
383 let packet = NtpPacket {
384 header: NtpHeader::V5(header),
385 efdata,
386 mac,
387 };
388
389 Ok::<_, ParsingError<std::convert::Infallible>>(packet)
390 };
391
392 let res_packet = match ExtensionFieldData::deserialize(
393 data,
394 header_size,
395 cipher,
396 ExtensionHeaderVersion::V5,
397 ) {
398 Ok(decoded) => {
399 let packet = construct_packet(decoded.remaining_bytes, decoded.efdata)
400 .map_err(|e| e.generalize())?;
401
402 Ok((packet, decoded.cookie))
403 }
404 Err(e) => {
405 let invalid = e.get_decrypt_error()?;
407
408 let packet = construct_packet(invalid.remaining_bytes, invalid.efdata)
409 .map_err(|e| e.generalize())?;
410
411 Err(ParsingError::DecryptError(packet))
412 }
413 };
414
415 let (packet, cookie) = res_packet?;
416
417 match packet.draft_id() {
418 Some(id) if id == v5::DRAFT_VERSION => Ok((packet, cookie)),
419 received @ (Some(_) | None) => {
420 tracing::error!(
421 expected = v5::DRAFT_VERSION,
422 received,
423 "Mismatched draft ID ignoring packet!"
424 );
425 Err(ParsingError::V5(v5::V5Error::InvalidDraftIdentification))
426 }
427 }
428 }
429 _ => Err(PacketParsingError::InvalidVersion(version)),
430 }
431 }
432
433 #[cfg(test)]
434 pub fn serialize_without_encryption_vec(
435 &self,
436 desired_size: Option<usize>,
437 ) -> std::io::Result<Vec<u8>> {
438 let mut buffer = vec![0u8; 1024];
439 let mut cursor = Cursor::new(buffer.as_mut_slice());
440
441 self.serialize(&mut cursor, &NoCipher, desired_size)?;
442
443 let length = cursor.position() as usize;
444 let buffer = cursor.into_inner()[..length].to_vec();
445
446 Ok(buffer)
447 }
448
449 pub fn serialize(
450 &self,
451 w: &mut Cursor<&mut [u8]>,
452 cipher: &(impl CipherProvider + ?Sized),
453 desired_size: Option<usize>,
454 ) -> std::io::Result<()> {
455 let start = w.position();
456
457 match self.header {
458 NtpHeader::V3(header) => header.serialize(&mut *w, 3)?,
459 NtpHeader::V4(header) => header.serialize(&mut *w, 4)?,
460 NtpHeader::V5(header) => header.serialize(&mut *w)?,
461 };
462
463 match self.header {
464 NtpHeader::V3(_) => { }
465 NtpHeader::V4(_) => {
466 self.efdata
467 .serialize(&mut *w, cipher, ExtensionHeaderVersion::V4)?
468 }
469 NtpHeader::V5(_) => {
470 self.efdata
471 .serialize(&mut *w, cipher, ExtensionHeaderVersion::V5)?
472 }
473 }
474
475 if let Some(ref mac) = self.mac {
476 mac.serialize(&mut *w)?;
477 }
478
479 if let Some(desired_size) = desired_size {
480 let written = (w.position() - start) as usize;
481 if desired_size > written {
482 ExtensionField::Padding(desired_size - written).serialize(
483 w,
484 4,
485 ExtensionHeaderVersion::V5,
486 )?;
487 }
488 }
489
490 Ok(())
491 }
492
493 pub fn nts_poll_message(
494 cookie: &'a [u8],
495 new_cookies: u8,
496 poll_interval: PollInterval,
497 ) -> (NtpPacket<'static>, RequestIdentifier) {
498 let (header, id) = NtpHeaderV3V4::poll_message(poll_interval);
499
500 let identifier: [u8; 32] = rand::thread_rng().gen();
501
502 let mut authenticated = vec![
503 ExtensionField::UniqueIdentifier(identifier.to_vec().into()),
504 ExtensionField::NtsCookie(cookie.to_vec().into()),
505 ];
506
507 for _ in 1..new_cookies {
508 authenticated.push(ExtensionField::NtsCookiePlaceholder {
509 cookie_length: cookie.len() as u16,
510 });
511 }
512
513 (
514 NtpPacket {
515 header: NtpHeader::V4(header),
516 efdata: ExtensionFieldData {
517 authenticated,
518 encrypted: vec![],
519 untrusted: vec![],
520 },
521 mac: None,
522 },
523 RequestIdentifier {
524 uid: Some(identifier),
525 ..id
526 },
527 )
528 }
529
530 pub fn nts_poll_message_v5(
531 cookie: &'a [u8],
532 new_cookies: u8,
533 poll_interval: PollInterval,
534 ) -> (NtpPacket<'static>, RequestIdentifier) {
535 let (header, id) = v5::NtpHeaderV5::poll_message(poll_interval);
536
537 let identifier: [u8; 32] = rand::thread_rng().gen();
538
539 let mut authenticated = vec![
540 ExtensionField::UniqueIdentifier(identifier.to_vec().into()),
541 ExtensionField::NtsCookie(cookie.to_vec().into()),
542 ];
543
544 for _ in 1..new_cookies {
545 authenticated.push(ExtensionField::NtsCookiePlaceholder {
546 cookie_length: cookie.len() as u16,
547 });
548 }
549
550 let draft_id = ExtensionField::DraftIdentification(Cow::Borrowed(v5::DRAFT_VERSION));
551 authenticated.push(draft_id);
552
553 (
554 NtpPacket {
555 header: NtpHeader::V5(header),
556 efdata: ExtensionFieldData {
557 authenticated,
558 encrypted: vec![],
559 untrusted: vec![],
560 },
561 mac: None,
562 },
563 RequestIdentifier {
564 uid: Some(identifier),
565 ..id
566 },
567 )
568 }
569
570 pub fn poll_message(poll_interval: PollInterval) -> (Self, RequestIdentifier) {
571 let (header, id) = NtpHeaderV3V4::poll_message(poll_interval);
572 (
573 NtpPacket {
574 header: NtpHeader::V4(header),
575 efdata: Default::default(),
576 mac: None,
577 },
578 id,
579 )
580 }
581
582 pub fn poll_message_upgrade_request(poll_interval: PollInterval) -> (Self, RequestIdentifier) {
583 let (mut header, id) = NtpHeaderV3V4::poll_message(poll_interval);
584
585 header.reference_timestamp = v5::UPGRADE_TIMESTAMP;
586
587 (
588 NtpPacket {
589 header: NtpHeader::V4(header),
590 efdata: ExtensionFieldData {
591 authenticated: vec![],
592 encrypted: vec![],
593 untrusted: vec![],
594 },
595 mac: None,
596 },
597 id,
598 )
599 }
600
601 pub fn poll_message_v5(poll_interval: PollInterval) -> (Self, RequestIdentifier) {
602 let (header, id) = v5::NtpHeaderV5::poll_message(poll_interval);
603
604 let draft_id = ExtensionField::DraftIdentification(Cow::Borrowed(v5::DRAFT_VERSION));
605
606 (
607 NtpPacket {
608 header: NtpHeader::V5(header),
609 efdata: ExtensionFieldData {
610 authenticated: vec![],
611 encrypted: vec![],
612 untrusted: vec![draft_id],
613 },
614 mac: None,
615 },
616 id,
617 )
618 }
619
620 pub fn timestamp_response<C: NtpClock>(
621 system: &SystemSnapshot,
622 input: Self,
623 recv_timestamp: NtpTimestamp,
624 clock: &C,
625 ) -> Self {
626 match &input.header {
627 NtpHeader::V3(header) => NtpPacket {
628 header: NtpHeader::V3(NtpHeaderV3V4::timestamp_response(
629 system,
630 *header,
631 recv_timestamp,
632 clock,
633 )),
634 efdata: Default::default(),
635 mac: None,
636 },
637 NtpHeader::V4(header) => {
638 let mut response_header =
639 NtpHeaderV3V4::timestamp_response(system, *header, recv_timestamp, clock);
640
641 if header.reference_timestamp == v5::UPGRADE_TIMESTAMP {
644 response_header.reference_timestamp = v5::UPGRADE_TIMESTAMP;
645 };
646
647 NtpPacket {
648 header: NtpHeader::V4(response_header),
649 efdata: ExtensionFieldData {
650 authenticated: vec![],
651 encrypted: vec![],
652 untrusted: input
654 .efdata
655 .untrusted
656 .into_iter()
657 .chain(input.efdata.authenticated)
658 .filter(|ef| matches!(ef, ExtensionField::UniqueIdentifier(_)))
659 .collect(),
660 },
661 mac: None,
662 }
663 }
664 NtpHeader::V5(header) => NtpPacket {
665 header: NtpHeader::V5(v5::NtpHeaderV5::timestamp_response(
667 system,
668 *header,
669 recv_timestamp,
670 clock,
671 )),
672 efdata: ExtensionFieldData {
673 authenticated: vec![],
674 encrypted: vec![],
675 untrusted: input
677 .efdata
678 .untrusted
679 .into_iter()
680 .chain(input.efdata.authenticated)
681 .filter_map(|ef| match ef {
682 uid @ ExtensionField::UniqueIdentifier(_) => Some(uid),
683 ExtensionField::ReferenceIdRequest(req) => {
684 let response = req.to_response(&system.bloom_filter)?;
685 Some(ExtensionField::ReferenceIdResponse(response).into_owned())
686 }
687 _ => None,
688 })
689 .chain(std::iter::once(ExtensionField::DraftIdentification(
690 Cow::Borrowed(v5::DRAFT_VERSION),
691 )))
692 .collect(),
693 },
694 mac: None,
695 },
696 }
697 }
698
699 fn draft_id(&self) -> Option<&'_ str> {
700 self.efdata
701 .untrusted
702 .iter()
703 .chain(self.efdata.authenticated.iter())
704 .find_map(|ef| match ef {
705 ExtensionField::DraftIdentification(id) => Some(&**id),
706 _ => None,
707 })
708 }
709
710 pub fn nts_timestamp_response<C: NtpClock>(
711 system: &SystemSnapshot,
712 input: Self,
713 recv_timestamp: NtpTimestamp,
714 clock: &C,
715 cookie: &DecodedServerCookie,
716 keyset: &KeySet,
717 ) -> Self {
718 match input.header {
719 NtpHeader::V3(_) => unreachable!("NTS shouldn't work with NTPv3"),
720 NtpHeader::V4(header) => NtpPacket {
721 header: NtpHeader::V4(NtpHeaderV3V4::timestamp_response(
722 system,
723 header,
724 recv_timestamp,
725 clock,
726 )),
727 efdata: ExtensionFieldData {
728 encrypted: input
729 .efdata
730 .authenticated
731 .iter()
732 .chain(input.efdata.encrypted.iter())
733 .filter_map(|f| match f {
734 ExtensionField::NtsCookiePlaceholder { cookie_length } => {
735 let new_cookie = keyset.encode_cookie(cookie);
736 if new_cookie.len() > *cookie_length as usize {
737 None
738 } else {
739 Some(ExtensionField::NtsCookie(Cow::Owned(new_cookie)))
740 }
741 }
742 ExtensionField::NtsCookie(old_cookie) => {
743 let new_cookie = keyset.encode_cookie(cookie);
744 if new_cookie.len() > old_cookie.len() {
745 None
746 } else {
747 Some(ExtensionField::NtsCookie(Cow::Owned(new_cookie)))
748 }
749 }
750 _ => None,
751 })
752 .collect(),
753 authenticated: input
754 .efdata
755 .authenticated
756 .into_iter()
757 .filter(|ef| matches!(ef, ExtensionField::UniqueIdentifier(_)))
758 .collect(),
759 untrusted: vec![],
761 },
762 mac: None,
763 },
764 NtpHeader::V5(header) => NtpPacket {
765 header: NtpHeader::V5(v5::NtpHeaderV5::timestamp_response(
766 system,
767 header,
768 recv_timestamp,
769 clock,
770 )),
771 efdata: ExtensionFieldData {
772 encrypted: input
773 .efdata
774 .authenticated
775 .iter()
776 .chain(input.efdata.encrypted.iter())
777 .filter_map(|f| match f {
778 ExtensionField::NtsCookiePlaceholder { cookie_length } => {
779 let new_cookie = keyset.encode_cookie(cookie);
780 if new_cookie.len() > *cookie_length as usize {
781 None
782 } else {
783 Some(ExtensionField::NtsCookie(Cow::Owned(new_cookie)))
784 }
785 }
786 ExtensionField::NtsCookie(old_cookie) => {
787 let new_cookie = keyset.encode_cookie(cookie);
788 if new_cookie.len() > old_cookie.len() {
789 None
790 } else {
791 Some(ExtensionField::NtsCookie(Cow::Owned(new_cookie)))
792 }
793 }
794 _ => None,
795 })
796 .collect(),
797 authenticated: input
798 .efdata
799 .authenticated
800 .into_iter()
801 .filter_map(|ef| match ef {
802 uid @ ExtensionField::UniqueIdentifier(_) => Some(uid),
803 ExtensionField::ReferenceIdRequest(req) => {
804 let response = req.to_response(&system.bloom_filter)?;
805 Some(ExtensionField::ReferenceIdResponse(response).into_owned())
806 }
807 _ => None,
808 })
809 .chain(std::iter::once(ExtensionField::DraftIdentification(
810 Cow::Borrowed(v5::DRAFT_VERSION),
811 )))
812 .collect(),
813 untrusted: vec![],
814 },
815 mac: None,
816 },
817 }
818 }
819
820 pub fn rate_limit_response(packet_from_client: Self) -> Self {
821 match packet_from_client.header {
822 NtpHeader::V3(header) => NtpPacket {
823 header: NtpHeader::V3(NtpHeaderV3V4::rate_limit_response(header)),
824 efdata: Default::default(),
825 mac: None,
826 },
827 NtpHeader::V4(header) => NtpPacket {
828 header: NtpHeader::V4(NtpHeaderV3V4::rate_limit_response(header)),
829 efdata: ExtensionFieldData {
830 authenticated: vec![],
831 encrypted: vec![],
832 untrusted: packet_from_client
834 .efdata
835 .untrusted
836 .into_iter()
837 .chain(packet_from_client.efdata.authenticated)
838 .filter(|ef| matches!(ef, ExtensionField::UniqueIdentifier(_)))
839 .collect(),
840 },
841 mac: None,
842 },
843 NtpHeader::V5(header) => NtpPacket {
844 header: NtpHeader::V5(v5::NtpHeaderV5::rate_limit_response(header)),
845 efdata: ExtensionFieldData {
846 authenticated: vec![],
847 encrypted: vec![],
848 untrusted: packet_from_client
850 .efdata
851 .untrusted
852 .into_iter()
853 .chain(packet_from_client.efdata.authenticated)
854 .filter(|ef| matches!(ef, ExtensionField::UniqueIdentifier(_)))
855 .chain(std::iter::once(ExtensionField::DraftIdentification(
856 Cow::Borrowed(v5::DRAFT_VERSION),
857 )))
858 .collect(),
859 },
860 mac: None,
861 },
862 }
863 }
864
865 pub fn nts_rate_limit_response(packet_from_client: Self) -> Self {
866 match packet_from_client.header {
867 NtpHeader::V3(_) => unreachable!("NTS shouldn't work with NTPv3"),
868 NtpHeader::V4(header) => NtpPacket {
869 header: NtpHeader::V4(NtpHeaderV3V4::rate_limit_response(header)),
870 efdata: ExtensionFieldData {
871 authenticated: packet_from_client
872 .efdata
873 .authenticated
874 .into_iter()
875 .filter(|ef| matches!(ef, ExtensionField::UniqueIdentifier(_)))
876 .collect(),
877 encrypted: vec![],
878 untrusted: vec![],
879 },
880 mac: None,
881 },
882 NtpHeader::V5(header) => NtpPacket {
883 header: NtpHeader::V5(v5::NtpHeaderV5::rate_limit_response(header)),
884 efdata: ExtensionFieldData {
885 authenticated: packet_from_client
886 .efdata
887 .authenticated
888 .into_iter()
889 .filter(|ef| matches!(ef, ExtensionField::UniqueIdentifier(_)))
890 .chain(std::iter::once(ExtensionField::DraftIdentification(
891 Cow::Borrowed(v5::DRAFT_VERSION),
892 )))
893 .collect(),
894 encrypted: vec![],
895 untrusted: vec![],
896 },
897 mac: None,
898 },
899 }
900 }
901
902 pub fn deny_response(packet_from_client: Self) -> Self {
903 match packet_from_client.header {
904 NtpHeader::V3(header) => NtpPacket {
905 header: NtpHeader::V3(NtpHeaderV3V4::deny_response(header)),
906 efdata: Default::default(),
907 mac: None,
908 },
909 NtpHeader::V4(header) => NtpPacket {
910 header: NtpHeader::V4(NtpHeaderV3V4::deny_response(header)),
911 efdata: ExtensionFieldData {
912 authenticated: vec![],
913 encrypted: vec![],
914 untrusted: packet_from_client
916 .efdata
917 .untrusted
918 .into_iter()
919 .chain(packet_from_client.efdata.authenticated)
920 .filter(|ef| matches!(ef, ExtensionField::UniqueIdentifier(_)))
921 .collect(),
922 },
923 mac: None,
924 },
925 NtpHeader::V5(header) => NtpPacket {
926 header: NtpHeader::V5(v5::NtpHeaderV5::deny_response(header)),
927 efdata: ExtensionFieldData {
928 authenticated: vec![],
929 encrypted: vec![],
930 untrusted: packet_from_client
932 .efdata
933 .untrusted
934 .into_iter()
935 .chain(packet_from_client.efdata.authenticated)
936 .filter(|ef| matches!(ef, ExtensionField::UniqueIdentifier(_)))
937 .chain(std::iter::once(ExtensionField::DraftIdentification(
938 Cow::Borrowed(v5::DRAFT_VERSION),
939 )))
940 .collect(),
941 },
942 mac: None,
943 },
944 }
945 }
946
947 pub fn nts_deny_response(packet_from_client: Self) -> Self {
948 match packet_from_client.header {
949 NtpHeader::V3(_) => unreachable!("NTS shouldn't work with NTPv3"),
950 NtpHeader::V4(header) => NtpPacket {
951 header: NtpHeader::V4(NtpHeaderV3V4::deny_response(header)),
952 efdata: ExtensionFieldData {
953 authenticated: packet_from_client
954 .efdata
955 .authenticated
956 .into_iter()
957 .filter(|ef| matches!(ef, ExtensionField::UniqueIdentifier(_)))
958 .collect(),
959 encrypted: vec![],
960 untrusted: vec![],
961 },
962 mac: None,
963 },
964 NtpHeader::V5(header) => NtpPacket {
965 header: NtpHeader::V5(v5::NtpHeaderV5::deny_response(header)),
966 efdata: ExtensionFieldData {
967 authenticated: packet_from_client
968 .efdata
969 .authenticated
970 .into_iter()
971 .filter(|ef| matches!(ef, ExtensionField::UniqueIdentifier(_)))
972 .chain(std::iter::once(ExtensionField::DraftIdentification(
973 Cow::Borrowed(v5::DRAFT_VERSION),
974 )))
975 .collect(),
976 encrypted: vec![],
977 untrusted: vec![],
978 },
979 mac: None,
980 },
981 }
982 }
983
984 pub fn nts_nak_response(packet_from_client: Self) -> Self {
985 match packet_from_client.header {
986 NtpHeader::V3(_) => unreachable!("NTS shouldn't work with NTPv3"),
987 NtpHeader::V4(header) => NtpPacket {
988 header: NtpHeader::V4(NtpHeaderV3V4::nts_nak_response(header)),
989 efdata: ExtensionFieldData {
990 authenticated: vec![],
991 encrypted: vec![],
992 untrusted: packet_from_client
993 .efdata
994 .untrusted
995 .into_iter()
996 .chain(packet_from_client.efdata.authenticated)
997 .filter(|ef| matches!(ef, ExtensionField::UniqueIdentifier(_)))
998 .collect(),
999 },
1000 mac: None,
1001 },
1002 NtpHeader::V5(header) => NtpPacket {
1003 header: NtpHeader::V5(v5::NtpHeaderV5::nts_nak_response(header)),
1004 efdata: ExtensionFieldData {
1005 authenticated: vec![],
1006 encrypted: vec![],
1007 untrusted: packet_from_client
1008 .efdata
1009 .untrusted
1010 .into_iter()
1011 .chain(packet_from_client.efdata.authenticated)
1012 .filter(|ef| matches!(ef, ExtensionField::UniqueIdentifier(_)))
1013 .chain(std::iter::once(ExtensionField::DraftIdentification(
1014 Cow::Borrowed(v5::DRAFT_VERSION),
1015 )))
1016 .collect(),
1017 },
1018 mac: None,
1019 },
1020 }
1021 }
1022}
1023
1024impl<'a> NtpPacket<'a> {
1025 pub fn new_cookies<'b: 'a>(&'b self) -> impl Iterator<Item = Vec<u8>> + 'b {
1026 self.efdata.encrypted.iter().filter_map(|ef| match ef {
1027 ExtensionField::NtsCookie(cookie) => Some(cookie.to_vec()),
1028 _ => None,
1029 })
1030 }
1031
1032 pub fn version(&self) -> NtpVersion {
1033 match self.header {
1034 NtpHeader::V3(_) => NtpVersion::V3,
1035 NtpHeader::V4(_) => NtpVersion::V4,
1036 NtpHeader::V5(_) => NtpVersion::V5,
1037 }
1038 }
1039
1040 pub fn header(&self) -> NtpHeader {
1041 self.header
1042 }
1043
1044 pub fn leap(&self) -> NtpLeapIndicator {
1045 match self.header {
1046 NtpHeader::V3(header) => header.leap,
1047 NtpHeader::V4(header) => header.leap,
1048 NtpHeader::V5(header) => header.leap,
1049 }
1050 }
1051
1052 pub fn mode(&self) -> NtpAssociationMode {
1053 match self.header {
1054 NtpHeader::V3(header) => header.mode,
1055 NtpHeader::V4(header) => header.mode,
1056
1057 NtpHeader::V5(header) => match header.mode {
1059 v5::NtpMode::Request => NtpAssociationMode::Client,
1060 v5::NtpMode::Response => NtpAssociationMode::Server,
1061 },
1062 }
1063 }
1064
1065 pub fn poll(&self) -> PollInterval {
1066 match self.header {
1067 NtpHeader::V3(h) | NtpHeader::V4(h) => h.poll,
1068 NtpHeader::V5(h) => h.poll,
1069 }
1070 }
1071
1072 pub fn stratum(&self) -> u8 {
1073 match self.header {
1074 NtpHeader::V3(header) => header.stratum,
1075 NtpHeader::V4(header) => header.stratum,
1076 NtpHeader::V5(header) => header.stratum,
1077 }
1078 }
1079
1080 pub fn precision(&self) -> i8 {
1081 match self.header {
1082 NtpHeader::V3(header) => header.precision,
1083 NtpHeader::V4(header) => header.precision,
1084 NtpHeader::V5(header) => header.precision,
1085 }
1086 }
1087
1088 pub fn root_delay(&self) -> NtpDuration {
1089 match self.header {
1090 NtpHeader::V3(header) => header.root_delay,
1091 NtpHeader::V4(header) => header.root_delay,
1092 NtpHeader::V5(header) => header.root_delay,
1093 }
1094 }
1095
1096 pub fn root_dispersion(&self) -> NtpDuration {
1097 match self.header {
1098 NtpHeader::V3(header) => header.root_dispersion,
1099 NtpHeader::V4(header) => header.root_dispersion,
1100 NtpHeader::V5(header) => header.root_dispersion,
1101 }
1102 }
1103
1104 pub fn receive_timestamp(&self) -> NtpTimestamp {
1105 match self.header {
1106 NtpHeader::V3(header) => header.receive_timestamp,
1107 NtpHeader::V4(header) => header.receive_timestamp,
1108 NtpHeader::V5(header) => header.receive_timestamp,
1109 }
1110 }
1111
1112 pub fn transmit_timestamp(&self) -> NtpTimestamp {
1113 match self.header {
1114 NtpHeader::V3(header) => header.transmit_timestamp,
1115 NtpHeader::V4(header) => header.transmit_timestamp,
1116 NtpHeader::V5(header) => header.transmit_timestamp,
1117 }
1118 }
1119
1120 pub fn reference_id(&self) -> ReferenceId {
1121 match self.header {
1122 NtpHeader::V3(header) => header.reference_id,
1123 NtpHeader::V4(header) => header.reference_id,
1124 NtpHeader::V5(_header) => ReferenceId::NONE,
1126 }
1127 }
1128
1129 fn kiss_code(&self) -> ReferenceId {
1130 match self.header {
1131 NtpHeader::V3(header) => header.reference_id,
1132 NtpHeader::V4(header) => header.reference_id,
1133 NtpHeader::V5(header) => {
1135 ReferenceId::from_bytes(header.server_cookie.0[..4].try_into().unwrap())
1136 }
1137 }
1138 }
1139
1140 pub fn is_kiss(&self) -> bool {
1141 match self.header {
1142 NtpHeader::V3(header) => header.stratum == 0,
1143 NtpHeader::V4(header) => header.stratum == 0,
1144 NtpHeader::V5(header) => header.stratum == 0,
1145 }
1146 }
1147
1148 pub fn is_kiss_deny(&self) -> bool {
1149 self.is_kiss()
1150 && match self.header {
1151 NtpHeader::V3(_) | NtpHeader::V4(_) => self.kiss_code().is_deny(),
1152 NtpHeader::V5(header) => header.poll == PollInterval::NEVER,
1153 }
1154 }
1155
1156 pub fn is_kiss_rate(&self, own_interval: PollInterval) -> bool {
1157 self.is_kiss()
1158 && match self.header {
1159 NtpHeader::V3(_) | NtpHeader::V4(_) => self.kiss_code().is_rate(),
1160 NtpHeader::V5(header) => {
1161 header.poll > own_interval && header.poll != PollInterval::NEVER
1162 }
1163 }
1164 }
1165
1166 pub fn is_kiss_rstr(&self) -> bool {
1167 self.is_kiss()
1168 && match self.header {
1169 NtpHeader::V3(_) | NtpHeader::V4(_) => self.kiss_code().is_rstr(),
1170 NtpHeader::V5(_) => false,
1171 }
1172 }
1173
1174 pub fn is_kiss_ntsn(&self) -> bool {
1175 self.is_kiss()
1176 && match self.header {
1177 NtpHeader::V3(_) | NtpHeader::V4(_) => self.kiss_code().is_ntsn(),
1178 NtpHeader::V5(header) => header.flags.authnak,
1179 }
1180 }
1181
1182 pub fn is_upgrade(&self) -> bool {
1183 matches!(
1184 self.header,
1185 NtpHeader::V4(NtpHeaderV3V4 {
1186 reference_timestamp: v5::UPGRADE_TIMESTAMP,
1187 ..
1188 }),
1189 )
1190 }
1191
1192 pub fn valid_server_response(&self, identifier: RequestIdentifier, nts_enabled: bool) -> bool {
1193 if let Some(uid) = identifier.uid {
1194 let auth = check_uid_extensionfield(self.efdata.authenticated.iter(), &uid);
1195 let encr = check_uid_extensionfield(self.efdata.encrypted.iter(), &uid);
1196 let untrusted = check_uid_extensionfield(self.efdata.untrusted.iter(), &uid);
1197
1198 let uid_ok = auth != Some(false)
1202 && encr != Some(false)
1203 && (untrusted != Some(false) || (nts_enabled && !self.is_kiss_ntsn()))
1204 && (auth.is_some()
1205 || encr.is_some()
1206 || ((!nts_enabled || self.is_kiss_ntsn()) && untrusted.is_some()));
1207 if !uid_ok {
1208 return false;
1209 }
1210 }
1211 match self.header {
1212 NtpHeader::V3(header) => {
1213 header.origin_timestamp == identifier.expected_origin_timestamp
1214 }
1215 NtpHeader::V4(header) => {
1216 header.origin_timestamp == identifier.expected_origin_timestamp
1217 }
1218 NtpHeader::V5(header) => {
1219 header.client_cookie
1220 == v5::NtpClientCookie::from_ntp_timestamp(identifier.expected_origin_timestamp)
1221 }
1222 }
1223 }
1224
1225 pub fn untrusted_extension_fields(&self) -> impl Iterator<Item = &ExtensionField> {
1226 self.efdata.untrusted.iter()
1227 }
1228
1229 pub fn authenticated_extension_fields(&self) -> impl Iterator<Item = &ExtensionField> {
1230 self.efdata.authenticated.iter()
1231 }
1232
1233 pub fn push_additional(&mut self, ef: ExtensionField<'static>) {
1234 if !self.efdata.authenticated.is_empty() || !self.efdata.encrypted.is_empty() {
1235 self.efdata.authenticated.push(ef);
1236 } else {
1237 self.efdata.untrusted.push(ef);
1238 }
1239 }
1240}
1241
1242fn check_uid_extensionfield<'a, I: IntoIterator<Item = &'a ExtensionField<'a>>>(
1245 iter: I,
1246 uid: &[u8],
1247) -> Option<bool> {
1248 let mut found_uid = false;
1249 for ef in iter {
1250 if let ExtensionField::UniqueIdentifier(pid) = ef {
1251 if pid.len() < uid.len() || &pid[0..uid.len()] != uid {
1252 return Some(false);
1253 }
1254 found_uid = true;
1255 }
1256 }
1257 if found_uid {
1258 Some(true)
1259 } else {
1260 None
1261 }
1262}
1263
1264#[cfg(any(test, feature = "__internal-fuzz", feature = "__internal-test"))]
1265impl NtpPacket<'_> {
1266 pub fn test() -> Self {
1267 Self::default()
1268 }
1269
1270 pub fn set_mode(&mut self, mode: NtpAssociationMode) {
1271 match &mut self.header {
1272 NtpHeader::V3(ref mut header) => header.mode = mode,
1273 NtpHeader::V4(ref mut header) => header.mode = mode,
1274 NtpHeader::V5(ref mut header) => {
1275 header.mode = match mode {
1276 NtpAssociationMode::Client => v5::NtpMode::Request,
1277 NtpAssociationMode::Server => v5::NtpMode::Response,
1278 _ => todo!("NTPv5 can only handle client-server"),
1279 }
1280 }
1281 }
1282 }
1283
1284 pub fn set_origin_timestamp(&mut self, timestamp: NtpTimestamp) {
1285 match &mut self.header {
1286 NtpHeader::V3(ref mut header) => header.origin_timestamp = timestamp,
1287 NtpHeader::V4(ref mut header) => header.origin_timestamp = timestamp,
1288 NtpHeader::V5(ref mut header) => {
1289 header.client_cookie = v5::NtpClientCookie::from_ntp_timestamp(timestamp)
1290 }
1291 }
1292 }
1293
1294 pub fn set_transmit_timestamp(&mut self, timestamp: NtpTimestamp) {
1295 match &mut self.header {
1296 NtpHeader::V3(ref mut header) => header.transmit_timestamp = timestamp,
1297 NtpHeader::V4(ref mut header) => header.transmit_timestamp = timestamp,
1298 NtpHeader::V5(ref mut header) => header.transmit_timestamp = timestamp,
1299 }
1300 }
1301
1302 pub fn set_receive_timestamp(&mut self, timestamp: NtpTimestamp) {
1303 match &mut self.header {
1304 NtpHeader::V3(ref mut header) => header.receive_timestamp = timestamp,
1305 NtpHeader::V4(ref mut header) => header.receive_timestamp = timestamp,
1306 NtpHeader::V5(ref mut header) => header.receive_timestamp = timestamp,
1307 }
1308 }
1309
1310 pub fn set_precision(&mut self, precision: i8) {
1311 match &mut self.header {
1312 NtpHeader::V3(ref mut header) => header.precision = precision,
1313 NtpHeader::V4(ref mut header) => header.precision = precision,
1314 NtpHeader::V5(ref mut header) => header.precision = precision,
1315 }
1316 }
1317
1318 pub fn set_leap(&mut self, leap: NtpLeapIndicator) {
1319 match &mut self.header {
1320 NtpHeader::V3(ref mut header) => header.leap = leap,
1321 NtpHeader::V4(ref mut header) => header.leap = leap,
1322 NtpHeader::V5(ref mut header) => header.leap = leap,
1323 }
1324 }
1325
1326 pub fn set_stratum(&mut self, stratum: u8) {
1327 match &mut self.header {
1328 NtpHeader::V3(ref mut header) => header.stratum = stratum,
1329 NtpHeader::V4(ref mut header) => header.stratum = stratum,
1330 NtpHeader::V5(ref mut header) => header.stratum = stratum,
1331 }
1332 }
1333
1334 pub fn set_reference_id(&mut self, reference_id: ReferenceId) {
1335 match &mut self.header {
1336 NtpHeader::V3(ref mut header) => header.reference_id = reference_id,
1337 NtpHeader::V4(ref mut header) => header.reference_id = reference_id,
1338 NtpHeader::V5(_header) => todo!("NTPv5 does not have reference IDs"),
1339 }
1340 }
1341
1342 pub fn set_root_delay(&mut self, root_delay: NtpDuration) {
1343 match &mut self.header {
1344 NtpHeader::V3(ref mut header) => header.root_delay = root_delay,
1345 NtpHeader::V4(ref mut header) => header.root_delay = root_delay,
1346 NtpHeader::V5(ref mut header) => header.root_delay = root_delay,
1347 }
1348 }
1349
1350 pub fn set_root_dispersion(&mut self, root_dispersion: NtpDuration) {
1351 match &mut self.header {
1352 NtpHeader::V3(ref mut header) => header.root_dispersion = root_dispersion,
1353 NtpHeader::V4(ref mut header) => header.root_dispersion = root_dispersion,
1354 NtpHeader::V5(ref mut header) => header.root_dispersion = root_dispersion,
1355 }
1356 }
1357}
1358
1359impl Default for NtpPacket<'_> {
1360 fn default() -> Self {
1361 Self {
1362 header: NtpHeader::V4(NtpHeaderV3V4::new()),
1363 efdata: Default::default(),
1364 mac: None,
1365 }
1366 }
1367}
1368
1369#[cfg(test)]
1370mod tests {
1371 use crate::{
1372 keyset::KeySetProvider, nts_record::AeadAlgorithm, system::TimeSnapshot,
1373 time_types::PollIntervalLimits,
1374 };
1375
1376 use super::*;
1377
1378 #[derive(Debug, Clone)]
1379 struct TestClock {
1380 now: NtpTimestamp,
1381 }
1382
1383 impl NtpClock for TestClock {
1384 type Error = std::io::Error;
1385
1386 fn now(&self) -> Result<NtpTimestamp, Self::Error> {
1387 Ok(self.now)
1388 }
1389
1390 fn set_frequency(&self, _freq: f64) -> Result<NtpTimestamp, Self::Error> {
1391 panic!("Unexpected clock steer");
1392 }
1393
1394 fn get_frequency(&self) -> Result<f64, Self::Error> {
1395 Ok(0.0)
1396 }
1397
1398 fn step_clock(&self, _offset: NtpDuration) -> Result<NtpTimestamp, Self::Error> {
1399 panic!("Unexpected clock steer");
1400 }
1401
1402 fn disable_ntp_algorithm(&self) -> Result<(), Self::Error> {
1403 panic!("Unexpected clock steer");
1404 }
1405
1406 fn error_estimate_update(
1407 &self,
1408 _est_error: NtpDuration,
1409 _max_error: NtpDuration,
1410 ) -> Result<(), Self::Error> {
1411 panic!("Unexpected clock steer");
1412 }
1413
1414 fn status_update(&self, _leap_status: NtpLeapIndicator) -> Result<(), Self::Error> {
1415 panic!("Unexpected clock steer");
1416 }
1417 }
1418
1419 #[test]
1420 fn roundtrip_bitrep_leap() {
1421 for i in 0..4u8 {
1422 let a = NtpLeapIndicator::from_bits(i);
1423 let b = a.to_bits();
1424 let c = NtpLeapIndicator::from_bits(b);
1425 assert_eq!(i, b);
1426 assert_eq!(a, c);
1427 }
1428 }
1429
1430 #[test]
1431 fn roundtrip_bitrep_mode() {
1432 for i in 0..8u8 {
1433 let a = NtpAssociationMode::from_bits(i);
1434 let b = a.to_bits();
1435 let c = NtpAssociationMode::from_bits(b);
1436 assert_eq!(i, b);
1437 assert_eq!(a, c);
1438 }
1439 }
1440
1441 #[test]
1442 fn test_captured_client() {
1443 let packet = b"\x23\x02\x06\xe8\x00\x00\x03\xff\x00\x00\x03\x7d\x5e\xc6\x9f\x0f\xe5\xf6\x62\x98\x7b\x61\xb9\xaf\xe5\xf6\x63\x66\x7b\x64\x99\x5d\xe5\xf6\x63\x66\x81\x40\x55\x90\xe5\xf6\x63\xa8\x76\x1d\xde\x48";
1444 let reference = NtpPacket {
1445 header: NtpHeader::V4(NtpHeaderV3V4 {
1446 leap: NtpLeapIndicator::NoWarning,
1447 mode: NtpAssociationMode::Client,
1448 stratum: 2,
1449 poll: PollInterval::from_byte(6),
1450 precision: -24,
1451 root_delay: NtpDuration::from_fixed_int(1023 << 16),
1452 root_dispersion: NtpDuration::from_fixed_int(893 << 16),
1453 reference_id: ReferenceId::from_int(0x5ec69f0f),
1454 reference_timestamp: NtpTimestamp::from_fixed_int(0xe5f662987b61b9af),
1455 origin_timestamp: NtpTimestamp::from_fixed_int(0xe5f663667b64995d),
1456 receive_timestamp: NtpTimestamp::from_fixed_int(0xe5f6636681405590),
1457 transmit_timestamp: NtpTimestamp::from_fixed_int(0xe5f663a8761dde48),
1458 }),
1459 efdata: Default::default(),
1460 mac: None,
1461 };
1462
1463 assert_eq!(
1464 reference,
1465 NtpPacket::deserialize(packet, &NoCipher).unwrap().0
1466 );
1467 match reference.serialize_without_encryption_vec(None) {
1468 Ok(buf) => assert_eq!(packet[..], buf[..]),
1469 Err(e) => panic!("{e:?}"),
1470 }
1471
1472 let packet = b"\x1B\x02\x06\xe8\x00\x00\x03\xff\x00\x00\x03\x7d\x5e\xc6\x9f\x0f\xe5\xf6\x62\x98\x7b\x61\xb9\xaf\xe5\xf6\x63\x66\x7b\x64\x99\x5d\xe5\xf6\x63\x66\x81\x40\x55\x90\xe5\xf6\x63\xa8\x76\x1d\xde\x48";
1473 let reference = NtpPacket {
1474 header: NtpHeader::V3(NtpHeaderV3V4 {
1475 leap: NtpLeapIndicator::NoWarning,
1476 mode: NtpAssociationMode::Client,
1477 stratum: 2,
1478 poll: PollInterval::from_byte(6),
1479 precision: -24,
1480 root_delay: NtpDuration::from_fixed_int(1023 << 16),
1481 root_dispersion: NtpDuration::from_fixed_int(893 << 16),
1482 reference_id: ReferenceId::from_int(0x5ec69f0f),
1483 reference_timestamp: NtpTimestamp::from_fixed_int(0xe5f662987b61b9af),
1484 origin_timestamp: NtpTimestamp::from_fixed_int(0xe5f663667b64995d),
1485 receive_timestamp: NtpTimestamp::from_fixed_int(0xe5f6636681405590),
1486 transmit_timestamp: NtpTimestamp::from_fixed_int(0xe5f663a8761dde48),
1487 }),
1488 efdata: Default::default(),
1489 mac: None,
1490 };
1491
1492 assert_eq!(
1493 reference,
1494 NtpPacket::deserialize(packet, &NoCipher).unwrap().0
1495 );
1496 match reference.serialize_without_encryption_vec(None) {
1497 Ok(buf) => assert_eq!(packet[..], buf[..]),
1498 Err(e) => panic!("{e:?}"),
1499 }
1500 }
1501
1502 #[test]
1503 fn test_captured_server() {
1504 let packet = b"\x24\x02\x06\xe9\x00\x00\x02\x36\x00\x00\x03\xb7\xc0\x35\x67\x6c\xe5\xf6\x61\xfd\x6f\x16\x5f\x03\xe5\xf6\x63\xa8\x76\x19\xef\x40\xe5\xf6\x63\xa8\x79\x8c\x65\x81\xe5\xf6\x63\xa8\x79\x8e\xae\x2b";
1505 let reference = NtpPacket {
1506 header: NtpHeader::V4(NtpHeaderV3V4 {
1507 leap: NtpLeapIndicator::NoWarning,
1508 mode: NtpAssociationMode::Server,
1509 stratum: 2,
1510 poll: PollInterval::from_byte(6),
1511 precision: -23,
1512 root_delay: NtpDuration::from_fixed_int(566 << 16),
1513 root_dispersion: NtpDuration::from_fixed_int(951 << 16),
1514 reference_id: ReferenceId::from_int(0xc035676c),
1515 reference_timestamp: NtpTimestamp::from_fixed_int(0xe5f661fd6f165f03),
1516 origin_timestamp: NtpTimestamp::from_fixed_int(0xe5f663a87619ef40),
1517 receive_timestamp: NtpTimestamp::from_fixed_int(0xe5f663a8798c6581),
1518 transmit_timestamp: NtpTimestamp::from_fixed_int(0xe5f663a8798eae2b),
1519 }),
1520 efdata: Default::default(),
1521 mac: None,
1522 };
1523
1524 assert_eq!(
1525 reference,
1526 NtpPacket::deserialize(packet, &NoCipher).unwrap().0
1527 );
1528 match reference.serialize_without_encryption_vec(None) {
1529 Ok(buf) => assert_eq!(packet[..], buf[..]),
1530 Err(e) => panic!("{e:?}"),
1531 }
1532 }
1533
1534 #[test]
1535 fn test_version() {
1536 let packet = b"\x04\x02\x06\xe9\x00\x00\x02\x36\x00\x00\x03\xb7\xc0\x35\x67\x6c\xe5\xf6\x61\xfd\x6f\x16\x5f\x03\xe5\xf6\x63\xa8\x76\x19\xef\x40\xe5\xf6\x63\xa8\x79\x8c\x65\x81\xe5\xf6\x63\xa8\x79\x8e\xae\x2b";
1537 assert!(NtpPacket::deserialize(packet, &NoCipher).is_err());
1538 let packet = b"\x0B\x02\x06\xe9\x00\x00\x02\x36\x00\x00\x03\xb7\xc0\x35\x67\x6c\xe5\xf6\x61\xfd\x6f\x16\x5f\x03\xe5\xf6\x63\xa8\x76\x19\xef\x40\xe5\xf6\x63\xa8\x79\x8c\x65\x81\xe5\xf6\x63\xa8\x79\x8e\xae\x2b";
1539 assert!(NtpPacket::deserialize(packet, &NoCipher).is_err());
1540 let packet = b"\x14\x02\x06\xe9\x00\x00\x02\x36\x00\x00\x03\xb7\xc0\x35\x67\x6c\xe5\xf6\x61\xfd\x6f\x16\x5f\x03\xe5\xf6\x63\xa8\x76\x19\xef\x40\xe5\xf6\x63\xa8\x79\x8c\x65\x81\xe5\xf6\x63\xa8\x79\x8e\xae\x2b";
1541 assert!(NtpPacket::deserialize(packet, &NoCipher).is_err());
1542 let packet = b"\x34\x02\x06\xe9\x00\x00\x02\x36\x00\x00\x03\xb7\xc0\x35\x67\x6c\xe5\xf6\x61\xfd\x6f\x16\x5f\x03\xe5\xf6\x63\xa8\x76\x19\xef\x40\xe5\xf6\x63\xa8\x79\x8c\x65\x81\xe5\xf6\x63\xa8\x79\x8e\xae\x2b";
1543 assert!(NtpPacket::deserialize(packet, &NoCipher).is_err());
1544 let packet = b"\x3B\x02\x06\xe9\x00\x00\x02\x36\x00\x00\x03\xb7\xc0\x35\x67\x6c\xe5\xf6\x61\xfd\x6f\x16\x5f\x03\xe5\xf6\x63\xa8\x76\x19\xef\x40\xe5\xf6\x63\xa8\x79\x8c\x65\x81\xe5\xf6\x63\xa8\x79\x8e\xae\x2b";
1545 assert!(NtpPacket::deserialize(packet, &NoCipher).is_err());
1546 }
1547
1548 #[test]
1549 fn test_packed_flags() {
1550 let base = b"\x24\x02\x06\xe9\x00\x00\x02\x36\x00\x00\x03\xb7\xc0\x35\x67\x6c\xe5\xf6\x61\xfd\x6f\x16\x5f\x03\xe5\xf6\x63\xa8\x76\x19\xef\x40\xe5\xf6\x63\xa8\x79\x8c\x65\x81\xe5\xf6\x63\xa8\x79\x8e\xae\x2b".to_owned();
1551 let base_structured = NtpPacket::deserialize(&base, &NoCipher).unwrap().0;
1552
1553 for leap_type in 0..3 {
1554 for mode in 0..8 {
1555 let mut header = base_structured.clone();
1556 header.set_leap(NtpLeapIndicator::from_bits(leap_type));
1557 header.set_mode(NtpAssociationMode::from_bits(mode));
1558
1559 let data = header.serialize_without_encryption_vec(None).unwrap();
1560 let copy = NtpPacket::deserialize(&data, &NoCipher).unwrap().0;
1561 assert_eq!(header, copy);
1562 }
1563 }
1564
1565 for i in 0..=0xFF {
1566 let mut packet = base;
1567 packet[0] = i;
1568
1569 if let Ok((a, _)) = NtpPacket::deserialize(&packet, &NoCipher) {
1570 let b = a.serialize_without_encryption_vec(None).unwrap();
1571 assert_eq!(packet[..], b[..]);
1572 }
1573 }
1574 }
1575
1576 #[test]
1577 fn test_nts_roundtrip() {
1578 let cookie = [0; 16];
1579 let (packet1, _) =
1580 NtpPacket::nts_poll_message(&cookie, 1, PollIntervalLimits::default().min);
1581 let cipher = AesSivCmac512::new(std::array::from_fn::<_, 64, _>(|i| i as u8).into());
1582
1583 let mut buffer = [0u8; 2048];
1584 let mut cursor = Cursor::new(buffer.as_mut());
1585 packet1.serialize(&mut cursor, &cipher, None).unwrap();
1586 let (packet2, _) =
1587 NtpPacket::deserialize(&cursor.get_ref()[..cursor.position() as usize], &cipher)
1588 .unwrap();
1589 assert_eq!(packet1, packet2);
1590 }
1591
1592 #[test]
1593 fn test_nts_captured_server() {
1594 let packet = b"\x24\x01\x04\xe8\x00\x00\x00\x00\x00\x00\x00\x60\x54\x4d\x4e\x4c\xe8\x49\x48\x92\xf9\x29\x57\x9e\x62\x87\xdb\x47\x3f\xf7\x5f\x58\xe8\x49\x48\xb2\xb6\x40\xd7\x01\xe8\x49\x48\xb2\xb6\x44\xbf\xf8\x01\x04\x00\x24\xe4\x83\x3a\x8d\x60\x0e\x13\x42\x43\x5c\xb2\x9d\xe5\x50\xac\xc0\xf8\xd8\xfa\x16\xe5\xc5\x37\x0a\x62\x0b\x15\x5f\x58\x6a\xda\xd6\x04\x04\x00\xd4\x00\x10\x00\xbc\x6a\x1d\xe3\xc2\x6e\x13\xeb\x10\xc7\x39\xd7\x0b\x84\x1f\xad\x1b\x86\xe2\x30\xc6\x3e\x9e\xa5\xf7\x1b\x62\xa8\xa7\x98\x81\xce\x7c\x6b\x17\xcb\x31\x32\x49\x0f\xde\xcf\x21\x10\x56\x4e\x36\x88\x92\xdd\xee\xf1\xf4\x23\xf6\x55\x53\x41\xc2\xc9\x17\x61\x20\xa5\x18\xdc\x1a\x7e\xdc\x5e\xe3\xc8\x3b\x05\x08\x7b\x73\x03\xf7\xab\x86\xd5\x2c\xc7\x49\x0c\xe8\x29\x39\x72\x23\xdc\xef\x2d\x94\xfa\xf8\xd7\x1d\x12\x80\xda\x03\x2d\xd7\x04\x69\xe9\xac\x5f\x82\xef\x57\x81\xd2\x07\xfb\xac\xb4\xa8\xb6\x31\x91\x14\xd5\xf5\x6f\xb2\x2a\x0c\xb6\xd7\xdc\xf7\x7d\xf0\x21\x46\xf6\x7e\x46\x01\xb5\x3b\x21\x7c\xa8\xac\x1a\x4d\x97\xd5\x9b\xce\xeb\x98\x33\x99\x7f\x10\x0e\xd4\x69\x85\x8b\xcd\x73\x52\x01\xad\xec\x38\xcf\x8c\xb2\xc6\xd0\x54\x1a\x97\x67\xdd\xb3\xea\x09\x1d\x63\xd9\x8d\x03\xdd\x6e\x48\x15\x3d\xc9\xb6\x1f\xe5\xd9\x1d\x74\xae\x35\x48";
1595 let cipher = AesSivCmac512::new(
1596 [
1597 244, 6, 63, 13, 47, 226, 180, 25, 104, 212, 47, 14, 186, 70, 187, 93, 134, 140, 2,
1598 82, 238, 254, 113, 79, 90, 31, 135, 138, 123, 210, 121, 47, 228, 208, 243, 76, 126,
1599 213, 196, 233, 65, 15, 33, 163, 196, 30, 6, 197, 222, 105, 40, 14, 73, 138, 200,
1600 45, 235, 127, 48, 248, 171, 8, 141, 180,
1601 ]
1602 .into(),
1603 );
1604
1605 assert!(NtpPacket::deserialize(packet, &cipher).is_ok());
1606 }
1607
1608 #[test]
1609 fn test_nts_captured_client() {
1610 let packet = b"\x23\x00\x04\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x62\x87\xdb\x47\x3f\xf7\x5f\x58\x01\x04\x00\x24\xe4\x83\x3a\x8d\x60\x0e\x13\x42\x43\x5c\xb2\x9d\xe5\x50\xac\xc0\xf8\xd8\xfa\x16\xe5\xc5\x37\x0a\x62\x0b\x15\x5f\x58\x6a\xda\xd6\x02\x04\x00\xac\x1c\xc4\x0a\x94\xda\x3f\x94\xa4\xd1\x2a\xc2\xd6\x09\xf1\x6f\x72\x11\x59\x6a\x0a\xce\xfc\x62\xd1\x1f\x28\x3a\xd1\x08\xd8\x01\xb5\x91\x38\x5d\x9b\xf5\x07\xf9\x0d\x21\x82\xe6\x81\x2a\x58\xa7\x35\xdc\x49\xc4\xd3\xe9\xb7\x9c\x72\xb7\xf6\x44\x64\xf8\xfc\x0d\xed\x25\xea\x1f\x7c\x9b\x31\x5c\xd8\x60\x86\xfd\x67\x74\x90\xf5\x0e\x61\xe6\x68\x0e\x29\x0d\x49\x77\x0c\xed\x44\xd4\x2f\x2d\x9b\xa8\x9f\x4d\x5d\xce\x4f\xdd\x57\x49\x51\x49\x5a\x1f\x38\xdb\xc7\xec\x1b\x86\x5b\xa5\x8f\x23\x1e\xdd\x76\xee\x1d\xaf\xdd\x66\xb2\xb2\x64\x1f\x03\xc6\x47\x9b\x42\x9c\x7f\xf6\x59\x6b\x82\x44\xcf\x67\xb5\xa2\xcd\x20\x9d\x39\xbb\xe6\x40\x2b\xf6\x20\x45\xdf\x95\x50\xf0\x38\x77\x06\x89\x79\x12\x18\x04\x04\x00\x28\x00\x10\x00\x10\xce\x89\xee\x97\x34\x42\xbc\x0f\x43\xaa\xce\x49\x99\xbd\xf5\x8e\x8f\xee\x7b\x1a\x2d\x58\xaf\x6d\xe9\xa2\x0e\x56\x1f\x7f\xf0\x6a";
1611 let cipher = AesSivCmac512::new(
1612 [
1613 170, 111, 161, 118, 7, 200, 232, 128, 145, 250, 170, 186, 87, 143, 171, 252, 110,
1614 241, 170, 179, 13, 150, 134, 147, 211, 248, 62, 207, 122, 155, 198, 109, 167, 15,
1615 18, 118, 146, 63, 186, 146, 212, 188, 175, 27, 89, 3, 237, 212, 52, 113, 28, 21,
1616 203, 200, 230, 17, 8, 186, 126, 1, 52, 230, 86, 40,
1617 ]
1618 .into(),
1619 );
1620
1621 assert!(NtpPacket::deserialize(packet, &cipher).is_ok());
1622 }
1623
1624 #[test]
1625 fn test_nts_poll_message() {
1626 let cookie = [0; 16];
1627 let (packet1, ref1) =
1628 NtpPacket::nts_poll_message(&cookie, 1, PollIntervalLimits::default().min);
1629 assert_eq!(0, packet1.efdata.encrypted.len());
1630 assert_eq!(0, packet1.efdata.untrusted.len());
1631 let mut have_uid = false;
1632 let mut have_cookie = false;
1633 let mut nplaceholders = 0;
1634 for ef in packet1.efdata.authenticated {
1635 match ef {
1636 ExtensionField::UniqueIdentifier(uid) => {
1637 assert_eq!(ref1.uid.as_ref().unwrap(), uid.as_ref());
1638 assert!(!have_uid);
1639 have_uid = true;
1640 }
1641 ExtensionField::NtsCookie(cookie_p) => {
1642 assert_eq!(&cookie, cookie_p.as_ref());
1643 assert!(!have_cookie);
1644 have_cookie = true;
1645 }
1646 ExtensionField::NtsCookiePlaceholder { cookie_length } => {
1647 assert_eq!(cookie_length, cookie.len() as u16);
1648 nplaceholders += 1;
1649 }
1650 _ => unreachable!(),
1651 }
1652 }
1653 assert!(have_cookie);
1654 assert!(have_uid);
1655 assert_eq!(nplaceholders, 0);
1656
1657 let (packet2, ref2) =
1658 NtpPacket::nts_poll_message(&cookie, 3, PollIntervalLimits::default().min);
1659 assert_ne!(
1660 ref1.expected_origin_timestamp,
1661 ref2.expected_origin_timestamp
1662 );
1663 assert_ne!(ref1.uid, ref2.uid);
1664
1665 assert_eq!(0, packet2.efdata.encrypted.len());
1666 assert_eq!(0, packet2.efdata.untrusted.len());
1667 let mut have_uid = false;
1668 let mut have_cookie = false;
1669 let mut nplaceholders = 0;
1670 for ef in packet2.efdata.authenticated {
1671 match ef {
1672 ExtensionField::UniqueIdentifier(uid) => {
1673 assert_eq!(ref2.uid.as_ref().unwrap(), uid.as_ref());
1674 assert!(!have_uid);
1675 have_uid = true;
1676 }
1677 ExtensionField::NtsCookie(cookie_p) => {
1678 assert_eq!(&cookie, cookie_p.as_ref());
1679 assert!(!have_cookie);
1680 have_cookie = true;
1681 }
1682 ExtensionField::NtsCookiePlaceholder { cookie_length } => {
1683 assert_eq!(cookie_length, cookie.len() as u16);
1684 nplaceholders += 1;
1685 }
1686 _ => unreachable!(),
1687 }
1688 }
1689 assert!(have_cookie);
1690 assert!(have_uid);
1691 assert_eq!(nplaceholders, 2);
1692 }
1693
1694 #[test]
1695 fn test_nts_response_validation() {
1696 let cookie = [0; 16];
1697 let (packet, id) =
1698 NtpPacket::nts_poll_message(&cookie, 0, PollIntervalLimits::default().min);
1699 let mut response = NtpPacket::timestamp_response(
1700 &SystemSnapshot::default(),
1701 packet,
1702 NtpTimestamp::from_fixed_int(0),
1703 &TestClock {
1704 now: NtpTimestamp::from_fixed_int(2),
1705 },
1706 );
1707
1708 assert!(response.valid_server_response(id, false));
1709 assert!(!response.valid_server_response(id, true));
1710
1711 response
1712 .efdata
1713 .untrusted
1714 .push(ExtensionField::UniqueIdentifier(Cow::Borrowed(
1715 id.uid.as_ref().unwrap(),
1716 )));
1717
1718 assert!(response.valid_server_response(id, false));
1719 assert!(!response.valid_server_response(id, true));
1720
1721 response.efdata.untrusted.clear();
1722 response
1723 .efdata
1724 .authenticated
1725 .push(ExtensionField::UniqueIdentifier(Cow::Borrowed(
1726 id.uid.as_ref().unwrap(),
1727 )));
1728
1729 assert!(response.valid_server_response(id, false));
1730 assert!(response.valid_server_response(id, true));
1731
1732 response
1733 .efdata
1734 .untrusted
1735 .push(ExtensionField::UniqueIdentifier(Cow::Borrowed(&[])));
1736
1737 assert!(!response.valid_server_response(id, false));
1738 assert!(response.valid_server_response(id, true));
1739
1740 response.efdata.untrusted.clear();
1741 response
1742 .efdata
1743 .encrypted
1744 .push(ExtensionField::UniqueIdentifier(Cow::Borrowed(&[])));
1745
1746 assert!(!response.valid_server_response(id, false));
1747 assert!(!response.valid_server_response(id, true));
1748 }
1749
1750 #[test]
1751 fn v5_upgrade_packet() {
1752 let (packet, _) = NtpPacket::poll_message_upgrade_request(PollInterval::default());
1753
1754 let response = NtpPacket::timestamp_response(
1755 &SystemSnapshot::default(),
1756 packet,
1757 NtpTimestamp::from_fixed_int(0),
1758 &TestClock {
1759 now: NtpTimestamp::from_fixed_int(1),
1760 },
1761 );
1762
1763 let NtpHeader::V4(header) = response.header else {
1764 panic!("wrong version");
1765 };
1766
1767 assert_eq!(
1768 header.reference_timestamp,
1769 NtpTimestamp::from_fixed_int(0x4E54503544524654)
1770 );
1771 }
1772
1773 #[test]
1774 fn test_timestamp_response() {
1775 let decoded = DecodedServerCookie {
1776 algorithm: AeadAlgorithm::AeadAesSivCmac256,
1777 s2c: Box::new(AesSivCmac256::new((0..32_u8).collect())),
1778 c2s: Box::new(AesSivCmac256::new((32..64_u8).collect())),
1779 };
1780 let keysetprovider = KeySetProvider::new(1);
1781 let cookie = keysetprovider.get().encode_cookie(&decoded);
1782
1783 let (packet, _) =
1784 NtpPacket::nts_poll_message(&cookie, 0, PollIntervalLimits::default().min);
1785 let packet_id = packet
1786 .efdata
1787 .authenticated
1788 .iter()
1789 .find_map(|f| {
1790 if let ExtensionField::UniqueIdentifier(id) = f {
1791 Some(id.clone().into_owned())
1792 } else {
1793 None
1794 }
1795 })
1796 .unwrap();
1797 let response = NtpPacket::timestamp_response(
1798 &SystemSnapshot {
1799 time_snapshot: TimeSnapshot {
1800 leap_indicator: NtpLeapIndicator::Leap59,
1801 ..Default::default()
1802 },
1803 ..Default::default()
1804 },
1805 packet,
1806 NtpTimestamp::from_fixed_int(0),
1807 &TestClock {
1808 now: NtpTimestamp::from_fixed_int(1),
1809 },
1810 );
1811 let response_id = response
1812 .efdata
1813 .untrusted
1814 .iter()
1815 .find_map(|f| {
1816 if let ExtensionField::UniqueIdentifier(id) = f {
1817 Some(id.clone().into_owned())
1818 } else {
1819 None
1820 }
1821 })
1822 .unwrap();
1823 assert_eq!(packet_id, response_id);
1824 assert_eq!(
1825 response.receive_timestamp(),
1826 NtpTimestamp::from_fixed_int(0)
1827 );
1828 assert_eq!(
1829 response.transmit_timestamp(),
1830 NtpTimestamp::from_fixed_int(1)
1831 );
1832 assert_eq!(response.leap(), NtpLeapIndicator::Leap59);
1833
1834 let (mut packet, _) =
1835 NtpPacket::nts_poll_message(&cookie, 0, PollIntervalLimits::default().min);
1836 std::mem::swap(
1837 &mut packet.efdata.authenticated,
1838 &mut packet.efdata.untrusted,
1839 );
1840 let packet_id = packet
1841 .efdata
1842 .untrusted
1843 .iter()
1844 .find_map(|f| {
1845 if let ExtensionField::UniqueIdentifier(id) = f {
1846 Some(id.clone().into_owned())
1847 } else {
1848 None
1849 }
1850 })
1851 .unwrap();
1852 let response = NtpPacket::timestamp_response(
1853 &SystemSnapshot::default(),
1854 packet,
1855 NtpTimestamp::from_fixed_int(0),
1856 &TestClock {
1857 now: NtpTimestamp::from_fixed_int(1),
1858 },
1859 );
1860 let response_id = response
1861 .efdata
1862 .untrusted
1863 .iter()
1864 .find_map(|f| {
1865 if let ExtensionField::UniqueIdentifier(id) = f {
1866 Some(id.clone().into_owned())
1867 } else {
1868 None
1869 }
1870 })
1871 .unwrap();
1872 assert_eq!(packet_id, response_id);
1873 assert_eq!(
1874 response.receive_timestamp(),
1875 NtpTimestamp::from_fixed_int(0)
1876 );
1877 assert_eq!(
1878 response.transmit_timestamp(),
1879 NtpTimestamp::from_fixed_int(1)
1880 );
1881
1882 let (packet, _) =
1883 NtpPacket::nts_poll_message(&cookie, 0, PollIntervalLimits::default().min);
1884 let packet_id = packet
1885 .efdata
1886 .authenticated
1887 .iter()
1888 .find_map(|f| {
1889 if let ExtensionField::UniqueIdentifier(id) = f {
1890 Some(id.clone().into_owned())
1891 } else {
1892 None
1893 }
1894 })
1895 .unwrap();
1896 let response = NtpPacket::nts_timestamp_response(
1897 &SystemSnapshot::default(),
1898 packet,
1899 NtpTimestamp::from_fixed_int(0),
1900 &TestClock {
1901 now: NtpTimestamp::from_fixed_int(1),
1902 },
1903 &decoded,
1904 &keysetprovider.get(),
1905 );
1906 let response_id = response
1907 .efdata
1908 .authenticated
1909 .iter()
1910 .find_map(|f| {
1911 if let ExtensionField::UniqueIdentifier(id) = f {
1912 Some(id.clone().into_owned())
1913 } else {
1914 None
1915 }
1916 })
1917 .unwrap();
1918 assert_eq!(packet_id, response_id);
1919 assert_eq!(
1920 response.receive_timestamp(),
1921 NtpTimestamp::from_fixed_int(0)
1922 );
1923 assert_eq!(
1924 response.transmit_timestamp(),
1925 NtpTimestamp::from_fixed_int(1)
1926 );
1927
1928 let (mut packet, _) =
1929 NtpPacket::nts_poll_message(&cookie, 0, PollIntervalLimits::default().min);
1930 std::mem::swap(
1931 &mut packet.efdata.authenticated,
1932 &mut packet.efdata.untrusted,
1933 );
1934 let response = NtpPacket::nts_timestamp_response(
1935 &SystemSnapshot::default(),
1936 packet,
1937 NtpTimestamp::from_fixed_int(0),
1938 &TestClock {
1939 now: NtpTimestamp::from_fixed_int(1),
1940 },
1941 &decoded,
1942 &keysetprovider.get(),
1943 );
1944 assert!(response
1945 .efdata
1946 .authenticated
1947 .iter()
1948 .find_map(|f| {
1949 if let ExtensionField::UniqueIdentifier(id) = f {
1950 Some(id.clone().into_owned())
1951 } else {
1952 None
1953 }
1954 })
1955 .is_none());
1956 assert_eq!(
1957 response.receive_timestamp(),
1958 NtpTimestamp::from_fixed_int(0)
1959 );
1960 assert_eq!(
1961 response.transmit_timestamp(),
1962 NtpTimestamp::from_fixed_int(1)
1963 );
1964 }
1965
1966 #[test]
1967 fn test_timestamp_cookies() {
1968 let decoded = DecodedServerCookie {
1969 algorithm: AeadAlgorithm::AeadAesSivCmac256,
1970 s2c: Box::new(AesSivCmac256::new((0..32_u8).collect())),
1971 c2s: Box::new(AesSivCmac256::new((32..64_u8).collect())),
1972 };
1973 let keysetprovider = KeySetProvider::new(1);
1974 let cookie = keysetprovider.get().encode_cookie(&decoded);
1975
1976 let (packet, _) =
1977 NtpPacket::nts_poll_message(&cookie, 1, PollIntervalLimits::default().min);
1978 let response = NtpPacket::nts_timestamp_response(
1979 &SystemSnapshot::default(),
1980 packet,
1981 NtpTimestamp::from_fixed_int(0),
1982 &TestClock {
1983 now: NtpTimestamp::from_fixed_int(1),
1984 },
1985 &decoded,
1986 &keysetprovider.get(),
1987 );
1988 assert_eq!(response.new_cookies().count(), 1);
1989
1990 let (packet, _) =
1991 NtpPacket::nts_poll_message(&cookie, 2, PollIntervalLimits::default().min);
1992 let response = NtpPacket::nts_timestamp_response(
1993 &SystemSnapshot::default(),
1994 packet,
1995 NtpTimestamp::from_fixed_int(0),
1996 &TestClock {
1997 now: NtpTimestamp::from_fixed_int(1),
1998 },
1999 &decoded,
2000 &keysetprovider.get(),
2001 );
2002 assert_eq!(response.new_cookies().count(), 2);
2003
2004 let (packet, _) =
2005 NtpPacket::nts_poll_message(&cookie, 3, PollIntervalLimits::default().min);
2006 let response = NtpPacket::nts_timestamp_response(
2007 &SystemSnapshot::default(),
2008 packet,
2009 NtpTimestamp::from_fixed_int(0),
2010 &TestClock {
2011 now: NtpTimestamp::from_fixed_int(1),
2012 },
2013 &decoded,
2014 &keysetprovider.get(),
2015 );
2016 assert_eq!(response.new_cookies().count(), 3);
2017
2018 let (packet, _) =
2019 NtpPacket::nts_poll_message(&cookie, 4, PollIntervalLimits::default().min);
2020 let response = NtpPacket::nts_timestamp_response(
2021 &SystemSnapshot::default(),
2022 packet,
2023 NtpTimestamp::from_fixed_int(0),
2024 &TestClock {
2025 now: NtpTimestamp::from_fixed_int(1),
2026 },
2027 &decoded,
2028 &keysetprovider.get(),
2029 );
2030 assert_eq!(response.new_cookies().count(), 4);
2031 }
2032
2033 #[test]
2034 fn test_deny_response() {
2035 let decoded = DecodedServerCookie {
2036 algorithm: AeadAlgorithm::AeadAesSivCmac256,
2037 s2c: Box::new(AesSivCmac256::new((0..32_u8).collect())),
2038 c2s: Box::new(AesSivCmac256::new((32..64_u8).collect())),
2039 };
2040 let keysetprovider = KeySetProvider::new(1);
2041 let cookie = keysetprovider.get().encode_cookie(&decoded);
2042
2043 let (packet, _) =
2044 NtpPacket::nts_poll_message(&cookie, 1, PollIntervalLimits::default().min);
2045 let packet_id = packet
2046 .efdata
2047 .authenticated
2048 .iter()
2049 .find_map(|f| {
2050 if let ExtensionField::UniqueIdentifier(id) = f {
2051 Some(id.clone().into_owned())
2052 } else {
2053 None
2054 }
2055 })
2056 .unwrap();
2057 let response = NtpPacket::deny_response(packet);
2058 let response_id = response
2059 .efdata
2060 .untrusted
2061 .iter()
2062 .find_map(|f| {
2063 if let ExtensionField::UniqueIdentifier(id) = f {
2064 Some(id.clone().into_owned())
2065 } else {
2066 None
2067 }
2068 })
2069 .unwrap();
2070 assert_eq!(packet_id, response_id);
2071 assert_eq!(response.new_cookies().count(), 0);
2072 assert!(response.is_kiss_deny());
2073
2074 let (mut packet, _) =
2075 NtpPacket::nts_poll_message(&cookie, 1, PollIntervalLimits::default().min);
2076 let packet_id = packet
2077 .efdata
2078 .authenticated
2079 .iter()
2080 .find_map(|f| {
2081 if let ExtensionField::UniqueIdentifier(id) = f {
2082 Some(id.clone().into_owned())
2083 } else {
2084 None
2085 }
2086 })
2087 .unwrap();
2088 std::mem::swap(
2089 &mut packet.efdata.authenticated,
2090 &mut packet.efdata.untrusted,
2091 );
2092 let response = NtpPacket::deny_response(packet);
2093 let response_id = response
2094 .efdata
2095 .untrusted
2096 .iter()
2097 .find_map(|f| {
2098 if let ExtensionField::UniqueIdentifier(id) = f {
2099 Some(id.clone().into_owned())
2100 } else {
2101 None
2102 }
2103 })
2104 .unwrap();
2105 assert_eq!(packet_id, response_id);
2106 assert_eq!(response.new_cookies().count(), 0);
2107 assert!(response.is_kiss_deny());
2108
2109 let (packet, _) =
2110 NtpPacket::nts_poll_message(&cookie, 1, PollIntervalLimits::default().min);
2111 let packet_id = packet
2112 .efdata
2113 .authenticated
2114 .iter()
2115 .find_map(|f| {
2116 if let ExtensionField::UniqueIdentifier(id) = f {
2117 Some(id.clone().into_owned())
2118 } else {
2119 None
2120 }
2121 })
2122 .unwrap();
2123 let response = NtpPacket::nts_deny_response(packet);
2124 let response_id = response
2125 .efdata
2126 .authenticated
2127 .iter()
2128 .find_map(|f| {
2129 if let ExtensionField::UniqueIdentifier(id) = f {
2130 Some(id.clone().into_owned())
2131 } else {
2132 None
2133 }
2134 })
2135 .unwrap();
2136 assert_eq!(packet_id, response_id);
2137 assert_eq!(response.new_cookies().count(), 0);
2138 assert!(response.is_kiss_deny());
2139
2140 let (mut packet, _) =
2141 NtpPacket::nts_poll_message(&cookie, 1, PollIntervalLimits::default().min);
2142 std::mem::swap(
2143 &mut packet.efdata.authenticated,
2144 &mut packet.efdata.untrusted,
2145 );
2146 let response = NtpPacket::nts_deny_response(packet);
2147 assert!(response
2148 .efdata
2149 .authenticated
2150 .iter()
2151 .find_map(|f| {
2152 if let ExtensionField::UniqueIdentifier(id) = f {
2153 Some(id.clone().into_owned())
2154 } else {
2155 None
2156 }
2157 })
2158 .is_none());
2159 assert_eq!(response.new_cookies().count(), 0);
2160 assert!(response.is_kiss_deny());
2161 }
2162
2163 #[test]
2164 fn test_rate_response() {
2165 let decoded = DecodedServerCookie {
2166 algorithm: AeadAlgorithm::AeadAesSivCmac256,
2167 s2c: Box::new(AesSivCmac256::new((0..32_u8).collect())),
2168 c2s: Box::new(AesSivCmac256::new((32..64_u8).collect())),
2169 };
2170 let keysetprovider = KeySetProvider::new(1);
2171 let cookie = keysetprovider.get().encode_cookie(&decoded);
2172
2173 let (packet, _) =
2174 NtpPacket::nts_poll_message(&cookie, 1, PollIntervalLimits::default().min);
2175 let packet_id = packet
2176 .efdata
2177 .authenticated
2178 .iter()
2179 .find_map(|f| {
2180 if let ExtensionField::UniqueIdentifier(id) = f {
2181 Some(id.clone().into_owned())
2182 } else {
2183 None
2184 }
2185 })
2186 .unwrap();
2187 let response = NtpPacket::rate_limit_response(packet);
2188 let response_id = response
2189 .efdata
2190 .untrusted
2191 .iter()
2192 .find_map(|f| {
2193 if let ExtensionField::UniqueIdentifier(id) = f {
2194 Some(id.clone().into_owned())
2195 } else {
2196 None
2197 }
2198 })
2199 .unwrap();
2200 assert_eq!(packet_id, response_id);
2201 assert_eq!(response.new_cookies().count(), 0);
2202 assert!(response.is_kiss_rate(PollIntervalLimits::default().min));
2203
2204 let (mut packet, _) =
2205 NtpPacket::nts_poll_message(&cookie, 1, PollIntervalLimits::default().min);
2206 let packet_id = packet
2207 .efdata
2208 .authenticated
2209 .iter()
2210 .find_map(|f| {
2211 if let ExtensionField::UniqueIdentifier(id) = f {
2212 Some(id.clone().into_owned())
2213 } else {
2214 None
2215 }
2216 })
2217 .unwrap();
2218 std::mem::swap(
2219 &mut packet.efdata.authenticated,
2220 &mut packet.efdata.untrusted,
2221 );
2222 let response = NtpPacket::rate_limit_response(packet);
2223 let response_id = response
2224 .efdata
2225 .untrusted
2226 .iter()
2227 .find_map(|f| {
2228 if let ExtensionField::UniqueIdentifier(id) = f {
2229 Some(id.clone().into_owned())
2230 } else {
2231 None
2232 }
2233 })
2234 .unwrap();
2235 assert_eq!(packet_id, response_id);
2236 assert_eq!(response.new_cookies().count(), 0);
2237 assert!(response.is_kiss_rate(PollIntervalLimits::default().min));
2238
2239 let (packet, _) =
2240 NtpPacket::nts_poll_message(&cookie, 1, PollIntervalLimits::default().min);
2241 let packet_id = packet
2242 .efdata
2243 .authenticated
2244 .iter()
2245 .find_map(|f| {
2246 if let ExtensionField::UniqueIdentifier(id) = f {
2247 Some(id.clone().into_owned())
2248 } else {
2249 None
2250 }
2251 })
2252 .unwrap();
2253 let response = NtpPacket::nts_rate_limit_response(packet);
2254 let response_id = response
2255 .efdata
2256 .authenticated
2257 .iter()
2258 .find_map(|f| {
2259 if let ExtensionField::UniqueIdentifier(id) = f {
2260 Some(id.clone().into_owned())
2261 } else {
2262 None
2263 }
2264 })
2265 .unwrap();
2266 assert_eq!(packet_id, response_id);
2267 assert_eq!(response.new_cookies().count(), 0);
2268 assert!(response.is_kiss_rate(PollIntervalLimits::default().min));
2269
2270 let (mut packet, _) =
2271 NtpPacket::nts_poll_message(&cookie, 1, PollIntervalLimits::default().min);
2272 std::mem::swap(
2273 &mut packet.efdata.authenticated,
2274 &mut packet.efdata.untrusted,
2275 );
2276 let response = NtpPacket::nts_rate_limit_response(packet);
2277 assert!(response
2278 .efdata
2279 .authenticated
2280 .iter()
2281 .find_map(|f| {
2282 if let ExtensionField::UniqueIdentifier(id) = f {
2283 Some(id.clone().into_owned())
2284 } else {
2285 None
2286 }
2287 })
2288 .is_none());
2289 assert_eq!(response.new_cookies().count(), 0);
2290 assert!(response.is_kiss_rate(PollIntervalLimits::default().min));
2291 }
2292
2293 #[test]
2294 fn test_new_cookies_only_from_encrypted() {
2295 let allowed: [u8; 16] = [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
2296 let disallowed: [u8; 16] = [2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
2297 let packet = NtpPacket {
2298 header: NtpHeader::V4(NtpHeaderV3V4::poll_message(PollIntervalLimits::default().min).0),
2299 efdata: ExtensionFieldData {
2300 authenticated: vec![ExtensionField::NtsCookie(Cow::Borrowed(&disallowed))],
2301 encrypted: vec![ExtensionField::NtsCookie(Cow::Borrowed(&allowed))],
2302 untrusted: vec![ExtensionField::NtsCookie(Cow::Borrowed(&disallowed))],
2303 },
2304 mac: None,
2305 };
2306
2307 assert_eq!(1, packet.new_cookies().count());
2308 for cookie in packet.new_cookies() {
2309 assert_eq!(&cookie, &allowed);
2310 }
2311 }
2312
2313 #[test]
2314 fn test_undersized_ef_in_encrypted_data() {
2315 let cipher = AesSivCmac256::new([0_u8; 32].into());
2316 let packet = [
2317 35, 2, 6, 232, 0, 0, 3, 255, 0, 0, 3, 125, 94, 198, 159, 15, 229, 246, 98, 152, 123,
2318 97, 185, 175, 229, 246, 99, 102, 123, 100, 153, 93, 229, 246, 99, 102, 129, 64, 85,
2319 144, 229, 246, 99, 168, 118, 29, 222, 72, 4, 4, 0, 44, 0, 16, 0, 18, 0, 0, 0, 0, 0, 0,
2320 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 39, 24, 181, 156, 166, 35, 154, 207, 38, 150, 15, 190,
2321 152, 87, 142, 206, 254, 105, 0, 0,
2322 ];
2323 assert!(NtpPacket::deserialize(&packet, &cipher).is_err());
2325 }
2326
2327 #[test]
2328 fn test_undersized_ef() {
2329 let packet = [
2330 35, 2, 6, 232, 0, 0, 3, 255, 0, 0, 3, 125, 94, 198, 159, 15, 229, 246, 98, 152, 123,
2331 97, 185, 175, 229, 246, 99, 102, 123, 100, 153, 93, 229, 246, 99, 102, 129, 64, 85,
2332 144, 229, 246, 99, 168, 118, 29, 222, 72, 4, 4,
2333 ];
2334 assert!(NtpPacket::deserialize(&packet, &NoCipher).is_err());
2336 }
2337
2338 #[test]
2339 fn test_undersized_nonce() {
2340 let input = [
2341 32, 206, 206, 206, 77, 206, 206, 255, 216, 216, 216, 127, 0, 0, 0, 0, 0, 0, 0, 216,
2342 216, 216, 216, 206, 217, 216, 216, 216, 216, 216, 216, 206, 206, 206, 1, 0, 0, 0, 206,
2343 206, 206, 4, 44, 4, 4, 4, 4, 4, 4, 4, 0, 4, 206, 206, 222, 206, 206, 206, 206, 0, 0, 0,
2344 206, 206, 206, 0, 0, 0, 206, 206, 206, 206, 206, 206, 131, 206, 206,
2345 ];
2346 assert!(NtpPacket::deserialize(&input, &NoCipher).is_err());
2348 }
2349
2350 #[test]
2351 fn test_undersized_encryption_ef() {
2352 let input = [
2353 32, 206, 206, 206, 77, 206, 216, 216, 127, 3, 3, 3, 0, 0, 0, 0, 0, 0, 0, 216, 216, 216,
2354 216, 206, 217, 216, 216, 216, 216, 216, 216, 206, 206, 206, 1, 0, 0, 0, 206, 206, 206,
2355 4, 44, 4, 4, 4, 4, 4, 4, 4, 0, 4, 4, 0, 12, 206, 206, 222, 206, 206, 206, 206, 0, 0, 0,
2356 12, 206, 206, 222, 206, 206, 206, 206, 206, 206, 206, 206, 131, 206, 206,
2357 ];
2358 assert!(NtpPacket::deserialize(&input, &NoCipher).is_err());
2359 }
2360
2361 #[test]
2362 fn round_trip_with_ef() {
2363 let (mut p, _) = NtpPacket::poll_message(PollInterval::default());
2364 p.efdata.untrusted.push(ExtensionField::Unknown {
2365 type_id: 0x42,
2366 data: vec![].into(),
2367 });
2368
2369 let serialized = p.serialize_without_encryption_vec(None).unwrap();
2370
2371 let (mut out, _) = NtpPacket::deserialize(&serialized, &NoCipher).unwrap();
2372
2373 let ExtensionField::Unknown { data, .. } = &mut out.efdata.untrusted[0] else {
2375 panic!("wrong ef");
2376 };
2377 assert!(data.iter().all(|&e| e == 0));
2378 *data = vec![].into();
2379
2380 assert_eq!(p, out);
2381 }
2382
2383 #[test]
2384 fn ef_with_missing_padding_v5() {
2385 let (packet, _) = NtpPacket::poll_message_v5(PollInterval::default());
2386 let mut data = packet.serialize_without_encryption_vec(None).unwrap();
2387 data.extend([
2388 0, 0, 0, 6, 1, 2, ]);
2393
2394 assert!(matches!(
2395 NtpPacket::deserialize(&data, &NoCipher),
2396 Err(ParsingError::IncorrectLength)
2397 ));
2398 }
2399
2400 #[test]
2401 fn padding_v5() {
2402 for i in 10..40 {
2403 let packet = NtpPacket::poll_message_v5(PollInterval::default()).0;
2404
2405 let data = packet
2406 .serialize_without_encryption_vec(Some(4 * i))
2407 .unwrap();
2408
2409 assert_eq!(data.len(), 76.max(i * 4));
2410
2411 assert!(NtpPacket::deserialize(&data, &NoCipher).is_ok());
2412 }
2413 }
2414}