From 11ced19bc560000057196e0586a1d1ddf9266bee Mon Sep 17 00:00:00 2001 From: ryan Date: Wed, 10 Jan 2024 15:34:29 -0500 Subject: [PATCH] keyfork-shard: use MnemonicSetValidator --- keyfork-shard/src/lib.rs | 46 ++++++++++++++---------------------- keyfork-shard/src/openpgp.rs | 46 ++++++++++++++---------------------- 2 files changed, 36 insertions(+), 56 deletions(-) diff --git a/keyfork-shard/src/lib.rs b/keyfork-shard/src/lib.rs index bb51102..5de6177 100644 --- a/keyfork-shard/src/lib.rs +++ b/keyfork-shard/src/lib.rs @@ -1,16 +1,17 @@ -use std::{ - io::{stdin, stdout, Write}, - str::FromStr, -}; +use std::io::{stdin, stdout, Write}; use aes_gcm::{ aead::{Aead, AeadCore, OsRng}, Aes256Gcm, KeyInit, }; use hkdf::Hkdf; -use sha2::Sha256; use keyfork_mnemonic_util::{Mnemonic, Wordlist}; -use keyfork_prompt::{qrencode, Message as PromptMessage, PromptManager}; +use keyfork_prompt::{ + qrencode, + validators::{mnemonic::MnemonicSetValidator, Validator}, + Message as PromptMessage, PromptManager, +}; +use sha2::Sha256; use sharks::{Share, Sharks}; use x25519_dalek::{EphemeralSecret, PublicKey}; @@ -66,27 +67,17 @@ pub fn remote_decrypt(w: &mut impl Write) -> Result<(), Box Result<(), Box::new(None, &shared_secret); let mut hkdf_output = [0u8; 256 / 8]; hkdf.expand(&[], &mut hkdf_output)?; - let shared_key = - Aes256Gcm::new_from_slice(&hkdf_output)?; + let shared_key = Aes256Gcm::new_from_slice(&hkdf_output)?; - let payload = Mnemonic::from_str(&payload_mnemonic)?.entropy(); + let payload = payload_mnemonic.entropy(); let payload = shared_key.decrypt(&nonce, &payload[..payload[payload.len() - 1] as usize])?; assert_eq!(HUNK_VERSION, payload[0], "Incompatible hunk version"); diff --git a/keyfork-shard/src/openpgp.rs b/keyfork-shard/src/openpgp.rs index daaba77..1b217e8 100644 --- a/keyfork-shard/src/openpgp.rs +++ b/keyfork-shard/src/openpgp.rs @@ -11,13 +11,16 @@ use aes_gcm::{ Aes256Gcm, Error as AesError, KeyInit, Nonce, }; use hkdf::{Hkdf, InvalidLength as HkdfInvalidLength}; -use sha2::Sha256; use keyfork_derive_openpgp::derive_util::{ request::{DerivationAlgorithm, DerivationRequest}, DerivationPath, }; use keyfork_mnemonic_util::{Mnemonic, MnemonicFromStrError, MnemonicGenerationError, Wordlist}; -use keyfork_prompt::{qrencode, Error as PromptError, Message as PromptMessage, PromptManager}; +use keyfork_prompt::{ + qrencode, + validators::{mnemonic::MnemonicSetValidator, Validator}, + Error as PromptError, Message as PromptMessage, PromptManager, +}; use openpgp::{ armor::{Kind, Writer}, cert::{Cert, CertParser, ValidCert}, @@ -35,6 +38,7 @@ use openpgp::{ Fingerprint, KeyID, PacketPile, }; pub use sequoia_openpgp as openpgp; +use sha2::Sha256; use sharks::{Share, Sharks}; use x25519_dalek::{EphemeralSecret, PublicKey}; @@ -51,7 +55,7 @@ use smartcard::SmartcardManager; const SHARD_METADATA_VERSION: u8 = 1; const SHARD_METADATA_OFFSET: usize = 2; -use super::{SharksError, InvalidMnemonicData, HUNK_VERSION}; +use super::{InvalidMnemonicData, SharksError, HUNK_VERSION}; // 256 bit share is 49 bytes + some amount of hunk bytes, gives us reasonable padding const ENC_LEN: u8 = 4 * 16; @@ -407,28 +411,17 @@ pub fn decrypt( ) -> Result<()> { let mut pm = PromptManager::new(stdin(), stdout())?; let wordlist = Wordlist::default(); - let their_words = pm.prompt_wordlist("Their words: ", &wordlist)?; - let mut nonce_words = their_words.split_whitespace().take(9).peekable(); - let mut pubkey_words = their_words.split_whitespace().skip(9).take(24).peekable(); - let mut nonce_mnemonic = String::new(); - let mut pubkey_mnemonic = String::new(); - while let Some(word) = nonce_words.next() { - nonce_mnemonic.push_str(word); - if nonce_words.peek().is_some() { - nonce_mnemonic.push(' '); - } - } - while let Some(word) = pubkey_words.next() { - pubkey_mnemonic.push_str(word); - if pubkey_words.peek().is_some() { - pubkey_mnemonic.push(' '); - } - } - let their_key = Mnemonic::from_str(&pubkey_mnemonic)?.entropy(); - let their_key: [u8; 32] = their_key + let validator = MnemonicSetValidator { + word_lengths: [9, 24], + }; + let [nonce_mnemonic, pubkey_mnemonic] = + pm.prompt_validated_wordlist("Their words: ", &wordlist, 3, validator.to_fn())?; + + let their_key: [u8; 32] = pubkey_mnemonic + .entropy() .try_into() .map_err(|_| InvalidMnemonicData)?; - let their_nonce = Mnemonic::from_str(&nonce_mnemonic)?.entropy(); + let their_nonce = nonce_mnemonic.entropy(); let their_nonce = Nonce::::from_slice(&their_nonce); let our_key = EphemeralSecret::random(); @@ -450,8 +443,7 @@ pub fn decrypt( let hkdf = Hkdf::::new(None, &shared_secret); let mut hkdf_output = [0u8; 256 / 8]; hkdf.expand(&[], &mut hkdf_output)?; - let shared_key = - Aes256Gcm::new_from_slice(&hkdf_output)?; + let shared_key = Aes256Gcm::new_from_slice(&hkdf_output)?; let bytes = shared_key.encrypt(their_nonce, share.as_slice())?; shared_key.decrypt(their_nonce, &bytes[..])?; @@ -568,9 +560,7 @@ pub fn combine( return Err(Error::InvalidSecret(derived_fp, expected_fp)); } - output - .write_all(&secret) - .map_err(Error::Io)?; + output.write_all(&secret).map_err(Error::Io)?; Ok(()) }