Skip to content

Commit 384772e

Browse files
author
Martin Vrachev
committed
Provide a way to generate a simple repository
I created a new script called "generate_md.py" which can be used to easily generate a repository. Additionally, I created a new test file making sure that the locally stored metadata files and the newly generated metadata roles are the same. This will allow us to test that we are not changing the metadata file structure when making changes. Signed-off-by: Martin Vrachev <mvrachev@vmware.com>
1 parent 69cc684 commit 384772e

7 files changed

Lines changed: 294 additions & 0 deletions

File tree

tests/generated_data/__init__.py

Whitespace-only changes.
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
{
2+
"signatures": [
3+
{
4+
"keyid": "5822582e7072996c1eef1cec24b61115d364987faa486659fe3d3dce8dae2aba",
5+
"sig": "e2cfb42fe355843c5284ca0bd58acb742a232c2c7b93a87c34ed086cfe51c41a21f77bb8d4ed0c74f9fb3f76f48f7005488347450c28468d8bb288e5988ae201"
6+
}
7+
],
8+
"signed": {
9+
"_type": "root",
10+
"consistent_snapshot": true,
11+
"expires": "2050-01-01T00:00:00Z",
12+
"keys": {
13+
"09d440e3725cec247dcb8703b646a87dd2a4d75343e8095c036c32795eefe3b9": {
14+
"keytype": "ed25519",
15+
"keyval": {
16+
"public": "250f9ae3d1d3d5c419a73cfb4a470c01de1d5d3d61a3825416b5f5d6b88f4a30"
17+
},
18+
"scheme": "ed25519"
19+
},
20+
"2be5c21e3614f9f178fb49c4a34d0c18ffac30abd14ced917c60a52c8d8094b7": {
21+
"keytype": "ed25519",
22+
"keyval": {
23+
"public": "0e6738fc1ac6fb4de680b4be99ecbcd99b030f3963f291277eef67bb9bd123e9"
24+
},
25+
"scheme": "ed25519"
26+
},
27+
"3458204ed467519c19a5316eb278b5608472a1bbf15850ebfb462d5315e4f86d": {
28+
"keytype": "ed25519",
29+
"keyval": {
30+
"public": "82380623abb9666d4bf274b1a02577469445a972e5650d270101faa5107b19c8"
31+
},
32+
"scheme": "ed25519"
33+
},
34+
"5822582e7072996c1eef1cec24b61115d364987faa486659fe3d3dce8dae2aba": {
35+
"keytype": "ed25519",
36+
"keyval": {
37+
"public": "b11d2ff132c033a657318c74c39526476c56de7556c776f11070842dbc4ac14c"
38+
},
39+
"scheme": "ed25519"
40+
}
41+
},
42+
"roles": {
43+
"root": {
44+
"keyids": [
45+
"5822582e7072996c1eef1cec24b61115d364987faa486659fe3d3dce8dae2aba"
46+
],
47+
"threshold": 1
48+
},
49+
"snapshot": {
50+
"keyids": [
51+
"3458204ed467519c19a5316eb278b5608472a1bbf15850ebfb462d5315e4f86d"
52+
],
53+
"threshold": 1
54+
},
55+
"targets": {
56+
"keyids": [
57+
"2be5c21e3614f9f178fb49c4a34d0c18ffac30abd14ced917c60a52c8d8094b7"
58+
],
59+
"threshold": 1
60+
},
61+
"timestamp": {
62+
"keyids": [
63+
"09d440e3725cec247dcb8703b646a87dd2a4d75343e8095c036c32795eefe3b9"
64+
],
65+
"threshold": 1
66+
}
67+
},
68+
"spec_version": "1.0.28",
69+
"version": 1
70+
}
71+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
{
2+
"signatures": [
3+
{
4+
"keyid": "3458204ed467519c19a5316eb278b5608472a1bbf15850ebfb462d5315e4f86d",
5+
"sig": "40853c2711d5ea49066c6b27a078f9e65419e0a4a3e298b75204e83713fe1d8ae9db91eccda5a8bfc14a5d71b3862c3ab27339db630056948ca0548a3e0edf03"
6+
}
7+
],
8+
"signed": {
9+
"_type": "snapshot",
10+
"expires": "2050-01-01T00:00:00Z",
11+
"meta": {
12+
"targets.json": {
13+
"version": 1
14+
}
15+
},
16+
"spec_version": "1.0.28",
17+
"version": 1
18+
}
19+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
{
2+
"signatures": [
3+
{
4+
"keyid": "2be5c21e3614f9f178fb49c4a34d0c18ffac30abd14ced917c60a52c8d8094b7",
5+
"sig": "2954a14e207e8de2e5a2f9c88cfe99c56d357b7c9fa4c5f4ad9496279a6a7e253cf80bcd109467bb2ff8b9691d240fef18aaf55ee24a22e8d2432338f6bbc40b"
6+
}
7+
],
8+
"signed": {
9+
"_type": "targets",
10+
"expires": "2050-01-01T00:00:00Z",
11+
"spec_version": "1.0.28",
12+
"targets": {},
13+
"version": 1
14+
}
15+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
{
2+
"signatures": [
3+
{
4+
"keyid": "09d440e3725cec247dcb8703b646a87dd2a4d75343e8095c036c32795eefe3b9",
5+
"sig": "c9094d7b773277f7acf040949358b684bd09be53be1deaaa7e1c5513bb30ded125dca1dd9d0893c542d52d53f555c85dca960ab147852757c0f5f7399787a704"
6+
}
7+
],
8+
"signed": {
9+
"_type": "timestamp",
10+
"expires": "2050-01-01T00:00:00Z",
11+
"meta": {
12+
"snapshot.json": {
13+
"version": 1
14+
}
15+
},
16+
"spec_version": "1.0.28",
17+
"version": 1
18+
}
19+
}
Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
"""Script for generating new metadata files."""
2+
3+
# Copyright New York University and the TUF contributors
4+
# SPDX-License-Identifier: MIT OR Apache-2.0
5+
6+
import os
7+
import sys
8+
from datetime import datetime
9+
from typing import Dict, List, Optional
10+
11+
from securesystemslib.signer import SSlibSigner
12+
13+
from tests import utils
14+
from tuf.api.metadata import (
15+
SPECIFICATION_VERSION,
16+
TOP_LEVEL_ROLE_NAMES,
17+
Key,
18+
Metadata,
19+
MetaFile,
20+
Role,
21+
Root,
22+
Snapshot,
23+
Targets,
24+
Timestamp,
25+
)
26+
from tuf.api.serialization.json import JSONSerializer
27+
28+
# Hardcode keys and expiry time to achieve reproducibility.
29+
public_values: List[str] = [
30+
"b11d2ff132c033a657318c74c39526476c56de7556c776f11070842dbc4ac14c",
31+
"250f9ae3d1d3d5c419a73cfb4a470c01de1d5d3d61a3825416b5f5d6b88f4a30",
32+
"82380623abb9666d4bf274b1a02577469445a972e5650d270101faa5107b19c8",
33+
"0e6738fc1ac6fb4de680b4be99ecbcd99b030f3963f291277eef67bb9bd123e9",
34+
]
35+
private_values: List[str] = [
36+
"510e5e04d7a364af850533856eacdf65d30cc0f8803ecd5fdc0acc56ca2aa91c",
37+
"e6645b00312c8a257782e3e61e85bafda4317ad072c52251ef933d480c387abd",
38+
"cd13dd2180334b24c19b32aaf27f7e375a614d7ba0777220d5c2290bb2f9b868",
39+
"7e2e751145d1b22f6e40d4ba2aa47158207acfd3c003f1cbd5a08141dfc22a15",
40+
]
41+
keyids: List[str] = [
42+
"5822582e7072996c1eef1cec24b61115d364987faa486659fe3d3dce8dae2aba",
43+
"09d440e3725cec247dcb8703b646a87dd2a4d75343e8095c036c32795eefe3b9",
44+
"3458204ed467519c19a5316eb278b5608472a1bbf15850ebfb462d5315e4f86d",
45+
"2be5c21e3614f9f178fb49c4a34d0c18ffac30abd14ced917c60a52c8d8094b7",
46+
]
47+
48+
keys: Dict[str, Key] = {}
49+
for index in range(4):
50+
keys[f"ed25519_{index}"] = Key.from_securesystemslib_key(
51+
{
52+
"keytype": "ed25519",
53+
"scheme": "ed25519",
54+
"keyid": keyids[index],
55+
"keyval": {
56+
"public": public_values[index],
57+
"private": private_values[index],
58+
},
59+
}
60+
)
61+
62+
expires_str = "2050-01-01T00:00:00Z"
63+
EXPIRY = datetime.strptime(expires_str, "%Y-%m-%dT%H:%M:%SZ")
64+
SPEC_VERSION = ".".join(SPECIFICATION_VERSION)
65+
OUT_DIR = "generated_data/ed25519_metadata"
66+
if not os.path.exists(OUT_DIR):
67+
os.mkdir(OUT_DIR)
68+
69+
SERIALIZER = JSONSerializer()
70+
ROLES = {role_name: Role([], 1) for role_name in TOP_LEVEL_ROLE_NAMES}
71+
72+
73+
def verify_generation(md: Metadata, path: str) -> None:
74+
"""Verify that newly generated file equals the locally stored one.
75+
76+
Args:
77+
md: Newly generated metadata object.
78+
path: Path to the locally stored metadata file.
79+
"""
80+
with open(path, "rb") as f:
81+
static_md_bytes = f.read()
82+
md_bytes = md.to_bytes(SERIALIZER)
83+
if static_md_bytes != md_bytes:
84+
raise ValueError(
85+
f"Generated data != local data at {path}. Generate a new "
86+
+ "metadata with 'python generated_data/generate_md.py'"
87+
)
88+
89+
90+
def generate_all_files(
91+
dump: Optional[bool] = False, verify: Optional[bool] = False
92+
) -> None:
93+
"""Generate a new repository and optionally verify it.
94+
95+
Args:
96+
dump: Wheter to dump the newly generated files.
97+
verify: Whether to verify the newly generated files with the
98+
local staored.
99+
"""
100+
root = Root(1, SPEC_VERSION, EXPIRY, {}, ROLES, True)
101+
root.add_key("root", keys["ed25519_0"])
102+
root.add_key("timestamp", keys["ed25519_1"])
103+
root.add_key("snapshot", keys["ed25519_2"])
104+
root.add_key("targets", keys["ed25519_3"])
105+
106+
md_root: Metadata[Root] = Metadata(root, {})
107+
108+
timestamp = Timestamp(1, SPEC_VERSION, EXPIRY, MetaFile(1))
109+
md_timestamp: Metadata[Timestamp] = Metadata(timestamp, {})
110+
111+
meta: Dict[str, MetaFile] = {"targets.json": MetaFile(1)}
112+
snapshot = Snapshot(1, SPEC_VERSION, EXPIRY, meta)
113+
md_snapshot: Metadata[Snapshot] = Metadata(snapshot, {})
114+
115+
targets = Targets(1, SPEC_VERSION, EXPIRY, {})
116+
md_targets: Metadata[Targets] = Metadata(targets, {})
117+
118+
for i, md in enumerate([md_root, md_timestamp, md_snapshot, md_targets]):
119+
assert isinstance(md, Metadata)
120+
signer = SSlibSigner(
121+
{
122+
"keytype": "ed25519",
123+
"scheme": "ed25519",
124+
"keyid": keyids[i],
125+
"keyval": {
126+
"public": public_values[i],
127+
"private": private_values[i],
128+
},
129+
}
130+
)
131+
md.sign(signer)
132+
path = os.path.join(OUT_DIR, f"{md.signed.type}_with_ed25519.json")
133+
if verify:
134+
verify_generation(md, path)
135+
136+
if dump:
137+
md.to_file(path, SERIALIZER)
138+
139+
140+
if __name__ == "__main__":
141+
utils.configure_test_logging(sys.argv)
142+
# To generate a new set of metadata files this script is supposed to be run
143+
# from the "tests" folder.
144+
generate_all_files(dump=True)

tests/test_metadata_generation.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
"""Unit tests for 'tests/generated_data/generate_md.py'."""
2+
3+
# Copyright New York University and the TUF contributors
4+
# SPDX-License-Identifier: MIT OR Apache-2.0
5+
6+
7+
import sys
8+
import unittest
9+
10+
from tests import utils
11+
from tests.generated_data.generate_md import generate_all_files
12+
13+
14+
class TestMetadataGeneration(unittest.TestCase):
15+
"""Test metadata files generation."""
16+
17+
@staticmethod
18+
def test_compare_static_md_to_generated() -> None:
19+
# md_generator = MetadataGenerator("generated_data/ed25519_metadata")
20+
generate_all_files(dump=False, verify=True)
21+
22+
23+
# Run unit test.
24+
if __name__ == "__main__":
25+
utils.configure_test_logging(sys.argv)
26+
unittest.main()

0 commit comments

Comments
 (0)