diff --git a/.fmf/version b/.fmf/version new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/.fmf/version @@ -0,0 +1 @@ +1 diff --git a/README.md b/README.md new file mode 100644 index 0000000..33554c0 --- /dev/null +++ b/README.md @@ -0,0 +1,59 @@ +# PyCA cryptography + +https://cryptography.io/en/latest/ + +## Packaging python-cryptography + +The example assumes + +* Fedora Rawhide (f34) +* PyCA cryptography release ``3.4`` +* Update Bugzilla issue is ``RHBZ#00000001`` + +### Build new python-cryptography + +Switch and update branch + +```shell +fedpkg switch-branch rawhide +fedpkg pull +``` + +Bump version and get sources + +```shell +rpmdev-bumpspec -c "Update to 3.4 (#00000001)" -n 3.4 python-cryptography.spec +spectool -gf python-cryptography.spec +``` + +Upload new source + +```shell +fedpkg new-sources cryptography-3.4.tar.gz +``` + +Commit changes + +```shell +fedpkg commit --clog +fedpkg push +``` + +Build + +```shell +fedpkg build +``` + +## RHEL/CentOS builds + +RHEL and CentOS use a different approach for Rust crates packaging than +Fedora. On Fedora Rust dependencies are packaged as RPMs, e.g. +``rust-pyo3+default-devel`` RPM. These packages don't exist on RHEL and +CentOS. Instead python-cryptography uses a tar ball with vendored crates. +The tar ball is created by a script: + +```shell +./vendor_rust.py +rhpkg upload cryptography-3.4-vendor.tar.bz2 +``` diff --git a/plan.fmf b/plan.fmf new file mode 100644 index 0000000..82e49a1 --- /dev/null +++ b/plan.fmf @@ -0,0 +1,51 @@ +execute: + how: tmt +discover: + how: shell + dist-git-source: true + tests: + - name: bundled tests - Prepare part + require: + - python3.11-pytest + - python3.11-cryptography + - python3.11-pip + test: | + cd $(dirname $TMT_SOURCE_DIR/cryptography-*/tests) && + rm -rf tests/hypothesis/ tests/test_fernet.py \ + tests/hazmat/primitives/test_scrypt.py && + cat $TMT_TREE/conftest-skipper.py >> tests/conftest.py && + sed -i -e 's/ --benchmark-disable//' pyproject.toml && + pip3.11 install pytz==2022.7.1 pytest-subtests==0.9.0 + - name: unittests-basic + test: | + cd $(dirname $TMT_SOURCE_DIR/cryptography-*/tests) && + PYTHONPATH=./vectors pytest-3.11 tests/test_*.py + - name: unittests-x509 + test: | + cd $(dirname $TMT_SOURCE_DIR/cryptography-*/tests) && + PYTHONPATH=./vectors OPENSSL_ENABLE_SHA1_SIGNATURES=yes pytest-3.11 tests/x509/ + - name: unittests-hazmat + test: | + cd $(dirname $TMT_SOURCE_DIR/cryptography-*/tests) && + PYTHONPATH=./vectors pytest-3.11 -k 'not test_openssl_memleak' tests/hazmat/backends/ tests/hazmat/bindings/ + - name: unittests-primitives-aead + test: | + cd $(dirname $TMT_SOURCE_DIR/cryptography-*/tests) && + PYTHONPATH=./vectors pytest-3.11 tests/hazmat/primitives/test_aead.py + - name: unittests-primitives-aes + test: | + cd $(dirname $TMT_SOURCE_DIR/cryptography-*/tests) && + PYTHONPATH=./vectors pytest-3.11 tests/hazmat/primitives/test_aes.py::TestAESModeCBC \ + tests/hazmat/primitives/test_aes.py::TestAESModeCTR \ + tests/hazmat/primitives/test_aes_gcm.py::TestAESModeGCM + - name: unittests-primitives-a-e + test: | + cd $(dirname $TMT_SOURCE_DIR/cryptography-*/tests) && + PYTHONPATH=./vectors pytest-3.11 tests/hazmat/primitives/test_arc4.py \ + tests/hazmat/primitives/test_asym_utils.py \ + tests/hazmat/primitives/test_[b-e]*.py + - name: unittests-primitives-f-z + test: | + cd $(dirname $TMT_SOURCE_DIR/cryptography-*/tests) && + PYTHONPATH=./vectors OPENSSL_ENABLE_SHA1_SIGNATURES=yes pytest-3.11 tests/hazmat/primitives/test_[f-z]*.py \ + tests/hazmat/primitives/twofactor diff --git a/vendor_rust.py b/vendor_rust.py new file mode 100755 index 0000000..cd8355e --- /dev/null +++ b/vendor_rust.py @@ -0,0 +1,112 @@ +#!/usr/bin/python3 +"""Vendor PyCA cryptography's Rust crates +""" +import argparse +import os +import re +import tarfile +import tempfile +import shutil +import subprocess +import sys + +VENDOR_DIR = "vendor" +CARGO_TOML = "src/rust/Cargo.toml" +RE_VERSION = re.compile("Version:\s*(.*)") + +parser = argparse.ArgumentParser(description="Vendor Rust packages") +parser.add_argument( + "--spec", default="python-cryptography.spec", help="cryptography source tar bundle" +) + + +def cargo(cmd, manifest): + args = ["cargo", cmd, f"--manifest-path={manifest}"] + return subprocess.check_call( + args, stdout=subprocess.DEVNULL, stderr=sys.stderr, env={} + ) + + +def tar_reset(tarinfo): + """Reset user, group, mtime, and mode to create reproducible tar""" + tarinfo.uid = 0 + tarinfo.gid = 0 + tarinfo.uname = "root" + tarinfo.gname = "root" + tarinfo.mtime = 0 + if tarinfo.type == tarfile.DIRTYPE: + tarinfo.mode = 0o755 + else: + tarinfo.mode = 0o644 + if tarinfo.pax_headers: + raise ValueError(tarinfo.name, tarinfo.pax_headers) + return tarinfo + + +def tar_reproducible(tar, basedir): + """Create reproducible tar file""" + + content = [basedir] + for root, dirs, files in os.walk(basedir): + for directory in dirs: + content.append(os.path.join(root, directory)) + for filename in files: + content.append(os.path.join(root, filename)) + content.sort() + + for fn in content: + tar.add(fn, filter=tar_reset, recursive=False, arcname=fn) + + +def main(): + args = parser.parse_args() + spec = args.spec + + # change cwd to work in bundle directory + here = os.path.dirname(os.path.abspath(spec)) + os.chdir(here) + + # extract version number from bundle name + with open(spec) as f: + for line in f: + mo = RE_VERSION.search(line) + if mo is not None: + version = mo.group(1) + break + else: + raise ValueError(f"Cannot find version in {spec}") + + bundle_file = f"cryptography-{version}.tar.gz" + vendor_file = f"cryptography-{version}-vendor.tar.bz2" + + # remove existing vendor directory and file + if os.path.isdir(VENDOR_DIR): + shutil.rmtree(VENDOR_DIR) + try: + os.unlink(vendor_file) + except FileNotFoundError: + pass + + print(f"Getting crates for {bundle_file}", file=sys.stderr) + + # extract tar file in tempdir + # fetch and vendor Rust crates + with tempfile.TemporaryDirectory(dir=here) as tmp: + with tarfile.open(bundle_file) as tar: + tar.extractall(path=tmp) + manifest = os.path.join(tmp, f"cryptography-{version}", CARGO_TOML) + cargo("fetch", manifest) + cargo("vendor", manifest) + + print("\nCreating tar ball...", file=sys.stderr) + with tarfile.open(vendor_file, "x:bz2") as tar: + tar_reproducible(tar, VENDOR_DIR) + + # remove vendor dir + shutil.rmtree(VENDOR_DIR) + + parser.exit(0, f"Created {vendor_file}\n") + + +if __name__ == "__main__": + main()