use std::fmt::Formatter;
#[cfg(feature = "envelope")]
use bc_envelope::prelude::*;
use bc_rand::RandomNumberGenerator;
use dcbor::Date;
#[cfg(not(feature = "envelope"))]
use dcbor::prelude::*;
use serde::{Deserialize, Serialize};
use crate::{
Error, ProvenanceMark, ProvenanceMarkResolution, ProvenanceSeed, Result,
RngState,
crypto_utils::sha256,
util::{deserialize_base64, serialize_base64},
xoshiro256starstar::Xoshiro256StarStar,
};
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
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 res(&self) -> &ProvenanceMarkResolution { &self.res }
pub fn seed(&self) -> &ProvenanceSeed { &self.seed }
pub fn chain_id(&self) -> &[u8] { &self.chain_id }
pub fn next_seq(&self) -> u32 { self.next_seq }
pub fn rng_state(&self) -> &RngState { &self.rng_state }
}
impl ProvenanceMarkGenerator {
pub fn new_with_seed(
res: ProvenanceMarkResolution,
seed: ProvenanceSeed,
) -> Self {
let digest1 = sha256(seed.to_bytes().as_ref());
let chain_id = digest1[..res.link_length()].to_vec();
let digest2 = sha256(digest1);
Self::new(res, seed.clone(), chain_id, 0, digest2.into()).unwrap()
}
pub fn new_with_passphrase(
res: ProvenanceMarkResolution,
passphrase: &str,
) -> Self {
let seed = ProvenanceSeed::new_with_passphrase(passphrase);
Self::new_with_seed(res, seed)
}
pub fn new_using(
res: ProvenanceMarkResolution,
rng: &mut impl RandomNumberGenerator,
) -> Self {
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,
) -> Result<Self> {
if chain_id.len() != res.link_length() {
return Err(Error::InvalidChainIdLength {
expected: res.link_length(),
actual: chain_id.len(),
});
}
Ok(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 {
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
)
}
}
#[cfg(feature = "envelope")]
impl From<ProvenanceMarkGenerator> for Envelope {
fn from(generator: ProvenanceMarkGenerator) -> Self {
Envelope::new(CBOR::to_byte_string(generator.chain_id()))
.add_type("provenance-generator")
.add_assertion("res", generator.res().to_cbor())
.add_assertion("seed", generator.seed().to_cbor())
.add_assertion("next-seq", generator.next_seq())
.add_assertion("rng-state", generator.rng_state().to_cbor())
}
}
#[cfg(feature = "envelope")]
impl TryFrom<Envelope> for ProvenanceMarkGenerator {
type Error = Error;
fn try_from(envelope: Envelope) -> Result<Self> {
envelope.check_type("provenance-generator")?;
let chain_id: Vec<u8> = envelope.subject().try_byte_string()?;
const EXPECTED_KEY_COUNT: usize = 5;
let assertion_count = envelope.assertions().len();
if assertion_count != EXPECTED_KEY_COUNT {
return Err(Error::ExtraKeys(EXPECTED_KEY_COUNT, assertion_count));
}
let res: ProvenanceMarkResolution = envelope
.object_for_predicate("res")?
.try_leaf()?
.try_into()?;
let seed: ProvenanceSeed = envelope
.object_for_predicate("seed")?
.try_leaf()?
.try_into()?;
let next_seq: u32 = envelope
.object_for_predicate("next-seq")?
.try_leaf()?
.try_into()?;
let rng_state: RngState = envelope
.object_for_predicate("rng-state")?
.try_leaf()?
.try_into()?;
ProvenanceMarkGenerator::new(res, seed, chain_id, next_seq, rng_state)
}
}