sos_core/
device.rs

1//! Types for device support.
2use crate::{Error, Result};
3use serde::{Deserialize, Serialize};
4use serde_json::Value;
5use std::{
6    collections::BTreeMap,
7    fmt,
8    hash::{Hash, Hasher},
9    sync::OnceLock,
10};
11use time::OffsetDateTime;
12
13/// Device meta data.
14///
15/// Applications can set this when they boot so that trusted devices
16/// will prefer this meta data.
17static DEVICE: OnceLock<DeviceMetaData> = OnceLock::new();
18
19/// Set the default device meta data.
20pub fn set_default_device_meta(meta: DeviceMetaData) {
21    DEVICE.get_or_init(|| meta);
22}
23
24/// Get the default device meta data.
25pub fn get_default_device_meta<'a>() -> Option<&'a DeviceMetaData> {
26    DEVICE.get()
27}
28
29/// Type of a device public key.
30#[derive(Debug, Copy, Clone, Serialize, Deserialize, Hash, Eq, PartialEq)]
31pub struct DevicePublicKey(
32    #[serde(with = "hex::serde")] [u8; DevicePublicKey::SIZE],
33);
34
35impl DevicePublicKey {
36    /// Device public key length.
37    pub const SIZE: usize = 32;
38}
39
40impl hex::FromHex for DevicePublicKey {
41    type Error = Error;
42    fn from_hex<T: AsRef<[u8]>>(value: T) -> Result<Self> {
43        let buf = hex::decode(value)?;
44        let buf: [u8; 32] = buf.as_slice().try_into()?;
45        Ok(Self(buf))
46    }
47}
48
49impl From<[u8; 32]> for DevicePublicKey {
50    fn from(value: [u8; 32]) -> Self {
51        Self(value)
52    }
53}
54
55impl From<&[u8; 32]> for DevicePublicKey {
56    fn from(value: &[u8; 32]) -> Self {
57        Self(*value)
58    }
59}
60
61impl TryFrom<&[u8]> for DevicePublicKey {
62    type Error = Error;
63
64    fn try_from(value: &[u8]) -> Result<Self> {
65        let value: [u8; 32] = value.try_into()?;
66        Ok(Self(value))
67    }
68}
69
70impl AsRef<[u8]> for DevicePublicKey {
71    fn as_ref(&self) -> &[u8] {
72        &self.0
73    }
74}
75
76impl fmt::Display for DevicePublicKey {
77    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
78        write!(f, "{}", hex::encode(self.0))
79    }
80}
81
82/// Additional information about the device such as the
83/// device name, manufacturer and model.
84#[derive(Default, Debug, Clone, Serialize, Deserialize, Eq, PartialEq)]
85pub struct DeviceMetaData {
86    // Note that order is very important here as this type
87    // is included in the device event log and if the order
88    // is non-deterministic the commit hashes will differ.
89    #[serde(flatten)]
90    info: BTreeMap<String, Value>,
91}
92
93impl fmt::Display for DeviceMetaData {
94    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
95        for (o, v) in &self.info {
96            if let Value::Object(map) = v {
97                for (k, v) in map {
98                    if let Value::String(s) = v {
99                        writeln!(f, "[{}] {}: {}", o, k, s)?;
100                    }
101                }
102            }
103        }
104        Ok(())
105    }
106}
107
108/// Device that has been trusted.
109#[derive(Debug, Clone, Serialize, Deserialize, Eq)]
110#[serde(rename_all = "camelCase")]
111pub struct TrustedDevice {
112    /// Public key of the device.
113    public_key: DevicePublicKey,
114    /// Extra device information.
115    extra_info: DeviceMetaData,
116    /// When this device was trusted.
117    created_date: OffsetDateTime,
118}
119
120impl PartialEq for TrustedDevice {
121    fn eq(&self, other: &Self) -> bool {
122        self.public_key == other.public_key
123    }
124}
125
126impl Hash for TrustedDevice {
127    fn hash<H: Hasher>(&self, state: &mut H) {
128        self.public_key.as_ref().hash(state);
129    }
130}
131
132impl TrustedDevice {
133    /// Create a new trusted device.
134    pub fn new(
135        public_key: DevicePublicKey,
136        extra_info: Option<DeviceMetaData>,
137        created_date: Option<OffsetDateTime>,
138    ) -> Self {
139        let extra_info = if let Some(extra_info) = extra_info {
140            extra_info
141        } else {
142            if let Some(device) = DEVICE.get() {
143                device.clone()
144            } else {
145                Default::default()
146            }
147        };
148
149        Self {
150            public_key,
151            extra_info,
152            created_date: created_date
153                .unwrap_or_else(|| OffsetDateTime::now_utc()),
154        }
155    }
156
157    /// Device public key.
158    pub fn public_key(&self) -> &DevicePublicKey {
159        &self.public_key
160    }
161
162    /// Public identifier derived from the public key (base58 encoded).
163    pub fn public_id(&self) -> Result<String> {
164        let mut encoded = String::new();
165        bs58::encode(&self.public_key).into(&mut encoded)?;
166        Ok(encoded)
167    }
168
169    /// Extra device information.
170    pub fn extra_info(&self) -> &DeviceMetaData {
171        &self.extra_info
172    }
173
174    /// Date and time this trusted device was created.
175    pub fn created_date(&self) -> &OffsetDateTime {
176        &self.created_date
177    }
178}
179
180impl From<DevicePublicKey> for TrustedDevice {
181    fn from(value: DevicePublicKey) -> Self {
182        Self {
183            extra_info: Default::default(),
184            public_key: value,
185            created_date: OffsetDateTime::now_utc(),
186        }
187    }
188}
189
190impl TryFrom<&TrustedDevice> for (DevicePublicKey, String) {
191    type Error = Error;
192    fn try_from(value: &TrustedDevice) -> Result<Self> {
193        Ok((
194            value.public_key.clone(),
195            serde_json::to_string(&value.extra_info)?,
196        ))
197    }
198}