Skip to main content

ndn_protocol/
certificate.rs

1//! NDN certificates: [`Certificate`], a signed [`Data`] packet carrying a
2//! public key, and [`SafeBag`], the format NDN tools export a certificate
3//! and its matching private key in together.
4//!
5//! [`RsaCertificate`] pairs a [`Certificate`] with the RSA key material
6//! needed to actually sign and verify with it -- see
7//! [`RsaCertificate::from_safebag`] to load one from a `.safebag`/`.ndnkeys`
8//! file exported by ndn-cxx tools.
9
10use std::path::Path;
11
12use base64::Engine;
13use bytes::Bytes;
14use ndn_tlv::{Tlv, TlvDecode, TlvEncode};
15use rsa::{
16    pkcs8::{DecodePrivateKey, DecodePublicKey},
17    RsaPrivateKey, RsaPublicKey,
18};
19
20use crate::{error::NdnError, Data, KeyLocator, Name, SignatureInfo};
21
22/// The on-disk format NDN tools (e.g. `ndnsec`) export a certificate and
23/// its private key together in, encrypted with a password.
24#[derive(Tlv, Clone, Hash, Debug)]
25#[tlv(128)]
26pub struct SafeBag {
27    /// The certificate, as a signed [`Data`] packet.
28    pub certificate: Data<Bytes>,
29    /// The private key, encrypted with the export password.
30    pub encrypted_key: EncryptedKey,
31}
32
33/// The encrypted private key portion of a [`SafeBag`].
34#[derive(Tlv, Clone, Hash, Debug)]
35#[tlv(129)]
36pub struct EncryptedKey {
37    /// The encrypted key data.
38    pub data: Bytes,
39}
40
41/// An NDN certificate: a [`Data`] packet whose content is a public key,
42/// signed by an issuer.
43#[derive(Clone, Debug, Hash)]
44pub struct Certificate(pub Data<Bytes>);
45
46/// Implemented by types that can produce the [`Certificate`] backing them,
47/// e.g. [`RsaCertificate`].
48pub trait ToCertificate {
49    /// Returns the certificate.
50    fn to_certificate(&self) -> Certificate;
51}
52
53/// A [`Certificate`] together with the RSA key material needed to sign and
54/// verify with it. The private key is only present if it was loaded, e.g.
55/// via [`RsaCertificate::from_safebag`] or [`RsaCertificate::with_private`].
56#[derive(Clone, Debug, Hash)]
57pub struct RsaCertificate {
58    cert: Certificate,
59    public_key: RsaPublicKey,
60    private_key: Option<RsaPrivateKey>,
61}
62
63impl RsaCertificate {
64    /// Wraps `cert` as an `RsaCertificate` with no private key, by
65    /// extracting its public key from the certificate's content.
66    ///
67    /// Returns `None` if the certificate's content isn't a valid RSA
68    /// public key.
69    pub fn new(cert: Certificate) -> Option<Self> {
70        let key = RsaPublicKey::from_public_key_der(&cert.0.content()?).ok()?;
71        Some(Self {
72            cert,
73            public_key: key,
74            private_key: None,
75        })
76    }
77
78    /// Wraps `cert` as an `RsaCertificate`, together with its matching
79    /// private key.
80    ///
81    /// Returns `None` if the certificate's content isn't a valid RSA
82    /// public key.
83    pub fn with_private(cert: Certificate, private_key: RsaPrivateKey) -> Option<Self> {
84        let key = RsaPublicKey::from_public_key_der(&cert.0.content()?).ok()?;
85        Some(Self {
86            cert,
87            public_key: key,
88            private_key: Some(private_key),
89        })
90    }
91
92    /// Decrypts the private key in `bag` with `password` and pairs it with
93    /// the certificate it came with.
94    ///
95    /// Returns `None` if the password is wrong or the key/certificate data
96    /// is malformed.
97    pub fn from_safebag<P>(bag: SafeBag, password: P) -> Option<Self>
98    where
99        P: AsRef<[u8]>,
100    {
101        let key =
102            RsaPrivateKey::from_pkcs8_encrypted_der(&bag.encrypted_key.data, password).ok()?;
103        Self::with_private(Certificate(bag.certificate), key)
104    }
105
106    /// The certificate's name.
107    pub fn name(&self) -> &Name {
108        self.cert.name()
109    }
110
111    /// The RSA public key.
112    pub fn public_key(&self) -> &RsaPublicKey {
113        &self.public_key
114    }
115
116    /// The RSA private key, if one was loaded.
117    pub fn private_key(&self) -> Option<&RsaPrivateKey> {
118        self.private_key.as_ref()
119    }
120}
121
122impl ToCertificate for RsaCertificate {
123    fn to_certificate(&self) -> Certificate {
124        self.cert.clone()
125    }
126}
127
128impl SafeBag {
129    /// Reads and decodes a base64-encoded `.safebag` file, e.g. one
130    /// exported by `ndnsec export`.
131    pub fn load_file(path: impl AsRef<Path>) -> Result<Self, NdnError> {
132        let mut file_content = std::fs::read(path)?;
133        file_content.retain(|x| *x != b'\n' && *x != b'\r');
134        let safebag_data = base64::engine::general_purpose::STANDARD
135            .decode(&file_content)
136            .map_err(|_| {
137                NdnError::GenericError("Could not base64-decode certificate".to_string())
138            })?;
139        Ok(SafeBag::decode(&mut Bytes::from(safebag_data))?)
140    }
141}
142
143impl Certificate {
144    /// Reads and decodes a base64-encoded certificate file (just the
145    /// certificate, without an accompanying private key).
146    pub fn load_file<P>(path: P) -> Result<Self, NdnError>
147    where
148        P: AsRef<Path>,
149    {
150        let mut file_content = std::fs::read(path)?;
151        file_content.retain(|x| *x != b'\n' && *x != b'\r');
152        let safebag_data = base64::engine::general_purpose::STANDARD
153            .decode(&file_content)
154            .map_err(|_| {
155                NdnError::GenericError("Could not base64-decode certificate".to_string())
156            })?;
157        Ok(Self(Data::<Bytes>::decode(&mut Bytes::from(safebag_data))?))
158    }
159
160    /// The certificate's name, of the form `/<identity>/KEY/<key-id>/<issuer-id>/<version>`.
161    pub fn name(&self) -> &Name {
162        self.0.name()
163    }
164
165    /// The identity this certificate belongs to: its name with the
166    /// trailing `KEY/<key-id>/<issuer-id>/<version>` components removed.
167    pub fn identity(&self) -> Name {
168        let mut name = self.name().clone();
169        name.components.pop();
170        name.components.pop();
171        name.components.pop();
172        name.components.pop();
173        name
174    }
175
176    /// A [`KeyLocator`] pointing at this certificate by name, for use in a
177    /// [`SignatureInfo`]/`InterestSignatureInfo`.
178    pub fn name_locator(&self) -> KeyLocator {
179        KeyLocator::new(crate::signature::KeyLocatorData::Name(self.name().clone()))
180    }
181
182    /// The certificate as the [`Data`] packet it's backed by.
183    pub fn as_data(&self) -> &Data<Bytes> {
184        &self.0
185    }
186
187    /// The signature info of the Data packet backing this certificate,
188    /// i.e. how the issuer signed it.
189    pub fn signature_info(&self) -> Option<&SignatureInfo> {
190        self.0.signature_info()
191    }
192}
193
194impl TlvEncode for Certificate {
195    fn encode(&self) -> Bytes {
196        self.0.encode()
197    }
198
199    fn size(&self) -> usize {
200        self.0.size()
201    }
202}
203
204impl TlvDecode for Certificate {
205    fn decode(bytes: &mut Bytes) -> ndn_tlv::Result<Self> {
206        Data::<Bytes>::decode(bytes).map(Self)
207    }
208}