1use crate::base::cmp::CanonicalOrd;
5use crate::base::iana::{DigestAlg, Rtype, SecAlg};
6use crate::base::rdata::{
7 ComposeRecordData, LongRecordData, ParseRecordData, RecordData
8};
9use crate::base::scan::{Scan, Scanner, ScannerError};
10use crate::base::wire::{Compose, Composer, Parse, ParseError};
11use crate::utils::{base16, base64};
12use core::cmp::Ordering;
13use core::{fmt, hash};
14use octseq::octets::{Octets, OctetsFrom, OctetsInto};
15use octseq::parse::Parser;
16
17#[derive(Clone)]
20#[cfg_attr(
21 feature = "serde",
22 derive(serde::Serialize, serde::Deserialize),
23 serde(bound(
24 serialize = "
25 Octs: octseq::serde::SerializeOctets + AsRef<[u8]>
26 ",
27 deserialize = "
28 Octs:
29 octseq::builder::FromBuilder
30 + octseq::serde::DeserializeOctets<'de>,
31 <Octs as octseq::builder::FromBuilder>::Builder:
32 octseq::builder::OctetsBuilder
33 + octseq::builder::EmptyBuilder,
34 ",
35 ))
36)]
37pub struct Cdnskey<Octs> {
38 flags: u16,
39 protocol: u8,
40 algorithm: SecAlg,
41 #[cfg_attr(
42 feature = "serde",
43 serde(with = "crate::utils::base64::serde")
44 )]
45 public_key: Octs,
46}
47
48impl<Octs> Cdnskey<Octs> {
49 pub fn new(
50 flags: u16,
51 protocol: u8,
52 algorithm: SecAlg,
53 public_key: Octs,
54 ) -> Result<Self, LongRecordData>
55 where Octs: AsRef<[u8]> {
56 LongRecordData::check_len(
57 usize::from(
58 u16::COMPOSE_LEN + u8::COMPOSE_LEN + SecAlg::COMPOSE_LEN
59 ).checked_add(public_key.as_ref().len()).expect("long key")
60 )?;
61 Ok(unsafe {
62 Cdnskey::new_unchecked(flags, protocol, algorithm, public_key)
63 })
64 }
65
66 pub unsafe fn new_unchecked(
73 flags: u16,
74 protocol: u8,
75 algorithm: SecAlg,
76 public_key: Octs,
77 ) -> Self {
78 Cdnskey {
79 flags,
80 protocol,
81 algorithm,
82 public_key,
83 }
84 }
85
86 pub fn flags(&self) -> u16 {
87 self.flags
88 }
89
90 pub fn protocol(&self) -> u8 {
91 self.protocol
92 }
93
94 pub fn algorithm(&self) -> SecAlg {
95 self.algorithm
96 }
97
98 pub fn public_key(&self) -> &Octs {
99 &self.public_key
100 }
101
102 pub(super) fn convert_octets<Target: OctetsFrom<Octs>>(
103 self,
104 ) -> Result<Cdnskey<Target>, Target::Error> {
105 Ok(unsafe {
106 Cdnskey::new_unchecked(
107 self.flags,
108 self.protocol,
109 self.algorithm,
110 self.public_key.try_octets_into()?,
111 )
112 })
113 }
114
115 pub(super) fn flatten<Target: OctetsFrom<Octs>>(
116 self,
117 ) -> Result<Cdnskey<Target>, Target::Error> {
118 self.convert_octets()
119 }
120
121 pub fn parse<'a, Src: Octets<Range<'a> = Octs> + ?Sized>(
122 parser: &mut Parser<'a, Src>,
123 ) -> Result<Self, ParseError> {
124 let len = match parser.remaining().checked_sub(4) {
125 Some(len) => len,
126 None => return Err(ParseError::ShortInput),
127 };
128 Ok(unsafe {
129 Self::new_unchecked(
130 u16::parse(parser)?,
131 u8::parse(parser)?,
132 SecAlg::parse(parser)?,
133 parser.parse_octets(len)?,
134 )
135 })
136 }
137
138 pub fn scan<S: Scanner<Octets = Octs>>(
139 scanner: &mut S,
140 ) -> Result<Self, S::Error>
141 where Octs: AsRef<[u8]> {
142 Self::new(
143 u16::scan(scanner)?,
144 u8::scan(scanner)?,
145 SecAlg::scan(scanner)?,
146 scanner.convert_entry(base64::SymbolConverter::new())?,
147 ).map_err(|err| S::Error::custom(err.as_str()))
148 }
149}
150
151impl<Octs, SrcOcts> OctetsFrom<Cdnskey<SrcOcts>> for Cdnskey<Octs>
154where
155 Octs: OctetsFrom<SrcOcts>,
156{
157 type Error = Octs::Error;
158
159 fn try_octets_from(
160 source: Cdnskey<SrcOcts>,
161 ) -> Result<Self, Self::Error> {
162 Ok(unsafe {
163 Cdnskey::new_unchecked(
164 source.flags,
165 source.protocol,
166 source.algorithm,
167 Octs::try_octets_from(source.public_key)?,
168 )
169 })
170 }
171}
172
173impl<Octs, Other> PartialEq<Cdnskey<Other>> for Cdnskey<Octs>
176where
177 Octs: AsRef<[u8]>,
178 Other: AsRef<[u8]>,
179{
180 fn eq(&self, other: &Cdnskey<Other>) -> bool {
181 self.flags == other.flags
182 && self.protocol == other.protocol
183 && self.algorithm == other.algorithm
184 && self.public_key.as_ref() == other.public_key.as_ref()
185 }
186}
187
188impl<Octs: AsRef<[u8]>> Eq for Cdnskey<Octs> {}
189
190impl<Octs, Other> PartialOrd<Cdnskey<Other>> for Cdnskey<Octs>
193where
194 Octs: AsRef<[u8]>,
195 Other: AsRef<[u8]>,
196{
197 fn partial_cmp(&self, other: &Cdnskey<Other>) -> Option<Ordering> {
198 Some(self.canonical_cmp(other))
199 }
200}
201
202impl<Octs, Other> CanonicalOrd<Cdnskey<Other>> for Cdnskey<Octs>
203where
204 Octs: AsRef<[u8]>,
205 Other: AsRef<[u8]>,
206{
207 fn canonical_cmp(&self, other: &Cdnskey<Other>) -> Ordering {
208 match self.flags.cmp(&other.flags) {
209 Ordering::Equal => {}
210 other => return other,
211 }
212 match self.protocol.cmp(&other.protocol) {
213 Ordering::Equal => {}
214 other => return other,
215 }
216 match self.algorithm.cmp(&other.algorithm) {
217 Ordering::Equal => {}
218 other => return other,
219 }
220 self.public_key.as_ref().cmp(other.public_key.as_ref())
221 }
222}
223
224impl<Octs: AsRef<[u8]>> Ord for Cdnskey<Octs> {
225 fn cmp(&self, other: &Self) -> Ordering {
226 self.canonical_cmp(other)
227 }
228}
229
230impl<Octs: AsRef<[u8]>> hash::Hash for Cdnskey<Octs> {
233 fn hash<H: hash::Hasher>(&self, state: &mut H) {
234 self.flags.hash(state);
235 self.protocol.hash(state);
236 self.algorithm.hash(state);
237 self.public_key.as_ref().hash(state);
238 }
239}
240
241impl<Octs> RecordData for Cdnskey<Octs> {
244 fn rtype(&self) -> Rtype {
245 Rtype::Cdnskey
246 }
247}
248
249impl<'a, Octs> ParseRecordData<'a, Octs> for Cdnskey<Octs::Range<'a>>
250where
251 Octs: Octets + ?Sized,
252{
253 fn parse_rdata(
254 rtype: Rtype,
255 parser: &mut Parser<'a, Octs>,
256 ) -> Result<Option<Self>, ParseError> {
257 if rtype == Rtype::Cdnskey {
258 Self::parse(parser).map(Some)
259 } else {
260 Ok(None)
261 }
262 }
263}
264
265impl<Octs: AsRef<[u8]>> ComposeRecordData for Cdnskey<Octs> {
266 fn rdlen(&self, _compress: bool) -> Option<u16> {
267 Some(
268 u16::try_from(self.public_key.as_ref().len())
269 .expect("long key")
270 .checked_add(
271 u16::COMPOSE_LEN + u8::COMPOSE_LEN + SecAlg::COMPOSE_LEN,
272 )
273 .expect("long key"),
274 )
275 }
276
277 fn compose_rdata<Target: Composer + ?Sized>(
278 &self,
279 target: &mut Target,
280 ) -> Result<(), Target::AppendError> {
281 self.flags.compose(target)?;
282 self.protocol.compose(target)?;
283 self.algorithm.compose(target)?;
284 target.append_slice(self.public_key.as_ref())
285 }
286
287 fn compose_canonical_rdata<Target: Composer + ?Sized>(
288 &self,
289 target: &mut Target,
290 ) -> Result<(), Target::AppendError> {
291 self.compose_rdata(target)
292 }
293}
294
295impl<Octs: AsRef<[u8]>> fmt::Display for Cdnskey<Octs> {
298 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
299 write!(f, "{} {} {} ", self.flags, self.protocol, self.algorithm)?;
300 base64::display(&self.public_key, f)
301 }
302}
303
304impl<Octs: AsRef<[u8]>> fmt::Debug for Cdnskey<Octs> {
307 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
308 f.debug_struct("Cdnskey")
309 .field("flags", &self.flags)
310 .field("protocol", &self.protocol)
311 .field("algorithm", &self.algorithm)
312 .field("public_key", &self.public_key.as_ref())
313 .finish()
314 }
315}
316
317#[derive(Clone)]
320#[cfg_attr(
321 feature = "serde",
322 derive(serde::Serialize, serde::Deserialize),
323 serde(bound(
324 serialize = "
325 Octs: octseq::serde::SerializeOctets + AsRef<[u8]>
326 ",
327 deserialize = "
328 Octs:
329 octseq::builder::FromBuilder
330 + octseq::serde::DeserializeOctets<'de>,
331 <Octs as octseq::builder::FromBuilder>::Builder:
332 octseq::builder::OctetsBuilder
333 + octseq::builder::EmptyBuilder,
334 ",
335 ))
336)]
337pub struct Cds<Octs> {
338 key_tag: u16,
339 algorithm: SecAlg,
340 digest_type: DigestAlg,
341 #[cfg_attr(
342 feature = "serde",
343 serde(with = "crate::utils::base64::serde")
344 )]
345 digest: Octs,
346}
347
348impl<Octs> Cds<Octs> {
349 pub fn new(
350 key_tag: u16,
351 algorithm: SecAlg,
352 digest_type: DigestAlg,
353 digest: Octs,
354 ) -> Result<Self, LongRecordData>
355 where Octs: AsRef<[u8]> {
356 LongRecordData::check_len(
357 usize::from(
358 u16::COMPOSE_LEN + SecAlg::COMPOSE_LEN + DigestAlg::COMPOSE_LEN
359 ).checked_add(digest.as_ref().len()).expect("long digest")
360 )?;
361 Ok(unsafe {
362 Cds::new_unchecked(key_tag, algorithm, digest_type, digest)
363 })
364 }
365
366 pub unsafe fn new_unchecked(
373 key_tag: u16,
374 algorithm: SecAlg,
375 digest_type: DigestAlg,
376 digest: Octs,
377 ) -> Self {
378 Cds {
379 key_tag,
380 algorithm,
381 digest_type,
382 digest,
383 }
384 }
385
386 pub fn key_tag(&self) -> u16 {
387 self.key_tag
388 }
389
390 pub fn algorithm(&self) -> SecAlg {
391 self.algorithm
392 }
393
394 pub fn digest_type(&self) -> DigestAlg {
395 self.digest_type
396 }
397
398 pub fn digest(&self) -> &Octs {
399 &self.digest
400 }
401
402 pub fn into_digest(self) -> Octs {
403 self.digest
404 }
405
406 pub(super) fn convert_octets<Target: OctetsFrom<Octs>>(
407 self,
408 ) -> Result<Cds<Target>, Target::Error> {
409 Ok(unsafe {
410 Cds::new_unchecked(
411 self.key_tag,
412 self.algorithm,
413 self.digest_type,
414 self.digest.try_octets_into()?,
415 )
416 })
417 }
418
419 pub(super) fn flatten<Target: OctetsFrom<Octs>>(
420 self,
421 ) -> Result<Cds<Target>, Target::Error> {
422 self.convert_octets()
423 }
424
425 pub fn parse<'a, Src: Octets<Range<'a> = Octs> + ?Sized>(
426 parser: &mut Parser<'a, Src>,
427 ) -> Result<Self, ParseError> {
428 let len = match parser.remaining().checked_sub(4) {
429 Some(len) => len,
430 None => return Err(ParseError::ShortInput),
431 };
432 Ok(unsafe {
433 Self::new_unchecked(
434 u16::parse(parser)?,
435 SecAlg::parse(parser)?,
436 DigestAlg::parse(parser)?,
437 parser.parse_octets(len)?,
438 )
439 })
440 }
441
442 pub fn scan<S: Scanner<Octets = Octs>>(
443 scanner: &mut S,
444 ) -> Result<Self, S::Error>
445 where Octs: AsRef<[u8]> {
446 Self::new(
447 u16::scan(scanner)?,
448 SecAlg::scan(scanner)?,
449 DigestAlg::scan(scanner)?,
450 scanner.convert_entry(base16::SymbolConverter::new())?,
451 ).map_err(|err| S::Error::custom(err.as_str()))
452 }
453}
454
455impl<Octs, SrcOcts> OctetsFrom<Cds<SrcOcts>> for Cds<Octs>
458where
459 Octs: OctetsFrom<SrcOcts>,
460{
461 type Error = Octs::Error;
462
463 fn try_octets_from(source: Cds<SrcOcts>) -> Result<Self, Self::Error> {
464 Ok(unsafe {
465 Cds::new_unchecked(
466 source.key_tag,
467 source.algorithm,
468 source.digest_type,
469 Octs::try_octets_from(source.digest)?,
470 )
471 })
472 }
473}
474
475impl<Octs, Other> PartialEq<Cds<Other>> for Cds<Octs>
478where
479 Octs: AsRef<[u8]>,
480 Other: AsRef<[u8]>,
481{
482 fn eq(&self, other: &Cds<Other>) -> bool {
483 self.key_tag == other.key_tag
484 && self.algorithm == other.algorithm
485 && self.digest_type == other.digest_type
486 && self.digest.as_ref().eq(other.digest.as_ref())
487 }
488}
489
490impl<Octs: AsRef<[u8]>> Eq for Cds<Octs> {}
491
492impl<Octs, Other> PartialOrd<Cds<Other>> for Cds<Octs>
495where
496 Octs: AsRef<[u8]>,
497 Other: AsRef<[u8]>,
498{
499 fn partial_cmp(&self, other: &Cds<Other>) -> Option<Ordering> {
500 match self.key_tag.partial_cmp(&other.key_tag) {
501 Some(Ordering::Equal) => {}
502 other => return other,
503 }
504 match self.algorithm.partial_cmp(&other.algorithm) {
505 Some(Ordering::Equal) => {}
506 other => return other,
507 }
508 match self.digest_type.partial_cmp(&other.digest_type) {
509 Some(Ordering::Equal) => {}
510 other => return other,
511 }
512 self.digest.as_ref().partial_cmp(other.digest.as_ref())
513 }
514}
515
516impl<Octs, Other> CanonicalOrd<Cds<Other>> for Cds<Octs>
517where
518 Octs: AsRef<[u8]>,
519 Other: AsRef<[u8]>,
520{
521 fn canonical_cmp(&self, other: &Cds<Other>) -> Ordering {
522 match self.key_tag.cmp(&other.key_tag) {
523 Ordering::Equal => {}
524 other => return other,
525 }
526 match self.algorithm.cmp(&other.algorithm) {
527 Ordering::Equal => {}
528 other => return other,
529 }
530 match self.digest_type.cmp(&other.digest_type) {
531 Ordering::Equal => {}
532 other => return other,
533 }
534 self.digest.as_ref().cmp(other.digest.as_ref())
535 }
536}
537
538impl<Octs: AsRef<[u8]>> Ord for Cds<Octs> {
539 fn cmp(&self, other: &Self) -> Ordering {
540 self.canonical_cmp(other)
541 }
542}
543
544impl<Octs: AsRef<[u8]>> hash::Hash for Cds<Octs> {
547 fn hash<H: hash::Hasher>(&self, state: &mut H) {
548 self.key_tag.hash(state);
549 self.algorithm.hash(state);
550 self.digest_type.hash(state);
551 self.digest.as_ref().hash(state);
552 }
553}
554
555impl<Octs> RecordData for Cds<Octs> {
558 fn rtype(&self) -> Rtype {
559 Rtype::Cds
560 }
561}
562
563impl<'a, Octs> ParseRecordData<'a, Octs> for Cds<Octs::Range<'a>>
564where
565 Octs: Octets + ?Sized,
566{
567 fn parse_rdata(
568 rtype: Rtype,
569 parser: &mut Parser<'a, Octs>,
570 ) -> Result<Option<Self>, ParseError> {
571 if rtype == Rtype::Cds {
572 Self::parse(parser).map(Some)
573 } else {
574 Ok(None)
575 }
576 }
577}
578
579impl<Octs: AsRef<[u8]>> ComposeRecordData for Cds<Octs> {
580 fn rdlen(&self, _compress: bool) -> Option<u16> {
581 Some(
582 u16::checked_add(
583 u16::COMPOSE_LEN
584 + SecAlg::COMPOSE_LEN
585 + DigestAlg::COMPOSE_LEN,
586 self.digest.as_ref().len().try_into().expect("long digest"),
587 )
588 .expect("long digest"),
589 )
590 }
591
592 fn compose_rdata<Target: Composer + ?Sized>(
593 &self,
594 target: &mut Target,
595 ) -> Result<(), Target::AppendError> {
596 self.key_tag.compose(target)?;
597 self.algorithm.compose(target)?;
598 self.digest_type.compose(target)?;
599 target.append_slice(self.digest.as_ref())
600 }
601
602 fn compose_canonical_rdata<Target: Composer + ?Sized>(
603 &self,
604 target: &mut Target,
605 ) -> Result<(), Target::AppendError> {
606 self.compose_rdata(target)
607 }
608}
609
610impl<Octs: AsRef<[u8]>> fmt::Display for Cds<Octs> {
613 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
614 write!(
615 f,
616 "{} {} {} ",
617 self.key_tag, self.algorithm, self.digest_type
618 )?;
619 for ch in self.digest.as_ref() {
620 write!(f, "{:02x}", ch)?
621 }
622 Ok(())
623 }
624}
625
626impl<Octs: AsRef<[u8]>> fmt::Debug for Cds<Octs> {
629 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
630 f.debug_struct("Cds")
631 .field("key_tag", &self.key_tag)
632 .field("algorithm", &self.algorithm)
633 .field("digest_type", &self.digest_type)
634 .field("digest", &self.digest.as_ref())
635 .finish()
636 }
637}
638
639pub mod parsed {
642 pub use super::{Cdnskey, Cds};
643}
644
645#[cfg(test)]
648#[cfg(all(feature = "std", feature = "bytes"))]
649mod test {
650 use super::*;
651 use crate::base::rdata::test::{
652 test_compose_parse, test_rdlen, test_scan,
653 };
654
655 #[test]
658 #[allow(clippy::redundant_closure)] fn cdnskey_compose_parse_scan() {
660 let rdata = Cdnskey::new(10, 11, SecAlg::RsaSha1, b"key").unwrap();
661 test_rdlen(&rdata);
662 test_compose_parse(&rdata, |parser| Cdnskey::parse(parser));
663 test_scan(&["10", "11", "RSASHA1", "a2V5"], Cdnskey::scan, &rdata);
664 }
665
666 #[test]
669 #[allow(clippy::redundant_closure)] fn cds_compose_parse_scan() {
671 let rdata = Cds::new(
672 10, SecAlg::RsaSha1, DigestAlg::Sha256, b"key"
673 ).unwrap();
674 test_rdlen(&rdata);
675 test_compose_parse(&rdata, |parser| Cds::parse(parser));
676 test_scan(&["10", "RSASHA1", "2", "6b6579"], Cds::scan, &rdata);
677 }
678}