def write_record(self, bdist_dir, distinfo_dir): from wheel.util import urlsafe_b64encode record_path = os.path.join(distinfo_dir, 'RECORD') record_relpath = os.path.relpath(record_path, bdist_dir) def walk(): for dir, dirs, files in os.walk(bdist_dir): dirs.sort() for f in sorted(files): yield os.path.join(dir, f) def skip(path): """Wheel hashes every possible file.""" return (path == record_relpath) with open_for_csv(record_path, 'w+') as record_file: writer = csv.writer(record_file) for path in walk(): relpath = os.path.relpath(path, bdist_dir) if skip(relpath): hash = '' size = '' else: with open(path, 'rb') as f: data = f.read() digest = hashlib.sha256(data).digest() hash = 'sha256=' + native(urlsafe_b64encode(digest)) size = len(data) record_path = os.path.relpath( path, bdist_dir).replace(os.path.sep, '/') writer.writerow((record_path, hash, size))
def _record_digest(self, data): '''Returns a three tuple of hash, size and digest.''' from wheel.util import urlsafe_b64encode digest = hashlib.sha256(data).digest() hash_text = 'sha256=' + native(urlsafe_b64encode(digest)) size = len(data) return (hash_text, size, digest)
def writestr(self, zinfo_or_arcname, bytes, compress_type=None): super(WheelFile, self).writestr(zinfo_or_arcname, bytes, compress_type) fname = (zinfo_or_arcname.filename if isinstance(zinfo_or_arcname, ZipInfo) else zinfo_or_arcname) logger.info("adding '%s'", fname) if fname != self.record_path: hash_ = self._default_algorithm(bytes) self._file_hashes[fname] = hash_.name, native(urlsafe_b64encode(hash_.digest())) self._file_sizes[fname] = len(bytes)
def writestr(self, zinfo_or_arcname, bytes, compress_type=None): ZipFile.writestr(self, zinfo_or_arcname, bytes, compress_type) fname = (zinfo_or_arcname.filename if isinstance( zinfo_or_arcname, ZipInfo) else zinfo_or_arcname) logger.info("adding '%s'", fname) if fname != self.record_path: hash_ = self._default_algorithm(bytes) self._file_hashes[fname] = hash_.name, native( urlsafe_b64encode(hash_.digest())) self._file_sizes[fname] = len(bytes)
def writestr(self, zinfo_or_arcname, data, compress_type=None): if isinstance(data, str): data = data.encode("utf-8") ZipFile.writestr(self, zinfo_or_arcname, data, compress_type) fname = (zinfo_or_arcname.filename if isinstance( zinfo_or_arcname, ZipInfo) else zinfo_or_arcname) log.info(f"adding '{fname}'") if fname != self.record_path: hash_ = self._default_algorithm(data) self._file_hashes[fname] = ( hash_.name, urlsafe_b64encode(hash_.digest()).decode("ascii"), ) self._file_sizes[fname] = len(data)
def rewrite_record(bdist_dir): """ Rewrite RECORD file with hashes for all files in `wheel_sdir` Copied from :method:`wheel.bdist_wheel.bdist_wheel.write_record` Will also unsign wheel Parameters ---------- bdist_dir : str Path of unpacked wheel file """ info_dirs = glob.glob(pjoin(bdist_dir, '*.dist-info')) if len(info_dirs) != 1: raise WheelToolsError("Should be exactly one `*.dist_info` directory") record_path = pjoin(info_dirs[0], 'RECORD') record_relpath = relpath(record_path, bdist_dir) # Unsign wheel - because we're invalidating the record hash sig_path = pjoin(info_dirs[0], 'RECORD.jws') if exists(sig_path): os.unlink(sig_path) def walk(): for dir, dirs, files in os.walk(bdist_dir): for f in files: yield pjoin(dir, f) def skip(path): """Wheel hashes every possible file.""" return (path == record_relpath) with _open_for_csv(record_path, 'w+') as record_file: writer = csv.writer(record_file) for path in walk(): relative_path = relpath(path, bdist_dir) if skip(relative_path): hash = '' size = '' else: with open(path, 'rb') as f: data = f.read() digest = hashlib.sha256(data).digest() hash = 'sha256=' + native(urlsafe_b64encode(digest)) size = len(data) path_for_record = relpath( path, bdist_dir).replace(psep, '/') writer.writerow((path_for_record, hash, size))
def rewrite_record(bdist_dir): """ Rewrite RECORD file with hashes for all files in `wheel_sdir` Copied from :method:`wheel.bdist_wheel.bdist_wheel.write_record` Will also unsign wheel Parameters ---------- bdist_dir : str Path of unpacked wheel file """ info_dir = _dist_info_dir(bdist_dir) record_path = pjoin(info_dir, 'RECORD') record_relpath = relpath(record_path, bdist_dir) # Unsign wheel - because we're invalidating the record hash sig_path = pjoin(info_dir, 'RECORD.jws') if exists(sig_path): os.unlink(sig_path) def walk(): for dir, dirs, files in os.walk(bdist_dir): for f in files: yield pjoin(dir, f) def skip(path): """Wheel hashes every possible file.""" return (path == record_relpath) with open_for_csv(record_path, 'w+') as record_file: writer = csv.writer(record_file) for path in walk(): relative_path = relpath(path, bdist_dir) if skip(relative_path): hash = '' size = '' else: with open(path, 'rb') as f: data = f.read() digest = hashlib.sha256(data).digest() hash = 'sha256=' + native(urlsafe_b64encode(digest)) size = len(data) record_path = relpath(path, bdist_dir).replace(psep, '/') writer.writerow((record_path, hash, size))
def verify(self, zipfile=None): """Configure the VerifyingZipFile `zipfile` by verifying its signature and setting expected hashes for every hash in RECORD. Caller must complete the verification process by completely reading every file in the archive (e.g. with extractall).""" sig = None if zipfile is None: zipfile = self.zipfile zipfile.strict = True record_name = '/'.join((self.distinfo_name, 'RECORD')) sig_name = '/'.join((self.distinfo_name, 'RECORD.jws')) # tolerate s/mime signatures: smime_sig_name = '/'.join((self.distinfo_name, 'RECORD.p7s')) zipfile.set_expected_hash(record_name, None) zipfile.set_expected_hash(sig_name, None) zipfile.set_expected_hash(smime_sig_name, None) record = zipfile.read(record_name) record_digest = urlsafe_b64encode(hashlib.sha256(record).digest()) try: sig = from_json(native(zipfile.read(sig_name))) except KeyError: # no signature pass if sig: headers, payload = signatures.verify(sig) if payload['hash'] != "sha256=" + native(record_digest): msg = "RECORD.sig claimed RECORD hash {0} != computed hash {1}." raise BadWheelFile( msg.format(payload['hash'], native(record_digest))) reader = csv.reader((native(r) for r in record.splitlines())) for row in reader: filename = row[0] hash = row[1] if not hash: if filename not in (record_name, sig_name): sys.stderr.write("%s has no hash!\n" % filename) continue algo, data = row[1].split('=', 1) assert algo == "sha256", "Unsupported hash algorithm" zipfile.set_expected_hash(filename, urlsafe_b64decode(binary(data)))
def rewrite_record(bdist_dir): """ Rewrite RECORD file with hashes for all files in `wheel_sdir` Copied from :method:`wheel.bdist_wheel.bdist_wheel.write_record` Will also unsign wheel Parameters ---------- bdist_dir : str Path of unpacked wheel file """ info_dir = _dist_info_dir(bdist_dir) record_path = pjoin(info_dir, "RECORD") record_relpath = relpath(record_path, bdist_dir) # Unsign wheel - because we're invalidating the record hash sig_path = pjoin(info_dir, "RECORD.jws") if exists(sig_path): os.unlink(sig_path) def walk(): for dir, dirs, files in os.walk(bdist_dir): for f in files: yield pjoin(dir, f) def skip(path): """Wheel hashes every possible file.""" return path == record_relpath with open_for_csv(record_path, "w+") as record_file: writer = csv.writer(record_file) for path in walk(): relative_path = relpath(path, bdist_dir) if skip(relative_path): hash = "" size = "" else: with open(path, "rb") as f: data = f.read() digest = hashlib.sha256(data).digest() hash = "sha256=" + native(urlsafe_b64encode(digest)) size = len(data) record_path = relpath(path, bdist_dir).replace(psep, "/") writer.writerow((record_path, hash, size))
def verify(self, zipfile=None): """Configure the VerifyingZipFile `zipfile` by verifying its signature and setting expected hashes for every hash in RECORD. Caller must complete the verification process by completely reading every file in the archive (e.g. with extractall).""" sig = None if zipfile is None: zipfile = self.zipfile zipfile.strict = True record_name = '/'.join((self.distinfo_name, 'RECORD')) sig_name = '/'.join((self.distinfo_name, 'RECORD.jws')) # tolerate s/mime signatures: smime_sig_name = '/'.join((self.distinfo_name, 'RECORD.p7s')) zipfile.set_expected_hash(record_name, None) zipfile.set_expected_hash(sig_name, None) zipfile.set_expected_hash(smime_sig_name, None) record = zipfile.read(record_name) record_digest = urlsafe_b64encode(hashlib.sha256(record).digest()) try: sig = from_json(native(zipfile.read(sig_name))) except KeyError: # no signature pass if sig: headers, payload = signatures.verify(sig) if payload['hash'] != "sha256=" + native(record_digest): msg = "RECORD.sig claimed RECORD hash {0} != computed hash {1}." raise BadWheelFile(msg.format(payload['hash'], native(record_digest))) reader = csv.reader((native(r) for r in record.splitlines())) for row in reader: filename = row[0] hash = row[1] if not hash: if filename not in (record_name, sig_name): sys.stderr.write("%s has no hash!\n" % filename) continue algo, data = row[1].split('=', 1) assert algo == "sha256", "Unsupported hash algorithm" zipfile.set_expected_hash(filename, urlsafe_b64decode(binary(data)))
def get_sha(data): return urlsafe_b64encode(sha256(data).digest())