git ssb

0+

Monero Pub / gitian.sigs



Tree: 6eadfcbb362c72d4100b0a5e3000807c1bdd24b6

Files: 6eadfcbb362c72d4100b0a5e3000807c1bdd24b6 / verify-merge.py

7289 bytesRaw
1#!/usr/bin/env python3
2import argparse
3import os
4import subprocess
5import glob
6import sys
7
8GIT = os.getenv('GIT', 'git')
9GPG = os.getenv('GPG', 'gpg')
10GITIAN_PUBKEYS_DIR = os.getenv('GITIAN_PUBKEYS_DIR', 'gitian-pubkeys')
11
12def verify():
13 global args, workdir
14 if args.import_keys:
15 import_gpg_keys()
16 if args.refresh_keys:
17 refresh_gpg_keys()
18 assert_files = get_assert_file_list()
19 verify_gpg_sigs(assert_files)
20 verify_checksums(assert_files)
21 print('All checks passed.')
22 os.chdir(workdir)
23
24def main():
25 global args, workdir
26 args = get_parsed_args()
27 workdir = os.getcwd()
28 if args.pull_id != None:
29 pull_request()
30 else:
31 verify()
32
33def get_parsed_args():
34 parser = argparse.ArgumentParser(usage='%(prog)s [options]', description='Use this script to verify the signatures of existing gitian assert files and / or assert files in a specific pull request.')
35 parser.add_argument('-p', '--pull_id', dest='pull_id', help='GitHub Pull request id to check')
36 parser.add_argument('-r', '--remote', dest='remote', default='upstream', help='The git remote repository')
37 parser.add_argument('-t', '--target-branch', dest='target_branch', default='master', help='Remote repository merge into branch')
38 parser.add_argument('-m', '--merge', action='store_true', dest='merge', help='Merge the given pull request id')
39 parser.add_argument('-k', '--refresh-keys', action='store_true', dest='refresh_keys', help='Refresh all public keys that are currently in the gpg keyring.')
40 parser.add_argument('-i', '--import-keys', action='store_true', dest='import_keys', help='Import all public keys in the gitian-pubkeys directory to the gpg keyring.')
41 parser.add_argument('-o', '--no-verify', action='store_true', dest='no_verify', help='Do not run any signature verification')
42 parser.add_argument('-v', '--version', dest='version', help='Version number of sigs to be verified (defaults to all versions if not specified).')
43 return parser.parse_args()
44
45def pull_request():
46 global args
47 # Get branch from remote pull request and compare
48 head_branch = args.pull_id + '_head'
49 subprocess.check_call([GIT, 'fetch', args.remote])
50 subprocess.check_call([GIT, 'checkout', args.remote + '/' + args.target_branch])
51 subprocess.check_call([GIT, 'fetch', '-q', args.remote, 'pull/' + args.pull_id + '/head:' + head_branch])
52 subprocess.check_call([GIT, 'checkout', '-f', head_branch])
53 if args.merge:
54 # Hard reset the target branch to the remote's state and merge the pull request's head branch into it
55 subprocess.check_call([GIT, 'checkout', args.target_branch])
56 subprocess.check_call([GIT, 'reset', '--hard', args.remote + '/' + args.target_branch])
57 print('Merging and signing pull request #' + args.pull_id + ' , if you are using a smartcard, confirm the signature now.')
58 subprocess.check_call([GIT, 'merge', '-q', '--commit', '--no-edit', '-m', 'Merge pull request #' + args.pull_id + ' into ' + args.target_branch, '--no-ff', '--gpg-sign', head_branch])
59 if not args.no_verify:
60 verify()
61 subprocess.check_call([GIT, 'checkout', 'master'])
62 subprocess.check_call([GIT, 'branch', '-D', head_branch])
63
64def refresh_gpg_keys():
65 print('Refreshing pubkeys...')
66 subprocess.check_call([GPG, '--refresh'])
67
68def import_gpg_keys():
69 os.chdir(GITIAN_PUBKEYS_DIR)
70 print('Importing gpg pubkeys...')
71 keys = [f for f in glob.glob('*.asc', recursive=False)]
72 for key in keys:
73 subprocess.check_call([GPG, '--import', key])
74 os.chdir('../')
75
76def get_assert_file_list():
77 global args
78 # Shell glob pattern for specific version or all builds:
79 ver_pattern = args.version if args.version else 'v0*'
80 assert_files = []
81 for assert_file in sorted(glob.glob(ver_pattern + '-*/*/*.assert')):
82 pieces = assert_file.split('/')
83 release_full = pieces[0] # eg v0.15.0.1-linux
84 release_num, platform = release_full.split('-')
85 assert_files.append({
86 'release_full': release_full,
87 'release_num': release_num,
88 'platform': platform,
89 'path': assert_file,
90 'user': pieces[1]})
91 return assert_files
92
93def verify_gpg_sigs(assert_files):
94 print('Verifying signatures:')
95 is_verification_error = False
96 for assert_file in assert_files:
97 sig_file = assert_file['path'] + '.sig'
98 print(' - ' + '{message: <{fill}}'.format(message=sig_file, fill='72'), end='')
99 result = verify_gpg_sig(sig_file)
100 if result.returncode != 0:
101 is_verification_error = True
102 print('\n')
103 sys.stderr.write('ERROR:\n' + result.stderr + '-' * 80 + '\n')
104 else:
105 print(' [OK]')
106 if is_verification_error:
107 sys.stderr.write('ERROR: One or more signatures failed verification.\n')
108 exit(1)
109 print('All signatures verified correctly.\n')
110
111def verify_gpg_sig(sig_file):
112 return subprocess.run([GPG, '--verify', sig_file], capture_output=True, encoding='utf-8')
113
114def verify_checksums(assert_files):
115 print('Beginning binary checksum comparison...\n')
116 # Check that the contents between the assertion signers match.
117 # This is meant for quick verification, not for validation of their contents.
118 # TODO: prevent false positives related to filenames / whitespace / formatting.
119 prev_release_num = ''
120 prev_release_full = ''
121 prev_platform = ''
122 for assert_file in assert_files:
123 release_full = assert_file['release_full']
124 if release_full != prev_release_full:
125 first_user = assert_file['user']
126 first_file = assert_file['path']
127 prev_release_full = release_full
128 if prev_release_num != assert_file['release_num']:
129 print(' ' + assert_file['release_num'])
130 prev_release_num = assert_file['release_num']
131 f = open(first_file, 'r')
132 first_file_contents = f.readlines()
133 f.close()
134 continue
135 platform = assert_file['platform']
136 if platform != prev_platform:
137 prev_platform = platform
138 print(' ' + platform)
139 print(' ' + first_user)
140 print(' ' + assert_file['user'])
141 assert_file_handle = open(assert_file['path'], 'r')
142 assert_file_contents = assert_file_handle.readlines()
143 assert_file_handle.close()
144 for i in range(len(assert_file_contents)):
145 # Compare each line in the assertion file until base_manifests:
146 if assert_file_contents[i] == '- base_manifests: !!omap\n':
147 break
148 # The OSX SDK may change from time to time:
149 if 'sdk' in assert_file_contents[i]:
150 continue
151 if assert_file_contents[i] != first_file_contents[i]:
152 sys.stderr.write('ERROR: Found conflicting contents on line: ' + str(i) + ' of file ')
153 sys.stderr.write(assert_file['path'] + ':\n' + assert_file_contents[i])
154 sys.stderr.write(first_file + ':\n' + first_file_contents[i])
155 exit(1)
156 print('No discrepancies found in assertion files.')
157
158if __name__ == '__main__':
159 main()
160

Built with git-ssb-web