openpgp-card/card-functionality/src/util.rs
Heiko Schaefer 77c7a90daf sq_util::get_subkey() now returns an Option.
Not finding any subkey is not an error.
2021-10-23 20:01:10 +02:00

144 lines
3.9 KiB
Rust

// SPDX-FileCopyrightText: 2021 Heiko Schaefer <heiko@schaefer.name>
// SPDX-License-Identifier: MIT OR Apache-2.0
use anyhow::Result;
use std::io::Write;
use std::time::SystemTime;
use sequoia_openpgp::parse::stream::{
DetachedVerifierBuilder, MessageLayer, MessageStructure,
VerificationHelper,
};
use sequoia_openpgp::parse::Parse;
use sequoia_openpgp::policy::{Policy, StandardPolicy};
use sequoia_openpgp::serialize::stream::{
Armorer, Encryptor, LiteralWriter, Message,
};
use sequoia_openpgp::Cert;
use openpgp_card::card_do::KeyGenerationTime;
use openpgp_card::{CardApp, KeyType};
use openpgp_card_sequoia::sq_util::get_subkey;
use openpgp_card_sequoia::util::vka_as_uploadable_key;
pub const SP: &StandardPolicy = &StandardPolicy::new();
pub(crate) fn upload_subkeys(
ca: &mut CardApp,
cert: &Cert,
policy: &dyn Policy,
) -> Result<Vec<(String, KeyGenerationTime)>> {
let mut out = vec![];
for kt in &[
KeyType::Signing,
KeyType::Decryption,
KeyType::Authentication,
] {
if let Some(vka) = get_subkey(cert, policy, *kt)? {
// store fingerprint as return-value
let fp = vka.fingerprint().to_hex();
// store key creation time as return-value
let creation = vka
.creation_time()
.duration_since(SystemTime::UNIX_EPOCH)
.unwrap()
.as_secs() as u32;
out.push((fp, creation.into()));
// upload key
let cuk = vka_as_uploadable_key(vka, None);
ca.key_import(cuk, *kt)?;
}
}
Ok(out)
}
/// Perform signature verification for one Cert and a simple signature
/// over a message.
struct VHelper<'a> {
cert: &'a Cert,
}
impl<'a> VHelper<'a> {
fn new(cert: &'a Cert) -> Self {
Self { cert }
}
}
impl<'a> VerificationHelper for VHelper<'a> {
fn get_certs(
&mut self,
_ids: &[sequoia_openpgp::KeyHandle],
) -> sequoia_openpgp::Result<Vec<Cert>> {
// Hand out our single Cert
Ok(vec![self.cert.clone()])
}
fn check(
&mut self,
structure: MessageStructure,
) -> sequoia_openpgp::Result<()> {
// We are interested in signatures over the data (level 0 signatures)
if let Some(MessageLayer::SignatureGroup { results }) =
structure.into_iter().next()
{
match results.into_iter().next() {
Some(Ok(_)) => Ok(()), // Good signature.
Some(Err(e)) => Err(sequoia_openpgp::Error::from(e).into()),
None => Err(anyhow::anyhow!("No signature")),
}
} else {
Err(anyhow::anyhow!("Unexpected message structure"))
}
}
}
pub fn verify_sig(cert: &Cert, msg: &[u8], sig: &[u8]) -> Result<bool> {
let vh = VHelper::new(cert);
let mut dv = DetachedVerifierBuilder::from_bytes(&sig)?
.with_policy(SP, None, vh)?;
Ok(dv.verify_bytes(msg).is_ok())
}
pub fn encrypt_to(cleartext: &str, cert: &Cert) -> Result<String> {
let p = &StandardPolicy::new();
let mut recipients = Vec::new();
// Make sure we add at least one subkey from every
// certificate.
let mut found_one = false;
for key in cert
.keys()
.with_policy(p, None)
.supported()
.alive()
.revoked(false)
.for_transport_encryption()
{
recipients.push(key);
found_one = true;
}
if !found_one {
return Err(anyhow::anyhow!(
"No suitable encryption subkey for {}",
cert
));
}
let mut sink = vec![];
let message = Message::new(&mut sink);
let message = Armorer::new(message).build()?;
let message = Encryptor::for_recipients(message, recipients).build()?;
let mut w = LiteralWriter::new(message).build()?;
w.write_all(cleartext.as_bytes())?;
w.finalize()?;
Ok(String::from_utf8(sink).unwrap())
}