use core::fmt; use zeroize::{Zeroize, ZeroizeOnDrop, Zeroizing}; use rand_core::{RngCore, CryptoRng}; use thiserror::Error; pub(crate) mod classic; use classic::{CLASSIC_SEED_LENGTH, CLASSIC_SEED_LENGTH_WITH_CHECKSUM, ClassicSeed}; /// Error when decoding a seed. #[derive(Clone, Copy, PartialEq, Eq, Debug, Error)] pub enum SeedError { #[error("invalid number of words in seed")] InvalidSeedLength, #[error("unknown language")] UnknownLanguage, #[error("invalid checksum")] InvalidChecksum, #[error("english old seeds don't support checksums")] EnglishOldWithChecksum, #[error("invalid seed")] InvalidSeed, } #[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)] pub enum Language { Chinese, English, Dutch, French, Spanish, German, Italian, Portuguese, Japanese, Russian, Esperanto, Lojban, EnglishOld, } /// A Monero seed. // TODO: Add polyseed to enum #[derive(Clone, PartialEq, Eq, Zeroize, ZeroizeOnDrop)] pub enum Seed { Classic(ClassicSeed), } impl fmt::Debug for Seed { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { Seed::Classic(_) => f.debug_struct("Seed::Classic").finish_non_exhaustive(), } } } impl Seed { /// Create a new seed. pub fn new(rng: &mut R, lang: Language) -> Seed { Seed::Classic(ClassicSeed::new(rng, lang)) } /// Parse a seed from a String. pub fn from_string(words: Zeroizing) -> Result { match words.split_whitespace().count() { CLASSIC_SEED_LENGTH | CLASSIC_SEED_LENGTH_WITH_CHECKSUM => { ClassicSeed::from_string(words).map(Seed::Classic) } _ => Err(SeedError::InvalidSeedLength)?, } } /// Create a Seed from entropy. pub fn from_entropy(lang: Language, entropy: Zeroizing<[u8; 32]>) -> Option { ClassicSeed::from_entropy(lang, entropy).map(Seed::Classic) } /// Convert a seed to a String. pub fn to_string(&self) -> Zeroizing { match self { Seed::Classic(seed) => seed.to_string(), } } /// Return the entropy for this seed. pub fn entropy(&self) -> Zeroizing<[u8; 32]> { match self { Seed::Classic(seed) => seed.entropy(), } } }