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 TUF target metadata.""" 15 16import enum 17import hashlib 18from typing import Dict, Iterable 19 20from pw_software_update.tuf_pb2 import (CommonMetadata, Hash, HashFunction, 21 TargetFile, TargetsMetadata) 22 23HASH_FACTORIES = { 24 HashFunction.SHA256: hashlib.sha256, 25} 26DEFAULT_HASHES = (HashFunction.SHA256, ) 27DEFAULT_SPEC_VERSION = "0.0.1" 28DEFAULT_METADATA_VERSION = 0 29 30 31class RoleType(enum.Enum): 32 """Set of allowed TUF metadata role types.""" 33 ROOT = 'root' 34 TARGETS = 'targets' 35 36 37def gen_common_metadata( 38 role: RoleType, 39 spec_version: str = DEFAULT_SPEC_VERSION, 40 version: int = DEFAULT_METADATA_VERSION) -> CommonMetadata: 41 """Generates CommonMetadata.""" 42 return CommonMetadata(role=role.value, 43 spec_version=spec_version, 44 version=version) 45 46 47def gen_targets_metadata( 48 target_payloads: Dict[str, bytes], 49 hash_funcs: Iterable['HashFunction.V'] = DEFAULT_HASHES, 50 version: int = DEFAULT_METADATA_VERSION, 51) -> TargetsMetadata: 52 """Generates TargetsMetadata the given target payloads.""" 53 target_files = [] 54 for target_file_name, target_payload in target_payloads.items(): 55 target_files.append( 56 TargetFile(file_name=target_file_name, 57 length=len(target_payload), 58 hashes=gen_hashes(target_payload, hash_funcs))) 59 60 common_metadata = gen_common_metadata(RoleType.TARGETS, version=version) 61 return TargetsMetadata(common_metadata=common_metadata, 62 target_files=target_files) 63 64 65def gen_hashes(data: bytes, 66 hash_funcs: Iterable['HashFunction.V']) -> Iterable[Hash]: 67 """Computes all the specified hashes over the data.""" 68 result = [] 69 for func in hash_funcs: 70 if func == HashFunction.UNKNOWN_HASH_FUNCTION: 71 raise ValueError( 72 'UNKNOWN_HASH_FUNCTION cannot be used to generate hashes.') 73 digest = HASH_FACTORIES[func](data).digest() 74 result.append(Hash(function=func, hash=digest)) 75 76 return result 77