updatehub_cloud_sdk/
api.rs1use serde::Serialize;
6use std::{collections::BTreeMap, fs, path::Path};
7
8#[derive(Debug)]
9pub enum ProbeResponse {
10 NoUpdate,
11 Update(UpdatePackage, Option<Signature>),
12 ExtraPoll(i64),
13}
14
15#[derive(Clone, Debug, PartialEq, Eq)]
16pub struct UpdatePackage {
17 pub inner: pkg_schema::UpdatePackage,
18 pub raw: Vec<u8>,
19}
20
21#[derive(Clone, Debug, PartialEq, Eq)]
22pub struct Signature(Vec<u8>);
23
24#[derive(Serialize)]
25#[serde(rename_all = "kebab-case")]
26pub struct FirmwareMetadata<'a> {
27 pub product_uid: &'a str,
28 pub version: &'a str,
29 pub hardware: &'a str,
30 pub device_identity: MetadataValue<'a>,
31 pub device_attributes: MetadataValue<'a>,
32}
33
34pub struct MetadataValue<'a>(pub &'a BTreeMap<String, Vec<String>>);
35
36impl<'a> serde::ser::Serialize for MetadataValue<'a> {
37 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
38 where
39 S: serde::ser::Serializer,
40 {
41 use serde::ser::SerializeMap;
42
43 let mut map = serializer.serialize_map(Some(self.0.len()))?;
44 for (k, v) in self.0 {
45 if v.len() == 1 {
46 map.serialize_entry(k, &v[0])?;
47 } else {
48 map.serialize_entry(k, v)?;
49 }
50 }
51 map.end()
52 }
53}
54
55impl UpdatePackage {
56 pub fn parse(content: &[u8]) -> crate::Result<Self> {
57 let update_package = serde_json::from_slice(content)?;
58 Ok(UpdatePackage { inner: update_package, raw: content.to_vec() })
59 }
60
61 pub fn package_uid(&self) -> String {
62 openssl::sha::sha256(&self.raw).iter().map(|c| format!("{:02x}", c)).collect()
63 }
64
65 pub fn version(&self) -> &str {
66 &self.inner.version
67 }
68}
69
70impl Signature {
71 pub fn from_base64_str(bytes: &str) -> crate::Result<Self> {
72 Ok(Signature(openssl::base64::decode_block(bytes)?.to_vec()))
73 }
74
75 pub fn validate(&self, key: &Path, package: &UpdatePackage) -> crate::Result<()> {
76 use openssl::{hash::MessageDigest, pkey::PKey, rsa::Rsa, sign::Verifier};
77 let key = PKey::from_rsa(Rsa::public_key_from_pem(&fs::read(key)?)?)?;
78 if Verifier::new(MessageDigest::sha256(), &key)?.verify_oneshot(&self.0, &package.raw)? {
79 return Ok(());
80 }
81 Err(crate::Error::InvalidSignature)
82 }
83}