keyfork-shard: add shard_and_encrypt
This commit is contained in:
parent
92fa68fa3b
commit
9f9ad54445
|
@ -2,14 +2,12 @@
|
||||||
|
|
||||||
use std::{env, path::PathBuf, process::ExitCode, str::FromStr};
|
use std::{env, path::PathBuf, process::ExitCode, str::FromStr};
|
||||||
|
|
||||||
use keyfork_shard::openpgp::{discover_certs, openpgp::Cert, split};
|
use keyfork_shard::{Format, openpgp::OpenPGP};
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
enum Error {
|
enum Error {
|
||||||
Usage(String),
|
Usage(String),
|
||||||
Input,
|
Input,
|
||||||
Threshold(u8, u8),
|
|
||||||
InvalidCertCount(usize, u8),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::fmt::Display for Error {
|
impl std::fmt::Display for Error {
|
||||||
|
@ -19,15 +17,6 @@ impl std::fmt::Display for Error {
|
||||||
write!(f, "Usage: {program_name} threshold max key_discovery")
|
write!(f, "Usage: {program_name} threshold max key_discovery")
|
||||||
}
|
}
|
||||||
Error::Input => f.write_str("Expected hex encoded input"),
|
Error::Input => f.write_str("Expected hex encoded input"),
|
||||||
Error::Threshold(threshold, max) => {
|
|
||||||
write!(
|
|
||||||
f,
|
|
||||||
"Invalid threshold: 0 < threshold {threshold} <= max {max} < 256"
|
|
||||||
)
|
|
||||||
}
|
|
||||||
Error::InvalidCertCount(count, max) => {
|
|
||||||
write!(f, "Invalid cert count: count {count} != max {max}")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -36,31 +25,20 @@ impl std::error::Error for Error {}
|
||||||
|
|
||||||
type Result<T, E = Box<dyn std::error::Error>> = std::result::Result<T, E>;
|
type Result<T, E = Box<dyn std::error::Error>> = std::result::Result<T, E>;
|
||||||
|
|
||||||
fn validate(threshold: &str, max: &str, key_discovery: &str) -> Result<(u8, Vec<Cert>)> {
|
fn validate(threshold: &str, max: &str, key_discovery: &str) -> Result<(u8, u8, PathBuf)> {
|
||||||
let threshold = u8::from_str(threshold)?;
|
let threshold = u8::from_str(threshold)?;
|
||||||
let max = u8::from_str(max)?;
|
let max = u8::from_str(max)?;
|
||||||
let key_discovery = PathBuf::from(key_discovery);
|
let key_discovery = PathBuf::from(key_discovery);
|
||||||
if threshold > max {
|
|
||||||
return Err(Error::Threshold(threshold, max).into());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Verify path exists
|
|
||||||
std::fs::metadata(&key_discovery)?;
|
std::fs::metadata(&key_discovery)?;
|
||||||
|
|
||||||
// Load certs from path
|
Ok((threshold, max, key_discovery))
|
||||||
let certs = discover_certs(key_discovery)?;
|
|
||||||
if certs.len() != max.into() {
|
|
||||||
return Err(Error::InvalidCertCount(certs.len(), max).into());
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok((threshold, certs))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run() -> Result<()> {
|
fn run() -> Result<()> {
|
||||||
let mut args = env::args();
|
let mut args = env::args();
|
||||||
let program_name = args.next().expect("program name");
|
let program_name = args.next().expect("program name");
|
||||||
let args = args.collect::<Vec<_>>();
|
let args = args.collect::<Vec<_>>();
|
||||||
let (threshold, cert_list) = match args.as_slice() {
|
let (threshold, max, key_discovery) = match args.as_slice() {
|
||||||
[threshold, max, key_discovery] => validate(threshold, max, key_discovery)?,
|
[threshold, max, key_discovery] => validate(threshold, max, key_discovery)?,
|
||||||
_ => return Err(Error::Usage(program_name).into()),
|
_ => return Err(Error::Usage(program_name).into()),
|
||||||
};
|
};
|
||||||
|
@ -72,8 +50,9 @@ fn run() -> Result<()> {
|
||||||
smex::decode(&line?)?
|
smex::decode(&line?)?
|
||||||
};
|
};
|
||||||
|
|
||||||
split(threshold, cert_list, &input, std::io::stdout())?;
|
let openpgp = OpenPGP;
|
||||||
|
|
||||||
|
openpgp.shard_and_encrypt(threshold, max, &input, key_discovery, std::io::stdout())?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -30,9 +30,6 @@ pub trait Format {
|
||||||
/// The error type returned from any failed operations.
|
/// The error type returned from any failed operations.
|
||||||
type Error: std::error::Error + 'static;
|
type Error: std::error::Error + 'static;
|
||||||
|
|
||||||
/// A type encapsulating the public key recipients of shards.
|
|
||||||
type PublicKeyData: IntoIterator<Item = Self::PublicKey>;
|
|
||||||
|
|
||||||
/// A type encapsulating a single public key recipient.
|
/// A type encapsulating a single public key recipient.
|
||||||
type PublicKey;
|
type PublicKey;
|
||||||
|
|
||||||
|
@ -43,7 +40,7 @@ pub trait Format {
|
||||||
type SigningKey;
|
type SigningKey;
|
||||||
|
|
||||||
/// A type representing the parsed, but encrypted, Shard data.
|
/// A type representing the parsed, but encrypted, Shard data.
|
||||||
type ShardData;
|
type EncryptedData;
|
||||||
|
|
||||||
/// Parse the public key data from a readable type.
|
/// Parse the public key data from a readable type.
|
||||||
///
|
///
|
||||||
|
@ -54,7 +51,7 @@ pub trait Format {
|
||||||
fn parse_public_key_data(
|
fn parse_public_key_data(
|
||||||
&self,
|
&self,
|
||||||
key_data_path: impl AsRef<Path>,
|
key_data_path: impl AsRef<Path>,
|
||||||
) -> Result<Self::PublicKeyData, Self::Error>;
|
) -> Result<Vec<Self::PublicKey>, Self::Error>;
|
||||||
|
|
||||||
/// Derive a signer
|
/// Derive a signer
|
||||||
fn derive_signing_key(&self, seed: &[u8]) -> Self::SigningKey;
|
fn derive_signing_key(&self, seed: &[u8]) -> Self::SigningKey;
|
||||||
|
@ -70,9 +67,9 @@ pub trait Format {
|
||||||
fn format_encrypted_header(
|
fn format_encrypted_header(
|
||||||
&self,
|
&self,
|
||||||
signing_key: &Self::SigningKey,
|
signing_key: &Self::SigningKey,
|
||||||
key_data: &Self::PublicKeyData,
|
key_data: &[Self::PublicKey],
|
||||||
threshold: u8,
|
threshold: u8,
|
||||||
) -> Result<Vec<u8>, Self::Error>;
|
) -> Result<Self::EncryptedData, Self::Error>;
|
||||||
|
|
||||||
/// Format a shard encrypted to the given public key, signing with the private key.
|
/// Format a shard encrypted to the given public key, signing with the private key.
|
||||||
///
|
///
|
||||||
|
@ -84,7 +81,7 @@ pub trait Format {
|
||||||
shard: &[u8],
|
shard: &[u8],
|
||||||
public_key: &Self::PublicKey,
|
public_key: &Self::PublicKey,
|
||||||
signing_key: &mut Self::SigningKey,
|
signing_key: &mut Self::SigningKey,
|
||||||
) -> Result<Vec<u8>, Self::Error>;
|
) -> Result<Self::EncryptedData, Self::Error>;
|
||||||
|
|
||||||
/// Parse the private key data from a readable type. The private key may not be accessible (it
|
/// Parse the private key data from a readable type. The private key may not be accessible (it
|
||||||
/// may be hardware only, such as a smartcard), for which this method may return None.
|
/// may be hardware only, such as a smartcard), for which this method may return None.
|
||||||
|
@ -105,7 +102,7 @@ pub trait Format {
|
||||||
fn parse_shard_file(
|
fn parse_shard_file(
|
||||||
&self,
|
&self,
|
||||||
shard_file: impl Read + Send + Sync,
|
shard_file: impl Read + Send + Sync,
|
||||||
) -> Result<Self::ShardData, Self::Error>;
|
) -> Result<Vec<Self::EncryptedData>, Self::Error>;
|
||||||
|
|
||||||
/// Write the Shard data to a Shard file.
|
/// Write the Shard data to a Shard file.
|
||||||
///
|
///
|
||||||
|
@ -114,8 +111,8 @@ pub trait Format {
|
||||||
/// Shard file could not be written to.
|
/// Shard file could not be written to.
|
||||||
fn format_shard_file(
|
fn format_shard_file(
|
||||||
&self,
|
&self,
|
||||||
shard_data: Self::ShardData,
|
encrypted_data: &[Self::EncryptedData],
|
||||||
shard_file: impl Write,
|
shard_file: impl Write + Send + Sync,
|
||||||
) -> Result<(), Self::Error>;
|
) -> Result<(), Self::Error>;
|
||||||
|
|
||||||
/// Decrypt shares and associated metadata from a readable input. For the current version of
|
/// Decrypt shares and associated metadata from a readable input. For the current version of
|
||||||
|
@ -128,7 +125,7 @@ pub trait Format {
|
||||||
fn decrypt_all_shards(
|
fn decrypt_all_shards(
|
||||||
&self,
|
&self,
|
||||||
private_keys: Option<Self::PrivateKeyData>,
|
private_keys: Option<Self::PrivateKeyData>,
|
||||||
shard_data: Self::ShardData,
|
encrypted_messages: &[Self::EncryptedData],
|
||||||
) -> Result<(Vec<Share>, u8), Self::Error>;
|
) -> Result<(Vec<Share>, u8), Self::Error>;
|
||||||
|
|
||||||
/// Decrypt a single share and associated metadata from a reaable input. For the current
|
/// Decrypt a single share and associated metadata from a reaable input. For the current
|
||||||
|
@ -141,7 +138,7 @@ pub trait Format {
|
||||||
fn decrypt_one_shard(
|
fn decrypt_one_shard(
|
||||||
&self,
|
&self,
|
||||||
private_keys: Option<Self::PrivateKeyData>,
|
private_keys: Option<Self::PrivateKeyData>,
|
||||||
shard_data: Self::ShardData,
|
encrypted_data: &[Self::EncryptedData],
|
||||||
) -> Result<(Share, u8), Self::Error>;
|
) -> Result<(Share, u8), Self::Error>;
|
||||||
|
|
||||||
/// Decrypt multiple shares and combine them to recreate a secret.
|
/// Decrypt multiple shares and combine them to recreate a secret.
|
||||||
|
@ -157,8 +154,8 @@ pub trait Format {
|
||||||
let private_keys = private_key_data_path
|
let private_keys = private_key_data_path
|
||||||
.map(|p| self.parse_private_key_data(p))
|
.map(|p| self.parse_private_key_data(p))
|
||||||
.transpose()?;
|
.transpose()?;
|
||||||
let shard_data = self.parse_shard_file(reader)?;
|
let encrypted_messages = self.parse_shard_file(reader)?;
|
||||||
let (shares, threshold) = self.decrypt_all_shards(private_keys, shard_data)?;
|
let (shares, threshold) = self.decrypt_all_shards(private_keys, &encrypted_messages)?;
|
||||||
|
|
||||||
let secret = Sharks(threshold)
|
let secret = Sharks(threshold)
|
||||||
.recover(&shares)
|
.recover(&shares)
|
||||||
|
@ -186,7 +183,7 @@ pub trait Format {
|
||||||
let private_keys = private_key_data_path
|
let private_keys = private_key_data_path
|
||||||
.map(|p| self.parse_private_key_data(p))
|
.map(|p| self.parse_private_key_data(p))
|
||||||
.transpose()?;
|
.transpose()?;
|
||||||
let shard_data = self.parse_shard_file(reader)?;
|
let encrypted_messages = self.parse_shard_file(reader)?;
|
||||||
|
|
||||||
// establish AES-256-GCM key via ECDH
|
// establish AES-256-GCM key via ECDH
|
||||||
let mut nonce_data: Option<[u8; 12]> = None;
|
let mut nonce_data: Option<[u8; 12]> = None;
|
||||||
|
@ -246,7 +243,7 @@ pub trait Format {
|
||||||
let shared_key = Aes256Gcm::new_from_slice(&hkdf_output)?;
|
let shared_key = Aes256Gcm::new_from_slice(&hkdf_output)?;
|
||||||
|
|
||||||
// decrypt a single shard and create the payload
|
// decrypt a single shard and create the payload
|
||||||
let (share, threshold) = self.decrypt_one_shard(private_keys, shard_data)?;
|
let (share, threshold) = self.decrypt_one_shard(private_keys, &encrypted_messages)?;
|
||||||
let mut payload = Vec::from(&share);
|
let mut payload = Vec::from(&share);
|
||||||
payload.insert(0, HUNK_VERSION);
|
payload.insert(0, HUNK_VERSION);
|
||||||
payload.insert(1, threshold);
|
payload.insert(1, threshold);
|
||||||
|
@ -313,6 +310,49 @@ pub trait Format {
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Split a secret into a shard for every shard in keys, with the given Shamir's Secret Sharing
|
||||||
|
/// threshold.
|
||||||
|
///
|
||||||
|
/// # Errors
|
||||||
|
/// The method may return an error if the shares can't be encrypted.
|
||||||
|
fn shard_and_encrypt(
|
||||||
|
&self,
|
||||||
|
threshold: u8,
|
||||||
|
max: u8,
|
||||||
|
secret: &[u8],
|
||||||
|
public_key_data_path: impl AsRef<Path>,
|
||||||
|
writer: impl Write + Send + Sync,
|
||||||
|
) -> Result<(), Box<dyn std::error::Error>> {
|
||||||
|
let mut signing_key = self.derive_signing_key(secret);
|
||||||
|
|
||||||
|
let sharks = Sharks(threshold);
|
||||||
|
let dealer = sharks.dealer(secret);
|
||||||
|
|
||||||
|
let public_keys = self.parse_public_key_data(public_key_data_path)?;
|
||||||
|
assert!(
|
||||||
|
public_keys.len() < u8::MAX as usize,
|
||||||
|
"must have less than u8::MAX public keys"
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
max,
|
||||||
|
public_keys.len() as u8,
|
||||||
|
"max must be equal to amount of public keys"
|
||||||
|
);
|
||||||
|
let max = public_keys.len() as u8;
|
||||||
|
assert!(max >= threshold, "threshold must not exceed max keys");
|
||||||
|
|
||||||
|
let header = self.format_encrypted_header(&signing_key, &public_keys, threshold)?;
|
||||||
|
let mut messages = vec![header];
|
||||||
|
for (pk, share) in public_keys.iter().zip(dealer) {
|
||||||
|
let shard = Vec::from(&share);
|
||||||
|
messages.push(self.encrypt_shard(&shard, pk, &mut signing_key)?);
|
||||||
|
}
|
||||||
|
|
||||||
|
self.format_shard_file(&messages, writer)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Errors encountered while creating or combining shares using Shamir's Secret Sharing.
|
/// Errors encountered while creating or combining shares using Shamir's Secret Sharing.
|
||||||
|
|
|
@ -163,15 +163,52 @@ impl EncryptedMessage {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Parse OpenPGP packets for encrypted messages.
|
||||||
|
pub fn from_reader(input: impl Read + Send + Sync) -> openpgp::Result<Vec<Self>> {
|
||||||
|
let mut pkesks = Vec::new();
|
||||||
|
let mut encrypted_messages = vec![];
|
||||||
|
|
||||||
|
for packet in PacketPile::from_reader(input)
|
||||||
|
.map_err(Error::Sequoia)?
|
||||||
|
.into_children()
|
||||||
|
{
|
||||||
|
match packet {
|
||||||
|
Packet::PKESK(p) => pkesks.push(p),
|
||||||
|
Packet::SEIP(s) => {
|
||||||
|
encrypted_messages.push(EncryptedMessage::new(&mut pkesks, s));
|
||||||
|
}
|
||||||
|
s => {
|
||||||
|
panic!("Invalid variant found: {}", s.tag());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(encrypted_messages)
|
||||||
|
}
|
||||||
|
|
||||||
/// Serialize all contents of the message to a writer.
|
/// Serialize all contents of the message to a writer.
|
||||||
///
|
///
|
||||||
/// # Errors
|
/// # Errors
|
||||||
/// The function may error for any condition in Sequoia's Serialize trait.
|
/// The function may error for any condition in Sequoia's Serialize trait.
|
||||||
pub fn serialize(&self, o: &mut dyn std::io::Write) -> openpgp::Result<()> {
|
fn serialize(&self, mut o: impl std::io::Write + Send + Sync) -> openpgp::Result<()> {
|
||||||
for pkesk in &self.pkesks {
|
for pkesk in &self.pkesks {
|
||||||
pkesk.serialize(o)?;
|
let mut packet = vec![];
|
||||||
|
pkesk.serialize(&mut packet).map_err(Error::Sequoia)?;
|
||||||
|
let message = Message::new(&mut o);
|
||||||
|
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)?;
|
||||||
}
|
}
|
||||||
self.message.serialize(o)?;
|
let mut packet = vec![];
|
||||||
|
self.message
|
||||||
|
.serialize(&mut packet)
|
||||||
|
.map_err(Error::Sequoia)?;
|
||||||
|
|
||||||
|
let message = Message::new(&mut o);
|
||||||
|
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)?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -188,23 +225,8 @@ impl EncryptedMessage {
|
||||||
H: VerificationHelper + DecryptionHelper,
|
H: VerificationHelper + DecryptionHelper,
|
||||||
{
|
{
|
||||||
let mut packets = vec![];
|
let mut packets = vec![];
|
||||||
|
self.serialize(&mut packets)
|
||||||
for pkesk in &self.pkesks {
|
|
||||||
let mut packet = vec![];
|
|
||||||
pkesk.serialize(&mut packet).map_err(Error::Sequoia)?;
|
|
||||||
let message = Message::new(&mut packets);
|
|
||||||
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)?;
|
|
||||||
}
|
|
||||||
let mut packet = vec![];
|
|
||||||
self.message
|
|
||||||
.serialize(&mut packet)
|
|
||||||
.map_err(Error::Sequoia)?;
|
.map_err(Error::Sequoia)?;
|
||||||
let message = Message::new(&mut packets);
|
|
||||||
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)?;
|
|
||||||
|
|
||||||
let mut decryptor = DecryptorBuilder::from_bytes(&packets)
|
let mut decryptor = DecryptorBuilder::from_bytes(&packets)
|
||||||
.map_err(Error::Sequoia)?
|
.map_err(Error::Sequoia)?
|
||||||
|
@ -256,15 +278,14 @@ impl OpenPGP {
|
||||||
impl Format for OpenPGP {
|
impl Format for OpenPGP {
|
||||||
type Error = Error;
|
type Error = Error;
|
||||||
type PublicKey = Cert;
|
type PublicKey = Cert;
|
||||||
type PublicKeyData = Vec<Cert>;
|
|
||||||
type PrivateKeyData = Vec<Cert>;
|
type PrivateKeyData = Vec<Cert>;
|
||||||
type SigningKey = Cert;
|
type SigningKey = Cert;
|
||||||
type ShardData = Vec<EncryptedMessage>;
|
type EncryptedData = EncryptedMessage;
|
||||||
|
|
||||||
fn parse_public_key_data(
|
fn parse_public_key_data(
|
||||||
&self,
|
&self,
|
||||||
key_data_path: impl AsRef<Path>,
|
key_data_path: impl AsRef<Path>,
|
||||||
) -> std::result::Result<Self::PublicKeyData, Self::Error> {
|
) -> std::result::Result<Vec<Self::PublicKey>, Self::Error> {
|
||||||
Self::discover_certs(key_data_path)
|
Self::discover_certs(key_data_path)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -288,9 +309,9 @@ impl Format for OpenPGP {
|
||||||
fn format_encrypted_header(
|
fn format_encrypted_header(
|
||||||
&self,
|
&self,
|
||||||
signing_key: &Self::SigningKey,
|
signing_key: &Self::SigningKey,
|
||||||
key_data: &Self::PublicKeyData,
|
key_data: &[Self::PublicKey],
|
||||||
threshold: u8,
|
threshold: u8,
|
||||||
) -> Result<Vec<u8>, Self::Error> {
|
) -> Result<Self::EncryptedData, Self::Error> {
|
||||||
let policy = StandardPolicy::new();
|
let policy = StandardPolicy::new();
|
||||||
let mut pp = vec![SHARD_METADATA_VERSION, threshold];
|
let mut pp = vec![SHARD_METADATA_VERSION, threshold];
|
||||||
// Note: Sequoia does not export private keys on a Cert, only on a TSK
|
// Note: Sequoia does not export private keys on a Cert, only on a TSK
|
||||||
|
@ -348,7 +369,22 @@ impl Format for OpenPGP {
|
||||||
literal_message.write_all(&pp).map_err(Error::SequoiaIo)?;
|
literal_message.write_all(&pp).map_err(Error::SequoiaIo)?;
|
||||||
literal_message.finalize().map_err(Error::Sequoia)?;
|
literal_message.finalize().map_err(Error::Sequoia)?;
|
||||||
|
|
||||||
Ok(output)
|
// Parse it into an EncryptedMessage. Yes, this takes a serialized message
|
||||||
|
// and deserializes it. Don't think about it too hard. It's easier this way.
|
||||||
|
|
||||||
|
let mut pkesks = vec![];
|
||||||
|
for packet in PacketPile::from_reader(output.as_slice())
|
||||||
|
.map_err(Error::Sequoia)?
|
||||||
|
.into_children()
|
||||||
|
{
|
||||||
|
match packet {
|
||||||
|
Packet::PKESK(p) => pkesks.push(p),
|
||||||
|
Packet::SEIP(s) => return Ok(EncryptedMessage::new(&mut pkesks, s)),
|
||||||
|
s => panic!("Invalid variant found: {}", s.tag()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
panic!("Unable to build EncryptedMessage from PacketPile");
|
||||||
}
|
}
|
||||||
|
|
||||||
fn encrypt_shard(
|
fn encrypt_shard(
|
||||||
|
@ -356,7 +392,7 @@ impl Format for OpenPGP {
|
||||||
shard: &[u8],
|
shard: &[u8],
|
||||||
public_key: &Cert,
|
public_key: &Cert,
|
||||||
signing_key: &mut Self::SigningKey,
|
signing_key: &mut Self::SigningKey,
|
||||||
) -> Result<Vec<u8>> {
|
) -> Result<EncryptedMessage> {
|
||||||
let policy = StandardPolicy::new();
|
let policy = StandardPolicy::new();
|
||||||
let valid_cert = public_key
|
let valid_cert = public_key
|
||||||
.with_policy(&policy, None)
|
.with_policy(&policy, None)
|
||||||
|
@ -404,7 +440,13 @@ impl Format for OpenPGP {
|
||||||
message.write_all(shard).map_err(Error::SequoiaIo)?;
|
message.write_all(shard).map_err(Error::SequoiaIo)?;
|
||||||
message.finalize().map_err(Error::Sequoia)?;
|
message.finalize().map_err(Error::Sequoia)?;
|
||||||
|
|
||||||
Ok(message_output)
|
let message = EncryptedMessage::from_reader(message_output.as_slice())
|
||||||
|
.map_err(Error::Sequoia)?
|
||||||
|
.into_iter()
|
||||||
|
.next()
|
||||||
|
.expect("serialized message should be parseable");
|
||||||
|
|
||||||
|
Ok(message)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_private_key_data(
|
fn parse_private_key_data(
|
||||||
|
@ -417,35 +459,17 @@ impl Format for OpenPGP {
|
||||||
fn parse_shard_file(
|
fn parse_shard_file(
|
||||||
&self,
|
&self,
|
||||||
shard_file: impl Read + Send + Sync,
|
shard_file: impl Read + Send + Sync,
|
||||||
) -> Result<Self::ShardData, Self::Error> {
|
) -> Result<Vec<Self::EncryptedData>, Self::Error> {
|
||||||
let mut pkesks = Vec::new();
|
EncryptedMessage::from_reader(shard_file).map_err(Error::Sequoia)
|
||||||
let mut encrypted_messages = vec![];
|
|
||||||
|
|
||||||
for packet in PacketPile::from_reader(shard_file)
|
|
||||||
.map_err(Error::Sequoia)?
|
|
||||||
.into_children()
|
|
||||||
{
|
|
||||||
match packet {
|
|
||||||
Packet::PKESK(p) => pkesks.push(p),
|
|
||||||
Packet::SEIP(s) => {
|
|
||||||
encrypted_messages.push(EncryptedMessage::new(&mut pkesks, s));
|
|
||||||
}
|
|
||||||
s => {
|
|
||||||
panic!("Invalid variant found: {}", s.tag());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(encrypted_messages)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn format_shard_file(
|
fn format_shard_file(
|
||||||
&self,
|
&self,
|
||||||
shard_data: Self::ShardData,
|
encrypted_data: &[Self::EncryptedData],
|
||||||
shard_file: impl Write,
|
shard_file: impl Write + Send + Sync,
|
||||||
) -> Result<(), Self::Error> {
|
) -> Result<(), Self::Error> {
|
||||||
let mut writer = Writer::new(shard_file, Kind::Message).map_err(Error::SequoiaIo)?;
|
let mut writer = Writer::new(shard_file, Kind::Message).map_err(Error::SequoiaIo)?;
|
||||||
for message in shard_data {
|
for message in encrypted_data {
|
||||||
message.serialize(&mut writer).map_err(Error::Sequoia)?;
|
message.serialize(&mut writer).map_err(Error::Sequoia)?;
|
||||||
}
|
}
|
||||||
writer.finalize().map_err(Error::SequoiaIo)?;
|
writer.finalize().map_err(Error::SequoiaIo)?;
|
||||||
|
@ -455,7 +479,7 @@ impl Format for OpenPGP {
|
||||||
fn decrypt_all_shards(
|
fn decrypt_all_shards(
|
||||||
&self,
|
&self,
|
||||||
private_keys: Option<Self::PrivateKeyData>,
|
private_keys: Option<Self::PrivateKeyData>,
|
||||||
mut shard_data: Self::ShardData,
|
encrypted_data: &[Self::EncryptedData],
|
||||||
) -> std::result::Result<(Vec<Share>, u8), Self::Error> {
|
) -> std::result::Result<(Vec<Share>, u8), Self::Error> {
|
||||||
// Be as liberal as possible when decrypting.
|
// Be as liberal as possible when decrypting.
|
||||||
// We don't want to invalidate someone's keys just because the old sig expired.
|
// We don't want to invalidate someone's keys just because the old sig expired.
|
||||||
|
@ -463,8 +487,10 @@ impl Format for OpenPGP {
|
||||||
let mut keyring = Keyring::new(private_keys.unwrap_or_default())?;
|
let mut keyring = Keyring::new(private_keys.unwrap_or_default())?;
|
||||||
let mut manager = SmartcardManager::new()?;
|
let mut manager = SmartcardManager::new()?;
|
||||||
|
|
||||||
let metadata = shard_data.remove(0);
|
let mut encrypted_messages = encrypted_data.iter();
|
||||||
let metadata_content = decrypt_metadata(&metadata, &policy, &mut keyring, &mut manager)?;
|
|
||||||
|
let metadata = encrypted_messages.next().expect("metdata");
|
||||||
|
let metadata_content = decrypt_metadata(metadata, &policy, &mut keyring, &mut manager)?;
|
||||||
|
|
||||||
let (threshold, root_cert, certs) = decode_metadata_v1(&metadata_content)?;
|
let (threshold, root_cert, certs) = decode_metadata_v1(&metadata_content)?;
|
||||||
|
|
||||||
|
@ -475,8 +501,11 @@ impl Format for OpenPGP {
|
||||||
// because we control the order packets are encrypted and certificates are stored.
|
// because we control the order packets are encrypted and certificates are stored.
|
||||||
|
|
||||||
// TODO: remove alloc, convert EncryptedMessage to &EncryptedMessage
|
// TODO: remove alloc, convert EncryptedMessage to &EncryptedMessage
|
||||||
let mut messages: HashMap<KeyID, EncryptedMessage> =
|
let mut messages: HashMap<KeyID, EncryptedMessage> = certs
|
||||||
certs.iter().map(Cert::keyid).zip(shard_data).collect();
|
.iter()
|
||||||
|
.map(Cert::keyid)
|
||||||
|
.zip(encrypted_messages.cloned())
|
||||||
|
.collect();
|
||||||
let mut decrypted_messages =
|
let mut decrypted_messages =
|
||||||
decrypt_with_keyring(&mut messages, &certs, &policy, &mut keyring)?;
|
decrypt_with_keyring(&mut messages, &certs, &policy, &mut keyring)?;
|
||||||
|
|
||||||
|
@ -507,21 +536,26 @@ impl Format for OpenPGP {
|
||||||
fn decrypt_one_shard(
|
fn decrypt_one_shard(
|
||||||
&self,
|
&self,
|
||||||
private_keys: Option<Self::PrivateKeyData>,
|
private_keys: Option<Self::PrivateKeyData>,
|
||||||
mut shard_data: Self::ShardData,
|
encrypted_data: &[Self::EncryptedData],
|
||||||
) -> std::result::Result<(Share, u8), Self::Error> {
|
) -> std::result::Result<(Share, u8), Self::Error> {
|
||||||
let policy = NullPolicy::new();
|
let policy = NullPolicy::new();
|
||||||
let mut keyring = Keyring::new(private_keys.unwrap_or_default())?;
|
let mut keyring = Keyring::new(private_keys.unwrap_or_default())?;
|
||||||
let mut manager = SmartcardManager::new()?;
|
let mut manager = SmartcardManager::new()?;
|
||||||
|
|
||||||
let metadata = shard_data.remove(0);
|
let mut encrypted_messages = encrypted_data.iter();
|
||||||
let metadata_content = decrypt_metadata(&metadata, &policy, &mut keyring, &mut manager)?;
|
|
||||||
|
let metadata = encrypted_messages.next().expect("metadata");
|
||||||
|
let metadata_content = decrypt_metadata(metadata, &policy, &mut keyring, &mut manager)?;
|
||||||
|
|
||||||
let (threshold, root_cert, certs) = decode_metadata_v1(&metadata_content)?;
|
let (threshold, root_cert, certs) = decode_metadata_v1(&metadata_content)?;
|
||||||
|
|
||||||
keyring.set_root_cert(root_cert.clone());
|
keyring.set_root_cert(root_cert.clone());
|
||||||
manager.set_root_cert(root_cert.clone());
|
manager.set_root_cert(root_cert.clone());
|
||||||
let mut messages: HashMap<KeyID, EncryptedMessage> =
|
let mut messages: HashMap<KeyID, EncryptedMessage> = certs
|
||||||
certs.iter().map(Cert::keyid).zip(shard_data).collect();
|
.iter()
|
||||||
|
.map(Cert::keyid)
|
||||||
|
.zip(encrypted_messages.cloned())
|
||||||
|
.collect();
|
||||||
|
|
||||||
let decrypted_messages =
|
let decrypted_messages =
|
||||||
decrypt_with_keyring(&mut messages, &certs, &policy, &mut keyring)?;
|
decrypt_with_keyring(&mut messages, &certs, &policy, &mut keyring)?;
|
||||||
|
@ -935,6 +969,7 @@ pub fn decrypt(
|
||||||
/// # Errors
|
/// # Errors
|
||||||
/// The function may return an error if an error occurs while decrypting shards, parsing shards, or
|
/// The function may return an error if an error occurs while decrypting shards, parsing shards, or
|
||||||
/// combining the shards into a secret.
|
/// combining the shards into a secret.
|
||||||
|
#[deprecated]
|
||||||
pub fn combine(
|
pub fn combine(
|
||||||
certs: Vec<Cert>,
|
certs: Vec<Cert>,
|
||||||
metadata: &EncryptedMessage,
|
metadata: &EncryptedMessage,
|
||||||
|
@ -1022,6 +1057,7 @@ pub fn combine(
|
||||||
///
|
///
|
||||||
/// The function may panic if the metadata can't properly store the certificates used to generate
|
/// The function may panic if the metadata can't properly store the certificates used to generate
|
||||||
/// the encrypted shares.
|
/// the encrypted shares.
|
||||||
|
#[deprecated]
|
||||||
pub fn split(threshold: u8, certs: Vec<Cert>, secret: &[u8], output: impl Write) -> Result<()> {
|
pub fn split(threshold: u8, certs: Vec<Cert>, secret: &[u8], output: impl Write) -> Result<()> {
|
||||||
let seed = VariableLengthSeed::new(secret);
|
let seed = VariableLengthSeed::new(secret);
|
||||||
// build cert to sign encrypted shares
|
// build cert to sign encrypted shares
|
||||||
|
|
|
@ -34,7 +34,7 @@ trait ShardExec {
|
||||||
max: u8,
|
max: u8,
|
||||||
key_discovery: impl AsRef<Path>,
|
key_discovery: impl AsRef<Path>,
|
||||||
secret: &[u8],
|
secret: &[u8],
|
||||||
output: &mut impl Write,
|
output: &mut (impl Write + Send + Sync),
|
||||||
) -> Result<(), Box<dyn std::error::Error>>;
|
) -> Result<(), Box<dyn std::error::Error>>;
|
||||||
|
|
||||||
fn combine<T>(
|
fn combine<T>(
|
||||||
|
@ -65,17 +65,10 @@ impl ShardExec for OpenPGP {
|
||||||
max: u8,
|
max: u8,
|
||||||
key_discovery: impl AsRef<Path>,
|
key_discovery: impl AsRef<Path>,
|
||||||
secret: &[u8],
|
secret: &[u8],
|
||||||
output: &mut impl Write,
|
output: &mut (impl Write + Send + Sync),
|
||||||
) -> Result<(), Box<dyn std::error::Error>> {
|
) -> Result<(), Box<dyn std::error::Error>> {
|
||||||
// Get certs and input
|
let opgp = keyfork_shard::openpgp::OpenPGP;
|
||||||
let certs = keyfork_shard::openpgp::discover_certs(key_discovery.as_ref())?;
|
opgp.shard_and_encrypt(threshold, max, secret, key_discovery, output)
|
||||||
assert_eq!(
|
|
||||||
certs.len(),
|
|
||||||
max.into(),
|
|
||||||
"cert count {} != max {max}",
|
|
||||||
certs.len()
|
|
||||||
);
|
|
||||||
keyfork_shard::openpgp::split(threshold, certs, secret, output).map_err(Into::into)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn combine<T>(
|
fn combine<T>(
|
||||||
|
|
|
@ -165,8 +165,10 @@ fn generate_shard_secret(
|
||||||
|
|
||||||
if let Some(output_file) = output_file {
|
if let Some(output_file) = output_file {
|
||||||
let output = File::create(output_file)?;
|
let output = File::create(output_file)?;
|
||||||
|
#[allow(deprecated)]
|
||||||
keyfork_shard::openpgp::split(threshold, certs, &seed, output)?;
|
keyfork_shard::openpgp::split(threshold, certs, &seed, output)?;
|
||||||
} else {
|
} else {
|
||||||
|
#[allow(deprecated)]
|
||||||
keyfork_shard::openpgp::split(threshold, certs, &seed, std::io::stdout())?;
|
keyfork_shard::openpgp::split(threshold, certs, &seed, std::io::stdout())?;
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
Loading…
Reference in New Issue