feat: support working on existing keys
This commit is contained in:
parent
52f3bd2620
commit
cebc64cb7f
21
src/main.rs
21
src/main.rs
|
@ -2,11 +2,12 @@ use anyhow::{Context, Result};
|
||||||
|
|
||||||
use sequoia_openpgp::{
|
use sequoia_openpgp::{
|
||||||
cert::CertBuilder,
|
cert::CertBuilder,
|
||||||
packet::signature::SignatureBuilder,
|
packet::{signature::SignatureBuilder, Signature},
|
||||||
types::{KeyFlags, SignatureType},
|
types::{KeyFlags, SignatureType},
|
||||||
|
Cert,
|
||||||
};
|
};
|
||||||
|
|
||||||
use spec::KeyFlag;
|
use spec::{KeyFlag, Spec};
|
||||||
|
|
||||||
mod paths;
|
mod paths;
|
||||||
mod setup;
|
mod setup;
|
||||||
|
@ -14,6 +15,14 @@ mod spec;
|
||||||
|
|
||||||
fn main() -> Result<()> {
|
fn main() -> Result<()> {
|
||||||
let (paths, spec) = crate::setup::setup().context("Failed to setup application")?;
|
let (paths, spec) = crate::setup::setup().context("Failed to setup application")?;
|
||||||
|
let (cert, rev) = paths.load()?.ok_or(()).or_else(|()| generate_new(spec))?;
|
||||||
|
paths
|
||||||
|
.write(cert, rev)
|
||||||
|
.context("Failed to store certificate!")?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn generate_new(spec: Spec) -> Result<(Cert, Signature)> {
|
||||||
let mut builder = CertBuilder::new()
|
let mut builder = CertBuilder::new()
|
||||||
.set_primary_key_flags(
|
.set_primary_key_flags(
|
||||||
spec.primary
|
spec.primary
|
||||||
|
@ -57,11 +66,7 @@ fn main() -> Result<()> {
|
||||||
))?;
|
))?;
|
||||||
}
|
}
|
||||||
|
|
||||||
let (cert, rev) = builder
|
builder
|
||||||
.generate()
|
.generate()
|
||||||
.context("Failed to generate certificate!")?;
|
.context("Failed to generate certificate!")
|
||||||
paths
|
|
||||||
.write(cert, rev)
|
|
||||||
.context("Failed to store certificate!")?;
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
|
112
src/paths.rs
112
src/paths.rs
|
@ -1,7 +1,17 @@
|
||||||
use std::path::{Path, PathBuf};
|
use std::{
|
||||||
|
path::{Path, PathBuf},
|
||||||
|
time::SystemTime,
|
||||||
|
};
|
||||||
|
|
||||||
use anyhow::{Context, Result};
|
use anyhow::{bail, Context, Result};
|
||||||
use sequoia_openpgp::{packet::Signature, serialize::SerializeInto, Cert};
|
use sequoia_openpgp::{
|
||||||
|
cert::CertRevocationBuilder,
|
||||||
|
packet::Signature,
|
||||||
|
parse::Parse,
|
||||||
|
serialize::SerializeInto,
|
||||||
|
types::{ReasonForRevocation, SignatureType},
|
||||||
|
Cert, Packet,
|
||||||
|
};
|
||||||
|
|
||||||
pub(crate) struct Paths {
|
pub(crate) struct Paths {
|
||||||
pub(crate) spec: PathBuf,
|
pub(crate) spec: PathBuf,
|
||||||
|
@ -27,6 +37,102 @@ impl Paths {
|
||||||
rev,
|
rev,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn load(&self) -> Result<Option<(Cert, Signature)>> {
|
||||||
|
let mut merged = if self
|
||||||
|
.secret
|
||||||
|
.try_exists()
|
||||||
|
.context("Couldn't check if secret part of certificate exists")?
|
||||||
|
{
|
||||||
|
Cert::from_file(&self.secret)
|
||||||
|
.context("Couldn't load secret part of certificate from file")?
|
||||||
|
} else {
|
||||||
|
return Ok(None);
|
||||||
|
};
|
||||||
|
|
||||||
|
if let (_, Some(secret_material)) = merged.primary_key().key().clone().take_secret() {
|
||||||
|
if secret_material.is_encrypted() {
|
||||||
|
bail!("Secret material for primary key is encrypted!");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
bail!("Primary key has no secret material!")
|
||||||
|
}
|
||||||
|
|
||||||
|
if self
|
||||||
|
.public
|
||||||
|
.try_exists()
|
||||||
|
.context("Couldn't check if public part of certificate exists")?
|
||||||
|
{
|
||||||
|
merged = merged
|
||||||
|
.merge_public_and_secret(
|
||||||
|
Cert::from_file(&self.public)
|
||||||
|
.context("Couldn't load public part from certificate from file")?,
|
||||||
|
)
|
||||||
|
.context("Couldn't merge public part onto secret part of certificate")?
|
||||||
|
}
|
||||||
|
if self.rev.try_exists()? {
|
||||||
|
merged = merged
|
||||||
|
.merge_public_and_secret(
|
||||||
|
Cert::from_file(&self.rev)
|
||||||
|
.context("Couldn't load revocation for certificate from file")?,
|
||||||
|
)
|
||||||
|
.context("Couldn't merge revocation onto secret part of certificate")?
|
||||||
|
}
|
||||||
|
|
||||||
|
let fingerprint = merged.fingerprint();
|
||||||
|
let mut filtered = vec![];
|
||||||
|
|
||||||
|
let mut revocation = None;
|
||||||
|
|
||||||
|
// strip revocation for primary key
|
||||||
|
for packet in merged.into_packets() {
|
||||||
|
match packet {
|
||||||
|
Packet::Signature(Signature::V4(sig)) => {
|
||||||
|
if sig.typ() == SignatureType::KeyRevocation
|
||||||
|
&& sig
|
||||||
|
.issuer_fingerprints()
|
||||||
|
.any(|issuer| issuer.eq(&fingerprint))
|
||||||
|
{
|
||||||
|
revocation = Some(Signature::V4(sig));
|
||||||
|
} else {
|
||||||
|
filtered.push(Packet::Signature(Signature::V4(sig)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
packet => filtered.push(packet),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let filtered = Cert::from_packets(filtered.into_iter())
|
||||||
|
.context("Could't parse cert from packets after removing primary key revocations")?;
|
||||||
|
|
||||||
|
let revocation = revocation
|
||||||
|
.ok_or(())
|
||||||
|
.or_else(|()| {
|
||||||
|
let now = SystemTime::now();
|
||||||
|
CertRevocationBuilder::new()
|
||||||
|
.set_signature_creation_time(now)
|
||||||
|
.context("Failed to set signature creation time")?
|
||||||
|
.set_reason_for_revocation(ReasonForRevocation::Unspecified, b"Unspecified")
|
||||||
|
.context("Failed to set revocation reason")?
|
||||||
|
.build(
|
||||||
|
&mut filtered
|
||||||
|
.primary_key()
|
||||||
|
.key()
|
||||||
|
.clone()
|
||||||
|
.parts_into_secret()
|
||||||
|
.context("Failed to get secret parts of certificate primary key")?
|
||||||
|
.into_keypair()
|
||||||
|
.context("Failed to convert primary key into keypair")?,
|
||||||
|
&filtered,
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.context("Failed to generate revocation for certificate primary key")
|
||||||
|
})
|
||||||
|
.context("Failed to generate new revocation after not finding an existing one")?;
|
||||||
|
|
||||||
|
Ok(Some((filtered, revocation)))
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) fn write(&self, cert: Cert, rev: Signature) -> Result<()> {
|
pub(crate) fn write(&self, cert: Cert, rev: Signature) -> Result<()> {
|
||||||
self.write_part(Self::public, cert.armored())
|
self.write_part(Self::public, cert.armored())
|
||||||
.context("Failed to write public certificate to file")?;
|
.context("Failed to write public certificate to file")?;
|
||||||
|
|
Loading…
Reference in a new issue