use core::fmt; use std_shims::string::String; use zeroize::{Zeroize, ZeroizeOnDrop, Zeroizing}; use rand_core::{RngCore, CryptoRng}; pub use monero_seed as original; pub use polyseed; use original::{SeedError as OriginalSeedError, Seed as OriginalSeed}; use polyseed::{PolyseedError, Polyseed}; /// An error from working with seeds. #[derive(Clone, Copy, PartialEq, Eq, Debug)] #[cfg_attr(feature = "std", derive(thiserror::Error))] pub enum SeedError { /// The seed was invalid. #[cfg_attr(feature = "std", error("invalid seed"))] InvalidSeed, /// The entropy was invalid. #[cfg_attr(feature = "std", error("invalid entropy"))] InvalidEntropy, /// The checksum did not match the data. #[cfg_attr(feature = "std", error("invalid checksum"))] InvalidChecksum, /// Unsupported features were enabled. #[cfg_attr(feature = "std", error("unsupported features"))] UnsupportedFeatures, } impl From for SeedError { fn from(error: OriginalSeedError) -> SeedError { match error { OriginalSeedError::DeprecatedEnglishWithChecksum | OriginalSeedError::InvalidChecksum => { SeedError::InvalidChecksum } OriginalSeedError::InvalidSeed => SeedError::InvalidSeed, } } } impl From for SeedError { fn from(error: PolyseedError) -> SeedError { match error { PolyseedError::UnsupportedFeatures => SeedError::UnsupportedFeatures, PolyseedError::InvalidEntropy => SeedError::InvalidEntropy, PolyseedError::InvalidSeed => SeedError::InvalidSeed, PolyseedError::InvalidChecksum => SeedError::InvalidChecksum, } } } /// The type of the seed. #[derive(Clone, Copy, PartialEq, Eq, Debug)] pub enum SeedType { /// The seed format originally used by Monero, Original(monero_seed::Language), /// Polyseed. Polyseed(polyseed::Language), } /// A seed, internally either the original format or a Polyseed. #[derive(Clone, PartialEq, Eq, Zeroize, ZeroizeOnDrop)] pub enum Seed { /// The originally formatted seed. Original(OriginalSeed), /// A Polyseed. Polyseed(Polyseed), } impl fmt::Debug for Seed { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { Seed::Original(_) => f.debug_struct("Seed::Original").finish_non_exhaustive(), Seed::Polyseed(_) => f.debug_struct("Seed::Polyseed").finish_non_exhaustive(), } } } impl Seed { /// Create a new seed. pub fn new(rng: &mut R, seed_type: SeedType) -> Seed { match seed_type { SeedType::Original(lang) => Seed::Original(OriginalSeed::new(rng, lang)), SeedType::Polyseed(lang) => Seed::Polyseed(Polyseed::new(rng, lang)), } } /// Parse a seed from a string. pub fn from_string(seed_type: SeedType, words: Zeroizing) -> Result { match seed_type { SeedType::Original(lang) => Ok(OriginalSeed::from_string(lang, words).map(Seed::Original)?), SeedType::Polyseed(lang) => Ok(Polyseed::from_string(lang, words).map(Seed::Polyseed)?), } } /// Create a seed from entropy. /// /// A birthday may be optionally provided, denoted in seconds since the epoch. For /// SeedType::Original, it will be ignored. For SeedType::Polyseed, it'll be embedded into the /// seed. /// /// For SeedType::Polyseed, the last 13 bytes of `entropy` must be 0. // TODO: Return Result, not Option pub fn from_entropy( seed_type: SeedType, entropy: Zeroizing<[u8; 32]>, birthday: Option, ) -> Option { match seed_type { SeedType::Original(lang) => OriginalSeed::from_entropy(lang, entropy).map(Seed::Original), SeedType::Polyseed(lang) => { Polyseed::from(lang, 0, birthday.unwrap_or(0), entropy).ok().map(Seed::Polyseed) } } } /// Converts the seed to a string. pub fn to_string(&self) -> Zeroizing { match self { Seed::Original(seed) => seed.to_string(), Seed::Polyseed(seed) => seed.to_string(), } } /// Get the entropy for this seed. pub fn entropy(&self) -> Zeroizing<[u8; 32]> { match self { Seed::Original(seed) => seed.entropy(), Seed::Polyseed(seed) => seed.entropy().clone(), } } /// Get the key derived from this seed. pub fn key(&self) -> Zeroizing<[u8; 32]> { match self { // Original does not differentiate between its entropy and its key Seed::Original(seed) => seed.entropy(), Seed::Polyseed(seed) => seed.key(), } } /// Get the birthday of this seed, denoted in seconds since the epoch. pub fn birthday(&self) -> u64 { match self { Seed::Original(_) => 0, Seed::Polyseed(seed) => seed.birthday(), } } }