Skip to main content

provenance_mark/
generator.rs

1use std::fmt::Formatter;
2
3#[cfg(feature = "envelope")]
4use bc_envelope::prelude::*;
5use bc_rand::RandomNumberGenerator;
6use dcbor::Date;
7#[cfg(not(feature = "envelope"))]
8use dcbor::prelude::*;
9use serde::{Deserialize, Serialize};
10
11use crate::{
12    Error, ProvenanceMark, ProvenanceMarkResolution, ProvenanceSeed, Result,
13    RngState,
14    crypto_utils::sha256,
15    util::{deserialize_base64, serialize_base64},
16    xoshiro256starstar::Xoshiro256StarStar,
17};
18
19#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
20pub struct ProvenanceMarkGenerator {
21    res: ProvenanceMarkResolution,
22    seed: ProvenanceSeed,
23    #[serde(rename = "chainID")]
24    #[serde(
25        serialize_with = "serialize_base64",
26        deserialize_with = "deserialize_base64"
27    )]
28    chain_id: Vec<u8>,
29    #[serde(rename = "nextSeq")]
30    next_seq: u32,
31    #[serde(rename = "rngState")]
32    rng_state: RngState,
33}
34
35impl ProvenanceMarkGenerator {
36    pub fn res(&self) -> &ProvenanceMarkResolution { &self.res }
37
38    pub fn seed(&self) -> &ProvenanceSeed { &self.seed }
39
40    pub fn chain_id(&self) -> &[u8] { &self.chain_id }
41
42    pub fn next_seq(&self) -> u32 { self.next_seq }
43
44    pub fn rng_state(&self) -> &RngState { &self.rng_state }
45}
46
47impl ProvenanceMarkGenerator {
48    pub fn new_with_seed(
49        res: ProvenanceMarkResolution,
50        seed: ProvenanceSeed,
51    ) -> Self {
52        // Definitely don't use the bare seed as the chain ID!
53        let digest1 = sha256(seed.to_bytes().as_ref());
54        let chain_id = digest1[..res.link_length()].to_vec();
55        let digest2 = sha256(digest1);
56        Self::new(res, seed.clone(), chain_id, 0, digest2.into()).unwrap()
57    }
58
59    pub fn new_with_passphrase(
60        res: ProvenanceMarkResolution,
61        passphrase: &str,
62    ) -> Self {
63        let seed = ProvenanceSeed::new_with_passphrase(passphrase);
64        Self::new_with_seed(res, seed)
65    }
66
67    pub fn new_using(
68        res: ProvenanceMarkResolution,
69        rng: &mut impl RandomNumberGenerator,
70    ) -> Self {
71        // Randomness for a new seed can come from any secure random number
72        // generator.
73        let seed = ProvenanceSeed::new_using(rng);
74        Self::new_with_seed(res, seed)
75    }
76
77    pub fn new_random(res: ProvenanceMarkResolution) -> Self {
78        let seed = ProvenanceSeed::new();
79        Self::new_with_seed(res, seed)
80    }
81
82    pub fn new(
83        res: ProvenanceMarkResolution,
84        seed: ProvenanceSeed,
85        chain_id: Vec<u8>,
86        next_seq: u32,
87        rng_state: RngState,
88    ) -> Result<Self> {
89        if chain_id.len() != res.link_length() {
90            return Err(Error::InvalidChainIdLength {
91                expected: res.link_length(),
92                actual: chain_id.len(),
93            });
94        }
95        Ok(Self { res, seed, chain_id, next_seq, rng_state })
96    }
97
98    pub fn next(
99        &mut self,
100        date: Date,
101        info: Option<impl CBOREncodable>,
102    ) -> ProvenanceMark {
103        let data: [u8; 32] = self.rng_state.clone().into();
104        let mut rng = Xoshiro256StarStar::from_data(&data);
105
106        let seq = self.next_seq;
107        self.next_seq += 1;
108
109        let key;
110        if seq == 0 {
111            key = self.chain_id.clone();
112        } else {
113            // The randomness generated by the PRNG should be portable across
114            // implementations.
115            key = rng.next_bytes(self.res.link_length());
116            self.rng_state = rng.to_data().into();
117        }
118
119        let mut next_rng = rng.clone();
120        let next_key = next_rng.next_bytes(self.res.link_length());
121
122        ProvenanceMark::new(
123            self.res,
124            key,
125            next_key,
126            self.chain_id.clone(),
127            seq,
128            date,
129            info,
130        )
131        .unwrap()
132    }
133}
134
135impl std::fmt::Display for ProvenanceMarkGenerator {
136    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
137        write!(
138            f,
139            "ProvenanceMarkGenerator(chainID: {}, res: {}, seed: {}, nextSeq: {}, rngState: {:?})",
140            hex::encode(&self.chain_id),
141            self.res,
142            self.seed.hex(),
143            self.next_seq,
144            self.rng_state
145        )
146    }
147}
148
149#[cfg(feature = "envelope")]
150impl From<ProvenanceMarkGenerator> for Envelope {
151    fn from(generator: ProvenanceMarkGenerator) -> Self {
152        Envelope::new(CBOR::to_byte_string(generator.chain_id()))
153            .add_type("provenance-generator")
154            .add_assertion("res", generator.res().to_cbor())
155            .add_assertion("seed", generator.seed().to_cbor())
156            .add_assertion("next-seq", generator.next_seq())
157            .add_assertion("rng-state", generator.rng_state().to_cbor())
158    }
159}
160
161#[cfg(feature = "envelope")]
162impl TryFrom<Envelope> for ProvenanceMarkGenerator {
163    type Error = Error;
164
165    fn try_from(envelope: Envelope) -> Result<Self> {
166        envelope.check_type("provenance-generator")?;
167        let chain_id: Vec<u8> = envelope.subject().try_byte_string()?;
168        const EXPECTED_KEY_COUNT: usize = 5;
169        let assertion_count = envelope.assertions().len();
170        if assertion_count != EXPECTED_KEY_COUNT {
171            return Err(Error::ExtraKeys(EXPECTED_KEY_COUNT, assertion_count));
172        }
173        let res: ProvenanceMarkResolution = envelope
174            .object_for_predicate("res")?
175            .try_leaf()?
176            .try_into()?;
177        let seed: ProvenanceSeed = envelope
178            .object_for_predicate("seed")?
179            .try_leaf()?
180            .try_into()?;
181        let next_seq: u32 = envelope
182            .object_for_predicate("next-seq")?
183            .try_leaf()?
184            .try_into()?;
185        let rng_state: RngState = envelope
186            .object_for_predicate("rng-state")?
187            .try_leaf()?
188            .try_into()?;
189
190        ProvenanceMarkGenerator::new(res, seed, chain_id, next_seq, rng_state)
191    }
192}