libcoap_rs/
oscore.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 * oscore.rs - Wrapper for libcoap OSCORE functionality.
9 */
10
11use core::{ffi::c_void, ptr};
12
13use libcoap_sys::{
14    coap_delete_oscore_conf, coap_delete_str_const, coap_new_oscore_conf, coap_new_str_const, coap_oscore_conf_t,
15};
16
17use crate::error::OscoreConfigError;
18
19/// Represents an oscore config object which stores the underlying
20/// coap_oscore_conf_t C struct.
21pub struct OscoreConf {
22    raw_conf: *mut coap_oscore_conf_t,
23    initial_recipient: Option<String>,
24}
25
26impl OscoreConf {
27    /// Creates a new OscoreConf.
28    ///
29    /// # Errors
30    /// Will return a [OscoreConfigError] if creating the oscore config fails (most likely due to
31    /// invalid oscore config bytes provided).
32    pub fn new(
33        seq_initial: u64,
34        oscore_conf_bytes: &[u8],
35        save_seq_num_func: extern "C" fn(seq_num: u64, _param: *mut c_void) -> i32,
36    ) -> Result<Self, OscoreConfigError> {
37        // Creates the raw_struct containing the config provided by the caller.
38        // SAFETY: Provided pointer and length point to a valid byte string usable by
39        // coap_new_str_const().
40        let conf = unsafe { coap_new_str_const(oscore_conf_bytes.as_ptr(), oscore_conf_bytes.len()) };
41        if conf.is_null() {
42            return Err(OscoreConfigError::Unknown);
43        }
44
45        // SAFETY:
46        // The parts of the byte string referenced by conf are defensively copied if used
47        // by the newly created oscore_conf.
48        // Conf was just checked for invalidity, whether or not it containes all required fields.
49        // - save_seq_num_func is specifically designed to work as a callback for this
50        //   function.
51        // - save_seq_num_func_param may be a null pointer (save_seq_num_func does
52        //   not use it).
53        let oscore_conf = unsafe { coap_new_oscore_conf(*conf, Some(save_seq_num_func), ptr::null_mut(), seq_initial) };
54        unsafe {
55            coap_delete_str_const(conf);
56        }
57        if oscore_conf.is_null() {
58            return Err(OscoreConfigError::Unknown);
59        }
60
61        // Save the initial recipient_id (if present). This needs to be added to the context when
62        // calling oscore_server to prevent a double free when trying to add an identical
63        // recipient_id later.
64        let mut initial_recipient: Option<String> = None;
65        let oscore_conf_str = core::str::from_utf8(oscore_conf_bytes).expect("could not parse config bytes to str");
66        for line in oscore_conf_str.lines() {
67            if line.starts_with("recipient_id") {
68                let parts: Vec<&str> = line.split(",").collect();
69                initial_recipient = Some(parts[2].trim().trim_matches('"').to_string());
70                break;
71            }
72        }
73
74        // Return the valid OscoreConf.
75        Ok(Self {
76            raw_conf: oscore_conf,
77            initial_recipient,
78        })
79    }
80
81    /// Cosumes the OscoreConf and returns the contained raw_conf libcoap struct as well as an
82    /// optional initial recipient if set.
83    /// The caller is responsible for managing the memory of the raw_conf returned by this function,
84    /// e.g., by using coap_delete_oscore_conf() to free the returned memory after use.
85    pub(crate) fn into_raw_conf(mut self) -> (*mut coap_oscore_conf_t, Option<String>) {
86        // Replace pointer in structure with a null pointer, so the destructor knows that we know longer own
87        // the raw structure and must therefore not free it.
88        let raw_conf = std::mem::replace(&mut self.raw_conf, ptr::null_mut());
89        (raw_conf, self.initial_recipient.clone())
90    }
91}
92
93impl Drop for OscoreConf {
94    /// Drop the OscoreConf's raw_conf.
95    fn drop(&mut self) {
96        if !self.raw_conf.is_null() {
97            // SAFETY: If the CoapConf was consumed by calling the unsafe function into_raw_conf() the
98            // pointer will be null, but we just checked that this is not the case.
99            // Therefore, we are still the owner of the raw config and can therefore free it.
100            unsafe {
101                coap_delete_oscore_conf(self.raw_conf);
102            }
103        }
104    }
105}