2024-01-16 02:44:48 +00:00
|
|
|
//! OpenPGP Shard functionality.
|
|
|
|
|
2023-10-19 22:06:34 +00:00
|
|
|
use std::{
|
|
|
|
collections::{HashMap, VecDeque},
|
2023-12-29 21:23:04 +00:00
|
|
|
io::{stdin, stdout, Read, Write},
|
2023-10-19 22:06:34 +00:00
|
|
|
path::Path,
|
|
|
|
str::FromStr,
|
|
|
|
};
|
|
|
|
|
2023-12-29 21:23:04 +00:00
|
|
|
use aes_gcm::{
|
2024-01-05 04:05:30 +00:00
|
|
|
aead::{consts::U12, Aead},
|
2024-01-07 04:23:03 +00:00
|
|
|
aes::cipher::InvalidLength,
|
2024-01-04 00:58:39 +00:00
|
|
|
Aes256Gcm, Error as AesError, KeyInit, Nonce,
|
2023-12-29 21:23:04 +00:00
|
|
|
};
|
2024-01-08 19:00:31 +00:00
|
|
|
use hkdf::{Hkdf, InvalidLength as HkdfInvalidLength};
|
2023-10-19 22:06:34 +00:00
|
|
|
use keyfork_derive_openpgp::derive_util::{
|
|
|
|
request::{DerivationAlgorithm, DerivationRequest},
|
|
|
|
DerivationPath,
|
|
|
|
};
|
2023-12-29 21:23:04 +00:00
|
|
|
use keyfork_mnemonic_util::{Mnemonic, MnemonicFromStrError, MnemonicGenerationError, Wordlist};
|
2024-01-10 20:34:29 +00:00
|
|
|
use keyfork_prompt::{
|
|
|
|
validators::{mnemonic::MnemonicSetValidator, Validator},
|
2024-01-12 00:49:56 +00:00
|
|
|
Error as PromptError, Message as PromptMessage, PromptHandler, Terminal,
|
2024-01-10 20:34:29 +00:00
|
|
|
};
|
2023-10-19 22:06:34 +00:00
|
|
|
use openpgp::{
|
|
|
|
armor::{Kind, Writer},
|
|
|
|
cert::{Cert, CertParser, ValidCert},
|
|
|
|
packet::{Packet, Tag, UserID, PKESK, SEIP},
|
2023-11-02 06:01:34 +00:00
|
|
|
parse::{
|
|
|
|
stream::{DecryptionHelper, DecryptorBuilder, VerificationHelper},
|
|
|
|
Parse,
|
|
|
|
},
|
2023-10-19 22:06:34 +00:00
|
|
|
policy::{NullPolicy, Policy, StandardPolicy},
|
|
|
|
serialize::{
|
2023-11-05 05:45:47 +00:00
|
|
|
stream::{ArbitraryWriter, Encryptor2, LiteralWriter, Message, Recipient, Signer},
|
2023-10-19 22:06:34 +00:00
|
|
|
Marshal,
|
|
|
|
},
|
|
|
|
types::KeyFlags,
|
2023-11-05 19:57:22 +00:00
|
|
|
Fingerprint, KeyID, PacketPile,
|
2023-10-19 22:06:34 +00:00
|
|
|
};
|
|
|
|
pub use sequoia_openpgp as openpgp;
|
2024-01-10 20:34:29 +00:00
|
|
|
use sha2::Sha256;
|
2023-10-19 22:06:34 +00:00
|
|
|
use sharks::{Share, Sharks};
|
2024-01-05 04:05:30 +00:00
|
|
|
use x25519_dalek::{EphemeralSecret, PublicKey};
|
2023-10-19 22:06:34 +00:00
|
|
|
|
|
|
|
mod keyring;
|
|
|
|
use keyring::Keyring;
|
|
|
|
|
2023-11-02 06:01:34 +00:00
|
|
|
mod smartcard;
|
|
|
|
use smartcard::SmartcardManager;
|
|
|
|
|
2023-12-20 18:14:24 +00:00
|
|
|
/// Shard metadata verson 1:
|
|
|
|
/// 1 byte: Version
|
|
|
|
/// 1 byte: Threshold
|
2024-01-07 04:23:03 +00:00
|
|
|
/// Packet Pile of Certs
|
2023-12-20 18:14:24 +00:00
|
|
|
const SHARD_METADATA_VERSION: u8 = 1;
|
|
|
|
const SHARD_METADATA_OFFSET: usize = 2;
|
|
|
|
|
2024-01-12 00:49:56 +00:00
|
|
|
use super::{InvalidData, SharksError, HUNK_VERSION};
|
2024-01-04 00:58:39 +00:00
|
|
|
|
|
|
|
// 256 bit share is 49 bytes + some amount of hunk bytes, gives us reasonable padding
|
|
|
|
const ENC_LEN: u8 = 4 * 16;
|
2024-01-01 21:58:15 +00:00
|
|
|
|
2024-01-16 02:44:48 +00:00
|
|
|
/// Errors encountered while performing operations using OpenPGP.
|
2023-11-05 19:57:22 +00:00
|
|
|
#[derive(Debug, thiserror::Error)]
|
|
|
|
pub enum Error {
|
2024-01-16 02:44:48 +00:00
|
|
|
/// Errors encountered while creating or combining shares.
|
2024-01-05 04:11:15 +00:00
|
|
|
#[error("{0}")]
|
|
|
|
Sharks(#[from] SharksError),
|
2023-10-19 22:06:34 +00:00
|
|
|
|
2024-01-16 02:44:48 +00:00
|
|
|
/// Unable to decrypt a share.
|
2024-01-01 21:58:15 +00:00
|
|
|
#[error("Error decrypting share: {0}")]
|
|
|
|
SymDecryptShare(#[from] AesError),
|
|
|
|
|
2024-01-16 02:44:48 +00:00
|
|
|
/// The generated AES key is of an invalid length.
|
2024-01-07 04:23:03 +00:00
|
|
|
#[error("Invalid length of AES key: {0}")]
|
|
|
|
AesLength(#[from] InvalidLength),
|
|
|
|
|
2024-01-16 02:44:48 +00:00
|
|
|
/// The HKDF function was given an input of an invalid length.
|
2024-01-08 19:00:31 +00:00
|
|
|
#[error("Invalid KDF length: {0}")]
|
|
|
|
HkdfLength(#[from] HkdfInvalidLength),
|
|
|
|
|
2024-01-16 02:44:48 +00:00
|
|
|
/// The secret did not match the previously-known secret fingerprint.
|
2023-11-05 19:57:22 +00:00
|
|
|
#[error("Derived secret hash {0} != expected {1}")]
|
|
|
|
InvalidSecret(Fingerprint, Fingerprint),
|
|
|
|
|
2024-01-16 02:44:48 +00:00
|
|
|
/// An error occurred while performing an OpenPGP operation.
|
2023-11-05 19:57:22 +00:00
|
|
|
#[error("OpenPGP error: {0}")]
|
|
|
|
Sequoia(#[source] anyhow::Error),
|
|
|
|
|
2024-01-16 02:44:48 +00:00
|
|
|
/// An IO error occurred while performing an OpenPGP operation.
|
2023-11-05 19:57:22 +00:00
|
|
|
#[error("OpenPGP IO error: {0}")]
|
|
|
|
SequoiaIo(#[source] std::io::Error),
|
|
|
|
|
2024-01-16 02:44:48 +00:00
|
|
|
/// An error occurred while using a keyring.
|
2023-11-05 19:57:22 +00:00
|
|
|
#[error("Keyring error: {0}")]
|
2023-11-05 22:21:54 +00:00
|
|
|
Keyring(#[from] keyring::Error),
|
2023-10-19 22:06:34 +00:00
|
|
|
|
2024-01-16 02:44:48 +00:00
|
|
|
/// An error occurred while using a smartcard.
|
2023-11-05 19:57:22 +00:00
|
|
|
#[error("Smartcard error: {0}")]
|
2023-11-05 22:21:54 +00:00
|
|
|
Smartcard(#[from] smartcard::Error),
|
2023-10-19 22:06:34 +00:00
|
|
|
|
2024-01-16 02:44:48 +00:00
|
|
|
/// An error occurred while displaying a prompt.
|
2023-12-29 21:23:04 +00:00
|
|
|
#[error("Prompt error: {0}")]
|
|
|
|
Prompt(#[from] PromptError),
|
|
|
|
|
2024-01-16 02:44:48 +00:00
|
|
|
/// An error occurred while generating a mnemonic.
|
2023-12-29 21:23:04 +00:00
|
|
|
#[error("Mnemonic generation error: {0}")]
|
|
|
|
MnemonicGeneration(#[from] MnemonicGenerationError),
|
|
|
|
|
2024-01-16 02:44:48 +00:00
|
|
|
/// An error occurred while parsing a mnemonic.
|
2023-12-29 21:23:04 +00:00
|
|
|
#[error("Mnemonic parse error: {0}")]
|
|
|
|
MnemonicFromStr(#[from] MnemonicFromStrError),
|
|
|
|
|
2024-01-16 02:44:48 +00:00
|
|
|
/// An error occurred while converting mnemonic data.
|
2024-01-07 04:23:03 +00:00
|
|
|
#[error("{0}")]
|
2024-01-12 00:49:56 +00:00
|
|
|
InvalidMnemonicData(#[from] InvalidData),
|
2024-01-07 04:23:03 +00:00
|
|
|
|
2024-01-16 02:44:48 +00:00
|
|
|
/// An IO error occurred.
|
2023-11-05 19:57:22 +00:00
|
|
|
#[error("IO error: {0}")]
|
|
|
|
Io(#[source] std::io::Error),
|
|
|
|
|
2024-01-16 02:44:48 +00:00
|
|
|
/// An error occurred while parsing a derivation path.
|
2023-11-05 19:57:22 +00:00
|
|
|
#[error("Derivation path: {0}")]
|
|
|
|
DerivationPath(#[from] keyfork_derive_openpgp::derive_util::path::Error),
|
|
|
|
|
2024-01-16 02:44:48 +00:00
|
|
|
/// An error occurred while requesting derivation.
|
2023-11-05 19:57:22 +00:00
|
|
|
#[error("Derivation request: {0}")]
|
|
|
|
DerivationRequest(#[from] keyfork_derive_openpgp::derive_util::request::DerivationError),
|
|
|
|
|
2024-01-16 02:44:48 +00:00
|
|
|
/// An error occurred while decoding hex.
|
2024-01-12 00:49:56 +00:00
|
|
|
#[error("Unable to decode hex: {0}")]
|
|
|
|
HexDecode(#[from] smex::DecodeError),
|
|
|
|
|
2024-01-16 02:44:48 +00:00
|
|
|
/// An error occurred while creating an OpenPGP cert.
|
2023-11-05 19:57:22 +00:00
|
|
|
#[error("Keyfork OpenPGP: {0}")]
|
|
|
|
KeyforkOpenPGP(#[from] keyfork_derive_openpgp::Error),
|
|
|
|
}
|
|
|
|
|
2024-01-16 02:44:48 +00:00
|
|
|
#[allow(missing_docs)]
|
2023-11-05 19:57:22 +00:00
|
|
|
pub type Result<T, E = Error> = std::result::Result<T, E>;
|
2023-10-19 22:06:34 +00:00
|
|
|
|
2024-01-16 02:44:48 +00:00
|
|
|
/// An OpenPGP encrypted message and public-key-encrypted-secret-key packets.
|
2023-10-19 22:06:34 +00:00
|
|
|
#[derive(Debug, Clone)]
|
|
|
|
pub struct EncryptedMessage {
|
|
|
|
pkesks: Vec<PKESK>,
|
|
|
|
message: SEIP,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl EncryptedMessage {
|
2024-01-16 02:44:48 +00:00
|
|
|
/// Create a new EncryptedMessage from known parts.
|
2023-12-19 14:55:22 +00:00
|
|
|
pub fn new(pkesks: &mut Vec<PKESK>, seip: SEIP) -> Self {
|
2023-10-19 22:06:34 +00:00
|
|
|
Self {
|
|
|
|
pkesks: std::mem::take(pkesks),
|
|
|
|
message: seip,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-01-16 02:44:48 +00:00
|
|
|
/// Decrypt the message with a Sequoia policy and decryptor.
|
|
|
|
///
|
|
|
|
/// This method creates a container containing the packets and passes the serialized container
|
|
|
|
/// to a DecryptorBuilder, which is used to decrypt the message.
|
|
|
|
///
|
|
|
|
/// # Errors
|
|
|
|
/// The method may return an error if it is unable to rebuild the message to decrypt or if it
|
|
|
|
/// is unable to decrypt the message.
|
2023-11-02 06:01:34 +00:00
|
|
|
pub fn decrypt_with<H>(&self, policy: &'_ dyn Policy, decryptor: H) -> Result<Vec<u8>>
|
|
|
|
where
|
|
|
|
H: VerificationHelper + DecryptionHelper,
|
|
|
|
{
|
2023-10-19 22:06:34 +00:00
|
|
|
let mut packets = vec![];
|
|
|
|
|
|
|
|
for pkesk in &self.pkesks {
|
|
|
|
let mut packet = vec![];
|
2023-11-05 19:57:22 +00:00
|
|
|
pkesk.serialize(&mut packet).map_err(Error::Sequoia)?;
|
2023-10-19 22:06:34 +00:00
|
|
|
let message = Message::new(&mut packets);
|
2023-11-05 19:57:22 +00:00
|
|
|
let mut message = ArbitraryWriter::new(message, Tag::PKESK).map_err(Error::Sequoia)?;
|
|
|
|
message.write_all(&packet).map_err(Error::SequoiaIo)?;
|
|
|
|
message.finalize().map_err(Error::Sequoia)?;
|
2023-10-19 22:06:34 +00:00
|
|
|
}
|
|
|
|
let mut packet = vec![];
|
2023-11-05 19:57:22 +00:00
|
|
|
self.message
|
|
|
|
.serialize(&mut packet)
|
|
|
|
.map_err(Error::Sequoia)?;
|
2023-10-19 22:06:34 +00:00
|
|
|
let message = Message::new(&mut packets);
|
2023-11-05 19:57:22 +00:00
|
|
|
let mut message = ArbitraryWriter::new(message, Tag::SEIP).map_err(Error::Sequoia)?;
|
|
|
|
message.write_all(&packet).map_err(Error::SequoiaIo)?;
|
|
|
|
message.finalize().map_err(Error::Sequoia)?;
|
2023-10-19 22:06:34 +00:00
|
|
|
|
2023-11-05 19:57:22 +00:00
|
|
|
let mut decryptor = DecryptorBuilder::from_bytes(&packets)
|
|
|
|
.map_err(Error::Sequoia)?
|
|
|
|
.with_policy(policy, None, decryptor)
|
|
|
|
.map_err(Error::Sequoia)?;
|
2023-10-19 22:06:34 +00:00
|
|
|
|
|
|
|
let mut content = vec![];
|
2023-11-05 19:57:22 +00:00
|
|
|
decryptor
|
|
|
|
.read_to_end(&mut content)
|
|
|
|
.map_err(Error::SequoiaIo)?;
|
2023-10-19 22:06:34 +00:00
|
|
|
Ok(content)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-01-16 02:44:48 +00:00
|
|
|
/// Read all OpenPGP certificates in a path and return a [`Vec`] of them. Certificates are read
|
|
|
|
/// from a file, or from files one level deep in a directory.
|
|
|
|
///
|
|
|
|
/// # Errors
|
|
|
|
/// The function may return an error if it is unable to read the directory or if Sequoia is unable
|
|
|
|
/// to load certificates from the file.
|
2023-10-19 22:06:34 +00:00
|
|
|
pub fn discover_certs(path: impl AsRef<Path>) -> Result<Vec<Cert>> {
|
|
|
|
let path = path.as_ref();
|
|
|
|
|
|
|
|
if path.is_file() {
|
|
|
|
let mut vec = vec![];
|
2023-11-05 19:57:22 +00:00
|
|
|
for cert in CertParser::from_file(path).map_err(Error::Sequoia)? {
|
|
|
|
vec.push(cert.map_err(Error::Sequoia)?);
|
2023-10-19 22:06:34 +00:00
|
|
|
}
|
|
|
|
Ok(vec)
|
|
|
|
} else {
|
|
|
|
let mut vec = vec![];
|
|
|
|
for entry in path
|
2023-11-05 19:57:22 +00:00
|
|
|
.read_dir()
|
|
|
|
.map_err(Error::Io)?
|
2023-10-19 22:06:34 +00:00
|
|
|
.filter_map(Result::ok)
|
|
|
|
.filter(|p| p.path().is_file())
|
|
|
|
{
|
2023-11-05 19:57:22 +00:00
|
|
|
vec.push(Cert::from_file(entry.path()).map_err(Error::Sequoia)?);
|
2023-10-19 22:06:34 +00:00
|
|
|
}
|
|
|
|
Ok(vec)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-01-16 02:44:48 +00:00
|
|
|
/// Parse messages from a type implementing [`Read`] and store them as [`EncryptedMessage`].
|
2024-01-07 04:23:03 +00:00
|
|
|
///
|
2024-01-16 02:44:48 +00:00
|
|
|
/// # Errors
|
|
|
|
/// The function may return an error if the reader has run out of data or if the data is not
|
|
|
|
/// properly formatted OpenPGP messages.
|
|
|
|
///
|
|
|
|
/// # Panics
|
2024-01-07 04:23:03 +00:00
|
|
|
/// When given packets that are not a list of PKESK packets and SEIP packets, the function panics.
|
|
|
|
/// The `split` utility should never give packets that are not in this format.
|
2023-10-20 01:10:02 +00:00
|
|
|
pub fn parse_messages(reader: impl Read + Send + Sync) -> Result<VecDeque<EncryptedMessage>> {
|
2023-10-19 22:06:34 +00:00
|
|
|
let mut pkesks = Vec::new();
|
|
|
|
let mut encrypted_messages = VecDeque::new();
|
|
|
|
|
2023-11-05 19:57:22 +00:00
|
|
|
for packet in PacketPile::from_reader(reader)
|
|
|
|
.map_err(Error::Sequoia)?
|
|
|
|
.into_children()
|
|
|
|
{
|
2023-10-19 22:06:34 +00:00
|
|
|
match packet {
|
|
|
|
Packet::PKESK(p) => pkesks.push(p),
|
|
|
|
Packet::SEIP(s) => {
|
2023-12-19 14:55:22 +00:00
|
|
|
encrypted_messages.push_back(EncryptedMessage::new(&mut pkesks, s));
|
2023-10-19 22:06:34 +00:00
|
|
|
}
|
|
|
|
s => {
|
|
|
|
panic!("Invalid variant found: {}", s.tag());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(encrypted_messages)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn get_encryption_keys<'a>(
|
|
|
|
cert: &'a ValidCert,
|
|
|
|
) -> openpgp::cert::prelude::ValidKeyAmalgamationIter<
|
|
|
|
'a,
|
|
|
|
openpgp::packet::key::PublicParts,
|
|
|
|
openpgp::packet::key::UnspecifiedRole,
|
|
|
|
> {
|
|
|
|
cert.keys()
|
|
|
|
.alive()
|
|
|
|
.revoked(false)
|
|
|
|
.supported()
|
|
|
|
.for_storage_encryption()
|
|
|
|
}
|
|
|
|
|
|
|
|
fn get_decryption_keys<'a>(
|
|
|
|
cert: &'a ValidCert,
|
|
|
|
) -> openpgp::cert::prelude::ValidKeyAmalgamationIter<
|
|
|
|
'a,
|
|
|
|
openpgp::packet::key::SecretParts,
|
|
|
|
openpgp::packet::key::UnspecifiedRole,
|
|
|
|
> {
|
|
|
|
cert.keys()
|
|
|
|
/*
|
|
|
|
.alive()
|
|
|
|
.revoked(false)
|
|
|
|
.supported()
|
|
|
|
*/
|
|
|
|
.for_storage_encryption()
|
|
|
|
.secret()
|
|
|
|
}
|
|
|
|
|
2023-12-26 20:17:14 +00:00
|
|
|
fn decode_metadata_v1(buf: &[u8]) -> Result<(u8, Cert, Vec<Cert>)> {
|
2023-12-20 18:14:24 +00:00
|
|
|
assert_eq!(
|
2023-12-26 20:17:14 +00:00
|
|
|
SHARD_METADATA_VERSION, buf[0],
|
|
|
|
"Incompatible metadata version"
|
2023-12-20 18:14:24 +00:00
|
|
|
);
|
2023-12-26 20:17:14 +00:00
|
|
|
let threshold = buf[1];
|
2023-12-20 18:14:24 +00:00
|
|
|
|
|
|
|
let mut cert_parser =
|
2023-12-26 20:17:14 +00:00
|
|
|
CertParser::from_bytes(&buf[SHARD_METADATA_OFFSET..]).map_err(Error::Sequoia)?;
|
|
|
|
|
2023-10-19 22:06:34 +00:00
|
|
|
let root_cert = match cert_parser.next() {
|
|
|
|
Some(Ok(c)) => c,
|
2023-12-26 20:17:14 +00:00
|
|
|
Some(Err(e)) => return Err(Error::Sequoia(e)),
|
|
|
|
None => panic!("No data found"),
|
2023-10-19 22:06:34 +00:00
|
|
|
};
|
2023-12-26 20:17:14 +00:00
|
|
|
|
2023-11-05 19:57:22 +00:00
|
|
|
let certs = cert_parser
|
|
|
|
.collect::<openpgp::Result<Vec<_>>>()
|
|
|
|
.map_err(Error::Sequoia)?;
|
2023-10-19 22:06:34 +00:00
|
|
|
|
2023-12-26 20:17:14 +00:00
|
|
|
Ok((threshold, root_cert, certs))
|
|
|
|
}
|
|
|
|
|
|
|
|
// NOTE: When using single-decryptor mechanism, use this method with `threshold = 1` to return a
|
|
|
|
// single message.
|
|
|
|
fn decrypt_with_manager(
|
|
|
|
threshold: u8,
|
|
|
|
messages: &mut HashMap<KeyID, EncryptedMessage>,
|
2023-12-26 20:45:11 +00:00
|
|
|
certs: &[Cert],
|
2024-01-07 04:23:03 +00:00
|
|
|
policy: &dyn Policy,
|
2023-12-26 20:17:14 +00:00
|
|
|
manager: &mut SmartcardManager,
|
|
|
|
) -> Result<HashMap<KeyID, Vec<u8>>> {
|
|
|
|
let mut decrypted_messages = HashMap::new();
|
|
|
|
|
|
|
|
while threshold as usize - decrypted_messages.len() > 0 {
|
|
|
|
// Build list of fingerprints that haven't yet been used for decrypting
|
|
|
|
let mut cert_by_fingerprint = HashMap::new();
|
|
|
|
let mut unused_fingerprints = vec![];
|
|
|
|
for valid_cert in certs
|
|
|
|
.iter()
|
|
|
|
.filter(|cert| !decrypted_messages.contains_key(&cert.keyid()))
|
2024-01-07 04:23:03 +00:00
|
|
|
.map(|cert| cert.with_policy(policy, None))
|
2023-12-26 20:17:14 +00:00
|
|
|
{
|
|
|
|
let valid_cert = valid_cert.map_err(Error::Sequoia)?;
|
|
|
|
let fp = valid_cert
|
|
|
|
.keys()
|
|
|
|
.for_storage_encryption()
|
|
|
|
.map(|k| k.fingerprint())
|
|
|
|
.collect::<Vec<_>>();
|
|
|
|
for fp in &fp {
|
|
|
|
cert_by_fingerprint.insert(fp.clone(), valid_cert.keyid());
|
|
|
|
}
|
|
|
|
unused_fingerprints.extend(fp.into_iter());
|
|
|
|
}
|
|
|
|
|
|
|
|
// Iterate over all fingerprints and use key_by_fingerprints to assoc with Enc. Message
|
|
|
|
if let Some(fp) = manager.load_any_fingerprint(unused_fingerprints)? {
|
|
|
|
let cert_keyid = cert_by_fingerprint.get(&fp).unwrap().clone();
|
|
|
|
if let Some(message) = messages.remove(&cert_keyid) {
|
2024-01-07 04:23:03 +00:00
|
|
|
let message = message.decrypt_with(policy, &mut *manager)?;
|
2023-12-26 20:17:14 +00:00
|
|
|
decrypted_messages.insert(cert_keyid, message);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(decrypted_messages)
|
|
|
|
}
|
|
|
|
|
|
|
|
// NOTE: When using single-decryptor mechanism, only a single key should be provided in Keyring to
|
|
|
|
// decrypt messages with.
|
|
|
|
fn decrypt_with_keyring(
|
2023-12-26 20:45:11 +00:00
|
|
|
messages: &mut HashMap<KeyID, EncryptedMessage>,
|
2023-12-26 20:17:14 +00:00
|
|
|
certs: &[Cert],
|
|
|
|
policy: &NullPolicy,
|
|
|
|
keyring: &mut Keyring,
|
|
|
|
) -> Result<HashMap<KeyID, Vec<u8>>, Error> {
|
|
|
|
let mut decrypted_messages = HashMap::new();
|
|
|
|
|
|
|
|
for valid_cert in certs.iter().map(|cert| cert.with_policy(policy, None)) {
|
2023-11-05 19:57:22 +00:00
|
|
|
let valid_cert = valid_cert.map_err(Error::Sequoia)?;
|
2023-10-19 22:06:34 +00:00
|
|
|
let Some(secret_cert) = keyring.get_cert_for_primary_keyid(&valid_cert.keyid()) else {
|
|
|
|
continue;
|
|
|
|
};
|
2023-11-05 19:57:22 +00:00
|
|
|
let secret_cert = secret_cert
|
2023-12-26 20:17:14 +00:00
|
|
|
.with_policy(policy, None)
|
2023-11-05 19:57:22 +00:00
|
|
|
.map_err(Error::Sequoia)?;
|
2023-10-19 22:06:34 +00:00
|
|
|
let keys = get_decryption_keys(&secret_cert).collect::<Vec<_>>();
|
|
|
|
if !keys.is_empty() {
|
|
|
|
if let Some(message) = messages.get_mut(&valid_cert.keyid()) {
|
|
|
|
for (pkesk, key) in message.pkesks.iter_mut().zip(keys) {
|
|
|
|
pkesk.set_recipient(key.keyid());
|
|
|
|
}
|
|
|
|
// we have a pkesk, decrypt via keyring
|
2023-11-05 22:26:19 +00:00
|
|
|
decrypted_messages.insert(
|
|
|
|
valid_cert.keyid(),
|
2023-12-26 20:17:14 +00:00
|
|
|
message.decrypt_with(policy, &mut *keyring)?,
|
2023-11-05 22:26:19 +00:00
|
|
|
);
|
2023-10-19 22:06:34 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-12-26 20:17:14 +00:00
|
|
|
Ok(decrypted_messages)
|
|
|
|
}
|
|
|
|
|
2023-12-26 20:45:11 +00:00
|
|
|
fn decrypt_metadata(
|
|
|
|
message: &EncryptedMessage,
|
|
|
|
policy: &NullPolicy,
|
|
|
|
keyring: &mut Keyring,
|
|
|
|
manager: &mut SmartcardManager,
|
|
|
|
) -> Result<Vec<u8>> {
|
|
|
|
Ok(if keyring.is_empty() {
|
|
|
|
manager.load_any_card()?;
|
|
|
|
message.decrypt_with(policy, manager)?
|
|
|
|
} else {
|
|
|
|
message.decrypt_with(policy, keyring)?
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2023-12-29 21:23:04 +00:00
|
|
|
fn decrypt_one(
|
2023-12-26 20:45:11 +00:00
|
|
|
messages: Vec<EncryptedMessage>,
|
2023-12-26 23:09:11 +00:00
|
|
|
certs: &[Cert],
|
2024-01-07 04:23:03 +00:00
|
|
|
metadata: &EncryptedMessage,
|
2024-01-01 21:58:15 +00:00
|
|
|
) -> Result<(Vec<u8>, u8, Cert)> {
|
2023-12-26 20:45:11 +00:00
|
|
|
let policy = NullPolicy::new();
|
|
|
|
|
2023-12-26 23:09:11 +00:00
|
|
|
let mut keyring = Keyring::new(certs)?;
|
|
|
|
let mut manager = SmartcardManager::new()?;
|
|
|
|
|
2024-01-07 04:23:03 +00:00
|
|
|
let content = decrypt_metadata(metadata, &policy, &mut keyring, &mut manager)?;
|
2023-12-26 20:45:11 +00:00
|
|
|
|
2024-01-01 21:58:15 +00:00
|
|
|
let (threshold, root_cert, certs) = decode_metadata_v1(&content)?;
|
2023-12-26 20:45:11 +00:00
|
|
|
|
|
|
|
keyring.set_root_cert(root_cert.clone());
|
2024-01-01 21:58:15 +00:00
|
|
|
manager.set_root_cert(root_cert.clone());
|
2023-12-26 20:45:11 +00:00
|
|
|
|
|
|
|
let mut messages: HashMap<KeyID, EncryptedMessage> =
|
2024-01-07 04:23:03 +00:00
|
|
|
certs.iter().map(Cert::keyid).zip(messages).collect();
|
2023-12-26 20:45:11 +00:00
|
|
|
|
2023-12-26 23:09:11 +00:00
|
|
|
let decrypted_messages = decrypt_with_keyring(&mut messages, &certs, &policy, &mut keyring)?;
|
2023-12-26 20:45:11 +00:00
|
|
|
|
|
|
|
if let Some(message) = decrypted_messages.into_values().next() {
|
2024-01-01 21:58:15 +00:00
|
|
|
return Ok((message, threshold, root_cert));
|
2023-12-26 20:45:11 +00:00
|
|
|
}
|
|
|
|
|
2024-01-07 04:23:03 +00:00
|
|
|
let decrypted_messages = decrypt_with_manager(1, &mut messages, &certs, &policy, &mut manager)?;
|
2023-12-26 20:45:11 +00:00
|
|
|
|
|
|
|
if let Some(message) = decrypted_messages.into_values().next() {
|
2024-01-01 21:58:15 +00:00
|
|
|
return Ok((message, threshold, root_cert));
|
2023-12-26 20:45:11 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
unreachable!("smartcard manager should always decrypt")
|
|
|
|
}
|
|
|
|
|
2024-01-16 02:44:48 +00:00
|
|
|
/// Decrypt a single shard, encrypt to a remote operator, and present the transport shard as a QR
|
|
|
|
/// code and mnemonic to be sent to the remote operator.
|
|
|
|
///
|
|
|
|
/// # Errors
|
|
|
|
///
|
|
|
|
/// The function may error if an error occurs while displaying a prompt or while decrypting the
|
|
|
|
/// shard. An error will not be returned if the camera has a hardware error while scanning a QR
|
|
|
|
/// code; instead, a mnemonic prompt will be used.
|
|
|
|
///
|
2024-01-07 04:23:03 +00:00
|
|
|
/// # Panics
|
|
|
|
///
|
|
|
|
/// The function may panic if a share is decrypted but has a length larger than 256 bits. This is
|
|
|
|
/// atypical usage and should not be encountered in normal usage, unless something that is not a
|
|
|
|
/// Keyfork seed has been fed into [`split`].
|
2023-12-29 21:23:04 +00:00
|
|
|
pub fn decrypt(
|
|
|
|
certs: &[Cert],
|
2024-01-07 04:23:03 +00:00
|
|
|
metadata: &EncryptedMessage,
|
2023-12-29 21:23:04 +00:00
|
|
|
encrypted_messages: &[EncryptedMessage],
|
|
|
|
) -> Result<()> {
|
2024-01-11 03:35:31 +00:00
|
|
|
let mut pm = Terminal::new(stdin(), stdout())?;
|
2023-12-29 21:23:04 +00:00
|
|
|
let wordlist = Wordlist::default();
|
2024-01-12 00:49:56 +00:00
|
|
|
|
|
|
|
let mut nonce_data: Option<[u8; 12]> = None;
|
|
|
|
let mut pubkey_data: Option<[u8; 32]> = None;
|
|
|
|
|
|
|
|
#[cfg(feature = "qrcode")]
|
|
|
|
{
|
|
|
|
pm.prompt_message(PromptMessage::Text(
|
|
|
|
"Press enter, then present QR code to camera".to_string(),
|
|
|
|
))?;
|
|
|
|
if let Ok(Some(hex)) = keyfork_qrcode::scan_camera(std::time::Duration::from_secs(30), 0) {
|
|
|
|
let decoded_data = smex::decode(&hex)?;
|
|
|
|
let _ = nonce_data.insert(decoded_data[..12].try_into().map_err(|_| InvalidData)?);
|
|
|
|
let _ = pubkey_data.insert(decoded_data[12..].try_into().map_err(|_| InvalidData)?);
|
|
|
|
} else {
|
|
|
|
pm.prompt_message(PromptMessage::Text(
|
|
|
|
"Unable to detect QR code, falling back to text".to_string(),
|
|
|
|
))?;
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
let (nonce, pubkey) = match (nonce_data, pubkey_data) {
|
|
|
|
(Some(nonce), Some(pubkey)) => (nonce, pubkey),
|
|
|
|
_ => {
|
|
|
|
let validator = MnemonicSetValidator {
|
|
|
|
word_lengths: [9, 24],
|
|
|
|
};
|
|
|
|
let [nonce_mnemonic, pubkey_mnemonic] =
|
|
|
|
pm.prompt_validated_wordlist("Their words: ", &wordlist, 3, validator.to_fn())?;
|
|
|
|
|
|
|
|
let nonce = nonce_mnemonic
|
2024-01-19 04:50:23 +00:00
|
|
|
.as_bytes()
|
2024-01-12 00:49:56 +00:00
|
|
|
.try_into()
|
|
|
|
.map_err(|_| InvalidData)?;
|
|
|
|
let pubkey = pubkey_mnemonic
|
2024-01-19 04:50:23 +00:00
|
|
|
.as_bytes()
|
2024-01-12 00:49:56 +00:00
|
|
|
.try_into()
|
|
|
|
.map_err(|_| InvalidData)?;
|
|
|
|
(nonce, pubkey)
|
|
|
|
}
|
2024-01-10 20:34:29 +00:00
|
|
|
};
|
|
|
|
|
2024-01-12 00:49:56 +00:00
|
|
|
let nonce = Nonce::<U12>::from_slice(&nonce);
|
2023-12-29 21:23:04 +00:00
|
|
|
|
|
|
|
let our_key = EphemeralSecret::random();
|
2024-01-12 00:49:56 +00:00
|
|
|
let our_pubkey_mnemonic =
|
2023-12-29 21:23:04 +00:00
|
|
|
Mnemonic::from_entropy(PublicKey::from(&our_key).as_bytes(), Default::default())?;
|
|
|
|
|
2024-01-12 00:49:56 +00:00
|
|
|
let shared_secret = our_key.diffie_hellman(&PublicKey::from(pubkey)).to_bytes();
|
2023-12-29 21:23:04 +00:00
|
|
|
|
2024-01-07 04:23:03 +00:00
|
|
|
let (mut share, threshold, ..) = decrypt_one(encrypted_messages.to_vec(), certs, metadata)?;
|
2024-01-04 00:58:39 +00:00
|
|
|
share.insert(0, HUNK_VERSION);
|
|
|
|
share.insert(1, threshold);
|
|
|
|
assert!(
|
|
|
|
share.len() <= ENC_LEN as usize,
|
|
|
|
"invalid share length (too long, max {ENC_LEN} bytes)"
|
|
|
|
);
|
2023-12-29 21:23:04 +00:00
|
|
|
|
2024-01-08 19:00:31 +00:00
|
|
|
let hkdf = Hkdf::<Sha256>::new(None, &shared_secret);
|
|
|
|
let mut hkdf_output = [0u8; 256 / 8];
|
|
|
|
hkdf.expand(&[], &mut hkdf_output)?;
|
2024-01-10 20:34:29 +00:00
|
|
|
let shared_key = Aes256Gcm::new_from_slice(&hkdf_output)?;
|
2024-01-08 19:00:31 +00:00
|
|
|
|
2024-01-12 00:49:56 +00:00
|
|
|
let bytes = shared_key.encrypt(nonce, share.as_slice())?;
|
|
|
|
shared_key.decrypt(nonce, &bytes[..])?;
|
2024-01-01 21:58:15 +00:00
|
|
|
|
|
|
|
// NOTE: Padding length is less than u8::MAX because ENC_LEN < u8::MAX
|
|
|
|
// NOTE: This previously used a single value as the padding byte, but resulted in
|
|
|
|
// difficulty when entering in prompts manually, as one's place could be lost due to repeated
|
|
|
|
// keywords. This is done below by having sequentially increasing numbers up to but not
|
|
|
|
// including the last byte.
|
2024-01-07 04:23:03 +00:00
|
|
|
#[allow(clippy::assertions_on_constants)]
|
|
|
|
{
|
|
|
|
assert!(ENC_LEN < u8::MAX, "padding byte can be u8");
|
|
|
|
}
|
|
|
|
#[allow(clippy::cast_possible_truncation)]
|
2024-01-01 21:58:15 +00:00
|
|
|
let mut out_bytes = [bytes.len() as u8; ENC_LEN as usize];
|
2023-12-29 21:23:04 +00:00
|
|
|
assert!(
|
|
|
|
bytes.len() < out_bytes.len(),
|
|
|
|
"encrypted payload larger than acceptable limit"
|
|
|
|
);
|
|
|
|
out_bytes[..bytes.len()].clone_from_slice(&bytes);
|
2024-01-07 04:23:03 +00:00
|
|
|
#[allow(clippy::cast_possible_truncation)]
|
|
|
|
for (i, byte) in (out_bytes[bytes.len()..(ENC_LEN as usize - 1)])
|
|
|
|
.iter_mut()
|
2024-01-01 21:58:15 +00:00
|
|
|
.enumerate()
|
|
|
|
{
|
|
|
|
*byte = (i % u8::MAX as usize) as u8;
|
|
|
|
}
|
2023-12-29 21:23:04 +00:00
|
|
|
|
|
|
|
// safety: size of out_bytes is constant and always % 4 == 0
|
2024-01-12 00:49:56 +00:00
|
|
|
let payload_mnemonic = unsafe { Mnemonic::from_raw_entropy(&out_bytes, Default::default()) };
|
|
|
|
|
|
|
|
#[cfg(feature = "qrcode")]
|
|
|
|
{
|
|
|
|
use keyfork_qrcode::{qrencode, ErrorCorrection};
|
2024-01-19 04:50:23 +00:00
|
|
|
let mut qrcode_data = our_pubkey_mnemonic.to_bytes();
|
|
|
|
qrcode_data.extend(payload_mnemonic.as_bytes());
|
2024-02-02 06:23:37 +00:00
|
|
|
if let Ok(qrcode) = qrencode(&smex::encode(&qrcode_data), ErrorCorrection::Highest) {
|
2024-01-12 00:49:56 +00:00
|
|
|
pm.prompt_message(PromptMessage::Data(qrcode))?;
|
|
|
|
}
|
|
|
|
}
|
2023-12-29 21:23:04 +00:00
|
|
|
|
2024-01-11 04:28:56 +00:00
|
|
|
pm.prompt_message(PromptMessage::Text(format!(
|
2024-01-12 00:49:56 +00:00
|
|
|
"Our words: {our_pubkey_mnemonic} {payload_mnemonic}"
|
2024-01-04 00:58:39 +00:00
|
|
|
)))?;
|
2023-12-29 21:23:04 +00:00
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2024-01-16 02:44:48 +00:00
|
|
|
/// Combine mulitple shards into a secret.
|
|
|
|
///
|
|
|
|
/// # Errors
|
|
|
|
/// The function may return an error if an error occurs while decrypting shards, parsing shards, or
|
|
|
|
/// combining the shards into a secret.
|
2023-12-26 20:17:14 +00:00
|
|
|
pub fn combine(
|
|
|
|
certs: Vec<Cert>,
|
2024-01-07 04:23:03 +00:00
|
|
|
metadata: &EncryptedMessage,
|
2023-12-26 20:17:14 +00:00
|
|
|
messages: Vec<EncryptedMessage>,
|
|
|
|
mut output: impl Write,
|
|
|
|
) -> Result<()> {
|
|
|
|
// Be as liberal as possible when decrypting.
|
|
|
|
// We don't want to invalidate someone's keys just because the old sig expired.
|
|
|
|
let policy = NullPolicy::new();
|
|
|
|
|
|
|
|
let mut keyring = Keyring::new(certs)?;
|
|
|
|
let mut manager = SmartcardManager::new()?;
|
2024-01-07 04:23:03 +00:00
|
|
|
let content = decrypt_metadata(metadata, &policy, &mut keyring, &mut manager)?;
|
2023-12-26 20:17:14 +00:00
|
|
|
|
|
|
|
let (threshold, root_cert, certs) = decode_metadata_v1(&content)?;
|
|
|
|
|
|
|
|
keyring.set_root_cert(root_cert.clone());
|
2024-01-01 21:58:15 +00:00
|
|
|
manager.set_root_cert(root_cert.clone());
|
2023-12-26 20:17:14 +00:00
|
|
|
|
|
|
|
// Generate a controlled binding from certificates to encrypted messages. This is stable
|
|
|
|
// because we control the order packets are encrypted and certificates are stored.
|
|
|
|
|
|
|
|
let mut messages: HashMap<KeyID, EncryptedMessage> =
|
2024-01-07 04:23:03 +00:00
|
|
|
certs.iter().map(Cert::keyid).zip(messages).collect();
|
2023-12-26 20:17:14 +00:00
|
|
|
|
|
|
|
let mut decrypted_messages =
|
2023-12-26 20:45:11 +00:00
|
|
|
decrypt_with_keyring(&mut messages, &certs, &policy, &mut keyring)?;
|
2023-12-26 20:17:14 +00:00
|
|
|
|
2023-10-19 22:06:34 +00:00
|
|
|
// clean decrypted messages from encrypted messages
|
|
|
|
messages.retain(|k, _v| !decrypted_messages.contains_key(k));
|
|
|
|
|
|
|
|
let left_from_threshold = threshold as usize - decrypted_messages.len();
|
|
|
|
if left_from_threshold > 0 {
|
2024-01-07 04:23:03 +00:00
|
|
|
#[allow(clippy::cast_possible_truncation)]
|
2023-12-26 20:17:14 +00:00
|
|
|
let new_messages = decrypt_with_manager(
|
|
|
|
left_from_threshold as u8,
|
|
|
|
&mut messages,
|
2023-12-26 20:45:11 +00:00
|
|
|
&certs,
|
2024-01-07 04:23:03 +00:00
|
|
|
&policy,
|
2023-12-26 20:17:14 +00:00
|
|
|
&mut manager,
|
|
|
|
)?;
|
2024-01-07 04:23:03 +00:00
|
|
|
decrypted_messages.extend(new_messages);
|
2023-10-19 22:06:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
let shares = decrypted_messages
|
|
|
|
.values()
|
|
|
|
.map(|message| Share::try_from(message.as_slice()))
|
|
|
|
.collect::<Result<Vec<_>, &str>>()
|
2024-01-05 04:11:15 +00:00
|
|
|
.map_err(|e| SharksError::Share(e.to_string()))?;
|
2023-11-05 19:57:22 +00:00
|
|
|
let secret = Sharks(threshold)
|
|
|
|
.recover(&shares)
|
2024-01-05 04:11:15 +00:00
|
|
|
.map_err(|e| SharksError::CombineShare(e.to_string()))?;
|
2023-10-19 22:06:34 +00:00
|
|
|
|
2024-01-01 21:58:15 +00:00
|
|
|
// TODO: extract as function
|
2023-10-20 01:10:02 +00:00
|
|
|
let userid = UserID::from("keyfork-sss");
|
|
|
|
let kdr = DerivationRequest::new(
|
|
|
|
DerivationAlgorithm::Ed25519,
|
|
|
|
&DerivationPath::from_str("m/7366512'/0'")?,
|
|
|
|
)
|
2024-01-07 04:23:03 +00:00
|
|
|
.derive_with_master_seed(secret.clone())?;
|
2023-10-20 01:10:02 +00:00
|
|
|
let derived_cert = keyfork_derive_openpgp::derive(
|
|
|
|
kdr,
|
|
|
|
&[KeyFlags::empty().set_certification().set_signing()],
|
2024-01-07 04:23:03 +00:00
|
|
|
&userid,
|
2023-10-20 01:10:02 +00:00
|
|
|
)?;
|
|
|
|
|
|
|
|
// NOTE: Signatures on certs will be different. Compare fingerprints instead.
|
2023-11-05 19:57:22 +00:00
|
|
|
let derived_fp = derived_cert.fingerprint();
|
2024-01-01 21:58:15 +00:00
|
|
|
let expected_fp = root_cert.fingerprint();
|
2023-11-05 19:57:22 +00:00
|
|
|
if derived_fp != expected_fp {
|
|
|
|
return Err(Error::InvalidSecret(derived_fp, expected_fp));
|
2023-10-20 01:10:02 +00:00
|
|
|
}
|
|
|
|
|
2024-01-10 20:34:29 +00:00
|
|
|
output.write_all(&secret).map_err(Error::Io)?;
|
2023-10-19 22:06:34 +00:00
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2024-01-16 02:44:48 +00:00
|
|
|
/// Split a secret into an OpenPGP formatted Shard file.
|
|
|
|
///
|
|
|
|
/// # Errors
|
|
|
|
///
|
|
|
|
/// The function may return an error if the shards can't be encrypted to the provided OpenPGP
|
|
|
|
/// certs or if an error happens while writing the Shard file.
|
|
|
|
///
|
2024-01-07 04:23:03 +00:00
|
|
|
/// # Panics
|
|
|
|
///
|
|
|
|
/// The function may panic if the metadata can't properly store the certificates used to generate
|
|
|
|
/// the encrypted shares.
|
2023-10-19 22:06:34 +00:00
|
|
|
pub fn split(threshold: u8, certs: Vec<Cert>, secret: &[u8], output: impl Write) -> Result<()> {
|
|
|
|
// build cert to sign encrypted shares
|
|
|
|
let userid = UserID::from("keyfork-sss");
|
|
|
|
let kdr = DerivationRequest::new(
|
|
|
|
DerivationAlgorithm::Ed25519,
|
|
|
|
&DerivationPath::from_str("m/7366512'/0'")?,
|
|
|
|
)
|
|
|
|
.derive_with_master_seed(secret.to_vec())?;
|
|
|
|
let derived_cert = keyfork_derive_openpgp::derive(
|
|
|
|
kdr,
|
|
|
|
&[KeyFlags::empty().set_certification().set_signing()],
|
2024-01-07 04:23:03 +00:00
|
|
|
&userid,
|
2023-10-19 22:06:34 +00:00
|
|
|
)?;
|
|
|
|
let signing_key = derived_cert
|
|
|
|
.primary_key()
|
2023-11-05 19:57:22 +00:00
|
|
|
.parts_into_secret()
|
|
|
|
.map_err(Error::Sequoia)?
|
2023-10-19 22:06:34 +00:00
|
|
|
.key()
|
|
|
|
.clone()
|
2023-11-05 19:57:22 +00:00
|
|
|
.into_keypair()
|
|
|
|
.map_err(Error::Sequoia)?;
|
2023-10-19 22:06:34 +00:00
|
|
|
|
|
|
|
let sharks = Sharks(threshold);
|
|
|
|
let dealer = sharks.dealer(secret);
|
2024-01-07 04:23:03 +00:00
|
|
|
let generated_shares = dealer.map(|s| Vec::from(&s)).collect::<Vec<_>>();
|
2023-10-19 22:06:34 +00:00
|
|
|
let policy = StandardPolicy::new();
|
2023-11-05 19:57:22 +00:00
|
|
|
let mut writer = Writer::new(output, Kind::Message).map_err(Error::SequoiaIo)?;
|
2023-10-19 22:06:34 +00:00
|
|
|
|
|
|
|
let mut total_recipients = vec![];
|
|
|
|
let mut messages = vec![];
|
|
|
|
|
2024-01-07 04:23:03 +00:00
|
|
|
for (share, cert) in generated_shares.iter().zip(certs) {
|
2023-10-19 22:06:34 +00:00
|
|
|
total_recipients.push(cert.clone());
|
2023-11-05 19:57:22 +00:00
|
|
|
let valid_cert = cert.with_policy(&policy, None).map_err(Error::Sequoia)?;
|
2023-10-19 22:06:34 +00:00
|
|
|
let encryption_keys = get_encryption_keys(&valid_cert).collect::<Vec<_>>();
|
|
|
|
|
|
|
|
let mut message_output = vec![];
|
|
|
|
let message = Message::new(&mut message_output);
|
2023-11-05 05:45:47 +00:00
|
|
|
let message = Encryptor2::for_recipients(
|
2023-10-19 22:06:34 +00:00
|
|
|
message,
|
|
|
|
encryption_keys
|
|
|
|
.iter()
|
|
|
|
.map(|k| Recipient::new(KeyID::wildcard(), k.key())),
|
|
|
|
)
|
2023-11-05 19:57:22 +00:00
|
|
|
.build()
|
|
|
|
.map_err(Error::Sequoia)?;
|
|
|
|
let message = Signer::new(message, signing_key.clone())
|
|
|
|
.build()
|
|
|
|
.map_err(Error::Sequoia)?;
|
|
|
|
let mut message = LiteralWriter::new(message)
|
|
|
|
.build()
|
|
|
|
.map_err(Error::Sequoia)?;
|
|
|
|
message.write_all(share).map_err(Error::SequoiaIo)?;
|
|
|
|
message.finalize().map_err(Error::Sequoia)?;
|
2023-10-19 22:06:34 +00:00
|
|
|
|
|
|
|
messages.push(message_output);
|
|
|
|
}
|
|
|
|
|
2023-12-20 18:14:24 +00:00
|
|
|
let mut pp = vec![SHARD_METADATA_VERSION, threshold];
|
2023-10-19 22:06:34 +00:00
|
|
|
// store derived cert to verify provided shares
|
2023-11-05 19:57:22 +00:00
|
|
|
derived_cert.serialize(&mut pp).map_err(Error::Sequoia)?;
|
2023-10-19 22:06:34 +00:00
|
|
|
for recipient in &total_recipients {
|
2023-11-05 19:57:22 +00:00
|
|
|
recipient.serialize(&mut pp).map_err(Error::Sequoia)?;
|
2023-10-19 22:06:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// verify packet pile
|
2023-12-20 18:14:24 +00:00
|
|
|
for (packet_cert, cert) in openpgp::cert::CertParser::from_bytes(&pp[2..])
|
2023-11-05 19:57:22 +00:00
|
|
|
.map_err(Error::Sequoia)?
|
2023-10-19 22:06:34 +00:00
|
|
|
.skip(1)
|
|
|
|
.zip(total_recipients.iter())
|
|
|
|
{
|
2024-01-07 04:23:03 +00:00
|
|
|
assert_eq!(
|
|
|
|
&packet_cert.map_err(Error::Sequoia)?,
|
|
|
|
cert,
|
|
|
|
"packet pile could not recreate cert: {}",
|
|
|
|
cert.fingerprint()
|
|
|
|
);
|
2023-10-19 22:06:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
let valid_certs = total_recipients
|
|
|
|
.iter()
|
|
|
|
.map(|c| c.with_policy(&policy, None))
|
2023-11-05 19:57:22 +00:00
|
|
|
.collect::<openpgp::Result<Vec<_>>>()
|
|
|
|
.map_err(Error::Sequoia)?;
|
2023-10-19 22:06:34 +00:00
|
|
|
|
|
|
|
let total_recipients = valid_certs.iter().flat_map(|vc| {
|
|
|
|
get_encryption_keys(vc).map(|key| Recipient::new(KeyID::wildcard(), key.key()))
|
|
|
|
});
|
|
|
|
|
|
|
|
// metadata
|
|
|
|
let mut message_output = vec![];
|
|
|
|
let message = Message::new(&mut message_output);
|
2023-11-05 19:57:22 +00:00
|
|
|
let message = Encryptor2::for_recipients(message, total_recipients)
|
|
|
|
.build()
|
|
|
|
.map_err(Error::Sequoia)?;
|
|
|
|
let mut message = LiteralWriter::new(message)
|
|
|
|
.build()
|
|
|
|
.map_err(Error::Sequoia)?;
|
|
|
|
message.write_all(&pp).map_err(Error::SequoiaIo)?;
|
|
|
|
message.finalize().map_err(Error::Sequoia)?;
|
|
|
|
writer
|
|
|
|
.write_all(&message_output)
|
|
|
|
.map_err(Error::SequoiaIo)?;
|
2023-10-19 22:06:34 +00:00
|
|
|
|
|
|
|
for message in messages {
|
2023-11-05 19:57:22 +00:00
|
|
|
writer.write_all(&message).map_err(Error::SequoiaIo)?;
|
2023-10-19 22:06:34 +00:00
|
|
|
}
|
|
|
|
|
2023-11-05 19:57:22 +00:00
|
|
|
writer.finalize().map_err(Error::SequoiaIo)?;
|
2023-10-19 22:06:34 +00:00
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|