• 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"""Metadata signing facilities."""
15
16import argparse
17from pathlib import Path
18
19from pw_software_update import keys
20from pw_software_update.tuf_pb2 import SignedRootMetadata
21from pw_software_update.update_bundle_pb2 import UpdateBundle
22
23
24def sign_root_metadata(root_metadata: SignedRootMetadata,
25                       root_key_pem: bytes) -> SignedRootMetadata:
26    """Signs or re-signs a Root Metadata.
27
28    Args:
29      root_metadata: A SignedRootMetadata to be signed/re-signed.
30      root_key_pem: The Root signing key in PEM.
31    """
32
33    signature = keys.create_ecdsa_signature(
34        root_metadata.serialized_root_metadata, root_key_pem)
35    root_metadata.signatures.append(signature)
36
37    return root_metadata
38
39
40def sign_update_bundle(bundle: UpdateBundle,
41                       targets_key_pem: bytes) -> UpdateBundle:
42    """Signs or re-signs an update bundle.
43
44    Args:
45      bundle: An UpdateBundle to be signed/re-signed.
46      targets_key_pem: The targets signing key in PEM.
47    """
48    bundle.targets_metadata['targets'].signatures.append(
49        keys.create_ecdsa_signature(
50            bundle.targets_metadata['targets'].serialized_targets_metadata,
51            targets_key_pem))
52    return bundle
53
54
55def parse_args():
56    """Parse CLI arguments."""
57    parser = argparse.ArgumentParser(description=__doc__)
58
59    parser.add_argument('--root-metadata',
60                        type=Path,
61                        required=False,
62                        help='Path to the root metadata to be signed')
63
64    parser.add_argument('--bundle',
65                        type=Path,
66                        required=False,
67                        help='Path to the bundle to be signed')
68
69    parser.add_argument(
70        '--output',
71        type=Path,
72        required=False,
73        help=('Path to save the signed root metadata or bundle '
74              'to; Defaults to the input path if unspecified'))
75
76    parser.add_argument('--key',
77                        type=Path,
78                        required=True,
79                        help='Path to the signing key')
80
81    args = parser.parse_args()
82
83    if not (args.root_metadata or args.bundle):
84        parser.error(
85            'either "--root-metadata" or "--bundle" must be specified')
86    if args.root_metadata and args.bundle:
87        parser.error('"--root-metadata" and "--bundle" are mutually exclusive')
88
89    return args
90
91
92def main(root_metadata: Path, bundle: Path, key: Path, output: Path) -> None:
93    """Signs or re-signs a root metadata or an update bundle."""
94    if root_metadata:
95        signed_root_metadata = sign_root_metadata(
96            SignedRootMetadata.FromString(root_metadata.read_bytes()),
97            key.read_bytes())
98
99        if not output:
100            output = root_metadata
101        output.write_bytes(signed_root_metadata.SerializeToString())
102    else:
103        signed_bundle = sign_update_bundle(
104            UpdateBundle.FromString(bundle.read_bytes()), key.read_bytes())
105
106        if not output:
107            output = bundle
108        output.write_bytes(signed_bundle.SerializeToString())
109
110
111if __name__ == '__main__':
112    main(**vars(parse_args()))
113