blob: 2cb73eb12e1b6981b41e0b4d942c8e40260a5809 [file] [log] [blame]
Sen Jiang9c89e842018-02-02 13:51:21 -08001//
2// Copyright (C) 2018 The Android Open Source Project
3//
4// Licensed under the Apache License, Version 2.0 (the "License");
5// you may not use this file except in compliance with the License.
6// You may obtain a copy of the License at
7//
8// http://www.apache.org/licenses/LICENSE-2.0
9//
10// Unless required by applicable law or agreed to in writing, software
11// distributed under the License is distributed on an "AS IS" BASIS,
12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13// See the License for the specific language governing permissions and
14// limitations under the License.
15//
16
17#include "update_engine/payload_consumer/payload_metadata.h"
18
19#include <endian.h>
20
Kelvin Zhangaba70ab2020-08-04 10:32:59 -040021#include <base/strings/stringprintf.h>
Sen Jiang9c89e842018-02-02 13:51:21 -080022#include <brillo/data_encoding.h>
23
xunchangcda3c032019-03-26 15:41:14 -070024#include "update_engine/common/constants.h"
Sen Jiang9c89e842018-02-02 13:51:21 -080025#include "update_engine/common/hash_calculator.h"
26#include "update_engine/common/utils.h"
27#include "update_engine/payload_consumer/payload_constants.h"
28#include "update_engine/payload_consumer/payload_verifier.h"
29
Sen Jiang9b2f1782019-01-24 14:27:50 -080030using std::string;
31
Sen Jiang9c89e842018-02-02 13:51:21 -080032namespace chromeos_update_engine {
33
34const uint64_t PayloadMetadata::kDeltaVersionOffset = sizeof(kDeltaMagic);
35const uint64_t PayloadMetadata::kDeltaVersionSize = 8;
36const uint64_t PayloadMetadata::kDeltaManifestSizeOffset =
37 kDeltaVersionOffset + kDeltaVersionSize;
38const uint64_t PayloadMetadata::kDeltaManifestSizeSize = 8;
39const uint64_t PayloadMetadata::kDeltaMetadataSignatureSizeSize = 4;
40
Amin Hassani822d4852020-03-10 01:50:42 +000041uint64_t PayloadMetadata::GetMetadataSignatureSizeOffset() const {
42 return kDeltaManifestSizeOffset + kDeltaManifestSizeSize;
Sen Jiang9c89e842018-02-02 13:51:21 -080043}
44
Amin Hassani822d4852020-03-10 01:50:42 +000045uint64_t PayloadMetadata::GetManifestOffset() const {
46 // Actual manifest begins right after the metadata signature size field.
47 return kDeltaManifestSizeOffset + kDeltaManifestSizeSize +
48 kDeltaMetadataSignatureSizeSize;
Sen Jiang9c89e842018-02-02 13:51:21 -080049}
50
51MetadataParseResult PayloadMetadata::ParsePayloadHeader(
Sen Jiangf1236632018-05-11 16:03:23 -070052 const brillo::Blob& payload, ErrorCode* error) {
Sen Jiang9c89e842018-02-02 13:51:21 -080053 // Ensure we have data to cover the major payload version.
54 if (payload.size() < kDeltaManifestSizeOffset)
55 return MetadataParseResult::kInsufficientData;
56
57 // Validate the magic string.
58 if (memcmp(payload.data(), kDeltaMagic, sizeof(kDeltaMagic)) != 0) {
Kelvin Zhangaba70ab2020-08-04 10:32:59 -040059 LOG(ERROR) << "Bad payload format -- invalid delta magic: "
60 << base::StringPrintf("%02x%02x%02x%02x",
61 payload[0],
62 payload[1],
63 payload[2],
64 payload[3])
65 << " Expected: "
66 << base::StringPrintf("%02x%02x%02x%02x",
67 kDeltaMagic[0],
68 kDeltaMagic[1],
69 kDeltaMagic[2],
70 kDeltaMagic[3]);
Sen Jiang9c89e842018-02-02 13:51:21 -080071 *error = ErrorCode::kDownloadInvalidMetadataMagicString;
72 return MetadataParseResult::kError;
73 }
74
Amin Hassani822d4852020-03-10 01:50:42 +000075 uint64_t manifest_offset = GetManifestOffset();
76 // Check again with the manifest offset.
77 if (payload.size() < manifest_offset)
78 return MetadataParseResult::kInsufficientData;
79
Sen Jiang9c89e842018-02-02 13:51:21 -080080 // Extract the payload version from the metadata.
81 static_assert(sizeof(major_payload_version_) == kDeltaVersionSize,
82 "Major payload version size mismatch");
83 memcpy(&major_payload_version_,
84 &payload[kDeltaVersionOffset],
85 kDeltaVersionSize);
86 // Switch big endian to host.
87 major_payload_version_ = be64toh(major_payload_version_);
88
Sen Jiangf1236632018-05-11 16:03:23 -070089 if (major_payload_version_ < kMinSupportedMajorPayloadVersion ||
90 major_payload_version_ > kMaxSupportedMajorPayloadVersion) {
Sen Jiang9c89e842018-02-02 13:51:21 -080091 LOG(ERROR) << "Bad payload format -- unsupported payload version: "
92 << major_payload_version_;
93 *error = ErrorCode::kUnsupportedMajorPayloadVersion;
94 return MetadataParseResult::kError;
95 }
96
Sen Jiang9c89e842018-02-02 13:51:21 -080097 // Next, parse the manifest size.
98 static_assert(sizeof(manifest_size_) == kDeltaManifestSizeSize,
99 "manifest_size size mismatch");
100 memcpy(&manifest_size_,
101 &payload[kDeltaManifestSizeOffset],
102 kDeltaManifestSizeSize);
103 manifest_size_ = be64toh(manifest_size_); // switch big endian to host
104
Sen Jiang840a7ea2018-09-19 14:29:44 -0700105 metadata_size_ = manifest_offset + manifest_size_;
106 if (metadata_size_ < manifest_size_) {
107 // Overflow detected.
Andrew3a7dc262019-12-19 11:38:08 -0800108 LOG(ERROR) << "Overflow detected on manifest size.";
Sen Jiang840a7ea2018-09-19 14:29:44 -0700109 *error = ErrorCode::kDownloadInvalidMetadataSize;
110 return MetadataParseResult::kError;
111 }
112
Amin Hassani822d4852020-03-10 01:50:42 +0000113 // Parse the metadata signature size.
114 static_assert(
115 sizeof(metadata_signature_size_) == kDeltaMetadataSignatureSizeSize,
116 "metadata_signature_size size mismatch");
117 uint64_t metadata_signature_size_offset = GetMetadataSignatureSizeOffset();
118 memcpy(&metadata_signature_size_,
119 &payload[metadata_signature_size_offset],
120 kDeltaMetadataSignatureSizeSize);
121 metadata_signature_size_ = be32toh(metadata_signature_size_);
Sen Jiang840a7ea2018-09-19 14:29:44 -0700122
Amin Hassani822d4852020-03-10 01:50:42 +0000123 if (metadata_size_ + metadata_signature_size_ < metadata_size_) {
124 // Overflow detected.
125 LOG(ERROR) << "Overflow detected on metadata and signature size.";
126 *error = ErrorCode::kDownloadInvalidMetadataSize;
127 return MetadataParseResult::kError;
Sen Jiang9c89e842018-02-02 13:51:21 -0800128 }
Sen Jiang9c89e842018-02-02 13:51:21 -0800129 return MetadataParseResult::kSuccess;
130}
131
Sen Jiang44ac3ea2018-10-18 15:10:20 -0700132bool PayloadMetadata::ParsePayloadHeader(const brillo::Blob& payload) {
133 ErrorCode error;
134 return ParsePayloadHeader(payload, &error) == MetadataParseResult::kSuccess;
135}
136
Sen Jiang9c89e842018-02-02 13:51:21 -0800137bool PayloadMetadata::GetManifest(const brillo::Blob& payload,
138 DeltaArchiveManifest* out_manifest) const {
Amin Hassani822d4852020-03-10 01:50:42 +0000139 uint64_t manifest_offset = GetManifestOffset();
Sen Jiang9c89e842018-02-02 13:51:21 -0800140 CHECK_GE(payload.size(), manifest_offset + manifest_size_);
141 return out_manifest->ParseFromArray(&payload[manifest_offset],
142 manifest_size_);
143}
144
145ErrorCode PayloadMetadata::ValidateMetadataSignature(
146 const brillo::Blob& payload,
Sen Jiang9b2f1782019-01-24 14:27:50 -0800147 const string& metadata_signature,
Tianjie Xu7a78d632019-10-08 16:32:39 -0700148 const PayloadVerifier& payload_verifier) const {
Sen Jiang9c89e842018-02-02 13:51:21 -0800149 if (payload.size() < metadata_size_ + metadata_signature_size_)
150 return ErrorCode::kDownloadMetadataSignatureError;
151
Sen Jiang9b2f1782019-01-24 14:27:50 -0800152 // A single signature in raw bytes.
153 brillo::Blob metadata_signature_blob;
154 // The serialized Signatures protobuf message stored in major version >=2
155 // payload, it may contain multiple signatures.
156 string metadata_signature_protobuf;
Sen Jiang9c89e842018-02-02 13:51:21 -0800157 if (!metadata_signature.empty()) {
158 // Convert base64-encoded signature to raw bytes.
159 if (!brillo::data_encoding::Base64Decode(metadata_signature,
160 &metadata_signature_blob)) {
161 LOG(ERROR) << "Unable to decode base64 metadata signature: "
162 << metadata_signature;
163 return ErrorCode::kDownloadMetadataSignatureError;
164 }
Amin Hassani822d4852020-03-10 01:50:42 +0000165 } else {
Sen Jiang9b2f1782019-01-24 14:27:50 -0800166 metadata_signature_protobuf.assign(
Sen Jiang9c89e842018-02-02 13:51:21 -0800167 payload.begin() + metadata_size_,
168 payload.begin() + metadata_size_ + metadata_signature_size_);
169 }
170
Sen Jiang9b2f1782019-01-24 14:27:50 -0800171 if (metadata_signature_blob.empty() && metadata_signature_protobuf.empty()) {
Sen Jiang9c89e842018-02-02 13:51:21 -0800172 LOG(ERROR) << "Missing mandatory metadata signature in both Omaha "
173 << "response and payload.";
174 return ErrorCode::kDownloadMetadataSignatureMissingError;
175 }
176
xunchangcda3c032019-03-26 15:41:14 -0700177 brillo::Blob metadata_hash;
Sen Jiang9c89e842018-02-02 13:51:21 -0800178 if (!HashCalculator::RawHashOfBytes(
xunchangcda3c032019-03-26 15:41:14 -0700179 payload.data(), metadata_size_, &metadata_hash)) {
Sen Jiang9c89e842018-02-02 13:51:21 -0800180 LOG(ERROR) << "Unable to compute actual hash of manifest";
181 return ErrorCode::kDownloadMetadataSignatureVerificationError;
182 }
183
xunchangcda3c032019-03-26 15:41:14 -0700184 if (metadata_hash.size() != kSHA256Size) {
185 LOG(ERROR) << "Computed actual hash of metadata has incorrect size: "
186 << metadata_hash.size();
Sen Jiang9c89e842018-02-02 13:51:21 -0800187 return ErrorCode::kDownloadMetadataSignatureVerificationError;
188 }
189
190 if (!metadata_signature_blob.empty()) {
Tianjie Xu6cf830b2019-09-30 11:31:49 -0700191 brillo::Blob decrypted_signature;
Tianjie Xu7a78d632019-10-08 16:32:39 -0700192 if (!payload_verifier.VerifyRawSignature(
Tianjie Xu6cf830b2019-09-30 11:31:49 -0700193 metadata_signature_blob, metadata_hash, &decrypted_signature)) {
194 LOG(ERROR) << "Manifest hash verification failed. Decrypted hash = ";
195 utils::HexDumpVector(decrypted_signature);
196 LOG(ERROR) << "Calculated hash before padding = ";
197 utils::HexDumpVector(metadata_hash);
Sen Jiang9c89e842018-02-02 13:51:21 -0800198 return ErrorCode::kDownloadMetadataSignatureMismatch;
199 }
200 } else {
Tianjie Xu7a78d632019-10-08 16:32:39 -0700201 if (!payload_verifier.VerifySignature(metadata_signature_protobuf,
202 metadata_hash)) {
Sen Jiang9c89e842018-02-02 13:51:21 -0800203 LOG(ERROR) << "Manifest hash verification failed.";
204 return ErrorCode::kDownloadMetadataSignatureMismatch;
205 }
206 }
207
208 // The autoupdate_CatchBadSignatures test checks for this string in
209 // log-files. Keep in sync.
210 LOG(INFO) << "Metadata hash signature matches value in Omaha response.";
211 return ErrorCode::kSuccess;
212}
213
Amin Hassani79821002019-05-06 17:40:49 -0700214bool PayloadMetadata::ParsePayloadFile(const string& payload_path,
215 DeltaArchiveManifest* manifest,
216 Signatures* metadata_signatures) {
217 brillo::Blob payload;
218 TEST_AND_RETURN_FALSE(
219 utils::ReadFileChunk(payload_path, 0, kMaxPayloadHeaderSize, &payload));
Amin Hassani822d4852020-03-10 01:50:42 +0000220 TEST_AND_RETURN_FALSE(ParsePayloadHeader(payload));
Amin Hassani79821002019-05-06 17:40:49 -0700221
222 if (manifest != nullptr) {
223 TEST_AND_RETURN_FALSE(
224 utils::ReadFileChunk(payload_path,
225 kMaxPayloadHeaderSize,
226 GetMetadataSize() - kMaxPayloadHeaderSize,
227 &payload));
228 TEST_AND_RETURN_FALSE(GetManifest(payload, manifest));
229 }
230
Amin Hassani822d4852020-03-10 01:50:42 +0000231 if (metadata_signatures != nullptr) {
Amin Hassani79821002019-05-06 17:40:49 -0700232 payload.clear();
233 TEST_AND_RETURN_FALSE(utils::ReadFileChunk(
234 payload_path, GetMetadataSize(), GetMetadataSignatureSize(), &payload));
235 TEST_AND_RETURN_FALSE(
236 metadata_signatures->ParseFromArray(payload.data(), payload.size()));
237 }
238
239 return true;
240}
241
Sen Jiang9c89e842018-02-02 13:51:21 -0800242} // namespace chromeos_update_engine