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,
461 // including:
462 // 1) Check role magic field.
463 // 2) Check signature requirement. Specifically, check that no key is
464 // reused across different roles and keys are unique in the same
465 // requirement.
466 // 3) Check key mapping. Specifically, check that all keys are unique,
467 // ECDSA keys, and the key ids are exactly the SHA256 of `key type +
468 // key scheme + key value`.
469
470 // Verify the signatures against the new root metadata.
471 verify_res = VerifyRootMetadataSignatures(new_root, new_root);
472 if (!(verify_res.status().ok() && verify_res.value())) {
473 PW_LOG_ERROR("Fail to verify incoming root against itself");
474 return Status::Unauthenticated();
475 }
476
477 // Retrieves the trusted root metadata content message.
478 protobuf::Message trusted_root_content =
479 trusted_root_.AsMessage(static_cast<uint32_t>(
480 SignedRootMetadata::Fields::kSerializedRootMetadata));
481 PW_TRY(trusted_root_content.status());
482 Result<uint32_t> trusted_root_version = GetMetadataVersion(
483 trusted_root_content,
484 static_cast<uint32_t>(RootMetadata::Fields::kCommonMetadata));
485 PW_TRY(trusted_root_version.status());
486
487 // Retrieves the serialized new root metadata message.
488 protobuf::Message new_root_content = new_root.AsMessage(static_cast<uint32_t>(
489 SignedRootMetadata::Fields::kSerializedRootMetadata));
490 PW_TRY(new_root_content.status());
491 Result<uint32_t> new_root_version = GetMetadataVersion(
492 new_root_content,
493 static_cast<uint32_t>(RootMetadata::Fields::kCommonMetadata));
494 PW_TRY(new_root_version.status());
495
496 if (trusted_root_version.value() > new_root_version.value()) {
497 PW_LOG_ERROR("Root attempts to rollback from %u to %u",
498 static_cast<unsigned>(trusted_root_version.value()),
499 static_cast<unsigned>(new_root_version.value()));
500 return Status::Unauthenticated();
501 }
502
503 if (!self_verification_) {
504 // Persist the root immediately after it is successfully verified. This is
505 // to make sure the trust anchor is up-to-date in storage as soon as
506 // we are confident. Although targets metadata and product-specific
507 // verification have not been done yet. They should be independent from and
508 // not gate the upgrade of root key. This allows timely revokation of
509 // compromise keys.
510 stream::IntervalReader new_root_reader =
511 new_root.ToBytes().GetBytesReader();
512 if (Status status = backend_.SafelyPersistRootMetadata(new_root_reader);
513 !status.ok()) {
514 PW_LOG_ERROR("Failed to persist rotated root metadata");
515 return status;
516 }
517 }
518
519 // TODO: b/237580538 - Implement key change detection to determine whether
520 // rotation has occured or not. Delete the persisted targets metadata version
521 // if any of the targets keys has been rotated.
522
523 return OkStatus();
524 #else
525 // Root metadata rotation opted out.
526 return OkStatus();
527 #endif // PW_SOFTWARE_UPDATE_WITH_ROOT_ROTATION
528 }
529
VerifyTargetsMetadata()530 Status UpdateBundleAccessor::VerifyTargetsMetadata() {
531 if (self_verification_ && !trusted_root_.status().ok()) {
532 PW_LOG_WARN(
533 "Self-verification won't verify Targets metadata because there is no "
534 "root");
535 return OkStatus();
536 }
537
538 // A valid trust anchor is required from now on.
539 PW_TRY(trusted_root_.status());
540
541 // Retrieve the signed targets metadata map.
542 //
543 // message UpdateBundle {
544 // ...
545 // map<string, SignedTargetsMetadata> target_metadata = <id>;
546 // ...
547 // }
548 protobuf::StringToMessageMap signed_targets_metadata_map =
549 bundle_.AsStringToMessageMap(
550 static_cast<uint32_t>(UpdateBundle::Fields::kTargetsMetadata));
551 PW_TRY(signed_targets_metadata_map.status());
552
553 // The top-level targets metadata is identified by key name "targets" in the
554 // map.
555 protobuf::Message signed_top_level_targets_metadata =
556 signed_targets_metadata_map[kTopLevelTargetsName];
557 PW_TRY(signed_top_level_targets_metadata.status());
558
559 // Retrieve the serialized metadata.
560 //
561 // message SignedTargetsMetadata {
562 // ...
563 // bytes serialized_target_metadata = <id>;
564 // ...
565 // }
566 protobuf::Message top_level_targets_metadata =
567 signed_top_level_targets_metadata.AsMessage(static_cast<uint32_t>(
568 SignedTargetsMetadata::Fields::kSerializedTargetsMetadata));
569
570 // Get the sigantures from the signed targets metadata.
571 protobuf::RepeatedMessages signatures =
572 signed_top_level_targets_metadata.AsRepeatedMessages(
573 static_cast<uint32_t>(SignedTargetsMetadata::Fields::kSignatures));
574 PW_TRY(signatures.status());
575
576 // Retrieve the trusted root metadata message.
577 protobuf::Message trusted_root =
578 trusted_root_.AsMessage(static_cast<uint32_t>(
579 SignedRootMetadata::Fields::kSerializedRootMetadata));
580 PW_TRY(trusted_root.status());
581
582 // Get the key_mapping from the trusted root metadata.
583 protobuf::StringToMessageMap key_mapping = trusted_root.AsStringToMessageMap(
584 static_cast<uint32_t>(RootMetadata::Fields::kKeys));
585 PW_TRY(key_mapping.status());
586
587 // Get the target metadtata signature requirement from the trusted root.
588 protobuf::Message signature_requirement =
589 trusted_root.AsMessage(static_cast<uint32_t>(
590 RootMetadata::Fields::kTargetsSignatureRequirement));
591 PW_TRY(signature_requirement.status());
592
593 // Verify the sigantures
594 Status sig_res =
595 VerifyMetadataSignatures(top_level_targets_metadata.ToBytes(),
596 signatures,
597 signature_requirement,
598 key_mapping);
599
600 if (self_verification_ && sig_res.IsNotFound()) {
601 PW_LOG_WARN("Self-verification ignoring unsigned bundle");
602 return OkStatus();
603 }
604
605 if (!sig_res.ok()) {
606 PW_LOG_ERROR("Targets Metadata failed signature verification");
607 return Status::Unauthenticated();
608 }
609
610 if (self_verification_) {
611 // Don't bother because it does not matter.
612 PW_LOG_WARN("Self verification skips Targets metadata anti-rollback");
613 return OkStatus();
614 }
615
616 // Anti-rollback check.
617 ManifestAccessor device_manifest = GetOnDeviceManifest();
618 if (device_manifest.status().IsNotFound()) {
619 PW_LOG_WARN("Skipping OTA anti-rollback due to absent device manifest");
620 return OkStatus();
621 }
622
623 protobuf::Uint32 current_version = device_manifest.GetVersion();
624 PW_TRY(current_version.status());
625
626 // Retrieves the version from the new metadata
627 Result<uint32_t> new_version = GetMetadataVersion(
628 top_level_targets_metadata,
629 static_cast<uint32_t>(
630 software_update::TargetsMetadata::Fields::kCommonMetadata));
631 PW_TRY(new_version.status());
632 if (current_version.value() > new_version.value()) {
633 PW_LOG_ERROR("Blocking Targets metadata rollback from %u to %u",
634 static_cast<unsigned>(current_version.value()),
635 static_cast<unsigned>(new_version.value()));
636 return Status::Unauthenticated();
637 }
638
639 return OkStatus();
640 }
641
VerifyTargetsPayloads()642 Status UpdateBundleAccessor::VerifyTargetsPayloads() {
643 ManifestAccessor bundle_manifest = ManifestAccessor::FromBundle(bundle_);
644 PW_TRY(bundle_manifest.status());
645
646 // Target file descriptors (pathname, length, hash, etc.) listed in the bundle
647 // manifest.
648 protobuf::RepeatedMessages target_files = bundle_manifest.GetTargetFiles();
649 PW_TRY(target_files.status());
650
651 // Verify length and SHA256 hash for each file listed in the manifest.
652 for (protobuf::Message target_file : target_files) {
653 // Extract target file name in the form of a `std::string_view`.
654 protobuf::String name_proto = target_file.AsString(
655 static_cast<uint32_t>(TargetFile::Fields::kFileName));
656 PW_TRY(name_proto.status());
657 char name_buf[MAX_TARGET_NAME_LENGTH] = {0};
658 Result<std::string_view> target_name =
659 ReadProtoString(name_proto, name_buf);
660 PW_TRY(target_name.status());
661
662 // Get target length.
663 protobuf::Uint64 target_length = target_file.AsUint64(
664 static_cast<uint32_t>(TargetFile::Fields::kLength));
665 PW_TRY(target_length.status());
666 if (target_length.value() > PW_SOFTWARE_UPDATE_MAX_TARGET_PAYLOAD_SIZE) {
667 PW_LOG_ERROR("Target payload too big. Maximum is %u bytes",
668 PW_SOFTWARE_UPDATE_MAX_TARGET_PAYLOAD_SIZE);
669 return Status::OutOfRange();
670 }
671
672 // Get target SHA256 hash.
673 protobuf::Bytes target_sha256 = Status::NotFound();
674 protobuf::RepeatedMessages hashes = target_file.AsRepeatedMessages(
675 static_cast<uint32_t>(TargetFile::Fields::kHashes));
676 for (protobuf::Message hash : hashes) {
677 protobuf::Uint32 hash_function =
678 hash.AsUint32(static_cast<uint32_t>(Hash::Fields::kFunction));
679 PW_TRY(hash_function.status());
680
681 if (hash_function.value() ==
682 static_cast<uint32_t>(HashFunction::SHA256)) {
683 target_sha256 =
684 hash.AsBytes(static_cast<uint32_t>(Hash::Fields::kHash));
685 break;
686 }
687 }
688 PW_TRY(target_sha256.status());
689
690 if (Status status = VerifyTargetPayload(
691 bundle_manifest, target_name.value(), target_length, target_sha256);
692 !status.ok()) {
693 PW_LOG_ERROR("Target: %s failed verification",
694 pw::MakeString(target_name.value()).c_str());
695 return status;
696 }
697 } // for each target file in manifest.
698
699 return OkStatus();
700 }
701
VerifyTargetPayload(ManifestAccessor,std::string_view target_name,protobuf::Uint64 expected_length,protobuf::Bytes expected_sha256)702 Status UpdateBundleAccessor::VerifyTargetPayload(
703 ManifestAccessor,
704 std::string_view target_name,
705 protobuf::Uint64 expected_length,
706 protobuf::Bytes expected_sha256) {
707 protobuf::StringToBytesMap payloads_map = bundle_.AsStringToBytesMap(
708 static_cast<uint32_t>(UpdateBundle::Fields::kTargetPayloads));
709 stream::IntervalReader payload_reader =
710 payloads_map[target_name].GetBytesReader();
711
712 Status status;
713
714 if (payload_reader.ok()) {
715 status = VerifyInBundleTargetPayload(
716 expected_length, expected_sha256, payload_reader);
717 } else {
718 status = VerifyOutOfBundleTargetPayload(
719 target_name, expected_length, expected_sha256);
720 }
721
722 // TODO(alizhang): Notify backend to do additional checks by calling
723 // backend_.VerifyTargetFile(...).
724 return status;
725 }
726
727 // TODO(alizhang): Add unit tests for all failure conditions.
VerifyOutOfBundleTargetPayload(std::string_view target_name,protobuf::Uint64 expected_length,protobuf::Bytes expected_sha256)728 Status UpdateBundleAccessor::VerifyOutOfBundleTargetPayload(
729 std::string_view target_name,
730 [[maybe_unused]] protobuf::Uint64 expected_length,
731 [[maybe_unused]] protobuf::Bytes expected_sha256) {
732 #if PW_SOFTWARE_UPDATE_WITH_PERSONALIZATION
733 // The target payload is "personalized out". We we can't take a measurement
734 // without backend help. For now we will check against the device manifest
735 // which contains a cached measurement of the last software update.
736 ManifestAccessor device_manifest = GetOnDeviceManifest();
737 if (!device_manifest.ok()) {
738 PW_LOG_ERROR(
739 "Can't verify personalized-out target because on-device manifest is "
740 "not found");
741 return Status::Unauthenticated();
742 }
743
744 protobuf::Message cached = device_manifest.GetTargetFile(target_name);
745 if (!cached.ok()) {
746 PW_LOG_ERROR(
747 "Can't verify personalized-out target because it is not found from "
748 "on-device manifest");
749 return Status::Unauthenticated();
750 }
751
752 protobuf::Uint64 cached_length =
753 cached.AsUint64(static_cast<uint32_t>(TargetFile::Fields::kLength));
754 PW_TRY(cached_length.status());
755 if (cached_length.value() != expected_length.value()) {
756 PW_LOG_ERROR("Personalized-out target has bad length: %u, expected: %u",
757 static_cast<unsigned>(cached_length.value()),
758 static_cast<unsigned>(expected_length.value()));
759 return Status::Unauthenticated();
760 }
761
762 protobuf::Bytes cached_sha256 = Status::NotFound();
763 protobuf::RepeatedMessages hashes = cached.AsRepeatedMessages(
764 static_cast<uint32_t>(TargetFile::Fields::kHashes));
765 for (protobuf::Message hash : hashes) {
766 protobuf::Uint32 hash_function =
767 hash.AsUint32(static_cast<uint32_t>(Hash::Fields::kFunction));
768 PW_TRY(hash_function.status());
769
770 if (hash_function.value() == static_cast<uint32_t>(HashFunction::SHA256)) {
771 cached_sha256 = hash.AsBytes(static_cast<uint32_t>(Hash::Fields::kHash));
772 break;
773 }
774 }
775 std::byte sha256[crypto::sha256::kDigestSizeBytes] = {};
776 PW_TRY(cached_sha256.GetBytesReader().Read(sha256));
777
778 Result<bool> hash_equal = expected_sha256.Equal(sha256);
779 PW_TRY(hash_equal.status());
780 if (!hash_equal.value()) {
781 PW_LOG_ERROR("Personalized-out target has a bad hash");
782 return Status::Unauthenticated();
783 }
784
785 return OkStatus();
786 #else
787 PW_LOG_ERROR("Target file %s not found in bundle", target_name.data());
788 return Status::Unauthenticated();
789 #endif // PW_SOFTWARE_UPDATE_WITH_PERSONALIZATION
790 }
791
VerifyInBundleTargetPayload(protobuf::Uint64 expected_length,protobuf::Bytes expected_sha256,stream::IntervalReader payload_reader)792 Status UpdateBundleAccessor::VerifyInBundleTargetPayload(
793 protobuf::Uint64 expected_length,
794 protobuf::Bytes expected_sha256,
795 stream::IntervalReader payload_reader) {
796 // If the target payload is included in the bundle, simply take a
797 // measurement.
798 uint64_t actual_length = payload_reader.interval_size();
799 if (actual_length != expected_length.value()) {
800 PW_LOG_ERROR("Wrong payload length. Expected: %u, actual: %u",
801 static_cast<unsigned>(expected_length.value()),
802 static_cast<unsigned>(actual_length));
803 return Status::Unauthenticated();
804 }
805
806 std::byte actual_sha256[crypto::sha256::kDigestSizeBytes] = {};
807 PW_TRY(crypto::sha256::Hash(payload_reader, actual_sha256));
808 Result<bool> hash_equal = expected_sha256.Equal(actual_sha256);
809 PW_TRY(hash_equal.status());
810 if (!hash_equal.value()) {
811 PW_LOG_ERROR("Wrong payload sha256 hash");
812 return Status::Unauthenticated();
813 }
814
815 return OkStatus();
816 }
817
GetManifest()818 ManifestAccessor UpdateBundleAccessor::GetManifest() {
819 if (!bundle_verified_) {
820 PW_LOG_DEBUG("Bundled has not passed verification yet");
821 return Status::FailedPrecondition();
822 }
823
824 return ManifestAccessor::FromBundle(bundle_);
825 }
826
827 } // namespace pw::software_update
828