From: Konstantin Ryabitsev <konstantin@linuxfoundation.org>
To: signatures@kernel.org
Subject: [PATCH] Sign Message-Id header if present
Date: Fri, 21 May 2021 11:43:21 -0400 [thread overview]
Message-ID: <e6fbfe8cafa3da6e9a2791325a633d4bab0c7460.1621611731.git.konstantin@linuxfoundation.org> (raw)
It is useful to sign the message-id header, because it is frequently
used as the patch identifier. Unfortunately, unless git-format-patch is
run with --thread, the message-id won't be generated until *after* the
sendemail-validate hook is invoked, so most of the time we won't end up
signing that header.
However, having this as an option is handy.
Signed-off-by: Konstantin Ryabitsev <konstantin@linuxfoundation.org>
---
man/patatt.5 | 2 +-
man/patatt.5.rst | 4 +--
patatt/__init__.py | 68 ++++++++++++++++++++++++++++++++--------------
3 files changed, 51 insertions(+), 23 deletions(-)
diff --git a/man/patatt.5 b/man/patatt.5
index 70cea05..1e46a8a 100644
--- a/man/patatt.5
+++ b/man/patatt.5
@@ -1,6 +1,6 @@
.\" Man page generated from reStructuredText.
.
-.TH PATATT 5 "2021-05-11" "0.2.0" ""
+.TH PATATT 5 "2021-05-21" "0.4.0" ""
.SH NAME
PATATT \- DKIM-like cryptographic patch attestation
.
diff --git a/man/patatt.5.rst b/man/patatt.5.rst
index 2ab345c..845595b 100644
--- a/man/patatt.5.rst
+++ b/man/patatt.5.rst
@@ -5,10 +5,10 @@ DKIM-like cryptographic patch attestation
-----------------------------------------
:Author: mricon@kernel.org
-:Date: 2021-05-11
+:Date: 2021-05-21
:Copyright: The Linux Foundation and contributors
:License: MIT-0
-:Version: 0.2.0
+:Version: 0.4.0
:Manual section: 5
SYNOPSIS
diff --git a/patatt/__init__.py b/patatt/__init__.py
index 92dee85..76028ae 100644
--- a/patatt/__init__.py
+++ b/patatt/__init__.py
@@ -41,12 +41,13 @@ RES_ERROR = 16
RES_BADSIG = 32
REQ_HDRS = [b'from', b'subject']
+OPT_HDRS = [b'message-id']
# Quick cache for key info
KEYCACHE = dict()
# My version
-__VERSION__ = '0.3.0'
+__VERSION__ = '0.4.0-dev'
MAX_SUPPORTED_FORMAT_VERSION = 1
@@ -126,29 +127,56 @@ class DevsigHeader:
self._body_hash = base64.b64encode(hashed.digest())
# do any git-mailinfo normalization prior to calling this
- def set_headers(self, headers: list) -> None:
- hfield = self.get_field('h')
- if hfield:
- # Make sure REQ_HEADERS are in this list
- want_headers = [x.strip() for x in hfield.split(b':')]
- for rqhdr in REQ_HDRS:
- if rqhdr not in want_headers:
- raise ValidationError('Signature is missing a required header %s' % rqhdr.decode())
- else:
- want_headers = REQ_HDRS
-
- self._headervals = list()
- for header in headers:
+ def set_headers(self, headers: list, mode: str) -> None:
+ parsed = list()
+ allhdrs = set()
+ # DKIM operates on headers in reverse order
+ for header in reversed(headers):
try:
left, right = header.split(b':', 1)
hname = left.strip().lower()
- if hname not in want_headers:
- continue
+ parsed.append((hname, right))
+ allhdrs.add(hname)
except ValueError:
continue
- self._headervals.append(hname + b':' + DevsigHeader._dkim_canonicalize_header(right))
- self.hdata['h'] = b':'.join(want_headers)
+ reqset = set(REQ_HDRS)
+ optset = set(OPT_HDRS)
+ self._headervals = list()
+ if mode == 'sign':
+ # Make sure REQ_HDRS is a subset of allhdrs
+ if not reqset.issubset(allhdrs):
+ raise SigningError('The following required headers not present: %s'
+ % (b', '.join(reqset.difference(allhdrs)).decode()))
+ # Add optional headers that are actually present
+ optpresent = allhdrs.intersection(optset)
+ signlist = list(reqset.union(optpresent))
+ self.hdata['h'] = b':'.join(signlist)
+
+ elif mode == 'validate':
+ hfield = self.get_field('h')
+ signlist = [x.strip() for x in hfield.split(b':')]
+ # Make sure REQ_HEADERS are in this set
+ if not reqset.issubset(set(signlist)):
+ raise ValidationError('The following required headers not signed: %s'
+ % (b', '.join(reqset.difference(set(signlist))).decode()))
+ else:
+ raise RuntimeError('Unknown set_header mode: %s' % mode)
+
+ for shname in signlist:
+ if shname not in allhdrs:
+ # Per RFC:
+ # Nonexistent header fields do not contribute to the signature computation (that is, they are
+ # treated as the null input, including the header field name, the separating colon, the header field
+ # value, and any CRLF terminator).
+ continue
+ at = 0
+ for hname, rawval in list(parsed):
+ if hname == shname:
+ self._headervals.append(hname + b':' + DevsigHeader._dkim_canonicalize_header(rawval))
+ parsed.pop(at)
+ break
+ at += 1
def sanity_check(self) -> None:
if 'a' not in self.hdata:
@@ -435,7 +463,7 @@ class PatattMessage:
self.headers.remove(header)
self.git_canonicalize()
ds = DevsigHeader()
- ds.set_headers(self.canon_headers)
+ ds.set_headers(self.canon_headers, mode='sign')
ds.set_body(self.canon_body)
ds.set_field('l', str(len(self.canon_body)))
if identity and identity != self.canon_identity:
@@ -478,7 +506,7 @@ class PatattMessage:
raise ValidationError('No signatures matching identity %s' % identity)
self.git_canonicalize()
- vds.set_headers(self.canon_headers)
+ vds.set_headers(self.canon_headers, mode='validate')
if trim_body:
lfield = vds.get_field('l')
--
2.31.1
reply other threads:[~2021-05-21 15:43 UTC|newest]
Thread overview: [no followups] expand[flat|nested] mbox.gz Atom feed
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=e6fbfe8cafa3da6e9a2791325a633d4bab0c7460.1621611731.git.konstantin@linuxfoundation.org \
--to=konstantin@linuxfoundation.org \
--cc=signatures@kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).