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 15 #pragma once 16 17 #include <cstddef> 18 19 #include "pw_blob_store/blob_store.h" 20 #include "pw_protobuf/map_utils.h" 21 #include "pw_protobuf/message.h" 22 #include "pw_software_update/bundled_update_backend.h" 23 #include "pw_software_update/manifest_accessor.h" 24 25 namespace pw::software_update { 26 class BundledUpdateBackend; 27 28 // Name of the top-level Targets metadata. 29 constexpr std::string_view kTopLevelTargetsName = "targets"; 30 31 // Name of the "user manifest" target file. The "user manifest" is a product 32 // specific blob that is opaque to upstream but need to be passed around in 33 // manifest handling (for now). 34 constexpr std::string_view kUserManifestTargetFileName = "user_manifest"; 35 36 // UpdateBundleAccessor is the trusted custodian of a staged incoming update 37 // bundle. 38 // 39 // It takes exclusive ownership of the blob_store that represents a staged, 40 // *untrusted* bundle, and presents convenient and *trusted* accessors. 41 // 42 // ALL ACCESS to the staged update bundle MUST GO THROUGH the 43 // `UpdateBundleAccessor`. 44 class UpdateBundleAccessor { 45 public: 46 // UpdateBundleAccessor 47 // blob_store - The staged incoming software update bundle. 48 // backend - Project-specific BundledUpdateBackend. 49 // disable_verification - Disable verification. 50 constexpr UpdateBundleAccessor(blob_store::BlobStore& blob_store, 51 BundledUpdateBackend& backend, 52 bool disable_verification = false) blob_store_(blob_store)53 : blob_store_(blob_store), 54 blob_store_reader_(blob_store_), 55 backend_(backend), 56 disable_verification_(disable_verification) {} 57 58 // Opens and verifies the software update bundle. 59 // 60 // Verification covers the following: 61 // 62 // 1. If a Root metadata is included with the incoming bundle, the Root 63 // metadata will be verified and used as the new Root metadata to verify 64 // other metadata in the bundle. 65 // 66 // 2. The Targets metadata is verified using the Root metadata. 67 // 68 // 3. All target payloads referenced in the Targets metadata are verified. 69 // 70 // Limitations and customizations (compared to standard TUF): 71 // 72 // 1. Does not yet support arbitrary Root key rotations. Which means 73 // There is only one (reliable) chance to rotate the Root key for all 74 // devices. Rotation of the Targets key is still unlimited. 75 // 2. Timestamp and Snapshot metadata are not used or supported. 76 // 3. Assumes a single top-level Targets metadata and no delegations. 77 // 4. The top-level Targets metadata doubles as the software update 78 // "manifest". Anti-rollback IS supported via the Targets metadata version. 79 // 5. Supports "personalization", where the staged bundle may have been 80 // stripped of any target payloads that the device already have. For those 81 // personalized-out targets, verification relies on the cached manifest of 82 // a previous successful update to verify target length and hash. 83 // 84 // Returns: 85 // OK - Bundle was successfully opened and verified. 86 Status OpenAndVerify(); 87 88 // Closes the bundle by invalidating the verification and closing 89 // the reader to release the read-only lock 90 // 91 // Returns: 92 // OK - success. 93 // DATA_LOSS - Error writing data or fail to verify written data. 94 Status Close(); 95 96 // Writes out the manifest of the staged bundle via a backend-supplied writer. 97 // 98 // Returns: 99 // FAILED_PRECONDITION - Bundle is not open and verified. 100 // TODO(pwbug/456): Add other error codes if necessary. 101 Status PersistManifest(); 102 103 // Returns a reader for the (verified) payload bytes of a specified target 104 // file. 105 // 106 // Returns: 107 // A reader instance for the target file. 108 // TODO(pwbug/456): Figure out a way to propagate error. 109 stream::IntervalReader GetTargetPayload(std::string_view target_name); 110 stream::IntervalReader GetTargetPayload(protobuf::String target_name); 111 112 // Exposes "manifest" information from the incoming update bundle once it has 113 // passed verification. 114 ManifestAccessor GetManifest(); 115 116 // Returns the total number of bytes of all target payloads listed in the 117 // manifest *AND* exists in the bundle. 118 Result<uint64_t> GetTotalPayloadSize(); 119 120 private: 121 blob_store::BlobStore& blob_store_; 122 blob_store::BlobStore::BlobReader blob_store_reader_; 123 BundledUpdateBackend& backend_; 124 protobuf::Message bundle_; 125 // The current, cached, trusted `SignedRootMetadata{}`. 126 protobuf::Message trusted_root_; 127 bool disable_verification_; 128 bool bundle_verified_ = false; 129 130 // Opens the bundle for read-only access and readies the parser. 131 Status DoOpen(); 132 133 // Performs TUF and downstream custom verification. 134 Status DoVerify(); 135 136 // The method checks whether the update bundle contains a root metadata 137 // different from the on-device one. If it does, it performs the following 138 // verification and upgrade flow: 139 // 140 // 1. Verify the signatures according to the on-device trusted 141 // root metadata obtained from the backend. 142 // 2. Verify content of the new root metadata, including: 143 // 1) Check role magic field. 144 // 2) Check signature requirement. Specifically, check that no key is 145 // reused across different roles and keys are unique in the same 146 // requirement. 147 // 3) Check key mapping. Specifically, check that all keys are unique, 148 // ECDSA keys, and the key ids are exactly the SHA256 of `key type + 149 // key scheme + key value`. 150 // 3. Verify the signatures against the new root metadata. 151 // 4. Check rollback. 152 // 5. Update on-device root metadata. 153 Status UpgradeRoot(); 154 155 // The method verifies the top-level targets metadata against the trusted 156 // root. The verification includes the following: 157 // 158 // 1. Verify the signatures of the targets metadata. 159 // 2. Check the content of the targets metadata. 160 // 3. Check rollback against the version from on-device manifest, if one 161 // exists (the manifest may be reset in the case of key rotation). 162 Status VerifyTargetsMetadata(); 163 164 // A helper to get the on-device trusted root metadata. It returns an 165 // instance of SignedRootMetadata proto message. 166 protobuf::Message GetOnDeviceTrustedRoot(); 167 168 // A helper to get an accessor to the on-device manifest. The on-device 169 // manifest is a serialized `message Manifest{...}` that represents the 170 // current running firmware of the device. The on-device manifest storage 171 // MUST meet the following requirements. 172 // 173 // 1. MUST NOT get wiped by a factory reset, otherwise a FDR can be used 174 // to circumvent anti-rollback check. 175 // 2. MUST be kept in-sync with the actual firmware on-device. If any 176 // mechanism is used to modify the firmware (e.g. via flashing), the 177 // on-device manifest MUST be updated to reflect the change as well. 178 // The on-device manifest CAN be erased if updating it is too cumbersome 179 // BUT ONLY ON DEV DEVICES as erasing the on-device manifest defeats 180 // anti-rollback. 181 // 3. MUST be integrity-protected and checked. Corrupted on-device manifest 182 // cannot be used as it may brick a device as a result of anti-rollback 183 // check. Integrity check is added and enforced by the backend via 184 // `BundledUpdateBackend` callbacks. 185 // 186 ManifestAccessor GetOnDeviceManifest(); 187 188 // Verify all targets referenced in the manifest (Targets metadata) has a 189 // payload blob either within the bundle or on-device, in both cases 190 // measuring up to the length and hash recorded in the manifest. 191 Status VerifyTargetsPayloads(); 192 193 // Verify a target specified by name measures up to the expected length and 194 // SHA256 hash. Additionally call the backend to perform any product-specific 195 // validations. 196 Status VerifyTargetPayload(ManifestAccessor manifest, 197 std::string_view name, 198 protobuf::Uint64 expected_length, 199 protobuf::Bytes expected_sha256); 200 201 // For a target the payload of which is included in the bundle, verify 202 // it measures up to the expected length and sha256 hash. 203 Status VerifyInBundleTargetPayload(protobuf::Uint64 expected_length, 204 protobuf::Bytes expected_sha256, 205 stream::IntervalReader payload_reader); 206 207 // For a target with no corresponding payload in the bundle, verify 208 // its on-device payload bytes measures up to the expected length and sha256 209 // hash. 210 Status VerifyOutOfBundleTargetPayload(std::string_view name, 211 protobuf::Uint64 expected_length, 212 protobuf::Bytes expected_sha256); 213 }; 214 215 } // namespace pw::software_update 216