blob: 2f1a2632e9455e5fa34217b777dae35f7bdcd03a [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.
"""Script for generating test bundles"""
import argparse
import subprocess
import sys
from typing import Dict
from pw_software_update import tuf_pb2, metadata, update_bundle_pb2
HEADER = """// 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.
#pragma once
#include "pw_bytes/span.h"
"""
def byte_array_declaration(data: bytes, name: str) -> str:
"""Generates a byte C array declaration for a byte array"""
type_name = '[[maybe_unused]] const std::byte'
byte_str = ''.join([f'std::byte{{0x{b:02x}}},' for b in data])
array_body = f'{{{byte_str}}}'
return f'{type_name} {name}[] = {array_body};'
def proto_array_declaration(proto, name: str) -> str:
"""Generates a byte array declaration for a proto"""
return byte_array_declaration(proto.SerializeToString(), name)
def sign_metadata(role: str, metadata_proto):
"""Signs a serialized metadata for a given role"""
signed = getattr(tuf_pb2, f'Signed{role.capitalize()}Metadata')()
serialized = metadata_proto.SerializeToString()
setattr(signed, f'serialized_{role}_metadata', serialized)
# TODO(pwbug:456): Generate signatures.
return signed
class Bundle:
"""A helper for test UpdateBundle generation"""
def __init__(self):
self._payloads: Dict[str, bytes] = {}
def add_payload(self, name: str, payload: bytes) -> None:
"""Adds a payload to the bundle"""
self._payloads[name] = payload
def generate_targets_metadata(self) -> tuf_pb2.TargetsMetadata:
"""Generates the targets metadata"""
targets = metadata.gen_targets_metadata(self._payloads)
return targets
def generate_signed_metadta(self, role: str):
"""Generate a signed metadata of a given role"""
unsigned = getattr(self, f'generate_{role}_metadata')()
return sign_metadata(role, unsigned)
def generate_bundle(self) -> update_bundle_pb2.UpdateBundle:
"""Generate the update bundle"""
bundle = update_bundle_pb2.UpdateBundle()
bundle.targets_metadata['targets'].CopyFrom(
self.generate_signed_metadta('targets'))
for name, payload in self._payloads.items():
bundle.target_payloads[name] = payload
return bundle
def generate_manifest(self) -> update_bundle_pb2.Manifest:
"""Generates the manifest"""
manifest = update_bundle_pb2.Manifest()
manifest.targets_metadata['targets'].CopyFrom(
self.generate_targets_metadata())
return manifest
def parse_args():
"""Setup argparse."""
parser = argparse.ArgumentParser()
parser.add_argument("output_header",
help="output path of the generated C header")
return parser.parse_args()
def main() -> int:
"""Main"""
args = parse_args()
test_bundle = Bundle()
# Add some update files.
test_bundle.add_payload('file1', 'file 1 content'.encode()) # type: ignore
test_bundle.add_payload('file2', 'file 2 content'.encode()) # type: ignore
update_bundle_proto = test_bundle.generate_bundle()
manifest_proto = test_bundle.generate_manifest()
with open(args.output_header, 'w') as header:
header.write(HEADER)
header.write('namespace {')
header.write(
proto_array_declaration(update_bundle_proto, 'kTestBundle'))
header.write(
proto_array_declaration(manifest_proto, 'kTestBundleManifest'))
header.write('}')
subprocess.run([
'clang-format',
'-i',
args.output_header,
], check=True)
return 0
if __name__ == "__main__":
sys.exit(main())