1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137
|
#!/usr/bin/env python
#
# A script to generate TUF repository files.
#
# A modification of generate.py from the Python implementation:
# https://github.com/theupdateframework/tuf/blob/v0.9.9/tests/repository_data/generate.py
# Updated a bit for the v1.0.0 version:
# https://github.com/theupdateframework/python-tuf/blob/v1.0.0/examples/repo_example/basic_repo.py
import datetime
import optparse
import shutil
from pathlib import Path
from typing import Dict
import securesystemslib.util
from securesystemslib.keys import generate_ed25519_key
from securesystemslib.signer import SSlibSigner
from tuf.api.metadata import (
Key,
Metadata,
MetaFile,
Role,
Root,
Snapshot,
TargetFile,
Targets,
Timestamp,
)
from tuf.api.serialization.json import JSONSerializer
SPEC_VERSION = "1.0.19"
PRETTY = JSONSerializer(compact=False)
EXPIRY = datetime.datetime(2030, 1, 1, 0, 0) # Far enough in the future
ROLES = set(("targets", "snapshot", "timestamp", "root"))
def make_targets(target_dir: Path, consistent_snapshot: bool) -> Dict[str, TargetFile]:
targets = {}
for target in (Path("file1.txt"), Path("dir/file2.txt")):
target_fspath = target_dir / target
target_fspath.parent.mkdir(parents=True, exist_ok=True)
target_fspath.write_text(target.name) # file contents are the file name
target_file_info = TargetFile.from_file(str(target), str(target_fspath))
if consistent_snapshot:
digest = next(iter(target_file_info.hashes.values()))
shutil.move(target_fspath, target_fspath.parent / f"{digest}.{target.name}")
targets[str(target)] = target_file_info
return targets
def make_test_repo(repo_dir: Path, consistent_snapshot: bool):
"""Create a test repository in `repo_dir`.
Two targets:
- `file1.txt`
- `dir/file2.txt`
"""
roles: Dict[str, Metadata] = {}
targets: Dict[str, TargetFile] = {}
target_dir = repo_dir / "targets"
target_dir.mkdir()
targets = make_targets(target_dir, consistent_snapshot)
target_metadata = Targets(
version=1, spec_version=SPEC_VERSION, expires=EXPIRY, targets=targets
)
roles["targets"] = Metadata[Targets](target_metadata, {})
snapshot_metadata = Snapshot(
version=1,
spec_version=SPEC_VERSION,
expires=EXPIRY,
meta={"targets.json": MetaFile(version=1)},
)
roles["snapshot"] = Metadata[Snapshot](snapshot_metadata, {})
timestamp_metadata = Timestamp(
version=1,
spec_version=SPEC_VERSION,
expires=EXPIRY,
snapshot_meta=MetaFile(version=1),
)
roles["timestamp"] = Metadata[Timestamp](timestamp_metadata, {})
keys = {name: generate_ed25519_key() for name in ROLES}
root_metadata = Root(
version=1,
spec_version=SPEC_VERSION,
expires=EXPIRY,
keys={
key["keyid"]: Key.from_securesystemslib_key(key) for key in keys.values()
},
roles={role: Role([key["keyid"]], threshold=1) for role, key in keys.items()},
consistent_snapshot=consistent_snapshot,
)
roles["root"] = Metadata[Root](root_metadata, {})
# Write the metadata files
metadata_dir = repo_dir / "metadata"
metadata_dir.mkdir()
for name in ["root", "targets", "snapshot", "timestamp"]:
role = roles[name]
key = keys[role.signed.type]
signer = SSlibSigner(key)
role.sign(signer)
if name == "root" or (consistent_snapshot and name != "timestamp"):
filename = f"{role.signed.version}.{name}.json"
else:
filename = f"{name}.json"
role.to_file(str(metadata_dir / filename), serializer=PRETTY)
def main():
parser = optparse.OptionParser()
parser.add_option(
"-c",
"--consistent-snapshot",
action="store_true",
dest="consistent_snapshot",
help="Generate consistent snapshot",
default=False,
)
(options, args) = parser.parse_args()
repo_dir = Path("repository")
repo_dir.mkdir()
make_test_repo(repo_dir, options.consistent_snapshot)
if __name__ == "__main__":
main()
|