keyfork-mnemonic-util: add MnemonicBase::from_nonstandard_bytes
This commit is contained in:
parent
5d2309e301
commit
6a265ad203
|
@ -249,7 +249,10 @@ pub trait Format {
|
||||||
let our_key = EphemeralSecret::random();
|
let our_key = EphemeralSecret::random();
|
||||||
let our_pubkey_mnemonic = Mnemonic::from_bytes(PublicKey::from(&our_key).as_bytes())?;
|
let our_pubkey_mnemonic = Mnemonic::from_bytes(PublicKey::from(&our_key).as_bytes())?;
|
||||||
let shared_secret = our_key.diffie_hellman(&PublicKey::from(their_pubkey));
|
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 hkdf = Hkdf::<Sha256>::new(None, shared_secret.as_bytes());
|
||||||
|
|
||||||
let mut shared_key_data = [0u8; 256 / 8];
|
let mut shared_key_data = [0u8; 256 / 8];
|
||||||
|
@ -289,12 +292,15 @@ pub trait Format {
|
||||||
|
|
||||||
// encrypt data
|
// encrypt data
|
||||||
let encrypted_bytes = shared_key.encrypt(nonce, plaintext_bytes.as_slice())?;
|
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);
|
let payload_mnemonic = Mnemonic::from_nonstandard_bytes(mnemonic_bytes);
|
||||||
|
|
||||||
// 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());
|
|
||||||
|
|
||||||
#[cfg(feature = "qrcode")]
|
#[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));
|
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 hkdf = Hkdf::<Sha256>::new(None, shared_secret.as_bytes());
|
||||||
|
|
||||||
let mut shared_key_data = [0u8; 256 / 8];
|
let mut shared_key_data = [0u8; 256 / 8];
|
||||||
|
|
|
@ -8,7 +8,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
input.read_line(&mut line)?;
|
input.read_line(&mut line)?;
|
||||||
let decoded = smex::decode(line.trim())?;
|
let decoded = smex::decode(line.trim())?;
|
||||||
|
|
||||||
let mnemonic = unsafe { Mnemonic::from_raw_bytes(&decoded) };
|
let mnemonic = Mnemonic::from_raw_bytes(&decoded) ;
|
||||||
|
|
||||||
println!("{mnemonic}");
|
println!("{mnemonic}");
|
||||||
|
|
||||||
|
|
|
@ -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`].
|
/// A BIP-0039 mnemonic with reference to a [`Wordlist`].
|
||||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
pub struct MnemonicBase<W: Wordlist> {
|
pub struct MnemonicBase<W: Wordlist> {
|
||||||
|
@ -276,7 +283,36 @@ where
|
||||||
return Err(MnemonicGenerationError::InvalidByteLength(bit_count));
|
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
|
/// 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
|
/// 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.
|
/// 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
|
/// 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
|
/// 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
|
/// # Examples
|
||||||
/// ```rust
|
/// ```rust
|
||||||
|
@ -315,11 +352,10 @@ where
|
||||||
/// // NOTE: Data is of invalid length, 31
|
/// // NOTE: Data is of invalid length, 31
|
||||||
/// let data = b"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
|
/// let data = b"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
|
||||||
/// let mnemonic = unsafe { Mnemonic::from_raw_bytes(data.as_slice()) };
|
/// 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 {
|
MnemonicBase {
|
||||||
data: bytes.to_vec(),
|
data: bytes.to_vec(),
|
||||||
marker: PhantomData,
|
marker: PhantomData,
|
||||||
|
@ -520,12 +556,30 @@ mod tests {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn can_do_up_to_1024_bits() {
|
fn can_do_up_to_8192_bits() {
|
||||||
let entropy = &mut [0u8; 128];
|
let mut entropy = [0u8; 1024];
|
||||||
let mut random = std::fs::File::open("/dev/urandom").unwrap();
|
let mut random = std::fs::File::open("/dev/urandom").unwrap();
|
||||||
random.read_exact(&mut entropy[..]).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();
|
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[..]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue