Commit 4aacad5 Add the initial gpg signature check code

4 files Authored and Committed by puiterwijk a year ago
Add the initial gpg signature check code

This does implement verification of signatures on commits and tags.
It does not implement getting the keys from a keyserver, verifying
the key is owned by the user, or any UI to indicate signature state.

Signed-off-by: Patrick Uiterwijk <puiterwijk@redhat.com>

    
 1 @@ -206,6 +206,10 @@
 2   # List of blacklisted group names
 3   BLACKLISTED_GROUPS = ['forks']
 4   
 5 + # GnuPG configuration
 6 + GPG_BINARY = 'gpg2'
 7 + GPG_HOMEDIR = 'gpghome'
 8 + 
 9   
10   ACLS = {
11       'create_project': 'Create a new project',
 1 @@ -11,6 +11,7 @@
 2   
 3   import datetime
 4   import hashlib
 5 + import gnupg
 6   import json
 7   import os
 8   import shutil
 9 @@ -1243,6 +1244,56 @@
10       return tags
11   
12   
13 + def _check_signature(message, signature):
14 +     """ This function performs the actual signature checking. """
15 +     gpg = gnupg.GPG(gnupghome=pagure.APP.config['GPG_HOMEDIR'],
16 +                     gpgbinary=pagure.APP.config['GPG_BINARY'])
17 +     with tempfile.NamedTemporaryFile() as sigfile:
18 +         sigfile.write(signature)
19 +         sigfile.flush()
20 +         verified = gpg.verify_data(sigfile.name, message)
21 +         return {'valid': verified.valid,
22 +                 'signer': verified.username,
23 +                 'keyid': verified.key_id}
24 +     return None
25 + 
26 + 
27 + def check_signature(raw, is_simple):
28 +     """ Returns signature state.
29 + 
30 +     is_simple is a flag whether this is a simple signature (case with tags)
31 +     where the signature is just at the end of the raw string, or if it's a
32 +     complicated (case with commit) format where we need to parse it."""
33 +     if not 'BEGIN PGP SIGNATURE' in raw:
34 +         return None
35 + 
36 +     if is_simple:
37 +         message, line, sig = raw.partition('-----BEGIN PGP SIGNATURE-----')
38 +         sig = line + sig
39 +     else:
40 +         message = ''
41 +         sig = ''
42 +         in_sig = False
43 +         for line in raw.split('\n'):
44 +             if line.startswith('gpgsig -----BEGIN PGP SIGNATURE-----'):
45 +                 in_sig = True
46 +             if in_sig:
47 +                 # The lines are all indented with a single space, remove those
48 +                 # We also remove the remaining part of the "gpgsig" indicator
49 +                 sig += line[1:].replace('pgsig ', '')
50 +                 sig += '\n'
51 +             else:
52 +                 message += line
53 +                 message += '\n'
54 + 
55 +             if in_sig and line.startswith(' -----END PGP SIGNATURE-----'):
56 +                 in_sig = False
57 +         # Remove the final newline, which is duplicate and breaks signature
58 +         message = message[:-1]
59 + 
60 +     return _check_signature(message, sig)
61 + 
62 + 
63   def get_git_tags_objects(project):
64       """ Returns the list of references of the tags created in the git
65       repositorie the specified project.
66 @@ -1278,6 +1329,8 @@
67                           '-----BEGIN PGP SIGNATURE-----', 1)[0].strip()
68                   tags[commit_time]["head_msg"] = head_msg
69                   tags[commit_time]["body_msg"] = body_msg
70 +                 tags[commit_time]["signed"] = check_signature(
71 +                     tags[commit_time]["object"].read_raw(), True)
72       sorted_tags = []
73   
74       for tag in sorted(tags, reverse=True):
 1 @@ -723,6 +723,9 @@
 2           # First commit in the repo
 3           diff = commit.tree.diff_to_tree(swap=True)
 4   
 5 +     # Get signature info
 6 +     signature = pagure.lib.git.check_signature(commit.read_raw(), False)
 7 + 
 8       return flask.render_template(
 9           'commit.html',
10           select='commits',
11 @@ -734,6 +737,7 @@
12           commit=commit,
13           diff=diff,
14           form=pagure.forms.ConfirmationForm(),
15 +         signature=signature,
16       )
17   
18   
1 @@ -11,6 +11,7 @@
2   flask
3   flask-wtf
4   flask-multistatic
5 + python-gnupg
6   kitchen
7   markdown
8   munch