Compare commits

..

No commits in common. "11ced19bc560000057196e0586a1d1ddf9266bee" and "0c4fc1628525f7096b5e1bb836f6930df5a1d27b" have entirely different histories.

3 changed files with 57 additions and 37 deletions

View File

@ -57,7 +57,7 @@ impl Validator for PinValidator {
#[cfg(feature = "mnemonic")] #[cfg(feature = "mnemonic")]
pub mod mnemonic { pub mod mnemonic {
use std::{ops::Range, str::FromStr}; use std::{mem::MaybeUninit, ops::Range, str::FromStr};
use super::Validator; use super::Validator;

View File

@ -1,17 +1,16 @@
use std::io::{stdin, stdout, Write}; use std::{
io::{stdin, stdout, Write},
str::FromStr,
};
use aes_gcm::{ use aes_gcm::{
aead::{Aead, AeadCore, OsRng}, aead::{Aead, AeadCore, OsRng},
Aes256Gcm, KeyInit, Aes256Gcm, KeyInit,
}; };
use hkdf::Hkdf; use hkdf::Hkdf;
use keyfork_mnemonic_util::{Mnemonic, Wordlist};
use keyfork_prompt::{
qrencode,
validators::{mnemonic::MnemonicSetValidator, Validator},
Message as PromptMessage, PromptManager,
};
use sha2::Sha256; use sha2::Sha256;
use keyfork_mnemonic_util::{Mnemonic, Wordlist};
use keyfork_prompt::{qrencode, Message as PromptMessage, PromptManager};
use sharks::{Share, Sharks}; use sharks::{Share, Sharks};
use x25519_dalek::{EphemeralSecret, PublicKey}; use x25519_dalek::{EphemeralSecret, PublicKey};
@ -67,17 +66,27 @@ pub fn remote_decrypt(w: &mut impl Write) -> Result<(), Box<dyn std::error::Erro
pm.prompt_message(&PromptMessage::Data(qrcode))?; pm.prompt_message(&PromptMessage::Data(qrcode))?;
} }
let validator = MnemonicSetValidator { let their_words = pm.prompt_wordlist("Their words: ", &wordlist)?;
word_lengths: [24, 48],
};
let [pubkey_mnemonic, payload_mnemonic] = let mut pubkey_words = their_words.split_whitespace().take(24).peekable();
pm.prompt_validated_wordlist("Their words: ", &wordlist, 3, validator.to_fn())?; let mut payload_words = their_words.split_whitespace().skip(24).take(48).peekable();
let mut pubkey_mnemonic = String::new();
let mut payload_mnemonic = String::new();
while let Some(word) = pubkey_words.next() {
pubkey_mnemonic.push_str(word);
if pubkey_words.peek().is_some() {
pubkey_mnemonic.push(' ');
}
}
while let Some(word) = payload_words.next() {
payload_mnemonic.push_str(word);
if payload_words.peek().is_some() {
payload_mnemonic.push(' ');
}
}
let their_key: [u8; 32] = pubkey_mnemonic let their_key = Mnemonic::from_str(&pubkey_mnemonic)?.entropy();
.entropy() let their_key: [u8; 32] = their_key.try_into().map_err(|_| InvalidMnemonicData)?;
.try_into()
.map_err(|_| InvalidMnemonicData)?;
let shared_secret = our_key let shared_secret = our_key
.diffie_hellman(&PublicKey::from(their_key)) .diffie_hellman(&PublicKey::from(their_key))
@ -85,9 +94,10 @@ pub fn remote_decrypt(w: &mut impl Write) -> Result<(), Box<dyn std::error::Erro
let hkdf = Hkdf::<Sha256>::new(None, &shared_secret); let hkdf = Hkdf::<Sha256>::new(None, &shared_secret);
let mut hkdf_output = [0u8; 256 / 8]; let mut hkdf_output = [0u8; 256 / 8];
hkdf.expand(&[], &mut hkdf_output)?; 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 = payload_mnemonic.entropy(); let payload = Mnemonic::from_str(&payload_mnemonic)?.entropy();
let payload = let payload =
shared_key.decrypt(&nonce, &payload[..payload[payload.len() - 1] as usize])?; shared_key.decrypt(&nonce, &payload[..payload[payload.len() - 1] as usize])?;
assert_eq!(HUNK_VERSION, payload[0], "Incompatible hunk version"); assert_eq!(HUNK_VERSION, payload[0], "Incompatible hunk version");

View File

@ -11,16 +11,13 @@ use aes_gcm::{
Aes256Gcm, Error as AesError, KeyInit, Nonce, Aes256Gcm, Error as AesError, KeyInit, Nonce,
}; };
use hkdf::{Hkdf, InvalidLength as HkdfInvalidLength}; use hkdf::{Hkdf, InvalidLength as HkdfInvalidLength};
use sha2::Sha256;
use keyfork_derive_openpgp::derive_util::{ use keyfork_derive_openpgp::derive_util::{
request::{DerivationAlgorithm, DerivationRequest}, request::{DerivationAlgorithm, DerivationRequest},
DerivationPath, DerivationPath,
}; };
use keyfork_mnemonic_util::{Mnemonic, MnemonicFromStrError, MnemonicGenerationError, Wordlist}; use keyfork_mnemonic_util::{Mnemonic, MnemonicFromStrError, MnemonicGenerationError, Wordlist};
use keyfork_prompt::{ use keyfork_prompt::{qrencode, Error as PromptError, Message as PromptMessage, PromptManager};
qrencode,
validators::{mnemonic::MnemonicSetValidator, Validator},
Error as PromptError, Message as PromptMessage, PromptManager,
};
use openpgp::{ use openpgp::{
armor::{Kind, Writer}, armor::{Kind, Writer},
cert::{Cert, CertParser, ValidCert}, cert::{Cert, CertParser, ValidCert},
@ -38,7 +35,6 @@ use openpgp::{
Fingerprint, KeyID, PacketPile, Fingerprint, KeyID, PacketPile,
}; };
pub use sequoia_openpgp as openpgp; pub use sequoia_openpgp as openpgp;
use sha2::Sha256;
use sharks::{Share, Sharks}; use sharks::{Share, Sharks};
use x25519_dalek::{EphemeralSecret, PublicKey}; use x25519_dalek::{EphemeralSecret, PublicKey};
@ -55,7 +51,7 @@ use smartcard::SmartcardManager;
const SHARD_METADATA_VERSION: u8 = 1; const SHARD_METADATA_VERSION: u8 = 1;
const SHARD_METADATA_OFFSET: usize = 2; const SHARD_METADATA_OFFSET: usize = 2;
use super::{InvalidMnemonicData, SharksError, HUNK_VERSION}; use super::{SharksError, InvalidMnemonicData, HUNK_VERSION};
// 256 bit share is 49 bytes + some amount of hunk bytes, gives us reasonable padding // 256 bit share is 49 bytes + some amount of hunk bytes, gives us reasonable padding
const ENC_LEN: u8 = 4 * 16; const ENC_LEN: u8 = 4 * 16;
@ -411,17 +407,28 @@ pub fn decrypt(
) -> Result<()> { ) -> Result<()> {
let mut pm = PromptManager::new(stdin(), stdout())?; let mut pm = PromptManager::new(stdin(), stdout())?;
let wordlist = Wordlist::default(); let wordlist = Wordlist::default();
let validator = MnemonicSetValidator { let their_words = pm.prompt_wordlist("Their words: ", &wordlist)?;
word_lengths: [9, 24], 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 [nonce_mnemonic, pubkey_mnemonic] = let mut nonce_mnemonic = String::new();
pm.prompt_validated_wordlist("Their words: ", &wordlist, 3, validator.to_fn())?; let mut pubkey_mnemonic = String::new();
while let Some(word) = nonce_words.next() {
let their_key: [u8; 32] = pubkey_mnemonic nonce_mnemonic.push_str(word);
.entropy() 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
.try_into() .try_into()
.map_err(|_| InvalidMnemonicData)?; .map_err(|_| InvalidMnemonicData)?;
let their_nonce = nonce_mnemonic.entropy(); let their_nonce = Mnemonic::from_str(&nonce_mnemonic)?.entropy();
let their_nonce = Nonce::<U12>::from_slice(&their_nonce); let their_nonce = Nonce::<U12>::from_slice(&their_nonce);
let our_key = EphemeralSecret::random(); let our_key = EphemeralSecret::random();
@ -443,7 +450,8 @@ pub fn decrypt(
let hkdf = Hkdf::<Sha256>::new(None, &shared_secret); let hkdf = Hkdf::<Sha256>::new(None, &shared_secret);
let mut hkdf_output = [0u8; 256 / 8]; let mut hkdf_output = [0u8; 256 / 8];
hkdf.expand(&[], &mut hkdf_output)?; 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())?; let bytes = shared_key.encrypt(their_nonce, share.as_slice())?;
shared_key.decrypt(their_nonce, &bytes[..])?; shared_key.decrypt(their_nonce, &bytes[..])?;
@ -560,7 +568,9 @@ pub fn combine(
return Err(Error::InvalidSecret(derived_fp, expected_fp)); 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(()) Ok(())
} }