blob: b83001a268ef8a81f2ba4d4aac869ba9d3c9b5e8 [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
Amin Hassani55c75412019-10-07 11:20:39 -070039uint64_t PayloadMetadata::GetMetadataSignatureSizeOffset() const {
40 return kDeltaManifestSizeOffset + kDeltaManifestSizeSize;
Sen Jiang9c89e842018-02-02 13:51:21 -080041}
42
Amin Hassani55c75412019-10-07 11:20:39 -070043uint64_t PayloadMetadata::GetManifestOffset() const {
44 // Actual manifest begins right after the metadata signature size field.
45 return kDeltaManifestSizeOffset + kDeltaManifestSizeSize +
46 kDeltaMetadataSignatureSizeSize;
Sen Jiang9c89e842018-02-02 13:51:21 -080047}
48
49MetadataParseResult PayloadMetadata::ParsePayloadHeader(
Sen Jiangf1236632018-05-11 16:03:23 -070050 const brillo::Blob& payload, ErrorCode* error) {
Sen Jiang9c89e842018-02-02 13:51:21 -080051 // Ensure we have data to cover the major payload version.
52 if (payload.size() < kDeltaManifestSizeOffset)
53 return MetadataParseResult::kInsufficientData;
54
55 // Validate the magic string.
56 if (memcmp(payload.data(), kDeltaMagic, sizeof(kDeltaMagic)) != 0) {
57 LOG(ERROR) << "Bad payload format -- invalid delta magic.";
58 *error = ErrorCode::kDownloadInvalidMetadataMagicString;
59 return MetadataParseResult::kError;
60 }
61
Amin Hassani55c75412019-10-07 11:20:39 -070062 uint64_t manifest_offset = GetManifestOffset();
63 // Check again with the manifest offset.
64 if (payload.size() < manifest_offset)
65 return MetadataParseResult::kInsufficientData;
66
Sen Jiang9c89e842018-02-02 13:51:21 -080067 // Extract the payload version from the metadata.
68 static_assert(sizeof(major_payload_version_) == kDeltaVersionSize,
69 "Major payload version size mismatch");
70 memcpy(&major_payload_version_,
71 &payload[kDeltaVersionOffset],
72 kDeltaVersionSize);
73 // Switch big endian to host.
74 major_payload_version_ = be64toh(major_payload_version_);
75
Sen Jiangf1236632018-05-11 16:03:23 -070076 if (major_payload_version_ < kMinSupportedMajorPayloadVersion ||
77 major_payload_version_ > kMaxSupportedMajorPayloadVersion) {
Sen Jiang9c89e842018-02-02 13:51:21 -080078 LOG(ERROR) << "Bad payload format -- unsupported payload version: "
79 << major_payload_version_;
80 *error = ErrorCode::kUnsupportedMajorPayloadVersion;
81 return MetadataParseResult::kError;
82 }
83
Sen Jiang9c89e842018-02-02 13:51:21 -080084 // Next, parse the manifest size.
85 static_assert(sizeof(manifest_size_) == kDeltaManifestSizeSize,
86 "manifest_size size mismatch");
87 memcpy(&manifest_size_,
88 &payload[kDeltaManifestSizeOffset],
89 kDeltaManifestSizeSize);
90 manifest_size_ = be64toh(manifest_size_); // switch big endian to host
91
Sen Jiang840a7ea2018-09-19 14:29:44 -070092 metadata_size_ = manifest_offset + manifest_size_;
93 if (metadata_size_ < manifest_size_) {
94 // Overflow detected.
Andrew3a7dc262019-12-19 11:38:08 -080095 LOG(ERROR) << "Overflow detected on manifest size.";
Sen Jiang840a7ea2018-09-19 14:29:44 -070096 *error = ErrorCode::kDownloadInvalidMetadataSize;
97 return MetadataParseResult::kError;
98 }
99
Amin Hassani55c75412019-10-07 11:20:39 -0700100 // Parse the metadata signature size.
101 static_assert(
102 sizeof(metadata_signature_size_) == kDeltaMetadataSignatureSizeSize,
103 "metadata_signature_size size mismatch");
104 uint64_t metadata_signature_size_offset = GetMetadataSignatureSizeOffset();
105 memcpy(&metadata_signature_size_,
106 &payload[metadata_signature_size_offset],
107 kDeltaMetadataSignatureSizeSize);
108 metadata_signature_size_ = be32toh(metadata_signature_size_);
Sen Jiang840a7ea2018-09-19 14:29:44 -0700109
Amin Hassani55c75412019-10-07 11:20:39 -0700110 if (metadata_size_ + metadata_signature_size_ < metadata_size_) {
111 // Overflow detected.
Andrew3a7dc262019-12-19 11:38:08 -0800112 LOG(ERROR) << "Overflow detected on metadata and signature size.";
Amin Hassani55c75412019-10-07 11:20:39 -0700113 *error = ErrorCode::kDownloadInvalidMetadataSize;
114 return MetadataParseResult::kError;
Sen Jiang9c89e842018-02-02 13:51:21 -0800115 }
Sen Jiang9c89e842018-02-02 13:51:21 -0800116 return MetadataParseResult::kSuccess;
117}
118
Sen Jiang44ac3ea2018-10-18 15:10:20 -0700119bool PayloadMetadata::ParsePayloadHeader(const brillo::Blob& payload) {
120 ErrorCode error;
121 return ParsePayloadHeader(payload, &error) == MetadataParseResult::kSuccess;
122}
123
Sen Jiang9c89e842018-02-02 13:51:21 -0800124bool PayloadMetadata::GetManifest(const brillo::Blob& payload,
125 DeltaArchiveManifest* out_manifest) const {
Amin Hassani55c75412019-10-07 11:20:39 -0700126 uint64_t manifest_offset = GetManifestOffset();
Sen Jiang9c89e842018-02-02 13:51:21 -0800127 CHECK_GE(payload.size(), manifest_offset + manifest_size_);
128 return out_manifest->ParseFromArray(&payload[manifest_offset],
129 manifest_size_);
130}
131
132ErrorCode PayloadMetadata::ValidateMetadataSignature(
133 const brillo::Blob& payload,
Sen Jiang08c6da12019-01-07 18:28:56 -0800134 const std::string& metadata_signature,
135 const std::string& pem_public_key) const {
Sen Jiang9c89e842018-02-02 13:51:21 -0800136 if (payload.size() < metadata_size_ + metadata_signature_size_)
137 return ErrorCode::kDownloadMetadataSignatureError;
138
139 brillo::Blob metadata_signature_blob, metadata_signature_protobuf_blob;
140 if (!metadata_signature.empty()) {
141 // Convert base64-encoded signature to raw bytes.
142 if (!brillo::data_encoding::Base64Decode(metadata_signature,
143 &metadata_signature_blob)) {
144 LOG(ERROR) << "Unable to decode base64 metadata signature: "
145 << metadata_signature;
146 return ErrorCode::kDownloadMetadataSignatureError;
147 }
Amin Hassani55c75412019-10-07 11:20:39 -0700148 } else {
Sen Jiang9c89e842018-02-02 13:51:21 -0800149 metadata_signature_protobuf_blob.assign(
150 payload.begin() + metadata_size_,
151 payload.begin() + metadata_size_ + metadata_signature_size_);
152 }
153
154 if (metadata_signature_blob.empty() &&
155 metadata_signature_protobuf_blob.empty()) {
156 LOG(ERROR) << "Missing mandatory metadata signature in both Omaha "
157 << "response and payload.";
158 return ErrorCode::kDownloadMetadataSignatureMissingError;
159 }
160
Sen Jiang9c89e842018-02-02 13:51:21 -0800161 brillo::Blob calculated_metadata_hash;
162 if (!HashCalculator::RawHashOfBytes(
163 payload.data(), metadata_size_, &calculated_metadata_hash)) {
164 LOG(ERROR) << "Unable to compute actual hash of manifest";
165 return ErrorCode::kDownloadMetadataSignatureVerificationError;
166 }
167
168 PayloadVerifier::PadRSA2048SHA256Hash(&calculated_metadata_hash);
169 if (calculated_metadata_hash.empty()) {
170 LOG(ERROR) << "Computed actual hash of metadata is empty.";
171 return ErrorCode::kDownloadMetadataSignatureVerificationError;
172 }
173
174 if (!metadata_signature_blob.empty()) {
175 brillo::Blob expected_metadata_hash;
Sen Jiang08c6da12019-01-07 18:28:56 -0800176 if (!PayloadVerifier::GetRawHashFromSignature(
177 metadata_signature_blob, pem_public_key, &expected_metadata_hash)) {
Sen Jiang9c89e842018-02-02 13:51:21 -0800178 LOG(ERROR) << "Unable to compute expected hash from metadata signature";
179 return ErrorCode::kDownloadMetadataSignatureError;
180 }
181 if (calculated_metadata_hash != expected_metadata_hash) {
182 LOG(ERROR) << "Manifest hash verification failed. Expected hash = ";
183 utils::HexDumpVector(expected_metadata_hash);
184 LOG(ERROR) << "Calculated hash = ";
185 utils::HexDumpVector(calculated_metadata_hash);
186 return ErrorCode::kDownloadMetadataSignatureMismatch;
187 }
188 } else {
189 if (!PayloadVerifier::VerifySignature(metadata_signature_protobuf_blob,
Sen Jiang08c6da12019-01-07 18:28:56 -0800190 pem_public_key,
Sen Jiang9c89e842018-02-02 13:51:21 -0800191 calculated_metadata_hash)) {
192 LOG(ERROR) << "Manifest hash verification failed.";
193 return ErrorCode::kDownloadMetadataSignatureMismatch;
194 }
195 }
196
197 // The autoupdate_CatchBadSignatures test checks for this string in
198 // log-files. Keep in sync.
199 LOG(INFO) << "Metadata hash signature matches value in Omaha response.";
200 return ErrorCode::kSuccess;
201}
202
Amin Hassani79821002019-05-06 17:40:49 -0700203bool PayloadMetadata::ParsePayloadFile(const string& payload_path,
204 DeltaArchiveManifest* manifest,
205 Signatures* metadata_signatures) {
206 brillo::Blob payload;
207 TEST_AND_RETURN_FALSE(
208 utils::ReadFileChunk(payload_path, 0, kMaxPayloadHeaderSize, &payload));
209 TEST_AND_RETURN_FALSE(ParsePayloadHeader(payload));
210
211 if (manifest != nullptr) {
212 TEST_AND_RETURN_FALSE(
213 utils::ReadFileChunk(payload_path,
214 kMaxPayloadHeaderSize,
215 GetMetadataSize() - kMaxPayloadHeaderSize,
216 &payload));
217 TEST_AND_RETURN_FALSE(GetManifest(payload, manifest));
218 }
219
Amin Hassani55c75412019-10-07 11:20:39 -0700220 if (metadata_signatures != nullptr) {
Amin Hassani79821002019-05-06 17:40:49 -0700221 payload.clear();
222 TEST_AND_RETURN_FALSE(utils::ReadFileChunk(
223 payload_path, GetMetadataSize(), GetMetadataSignatureSize(), &payload));
224 TEST_AND_RETURN_FALSE(
225 metadata_signatures->ParseFromArray(payload.data(), payload.size()));
226 }
227
228 return true;
229}
230
Sen Jiang9c89e842018-02-02 13:51:21 -0800231} // namespace chromeos_update_engine