aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKonstantin Ryabitsev <konstantin@linuxfoundation.org>2021-05-05 17:07:25 -0400
committerKonstantin Ryabitsev <konstantin@linuxfoundation.org>2021-05-05 17:07:25 -0400
commit07e2bead0efc4b46cbe985057520a29a0767bb6b (patch)
treed0daeba4a530d73d9dea91e3b44d02878e3b5d06
parent14f9971ff8dae049f7b17d1b36e06c824ba7585b (diff)
downloadpatatt-07e2bead0efc4b46cbe985057520a29a0767bb6b.tar.gz
Add most of the contributor docs
Will need to duplicate most of it to b4, but it's good to have it in both locations. B4 could be an overkill for someone just looking to validate a couple of patches. I may change my mind later and just move all of the maintainer docs into b4, once they take final shape. :) Signed-off-by: Konstantin Ryabitsev <konstantin@linuxfoundation.org>
-rw-r--r--README.rst178
-rw-r--r--patatt/__init__.py10
2 files changed, 168 insertions, 20 deletions
diff --git a/README.rst b/README.rst
index f9307c3..cbf7ed9 100644
--- a/README.rst
+++ b/README.rst
@@ -22,11 +22,11 @@ This utility uses the exact same DKIM standard to hash the headers and
the body of the patch message, but uses a different set of fields and
canonicalization routines:
- - the d= field is not used (no domain signatures involved)
- - the q= field is not used (key lookup is handled differently)
- - the c= field is not used (see below for canonicalization)
- - the i= field is optional, but MUST be the canonical email address of
- the sender, if not the same as the From: field
+- the d= field is not used (no domain signatures involved)
+- the q= field is not used (key lookup is handled differently)
+- the c= field is not used (see below for canonicalization)
+- the i= field is optional, but MUST be the canonical email address of
+ the sender, if not the same as the From: field
Canonicalization
~~~~~~~~~~~~~~~~
@@ -34,12 +34,12 @@ Patatt uses the "relaxed/simple" canonicalization as defined by the DKIM
standard, but the message is first parsed by "git-mailinfo" in order to
achieve the following:
- - normalize any content-transfer-encoding modifications (convert back
- from base64/quoted-printable/etc into 8-bit)
- - use any encountered in-body From: and Subject: headers to
- rewrite the outer message headers
- - perform any subject-line normalization in order to strip content not
- considered by git-am when applying the patch
+- normalize any content-transfer-encoding modifications (convert back
+ from base64/quoted-printable/etc into 8-bit)
+- use any encountered in-body From: and Subject: headers to
+ rewrite the outer message headers
+- perform any subject-line normalization in order to strip content not
+ considered by git-am when applying the patch
To achieve this, the message is passed through git-mailinfo with the
following flags::
@@ -66,8 +66,8 @@ it to support ED25519 keys as well. While it is possible to use any of
the DKIM-defined algorithms, patatt only supports the following
two signing/hashing schemes:
- - ed25519-sha256: exactly as defined in RFC8463
- - openpgp-sha256: uses OpenPGP to create the signature
+- ed25519-sha256: exactly as defined in RFC8463
+- openpgp-sha256: uses OpenPGP to create the signature
X-Developer-Key header
~~~~~~~~~~~~~~~~~~~~~~
@@ -155,8 +155,8 @@ To test if it's working::
$ git format-patch -1 --stdout | patatt sign > /tmp/test
If you didn't get an error message, then the process was successful. You
-can review /tmp/test to see that X-Developer-Signature and
-X-Developer-Key headers were successfully added.
+can review /tmp/test to see that ``X-Developer-Signature`` and
+``X-Developer-Key`` headers were successfully added.
You can now validate your own message::
@@ -167,7 +167,7 @@ Automatic signing via the sendemail-validate hook
If everything is working well, you can start automatically signing all
outgoing patches sent via git-send-email::
- $ echo 'patatt sign --hook "{$1}"' > .git/hooks/sendemail-validate
+ $ echo 'patatt sign --hook "${1}"' > .git/hooks/sendemail-validate
$ chmod a+x .git/hooks/sendemail-validate
PGP vs ed25519 keys
@@ -198,4 +198,148 @@ installed (pip install should have already taken care of it for you).
Getting started as git repository maintainer
--------------------------------------------
-Coming.
+Patatt implements basic signature validation, but it's a tool aimed
+primarily at contributors. If you are processing mailed-in patches, then
+you should look into using b4, which aims at making the entire process
+easier. B4 implements patatt-style signature validation starting with
+version 0.7.0.
+
+- https://pypi.org/project/b4/
+
+That said, keyring management as discussed below applies both to patatt
+and b4.
+
+In-git pubkey management
+~~~~~~~~~~~~~~~~~~~~~~~~
+The trickiest part of all decentralized PKI schemes is not the crypto
+itself, but public key distribution. PGP famously tried to solve this
+problem by relying on cross-key certification and keyservers, but the
+results were not encouraging.
+
+However, within the context of git repositories, we already have a
+suitable mechanism for distributing developer public keys, which is the
+repository itself. Consider this:
+
+- git is already decentralized and can be mirrored to multiple
+ locations, avoiding any single points of failure
+- all contents are already versioned and key additions/removals can be
+ audited and "git blame'd"
+- git commits themselves can be cryptographically signed, which allows a
+ small subset of keys to act as "trusted introducers" to many other
+ contributors
+
+The idea of using git itself for keyring management was originally
+suggested by the did:git project, though we do not currently implement
+the proposed standard itself.
+
+- https://github.com/dhuseby/did-git-spec/blob/master/did-git-spec.md
+
+Keyring structure
+~~~~~~~~~~~~~~~~~
+The keyring is structured as follows::
+
+ - dir: topdir (e.g. ".keys")
+ |
+ - dir: keytype (e.g. "ed25519" or "openpgp")
+ |
+ - dir: domainname (e.g. "example.org")
+ |
+ - dir: address-localpart (e.g. "developer")
+ |
+ - file: selector (e.g. "default")
+
+For example, let's take the following signature::
+
+ From: Konstantin Ryabitsev <konstantin@linuxfoundation.org>
+ X-Developer-Signature: v=1; a=ed25519-sha256; t=1620240207; l=2577;
+ h=from:subject; bh=yqviDBgyf3/dQgHcBe3B7fTP39SuKnYInPBxnOiuGcA=;
+ b=Xzd0287MvPE9NLX7xbQ6xnyrvqQOMK01mxHnrPmm1f6O7KKyogc8YH6IAlwIPdo+jk1CkdYYQsyZ
+ sS0cJdX2B4uTmV9mxOe7hssjtjLcj5/NU9zAw6WJARybaNAKH8rv
+
+The key would be located in the following subpath::
+
+ .keys/ed25519/linuxfoundation.org/konstantin/default
+
+If i= and s= fields are specified in the signature, as below::
+
+ X-Developer-Signature: v=1; a=ed25519-sha256; t=1620244687; l=12645;
+ i=mricon@kernel.org; s=20210505; h=from:subject;
+ bh=KRCBcYiMdeoSX0l1XJ2YzP/uJhmym3Pi6CmbN9fs4aM=;
+ b=sSY2vXzju7zU3KK4VQ5vFa5iPpDr3nrf221lnpq2+uuXmCODlAsgoqDmjKUBmbPtlY1Bcb2N0XZQ
+ 0KX+OShCAAwB5U1dtFtRnB/mgVibMxwl68A7OivGIVYe491yll5q
+
+Then the path would reflect those changes::
+
+ .keys/ed25519/kernel.org/mricon/20210505
+
+In the case of ed25519 keys, the contents of the file are just the
+public key itself, base64-encoded. For openpgp keys, the format should
+be an ascii-armored public key export, e.g. using the following
+command::
+
+ gpg -a --export --export-options export-minimal keyid
+
+What keys to add to the keyring
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+It does not really make sense to require cryptographic attestation for
+patches submitted by occasional contributors. The only keys added to the
+keyring should be those of the core maintainers who have push access to
+the "canonical" repository location, plus the keys belonging to regular
+contributors with a long-term ongoing relationship with the project.
+
+Managing the keyring: small teams
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+For smaller repositories with a handful of core maintainers, it makes
+sense to keep the keyring in the main branch, together with all other
+project files.
+
+Managing the keyring: large teams
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+For large teams with thousands of regular contributors and teams of
+subsystem maintainers (e.g. the Linux kernel), it does not make sense to
+have a centrally managed keyring tracked in the main repository.
+Instead, each subsystem maintainer team should manage their own keyring
+in a separate ref of their repository.
+
+For example, to create a blank new ref called ``refs/meta/keyring``::
+
+ git symbolic-ref HEAD refs/meta/keyring
+ git reset --hard
+ mkdir ed25519 openpgp
+
+Then they can add keyring files just as described above and commit them
+to the keyring ref without worrying that this will interfere with their
+pull requests or git-format-patch operation. To commit and push the
+files after adding them, regular git operations are used::
+
+ git commit -asS
+ git push origin HEAD:refs/meta/keyring
+ git checkout regular-branch
+
+To make changes to an existing keyring ref, a similar workflow can be
+used::
+
+ git fetch origin refs/meta/keyring
+ git checkout FETCH_HEAD
+ # Verify that the commit is signed
+ git verify-commit FETCH_HEAD
+ # make any changes to the keys
+ git commit -asS
+ git push origin HEAD:refs/meta/keyring
+ git checkout regular-branch
+
+To use the keyring with patatt or b4, just tell them which paths to
+check, via the ``keyringsrc`` setting (can be specified multiple
+times and will be checked in the listed order)::
+
+ [patatt]
+ # Empty ref means "use currently checked out ref"
+ keyringsrc = ref::.keys
+ # Use a dedicated ref called refs/meta/keyring
+ keyringsrc = ref:refs/meta/keyring:
+ # You can fetch other project's keyring into its own ref
+ keyringsrc = ref:refs/meta/someone-elses-keyring:
+
+Support and contributing patches
+--------------------------------
+Please send patches and support requests to tools@linux.kernel.org.
diff --git a/patatt/__init__.py b/patatt/__init__.py
index ed7ab7f..91c79ac 100644
--- a/patatt/__init__.py
+++ b/patatt/__init__.py
@@ -35,7 +35,7 @@ DEVSIG_HDR = b'X-Developer-Signature'
DEVKEY_HDR = b'X-Developer-Key'
REQ_HDRS = [b'from', b'subject']
DEFAULT_CONFIG = {
- 'publickeypath': ['ref::.keys', 'ref::.local-keys'],
+ 'keyringsrc': ['ref::.keys', 'ref:refs/meta/keyring:', 'ref::.local-keys'],
}
# Quick cache for key info
@@ -671,6 +671,7 @@ def make_pkey_path(keytype: str, identity: str, selector: str) -> str:
def get_public_key(source: str, keytype: str, identity: str, selector: str) -> Tuple[bytes, str]:
keypath = make_pkey_path(keytype, identity, selector)
+ logger.debug('Looking for %s in %s', keypath, source)
if source.find('ref:') == 0:
gittop = get_git_toplevel()
@@ -896,7 +897,10 @@ def cmd_validate(cmdargs, config: dict):
ddir = get_data_dir()
pdir = os.path.join(ddir, 'public')
- sources = config.get('publickeypath', list())
+ sources = config.get('keyringsrc')
+ if not sources:
+ sources = ['ref::.keys', 'ref:refs/meta/keyring:', 'ref::.local-keys']
+
if pdir not in sources:
sources.append(pdir)
@@ -1054,7 +1058,7 @@ def command() -> None:
ch.setLevel(logging.CRITICAL)
logger.addHandler(ch)
- config = get_config_from_git(r'patatt\..*', section=_args.section, defaults=DEFAULT_CONFIG)
+ config = get_config_from_git(r'patatt\..*', section=_args.section)
if 'func' not in _args:
parser.print_help()