1
// SPDX-License-Identifier: BSD-2-Clause
2
/*
3
 * crypto/pki_rpk/rpk.rs - Interfaces and types for RPK 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, CnCallback, KeyComponent, KeyDef, KeyDefSealed, NonCertVerifying, PemMemoryKeyComponent,
14
    Pkcs11KeyComponent, PkiRpkContext, PkiRpkContextBuilder, ServerPkiRpkCryptoContext,
15
};
16
use crate::crypto::ClientCryptoContext;
17
use crate::session::CoapSession;
18
use libcoap_sys::{
19
    coap_const_char_ptr_t, coap_dtls_key_t, coap_dtls_key_t__bindgen_ty_1, coap_dtls_pki_t, coap_pki_define_t,
20
    coap_pki_key_define_t, coap_pki_key_t,
21
};
22
use std::ffi::CString;
23
use std::fmt::Debug;
24

            
25
/// (Marker) key type for asymmetric DTLS keys not signed by a CA (raw public keys).
26
#[derive(Debug, Clone, Copy)]
27
pub struct Rpk {}
28

            
29
impl KeyTypeSealed for Rpk {
30
26
    fn set_key_type_defaults(ctx: &mut coap_dtls_pki_t) {
31
26
        ctx.is_rpk_not_cert = 1;
32
26
    }
33
}
34

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

            
38
impl<'a> From<PkiRpkContext<'a, Rpk>> for ClientCryptoContext<'a> {
39
13
    fn from(value: PkiRpkContext<'a, Rpk>) -> Self {
40
13
        ClientCryptoContext::Rpk(value)
41
13
    }
42
}
43

            
44
impl<'a> From<PkiRpkContext<'a, Rpk>> for ServerPkiRpkCryptoContext<'a> {
45
13
    fn from(value: PkiRpkContext<'a, Rpk>) -> Self {
46
13
        ServerPkiRpkCryptoContext::Rpk(value)
47
13
    }
48
}
49

            
50
impl<'a> PkiRpkContextBuilder<'a, Rpk, NonCertVerifying> {
51
    /// Sets the raw public key validator for this encryption context.
52
    ///
53
    /// The raw public key validator's [`validate_rpk`](RpkValidator::validate_rpk) function will be
54
    /// called after the TLS-level validation checks have been completed in order to check whether
55
    /// the RPK provided by the peer is allowed/as expected.
56
    ///
57
    /// # Implementation details (informative, not covered by semver guarantees)
58
    ///
59
    /// Setting an RPK validator will set the `validate_cn_call_back` of the underlying
60
    /// [`coap_dtls_pki_t`] to a wrapper function, which will then call the RPK validator.
61
    pub fn rpk_validator(mut self, validator: impl RpkValidator + 'a) -> Self {
62
        self.ctx.cn_callback = Some(CnCallback::Rpk(Box::new(validator)));
63
        self.ctx.raw_cfg.validate_cn_call_back = Some(pki_rpk::dtls_pki_cn_callback::<Rpk>);
64
        self
65
    }
66
}
67

            
68
/// Trait for types that can validate that a raw public key is the one expected for a given peer.
69
pub trait RpkValidator {
70
    /// Validates the raw public key of a peer.
71
    ///
72
    /// This function is provided with the public key (`asn1_public_key`), the respective `session`,
73
    /// and the TLS library's `validated` status, and should return `true` if the connection is to
74
    /// be accepted and `false` if the connection should be aborted.
75
    ///
76
    /// `asn1_encoded_key` should be the certificate structure defined in
77
    /// [RFC 7250, section 3](https://datatracker.ietf.org/doc/html/rfc7250#section-3), which you
78
    /// might be able to parse with crates like
79
    /// [x509-cert](https://docs.rs/x509-cert/latest/x509_cert/index.html) and
80
    /// [spki](https://docs.rs/spki/0.7.3/spki/index.html) to obtain and match the
81
    /// SubjectPublicKeyInformation encoded within.
82
    ///
83
    /// See [the libcoap documentation](https://libcoap.net/doc/reference/4.3.5/group__dtls.html#gaef7a2800757a4922102311c94c3fa529)
84
    /// for more information.
85
    fn validate_rpk(&self, asn1_public_key: &[u8], session: &CoapSession, validated: bool) -> bool;
86
}
87

            
88
impl<T: Fn(&[u8], &CoapSession, bool) -> bool> RpkValidator for T {
89
    fn validate_rpk(&self, asn1_public_key: &[u8], session: &CoapSession, validated: bool) -> bool {
90
        self(asn1_public_key, session, validated)
91
    }
92
}
93

            
94
/// Key definition for a DTLS key consisting of a private and public key component without a signed
95
/// certificate.
96
///
97
/// # Note on key construction
98
///
99
/// For maximum compatibility, you should stick to the `with_*` constructors defined for this type.
100
/// While in theory you could use an arbitrary combination of key component types for a key
101
/// definition, those defined using `with_*` match explicit key types provided in libcoap and should
102
/// therefore always be supported.
103
#[derive(Clone, Debug)]
104
pub struct RpkKeyDef<PK: KeyComponent<Rpk>, SK: KeyComponent<Rpk>> {
105
    public_key: PK,
106
    private_key: SK,
107
    user_pin: Option<CString>,
108
    asn1_private_key_type: Asn1PrivateKeyType,
109
}
110

            
111
impl<PK: KeyComponent<Rpk>, SK: KeyComponent<Rpk>> RpkKeyDef<PK, SK> {
112
    /// Creates a new key definition using the given components.
113
    ///
114
    /// # Parameters
115
    ///
116
    /// - `public_key`:  The public key component of this key.
117
    /// - `private_key`: The private key.
118
    /// - `user_pin`:    The PIN that should be used when unlocking a token (for PKCS11 keys stored
119
    ///                  on a token, ignored otherwise)
120
    /// - `asn1_private_key_type`: The type of the private key (only used for DER/ASN.1 encoded
121
    ///                  keys).
122
4
    pub fn new(
123
4
        public_key: PK,
124
4
        private_key: SK,
125
4
        user_pin: Option<CString>,
126
4
        asn1_private_key_type: Asn1PrivateKeyType,
127
4
    ) -> Self {
128
4
        Self {
129
4
            public_key,
130
4
            private_key,
131
4
            user_pin,
132
4
            asn1_private_key_type,
133
4
        }
134
4
    }
135
}
136

            
137
impl RpkKeyDef<PemMemoryKeyComponent, PemMemoryKeyComponent> {
138
    /// Creates a new key definition using PEM-encoded byte sequences in memory as components.
139
    ///
140
    /// See the documentation of [`RpkKeyDef::new`] for more information on the parameters.
141
4
    pub fn with_pem_memory(
142
4
        public_key: impl Into<PemMemoryKeyComponent>,
143
4
        private_key: impl Into<PemMemoryKeyComponent>,
144
4
    ) -> Self {
145
4
        Self::new(public_key.into(), private_key.into(), None, Asn1PrivateKeyType::None)
146
4
    }
147
}
148

            
149
impl RpkKeyDef<Pkcs11KeyComponent, Pkcs11KeyComponent> {
150
    /// Creates a new key definition using PKCS11 URIs as components.
151
    ///
152
    /// See the documentation of [`RpkKeyDef::new`] for more information on the parameters.
153
    pub fn with_pkcs11(
154
        public_key: impl Into<Pkcs11KeyComponent>,
155
        private_key: impl Into<Pkcs11KeyComponent>,
156
        user_pin: Option<CString>,
157
    ) -> Self {
158
        Self::new(
159
            public_key.into(),
160
            private_key.into(),
161
            user_pin,
162
            Asn1PrivateKeyType::None,
163
        )
164
    }
165
}
166

            
167
impl<PK: KeyComponent<Rpk>, SK: KeyComponent<Rpk>> KeyDefSealed for RpkKeyDef<PK, SK> {
168
4
    fn as_raw_dtls_key(&self) -> coap_dtls_key_t {
169
4
        let (public_cert, public_cert_len) = self.public_key.as_raw_key_component();
170
4
        let (private_key, private_key_len) = self.private_key.as_raw_key_component();
171
4

            
172
4
        coap_dtls_key_t {
173
4
            key_type: coap_pki_key_t::COAP_PKI_KEY_DEFINE,
174
4
            key: coap_dtls_key_t__bindgen_ty_1 {
175
4
                define: coap_pki_key_define_t {
176
4
                    ca: coap_const_char_ptr_t {
177
4
                        u_byte: std::ptr::null(),
178
4
                    },
179
4
                    public_cert,
180
4
                    private_key,
181
4
                    ca_len: 0,
182
4
                    public_cert_len,
183
4
                    private_key_len,
184
4
                    ca_def: coap_pki_define_t::COAP_PKI_KEY_DEF_PEM,
185
4
                    public_cert_def: <PK as KeyComponentSealed<Rpk>>::DEFINE_TYPE,
186
4
                    private_key_def: <SK as KeyComponentSealed<Rpk>>::DEFINE_TYPE,
187
4
                    private_key_type: self.asn1_private_key_type.into(),
188
4
                    user_pin: self.user_pin.as_ref().map(|v| v.as_ptr()).unwrap_or(std::ptr::null()),
189
4
                },
190
4
            },
191
4
        }
192
4
    }
193
}
194

            
195
impl<PK: KeyComponent<Rpk>, SK: KeyComponent<Rpk>> KeyDef for RpkKeyDef<PK, SK> {
196
    type KeyType = Rpk;
197
}
198

            
199
impl KeyComponentSealed<Rpk> for PemMemoryKeyComponent {
200
    const DEFINE_TYPE: coap_pki_define_t = coap_pki_define_t::COAP_PKI_KEY_DEF_RPK_BUF;
201
}
202

            
203
impl KeyComponentSealed<Rpk> for Pkcs11KeyComponent {
204
    const DEFINE_TYPE: coap_pki_define_t = coap_pki_define_t::COAP_PKI_KEY_DEF_PKCS11_RPK;
205
}