Anthony Towns wrote: > That's a technical issue, however -- one that seems like it should be > emminently solvable. Ensuring that any such solution is written in a way > that encourages auditability (of the code, of the input and of the output) > is an important part though, and I don't know that anyone even has a good > understanding of how that would be achieved. That probably means someone > needs to try to implement it, and then we can see what doesn't work. Perhaps something like this: * Each change to the gpg keyring is stored in a separate changeset file. Changesets can reflect any set of changes to the keyring. Changesets can also include arbitrary metadata. * Changesets are never removed or modified, only new ones added. * There's an ordering of the changesets. This ordering is stored in an index file. * The index file is only appended to, to add new changesets. * Changesets can be generated using some simple tool. This might be $EDITOR, or it might compare two keyrings. * Changesets can be fully examined to see what change they make before applying them. * Changesets can be applied by a tool that drives gnupg to make the changes. * To accept a changeset, keyring-maint gpg signs it, adds it to the index file, and gpg signs that. * keyring-maint can verify at any time that all changesets in a directory are gpg signed by trusted parties (ie, himself, or the previous keyring-maint(s)), and that the index file is signed by a trusted party. * A simple tool can build the keyring. It has two modes, for full rebuild and incremental build: 1. Start with an empty keyring, and apply each changeset in turn in the order given in the index file. Record the last applied changeset. 2. Start with a populated keyring, apply each changeset after the last applied one, and record the last applied changeset. What format to use for the changesets is an interesting question. Perhaps something like this, which is an example changeset to modify my current key, and add a new key: Changed-By: Joey Hess <joeyh@debian.org> Comment: Removing an old email address. Action: edit-key 788A3F4C Data: uid 3 deluid y save Changed-By: Joey Hess <joeyh@debian.org> Comment: Joey also wants to have two keys in the keyring, here's the new one. Action: import Data: -----BEGIN PGP PUBLIC KEY BLOCK----- Version: GnuPG v1.4.6 (GNU/Linux) mQGiBEXfns0RBACaKfCKEA7O+K10XSkxmVj/CxQovoHoQlplw/2PJdvfvPRXtQVB P6RYl7dShpZ1hT9kbcxuTbriGPjA10WP0kbg36TJ4xbddk4oXJVjpVt51AaP9Ac3 bdWd2LeCnizUyazUWoZl+ulzenFod1J8drwPs6MGvKUMZEYMKprhgnaQxwCg4AcS mpUu3iA/3kDs9GGB7HUPH5sEAJR0N5GH+Kk6D36fsV13KtijYVK9sAygKXfcCW13 nXvssNNcrR9aBCel7ZkPtIGM4tW6kNXJXUyzIfFmKHVKYbSjSeKSMAiaDNfIOwBi XA9dgFcPCVQ/Dz+zAs+qo/imnqBohrFOMsBgcP52lZzCYBv7GjGNk9dPjYEyrqul olYAA/4kJj+LHmBMCyrOFvTOxuiMbazPAbxOQtyGS4W9Kq0/zNZUIjXd0mG68w0z bTbBk2xmlll+4HSc/wbSdsm/ehbSreyp7q8nzq0fK1AQ7t/e2MPunt+ayanB9KXl 08aI5zx93LDatAd1RIYzc1u/R+tk3ks8ViC3+sdWfo1lOkhlcrQYc29zb3NvcyA8 c3Nvc29Aa2l0aXRpdHQ+iGAEExECACAFAkXfns0CGwMGCwkIBwMCBBUCCAMEFgID AQIeAQIXgAAKCRD/fMTh/I6hNFi/AJ9Oc2ZxjnQhPJ4lANYQEMqaVa/8ogCdFiXm zibrof8Wb+Sn8wXgg0XnYQa5Ag0ERd+e8RAIAJQd/dve6IqN0UnCeTd85IFzMmMG 9ExrZx/Nm4MczYcQRC9KsiBus1UFY6XImq1Z9F13B8pr0qE3YaBBA7c979fy9qDf bJ9D1jixhAU1vLasH9pTkwBN2Hvd5/Nm1wlMv06Kzsceb3EmgCHF716dmlYzia+0 v9cVdpZrc6G3jfDSvLGmNbmIHmXJM5Mia+k7SzJdKSjjcun94YzeIiM4iRGj3S88 3wtll9Mr08lexYuu3xC7SK2bM0PZaC2E3qshLhXLmjaoS9m9zinhg43g8WCXE0c6 X/ePf9dxIgxaF/TkReOpgZ+W6okkyyDVjGdC8ldoyKBpe20YV9x96V+77qsAAwUH /j1MC9Qp44y/KYthuoUxTffTeGdAkSsCsoGPiilLRE5HB3KiWhrr9oPq/6hvM+Bk 92mCdh1FVsIOmLkSrQ54y2kA3Iu8MdvHveu6nrhQIHPY+6AJVvkN/zL8h1H/+igu sCqQyyXph+cXRK8ZMwke+fkp7TqOMtXyjCn9i5RekI/S9xfbWW2zFKc9QXsyyfc3 bm2pTtaNQEkpgfBFSNrrHmgTH8ES/lOoinnfKrVz8me9iO/JK/hh6I7XigIsGbtH hLmwzyOD9IvD+QEDSmPWOTO0K3efrrSBS/aFtAYlKtx5creJgdcfobpD0Ugvlykq y2mr6UrU3ucQJ3/hdVlqR+eISQQYEQIACQUCRd+e8QIbDAAKCRD/fMTh/I6hNL/M AKC+/JPLSXz2wauck429Zv5pVN7geACfZLznlUiaJuGuMMLUs+T4bDej03s= =TNPZ -----END PGP PUBLIC KEY BLOCK----- Changed-By: Joey Hess <joeyh@debian.org> Comment: On second thought, that new key is no good Action: delete-key FC8EA134 Data: y Note that this is a relative changeset: its action depends on the keyring it's run on, since it deletes uid 3 of 788A3F4C. Which points to the need for the review tool. Luckily, it's really easy[1] to write (attached as changeset-review). (A simple modification of that also gives the changeset apply tool.) Here's its output for the changeset above: joey@kodama:~>cp input.gpg TESTRING.gpg joey@kodama:~>./changeset-review ~/TESTRING.gpg c gpg --edit-key 788A3F4C >> uid 3 gpg (GnuPG) 1.4.6; Copyright (C) 2006 Free Software Foundation, Inc. This program comes with ABSOLUTELY NO WARRANTY. This is free software, and you are welcome to redistribute it under certain conditions. See the file COPYING for details. gpg: please do a --check-trustdb pub 1024D/788A3F4C created: 1999-09-08 expires: never usage: SC trust: unknown validity: full sub 2048g/1950ED18 created: 1999-09-08 expires: never usage: E [ full ] (1). Joey Hess <joeyh@debian.org> [ full ] (2) Joey Hess <joey@kitenet.net> [ full ] (3) Joey Hess <joeyh@master.debian.org> [ full ] (4) Joey Hess <joey@mooix.net> Please note that the shown key validity is not necessarily correct unless you restart the program. pub 1024D/788A3F4C created: 1999-09-08 expires: never usage: SC trust: unknown validity: full sub 2048g/1950ED18 created: 1999-09-08 expires: never usage: E [ full ] (1). Joey Hess <joeyh@debian.org> [ full ] (2) Joey Hess <joey@kitenet.net> [ full ] (3)* Joey Hess <joeyh@master.debian.org> [ full ] (4) Joey Hess <joey@mooix.net> >> deluid >> y pub 1024D/788A3F4C created: 1999-09-08 expires: never usage: SC trust: unknown validity: full sub 2048g/1950ED18 created: 1999-09-08 expires: never usage: E [ full ] (1). Joey Hess <joeyh@debian.org> [ full ] (2) Joey Hess <joey@kitenet.net> [ full ] (3) Joey Hess <joey@mooix.net> >> save gpg operation complete gpg --import gpg: key FC8EA134: public key "sososos <ssoso@kitititt>" imported gpg: Total number processed: 1 gpg: imported: 1 gpg operation complete gpg --delete-key FC8EA134 >> y gpg (GnuPG) 1.4.6; Copyright (C) 2006 Free Software Foundation, Inc. This program comes with ABSOLUTELY NO WARRANTY. This is free software, and you are welcome to redistribute it under certain conditions. See the file COPYING for details. pub 1024D/FC8EA134 2007-02-24 sososos <ssoso@kitititt> gpg operation complete joey@kodama:~>cmp input.gpg TESTRING.gpg joey@kodama:~> One easy way to create the changesets is to have some templates for common operations such as adding a key, removing a key, changing a key, etc, and just fill in the details. A program that watches what is done in gpg and generates a changeset would be a neat alternative. If this seems approximately right I will put a bit more time into it, to get an actual usable demo system based on the full keyring data. -- see shy jo [1] Well, ok, it actually took me 3 hours..
#!/usr/bin/perl # Given a keyring and a changeset, shows what gpg would do if it # applied the changeset to the keyring. The keyring is not modified. use warnings; use strict; use File::Temp; my @allowed_actions=qw(import edit-key delete-key); my @gpgopts=qw(--command-fd 0 --no-auto-check-trustdb --no-default-keyring); my $keyring=shift || usage(); my $changeset=shift || usage(); my $testring=$keyring.".tmp.$$"; if (-e $testring) { die "$testring exists"; } system("cp", $keyring, $testring) == 0 || die "copy failed"; push @gpgopts, "--keyring", $testring; my %fields; my $field; open(CHANGESET, "<", $changeset) || die "$changeset: $!"; while (<CHANGESET>) { chomp; if (/^([^\s]+):(?:\s+(.*))?/) { $field=lc $1; if (defined $2) { $fields{$field}=$2; } else { $fields{$field}=''; } } elsif (/^\s+\.$/ && defined $field) { $fields{$field}.="\n"; } elsif (/^\s+(.*)/ && defined $field) { $fields{$field}.="\n" if length $fields{$field}; $fields{$field}.=$1; } elsif ($_ eq "") { process() if defined $field; %fields=(); } else { die "parse error on line $. of $changeset"; } } close CHANGESET; process() if defined $field; sub process { if (! exists $fields{action}) { die "$changeset missing action field"; } my @action=split(' ', $fields{action}); my $command=shift @action; if (! grep { $_ eq $command } @allowed_actions) { die "$changeset contains disallowed action \"$command\""; } if (! exists $fields{data}) { die "$changeset missing data field"; } my $pid = open(GPG, "|-"); $SIG{PIPE} = sub { die "whoops, pipe broke" }; if (! $pid) { print "gpg --$command @action\n"; exec("gpg", @gpgopts, "--$command", @action) || die("failed to run gpg"); } $|=1; GPG->autoflush(1); foreach my $line (split("\n", $fields{data})) { print ">> $line\n" if $command ne 'import'; print GPG "$line\n" || die "failed talking to gpg"; sleep 1 if $command ne 'import'; # makes output clearer } close GPG || die "gpg exited nonzero"; print "gpg operation complete\n\n"; } unlink $testring; unlink $testring."~";# gpg backup file sub usage { die "Usage: changeset-review keyring changeset\n"; }
Attachment:
signature.asc
Description: Digital signature