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