• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Copyright 2021 The Pigweed Authors
2#
3# Licensed under the Apache License, Version 2.0 (the "License"); you may not
4# use this file except in compliance with the License. You may obtain a copy of
5# the License at
6#
7#     https://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12# License for the specific language governing permissions and limitations under
13# the License.
14"""Facilities for generating the 'root' metadata."""
15
16import argparse
17from pathlib import Path
18from typing import Iterable, List, NewType
19
20from pw_software_update import keys, metadata
21from pw_software_update.tuf_pb2 import (RootMetadata, SignedRootMetadata,
22                                        SignatureRequirement)
23
24RootKeys = NewType('RootKeys', List[bytes])
25TargetsKeys = NewType('TargetsKeys', List[bytes])
26
27
28def gen_root_metadata(root_key_pems: RootKeys,
29                      targets_key_pems: TargetsKeys,
30                      version: int = 1) -> RootMetadata:
31    """Generates a RootMetadata.
32
33    Args:
34      root_key_pems: list of root public keys in PEM format.
35      targets_key_pems: list of targets keys in PEM format.
36      version: Version number for rollback checks.
37    """
38    common = metadata.gen_common_metadata(metadata.RoleType.ROOT,
39                                          version=version)
40
41    root_keys = [keys.import_ecdsa_public_key(pem) for pem in root_key_pems]
42    targets_keys = [
43        keys.import_ecdsa_public_key(pem) for pem in targets_key_pems
44    ]
45
46    return RootMetadata(common_metadata=common,
47                        consistent_snapshot=False,
48                        keys=root_keys + targets_keys,
49                        root_signature_requirement=SignatureRequirement(
50                            key_ids=[k.key_id for k in root_keys],
51                            threshold=1),
52                        targets_signature_requirement=SignatureRequirement(
53                            key_ids=[k.key_id for k in targets_keys],
54                            threshold=1))
55
56
57def parse_args():
58    """Parse CLI arguments."""
59    parser = argparse.ArgumentParser(description=__doc__)
60
61    parser.add_argument('-o',
62                        '--out',
63                        type=Path,
64                        required=True,
65                        help='Output path for the generated root metadata')
66
67    parser.add_argument('--version',
68                        type=int,
69                        default=1,
70                        help='Canonical version number for rollback checks')
71
72    parser.add_argument('--root-key',
73                        type=Path,
74                        required=True,
75                        nargs='+',
76                        help='Public key filename for the "Root" role')
77
78    parser.add_argument('--targets-key',
79                        type=Path,
80                        required=True,
81                        nargs='+',
82                        help='Public key filename for the "Targets" role')
83    return parser.parse_args()
84
85
86def main(out: Path, root_key: Iterable[Path], targets_key: Iterable[Path],
87         version: int) -> None:
88    """Generates and writes to disk an unsigned SignedRootMetadata."""
89
90    root_metadata = gen_root_metadata(
91        RootKeys([k.read_bytes() for k in root_key]),
92        TargetsKeys([k.read_bytes() for k in targets_key]), version)
93    signed = SignedRootMetadata(
94        serialized_root_metadata=root_metadata.SerializeToString())
95    out.write_bytes(signed.SerializeToString())
96
97
98if __name__ == '__main__':
99    main(**vars(parse_args()))
100