| # Copyright 2021 The Pigweed Authors |
| # |
| # Licensed under the Apache License, Version 2.0 (the "License"); you may not |
| # use this file except in compliance with the License. You may obtain a copy of |
| # the License at |
| # |
| # https://www.apache.org/licenses/LICENSE-2.0 |
| # |
| # Unless required by applicable law or agreed to in writing, software |
| # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
| # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
| # License for the specific language governing permissions and limitations under |
| # the License. |
| """Facilities for generating the 'root' metadata.""" |
| |
| import argparse |
| from pathlib import Path |
| from typing import Iterable, List, NewType |
| |
| from pw_software_update import keys, metadata |
| from pw_software_update.tuf_pb2 import (RootMetadata, SignedRootMetadata, |
| SignatureRequirement) |
| |
| RootKeys = NewType('RootKeys', List[bytes]) |
| TargetsKeys = NewType('TargetsKeys', List[bytes]) |
| |
| |
| def gen_root_metadata(root_key_pems: RootKeys, |
| targets_key_pems: TargetsKeys, |
| version: int = 1) -> RootMetadata: |
| """Generates a RootMetadata. |
| |
| Args: |
| root_key_pems: list of root public keys in PEM format. |
| targets_key_pems: list of targets keys in PEM format. |
| version: Version number for rollback checks. |
| """ |
| common = metadata.gen_common_metadata(metadata.RoleType.ROOT, |
| version=version) |
| |
| root_keys = [keys.import_ecdsa_public_key(pem) for pem in root_key_pems] |
| targets_keys = [ |
| keys.import_ecdsa_public_key(pem) for pem in targets_key_pems |
| ] |
| |
| return RootMetadata(common_metadata=common, |
| consistent_snapshot=False, |
| keys=root_keys + targets_keys, |
| root_signature_requirement=SignatureRequirement( |
| key_ids=[k.key_id for k in root_keys], |
| threshold=1), |
| targets_signature_requirement=SignatureRequirement( |
| key_ids=[k.key_id for k in targets_keys], |
| threshold=1)) |
| |
| |
| def parse_args(): |
| """Parse CLI arguments.""" |
| parser = argparse.ArgumentParser(description=__doc__) |
| |
| parser.add_argument('-o', |
| '--out', |
| type=Path, |
| required=True, |
| help='Output path for the generated root metadata') |
| |
| parser.add_argument('--version', |
| type=int, |
| default=1, |
| help='Canonical version number for rollback checks') |
| |
| parser.add_argument('--root-key', |
| type=Path, |
| required=True, |
| nargs='+', |
| help='Public key filename for the "Root" role') |
| |
| parser.add_argument('--targets-key', |
| type=Path, |
| required=True, |
| nargs='+', |
| help='Public key filename for the "Targets" role') |
| return parser.parse_args() |
| |
| |
| def main(out: Path, root_key: Iterable[Path], targets_key: Iterable[Path], |
| version: int) -> None: |
| """Generates and writes to disk an unsigned SignedRootMetadata.""" |
| |
| root_metadata = gen_root_metadata( |
| RootKeys([k.read_bytes() for k in root_key]), |
| TargetsKeys([k.read_bytes() for k in targets_key]), version) |
| signed = SignedRootMetadata( |
| serialized_root_metadata=root_metadata.SerializeToString()) |
| out.write_bytes(signed.SerializeToString()) |
| |
| |
| if __name__ == '__main__': |
| main(**vars(parse_args())) |