dev-python/agate: Bump to 1.13.0
[gentoo/gentoo.git] / sec-keys / openpgp-keys-gentoo-developers / files / keyring-mangler.py
blob9019a7b3da88e91f54f4ff8b79afcccbe56f80b9
1 #!/usr/bin/env python
3 # Distributed under the terms of the GNU General Public License v2
5 # Takes as input:
6 # 1. authority keys as gentoo-auth.asc (sec-keys/openpgp-keys-gentoo-auth);
7 # 2. a downloaded, unverified bundle of active developers from qa-reports.gentoo.org (https://qa-reports.gentoo.org/output/active-devs.gpg);
8 # but this must be converted to ASCII first(!), and
9 # 3. an output file (armored).
11 # Outputs armored keyring with all expired keys dropped and all keys without
12 # a signature from the L2 developer key dropped.
14 # Usage: python keyring-mangler.py <gentoo-auth.asc> <active-devs.gpg> <output for armored keys.asc>
15 from xml.dom import ValidationErr
17 import gnupg
18 import os
19 import sys
21 AUTHORITY_KEYS = [
22 # Gentoo Authority Key L1
23 "ABD00913019D6354BA1D9A132839FE0D796198B1",
24 # Gentoo Authority Key L2 for Services
25 "18F703D702B1B9591373148C55D3238EC050396E",
26 # Gentoo Authority Key L2 for Developers
27 "2C13823B8237310FA213034930D132FF0FF50EEB",
30 L2_DEVELOPER_KEY = "30D132FF0FF50EEB"
32 # logging.basicConfig(level=os.environ.get("LOGLEVEL", "DEBUG"))
34 gentoo_auth = sys.argv[1]
35 active_devs = sys.argv[2]
36 armored_output = sys.argv[3]
38 gpg = gnupg.GPG(verbose=False, gnupghome=os.environ["GNUPGHOME"], options="--auto-check-trustdb")
39 gpg.encoding = "utf-8"
41 with open(gentoo_auth, "r", encoding="utf8") as keyring:
42 keys = keyring.read()
43 gpg.import_keys(keys)
45 gpg.trust_keys([AUTHORITY_KEYS[0]], "TRUST_ULTIMATE")
47 with open(active_devs, "r", encoding="utf8") as keyring:
48 keys = keyring.read()
49 gpg.import_keys(keys)
51 # print(keys)
52 # print(gpg.list_keys)
54 good_keys = []
56 # TODO: Use new 'problems' key from python-gnupg-0.5.1?
57 for key in gpg.list_keys(sigs=True):
58 print(f"Checking key={key['keyid']}, uids={key['uids']}")
60 # pprint.pprint(key)
62 if key["fingerprint"] in AUTHORITY_KEYS:
63 # Just add this in.
64 good_keys.append(key["fingerprint"])
65 continue
67 # https://security.stackexchange.com/questions/41208/what-is-the-exact-meaning-of-this-gpg-output-regarding-trust
68 if key["trust"] == "e":
69 # If it's expired, drop the key, as we can't easily then
70 # verify it because of gpg limitations.
71 print(
72 f"Dropping expired {key['keyid']=}, {key['uids']=} (because this prevents validation)"
74 continue
76 if key["trust"] == "-":
77 print(f"Dropping {key['keyid']=}, {key['uids']=} because no trust calculated")
78 continue
80 if key["trust"] != "f":
81 print(
82 f"Dropping {key['keyid']=}, {key['uids']=} because not calculated as fully trusted"
84 continue
86 # As a sanity check, make sure each key has a signature from
87 # the L2 developer signing key.
88 got_l2_signature = any(sig[0] == "30D132FF0FF50EEB" for sig in key["sigs"])
89 if not got_l2_signature:
90 raise ValidationErr(f"{key['uids']=} lacks a signature from L2 key!")
92 good_keys.append(key["fingerprint"])
94 if len(good_keys) <= len(AUTHORITY_KEYS):
95 raise RuntimeError("No valid developer keys were found!")
97 with open(armored_output, "w", encoding="utf8") as keyring:
98 keyring.write(gpg.export_keys(good_keys))