Skip to content

Commit 90000ca

Browse files
committed
address comments to kivy#2351, bringing hashes in line with pypi
1 parent 406a258 commit 90000ca

File tree

2 files changed

+71
-42
lines changed

2 files changed

+71
-42
lines changed

pythonforandroid/recipe.py

Lines changed: 40 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -53,22 +53,21 @@ class Recipe(with_metaclass(RecipeMeta)):
5353
'''A string giving the version of the software the recipe describes,
5454
e.g. ``2.0.3`` or ``master``.'''
5555

56-
md5sum = None
57-
'''The md5sum of the source from the :attr:`url`. Non-essential, but
58-
you should try to include this, it is used to check that the download
59-
finished correctly.
56+
sha256sum = None
57+
'''The sha256sum of the source from the :attr:`url`. As of 2020, pip
58+
recommendes use of this hash. You should try to include this. It
59+
is used to check that the download finished correctly.
6060
'''
6161

62-
sha512sum = None
63-
'''The sha512sum of the source from the :attr:`url`. Non-essential, but
64-
you should try to include this, it is used to check that the download
65-
finished correctly.
62+
md5sum = None
63+
'''The md5sum of the source from the :attr:`url`. Non-essential. It
64+
is used to check that the download finished correctly.
6665
'''
6766

68-
blake2bsum = None
69-
'''The blake2bsum of the source from the :attr:`url`. Non-essential, but
70-
you should try to include this, it is used to check that the download
71-
finished correctly.
67+
blake2b_256sum = None
68+
'''The blake2b_256sum of the source from the :attr:`url`. Non-essential,
69+
but you should try to include this, it is used to check that the
70+
download finished correctly.
7271
'''
7372

7473
depends = []
@@ -355,8 +354,8 @@ def download(self):
355354

356355
url = self.versioned_url
357356
expected_digests = {}
358-
for alg in set(hashlib.algorithms_guaranteed) | set(('md5', 'sha512', 'blake2b')):
359-
expected_digest = getattr(self, alg + 'sum') if hasattr(self, alg + 'sum') else None
357+
for alg in set(hashlib.algorithms_guaranteed) | set(('sha256', 'md5', 'blake2b_256')):
358+
expected_digest = getattr(self, alg + 'sum', None)
360359
ma = match(u'^(.+)#' + alg + u'=([0-9a-f]{32,})$', url)
361360
if ma: # fragmented URL?
362361
if expected_digest:
@@ -379,16 +378,7 @@ def download(self):
379378
if not exists(marker_filename):
380379
shprint(sh.rm, filename)
381380
else:
382-
for alg, expected_digest in expected_digests.items():
383-
current_digest = algsum(alg, filename)
384-
if current_digest != expected_digest:
385-
debug('* Generated {}sum: {}'.format(alg,
386-
current_digest))
387-
debug('* Expected {}sum: {}'.format(alg,
388-
expected_digest))
389-
raise ValueError(
390-
('Generated {0}sum does not match expected {0}sum '
391-
'for {1} recipe').format(alg, self.name))
381+
self.verify_algsum(expected_digests, filename)
392382
do_download = False
393383

394384
# If we got this far, we will download
@@ -400,16 +390,7 @@ def download(self):
400390
shprint(sh.touch, marker_filename)
401391

402392
if exists(filename) and isfile(filename):
403-
for alg, expected_digest in expected_digests.items():
404-
current_digest = algsum(alg, filename)
405-
if current_digest != expected_digest:
406-
debug('* Generated {}sum: {}'.format(alg,
407-
current_digest))
408-
debug('* Expected {}sum: {}'.format(alg,
409-
expected_digest))
410-
raise ValueError(
411-
('Generated {0}sum does not match expected {0}sum '
412-
'for {1} recipe').format(alg, self.name))
393+
self.verify_algsum(expected_digests, filename)
413394
else:
414395
info('{} download already cached, skipping'.format(self.name))
415396

@@ -1195,10 +1176,28 @@ def reduce_object_file_names(self, dirn):
11951176
shprint(sh.mv, filen, join(file_dirname, parts[0] + '.so'))
11961177

11971178

1198-
def algsum(alg, filen):
1199-
'''Calculate the digest of a file.
1200-
'''
1201-
with open(filen, 'rb') as fileh:
1202-
digest = getattr(hashlib, alg)(fileh.read())
1203-
1204-
return digest.hexdigest()
1179+
def verify_algsum(self, algs, filen):
1180+
'''Verify digest of a file.
1181+
'''
1182+
1183+
for alg, expected_digest in algs.items():
1184+
1185+
with open(filen, 'rb') as fileh:
1186+
func = getattr(hashlib, alg, None)
1187+
if func is not None:
1188+
digest = func(fileh.read())
1189+
elif '_' in alg: # for custom digest_sizes, such as blake2b_256
1190+
offset = alg.rfind('_')
1191+
func = getattr(hashlib, alg[:offset])
1192+
digest_size = int(alg[offset + 1:])
1193+
digest = func(fileh.read(), digest_size = digest_size)
1194+
current_digest = digest.hexdigest()
1195+
1196+
if current_digest != expected_digest:
1197+
debug('* Generated {}sum: {}'.format(alg,
1198+
current_digest))
1199+
debug('* Expected {}sum: {}'.format(alg,
1200+
expected_digest))
1201+
raise ValueError(
1202+
('Generated {0}sum does not match expected {0}sum '
1203+
'for {1} recipe').format(alg, self.name))

tests/test_recipe.py

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -115,13 +115,23 @@ def test_download_url_not_set(self):
115115
mock.call('Skipping test_recipe download as no URL is set')]
116116

117117
@staticmethod
118-
def get_dummy_python_recipe_for_download_tests():
118+
def get_dummy_python_recipe_for_download_tests(good_digests = [], bad_digest = []):
119119
"""
120120
Helper method for creating a test recipe used in download tests.
121121
"""
122122
recipe = DummyRecipe()
123123
filename = 'Python-3.7.4.tgz'
124124
url = 'https://www.python.org/ftp/python/3.7.4/{}'.format(filename)
125+
for bad_digest in bad_digests:
126+
setattr(recipe, bad_digest + 'sum', 'x')
127+
if 'sha256' in good_digests:
128+
recipe.sha256sum = 'd63e63e14e6d29e17490abbe6f7d17afb3db182dbd801229f14e55f4157c4ba3'
129+
if 'md5' in good_digests:
130+
recipe.md5sum = '68111671e5b2db4aef7b9ab01bf0f9be'
131+
if 'blake2b_256' in good_digests:
132+
recipe.blake2b_256sum = '24a28aeace25e4397edc62ede83790541257e7281cc33539cece84dd3371f640'
133+
if 'sha512' in good_digests:
134+
recipe.sha512sum = 'c25a72ad792f7c1b4c2f79faebbe9608d04b04b2fe58ab804cb4732cdaa75ea93d175f5e52b38e91cb6ae0559ea6b645d802c8b6a869584e8bb9b5018367ce3d'
125135
recipe._url = url
126136
recipe.ctx = Context()
127137
return recipe, filename
@@ -146,6 +156,26 @@ def test_download_url_is_set(self):
146156
'https://www.python.org/ftp/python/3.7.4/Python-3.7.4.tgz')]
147157
assert m_touch.call_count == 1
148158

159+
def test_hashes(self):
160+
"""
161+
Verifies digest hashes are enforced.
162+
"""
163+
with (
164+
tempfile.TemporaryDirectory()) as temp_dir:
165+
digests = set(('sha256', 'md5', 'blake2b_256', 'sha512'))
166+
recipe, filename = self.get_dummy_python_recipe_for_download_tests(
167+
good_digests = digests)
168+
recipe.ctx.setup_dirs(temp_dir)
169+
recipe.download()
170+
for bad_digest in digests:
171+
good_digests = digests - set([bad_digest])
172+
recipe, filename = self.get_dummy_python_recipe_for_download_tests(
173+
good_digests = good_digests,
174+
bad_digests = [bad_digest])
175+
recipe.ctx.setup_dirs(temp_dir)
176+
with self.assertRaises(ValueError) as e:
177+
recipe.download()
178+
149179
def test_download_file_scheme_https(self):
150180
"""
151181
Verifies `urlretrieve()` is being called on https downloads.

0 commit comments

Comments
 (0)