1
// SPDX-License-Identifier: BSD-2-Clause
2
/*
3
 * crypto/pki_rpk/pki.rs - Interfaces and types for PKI support in libcoap-rs.
4
 * This file is part of the libcoap-rs crate, see the README and LICENSE files for
5
 * more information and terms of use.
6
 * Copyright © 2021-2024 The NAMIB Project Developers, all rights reserved.
7
 * See the README as well as the LICENSE file for more information.
8
 */
9

            
10
use crate::crypto::pki_rpk;
11
use crate::crypto::pki_rpk::key::{KeyComponentSealed, KeyTypeSealed};
12
use crate::crypto::pki_rpk::{
13
    Asn1PrivateKeyType, CertVerificationMode, CertVerifying, CnCallback, DerFileKeyComponent, DerMemoryKeyComponent,
14
    EngineKeyComponent, KeyComponent, KeyDef, KeyDefSealed, NonCertVerifying, PemFileKeyComponent,
15
    PemMemoryKeyComponent, Pkcs11KeyComponent, PkiRpkContext, PkiRpkContextBuilder, ServerPkiRpkCryptoContext,
16
};
17
use crate::crypto::ClientCryptoContext;
18
use crate::session::CoapSession;
19
use libcoap_sys::{
20
    coap_const_char_ptr_t, coap_dtls_key_t, coap_dtls_key_t__bindgen_ty_1, coap_dtls_pki_t, coap_pki_define_t,
21
    coap_pki_key_define_t, coap_pki_key_t,
22
};
23
use std::ffi::{c_uint, CStr, CString};
24
use std::fmt::Debug;
25

            
26
/// (Marker) key type for keys with a certificate signed by a trusted CA.
27
#[derive(Debug, Clone, Copy)]
28
pub struct Pki {}
29

            
30
impl KeyTypeSealed for Pki {
31
230
    fn set_key_type_defaults(ctx: &mut coap_dtls_pki_t) {
32
230
        ctx.is_rpk_not_cert = 0;
33
230
    }
34
}
35

            
36
// If PKI is enabled, implement conversions for PKI contexts to PKI-supporting server/client-side
37
// cryptographic contexts.
38

            
39
impl<'a> From<PkiRpkContext<'a, Pki>> for ServerPkiRpkCryptoContext<'a> {
40
115
    fn from(value: PkiRpkContext<'a, Pki>) -> Self {
41
115
        ServerPkiRpkCryptoContext::Pki(value)
42
115
    }
43
}
44

            
45
impl<'a> From<PkiRpkContext<'a, Pki>> for ClientCryptoContext<'a> {
46
115
    fn from(value: PkiRpkContext<'a, Pki>) -> Self {
47
115
        ClientCryptoContext::Pki(value)
48
115
    }
49
}
50

            
51
impl<'a> PkiRpkContextBuilder<'a, Pki, NonCertVerifying> {
52
    /// Enables PKI certificate verification of the peer's certificate when using the build
53
    /// encryption context.
54
    ///
55
    /// Note: While this will enable peer certificate validation, the other settings relating to
56
    /// certificate validation will not automatically be enabled.
57
    /// In particular, you might want to consider enabling certificate chain validation using
58
    /// [`PkiRpkContextBuilder::cert_chain_validation`].
59
    ///
60
    /// Depending on your circumstances, you might want to add additional root certificates
61
    /// using [`CoapContext::set_pki_root_cas`](crate::CoapContext::set_pki_root_ca_paths).
62
    ///
63
    /// # Implementation details (informative, not covered by semver guarantees)
64
    ///
65
    /// Equivalent to setting `verify_peer_cert` to `1` in the underlying [`coap_dtls_pki_t`]
66
    /// structure.
67
230
    pub fn verify_peer_cert(mut self) -> PkiRpkContextBuilder<'a, Pki, CertVerifying> {
68
230
        self.ctx.raw_cfg.verify_peer_cert = 1;
69
230
        PkiRpkContextBuilder::<Pki, CertVerifying> {
70
230
            ctx: self.ctx,
71
230
            verifying: Default::default(),
72
230
        }
73
230
    }
74
}
75

            
76
impl<'a> PkiRpkContextBuilder<'a, Pki, CertVerifying> {
77
    pub fn new<K: KeyDef<KeyType = Pki> + 'a>(key: K) -> Self {
78
        PkiRpkContextBuilder::<'a, Pki, NonCertVerifying>::new(key).verify_peer_cert()
79
    }
80
}
81

            
82
impl<'a, V: CertVerificationMode> PkiRpkContextBuilder<'a, Pki, V> {
83
    /// Sets the common name validator for this encryption context.
84
    ///
85
    /// The common name validator's [`validate_cn`](PkiCnValidator::validate_cn) function will be
86
    /// called after the TLS level validation checks have been completed in order to check whether
87
    /// the common name provided by the peer is allowed/as expected.
88
    ///
89
    /// # Implementation details (informative, not covered by semver guarantees)
90
    ///
91
    /// Setting a `cn_validator` will set the `validate_cn_call_back` of the underlying
92
    /// [`coap_dtls_pki_t`] to a wrapper function, which will then call the CN validator.
93
    pub fn cn_validator(mut self, validator: impl PkiCnValidator + 'a) -> Self {
94
        self.ctx.cn_callback = Some(CnCallback::Pki(Box::new(validator)));
95
        self.ctx.raw_cfg.validate_cn_call_back = Some(pki_rpk::dtls_pki_cn_callback::<Pki>);
96
        self
97
    }
98
}
99

            
100
/// Trait for types that can check whether a peer's or CA certificate's common name is allowed/as
101
/// expected for a session.
102
pub trait PkiCnValidator {
103
    /// Validates the common name of a peer or intermediate certificate.
104
    ///
105
    /// Aside from the common name given as `cn`, this function is also provided with the raw bytes
106
    /// of the ASN.1/DER encoded public certificate (`asn1_public_cert`), the respective `session`,
107
    /// the TLS library's `validated` status and the current `depth` that should be validated.
108
    ///
109
    /// `depth` will be 0 for the peer's certificate, and larger than 0 for a CA certificate.
110
    ///
111
    /// Should return `true` if the connection is to be accepted and `false` if the connection
112
    /// should be aborted.
113
    ///
114
    /// See [the libcoap documentation](https://libcoap.net/doc/reference/4.3.5/group__dtls.html#gaef7a2800757a4922102311c94c3fa529)
115
    /// for more background information.
116
    fn validate_cn(
117
        &self,
118
        cn: &CStr,
119
        asn1_public_cert: &[u8],
120
        session: &CoapSession,
121
        depth: c_uint,
122
        validated: bool,
123
    ) -> bool;
124
}
125

            
126
impl<T: Fn(&CStr, &[u8], &CoapSession, c_uint, bool) -> bool> PkiCnValidator for T {
127
    fn validate_cn(
128
        &self,
129
        cn: &CStr,
130
        asn1_public_cert: &[u8],
131
        session: &CoapSession,
132
        depth: c_uint,
133
        validated: bool,
134
    ) -> bool {
135
        self(cn, asn1_public_cert, session, depth, validated)
136
    }
137
}
138

            
139
/// Key definition for a DTLS key consisting of a private key and a CA-signed certificate.
140
///
141
/// Optionally, it may also contain a CA certificate whose name will be sent to clients to indicate
142
/// the key that they should themselves send.
143
///
144
/// # Note on key construction
145
///
146
/// For maximum compatibility, you should stick to the `with_*` constructors defined for this type.
147
/// While in theory you could use an arbitrary combination of key component types for a key
148
/// definition, those defined using `with_*` match explicit key types provided in libcoap and should
149
/// therefore always be supported.
150
///
151
/// # The CA certificate field
152
///
153
/// **Important:** The CA certificate field/parameter is not to be confused with the CA certificate
154
/// you may set while configuring HTTP servers. The CA certificate will **not** be sent in full to
155
/// the peer during connection establishment and does not have to refer to the CA that signed the
156
/// public certificate.
157
/// It will only be used to set the CA list sent to the client for client certificate validation.
158
///
159
/// Therefore, in order for TLS certificate validation to succeed, the peer must already know the
160
/// root CA's and all intermediate CAs' certificates.
161
#[derive(Clone, Debug)]
162
pub struct PkiKeyDef<CA: KeyComponent<Pki>, PK: KeyComponent<Pki>, SK: KeyComponent<Pki>> {
163
    ca_cert: Option<CA>,
164
    public_cert: PK,
165
    private_key: SK,
166
    user_pin: Option<CString>,
167
    asn1_private_key_type: Asn1PrivateKeyType,
168
}
169

            
170
impl<CA: KeyComponent<Pki>, PK: KeyComponent<Pki>, SK: KeyComponent<Pki>> PkiKeyDef<CA, PK, SK> {
171
    /// Creates a new key definition using the given components.
172
    ///
173
    /// # Parameters
174
    ///
175
    /// - `ca_cert`:     The certificate of the CA whose name should be provided to clients when
176
    ///                  requesting client certificates.
177
    ///                  **Important:** See the section in the struct-level documentation regarding
178
    ///                  this field for more information.
179
    /// - `public_cert`: The public (signed) certificate of this key.
180
    /// - `private_key`: The private key.
181
    /// - `user_pin`:    The PIN that should be used when unlocking a token (for PKCS11 keys stored
182
    ///                  on a token, ignored otherwise)
183
    /// - `asn1_private_key_type`: The type of the private key (only used for DER/ASN.1 encoded
184
    ///                  keys).
185
22
    pub fn new(
186
22
        ca_cert: Option<CA>,
187
22
        public_cert: PK,
188
22
        private_key: SK,
189
22
        user_pin: Option<CString>,
190
22
        asn1_private_key_type: Asn1PrivateKeyType,
191
22
    ) -> Self {
192
22
        Self {
193
22
            ca_cert,
194
22
            public_cert,
195
22
            private_key,
196
22
            user_pin,
197
22
            asn1_private_key_type,
198
22
        }
199
22
    }
200
}
201

            
202
impl PkiKeyDef<PemFileKeyComponent, PemFileKeyComponent, PemFileKeyComponent> {
203
    /// Creates a new key definition using PEM-encoded files as components.
204
    ///
205
    /// See the documentation of [PkiKeyDef::new] for more information on the parameters, especially
206
    /// regarding the `ca_cert` field.
207
6
    pub fn with_pem_files(
208
6
        ca_cert: Option<impl Into<PemFileKeyComponent>>,
209
6
        public_cert: impl Into<PemFileKeyComponent>,
210
6
        private_key: impl Into<PemFileKeyComponent>,
211
6
    ) -> Self {
212
6
        Self::new(
213
6
            ca_cert.map(|v| v.into()),
214
6
            public_cert.into(),
215
6
            private_key.into(),
216
6
            None,
217
6
            Asn1PrivateKeyType::None,
218
6
        )
219
6
    }
220
}
221

            
222
impl PkiKeyDef<PemMemoryKeyComponent, PemMemoryKeyComponent, PemMemoryKeyComponent> {
223
    /// Creates a new key definition using PEM-encoded byte sequences in memory as components.
224
    ///
225
    /// See the documentation of [`PkiKeyDef::new`] for more information on the parameters, especially
226
    /// regarding the `ca_cert` field.
227
6
    pub fn with_pem_memory(
228
6
        ca_cert: Option<impl Into<PemMemoryKeyComponent>>,
229
6
        public_cert: impl Into<PemMemoryKeyComponent>,
230
6
        private_key: impl Into<PemMemoryKeyComponent>,
231
6
    ) -> Self {
232
6
        Self::new(
233
6
            ca_cert.map(|v| v.into()),
234
6
            public_cert.into(),
235
6
            private_key.into(),
236
6
            None,
237
6
            Asn1PrivateKeyType::None,
238
6
        )
239
6
    }
240
}
241

            
242
impl PkiKeyDef<DerFileKeyComponent, DerFileKeyComponent, DerFileKeyComponent> {
243
    /// Creates a new key definition using DER-encoded files as components.
244
    ///
245
    /// See the documentation of [`PkiKeyDef::new`] for more information on the parameters, especially
246
    /// regarding the `ca_cert` field.
247
6
    pub fn with_asn1_files(
248
6
        ca_cert: Option<impl Into<DerFileKeyComponent>>,
249
6
        public_cert: impl Into<DerFileKeyComponent>,
250
6
        private_key: impl Into<DerFileKeyComponent>,
251
6
        private_key_type: Asn1PrivateKeyType,
252
6
    ) -> Self {
253
6
        Self::new(
254
6
            ca_cert.map(|v| v.into()),
255
6
            public_cert.into(),
256
6
            private_key.into(),
257
6
            None,
258
6
            private_key_type,
259
6
        )
260
6
    }
261
}
262

            
263
impl PkiKeyDef<DerMemoryKeyComponent, DerMemoryKeyComponent, DerMemoryKeyComponent> {
264
    /// Creates a new key definition using DER-encoded byte sequences in memory as components.
265
    ///
266
    /// See the documentation of [`PkiKeyDef::new`] for more information on the parameters, especially
267
    /// regarding the `ca_cert` field.
268
4
    pub fn with_asn1_memory(
269
4
        ca_cert: Option<impl Into<DerMemoryKeyComponent>>,
270
4
        public_cert: impl Into<DerMemoryKeyComponent>,
271
4
        private_key: impl Into<DerMemoryKeyComponent>,
272
4
        private_key_type: Asn1PrivateKeyType,
273
4
    ) -> Self {
274
4
        Self::new(
275
4
            ca_cert.map(|v| v.into()),
276
4
            public_cert.into(),
277
4
            private_key.into(),
278
4
            None,
279
4
            private_key_type,
280
4
        )
281
4
    }
282
}
283

            
284
impl PkiKeyDef<Pkcs11KeyComponent, Pkcs11KeyComponent, Pkcs11KeyComponent> {
285
    /// Creates a new key definition using PKCS11 URIs as components.
286
    ///
287
    /// See the documentation of [`PkiKeyDef::new`] for more information on the parameters, especially
288
    /// regarding the `ca_cert` field.
289
    pub fn with_pkcs11(
290
        ca_cert: Option<impl Into<Pkcs11KeyComponent>>,
291
        public_cert: impl Into<Pkcs11KeyComponent>,
292
        private_key: impl Into<Pkcs11KeyComponent>,
293
        user_pin: Option<CString>,
294
    ) -> Self {
295
        Self::new(
296
            ca_cert.map(|v| v.into()),
297
            public_cert.into(),
298
            private_key.into(),
299
            user_pin,
300
            Asn1PrivateKeyType::None,
301
        )
302
    }
303
}
304

            
305
impl<CA: KeyComponent<Pki>, PK: KeyComponent<Pki>, SK: KeyComponent<Pki>> KeyDefSealed for PkiKeyDef<CA, PK, SK> {
306
22
    fn as_raw_dtls_key(&self) -> coap_dtls_key_t {
307
22
        let (ca, ca_len) = self.ca_cert.as_ref().map(|v| v.as_raw_key_component()).unwrap_or((
308
22
            coap_const_char_ptr_t {
309
22
                u_byte: std::ptr::null(),
310
22
            },
311
22
            0,
312
22
        ));
313
22
        let (public_cert, public_cert_len) = self.public_cert.as_raw_key_component();
314
22
        let (private_key, private_key_len) = self.private_key.as_raw_key_component();
315
22

            
316
22
        coap_dtls_key_t {
317
22
            key_type: coap_pki_key_t::COAP_PKI_KEY_DEFINE,
318
22
            key: coap_dtls_key_t__bindgen_ty_1 {
319
22
                define: coap_pki_key_define_t {
320
22
                    ca,
321
22
                    public_cert,
322
22
                    private_key,
323
22
                    ca_len,
324
22
                    public_cert_len,
325
22
                    private_key_len,
326
22
                    ca_def: <CA as KeyComponentSealed<Pki>>::DEFINE_TYPE,
327
22
                    public_cert_def: <PK as KeyComponentSealed<Pki>>::DEFINE_TYPE,
328
22
                    private_key_def: <SK as KeyComponentSealed<Pki>>::DEFINE_TYPE,
329
22
                    private_key_type: self.asn1_private_key_type.into(),
330
22
                    user_pin: self.user_pin.as_ref().map(|v| v.as_ptr()).unwrap_or(std::ptr::null()),
331
22
                },
332
22
            },
333
22
        }
334
22
    }
335
}
336

            
337
impl<CA: KeyComponent<Pki>, PK: KeyComponent<Pki>, SK: KeyComponent<Pki>> KeyDef for PkiKeyDef<CA, PK, SK> {
338
    type KeyType = Pki;
339
}
340

            
341
impl KeyComponentSealed<Pki> for PemFileKeyComponent {
342
    const DEFINE_TYPE: coap_pki_define_t = coap_pki_define_t::COAP_PKI_KEY_DEF_PEM;
343
}
344

            
345
impl KeyComponentSealed<Pki> for PemMemoryKeyComponent {
346
    const DEFINE_TYPE: coap_pki_define_t = coap_pki_define_t::COAP_PKI_KEY_DEF_PEM_BUF;
347
}
348

            
349
impl KeyComponentSealed<Pki> for DerFileKeyComponent {
350
    const DEFINE_TYPE: coap_pki_define_t = coap_pki_define_t::COAP_PKI_KEY_DEF_DER;
351
}
352

            
353
impl KeyComponentSealed<Pki> for DerMemoryKeyComponent {
354
    const DEFINE_TYPE: coap_pki_define_t = coap_pki_define_t::COAP_PKI_KEY_DEF_DER_BUF;
355
}
356

            
357
impl KeyComponentSealed<Pki> for Pkcs11KeyComponent {
358
    const DEFINE_TYPE: coap_pki_define_t = coap_pki_define_t::COAP_PKI_KEY_DEF_PKCS11;
359
}
360

            
361
impl KeyComponentSealed<Pki> for EngineKeyComponent {
362
    const DEFINE_TYPE: coap_pki_define_t = coap_pki_define_t::COAP_PKI_KEY_DEF_ENGINE;
363
}