sos_core/
device.rs

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