keyfork-shard: add instructions for sending QR code to operators
This commit is contained in:
parent
d6b52a8f0a
commit
1879a250c8
|
@ -46,6 +46,8 @@ pub(crate) const HUNK_OFFSET: usize = 2;
|
||||||
|
|
||||||
const QRCODE_PROMPT: &str = "Press enter, then present QR code to camera.";
|
const QRCODE_PROMPT: &str = "Press enter, then present QR code to camera.";
|
||||||
const QRCODE_TIMEOUT: u64 = 60; // One minute
|
const QRCODE_TIMEOUT: u64 = 60; // One minute
|
||||||
|
const QRCODE_COULDNT_READ: &str = "A QR code could not be scanned. Please enter their words: ";
|
||||||
|
const QRCODE_ERROR: &str = "Unable to scan a QR code. Falling back to text entry.";
|
||||||
|
|
||||||
/// Establish ECDH transport for remote operators, receive transport-encrypted shares, decrypt the
|
/// Establish ECDH transport for remote operators, receive transport-encrypted shares, decrypt the
|
||||||
/// shares, and combine them.
|
/// shares, and combine them.
|
||||||
|
@ -67,8 +69,10 @@ pub fn remote_decrypt(w: &mut impl Write) -> Result<(), Box<dyn std::error::Erro
|
||||||
let mut shares = vec![];
|
let mut shares = vec![];
|
||||||
|
|
||||||
let mut threshold = 0;
|
let mut threshold = 0;
|
||||||
|
let mut iter = 0;
|
||||||
|
|
||||||
while iter_count.is_none() || iter_count.is_some_and(|i| i > 0) {
|
while iter_count.is_none() || iter_count.is_some_and(|i| i > 0) {
|
||||||
|
iter += 1;
|
||||||
let nonce = Aes256Gcm::generate_nonce(&mut OsRng);
|
let nonce = Aes256Gcm::generate_nonce(&mut OsRng);
|
||||||
let nonce_mnemonic =
|
let nonce_mnemonic =
|
||||||
unsafe { Mnemonic::from_raw_entropy(nonce.as_slice(), Default::default()) };
|
unsafe { Mnemonic::from_raw_entropy(nonce.as_slice(), Default::default()) };
|
||||||
|
@ -82,12 +86,26 @@ pub fn remote_decrypt(w: &mut impl Write) -> Result<(), Box<dyn std::error::Erro
|
||||||
let mut qrcode_data = nonce_mnemonic.to_bytes();
|
let mut qrcode_data = nonce_mnemonic.to_bytes();
|
||||||
qrcode_data.extend(key_mnemonic.as_bytes());
|
qrcode_data.extend(key_mnemonic.as_bytes());
|
||||||
if let Ok(qrcode) = qrencode(&smex::encode(&qrcode_data), ErrorCorrection::Highest) {
|
if let Ok(qrcode) = qrencode(&smex::encode(&qrcode_data), ErrorCorrection::Highest) {
|
||||||
|
pm.prompt_message(PromptMessage::Text(format!(
|
||||||
|
concat!(
|
||||||
|
"A QR code will be displayed after this prompt. ",
|
||||||
|
"Send the QR code to only shardholder {iter}. ",
|
||||||
|
"Nobody else should scan this QR code."
|
||||||
|
),
|
||||||
|
iter = iter
|
||||||
|
)))?;
|
||||||
pm.prompt_message(PromptMessage::Data(qrcode))?;
|
pm.prompt_message(PromptMessage::Data(qrcode))?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pm.prompt_message(PromptMessage::Text(format!(
|
pm.prompt_message(PromptMessage::Text(format!(
|
||||||
"Our words: {nonce_mnemonic} {key_mnemonic}"
|
concat!(
|
||||||
|
"Upon request, these words should be sent to shardholder {iter}: ",
|
||||||
|
"{nonce_mnemonic} {key_mnemonic}"
|
||||||
|
),
|
||||||
|
iter = iter,
|
||||||
|
nonce_mnemonic = nonce_mnemonic,
|
||||||
|
key_mnemonic = key_mnemonic,
|
||||||
)))?;
|
)))?;
|
||||||
|
|
||||||
let mut pubkey_data: Option<[u8; 32]> = None;
|
let mut pubkey_data: Option<[u8; 32]> = None;
|
||||||
|
@ -103,9 +121,7 @@ pub fn remote_decrypt(w: &mut impl Write) -> Result<(), Box<dyn std::error::Erro
|
||||||
let _ = pubkey_data.insert(decoded_data[..32].try_into().map_err(|_| InvalidData)?);
|
let _ = pubkey_data.insert(decoded_data[..32].try_into().map_err(|_| InvalidData)?);
|
||||||
let _ = payload_data.insert(decoded_data[32..].to_vec());
|
let _ = payload_data.insert(decoded_data[32..].to_vec());
|
||||||
} else {
|
} else {
|
||||||
pm.prompt_message(PromptMessage::Text(
|
pm.prompt_message(PromptMessage::Text(QRCODE_ERROR.to_string()))?;
|
||||||
"Unable to detect QR code, falling back to text".to_string(),
|
|
||||||
))?;
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -116,8 +132,12 @@ pub fn remote_decrypt(w: &mut impl Write) -> Result<(), Box<dyn std::error::Erro
|
||||||
word_lengths: [24, 48],
|
word_lengths: [24, 48],
|
||||||
};
|
};
|
||||||
|
|
||||||
let [pubkey_mnemonic, payload_mnemonic] =
|
let [pubkey_mnemonic, payload_mnemonic] = pm.prompt_validated_wordlist(
|
||||||
pm.prompt_validated_wordlist("Their words: ", &wordlist, 3, validator.to_fn())?;
|
QRCODE_COULDNT_READ,
|
||||||
|
&wordlist,
|
||||||
|
3,
|
||||||
|
validator.to_fn(),
|
||||||
|
)?;
|
||||||
let pubkey = pubkey_mnemonic
|
let pubkey = pubkey_mnemonic
|
||||||
.as_bytes()
|
.as_bytes()
|
||||||
.try_into()
|
.try_into()
|
||||||
|
|
|
@ -56,7 +56,10 @@ 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::{InvalidData, SharksError, HUNK_VERSION, QRCODE_PROMPT, QRCODE_TIMEOUT};
|
use super::{
|
||||||
|
InvalidData, SharksError, HUNK_VERSION, QRCODE_COULDNT_READ, QRCODE_ERROR, QRCODE_PROMPT,
|
||||||
|
QRCODE_TIMEOUT,
|
||||||
|
};
|
||||||
|
|
||||||
// 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;
|
||||||
|
@ -472,14 +475,14 @@ pub fn decrypt(
|
||||||
#[cfg(feature = "qrcode")]
|
#[cfg(feature = "qrcode")]
|
||||||
{
|
{
|
||||||
pm.prompt_message(PromptMessage::Text(QRCODE_PROMPT.to_string()))?;
|
pm.prompt_message(PromptMessage::Text(QRCODE_PROMPT.to_string()))?;
|
||||||
if let Ok(Some(hex)) = keyfork_qrcode::scan_camera(std::time::Duration::from_secs(QRCODE_TIMEOUT), 0) {
|
if let Ok(Some(hex)) =
|
||||||
|
keyfork_qrcode::scan_camera(std::time::Duration::from_secs(QRCODE_TIMEOUT), 0)
|
||||||
|
{
|
||||||
let decoded_data = smex::decode(&hex)?;
|
let decoded_data = smex::decode(&hex)?;
|
||||||
let _ = nonce_data.insert(decoded_data[..12].try_into().map_err(|_| InvalidData)?);
|
let _ = nonce_data.insert(decoded_data[..12].try_into().map_err(|_| InvalidData)?);
|
||||||
let _ = pubkey_data.insert(decoded_data[12..].try_into().map_err(|_| InvalidData)?);
|
let _ = pubkey_data.insert(decoded_data[12..].try_into().map_err(|_| InvalidData)?);
|
||||||
} else {
|
} else {
|
||||||
pm.prompt_message(PromptMessage::Text(
|
pm.prompt_message(PromptMessage::Text(QRCODE_ERROR.to_string()))?;
|
||||||
"Unable to detect QR code, falling back to text".to_string(),
|
|
||||||
))?;
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -490,7 +493,7 @@ pub fn decrypt(
|
||||||
word_lengths: [9, 24],
|
word_lengths: [9, 24],
|
||||||
};
|
};
|
||||||
let [nonce_mnemonic, pubkey_mnemonic] =
|
let [nonce_mnemonic, pubkey_mnemonic] =
|
||||||
pm.prompt_validated_wordlist("Their words: ", &wordlist, 3, validator.to_fn())?;
|
pm.prompt_validated_wordlist(QRCODE_COULDNT_READ, &wordlist, 3, validator.to_fn())?;
|
||||||
|
|
||||||
let nonce = nonce_mnemonic
|
let nonce = nonce_mnemonic
|
||||||
.as_bytes()
|
.as_bytes()
|
||||||
|
@ -561,12 +564,20 @@ pub fn decrypt(
|
||||||
let mut qrcode_data = our_pubkey_mnemonic.to_bytes();
|
let mut qrcode_data = our_pubkey_mnemonic.to_bytes();
|
||||||
qrcode_data.extend(payload_mnemonic.as_bytes());
|
qrcode_data.extend(payload_mnemonic.as_bytes());
|
||||||
if let Ok(qrcode) = qrencode(&smex::encode(&qrcode_data), ErrorCorrection::Highest) {
|
if let Ok(qrcode) = qrencode(&smex::encode(&qrcode_data), ErrorCorrection::Highest) {
|
||||||
|
pm.prompt_message(PromptMessage::Text(
|
||||||
|
concat!(
|
||||||
|
"A QR code will be displayed after this prompt. ",
|
||||||
|
"Send the QR code back to the operator combining the shards. ",
|
||||||
|
"Nobody else should scan this QR code."
|
||||||
|
)
|
||||||
|
.to_string(),
|
||||||
|
))?;
|
||||||
pm.prompt_message(PromptMessage::Data(qrcode))?;
|
pm.prompt_message(PromptMessage::Data(qrcode))?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pm.prompt_message(PromptMessage::Text(format!(
|
pm.prompt_message(PromptMessage::Text(format!(
|
||||||
"Our words: {our_pubkey_mnemonic} {payload_mnemonic}"
|
"Upon request, these words should be sent: {our_pubkey_mnemonic} {payload_mnemonic}"
|
||||||
)))?;
|
)))?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
Loading…
Reference in New Issue