libcoap_rs/crypto/psk/
key.rs

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
11use std::{borrow::Cow, marker::PhantomData, ptr::NonNull};
12
13use 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)]
17pub 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
29impl<'a> PskKey<'a> {
30    /// Creates a new key object with the given `identity` and the actual key bytes given in `data`.
31    pub fn new<T: Into<Vec<u8>>, U: Into<Vec<u8>>>(identity: Option<T>, data: U) -> PskKey<'a> {
32        PskKey {
33            identity: identity.map(Into::into).map(|v| v.into_boxed_slice()),
34            data: data.into().into_boxed_slice(),
35            _lifetime_marker: Default::default(),
36        }
37    }
38}
39
40impl 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    pub(crate) fn into_raw_spsk_info(self) -> coap_dtls_spsk_info_t {
63        let (hint, key) = self.into_bin_consts();
64        coap_dtls_spsk_info_t { hint, key }
65    }
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    pub(crate) unsafe fn from_raw_spsk_info(spsk_info: coap_dtls_spsk_info_t) -> Self {
77        // SAFETY: Caller contract requires the provided spsk_info to be created by a previous call
78        // to into_raw_spsk_info.
79        Self::from_bin_consts(&spsk_info.hint, &spsk_info.key)
80    }
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    pub(crate) fn into_raw_cpsk_info(self) -> coap_dtls_cpsk_info_t {
94        let (identity, key) = self.into_bin_consts();
95        coap_dtls_cpsk_info_t { identity, key }
96    }
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    pub(crate) unsafe fn from_raw_cpsk_info(cpsk_info: coap_dtls_cpsk_info_t) -> Self {
108        // SAFETY: Caller contract requires the provided cpsk_info to be created by a previous call
109        // to into_raw_cpsk_info.
110        Self::from_bin_consts(&cpsk_info.identity, &cpsk_info.key)
111    }
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    fn into_bin_consts(self) -> (coap_bin_const_t, coap_bin_const_t) {
119        let identity = self
120            .identity
121            .map(|v| coap_bin_const_t {
122                length: v.len(),
123                s: Box::into_raw(v) as *const u8,
124            })
125            .unwrap_or(coap_bin_const_t {
126                length: 0,
127                s: std::ptr::null(),
128            });
129        let key = coap_bin_const_t {
130            length: self.data.len(),
131            s: Box::into_raw(self.data) as *const u8,
132        };
133        (identity, key)
134    }
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    unsafe fn from_bin_consts(identity: &coap_bin_const_t, key: &coap_bin_const_t) -> Self {
144        // SAFETY: Caller contract requires the provided identity and key to be created by a
145        // previous call to into_bin_consts, which means that the pointer in identity.s refers to a
146        // pointer created by a previous call to Box::into_raw(), identity.length refers
147        // to the correct length of the slice, and the pointer can actually be treated as a mutable
148        // pointer.
149        let identity = NonNull::new(identity.s as *mut u8)
150            .map(|mut v| unsafe { Box::from_raw(std::slice::from_raw_parts_mut(v.as_mut(), identity.length)) });
151
152        // SAFETY same as above.
153        let data = unsafe { Box::from_raw(std::slice::from_raw_parts_mut(key.s as *mut u8, key.length)) };
154        Self {
155            identity,
156            data,
157            _lifetime_marker: Default::default(),
158        }
159    }
160}
161
162impl 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
172impl 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
182impl<'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
192impl<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
202impl<'a> AsRef<PskKey<'a>> for PskKey<'a> {
203    fn as_ref(&self) -> &PskKey<'a> {
204        self
205    }
206}