1
// SPDX-License-Identifier: BSD-2-Clause
2
/*
3
 * Copyright © The libcoap-rs Contributors, all rights reserved.
4
 * This file is part of the libcoap-rs project, see the README file for
5
 * general information on this project and the NOTICE.md and LICENSE files
6
 * for information regarding copyright ownership and terms of use.
7
 *
8
 * crypto/psk/key.rs - Interfaces and types for PSK keys in libcoap-rs.
9
 */
10

            
11
use std::{borrow::Cow, marker::PhantomData, ptr::NonNull};
12

            
13
use libcoap_sys::{coap_bin_const_t, coap_dtls_cpsk_info_t, coap_dtls_spsk_info_t};
14

            
15
/// A pre-shared DTLS key.
16
#[derive(Debug, Clone)]
17
pub struct PskKey<'a> {
18
    /// Identity of this key (or None if no identity is known).
19
    identity: Option<Box<[u8]>>,
20
    /// Actual key data (the key bytes).
21
    data: Box<[u8]>,
22
    // This lifetime is not strictly necessary for now. This is just future-proofing for later
23
    // changes, which might allow PskKey instances with limited lifetimes (e.g. using borrowed byte
24
    // slices).
25
    // In practice (at least for now), all PskKey instances have a 'static lifetime.
26
    _lifetime_marker: PhantomData<&'a ()>,
27
}
28

            
29
impl<'a> PskKey<'a> {
30
    /// Creates a new key object with the given `identity` and the actual key bytes given in `data`.
31
4
    pub fn new<T: Into<Vec<u8>>, U: Into<Vec<u8>>>(identity: Option<T>, data: U) -> PskKey<'a> {
32
4
        PskKey {
33
4
            identity: identity.map(Into::into).map(|v| v.into_boxed_slice()),
34
4
            data: data.into().into_boxed_slice(),
35
4
            _lifetime_marker: Default::default(),
36
4
        }
37
4
    }
38
}
39

            
40
impl PskKey<'_> {
41
    /// Returns the key's identity or `None` if no key identity was set.
42
    pub fn identity(&self) -> Option<&[u8]> {
43
        self.identity.as_ref().map(|v| v.as_ref())
44
    }
45

            
46
    /// Returns the key data bytes as an immutable slice.
47
    pub fn data(&self) -> &[u8] {
48
        self.data.as_ref()
49
    }
50

            
51
    /// Creates a [`coap_dtls_spsk_info_t`] instance from this [`PskKey`].
52
    ///
53
    /// This call converts the identity and data field of this PSK into raw pointers and creates a
54
    /// [`coap_dtls_spsk_info_t`] structure that allows libcoap to use those values.
55
    ///
56
    /// After this call, the caller is responsible for managing the memory allocated for the
57
    /// identity and key byte strings referred to be the created struct instance, i.e., simply
58
    /// dropping the created [`coap_dtls_spsk_info_t`] will cause a memory leak.
59
    /// The easiest way to clean up the memory is by calling [`from_raw_spsk_info`](Self::from_raw_spsk_info)
60
    /// to reverse the conversion done by this method and then dropping the restored [`PskKey`]
61
    /// instance.
62
35
    pub(crate) fn into_raw_spsk_info(self) -> coap_dtls_spsk_info_t {
63
35
        let (hint, key) = self.into_bin_consts();
64
35
        coap_dtls_spsk_info_t { hint, key }
65
35
    }
66

            
67
    /// Restores a `DtlsPsk` instance from a [`coap_dtls_spsk_info_t`] structure.
68
    ///
69
    /// # Safety
70
    ///
71
    /// The provided object must point to a valid instance of [`coap_dtls_spsk_info_t`] that *must*
72
    /// have been created by a previous call to [`into_raw_spsk_info`](Self::into_raw_spsk_info).
73
    ///
74
    /// The byte strings the provided `spsk_info` points to *must* not be in use anywhere else (as
75
    /// this might violate the aliasing rules), i.e. libcoap must no longer use these byte strings.
76
35
    pub(crate) unsafe fn from_raw_spsk_info(spsk_info: coap_dtls_spsk_info_t) -> Self {
77
35
        // SAFETY: Caller contract requires the provided spsk_info to be created by a previous call
78
35
        // to into_raw_spsk_info.
79
35
        Self::from_bin_consts(&spsk_info.hint, &spsk_info.key)
80
35
    }
81

            
82
    /// Creates a [`coap_dtls_cpsk_info_t`] instance from this [`PskKey`].
83
    ///
84
    /// This call converts the identity and data field of this PSK into raw pointers and creates a
85
    /// [`coap_dtls_cpsk_info_t`] structure that allows libcoap to use those values.
86
    ///
87
    /// After this call, the caller is responsible for managing the memory allocated for the
88
    /// identity and key byte strings referred to be the created struct instance, i.e., simply
89
    /// dropping the created [`coap_dtls_cpsk_info_t`] will cause a memory leak.
90
    /// The easiest way to clean up the memory is by calling [`from_raw_cpsk_info`](Self::from_raw_cpsk_info)
91
    /// to reverse the conversion done by this method and then dropping the restored [`PskKey`]
92
    /// instance.
93
35
    pub(crate) fn into_raw_cpsk_info(self) -> coap_dtls_cpsk_info_t {
94
35
        let (identity, key) = self.into_bin_consts();
95
35
        coap_dtls_cpsk_info_t { identity, key }
96
35
    }
97

            
98
    /// Restores a DtlsPsk instance from a [`coap_dtls_cpsk_info_t`] structure.
99
    ///
100
    /// # Safety
101
    ///
102
    /// The provided object must point to a valid instance of [`coap_dtls_cpsk_info_t`] that *must*
103
    /// have been created by a previous call to [`into_raw_cpsk_info`](Self::into_raw_cpsk_info).
104
    ///
105
    /// The byte strings the provided `cpsk_info` points to *must* not be in use anywhere else (as
106
    /// this might violate the aliasing rules), i.e., libcoap must no longer use these byte strings.
107
35
    pub(crate) unsafe fn from_raw_cpsk_info(cpsk_info: coap_dtls_cpsk_info_t) -> Self {
108
35
        // SAFETY: Caller contract requires the provided cpsk_info to be created by a previous call
109
35
        // to into_raw_cpsk_info.
110
35
        Self::from_bin_consts(&cpsk_info.identity, &cpsk_info.key)
111
35
    }
112

            
113
    /// Consumes this key object to create two [`coap_bin_const_t`] instances referring to the
114
    /// `identity` and `data` fields.
115
    ///
116
    /// The pointers given in [`coap_bin_const_t`] have been created by a call to [`Box::into_raw`]
117
    /// with the `length` field set to the length of the given field.
118
70
    fn into_bin_consts(self) -> (coap_bin_const_t, coap_bin_const_t) {
119
70
        let identity = self
120
70
            .identity
121
70
            .map(|v| coap_bin_const_t {
122
70
                length: v.len(),
123
70
                s: Box::into_raw(v) as *const u8,
124
70
            })
125
70
            .unwrap_or(coap_bin_const_t {
126
70
                length: 0,
127
70
                s: std::ptr::null(),
128
70
            });
129
70
        let key = coap_bin_const_t {
130
70
            length: self.data.len(),
131
70
            s: Box::into_raw(self.data) as *const u8,
132
70
        };
133
70
        (identity, key)
134
70
    }
135

            
136
    /// Converts the given pair of [`coap_bin_const_t`]s back into a [`PskKey`] instance with the
137
    /// given `identity` and `key`
138
    ///
139
    /// # Safety
140
    /// The provided `identity` and `key` must have been created by a previous call to
141
    /// [`PskKey::into_bin_consts`], the `length` field and pointers of both constants must not have
142
    /// been modified.
143
70
    unsafe fn from_bin_consts(identity: &coap_bin_const_t, key: &coap_bin_const_t) -> Self {
144
70
        // SAFETY: Caller contract requires the provided identity and key to be created by a
145
70
        // previous call to into_bin_consts, which means that the pointer in identity.s refers to a
146
70
        // pointer created by a previous call to Box::into_raw(), identity.length refers
147
70
        // to the correct length of the slice, and the pointer can actually be treated as a mutable
148
70
        // pointer.
149
70
        let identity = NonNull::new(identity.s as *mut u8)
150
70
            .map(|mut v| unsafe { Box::from_raw(std::slice::from_raw_parts_mut(v.as_mut(), identity.length)) });
151
70

            
152
70
        // SAFETY same as above.
153
70
        let data = unsafe { Box::from_raw(std::slice::from_raw_parts_mut(key.s as *mut u8, key.length)) };
154
70
        Self {
155
70
            identity,
156
70
            data,
157
70
            _lifetime_marker: Default::default(),
158
70
        }
159
70
    }
160
}
161

            
162
impl From<Box<[u8]>> for PskKey<'static> {
163
    fn from(value: Box<[u8]>) -> Self {
164
        PskKey {
165
            identity: None,
166
            data: value,
167
            _lifetime_marker: Default::default(),
168
        }
169
    }
170
}
171

            
172
impl From<&[u8]> for PskKey<'static> {
173
    fn from(value: &[u8]) -> Self {
174
        PskKey {
175
            identity: None,
176
            data: value.into(),
177
            _lifetime_marker: Default::default(),
178
        }
179
    }
180
}
181

            
182
impl<'a> From<Cow<'a, [u8]>> for PskKey<'static> {
183
    fn from(value: Cow<'a, [u8]>) -> Self {
184
        PskKey {
185
            identity: None,
186
            data: value.into(),
187
            _lifetime_marker: Default::default(),
188
        }
189
    }
190
}
191

            
192
impl<T: Into<Box<[u8]>>, U: Into<Box<[u8]>>> From<(T, U)> for PskKey<'static> {
193
    fn from(value: (T, U)) -> Self {
194
        PskKey {
195
            identity: Some(value.0.into()),
196
            data: value.1.into(),
197
            _lifetime_marker: Default::default(),
198
        }
199
    }
200
}
201

            
202
impl<'a> AsRef<PskKey<'a>> for PskKey<'a> {
203
    fn as_ref(&self) -> &PskKey<'a> {
204
        self
205
    }
206
}