keyfork-mnemonic-util: add MnemonicBase::from_nonstandard_bytes

This commit is contained in:
Ryan Heywood 2024-04-18 23:39:29 -04:00
parent 5d2309e301
commit 6a265ad203
Signed by: ryan
GPG Key ID: 8E401478A3FBEF72
3 changed files with 83 additions and 20 deletions

View File

@ -249,7 +249,10 @@ pub trait Format {
let our_key = EphemeralSecret::random();
let our_pubkey_mnemonic = Mnemonic::from_bytes(PublicKey::from(&our_key).as_bytes())?;
let shared_secret = our_key.diffie_hellman(&PublicKey::from(their_pubkey));
assert!(shared_secret.was_contributory(), bug!("shared secret might be insecure"));
assert!(
shared_secret.was_contributory(),
bug!("shared secret might be insecure")
);
let hkdf = Hkdf::<Sha256>::new(None, shared_secret.as_bytes());
let mut shared_key_data = [0u8; 256 / 8];
@ -289,12 +292,15 @@ pub trait Format {
// encrypt data
let encrypted_bytes = shared_key.encrypt(nonce, plaintext_bytes.as_slice())?;
assert_eq!(
encrypted_bytes.len(),
ENCRYPTED_LENGTH as usize,
bug!("encrypted bytes size != expected len"),
);
let mut mnemonic_bytes = [0u8; ENCRYPTED_LENGTH as usize];
mnemonic_bytes.copy_from_slice(&encrypted_bytes);
assert_eq!(encrypted_bytes.len(), ENCRYPTED_LENGTH as usize);
// safety: size of out_bytes is constant and always % 4 == 0
let payload_mnemonic = unsafe { Mnemonic::from_raw_bytes(&encrypted_bytes) };
dbg!(payload_mnemonic.words().len());
let payload_mnemonic = Mnemonic::from_nonstandard_bytes(mnemonic_bytes);
#[cfg(feature = "qrcode")]
{
@ -515,7 +521,10 @@ pub fn remote_decrypt(w: &mut impl Write) -> Result<(), Box<dyn std::error::Erro
);
let shared_secret = our_key.diffie_hellman(&PublicKey::from(pubkey));
assert!(shared_secret.was_contributory(), bug!("shared secret might be insecure"));
assert!(
shared_secret.was_contributory(),
bug!("shared secret might be insecure")
);
let hkdf = Hkdf::<Sha256>::new(None, shared_secret.as_bytes());
let mut shared_key_data = [0u8; 256 / 8];

View File

@ -8,7 +8,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
input.read_line(&mut line)?;
let decoded = smex::decode(line.trim())?;
let mnemonic = unsafe { Mnemonic::from_raw_bytes(&decoded) };
let mnemonic = Mnemonic::from_raw_bytes(&decoded) ;
println!("{mnemonic}");

View File

@ -125,6 +125,13 @@ impl Wordlist for English {
}
}
struct AssertValidMnemonicSize<const N: usize>;
impl<const N: usize> AssertValidMnemonicSize<N> {
const OK_CHUNKS: () = assert!(N % 4 == 0, "bytes must be a length divisible by 4");
const OK_SIZE: () = assert!(N <= 1024, "bytes must be less-or-equal 1024");
}
/// A BIP-0039 mnemonic with reference to a [`Wordlist`].
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct MnemonicBase<W: Wordlist> {
@ -276,7 +283,36 @@ where
return Err(MnemonicGenerationError::InvalidByteLength(bit_count));
}
Ok(unsafe { Self::from_raw_bytes(bytes) })
Ok( Self::from_raw_bytes(bytes) )
}
/// Generate a [`Mnemonic`] from the provided data and [`Wordlist`]. The data may be of a size
/// of a factor of 4, up to 1024 bytes.
///
/// ```rust
/// use keyfork_mnemonic_util::Mnemonic;
/// let data = b"hello world!";
/// let mnemonic = Mnemonic::from_nonstandard_bytes(*data);
/// ```
///
/// If an invalid size is requested, the code will fail to compile:
///
/// ```rust,compile_fail
/// use keyfork_mnemonic_util::Mnemonic;
/// let mnemonic = Mnemonic::from_nonstandard_bytes([0u8; 53]);
/// ```
///
/// ```rust,compile_fail
/// use keyfork_mnemonic_util::Mnemonic;
/// let mnemonic = Mnemonic::from_nonstandard_bytes([0u8; 1024 + 4]);
/// ```
pub fn from_nonstandard_bytes<const N: usize>(bytes: [u8; N]) -> MnemonicBase<W> {
#[allow(clippy::let_unit_value)]
{
let () = AssertValidMnemonicSize::<N>::OK_CHUNKS;
let () = AssertValidMnemonicSize::<N>::OK_SIZE;
}
Self::from_raw_bytes(&bytes)
}
/// Generate a [`Mnemonic`] from the provided data and [`Wordlist`]. The data is expected to be
@ -292,11 +328,12 @@ where
/// Create a Mnemonic using an arbitrary length of given data. The length does not need to
/// conform to BIP-0039 standards, but should be a multiple of 32 bits or 4 bytes.
///
/// # Safety
///
/// # Panics
/// This function can potentially produce mnemonics that are not BIP-0039 compliant or can't
/// properly be encoded as a mnemonic. It is assumed the caller asserts the byte count is `% 4
/// == 0`. If the assumption is incorrect, code may panic.
/// == 0`. If the assumption is incorrect, code may panic. The
/// [`MnemonicBase::from_nonstandard_bytes`] function may be used to generate entropy if the
/// length of the data is known at compile-time.
///
/// # Examples
/// ```rust
@ -315,11 +352,10 @@ where
/// // NOTE: Data is of invalid length, 31
/// let data = b"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
/// let mnemonic = unsafe { Mnemonic::from_raw_bytes(data.as_slice()) };
/// let mnemonic_text = mnemonic.to_string();
/// // NOTE: panic happens here
/// let new_mnemonic = Mnemonic::from_str(&mnemonic_text).unwrap();
/// ```
pub unsafe fn from_raw_bytes(bytes: &[u8]) -> MnemonicBase<W> {
pub fn from_raw_bytes(bytes: &[u8]) -> MnemonicBase<W> {
assert!(bytes.len() % 4 == 0);
assert!(bytes.len() <= 1024);
MnemonicBase {
data: bytes.to_vec(),
marker: PhantomData,
@ -520,12 +556,30 @@ mod tests {
}
#[test]
fn can_do_up_to_1024_bits() {
let entropy = &mut [0u8; 128];
fn can_do_up_to_8192_bits() {
let mut entropy = [0u8; 1024];
let mut random = std::fs::File::open("/dev/urandom").unwrap();
random.read_exact(&mut entropy[..]).unwrap();
let mnemonic = unsafe { Mnemonic::from_raw_bytes(&entropy[..]) };
let mnemonic = Mnemonic::from_nonstandard_bytes(entropy);
let words = mnemonic.words();
assert!(words.len() == 96);
assert_eq!(words.len(), 768);
}
#[test]
#[should_panic]
fn fails_over_8192_bits() {
let entropy = &mut [0u8; 1024 + 4];
let mut random = std::fs::File::open("/dev/urandom").unwrap();
random.read_exact(&mut entropy[..]).unwrap();
let _mnemonic = Mnemonic::from_raw_bytes(&entropy[..]);
}
#[test]
#[should_panic]
fn fails_over_invalid_size() {
let entropy = &mut [0u8; 255];
let mut random = std::fs::File::open("/dev/urandom").unwrap();
random.read_exact(&mut entropy[..]).unwrap();
let _mnemonic = Mnemonic::from_raw_bytes(&entropy[..]);
}
}