libcoap_rs/crypto/psk/
key.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
// SPDX-License-Identifier: BSD-2-Clause
/*
 * Copyright © The libcoap-rs Contributors, all rights reserved.
 * This file is part of the libcoap-rs project, see the README file for
 * general information on this project and the NOTICE.md and LICENSE files
 * for information regarding copyright ownership and terms of use.
 *
 * crypto/psk/key.rs - Interfaces and types for PSK keys in libcoap-rs.
 */

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

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

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

impl<'a> PskKey<'a> {
    /// Creates a new key object with the given `identity` and the actual key bytes given in `data`.
    pub fn new<T: Into<Vec<u8>>, U: Into<Vec<u8>>>(identity: Option<T>, data: U) -> PskKey<'a> {
        PskKey {
            identity: identity.map(Into::into).map(|v| v.into_boxed_slice()),
            data: data.into().into_boxed_slice(),
            _lifetime_marker: Default::default(),
        }
    }
}

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

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

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

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

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

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

    /// Consumes this key object to create two [`coap_bin_const_t`] instances referring to the
    /// `identity` and `data` fields.
    ///
    /// The pointers given in [`coap_bin_const_t`] have been created by a call to [`Box::into_raw`]
    /// with the `length` field set to the length of the given field.
    fn into_bin_consts(self) -> (coap_bin_const_t, coap_bin_const_t) {
        let identity = self
            .identity
            .map(|v| coap_bin_const_t {
                length: v.len(),
                s: Box::into_raw(v) as *const u8,
            })
            .unwrap_or(coap_bin_const_t {
                length: 0,
                s: std::ptr::null(),
            });
        let key = coap_bin_const_t {
            length: self.data.len(),
            s: Box::into_raw(self.data) as *const u8,
        };
        (identity, key)
    }

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

        // SAFETY same as above.
        let data = unsafe { Box::from_raw(std::slice::from_raw_parts_mut(key.s as *mut u8, key.length)) };
        Self {
            identity,
            data,
            _lifetime_marker: Default::default(),
        }
    }
}

impl From<Box<[u8]>> for PskKey<'static> {
    fn from(value: Box<[u8]>) -> Self {
        PskKey {
            identity: None,
            data: value,
            _lifetime_marker: Default::default(),
        }
    }
}

impl From<&[u8]> for PskKey<'static> {
    fn from(value: &[u8]) -> Self {
        PskKey {
            identity: None,
            data: value.into(),
            _lifetime_marker: Default::default(),
        }
    }
}

impl<'a> From<Cow<'a, [u8]>> for PskKey<'static> {
    fn from(value: Cow<'a, [u8]>) -> Self {
        PskKey {
            identity: None,
            data: value.into(),
            _lifetime_marker: Default::default(),
        }
    }
}

impl<T: Into<Box<[u8]>>, U: Into<Box<[u8]>>> From<(T, U)> for PskKey<'static> {
    fn from(value: (T, U)) -> Self {
        PskKey {
            identity: Some(value.0.into()),
            data: value.1.into(),
            _lifetime_marker: Default::default(),
        }
    }
}

impl<'a> AsRef<PskKey<'a>> for PskKey<'a> {
    fn as_ref(&self) -> &PskKey<'a> {
        self
    }
}