updatehub_cloud_sdk/
api.rs

1// Copyright (C) 2020 O.S. Systems Sofware LTDA
2//
3// SPDX-License-Identifier: Apache-2.0
4
5use 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}