blob: 337edb43629cf42937fbed8bdb2f6f2364404181 [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
23#include "update_engine/common/hash_calculator.h"
24#include "update_engine/common/utils.h"
25#include "update_engine/payload_consumer/payload_constants.h"
26#include "update_engine/payload_consumer/payload_verifier.h"
27
Amin Hassani79821002019-05-06 17:40:49 -070028using std::string;
29
Sen Jiang9c89e842018-02-02 13:51:21 -080030namespace chromeos_update_engine {
31
32const uint64_t PayloadMetadata::kDeltaVersionOffset = sizeof(kDeltaMagic);
33const uint64_t PayloadMetadata::kDeltaVersionSize = 8;
34const uint64_t PayloadMetadata::kDeltaManifestSizeOffset =
35 kDeltaVersionOffset + kDeltaVersionSize;
36const uint64_t PayloadMetadata::kDeltaManifestSizeSize = 8;
37const uint64_t PayloadMetadata::kDeltaMetadataSignatureSizeSize = 4;
38
39bool PayloadMetadata::GetMetadataSignatureSizeOffset(
40 uint64_t* out_offset) const {
41 if (GetMajorVersion() == kBrilloMajorPayloadVersion) {
42 *out_offset = kDeltaManifestSizeOffset + kDeltaManifestSizeSize;
43 return true;
44 }
45 return false;
46}
47
48bool PayloadMetadata::GetManifestOffset(uint64_t* out_offset) const {
49 // Actual manifest begins right after the manifest size field or
50 // metadata signature size field if major version >= 2.
51 if (major_payload_version_ == kChromeOSMajorPayloadVersion) {
52 *out_offset = kDeltaManifestSizeOffset + kDeltaManifestSizeSize;
53 return true;
54 }
55 if (major_payload_version_ == kBrilloMajorPayloadVersion) {
56 *out_offset = kDeltaManifestSizeOffset + kDeltaManifestSizeSize +
57 kDeltaMetadataSignatureSizeSize;
58 return true;
59 }
60 LOG(ERROR) << "Unknown major payload version: " << major_payload_version_;
61 return false;
62}
63
64MetadataParseResult PayloadMetadata::ParsePayloadHeader(
Sen Jiangf1236632018-05-11 16:03:23 -070065 const brillo::Blob& payload, ErrorCode* error) {
Sen Jiang9c89e842018-02-02 13:51:21 -080066 uint64_t manifest_offset;
67 // Ensure we have data to cover the major payload version.
68 if (payload.size() < kDeltaManifestSizeOffset)
69 return MetadataParseResult::kInsufficientData;
70
71 // Validate the magic string.
72 if (memcmp(payload.data(), kDeltaMagic, sizeof(kDeltaMagic)) != 0) {
73 LOG(ERROR) << "Bad payload format -- invalid delta magic.";
74 *error = ErrorCode::kDownloadInvalidMetadataMagicString;
75 return MetadataParseResult::kError;
76 }
77
78 // Extract the payload version from the metadata.
79 static_assert(sizeof(major_payload_version_) == kDeltaVersionSize,
80 "Major payload version size mismatch");
81 memcpy(&major_payload_version_,
82 &payload[kDeltaVersionOffset],
83 kDeltaVersionSize);
84 // Switch big endian to host.
85 major_payload_version_ = be64toh(major_payload_version_);
86
Sen Jiangf1236632018-05-11 16:03:23 -070087 if (major_payload_version_ < kMinSupportedMajorPayloadVersion ||
88 major_payload_version_ > kMaxSupportedMajorPayloadVersion) {
Sen Jiang9c89e842018-02-02 13:51:21 -080089 LOG(ERROR) << "Bad payload format -- unsupported payload version: "
90 << major_payload_version_;
91 *error = ErrorCode::kUnsupportedMajorPayloadVersion;
92 return MetadataParseResult::kError;
93 }
94
95 // Get the manifest offset now that we have payload version.
96 if (!GetManifestOffset(&manifest_offset)) {
97 *error = ErrorCode::kUnsupportedMajorPayloadVersion;
98 return MetadataParseResult::kError;
99 }
100 // Check again with the manifest offset.
101 if (payload.size() < manifest_offset)
102 return MetadataParseResult::kInsufficientData;
103
104 // Next, parse the manifest size.
105 static_assert(sizeof(manifest_size_) == kDeltaManifestSizeSize,
106 "manifest_size size mismatch");
107 memcpy(&manifest_size_,
108 &payload[kDeltaManifestSizeOffset],
109 kDeltaManifestSizeSize);
110 manifest_size_ = be64toh(manifest_size_); // switch big endian to host
111
Sen Jiang840a7ea2018-09-19 14:29:44 -0700112 metadata_size_ = manifest_offset + manifest_size_;
113 if (metadata_size_ < manifest_size_) {
114 // Overflow detected.
115 *error = ErrorCode::kDownloadInvalidMetadataSize;
116 return MetadataParseResult::kError;
117 }
118
Sen Jiang9c89e842018-02-02 13:51:21 -0800119 if (GetMajorVersion() == kBrilloMajorPayloadVersion) {
120 // Parse the metadata signature size.
121 static_assert(
122 sizeof(metadata_signature_size_) == kDeltaMetadataSignatureSizeSize,
123 "metadata_signature_size size mismatch");
124 uint64_t metadata_signature_size_offset;
125 if (!GetMetadataSignatureSizeOffset(&metadata_signature_size_offset)) {
126 *error = ErrorCode::kError;
127 return MetadataParseResult::kError;
128 }
129 memcpy(&metadata_signature_size_,
130 &payload[metadata_signature_size_offset],
131 kDeltaMetadataSignatureSizeSize);
132 metadata_signature_size_ = be32toh(metadata_signature_size_);
Sen Jiang840a7ea2018-09-19 14:29:44 -0700133
134 if (metadata_size_ + metadata_signature_size_ < metadata_size_) {
135 // Overflow detected.
136 *error = ErrorCode::kDownloadInvalidMetadataSize;
137 return MetadataParseResult::kError;
138 }
Sen Jiang9c89e842018-02-02 13:51:21 -0800139 }
Sen Jiang9c89e842018-02-02 13:51:21 -0800140 return MetadataParseResult::kSuccess;
141}
142
Sen Jiang44ac3ea2018-10-18 15:10:20 -0700143bool PayloadMetadata::ParsePayloadHeader(const brillo::Blob& payload) {
144 ErrorCode error;
145 return ParsePayloadHeader(payload, &error) == MetadataParseResult::kSuccess;
146}
147
Sen Jiang9c89e842018-02-02 13:51:21 -0800148bool PayloadMetadata::GetManifest(const brillo::Blob& payload,
149 DeltaArchiveManifest* out_manifest) const {
150 uint64_t manifest_offset;
151 if (!GetManifestOffset(&manifest_offset))
152 return false;
153 CHECK_GE(payload.size(), manifest_offset + manifest_size_);
154 return out_manifest->ParseFromArray(&payload[manifest_offset],
155 manifest_size_);
156}
157
158ErrorCode PayloadMetadata::ValidateMetadataSignature(
159 const brillo::Blob& payload,
Sen Jiang08c6da12019-01-07 18:28:56 -0800160 const std::string& metadata_signature,
161 const std::string& pem_public_key) const {
Sen Jiang9c89e842018-02-02 13:51:21 -0800162 if (payload.size() < metadata_size_ + metadata_signature_size_)
163 return ErrorCode::kDownloadMetadataSignatureError;
164
165 brillo::Blob metadata_signature_blob, metadata_signature_protobuf_blob;
166 if (!metadata_signature.empty()) {
167 // Convert base64-encoded signature to raw bytes.
168 if (!brillo::data_encoding::Base64Decode(metadata_signature,
169 &metadata_signature_blob)) {
170 LOG(ERROR) << "Unable to decode base64 metadata signature: "
171 << metadata_signature;
172 return ErrorCode::kDownloadMetadataSignatureError;
173 }
174 } else if (major_payload_version_ == kBrilloMajorPayloadVersion) {
175 metadata_signature_protobuf_blob.assign(
176 payload.begin() + metadata_size_,
177 payload.begin() + metadata_size_ + metadata_signature_size_);
178 }
179
180 if (metadata_signature_blob.empty() &&
181 metadata_signature_protobuf_blob.empty()) {
182 LOG(ERROR) << "Missing mandatory metadata signature in both Omaha "
183 << "response and payload.";
184 return ErrorCode::kDownloadMetadataSignatureMissingError;
185 }
186
Sen Jiang9c89e842018-02-02 13:51:21 -0800187 brillo::Blob calculated_metadata_hash;
188 if (!HashCalculator::RawHashOfBytes(
189 payload.data(), metadata_size_, &calculated_metadata_hash)) {
190 LOG(ERROR) << "Unable to compute actual hash of manifest";
191 return ErrorCode::kDownloadMetadataSignatureVerificationError;
192 }
193
194 PayloadVerifier::PadRSA2048SHA256Hash(&calculated_metadata_hash);
195 if (calculated_metadata_hash.empty()) {
196 LOG(ERROR) << "Computed actual hash of metadata is empty.";
197 return ErrorCode::kDownloadMetadataSignatureVerificationError;
198 }
199
200 if (!metadata_signature_blob.empty()) {
201 brillo::Blob expected_metadata_hash;
Sen Jiang08c6da12019-01-07 18:28:56 -0800202 if (!PayloadVerifier::GetRawHashFromSignature(
203 metadata_signature_blob, pem_public_key, &expected_metadata_hash)) {
Sen Jiang9c89e842018-02-02 13:51:21 -0800204 LOG(ERROR) << "Unable to compute expected hash from metadata signature";
205 return ErrorCode::kDownloadMetadataSignatureError;
206 }
207 if (calculated_metadata_hash != expected_metadata_hash) {
208 LOG(ERROR) << "Manifest hash verification failed. Expected hash = ";
209 utils::HexDumpVector(expected_metadata_hash);
210 LOG(ERROR) << "Calculated hash = ";
211 utils::HexDumpVector(calculated_metadata_hash);
212 return ErrorCode::kDownloadMetadataSignatureMismatch;
213 }
214 } else {
215 if (!PayloadVerifier::VerifySignature(metadata_signature_protobuf_blob,
Sen Jiang08c6da12019-01-07 18:28:56 -0800216 pem_public_key,
Sen Jiang9c89e842018-02-02 13:51:21 -0800217 calculated_metadata_hash)) {
218 LOG(ERROR) << "Manifest hash verification failed.";
219 return ErrorCode::kDownloadMetadataSignatureMismatch;
220 }
221 }
222
223 // The autoupdate_CatchBadSignatures test checks for this string in
224 // log-files. Keep in sync.
225 LOG(INFO) << "Metadata hash signature matches value in Omaha response.";
226 return ErrorCode::kSuccess;
227}
228
Amin Hassani79821002019-05-06 17:40:49 -0700229bool PayloadMetadata::ParsePayloadFile(const string& payload_path,
230 DeltaArchiveManifest* manifest,
231 Signatures* metadata_signatures) {
232 brillo::Blob payload;
233 TEST_AND_RETURN_FALSE(
234 utils::ReadFileChunk(payload_path, 0, kMaxPayloadHeaderSize, &payload));
235 TEST_AND_RETURN_FALSE(ParsePayloadHeader(payload));
236
237 if (manifest != nullptr) {
238 TEST_AND_RETURN_FALSE(
239 utils::ReadFileChunk(payload_path,
240 kMaxPayloadHeaderSize,
241 GetMetadataSize() - kMaxPayloadHeaderSize,
242 &payload));
243 TEST_AND_RETURN_FALSE(GetManifest(payload, manifest));
244 }
245
246 if (metadata_signatures != nullptr &&
247 GetMajorVersion() >= kBrilloMajorPayloadVersion) {
248 payload.clear();
249 TEST_AND_RETURN_FALSE(utils::ReadFileChunk(
250 payload_path, GetMetadataSize(), GetMetadataSignatureSize(), &payload));
251 TEST_AND_RETURN_FALSE(
252 metadata_signatures->ParseFromArray(payload.data(), payload.size()));
253 }
254
255 return true;
256}
257
Sen Jiang9c89e842018-02-02 13:51:21 -0800258} // namespace chromeos_update_engine