foundation_urtypes/
supply_chain_validation.rs

1// SPDX-FileCopyrightText: © 2023 Foundation Devices, Inc. <hello@foundationdevices.com>
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4//! # Supply Chain Validation.
5//!
6//! ## CDDL for Supply Chain Validation.
7//!
8//! ```cddl
9//! scv-challenge = {
10//!     scv-challenge-id: text .size 64,         ; hex encoded string.
11//!     scv-challenge-signature: text .size 128, ; hex encoded string.
12//! }
13//!
14//! scv-solution = {
15//!     scv-solution-word1: text,
16//!     scv-solution-word2: text,
17//!     scv-solution-word3: text,
18//!     scv-solution-word4: text,
19//! }
20//!
21//! scv-challenge-id = 1
22//! scv-challenge-signature = 2
23//!
24//! scv-solution-word1 = 1
25//! scv-solution-word2 = 2
26//! scv-solution-word3 = 3
27//! scv-solution-word4 = 4
28//! ```
29
30use core::str;
31
32use minicbor::data::{Tag, Type};
33use minicbor::decode::Error;
34use minicbor::encode::Write;
35use minicbor::{Decode, Decoder, Encode, Encoder};
36
37/// Supply Chain Validation challenge.
38#[derive(Debug, Clone, Eq, PartialEq, Hash)]
39pub struct Challenge {
40    /// The ID of the challenge.
41    pub id: [u8; 32],
42    /// The signature of the challenge.
43    pub signature: [u8; 64],
44}
45
46impl Challenge {
47    /// Tag for embedding [`Challenge`] in other types.
48    pub const TAG: Tag = Tag::new(710);
49}
50
51impl<'b, C> Decode<'b, C> for Challenge {
52    fn decode(d: &mut Decoder<'b>, _ctx: &mut C) -> Result<Self, Error> {
53        let mut id = None;
54        let mut signature = None;
55
56        macro_rules! decode_inner {
57            () => {
58                match d.u32()? {
59                    1 => {
60                        let mut buf = [0; 32];
61                        hex::decode_to_slice(d.str()?, &mut buf)
62                            .map_err(|_| Error::message("invalid hex string (id)"))?;
63                        id = Some(buf);
64                    }
65                    2 => {
66                        let mut buf = [0; 64];
67                        hex::decode_to_slice(d.str()?, &mut buf)
68                            .map_err(|_| Error::message("invalid hex string (signature)"))?;
69                        signature = Some(buf);
70                    }
71                    3 => (),
72                    _ => return Err(Error::message("unknown map entry")),
73                }
74            };
75        }
76
77        if let Some(len) = d.map()? {
78            for _ in 0..len {
79                decode_inner!();
80            }
81        } else {
82            while d.datatype()? != Type::Break {
83                decode_inner!();
84            }
85        }
86
87        Ok(Self {
88            id: id.ok_or_else(|| Error::message("id is missing"))?,
89            signature: signature.ok_or_else(|| Error::message("signature is missing"))?,
90        })
91    }
92}
93
94impl<C> Encode<C> for Challenge {
95    fn encode<W: Write>(
96        &self,
97        e: &mut Encoder<W>,
98        _ctx: &mut C,
99    ) -> Result<(), minicbor::encode::Error<W::Error>> {
100        let mut id = [0; 64];
101        let mut signature = [0; 128];
102
103        // unreachable errors.
104        hex::encode_to_slice(self.id, &mut id).unwrap();
105        hex::encode_to_slice(self.signature, &mut signature).unwrap();
106        let id = str::from_utf8(&id).unwrap();
107        let signature = str::from_utf8(&signature).unwrap();
108
109        e.map(2)?;
110        e.u8(1)?.str(id)?;
111        e.u8(2)?.str(signature)?;
112
113        Ok(())
114    }
115}
116
117/// Supply Chain Validation solution.
118#[derive(Debug, Decode, Clone, Encode, Eq, PartialEq, Hash)]
119#[cbor(map)]
120pub struct Solution<'a> {
121    /// Word 1.
122    #[cbor(b(1))]
123    pub word1: &'a str,
124    /// Word 2.
125    #[cbor(b(2))]
126    pub word2: &'a str,
127    /// Word 3.
128    #[cbor(b(3))]
129    pub word3: &'a str,
130    /// Word 4.
131    #[cbor(b(4))]
132    pub word4: &'a str,
133}
134
135impl<'a> Solution<'a> {
136    /// Tag for embedding [`Solution`] in other types.
137    pub const TAG: Tag = Tag::new(711);
138}