]> arthur.barton.de Git - bup.git/commitdiff
Support signed commits (i.e. the gpgsig header)
authorRob Browning <rlb@defaultvalue.org>
Tue, 5 Oct 2021 01:54:50 +0000 (20:54 -0500)
committerRob Browning <rlb@defaultvalue.org>
Sat, 16 Oct 2021 19:11:23 +0000 (14:11 -0500)
Signed-off-by: Rob Browning <rlb@defaultvalue.org>
Tested-by: Rob Browning <rlb@defaultvalue.org>
lib/bup/git.py
test/int/test_git.py

index dbfe78f6e81d48b7e8a21bdad7b682899ea91b01..fe297cd2e34edc4cd15734229c5bf006b5532ed1 100644 (file)
@@ -102,9 +102,26 @@ def parse_tz_offset(s):
         return - tz_off
     return tz_off
 
+def parse_commit_gpgsig(sig):
+    """Return the original signature bytes.
+
+    i.e. with the "gpgsig " header and the leading space character on
+    each continuation line removed.
+
+    """
+    if not sig:
+        return None
+    assert sig.startswith(b'gpgsig ')
+    sig = sig[7:]
+    return sig.replace(b'\n ', b'\n')
 
 # FIXME: derived from http://git.rsbx.net/Documents/Git_Data_Formats.txt
 # Make sure that's authoritative.
+
+# See also
+# https://github.com/git/git/blob/master/Documentation/technical/signature-format.txt
+# The continuation lines have only one leading space.
+
 _start_end_char = br'[^ .,:;<>"\'\0\n]'
 _content_char = br'[^\0\n<>]'
 _safe_str_rx = br'(?:%s{1,2}|(?:%s%s*%s))' \
@@ -118,7 +135,7 @@ _mergetag_rx = br'(?:\nmergetag object [abcdefABCDEF0123456789]{40}(?:\n [^\0\n]
 _commit_rx = re.compile(br'''tree (?P<tree>[abcdefABCDEF0123456789]{40})
 (?P<parents>%s*)author (?P<author_name>%s) <(?P<author_mail>%s)> (?P<asec>\d+) (?P<atz>%s)
 committer (?P<committer_name>%s) <(?P<committer_mail>%s)> (?P<csec>\d+) (?P<ctz>%s)(?P<mergetag>%s?)
-
+(?P<gpgsig>gpgsig .*\n(?: .*\n)*)?
 (?P<message>(?:.|\n)*)''' % (_parent_rx,
                              _safe_str_rx, _safe_str_rx, _tz_rx,
                              _safe_str_rx, _safe_str_rx, _tz_rx,
@@ -132,6 +149,7 @@ CommitInfo = namedtuple('CommitInfo', ['tree', 'parents',
                                        'author_sec', 'author_offset',
                                        'committer_name', 'committer_mail',
                                        'committer_sec', 'committer_offset',
+                                       'gpgsig',
                                        'message'])
 
 def parse_commit(content):
@@ -149,6 +167,7 @@ def parse_commit(content):
                       committer_mail=matches['committer_mail'],
                       committer_sec=int(matches['csec']),
                       committer_offset=parse_tz_offset(matches['ctz']),
+                      gpgsig=parse_commit_gpgsig(matches['gpgsig']),
                       message=matches['message'])
 
 
index 7965c60ce2314ab2105aa20c1ab405fa68e47720..339f8581bf84e34efcadba3be059d7fe1041aaa2 100644 (file)
@@ -305,6 +305,42 @@ def test_commit_parsing(tmpdir):
         restore_env_var(b'GIT_COMMITTER_EMAIL', orig_committer_email)
 
 
+gpgsig_example_1 = b'''tree 3fab08ade2fbbda60bef180bb8e0cc5724d6bd4d
+parent 36db87b46a95ca5079f43dfe9b72220acab7c731
+author Rob Browning <rlb@defaultvalue.org> 1633397238 -0500
+committer Rob Browning <rlb@defaultvalue.org> 1633397238 -0500
+gpgsig -----BEGIN PGP SIGNATURE-----
+ ...
+ -----END PGP SIGNATURE-----
+
+Sample signed commit.
+'''
+
+gpgsig_example_2 = b'''tree 3fab08ade2fbbda60bef180bb8e0cc5724d6bd4d
+parent 36db87b46a95ca5079f43dfe9b72220acab7c731
+author Rob Browning <rlb@defaultvalue.org> 1633397238 -0500
+committer Rob Browning <rlb@defaultvalue.org> 1633397238 -0500
+gpgsig -----BEGIN PGP SIGNATURE-----
+ ...
+ -----END PGP SIGNATURE-----
+
+Sample signed commit.
+'''
+
+def test_commit_gpgsig_parsing():
+    c = git.parse_commit(gpgsig_example_1)
+    assert c.gpgsig
+    assert c.gpgsig.startswith(b'-----BEGIN PGP SIGNATURE-----\n')
+    assert c.gpgsig.endswith(b'\n-----END PGP SIGNATURE-----\n')
+    c = git.parse_commit(gpgsig_example_2)
+    assert c.gpgsig
+    assert c.gpgsig.startswith(b'-----BEGIN PGP SIGNATURE-----')
+    assert c.gpgsig.endswith(b'\n-----END PGP SIGNATURE-----\n\n')
+
+
 def test_new_commit(tmpdir):
     environ[b'BUP_DIR'] = bupdir = tmpdir + b'/bup'
     git.init_repo(bupdir)