keyfork-shard: use MnemonicSetValidator
This commit is contained in:
parent
b396497108
commit
11ced19bc5
|
@ -1,16 +1,17 @@
|
||||||
use std::{
|
use std::io::{stdin, stdout, Write};
|
||||||
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 sha2::Sha256;
|
|
||||||
use keyfork_mnemonic_util::{Mnemonic, Wordlist};
|
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 sharks::{Share, Sharks};
|
||||||
use x25519_dalek::{EphemeralSecret, PublicKey};
|
use x25519_dalek::{EphemeralSecret, PublicKey};
|
||||||
|
|
||||||
|
@ -66,27 +67,17 @@ 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 their_words = pm.prompt_wordlist("Their words: ", &wordlist)?;
|
let validator = MnemonicSetValidator {
|
||||||
|
word_lengths: [24, 48],
|
||||||
|
};
|
||||||
|
|
||||||
let mut pubkey_words = their_words.split_whitespace().take(24).peekable();
|
let [pubkey_mnemonic, payload_mnemonic] =
|
||||||
let mut payload_words = their_words.split_whitespace().skip(24).take(48).peekable();
|
pm.prompt_validated_wordlist("Their words: ", &wordlist, 3, validator.to_fn())?;
|
||||||
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 = Mnemonic::from_str(&pubkey_mnemonic)?.entropy();
|
let their_key: [u8; 32] = pubkey_mnemonic
|
||||||
let their_key: [u8; 32] = their_key.try_into().map_err(|_| InvalidMnemonicData)?;
|
.entropy()
|
||||||
|
.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))
|
||||||
|
@ -94,10 +85,9 @@ 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 =
|
let shared_key = Aes256Gcm::new_from_slice(&hkdf_output)?;
|
||||||
Aes256Gcm::new_from_slice(&hkdf_output)?;
|
|
||||||
|
|
||||||
let payload = Mnemonic::from_str(&payload_mnemonic)?.entropy();
|
let payload = 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");
|
||||||
|
|
|
@ -11,13 +11,16 @@ 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::{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::{
|
use openpgp::{
|
||||||
armor::{Kind, Writer},
|
armor::{Kind, Writer},
|
||||||
cert::{Cert, CertParser, ValidCert},
|
cert::{Cert, CertParser, ValidCert},
|
||||||
|
@ -35,6 +38,7 @@ 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};
|
||||||
|
|
||||||
|
@ -51,7 +55,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::{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
|
// 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;
|
||||||
|
@ -407,28 +411,17 @@ 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 their_words = pm.prompt_wordlist("Their words: ", &wordlist)?;
|
let validator = MnemonicSetValidator {
|
||||||
let mut nonce_words = their_words.split_whitespace().take(9).peekable();
|
word_lengths: [9, 24],
|
||||||
let mut pubkey_words = their_words.split_whitespace().skip(9).take(24).peekable();
|
};
|
||||||
let mut nonce_mnemonic = String::new();
|
let [nonce_mnemonic, pubkey_mnemonic] =
|
||||||
let mut pubkey_mnemonic = String::new();
|
pm.prompt_validated_wordlist("Their words: ", &wordlist, 3, validator.to_fn())?;
|
||||||
while let Some(word) = nonce_words.next() {
|
|
||||||
nonce_mnemonic.push_str(word);
|
let their_key: [u8; 32] = pubkey_mnemonic
|
||||||
if nonce_words.peek().is_some() {
|
.entropy()
|
||||||
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 = Mnemonic::from_str(&nonce_mnemonic)?.entropy();
|
let their_nonce = 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();
|
||||||
|
@ -450,8 +443,7 @@ 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 =
|
let shared_key = Aes256Gcm::new_from_slice(&hkdf_output)?;
|
||||||
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[..])?;
|
||||||
|
@ -568,9 +560,7 @@ pub fn combine(
|
||||||
return Err(Error::InvalidSecret(derived_fp, expected_fp));
|
return Err(Error::InvalidSecret(derived_fp, expected_fp));
|
||||||
}
|
}
|
||||||
|
|
||||||
output
|
output.write_all(&secret).map_err(Error::Io)?;
|
||||||
.write_all(&secret)
|
|
||||||
.map_err(Error::Io)?;
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue