blob: 95e728e24d158b52d9dc59b272a0597c6468629b [file] [log] [blame]
# 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.
"""Metadata signing facilities."""
import argparse
from pathlib import Path
from pw_software_update import keys
from pw_software_update.tuf_pb2 import SignedRootMetadata
from pw_software_update.update_bundle_pb2 import UpdateBundle
def sign_root_metadata(root_metadata: SignedRootMetadata,
root_key_pem: bytes) -> SignedRootMetadata:
"""Signs or re-signs a Root Metadata.
Args:
root_metadata: A SignedRootMetadata to be signed/re-signed.
root_key_pem: The Root signing key in PEM.
"""
signature = keys.create_ecdsa_signature(
root_metadata.serialized_root_metadata, root_key_pem)
root_metadata.signatures.append(signature)
return root_metadata
def sign_update_bundle(bundle: UpdateBundle,
targets_key_pem: bytes) -> UpdateBundle:
"""Signs or re-signs an update bundle.
Args:
bundle: An UpdateBundle to be signed/re-signed.
targets_key_pem: The targets signing key in PEM.
"""
bundle.targets_metadata['targets'].signatures.append(
keys.create_ecdsa_signature(
bundle.targets_metadata['targets'].serialized_targets_metadata,
targets_key_pem))
return bundle
def parse_args():
"""Parse CLI arguments."""
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument('--root-metadata',
type=Path,
required=False,
help='Path to the root metadata to be signed')
parser.add_argument('--bundle',
type=Path,
required=False,
help='Path to the bundle to be signed')
parser.add_argument(
'--output',
type=Path,
required=False,
help=('Path to save the signed root metadata or bundle '
'to; Defaults to the input path if unspecified'))
parser.add_argument('--key',
type=Path,
required=True,
help='Path to the signing key')
args = parser.parse_args()
if not (args.root_metadata or args.bundle):
parser.error(
'either "--root-metadata" or "--bundle" must be specified')
if args.root_metadata and args.bundle:
parser.error('"--root-metadata" and "--bundle" are mutually exclusive')
return args
def main(root_metadata: Path, bundle: Path, key: Path, output: Path) -> None:
"""Signs or re-signs a root metadata or an update bundle."""
if root_metadata:
signed_root_metadata = sign_root_metadata(
SignedRootMetadata.FromString(root_metadata.read_bytes()),
key.read_bytes())
if not output:
output = root_metadata
output.write_bytes(signed_root_metadata.SerializeToString())
else:
signed_bundle = sign_update_bundle(
UpdateBundle.FromString(bundle.read_bytes()), key.read_bytes())
if not output:
output = bundle
output.write_bytes(signed_bundle.SerializeToString())
if __name__ == '__main__':
main(**vars(parse_args()))