Skip to main content

hickory_proto/rr/rdata/
openpgpkey.rs

1// Copyright 2015-2023 Benjamin Fry <benjaminfry@me.com>
2//
3// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
4// https://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
5// https://opensource.org/licenses/MIT>, at your option. This file may not be
6// copied, modified, or distributed except according to those terms.
7
8//! OPENPGPKEY records for OpenPGP public keys
9use alloc::vec::Vec;
10use core::fmt;
11
12#[cfg(feature = "serde")]
13use serde::{Deserialize, Serialize};
14
15use crate::{
16    error::ProtoResult,
17    rr::{RData, RecordData, RecordDataDecodable, RecordType},
18    serialize::{
19        binary::{BinDecoder, BinEncodable, BinEncoder, DecodeError, Restrict},
20        txt::ParseError,
21    },
22};
23
24/// [RFC 7929](https://tools.ietf.org/html/rfc7929#section-2.1)
25///
26/// ```text
27/// The RDATA portion of an OPENPGPKEY resource record contains a single
28/// value consisting of a Transferable Public Key formatted as specified
29/// in [RFC4880].
30/// ```
31#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
32#[derive(Debug, PartialEq, Eq, Hash, Clone)]
33#[non_exhaustive]
34pub struct OPENPGPKEY {
35    /// The public key.
36    ///
37    /// This should be an OpenPGP Transferable Public Key, but this is not guaranteed.
38    pub public_key: Vec<u8>,
39}
40
41impl OPENPGPKEY {
42    /// Creates a new OPENPGPKEY record data.
43    ///
44    /// # Arguments
45    ///
46    /// * `public_key` - an OpenPGP Transferable Public Key. This will NOT
47    ///   be checked.
48    pub fn new(public_key: Vec<u8>) -> Self {
49        Self { public_key }
50    }
51
52    /// Parse the RData from a set of tokens.
53    ///
54    /// [RFC 7929](https://tools.ietf.org/html/rfc7929#section-2.3)
55    ///
56    /// ```text
57    /// 2.3.  The OPENPGPKEY RDATA Presentation Format
58    ///
59    ///    The RDATA Presentation Format, as visible in Zone Files [RFC1035],
60    ///    consists of a single OpenPGP Transferable Public Key as defined in
61    ///    Section 11.1 of [RFC4880] encoded in base64 as defined in Section 4
62    ///    of [RFC4648].
63    /// ```
64    pub(crate) fn from_tokens<'i, I: Iterator<Item = &'i str>>(
65        mut tokens: I,
66    ) -> Result<Self, ParseError> {
67        let encoded_public_key = tokens.next().ok_or(ParseError::Message(
68            "OPENPGPKEY public key field is missing",
69        ))?;
70        let public_key = data_encoding::BASE64.decode(encoded_public_key.as_bytes())?;
71        Some(Self::new(public_key))
72            .filter(|_| tokens.next().is_none())
73            .ok_or(ParseError::Message("too many fields for OPENPGPKEY"))
74    }
75}
76
77impl BinEncodable for OPENPGPKEY {
78    fn emit(&self, encoder: &mut BinEncoder<'_>) -> ProtoResult<()> {
79        encoder.emit_vec(&self.public_key)
80    }
81}
82
83impl<'r> RecordDataDecodable<'r> for OPENPGPKEY {
84    fn read_data(decoder: &mut BinDecoder<'r>, length: Restrict<u16>) -> Result<Self, DecodeError> {
85        let rdata_length = length.map(usize::from).unverified();
86        let public_key =
87            decoder.read_vec(rdata_length)?.unverified(/*we do not enforce a specific format*/);
88        Ok(Self::new(public_key))
89    }
90}
91
92impl RecordData for OPENPGPKEY {
93    fn try_borrow(data: &RData) -> Option<&Self> {
94        match data {
95            RData::OPENPGPKEY(csync) => Some(csync),
96            _ => None,
97        }
98    }
99
100    fn record_type(&self) -> RecordType {
101        RecordType::OPENPGPKEY
102    }
103
104    fn into_rdata(self) -> RData {
105        RData::OPENPGPKEY(self)
106    }
107}
108
109/// Parse the RData from a set of tokens.
110///
111/// [RFC 7929](https://tools.ietf.org/html/rfc7929#section-2.3)
112///
113/// ```text
114/// 2.3.  The OPENPGPKEY RDATA Presentation Format
115///
116///    The RDATA Presentation Format, as visible in Zone Files [RFC1035],
117///    consists of a single OpenPGP Transferable Public Key as defined in
118///    Section 11.1 of [RFC4880] encoded in base64 as defined in Section 4
119///    of [RFC4648].
120/// ```
121impl fmt::Display for OPENPGPKEY {
122    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
123        f.write_str(&data_encoding::BASE64.encode(&self.public_key))
124    }
125}
126
127#[cfg(test)]
128mod tests {
129    use super::*;
130
131    #[test]
132    fn test_parsing() {
133        assert!(OPENPGPKEY::from_tokens(core::iter::empty()).is_err());
134        assert!(OPENPGPKEY::from_tokens(vec!["äöüäööüä"].into_iter()).is_err());
135        assert!(OPENPGPKEY::from_tokens(vec!["ZmFpbGVk", "äöüäöüö"].into_iter()).is_err());
136
137        assert!(
138            OPENPGPKEY::from_tokens(vec!["dHJ1c3RfZG5zIGlzIGF3ZXNvbWU="].into_iter())
139                .map(|rd| rd == OPENPGPKEY::new(b"trust_dns is awesome".to_vec()))
140                .unwrap_or(false)
141        );
142        assert!(
143            OPENPGPKEY::from_tokens(vec!["c2VsZi1wcmFpc2Ugc3Rpbmtz"].into_iter())
144                .map(|rd| rd == OPENPGPKEY::new(b"self-praise stinks".to_vec()))
145                .unwrap_or(false)
146        );
147    }
148}