use bc_rand::{RandomNumberGenerator, rng_random_data};
use bc_ur::prelude::*;
use crate::{Error, PrivateKeyDataProvider, Result, tags};
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct Seed {
data: Vec<u8>,
name: String,
note: String,
creation_date: Option<Date>,
}
impl Seed {
pub const MIN_SEED_LENGTH: usize = 16;
pub fn new() -> Self { Self::new_with_len(Self::MIN_SEED_LENGTH).unwrap() }
pub fn new_with_len(count: usize) -> Result<Self> {
let mut rng = bc_rand::SecureRandomNumberGenerator;
Self::new_with_len_using(count, &mut rng)
}
pub fn new_with_len_using(
count: usize,
rng: &mut impl RandomNumberGenerator,
) -> Result<Self> {
let data = rng_random_data(rng, count);
Self::new_opt(data, None, None, None)
}
pub fn new_opt(
data: impl AsRef<[u8]>,
name: Option<String>,
note: Option<String>,
creation_date: Option<Date>,
) -> Result<Self> {
let data = data.as_ref().to_vec();
if data.len() < Self::MIN_SEED_LENGTH {
return Err(Error::data_too_short(
"seed",
Self::MIN_SEED_LENGTH,
data.len(),
));
}
Ok(Self {
data,
name: name.unwrap_or_default(),
note: note.unwrap_or_default(),
creation_date,
})
}
pub fn as_bytes(&self) -> &[u8] { self.as_ref() }
pub fn name(&self) -> &str { &self.name }
pub fn set_name(&mut self, name: &str) { self.name = name.to_string(); }
pub fn note(&self) -> &str { &self.note }
pub fn set_note(&mut self, note: &str) { self.note = note.to_string(); }
pub fn creation_date(&self) -> Option<Date> { self.creation_date }
pub fn set_creation_date(&mut self, creation_date: Option<Date>) {
self.creation_date = creation_date;
}
}
impl Default for Seed {
fn default() -> Self { Self::new() }
}
impl AsRef<[u8]> for Seed {
fn as_ref(&self) -> &[u8] { self.data.as_ref() }
}
impl AsRef<Seed> for Seed {
fn as_ref(&self) -> &Seed { self }
}
impl PrivateKeyDataProvider for Seed {
fn private_key_data(&self) -> Vec<u8> { self.as_bytes().to_vec() }
}
impl CBORTagged for Seed {
fn cbor_tags() -> Vec<Tag> {
tags_for_values(&[tags::TAG_SEED, tags::TAG_SEED_V1])
}
}
impl From<Seed> for CBOR {
fn from(value: Seed) -> Self { value.tagged_cbor() }
}
impl CBORTaggedEncodable for Seed {
fn untagged_cbor(&self) -> CBOR {
let mut map = dcbor::Map::new();
map.insert(1, CBOR::to_byte_string(self.as_bytes()));
if let Some(creation_date) = self.creation_date() {
map.insert(2, creation_date);
}
if !self.name().is_empty() {
map.insert(3, self.name());
}
if !self.note().is_empty() {
map.insert(4, self.note());
}
map.into()
}
}
impl TryFrom<CBOR> for Seed {
type Error = dcbor::Error;
fn try_from(cbor: CBOR) -> dcbor::Result<Self> {
Self::from_tagged_cbor(cbor)
}
}
impl CBORTaggedDecodable for Seed {
fn from_untagged_cbor(cbor: CBOR) -> dcbor::Result<Self> {
let map = cbor.try_into_map()?;
let data = map
.extract::<i32, CBOR>(1)?
.try_into_byte_string()?
.to_vec();
if data.is_empty() {
return Err("Seed data is empty".into());
}
let creation_date = map.get::<i32, Date>(2);
let name = map.get::<i32, String>(3);
let note = map.get::<i32, String>(4);
Ok(Self::new_opt(data, name, note, creation_date)?)
}
}