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