mirror of
https://github.com/bitcoin/bitcoin.git
synced 2024-11-19 18:09:47 +01:00
contrib: Add verifybinaries command for specifying files to verify
In addition to verifying the published releases with the `pub` command, the verifybinaries script is updated to take a `bin` command where the user specifies the local files, sums, and sigs to verify.
This commit is contained in:
parent
e4d5778228
commit
6b2cebfa2f
@ -42,7 +42,7 @@ import textwrap
|
||||
import urllib.request
|
||||
import enum
|
||||
from hashlib import sha256
|
||||
from pathlib import Path
|
||||
from pathlib import PurePath
|
||||
|
||||
# The primary host; this will fail if we can't retrieve files from here.
|
||||
HOST1 = "https://bitcoincore.org"
|
||||
@ -440,11 +440,11 @@ def verify_shasums_signature(
|
||||
return (ReturnCode.SUCCESS, good_trusted, good_untrusted, unknown, bad)
|
||||
|
||||
|
||||
def parse_sums_file(sums_file_path: Path, filename_filter: str) -> t.List[t.List[str]]:
|
||||
def parse_sums_file(sums_file_path: str, filename_filter: t.List[str]) -> t.List[t.List[str]]:
|
||||
# extract hashes/filenames of binaries to verify from hash file;
|
||||
# each line has the following format: "<hash> <binary_filename>"
|
||||
with open(sums_file_path, 'r', encoding='utf8') as hash_file:
|
||||
return [line.split()[:2] for line in hash_file if filename_filter in line]
|
||||
return [line.split()[:2] for line in hash_file if len(filename_filter) == 0 or any(f in line for f in filename_filter)]
|
||||
|
||||
|
||||
def verify_binary_hashes(hashes_to_verify: t.List[t.List[str]]) -> t.Tuple[ReturnCode, t.Dict[str, str]]:
|
||||
@ -579,6 +579,73 @@ def verify_published_handler(args: argparse.Namespace) -> ReturnCode:
|
||||
return ReturnCode.SUCCESS
|
||||
|
||||
|
||||
def verify_binaries_handler(args: argparse.Namespace) -> ReturnCode:
|
||||
binary_to_basename = {}
|
||||
for file in args.binary:
|
||||
binary_to_basename[PurePath(file).name] = file
|
||||
|
||||
sums_sig_path = None
|
||||
if args.sums_sig_file:
|
||||
sums_sig_path = Path(args.sums_sig_file)
|
||||
else:
|
||||
log.info(f"No signature file specified, assuming it is {args.sums_file}.asc")
|
||||
sums_sig_path = Path(args.sums_file).with_suffix(".asc")
|
||||
|
||||
# Verify the signature on the SHA256SUMS file
|
||||
sigs_status, good_trusted, good_untrusted, unknown, bad = verify_shasums_signature(sums_sig_path, args.sums_file, args)
|
||||
if sigs_status != ReturnCode.SUCCESS:
|
||||
return sigs_status
|
||||
|
||||
# Extract hashes and filenames
|
||||
hashes_to_verify = parse_sums_file(args.sums_file, [k for k, n in binary_to_basename.items()])
|
||||
if not hashes_to_verify:
|
||||
log.error(f"No files in {args.sums_file} match the specified binaries")
|
||||
return ReturnCode.NO_BINARIES_MATCH
|
||||
|
||||
# Make sure all files are accounted for
|
||||
sums_file_path = Path(args.sums_file)
|
||||
missing_files = []
|
||||
files_to_hash = []
|
||||
if len(binary_to_basename) > 0:
|
||||
for file_hash, file in hashes_to_verify:
|
||||
files_to_hash.append([file_hash, binary_to_basename[file]])
|
||||
del binary_to_basename[file]
|
||||
if len(binary_to_basename) > 0:
|
||||
log.error(f"Not all specified binaries are in {args.sums_file}")
|
||||
return ReturnCode.NO_BINARIES_MATCH
|
||||
else:
|
||||
log.info(f"No binaries specified, assuming all files specified in {args.sums_file} are located relatively")
|
||||
for file_hash, file in hashes_to_verify:
|
||||
file_path = Path(sums_file_path.parent.joinpath(file))
|
||||
if file_path.exists():
|
||||
files_to_hash.append([file_hash, str(file_path)])
|
||||
else:
|
||||
missing_files.append(file)
|
||||
|
||||
# verify hashes
|
||||
hashes_status, files_to_hashes = verify_binary_hashes(files_to_hash)
|
||||
if hashes_status != ReturnCode.SUCCESS:
|
||||
return hashes_status
|
||||
|
||||
if args.json:
|
||||
output = {
|
||||
'good_trusted_sigs': [str(s) for s in good_trusted],
|
||||
'good_untrusted_sigs': [str(s) for s in good_untrusted],
|
||||
'unknown_sigs': [str(s) for s in unknown],
|
||||
'bad_sigs': [str(s) for s in bad],
|
||||
'verified_binaries': files_to_hashes,
|
||||
"missing_binaries": missing_files,
|
||||
}
|
||||
print(json.dumps(output, indent=2))
|
||||
else:
|
||||
for filename in files_to_hashes:
|
||||
print(f"VERIFIED: {filename}")
|
||||
for filename in missing_files:
|
||||
print(f"MISSING: {filename}")
|
||||
|
||||
return ReturnCode.SUCCESS
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
parser.add_argument(
|
||||
@ -638,6 +705,15 @@ def main():
|
||||
'(Sometimes bitcoin.org lags behind bitcoincore.org.)')
|
||||
)
|
||||
|
||||
bin_parser = subparsers.add_parser("bin", help="Verify local binaries.")
|
||||
bin_parser.set_defaults(func=verify_binaries_handler)
|
||||
bin_parser.add_argument("--sums-sig-file", "-s", help="Path to the SHA256SUMS.asc file to verify")
|
||||
bin_parser.add_argument("sums_file", help="Path to the SHA256SUMS file to verify")
|
||||
bin_parser.add_argument(
|
||||
"binary", nargs="*",
|
||||
help="Path to a binary distribution file to verify. Can be specified multiple times for multiple files to verify."
|
||||
)
|
||||
|
||||
args = parser.parse_args()
|
||||
if args.quiet:
|
||||
log.setLevel(logging.WARNING)
|
||||
|
Loading…
Reference in New Issue
Block a user