• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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