blob: 69ccb4612f2794d5813a88e5726f8a79bfc07fa3 [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
21#include <brillo/data_encoding.h>
22
Amin Hassanifac20222020-01-21 11:32:50 -080023#include "update_engine/common/hardware_interface.h"
Sen Jiang9c89e842018-02-02 13:51:21 -080024#include "update_engine/common/hash_calculator.h"
25#include "update_engine/common/utils.h"
26#include "update_engine/payload_consumer/payload_constants.h"
27#include "update_engine/payload_consumer/payload_verifier.h"
28
Amin Hassani79821002019-05-06 17:40:49 -070029using std::string;
30
Sen Jiang9c89e842018-02-02 13:51:21 -080031namespace chromeos_update_engine {
32
33const uint64_t PayloadMetadata::kDeltaVersionOffset = sizeof(kDeltaMagic);
34const uint64_t PayloadMetadata::kDeltaVersionSize = 8;
35const uint64_t PayloadMetadata::kDeltaManifestSizeOffset =
36 kDeltaVersionOffset + kDeltaVersionSize;
37const uint64_t PayloadMetadata::kDeltaManifestSizeSize = 8;
38const uint64_t PayloadMetadata::kDeltaMetadataSignatureSizeSize = 4;
39
Amin Hassanifac20222020-01-21 11:32:50 -080040bool PayloadMetadata::GetMetadataSignatureSizeOffset(
41 uint64_t* out_offset) const {
42 if (GetMajorVersion() == kBrilloMajorPayloadVersion) {
43 *out_offset = kDeltaManifestSizeOffset + kDeltaManifestSizeSize;
44 return true;
45 }
46 return false;
Sen Jiang9c89e842018-02-02 13:51:21 -080047}
48
Amin Hassanifac20222020-01-21 11:32:50 -080049bool PayloadMetadata::GetManifestOffset(uint64_t* out_offset) const {
50 // Actual manifest begins right after the manifest size field or
51 // metadata signature size field if major version >= 2.
52 if (major_payload_version_ == kChromeOSMajorPayloadVersion) {
53 *out_offset = kDeltaManifestSizeOffset + kDeltaManifestSizeSize;
54 return true;
55 }
56 if (major_payload_version_ == kBrilloMajorPayloadVersion) {
57 *out_offset = kDeltaManifestSizeOffset + kDeltaManifestSizeSize +
58 kDeltaMetadataSignatureSizeSize;
59 return true;
60 }
61 LOG(ERROR) << "Unknown major payload version: " << major_payload_version_;
62 return false;
Sen Jiang9c89e842018-02-02 13:51:21 -080063}
64
65MetadataParseResult PayloadMetadata::ParsePayloadHeader(
Amin Hassanifac20222020-01-21 11:32:50 -080066 const brillo::Blob& payload,
67 HardwareInterface* hardware,
68 ErrorCode* error) {
69 uint64_t manifest_offset;
Sen Jiang9c89e842018-02-02 13:51:21 -080070 // Ensure we have data to cover the major payload version.
71 if (payload.size() < kDeltaManifestSizeOffset)
72 return MetadataParseResult::kInsufficientData;
73
74 // Validate the magic string.
75 if (memcmp(payload.data(), kDeltaMagic, sizeof(kDeltaMagic)) != 0) {
76 LOG(ERROR) << "Bad payload format -- invalid delta magic.";
77 *error = ErrorCode::kDownloadInvalidMetadataMagicString;
78 return MetadataParseResult::kError;
79 }
80
81 // Extract the payload version from the metadata.
82 static_assert(sizeof(major_payload_version_) == kDeltaVersionSize,
83 "Major payload version size mismatch");
84 memcpy(&major_payload_version_,
85 &payload[kDeltaVersionOffset],
86 kDeltaVersionSize);
87 // Switch big endian to host.
88 major_payload_version_ = be64toh(major_payload_version_);
89
Amin Hassanifac20222020-01-21 11:32:50 -080090 // We only want to test major version 1 for test images.
91 if (major_payload_version_ == kChromeOSMajorPayloadVersion
92 ? hardware != nullptr && hardware->IsOfficialBuild()
93 : major_payload_version_ < kMinSupportedMajorPayloadVersion ||
94 major_payload_version_ > kMaxSupportedMajorPayloadVersion) {
Sen Jiang9c89e842018-02-02 13:51:21 -080095 LOG(ERROR) << "Bad payload format -- unsupported payload version: "
96 << major_payload_version_;
97 *error = ErrorCode::kUnsupportedMajorPayloadVersion;
98 return MetadataParseResult::kError;
99 }
100
Amin Hassanifac20222020-01-21 11:32:50 -0800101 // Get the manifest offset now that we have payload version.
102 if (!GetManifestOffset(&manifest_offset)) {
103 *error = ErrorCode::kUnsupportedMajorPayloadVersion;
104 return MetadataParseResult::kError;
105 }
106 // Check again with the manifest offset.
107 if (payload.size() < manifest_offset)
108 return MetadataParseResult::kInsufficientData;
109
Sen Jiang9c89e842018-02-02 13:51:21 -0800110 // Next, parse the manifest size.
111 static_assert(sizeof(manifest_size_) == kDeltaManifestSizeSize,
112 "manifest_size size mismatch");
113 memcpy(&manifest_size_,
114 &payload[kDeltaManifestSizeOffset],
115 kDeltaManifestSizeSize);
116 manifest_size_ = be64toh(manifest_size_); // switch big endian to host
117
Sen Jiang840a7ea2018-09-19 14:29:44 -0700118 metadata_size_ = manifest_offset + manifest_size_;
119 if (metadata_size_ < manifest_size_) {
120 // Overflow detected.
Andrew3a7dc262019-12-19 11:38:08 -0800121 LOG(ERROR) << "Overflow detected on manifest size.";
Sen Jiang840a7ea2018-09-19 14:29:44 -0700122 *error = ErrorCode::kDownloadInvalidMetadataSize;
123 return MetadataParseResult::kError;
124 }
125
Amin Hassanifac20222020-01-21 11:32:50 -0800126 if (GetMajorVersion() == kBrilloMajorPayloadVersion) {
127 // Parse the metadata signature size.
128 static_assert(
129 sizeof(metadata_signature_size_) == kDeltaMetadataSignatureSizeSize,
130 "metadata_signature_size size mismatch");
131 uint64_t metadata_signature_size_offset;
132 if (!GetMetadataSignatureSizeOffset(&metadata_signature_size_offset)) {
133 *error = ErrorCode::kError;
134 return MetadataParseResult::kError;
135 }
136 memcpy(&metadata_signature_size_,
137 &payload[metadata_signature_size_offset],
138 kDeltaMetadataSignatureSizeSize);
139 metadata_signature_size_ = be32toh(metadata_signature_size_);
Sen Jiang840a7ea2018-09-19 14:29:44 -0700140
Amin Hassanifac20222020-01-21 11:32:50 -0800141 if (metadata_size_ + metadata_signature_size_ < metadata_size_) {
142 // Overflow detected.
143 LOG(ERROR) << "Overflow detected on metadata and signature size.";
144 *error = ErrorCode::kDownloadInvalidMetadataSize;
145 return MetadataParseResult::kError;
146 }
Sen Jiang9c89e842018-02-02 13:51:21 -0800147 }
Sen Jiang9c89e842018-02-02 13:51:21 -0800148 return MetadataParseResult::kSuccess;
149}
150
Amin Hassanifac20222020-01-21 11:32:50 -0800151bool PayloadMetadata::ParsePayloadHeader(const brillo::Blob& payload,
152 HardwareInterface* hardware) {
Sen Jiang44ac3ea2018-10-18 15:10:20 -0700153 ErrorCode error;
Amin Hassanifac20222020-01-21 11:32:50 -0800154 return ParsePayloadHeader(payload, hardware, &error) ==
155 MetadataParseResult::kSuccess;
Sen Jiang44ac3ea2018-10-18 15:10:20 -0700156}
157
Sen Jiang9c89e842018-02-02 13:51:21 -0800158bool PayloadMetadata::GetManifest(const brillo::Blob& payload,
159 DeltaArchiveManifest* out_manifest) const {
Amin Hassanifac20222020-01-21 11:32:50 -0800160 uint64_t manifest_offset;
161 if (!GetManifestOffset(&manifest_offset))
162 return false;
Sen Jiang9c89e842018-02-02 13:51:21 -0800163 CHECK_GE(payload.size(), manifest_offset + manifest_size_);
164 return out_manifest->ParseFromArray(&payload[manifest_offset],
165 manifest_size_);
166}
167
168ErrorCode PayloadMetadata::ValidateMetadataSignature(
169 const brillo::Blob& payload,
Sen Jiang08c6da12019-01-07 18:28:56 -0800170 const std::string& metadata_signature,
171 const std::string& pem_public_key) const {
Sen Jiang9c89e842018-02-02 13:51:21 -0800172 if (payload.size() < metadata_size_ + metadata_signature_size_)
173 return ErrorCode::kDownloadMetadataSignatureError;
174
175 brillo::Blob metadata_signature_blob, metadata_signature_protobuf_blob;
176 if (!metadata_signature.empty()) {
177 // Convert base64-encoded signature to raw bytes.
178 if (!brillo::data_encoding::Base64Decode(metadata_signature,
179 &metadata_signature_blob)) {
180 LOG(ERROR) << "Unable to decode base64 metadata signature: "
181 << metadata_signature;
182 return ErrorCode::kDownloadMetadataSignatureError;
183 }
Amin Hassanifac20222020-01-21 11:32:50 -0800184 } else if (major_payload_version_ == kBrilloMajorPayloadVersion) {
Sen Jiang9c89e842018-02-02 13:51:21 -0800185 metadata_signature_protobuf_blob.assign(
186 payload.begin() + metadata_size_,
187 payload.begin() + metadata_size_ + metadata_signature_size_);
188 }
189
190 if (metadata_signature_blob.empty() &&
191 metadata_signature_protobuf_blob.empty()) {
192 LOG(ERROR) << "Missing mandatory metadata signature in both Omaha "
193 << "response and payload.";
194 return ErrorCode::kDownloadMetadataSignatureMissingError;
195 }
196
Sen Jiang9c89e842018-02-02 13:51:21 -0800197 brillo::Blob calculated_metadata_hash;
198 if (!HashCalculator::RawHashOfBytes(
199 payload.data(), metadata_size_, &calculated_metadata_hash)) {
200 LOG(ERROR) << "Unable to compute actual hash of manifest";
201 return ErrorCode::kDownloadMetadataSignatureVerificationError;
202 }
203
204 PayloadVerifier::PadRSA2048SHA256Hash(&calculated_metadata_hash);
205 if (calculated_metadata_hash.empty()) {
206 LOG(ERROR) << "Computed actual hash of metadata is empty.";
207 return ErrorCode::kDownloadMetadataSignatureVerificationError;
208 }
209
210 if (!metadata_signature_blob.empty()) {
211 brillo::Blob expected_metadata_hash;
Sen Jiang08c6da12019-01-07 18:28:56 -0800212 if (!PayloadVerifier::GetRawHashFromSignature(
213 metadata_signature_blob, pem_public_key, &expected_metadata_hash)) {
Sen Jiang9c89e842018-02-02 13:51:21 -0800214 LOG(ERROR) << "Unable to compute expected hash from metadata signature";
215 return ErrorCode::kDownloadMetadataSignatureError;
216 }
217 if (calculated_metadata_hash != expected_metadata_hash) {
218 LOG(ERROR) << "Manifest hash verification failed. Expected hash = ";
219 utils::HexDumpVector(expected_metadata_hash);
220 LOG(ERROR) << "Calculated hash = ";
221 utils::HexDumpVector(calculated_metadata_hash);
222 return ErrorCode::kDownloadMetadataSignatureMismatch;
223 }
224 } else {
225 if (!PayloadVerifier::VerifySignature(metadata_signature_protobuf_blob,
Sen Jiang08c6da12019-01-07 18:28:56 -0800226 pem_public_key,
Sen Jiang9c89e842018-02-02 13:51:21 -0800227 calculated_metadata_hash)) {
228 LOG(ERROR) << "Manifest hash verification failed.";
229 return ErrorCode::kDownloadMetadataSignatureMismatch;
230 }
231 }
232
233 // The autoupdate_CatchBadSignatures test checks for this string in
234 // log-files. Keep in sync.
235 LOG(INFO) << "Metadata hash signature matches value in Omaha response.";
236 return ErrorCode::kSuccess;
237}
238
Amin Hassani79821002019-05-06 17:40:49 -0700239bool PayloadMetadata::ParsePayloadFile(const string& payload_path,
240 DeltaArchiveManifest* manifest,
241 Signatures* metadata_signatures) {
242 brillo::Blob payload;
243 TEST_AND_RETURN_FALSE(
244 utils::ReadFileChunk(payload_path, 0, kMaxPayloadHeaderSize, &payload));
Amin Hassanifac20222020-01-21 11:32:50 -0800245 TEST_AND_RETURN_FALSE(ParsePayloadHeader(payload, nullptr));
Amin Hassani79821002019-05-06 17:40:49 -0700246
247 if (manifest != nullptr) {
248 TEST_AND_RETURN_FALSE(
249 utils::ReadFileChunk(payload_path,
250 kMaxPayloadHeaderSize,
251 GetMetadataSize() - kMaxPayloadHeaderSize,
252 &payload));
253 TEST_AND_RETURN_FALSE(GetManifest(payload, manifest));
254 }
255
Amin Hassanifac20222020-01-21 11:32:50 -0800256 if (metadata_signatures != nullptr &&
257 GetMajorVersion() >= kBrilloMajorPayloadVersion) {
Amin Hassani79821002019-05-06 17:40:49 -0700258 payload.clear();
259 TEST_AND_RETURN_FALSE(utils::ReadFileChunk(
260 payload_path, GetMetadataSize(), GetMetadataSignatureSize(), &payload));
261 TEST_AND_RETURN_FALSE(
262 metadata_signatures->ParseFromArray(payload.data(), payload.size()));
263 }
264
265 return true;
266}
267
Sen Jiang9c89e842018-02-02 13:51:21 -0800268} // namespace chromeos_update_engine