1use core::fmt;
2use core::marker::PhantomData;
3
4use bip39::Mnemonic;
5use kdk_entropy::EntropyError;
6use kdk_zeroize::{prelude::*, SensitiveBytes};
7
8use crate::error::MnemonicError;
9
10pub struct GameMnemonic<O, const W: u8> {
15 inner: Mnemonic,
16 _origin: PhantomData<O>,
17}
18
19impl<O, const W: u8> GameMnemonic<O, W> {
20 pub(crate) fn new(inner: Mnemonic) -> Self {
23 Self {
24 inner,
25 _origin: PhantomData,
26 }
27 }
28
29 pub const fn word_count(&self) -> usize {
34 W as usize
35 }
36}
37
38impl<O, const W: u8> fmt::Debug for GameMnemonic<O, W> {
39 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
40 f.write_str("GameMnemonic(REDACTED)")
41 }
42}
43
44impl<O, const W: u8> fmt::Display for GameMnemonic<O, W> {
45 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
46 f.write_str("GameMnemonic(REDACTED)")
47 }
48}
49
50impl<O, const W: u8> Sensitive for GameMnemonic<O, W> {
51 type Inner = Mnemonic;
52
53 fn expose_secret(&self) -> &Mnemonic {
54 &self.inner
55 }
56}
57
58pub trait SensitiveGame: Sized {
61 fn to_entropy<const N: usize>(input: &[u8]) -> Result<SensitiveBytes<N, Self>, EntropyError>;
64
65 fn mnemonic<const W: u8>(input: &[u8]) -> Result<GameMnemonic<Self, W>, MnemonicError> {
68 match W {
69 #[cfg(feature = "words-12")]
70 12 => Self::build::<W, 16>(input),
71 #[cfg(feature = "words-15")]
72 15 => Self::build::<W, 20>(input),
73 #[cfg(feature = "words-18")]
74 18 => Self::build::<W, 24>(input),
75 #[cfg(feature = "words-21")]
76 21 => Self::build::<W, 28>(input),
77 #[cfg(feature = "words-24")]
78 24 => Self::build::<W, 32>(input),
79 _ => Err(MnemonicError::InvalidWordCount(W)),
80 }
81 }
82
83 fn build<const W: u8, const N: usize>(
85 input: &[u8],
86 ) -> Result<GameMnemonic<Self, W>, MnemonicError> {
87 let entropy = Self::to_entropy::<N>(input)?;
88 Mnemonic::from_entropy(entropy.expose_secret())
89 .map(GameMnemonic::new)
90 .map_err(MnemonicError::Bip39)
91 }
92}
93
94pub fn entropy_to_mnemonic<O, const W: u8, const N: usize>(
98 entropy: &SensitiveBytes<N, O>,
99) -> Result<GameMnemonic<O, W>, MnemonicError> {
100 let expected = match W {
101 #[cfg(feature = "words-12")]
102 12 => 16,
103 #[cfg(feature = "words-15")]
104 15 => 20,
105 #[cfg(feature = "words-18")]
106 18 => 24,
107 #[cfg(feature = "words-21")]
108 21 => 28,
109 #[cfg(feature = "words-24")]
110 24 => 32,
111 _ => return Err(MnemonicError::InvalidWordCount(W)),
112 };
113 if N != expected {
114 return Err(MnemonicError::InvalidEntropyLength(N));
115 }
116 Mnemonic::from_entropy(entropy.expose_secret())
117 .map(GameMnemonic::new)
118 .map_err(MnemonicError::Bip39)
119}