summaryrefslogtreecommitdiffstats
path: root/openpgp/examples/generate-group-key.rs
blob: 6dee235a8cac46159cb109c7d07f7241ed3e7ccc (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
//! Illustrates how to create keys and certificates for group
//! conversations.
//!
//! This example, when run, generates four artifacts:
//!
//! ```text
//! % cargo run -p sequoia-openpgp --example generate-group-key cabal@example.org
//! [...]
//! Writing certificate to cabal@example.org.cert.pgp
//! Writing full key to cabal@example.org.key.pgp
//! Writing key with detached primary to cabal@example.org.only_subkey.pgp
//! Writing revocation certificate to cabal@example.org.revocation.pgp
//! ```
//!
//! - If you want to receive unsolicited encrypted messages on the
//!   address (e.g. in case of a security contact or a public mailing
//!   list), you should publish this in a suitable way, e.g. using
//!   WKD.  Consider authenticating this certificate using OpenPGP-CA.
//!
//! - Distribute the certificate freely if you want to receive
//!   unsolicited encrypted messages.
//!
//! - Distribute the key with the detached primary among the group,
//!   for example by including it in an email encrypted to all
//!   members.
//!
//! - Make a backup of full key and revocation certificate.
//!
//! - Consider distributing the revocation certificate among a select,
//!   trusted group that can help when disaster strikes.  The security
//!   implication of handing out the revocation certificate is a
//!   denial-of-service vector.

use std::fs::File;
use std::time::Duration;

use sequoia_openpgp as openpgp;
use openpgp::armor;
use openpgp::cert::prelude::*;
use openpgp::types::KeyFlags;
use openpgp::serialize::Serialize;

fn main() -> openpgp::Result<()> {
    let args: Vec<String> = std::env::args().collect();
    let name = if let Some(n) = args.get(1).cloned() {
        n
    } else {
        return Err(anyhow::anyhow!(
            "Missing list address parameter.\n\n\
             Usage: {} <LIST-ADDRESS>",
            args.get(0).cloned().unwrap_or("generate-group-key".into())));
    };

    // Generate the key.
    let (cert, revocation) = CertBuilder::new()
        .set_validity_period(Duration::new(5 * 365 * 24 * 60 * 60, 0))
        .add_userid(format!("<{}>", name))
        .add_subkey(KeyFlags::empty()
                    .set_transport_encryption()
                    .set_group_key(),
                    None,
                    None)
        .generate()?;

    // First, emit the certificate.
    let n = format!("{}.cert.pgp", name);
    eprintln!("Writing certificate to {}", n);
    cert.armored().serialize(&mut File::create(n)?)?;

    // Second, emit the key.  This includes all secret key material.
    // Back this up.
    let n = format!("{}.key.pgp", name);
    eprintln!("Writing full key to {}", n);
    cert.as_tsk().armored().serialize(&mut File::create(n)?)?;

    // Third, emit they key, but only include the encryption subkey's
    // secret key material.
    let n = format!("{}.only_subkey.pgp", name);
    eprintln!("Writing key with detached primary to {}", n);
    cert.as_tsk()
        .set_filter(|k| k.fingerprint() != cert.fingerprint())
        .emit_secret_key_stubs(true) // Enable GnuPG-style.
        .armored()
        .serialize(&mut File::create(n)?)?;

    // Finally, emit a revocation certificate.  Back this up.
    let n = format!("{}.revocation.pgp", name);
    eprintln!("Writing revocation certificate to {}", n);

    // Be fancy and include comments in the revocation cert.
    let mut comments = cert.armor_headers();
    comments.insert(0, "Revocation certificate for the following key:".into());
    comments.insert(1, "".into());
    let mut w = armor::Writer::with_headers(
        File::create(n)?,
        armor::Kind::PublicKey,
        comments.iter().map(|c| ("Comment", c)))?;
    openpgp::Packet::from(revocation).serialize(&mut w)?;
    w.finalize()?;

    Ok(())
}