provenance_mark/
generator.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
use std::fmt::Formatter;

use bc_crypto::sha256;
use bc_rand::RandomNumberGenerator;
use dcbor::{ CBOREncodable, Date };
use serde::{ Serialize, Deserialize };
use crate::util::{ serialize_base64, deserialize_base64 };

use crate::{ ProvenanceSeed, RngState };
use crate::{
    crypto_utils::extend_key,
    xoshiro256starstar::Xoshiro256StarStar,
    ProvenanceMark,
    ProvenanceMarkResolution,
};

#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct ProvenanceMarkGenerator {
    res: ProvenanceMarkResolution,
    seed: ProvenanceSeed,
    #[serde(rename = "chainID")]
    #[serde(serialize_with = "serialize_base64", deserialize_with = "deserialize_base64")]
    chain_id: Vec<u8>,
    #[serde(rename = "nextSeq")]
    next_seq: u32,
    #[serde(rename = "rngState")]
    rng_state: RngState,
}

impl ProvenanceMarkGenerator {
    pub fn new_with_seed(res: ProvenanceMarkResolution, seed: ProvenanceSeed) -> Self {
        // Definitely don't use the bare seed as the chain ID!
        let seed_digest = sha256(seed.to_bytes().as_ref());
        let chain_id = seed_digest[..res.link_length()].to_vec();
        Self::new(res, seed.clone(), chain_id, 0, seed_digest.into())
    }

    pub fn new_with_passphrase(res: ProvenanceMarkResolution, passphrase: &str) -> Self {
        let seed_data = extend_key(passphrase.as_bytes());
        let seed = ProvenanceSeed::from_bytes(seed_data);
        Self::new_with_seed(res, seed)
    }

    pub fn new_using(res: ProvenanceMarkResolution, rng: &mut impl RandomNumberGenerator) -> Self {
        // Randomness for a new seed can come from any secure random number generator.
        let seed = ProvenanceSeed::new_using(rng);
        Self::new_with_seed(res, seed)
    }

    pub fn new_random(res: ProvenanceMarkResolution) -> Self {
        let seed = ProvenanceSeed::new();
        Self::new_with_seed(res, seed)
    }

    pub fn new(
        res: ProvenanceMarkResolution,
        seed: ProvenanceSeed,
        chain_id: Vec<u8>,
        next_seq: u32,
        rng_state: RngState
    ) -> Self {
        assert!(chain_id.len() == res.link_length());
        Self { res, seed, chain_id, next_seq, rng_state }
    }

    pub fn next(&mut self, date: Date, info: Option<impl CBOREncodable>) -> ProvenanceMark {
        let data: [u8; 32] = self.rng_state.clone().into();
        let mut rng = Xoshiro256StarStar::from_data(&data);

        let seq = self.next_seq;
        self.next_seq += 1;

        let key;
        if seq == 0 {
            key = self.chain_id.clone();
        } else {
            // The randomness generated by the PRNG should be portable across implementations.
            key = rng.next_bytes(self.res.link_length());
            self.rng_state = rng.to_data().into();
        }

        let mut next_rng = rng.clone();
        let next_key = next_rng.next_bytes(self.res.link_length());

        ProvenanceMark::new(
            self.res,
            key,
            next_key,
            self.chain_id.clone(),
            seq,
            date,
            info
        ).unwrap()
    }
}

impl std::fmt::Display for ProvenanceMarkGenerator {
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
        write!(
            f,
            "ProvenanceMarkGenerator(chainID: {}, res: {}, seed: {}, nextSeq: {}, rngState: {:?})",
            hex::encode(&self.chain_id),
            self.res,
            self.seed.hex(),
            self.next_seq,
            self.rng_state
        )
    }
}