Skip to content

Commit b5ab93f

Browse files
Verify signatures of tarballs (#108)
If they stop signing the tzdata releases in the future, or if this turns out to just create a bunch of pointless noise, we can just disable this check. It's a nice-to-have thing as long as the signing works well, but my impression is that the modern view of GPG-signed releases is that it doesn't add much to the security model, and plus I don't see it as especially risky if we get malicious data anyway.
1 parent 6f866b8 commit b5ab93f

File tree

2 files changed

+27
-2
lines changed

2 files changed

+27
-2
lines changed

tox.ini

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ deps =
2727
requests
2828
click
2929
parver
30+
python-gnupg
3031
commands =
3132
python update.py {posargs}
3233

update.py

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
from datetime import datetime, timezone
1616

1717
import click
18+
import gnupg # type: ignore
1819
import parver # type: ignore
1920
import requests
2021

@@ -37,13 +38,15 @@ def download_tzdb_tarballs(
3738
"""Download the tzdata and tzcode tarballs."""
3839
tzdata_file = f"tzdata{version}.tar.gz"
3940
tzcode_file = f"tzcode{version}.tar.gz"
41+
tzdata_file_asc = tzdata_file + ".asc"
42+
tzcode_file_asc = tzcode_file + ".asc"
4043

4144
target_dir = working_dir / version / "download"
4245
# mkdir -p target_dir
4346
target_dir.mkdir(parents=True, exist_ok=True)
4447

4548
download_locations = []
46-
for filename in [tzdata_file, tzcode_file]:
49+
for filename in [tzdata_file, tzcode_file, tzdata_file_asc, tzcode_file_asc]:
4750
download_location = target_dir / filename
4851
download_locations.append(download_location)
4952

@@ -58,6 +61,25 @@ def download_tzdb_tarballs(
5861
with open(download_location, "wb") as f:
5962
f.write(r.content)
6063

64+
# Verify tarballs
65+
gpg_home = tempfile.TemporaryDirectory()
66+
gpg = gnupg.GPG(gnupghome=gpg_home.name)
67+
gpg.recv_keys("hkps://keyserver.ubuntu.com", "ed97e90e62aa7e34")
68+
69+
for tar, asc in [(tzdata_file, tzdata_file_asc), (tzcode_file, tzcode_file_asc)]:
70+
tar_path = target_dir / tar
71+
sig_path = target_dir / asc
72+
73+
if not tar_path.exists() or not sig_path.exists():
74+
raise FileNotFoundError(
75+
f"Missing file or signature: {tar_path}, {sig_path}"
76+
)
77+
78+
with open(sig_path, "rb") as f:
79+
check = gpg.verify_file(f, str(tar_path))
80+
if not check.valid:
81+
raise RuntimeError(f"signature verification failed for {tar_path}")
82+
6183
return download_locations
6284

6385

@@ -95,7 +117,7 @@ def retrieve_local_tarballs(
95117
def unpack_tzdb_tarballs(
96118
download_locations: Sequence[pathlib.Path],
97119
) -> pathlib.Path:
98-
assert len(download_locations) == 2
120+
assert len(download_locations) == 4
99121
assert download_locations[0].parent == download_locations[1].parent
100122
base_dir = download_locations[0].parent.parent
101123
target_dir = base_dir / "tzdb"
@@ -107,6 +129,8 @@ def unpack_tzdb_tarballs(
107129
target_dir.mkdir()
108130

109131
for tarball in download_locations:
132+
if tarball.suffix == ".asc":
133+
continue
110134
logging.info("Unpacking %s to %s", tarball, target_dir)
111135
subprocess.run(
112136
["tar", "-xf", os.fspath(tarball.absolute())],

0 commit comments

Comments
 (0)