• 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 #define PW_LOG_MODULE_NAME "PWSU"
16 #define PW_LOG_LEVEL PW_LOG_LEVEL_WARN
17 
18 #include "pw_software_update/update_bundle_accessor.h"
19 
20 #include <cstddef>
21 #include <cstring>
22 #include <string_view>
23 
24 #include "pw_crypto/ecdsa.h"
25 #include "pw_crypto/sha256.h"
26 #include "pw_log/log.h"
27 #include "pw_protobuf/message.h"
28 #include "pw_result/result.h"
29 #include "pw_software_update/config.h"
30 #include "pw_software_update/manifest_accessor.h"
31 #include "pw_software_update/update_bundle.pwpb.h"
32 #include "pw_stream/interval_reader.h"
33 #include "pw_stream/memory_stream.h"
34 #include "pw_string/string_builder.h"
35 
36 namespace pw::software_update {
37 namespace {
38 
VerifyEcdsaSignature(protobuf::Bytes public_key,ConstByteSpan digest,protobuf::Bytes signature)39 Result<bool> VerifyEcdsaSignature(protobuf::Bytes public_key,
40                                   ConstByteSpan digest,
41                                   protobuf::Bytes signature) {
42   // TODO(b/237580538): Move this logic into an variant of the API in
43   // pw_crypto:ecdsa that takes readers as inputs.
44   std::byte public_key_bytes[65];
45   std::byte signature_bytes[64];
46   stream::IntervalReader key_reader = public_key.GetBytesReader();
47   stream::IntervalReader sig_reader = signature.GetBytesReader();
48   PW_TRY(key_reader.Read(public_key_bytes));
49   PW_TRY(sig_reader.Read(signature_bytes));
50   Status status = crypto::ecdsa::VerifyP256Signature(
51       public_key_bytes, digest, signature_bytes);
52   if (!status.ok()) {
53     return false;
54   }
55 
56   return true;
57 }
58 
59 // Convert an integer from [0, 16) to a hex char
IntToHex(uint8_t val)60 char IntToHex(uint8_t val) {
61   PW_ASSERT(val < 16);
62   return val >= 10 ? (val - 10) + 'a' : val + '0';
63 }
64 
LogKeyId(ConstByteSpan key_id)65 void LogKeyId(ConstByteSpan key_id) {
66   char key_id_str[pw::crypto::sha256::kDigestSizeBytes * 2 + 1] = {0};
67   for (size_t i = 0; i < pw::crypto::sha256::kDigestSizeBytes; i++) {
68     uint8_t value = std::to_integer<uint8_t>(key_id[i]);
69     key_id_str[i * 2] = IntToHex((value >> 4) & 0xf);
70     key_id_str[i * 2 + 1] = IntToHex(value & 0xf);
71   }
72 
73   PW_LOG_DEBUG("key_id: %s", key_id_str);
74 }
75 
76 // Verifies signatures of a TUF metadata.
VerifyMetadataSignatures(protobuf::Bytes message,protobuf::RepeatedMessages signatures,protobuf::Message signature_requirement,protobuf::StringToMessageMap key_mapping)77 Status VerifyMetadataSignatures(protobuf::Bytes message,
78                                 protobuf::RepeatedMessages signatures,
79                                 protobuf::Message signature_requirement,
80                                 protobuf::StringToMessageMap key_mapping) {
81   // Gets the threshold -- at least `threshold` number of signatures must
82   // pass verification in order to trust this metadata.
83   protobuf::Uint32 threshold = signature_requirement.AsUint32(
84       static_cast<uint32_t>(SignatureRequirement::Fields::kThreshold));
85   PW_TRY(threshold.status());
86 
87   // Gets the ids of keys that are allowed for verifying the signatures.
88   protobuf::RepeatedBytes allowed_key_ids =
89       signature_requirement.AsRepeatedBytes(
90           static_cast<uint32_t>(SignatureRequirement::Fields::kKeyIds));
91   PW_TRY(allowed_key_ids.status());
92 
93   // Verifies the signatures. Check that at least `threshold` number of
94   // signatures can be verified using the allowed keys.
95   size_t verified_count = 0;
96   size_t total_signatures = 0;
97   for (protobuf::Message signature : signatures) {
98     total_signatures++;
99     protobuf::Bytes key_id =
100         signature.AsBytes(static_cast<uint32_t>(Signature::Fields::kKeyId));
101     PW_TRY(key_id.status());
102 
103     // Reads the key id into a buffer, so that we can check whether it is
104     // listed as allowed and look up the key value later.
105     std::byte key_id_buf[pw::crypto::sha256::kDigestSizeBytes];
106     stream::IntervalReader key_id_reader = key_id.GetBytesReader();
107     Result<ByteSpan> key_id_read_res = key_id_reader.Read(key_id_buf);
108     PW_TRY(key_id_read_res.status());
109     if (key_id_read_res.value().size() != sizeof(key_id_buf)) {
110       return Status::Internal();
111     }
112 
113     // Verify that the `key_id` is listed in `allowed_key_ids`.
114     // Note that the function assumes that the key id is properly derived
115     // from the key (via sha256).
116     bool key_id_is_allowed = false;
117     for (protobuf::Bytes trusted : allowed_key_ids) {
118       Result<bool> key_id_equal = trusted.Equal(key_id_buf);
119       PW_TRY(key_id_equal.status());
120       if (key_id_equal.value()) {
121         key_id_is_allowed = true;
122         break;
123       }
124     }
125 
126     if (!key_id_is_allowed) {
127       PW_LOG_DEBUG("Skipping a key id not listed in allowed key ids");
128       LogKeyId(key_id_buf);
129       continue;
130     }
131 
132     // Retrieves the signature bytes.
133     protobuf::Bytes sig =
134         signature.AsBytes(static_cast<uint32_t>(Signature::Fields::kSig));
135     PW_TRY(sig.status());
136 
137     // Extracts the key type, scheme and value information.
138     std::string_view key_id_str(reinterpret_cast<const char*>(key_id_buf),
139                                 sizeof(key_id_buf));
140     protobuf::Message key_info = key_mapping[key_id_str];
141     PW_TRY(key_info.status());
142 
143     protobuf::Bytes key_val =
144         key_info.AsBytes(static_cast<uint32_t>(Key::Fields::kKeyval));
145     PW_TRY(key_val.status());
146 
147     // The function assume that all keys are ECDSA keys. This is guaranteed
148     // by the fact that all trusted roots have undergone content check.
149 
150     // computes the sha256 hash
151     std::byte sha256_digest[32];
152     stream::IntervalReader bytes_reader = message.GetBytesReader();
153     PW_TRY(crypto::sha256::Hash(bytes_reader, sha256_digest));
154     Result<bool> res = VerifyEcdsaSignature(key_val, sha256_digest, sig);
155     PW_TRY(res.status());
156     if (res.value()) {
157       verified_count++;
158       if (verified_count == threshold.value()) {
159         return OkStatus();
160       }
161     }
162   }
163 
164   if (total_signatures == 0) {
165     // For self verification to tell apart unsigned bundles.
166     return Status::NotFound();
167   }
168 
169   PW_LOG_ERROR("Insufficient signatures. Requires at least %u, verified %zu",
170                static_cast<unsigned>(threshold.value()),
171                verified_count);
172   return Status::Unauthenticated();
173 }
174 
175 // Verifies the signatures of a signed new root metadata against a given
176 // trusted root. The helper function extracts the corresponding key maping
177 // signature requirement, signatures from the trusted root and passes them
178 // to VerifyMetadataSignatures().
179 //
180 // Precondition: The trusted root metadata has undergone content validity check.
VerifyRootMetadataSignatures(protobuf::Message trusted_root,protobuf::Message new_root)181 Result<bool> VerifyRootMetadataSignatures(protobuf::Message trusted_root,
182                                           protobuf::Message new_root) {
183   // Retrieves the trusted root metadata content message.
184   protobuf::Message trusted = trusted_root.AsMessage(static_cast<uint32_t>(
185       SignedRootMetadata::Fields::kSerializedRootMetadata));
186   PW_TRY(trusted.status());
187 
188   // Retrieves the serialized new root metadata bytes.
189   protobuf::Bytes serialized = new_root.AsBytes(static_cast<uint32_t>(
190       SignedRootMetadata::Fields::kSerializedRootMetadata));
191   PW_TRY(serialized.status());
192 
193   // Gets the key mapping from the trusted root metadata.
194   protobuf::StringToMessageMap key_mapping = trusted.AsStringToMessageMap(
195       static_cast<uint32_t>(RootMetadata::Fields::kKeys));
196   PW_TRY(key_mapping.status());
197 
198   // Gets the signatures of the new root.
199   protobuf::RepeatedMessages signatures = new_root.AsRepeatedMessages(
200       static_cast<uint32_t>(SignedRootMetadata::Fields::kSignatures));
201   PW_TRY(signatures.status());
202 
203   // Gets the signature requirement from the trusted root metadata.
204   protobuf::Message signature_requirement = trusted.AsMessage(
205       static_cast<uint32_t>(RootMetadata::Fields::kRootSignatureRequirement));
206   PW_TRY(signature_requirement.status());
207 
208   // Verifies the signatures.
209   PW_TRY(VerifyMetadataSignatures(
210       serialized, signatures, signature_requirement, key_mapping));
211   return true;
212 }
213 
GetMetadataVersion(protobuf::Message & metadata,uint32_t common_metatdata_field_number)214 Result<uint32_t> GetMetadataVersion(protobuf::Message& metadata,
215                                     uint32_t common_metatdata_field_number) {
216   // message [Root|Targets]Metadata {
217   //   ...
218   //   CommonMetadata common_metadata = <field_number>;
219   //   ...
220   // }
221   //
222   // message CommonMetadata {
223   //   ...
224   //   uint32 version = <field_number>;
225   //   ...
226   // }
227   protobuf::Message common_metadata =
228       metadata.AsMessage(common_metatdata_field_number);
229   PW_TRY(common_metadata.status());
230   protobuf::Uint32 res = common_metadata.AsUint32(
231       static_cast<uint32_t>(software_update::CommonMetadata::Fields::kVersion));
232   PW_TRY(res.status());
233   return res.value();
234 }
235 
236 // Reads a protobuf::String into a buffer and returns a std::string_view.
ReadProtoString(protobuf::String str,span<char> buffer)237 Result<std::string_view> ReadProtoString(protobuf::String str,
238                                          span<char> buffer) {
239   stream::IntervalReader reader = str.GetBytesReader();
240   if (reader.interval_size() > buffer.size()) {
241     return Status::ResourceExhausted();
242   }
243 
244   Result<ByteSpan> res = reader.Read(as_writable_bytes(buffer));
245   PW_TRY(res.status());
246   return std::string_view(buffer.data(), res.value().size());
247 }
248 
249 }  // namespace
250 
OpenAndVerify()251 Status UpdateBundleAccessor::OpenAndVerify() {
252   if (Status status = DoOpen(); !status.ok()) {
253     PW_LOG_ERROR("Failed to open staged bundle");
254     return status;
255   }
256 
257   if (Status status = DoVerify(); !status.ok()) {
258     PW_LOG_ERROR("Failed to verified staged bundle");
259     Close().IgnoreError();
260     return status;
261   }
262 
263   return OkStatus();
264 }
265 
GetTotalPayloadSize()266 Result<uint64_t> UpdateBundleAccessor::GetTotalPayloadSize() {
267   protobuf::RepeatedMessages manifested_targets =
268       GetManifest().GetTargetFiles();
269   PW_TRY(manifested_targets.status());
270 
271   protobuf::StringToBytesMap bundled_payloads = bundle_.AsStringToBytesMap(
272       static_cast<uint32_t>(UpdateBundle::Fields::kTargetPayloads));
273   PW_TRY(bundled_payloads.status());
274 
275   uint64_t total_bytes = 0;
276   std::array<std::byte, MAX_TARGET_NAME_LENGTH> name_buffer = {};
277   for (protobuf::Message target : manifested_targets) {
278     protobuf::String target_name =
279         target.AsString(static_cast<uint32_t>(TargetFile::Fields::kFileName));
280 
281     stream::IntervalReader name_reader = target_name.GetBytesReader();
282     PW_TRY(name_reader.status());
283     if (name_reader.interval_size() > name_buffer.size()) {
284       return Status::OutOfRange();
285     }
286 
287     Result<ByteSpan> read_result = name_reader.Read(name_buffer);
288     PW_TRY(read_result.status());
289 
290     ConstByteSpan name_span = read_result.value();
291     std::string_view name_view(reinterpret_cast<const char*>(name_span.data()),
292                                name_span.size_bytes());
293 
294     if (!bundled_payloads[name_view].ok()) {
295       continue;
296     }
297     protobuf::Uint64 target_length =
298         target.AsUint64(static_cast<uint32_t>(TargetFile::Fields::kLength));
299     PW_TRY(target_length.status());
300     total_bytes += target_length.value();
301   }
302 
303   return total_bytes;
304 }
305 
306 // Get the target element corresponding to `target_file`
GetTargetPayload(std::string_view target_name)307 stream::IntervalReader UpdateBundleAccessor::GetTargetPayload(
308     std::string_view target_name) {
309   protobuf::Message manifest_entry = GetManifest().GetTargetFile(target_name);
310   PW_TRY(manifest_entry.status());
311 
312   protobuf::StringToBytesMap payloads_map = bundle_.AsStringToBytesMap(
313       static_cast<uint32_t>(UpdateBundle::Fields::kTargetPayloads));
314   return payloads_map[target_name].GetBytesReader();
315 }
316 
317 // Get the target element corresponding to `target_file`
GetTargetPayload(protobuf::String target_name)318 stream::IntervalReader UpdateBundleAccessor::GetTargetPayload(
319     protobuf::String target_name) {
320   char name_buf[MAX_TARGET_NAME_LENGTH] = {0};
321   Result<std::string_view> name_view = ReadProtoString(target_name, name_buf);
322   PW_TRY(name_view.status());
323   return GetTargetPayload(name_view.value());
324 }
325 
PersistManifest()326 Status UpdateBundleAccessor::PersistManifest() {
327   ManifestAccessor manifest = GetManifest();
328   // GetManifest() fails if the bundle is yet to be verified.
329   PW_TRY(manifest.status());
330 
331   // Notify backend to prepare to receive a new manifest.
332   PW_TRY(backend_.BeforeManifestWrite());
333 
334   Result<stream::Writer*> writer = backend_.GetManifestWriter();
335   PW_TRY(writer.status());
336   PW_CHECK_NOTNULL(writer.value());
337 
338   PW_TRY(manifest.Export(*writer.value()));
339 
340   // Notify backend we are done writing. Backend should finalize
341   // (seal the box).
342   PW_TRY(backend_.AfterManifestWrite());
343 
344   return OkStatus();
345 }
346 
Close()347 Status UpdateBundleAccessor::Close() {
348   bundle_verified_ = false;
349   return update_reader_.IsOpen() ? update_reader_.Close() : OkStatus();
350 }
351 
DoOpen()352 Status UpdateBundleAccessor::DoOpen() {
353   PW_TRY(update_reader_.Open());
354   bundle_ = protobuf::Message(update_reader_.reader(),
355                               update_reader_.reader().ConservativeReadLimit());
356   if (!bundle_.ok()) {
357     update_reader_.Close().IgnoreError();
358     return bundle_.status();
359   }
360   return OkStatus();
361 }
362 
DoVerify()363 Status UpdateBundleAccessor::DoVerify() {
364 #if PW_SOFTWARE_UPDATE_DISABLE_BUNDLE_VERIFICATION
365   PW_LOG_WARN("Bundle verification is compiled out.");
366   bundle_verified_ = true;
367   return OkStatus();
368 #else   // PW_SOFTWARE_UPDATE_DISABLE_BUNDLE_VERIFICATION
369   bundle_verified_ = false;
370 
371   if (self_verification_) {
372     // Use root metadata in staged bundle for self-verification. This root
373     // metadata is optional and used opportunistically in the rest of the
374     // verification flow.
375     trusted_root_ = bundle_.AsMessage(
376         static_cast<uint32_t>(UpdateBundle::Fields::kRootMetadata));
377   } else {
378     // A provisioned on-device root metadata is *required* for formal
379     // verification.
380     if (trusted_root_ = GetOnDeviceTrustedRoot(); !trusted_root_.ok()) {
381       PW_LOG_CRITICAL("Missing on-device trusted root");
382       return Status::Unauthenticated();
383     }
384   }
385 
386   // Verify and upgrade the on-device trust to the incoming root metadata if
387   // one is included.
388   if (Status status = UpgradeRoot(); !status.ok()) {
389     PW_LOG_ERROR("Failed to rotate root metadata");
390     return status;
391   }
392 
393   if (Status status = VerifyTargetsMetadata(); !status.ok()) {
394     PW_LOG_ERROR("Failed to verify Targets metadata");
395     return status;
396   }
397 
398   if (Status status = VerifyTargetsPayloads(); !status.ok()) {
399     PW_LOG_ERROR("Failed to verify all manifested payloads");
400     return status;
401   }
402 
403   bundle_verified_ = true;
404   return OkStatus();
405 #endif  // PW_SOFTWARE_UPDATE_DISABLE_BUNDLE_VERIFICATION
406 }
407 
GetOnDeviceTrustedRoot()408 protobuf::Message UpdateBundleAccessor::GetOnDeviceTrustedRoot() {
409   Result<stream::SeekableReader*> res = backend_.GetRootMetadataReader();
410   if (!(res.ok() && res.value())) {
411     PW_LOG_ERROR("Failed to get on-device Root metadata");
412     return res.status();
413   }
414   // Seek to the beginning so that ConservativeReadLimit() returns the correct
415   // value.
416   PW_TRY(res.value()->Seek(0, stream::Stream::Whence::kBeginning));
417   return protobuf::Message(*res.value(), res.value()->ConservativeReadLimit());
418 }
419 
GetOnDeviceManifest()420 ManifestAccessor UpdateBundleAccessor::GetOnDeviceManifest() {
421   // Notify backend to check if an on-device manifest exists and is valid and if
422   // yes, prepare a ready-to-go reader.
423   PW_TRY(backend_.BeforeManifestRead());
424 
425   Result<stream::SeekableReader*> manifest_reader =
426       backend_.GetManifestReader();
427   PW_TRY(manifest_reader.status());
428   PW_CHECK_NOTNULL(manifest_reader.value());
429 
430   // In case `backend_.BeforeManifestRead()` forgot to reset the reader.
431   PW_TRY(manifest_reader.value()->Seek(0, stream::Stream::Whence::kBeginning));
432 
433   return ManifestAccessor::FromManifest(
434       protobuf::Message(*manifest_reader.value(),
435                         manifest_reader.value()->ConservativeReadLimit()));
436 }
437 
UpgradeRoot()438 Status UpdateBundleAccessor::UpgradeRoot() {
439 #if PW_SOFTWARE_UPDATE_WITH_ROOT_ROTATION
440   protobuf::Message new_root = bundle_.AsMessage(
441       static_cast<uint32_t>(UpdateBundle::Fields::kRootMetadata));
442 
443   if (!new_root.status().ok()) {
444     // Don't bother upgrading if not found or invalid.
445     PW_LOG_WARN("Skipping root metadata rotation: not found or invalid");
446     return OkStatus();
447   }
448 
449   // TODO(b/237580538): Check whether the bundle contains a root metadata that
450   // is different from the on-device trusted root.
451 
452   // Verify the signatures against the trusted root metadata.
453   Result<bool> verify_res =
454       VerifyRootMetadataSignatures(trusted_root_, new_root);
455   if (!(verify_res.status().ok() && verify_res.value())) {
456     PW_LOG_ERROR("Failed to verify incoming root against the current root");
457     return Status::Unauthenticated();
458   }
459 
460   // TODO(b/237580538): Verifiy the content of the new root metadata, including:
461   //    1) Check role magic field.
462   //    2) Check signature requirement. Specifically, check that no key is
463   //       reused across different roles and keys are unique in the same
464   //       requirement.
465   //    3) Check key mapping. Specifically, check that all keys are unique,
466   //       ECDSA keys, and the key ids are exactly the SHA256 of `key type +
467   //       key scheme + key value`.
468 
469   // Verify the signatures against the new root metadata.
470   verify_res = VerifyRootMetadataSignatures(new_root, new_root);
471   if (!(verify_res.status().ok() && verify_res.value())) {
472     PW_LOG_ERROR("Fail to verify incoming root against itself");
473     return Status::Unauthenticated();
474   }
475 
476   // Retrieves the trusted root metadata content message.
477   protobuf::Message trusted_root_content =
478       trusted_root_.AsMessage(static_cast<uint32_t>(
479           SignedRootMetadata::Fields::kSerializedRootMetadata));
480   PW_TRY(trusted_root_content.status());
481   Result<uint32_t> trusted_root_version = GetMetadataVersion(
482       trusted_root_content,
483       static_cast<uint32_t>(RootMetadata::Fields::kCommonMetadata));
484   PW_TRY(trusted_root_version.status());
485 
486   // Retrieves the serialized new root metadata message.
487   protobuf::Message new_root_content = new_root.AsMessage(static_cast<uint32_t>(
488       SignedRootMetadata::Fields::kSerializedRootMetadata));
489   PW_TRY(new_root_content.status());
490   Result<uint32_t> new_root_version = GetMetadataVersion(
491       new_root_content,
492       static_cast<uint32_t>(RootMetadata::Fields::kCommonMetadata));
493   PW_TRY(new_root_version.status());
494 
495   if (trusted_root_version.value() > new_root_version.value()) {
496     PW_LOG_ERROR("Root attempts to rollback from %u to %u",
497                  static_cast<unsigned>(trusted_root_version.value()),
498                  static_cast<unsigned>(new_root_version.value()));
499     return Status::Unauthenticated();
500   }
501 
502   if (!self_verification_) {
503     // Persist the root immediately after it is successfully verified. This is
504     // to make sure the trust anchor is up-to-date in storage as soon as
505     // we are confident. Although targets metadata and product-specific
506     // verification have not been done yet. They should be independent from and
507     // not gate the upgrade of root key. This allows timely revokation of
508     // compromise keys.
509     stream::IntervalReader new_root_reader =
510         new_root.ToBytes().GetBytesReader();
511     if (Status status = backend_.SafelyPersistRootMetadata(new_root_reader);
512         !status.ok()) {
513       PW_LOG_ERROR("Failed to persist rotated root metadata");
514       return status;
515     }
516   }
517 
518   // TODO(b/237580538): Implement key change detection to determine whether
519   // rotation has occured or not. Delete the persisted targets metadata version
520   // if any of the targets keys has been rotated.
521 
522   return OkStatus();
523 #else
524   // Root metadata rotation opted out.
525   return OkStatus();
526 #endif  // PW_SOFTWARE_UPDATE_WITH_ROOT_ROTATION
527 }
528 
VerifyTargetsMetadata()529 Status UpdateBundleAccessor::VerifyTargetsMetadata() {
530   if (self_verification_ && !trusted_root_.status().ok()) {
531     PW_LOG_WARN(
532         "Self-verification won't verify Targets metadata because there is no "
533         "root");
534     return OkStatus();
535   }
536 
537   // A valid trust anchor is required from now on.
538   PW_TRY(trusted_root_.status());
539 
540   // Retrieve the signed targets metadata map.
541   //
542   // message UpdateBundle {
543   //   ...
544   //   map<string, SignedTargetsMetadata> target_metadata = <id>;
545   //   ...
546   // }
547   protobuf::StringToMessageMap signed_targets_metadata_map =
548       bundle_.AsStringToMessageMap(
549           static_cast<uint32_t>(UpdateBundle::Fields::kTargetsMetadata));
550   PW_TRY(signed_targets_metadata_map.status());
551 
552   // The top-level targets metadata is identified by key name "targets" in the
553   // map.
554   protobuf::Message signed_top_level_targets_metadata =
555       signed_targets_metadata_map[kTopLevelTargetsName];
556   PW_TRY(signed_top_level_targets_metadata.status());
557 
558   // Retrieve the serialized metadata.
559   //
560   // message SignedTargetsMetadata {
561   //   ...
562   //   bytes serialized_target_metadata = <id>;
563   //   ...
564   // }
565   protobuf::Message top_level_targets_metadata =
566       signed_top_level_targets_metadata.AsMessage(static_cast<uint32_t>(
567           SignedTargetsMetadata::Fields::kSerializedTargetsMetadata));
568 
569   // Get the sigantures from the signed targets metadata.
570   protobuf::RepeatedMessages signatures =
571       signed_top_level_targets_metadata.AsRepeatedMessages(
572           static_cast<uint32_t>(SignedTargetsMetadata::Fields::kSignatures));
573   PW_TRY(signatures.status());
574 
575   // Retrieve the trusted root metadata message.
576   protobuf::Message trusted_root =
577       trusted_root_.AsMessage(static_cast<uint32_t>(
578           SignedRootMetadata::Fields::kSerializedRootMetadata));
579   PW_TRY(trusted_root.status());
580 
581   // Get the key_mapping from the trusted root metadata.
582   protobuf::StringToMessageMap key_mapping = trusted_root.AsStringToMessageMap(
583       static_cast<uint32_t>(RootMetadata::Fields::kKeys));
584   PW_TRY(key_mapping.status());
585 
586   // Get the target metadtata signature requirement from the trusted root.
587   protobuf::Message signature_requirement =
588       trusted_root.AsMessage(static_cast<uint32_t>(
589           RootMetadata::Fields::kTargetsSignatureRequirement));
590   PW_TRY(signature_requirement.status());
591 
592   // Verify the sigantures
593   Status sig_res =
594       VerifyMetadataSignatures(top_level_targets_metadata.ToBytes(),
595                                signatures,
596                                signature_requirement,
597                                key_mapping);
598 
599   if (self_verification_ && sig_res.IsNotFound()) {
600     PW_LOG_WARN("Self-verification ignoring unsigned bundle");
601     return OkStatus();
602   }
603 
604   if (!sig_res.ok()) {
605     PW_LOG_ERROR("Targets Metadata failed signature verification");
606     return Status::Unauthenticated();
607   }
608 
609   if (self_verification_) {
610     // Don't bother because it does not matter.
611     PW_LOG_WARN("Self verification skips Targets metadata anti-rollback");
612     return OkStatus();
613   }
614 
615   // Anti-rollback check.
616   ManifestAccessor device_manifest = GetOnDeviceManifest();
617   if (device_manifest.status().IsNotFound()) {
618     PW_LOG_WARN("Skipping OTA anti-rollback due to absent device manifest");
619     return OkStatus();
620   }
621 
622   protobuf::Uint32 current_version = device_manifest.GetVersion();
623   PW_TRY(current_version.status());
624 
625   // Retrieves the version from the new metadata
626   Result<uint32_t> new_version = GetMetadataVersion(
627       top_level_targets_metadata,
628       static_cast<uint32_t>(
629           software_update::TargetsMetadata::Fields::kCommonMetadata));
630   PW_TRY(new_version.status());
631   if (current_version.value() > new_version.value()) {
632     PW_LOG_ERROR("Blocking Targets metadata rollback from %u to %u",
633                  static_cast<unsigned>(current_version.value()),
634                  static_cast<unsigned>(new_version.value()));
635     return Status::Unauthenticated();
636   }
637 
638   return OkStatus();
639 }
640 
VerifyTargetsPayloads()641 Status UpdateBundleAccessor::VerifyTargetsPayloads() {
642   ManifestAccessor bundle_manifest = ManifestAccessor::FromBundle(bundle_);
643   PW_TRY(bundle_manifest.status());
644 
645   // Target file descriptors (pathname, length, hash, etc.) listed in the bundle
646   // manifest.
647   protobuf::RepeatedMessages target_files = bundle_manifest.GetTargetFiles();
648   PW_TRY(target_files.status());
649 
650   // Verify length and SHA256 hash for each file listed in the manifest.
651   for (protobuf::Message target_file : target_files) {
652     // Extract target file name in the form of a `std::string_view`.
653     protobuf::String name_proto = target_file.AsString(
654         static_cast<uint32_t>(TargetFile::Fields::kFileName));
655     PW_TRY(name_proto.status());
656     char name_buf[MAX_TARGET_NAME_LENGTH] = {0};
657     Result<std::string_view> target_name =
658         ReadProtoString(name_proto, name_buf);
659     PW_TRY(target_name.status());
660 
661     // Get target length.
662     protobuf::Uint64 target_length = target_file.AsUint64(
663         static_cast<uint32_t>(TargetFile::Fields::kLength));
664     PW_TRY(target_length.status());
665     if (target_length.value() > PW_SOFTWARE_UPDATE_MAX_TARGET_PAYLOAD_SIZE) {
666       PW_LOG_ERROR("Target payload too big. Maximum is %u bytes",
667                    PW_SOFTWARE_UPDATE_MAX_TARGET_PAYLOAD_SIZE);
668       return Status::OutOfRange();
669     }
670 
671     // Get target SHA256 hash.
672     protobuf::Bytes target_sha256 = Status::NotFound();
673     protobuf::RepeatedMessages hashes = target_file.AsRepeatedMessages(
674         static_cast<uint32_t>(TargetFile::Fields::kHashes));
675     for (protobuf::Message hash : hashes) {
676       protobuf::Uint32 hash_function =
677           hash.AsUint32(static_cast<uint32_t>(Hash::Fields::kFunction));
678       PW_TRY(hash_function.status());
679 
680       if (hash_function.value() ==
681           static_cast<uint32_t>(HashFunction::SHA256)) {
682         target_sha256 =
683             hash.AsBytes(static_cast<uint32_t>(Hash::Fields::kHash));
684         break;
685       }
686     }
687     PW_TRY(target_sha256.status());
688 
689     if (Status status = VerifyTargetPayload(
690             bundle_manifest, target_name.value(), target_length, target_sha256);
691         !status.ok()) {
692       PW_LOG_ERROR("Target: %s failed verification",
693                    pw::MakeString(target_name.value()).c_str());
694       return status;
695     }
696   }  // for each target file in manifest.
697 
698   return OkStatus();
699 }
700 
VerifyTargetPayload(ManifestAccessor,std::string_view target_name,protobuf::Uint64 expected_length,protobuf::Bytes expected_sha256)701 Status UpdateBundleAccessor::VerifyTargetPayload(
702     ManifestAccessor,
703     std::string_view target_name,
704     protobuf::Uint64 expected_length,
705     protobuf::Bytes expected_sha256) {
706   protobuf::StringToBytesMap payloads_map = bundle_.AsStringToBytesMap(
707       static_cast<uint32_t>(UpdateBundle::Fields::kTargetPayloads));
708   stream::IntervalReader payload_reader =
709       payloads_map[target_name].GetBytesReader();
710 
711   Status status;
712 
713   if (payload_reader.ok()) {
714     status = VerifyInBundleTargetPayload(
715         expected_length, expected_sha256, payload_reader);
716   } else {
717     status = VerifyOutOfBundleTargetPayload(
718         target_name, expected_length, expected_sha256);
719   }
720 
721   // TODO(alizhang): Notify backend to do additional checks by calling
722   // backend_.VerifyTargetFile(...).
723   return status;
724 }
725 
726 // TODO(alizhang): Add unit tests for all failure conditions.
VerifyOutOfBundleTargetPayload(std::string_view target_name,protobuf::Uint64 expected_length,protobuf::Bytes expected_sha256)727 Status UpdateBundleAccessor::VerifyOutOfBundleTargetPayload(
728     std::string_view target_name,
729     [[maybe_unused]] protobuf::Uint64 expected_length,
730     [[maybe_unused]] protobuf::Bytes expected_sha256) {
731 #if PW_SOFTWARE_UPDATE_WITH_PERSONALIZATION
732   // The target payload is "personalized out". We we can't take a measurement
733   // without backend help. For now we will check against the device manifest
734   // which contains a cached measurement of the last software update.
735   ManifestAccessor device_manifest = GetOnDeviceManifest();
736   if (!device_manifest.ok()) {
737     PW_LOG_ERROR(
738         "Can't verify personalized-out target because on-device manifest is "
739         "not found");
740     return Status::Unauthenticated();
741   }
742 
743   protobuf::Message cached = device_manifest.GetTargetFile(target_name);
744   if (!cached.ok()) {
745     PW_LOG_ERROR(
746         "Can't verify personalized-out target because it is not found from "
747         "on-device manifest");
748     return Status::Unauthenticated();
749   }
750 
751   protobuf::Uint64 cached_length =
752       cached.AsUint64(static_cast<uint32_t>(TargetFile::Fields::kLength));
753   PW_TRY(cached_length.status());
754   if (cached_length.value() != expected_length.value()) {
755     PW_LOG_ERROR("Personalized-out target has bad length: %u, expected: %u",
756                  static_cast<unsigned>(cached_length.value()),
757                  static_cast<unsigned>(expected_length.value()));
758     return Status::Unauthenticated();
759   }
760 
761   protobuf::Bytes cached_sha256 = Status::NotFound();
762   protobuf::RepeatedMessages hashes = cached.AsRepeatedMessages(
763       static_cast<uint32_t>(TargetFile::Fields::kHashes));
764   for (protobuf::Message hash : hashes) {
765     protobuf::Uint32 hash_function =
766         hash.AsUint32(static_cast<uint32_t>(Hash::Fields::kFunction));
767     PW_TRY(hash_function.status());
768 
769     if (hash_function.value() == static_cast<uint32_t>(HashFunction::SHA256)) {
770       cached_sha256 = hash.AsBytes(static_cast<uint32_t>(Hash::Fields::kHash));
771       break;
772     }
773   }
774   std::byte sha256[crypto::sha256::kDigestSizeBytes] = {};
775   PW_TRY(cached_sha256.GetBytesReader().Read(sha256));
776 
777   Result<bool> hash_equal = expected_sha256.Equal(sha256);
778   PW_TRY(hash_equal.status());
779   if (!hash_equal.value()) {
780     PW_LOG_ERROR("Personalized-out target has a bad hash");
781     return Status::Unauthenticated();
782   }
783 
784   return OkStatus();
785 #else
786   PW_LOG_ERROR("Target file %s not found in bundle", target_name.data());
787   return Status::Unauthenticated();
788 #endif  // PW_SOFTWARE_UPDATE_WITH_PERSONALIZATION
789 }
790 
VerifyInBundleTargetPayload(protobuf::Uint64 expected_length,protobuf::Bytes expected_sha256,stream::IntervalReader payload_reader)791 Status UpdateBundleAccessor::VerifyInBundleTargetPayload(
792     protobuf::Uint64 expected_length,
793     protobuf::Bytes expected_sha256,
794     stream::IntervalReader payload_reader) {
795   // If the target payload is included in the bundle, simply take a
796   // measurement.
797   uint64_t actual_length = payload_reader.interval_size();
798   if (actual_length != expected_length.value()) {
799     PW_LOG_ERROR("Wrong payload length. Expected: %u, actual: %u",
800                  static_cast<unsigned>(expected_length.value()),
801                  static_cast<unsigned>(actual_length));
802     return Status::Unauthenticated();
803   }
804 
805   std::byte actual_sha256[crypto::sha256::kDigestSizeBytes] = {};
806   PW_TRY(crypto::sha256::Hash(payload_reader, actual_sha256));
807   Result<bool> hash_equal = expected_sha256.Equal(actual_sha256);
808   PW_TRY(hash_equal.status());
809   if (!hash_equal.value()) {
810     PW_LOG_ERROR("Wrong payload sha256 hash");
811     return Status::Unauthenticated();
812   }
813 
814   return OkStatus();
815 }
816 
GetManifest()817 ManifestAccessor UpdateBundleAccessor::GetManifest() {
818   if (!bundle_verified_) {
819     PW_LOG_DEBUG("Bundled has not passed verification yet");
820     return Status::FailedPrecondition();
821   }
822 
823   return ManifestAccessor::FromBundle(bundle_);
824 }
825 
826 }  // namespace pw::software_update
827