Compare commits

..

2 Commits

8 changed files with 145 additions and 10 deletions

1
Cargo.lock generated
View File

@ -1786,6 +1786,7 @@ dependencies = [
"pbkdf2", "pbkdf2",
"serde_json", "serde_json",
"sha2", "sha2",
"smex",
] ]
[[package]] [[package]]

View File

@ -78,8 +78,8 @@ pub fn remote_decrypt(w: &mut impl Write) -> Result<(), Box<dyn std::error::Erro
#[cfg(feature = "qrcode")] #[cfg(feature = "qrcode")]
{ {
use keyfork_qrcode::{qrencode, ErrorCorrection}; use keyfork_qrcode::{qrencode, ErrorCorrection};
let mut qrcode_data = nonce_mnemonic.entropy(); let mut qrcode_data = nonce_mnemonic.to_bytes();
qrcode_data.extend(key_mnemonic.entropy()); qrcode_data.extend(key_mnemonic.as_bytes());
if let Ok(qrcode) = qrencode(&smex::encode(&qrcode_data), ErrorCorrection::Medium) { if let Ok(qrcode) = qrencode(&smex::encode(&qrcode_data), ErrorCorrection::Medium) {
pm.prompt_message(PromptMessage::Data(qrcode))?; pm.prompt_message(PromptMessage::Data(qrcode))?;
} }
@ -120,10 +120,10 @@ pub fn remote_decrypt(w: &mut impl Write) -> Result<(), Box<dyn std::error::Erro
let [pubkey_mnemonic, payload_mnemonic] = let [pubkey_mnemonic, payload_mnemonic] =
pm.prompt_validated_wordlist("Their words: ", &wordlist, 3, validator.to_fn())?; pm.prompt_validated_wordlist("Their words: ", &wordlist, 3, validator.to_fn())?;
let pubkey = pubkey_mnemonic let pubkey = pubkey_mnemonic
.entropy() .as_bytes()
.try_into() .try_into()
.map_err(|_| InvalidData)?; .map_err(|_| InvalidData)?;
let payload = payload_mnemonic.entropy(); let payload = payload_mnemonic.to_bytes();
(pubkey, payload) (pubkey, payload)
} }
}; };

View File

@ -495,11 +495,11 @@ pub fn decrypt(
pm.prompt_validated_wordlist("Their words: ", &wordlist, 3, validator.to_fn())?; pm.prompt_validated_wordlist("Their words: ", &wordlist, 3, validator.to_fn())?;
let nonce = nonce_mnemonic let nonce = nonce_mnemonic
.entropy() .as_bytes()
.try_into() .try_into()
.map_err(|_| InvalidData)?; .map_err(|_| InvalidData)?;
let pubkey = pubkey_mnemonic let pubkey = pubkey_mnemonic
.entropy() .as_bytes()
.try_into() .try_into()
.map_err(|_| InvalidData)?; .map_err(|_| InvalidData)?;
(nonce, pubkey) (nonce, pubkey)
@ -560,8 +560,8 @@ pub fn decrypt(
#[cfg(feature = "qrcode")] #[cfg(feature = "qrcode")]
{ {
use keyfork_qrcode::{qrencode, ErrorCorrection}; use keyfork_qrcode::{qrencode, ErrorCorrection};
let mut qrcode_data = our_pubkey_mnemonic.entropy(); let mut qrcode_data = our_pubkey_mnemonic.to_bytes();
qrcode_data.extend(payload_mnemonic.entropy()); qrcode_data.extend(payload_mnemonic.as_bytes());
if let Ok(qrcode) = qrencode(&smex::encode(&qrcode_data), ErrorCorrection::Lowest) { if let Ok(qrcode) = qrencode(&smex::encode(&qrcode_data), ErrorCorrection::Lowest) {
pm.prompt_message(PromptMessage::Data(qrcode))?; pm.prompt_message(PromptMessage::Data(qrcode))?;
} }

View File

@ -75,7 +75,7 @@ impl RecoverSubcommands {
3, 3,
validator.to_fn(), validator.to_fn(),
)?; )?;
Ok(mnemonic.entropy()) Ok(mnemonic.to_bytes())
} }
} }
} }

View File

@ -6,12 +6,17 @@ repository = "https://git.distrust.co/public/keyfork"
edition = "2021" edition = "2021"
license = "MIT" license = "MIT"
[features]
default = []
bin = ["smex"]
[dependencies] [dependencies]
# Included in rust # Included in rust
sha2 = "0.10.7" sha2 = "0.10.7"
hmac = "0.12.1" hmac = "0.12.1"
pbkdf2 = "0.12.2" pbkdf2 = "0.12.2"
smex = { version = "0.1.0", path = "../smex", optional = true }
[dev-dependencies] [dev-dependencies]
bip39 = "2.0.0" bip39 = "2.0.0"

View File

@ -265,17 +265,27 @@ impl Mnemonic {
&self.entropy &self.entropy
} }
/// The internal representation of the decoded data, as a [`Vec<u8>`].
pub fn to_bytes(&self) -> Vec<u8> {
self.entropy.to_vec()
}
/// Drop self, returning the decoded data. /// Drop self, returning the decoded data.
pub fn to_bytes(self) -> Vec<u8> { pub fn into_bytes(self) -> Vec<u8> {
self.entropy self.entropy
} }
/// Clone the existing entropy. /// Clone the existing entropy.
#[deprecated]
pub fn entropy(&self) -> Vec<u8> { pub fn entropy(&self) -> Vec<u8> {
self.entropy.clone() self.entropy.clone()
} }
/// Create a BIP-0032 seed from the provided data and an optional passphrase. /// Create a BIP-0032 seed from the provided data and an optional passphrase.
///
/// # Errors
/// The method may return an error if the pbkdf2 function returns an invalid length, but this
/// case should not be reached.
pub fn seed<'a>( pub fn seed<'a>(
&self, &self,
passphrase: impl Into<Option<&'a str>>, passphrase: impl Into<Option<&'a str>>,

View File

@ -25,6 +25,7 @@
# Developers Guide # Developers Guide
- [Handling Data](./dev-guide/handling-data.md)
- [Writing Binaries](./dev-guide/index.md) - [Writing Binaries](./dev-guide/index.md)
- [Provisioners](./dev-guide/provisioners.md) - [Provisioners](./dev-guide/provisioners.md)
- [Auditing Dependencies](./dev-guide/auditing.md) - [Auditing Dependencies](./dev-guide/auditing.md)

View File

@ -0,0 +1,118 @@
# Handling Data
In Rust, it is common to name things `as_*`, `to_*`, and `into_*`. These three
methods perform three different, but related, functions, and it's important to
choose the correct name when writing methods. These methods take data that are
stored inside of the struct, and return them in some manner. For example, let's
look at the `Mnemonic` struct:
```rust
pub struct Mnemonic {
entropy: Vec<u8>,
// fields omitted
}
```
We're going to define the three methods `as_bytes()`, `to_bytes()`, and
`into_bytes()`, explaining what the details of each are. To start with:
### `Mnemonic::as_bytes()`
This method will return a `&[u8]`, a reference to a slice of bytes, without
allocating anything. The `Mnemonic` type is not consumed by this process.
```rust
impl Mnemonic {
/// Return a slice of bytes represented by the mnemonic.
pub fn as_bytes(&self) -> &[u8] {
&self.entropy
}
}
```
### `Mnemonic::to_bytes()`
This method will return a `Vec<u8>`, an allocated type containing the bytes,
without consuming the `Mnemonic` type. This is useful if you may need the
`Mnemonic` type in the future.
```rust
impl Mnemonic {
/// Return a `Vec<u8>` of bytes represented by the mnemonic.
pub fn to_bytes(&self) -> Vec<u8> {
self.entropy.to_vec()
}
}
```
### `Mnemonic::into_bytes()`
This method will return the internal representation of bytes. No allocation is
performed, but the `Mnemonic` value is no longer accessible.
```rust
impl Mnemonic {
/// Return a Vec<u8> of bytes represented by the mnemonic.
pub fn into_bytes(self) -> Vec<u8> {
self.entropy
}
}
```
## Creating Data: `Mnemonic::seed()`
Sometimes you may have an operation (such as creating a seed from a mnemonic)
which can't possibly happen without allocating some kind of data. In that case,
it is not reasonable - or even possible - to have three methods for that case.
In that case, it is reasonable to have a method with a "bare" name, such as
`Mnemonic::seed()`. Additionally, unless it makes sense to make the type
unusable after such an operation (using the type system to your advantage), it
makes sense to borrow from self while returning the allocated value.
```rust
impl Mnemonic {
/// Create a seed from the mnemonic's data.
pub fn seed(&self) -> Result<Vec<u8>, Error> {
let result = perform_secret_operation(self.entropy)?;
Ok(result)
}
}
```
## Complex Example: `Mnemonic::into_parts()`
Let's add a `Wordlist` to our `Mnemonic` type, so we can format it as a string.
```rust
pub struct Mnemonic {
entropy: Vec<u8>,
wordlist: Wordlist,
}
```
Because we now have two expensive-to-allocate variables in our type, we need to
change how we deconstruct it into parts:
```rust
impl Mnemonic {
/// Split the Mnemonic into its interior parts.
pub fn into_parts(self) -> (Vec<u8>, Wordlist) {
let Mnemonic { entropy, wordlist } = self;
(entropy, wordlist)
}
/// Return a Vec<u8> of bytes represented by the mnemonic.
pub fn into_bytes(self) -> Vec<u8> {
self.into_parts().0
}
/// Return the Wordlist used by the mnemonic.
pub fn into_wordlist(self) -> Wordlist {
self.into_parts().1
}
}
```
In practical use, `Mnemonic` stores `wordlist` as an `Arc<T>` to allow reusing
the same wordlist (parsing it is expensive!), but we'll ignore that for
demonstration purposes.