blob: 4d8ee7b4ffb9d68b7a7736a315c191f4c3d75fdf [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.
95 *error = ErrorCode::kDownloadInvalidMetadataSize;
96 return MetadataParseResult::kError;
97 }
98
Amin Hassani55c75412019-10-07 11:20:39 -070099 // Parse the metadata signature size.
100 static_assert(
101 sizeof(metadata_signature_size_) == kDeltaMetadataSignatureSizeSize,
102 "metadata_signature_size size mismatch");
103 uint64_t metadata_signature_size_offset = GetMetadataSignatureSizeOffset();
104 memcpy(&metadata_signature_size_,
105 &payload[metadata_signature_size_offset],
106 kDeltaMetadataSignatureSizeSize);
107 metadata_signature_size_ = be32toh(metadata_signature_size_);
Sen Jiang840a7ea2018-09-19 14:29:44 -0700108
Amin Hassani55c75412019-10-07 11:20:39 -0700109 if (metadata_size_ + metadata_signature_size_ < metadata_size_) {
110 // Overflow detected.
111 *error = ErrorCode::kDownloadInvalidMetadataSize;
112 return MetadataParseResult::kError;
Sen Jiang9c89e842018-02-02 13:51:21 -0800113 }
Sen Jiang9c89e842018-02-02 13:51:21 -0800114 return MetadataParseResult::kSuccess;
115}
116
Sen Jiang44ac3ea2018-10-18 15:10:20 -0700117bool PayloadMetadata::ParsePayloadHeader(const brillo::Blob& payload) {
118 ErrorCode error;
119 return ParsePayloadHeader(payload, &error) == MetadataParseResult::kSuccess;
120}
121
Sen Jiang9c89e842018-02-02 13:51:21 -0800122bool PayloadMetadata::GetManifest(const brillo::Blob& payload,
123 DeltaArchiveManifest* out_manifest) const {
Amin Hassani55c75412019-10-07 11:20:39 -0700124 uint64_t manifest_offset = GetManifestOffset();
Sen Jiang9c89e842018-02-02 13:51:21 -0800125 CHECK_GE(payload.size(), manifest_offset + manifest_size_);
126 return out_manifest->ParseFromArray(&payload[manifest_offset],
127 manifest_size_);
128}
129
130ErrorCode PayloadMetadata::ValidateMetadataSignature(
131 const brillo::Blob& payload,
Sen Jiang08c6da12019-01-07 18:28:56 -0800132 const std::string& metadata_signature,
133 const std::string& pem_public_key) const {
Sen Jiang9c89e842018-02-02 13:51:21 -0800134 if (payload.size() < metadata_size_ + metadata_signature_size_)
135 return ErrorCode::kDownloadMetadataSignatureError;
136
137 brillo::Blob metadata_signature_blob, metadata_signature_protobuf_blob;
138 if (!metadata_signature.empty()) {
139 // Convert base64-encoded signature to raw bytes.
140 if (!brillo::data_encoding::Base64Decode(metadata_signature,
141 &metadata_signature_blob)) {
142 LOG(ERROR) << "Unable to decode base64 metadata signature: "
143 << metadata_signature;
144 return ErrorCode::kDownloadMetadataSignatureError;
145 }
Amin Hassani55c75412019-10-07 11:20:39 -0700146 } else {
Sen Jiang9c89e842018-02-02 13:51:21 -0800147 metadata_signature_protobuf_blob.assign(
148 payload.begin() + metadata_size_,
149 payload.begin() + metadata_size_ + metadata_signature_size_);
150 }
151
152 if (metadata_signature_blob.empty() &&
153 metadata_signature_protobuf_blob.empty()) {
154 LOG(ERROR) << "Missing mandatory metadata signature in both Omaha "
155 << "response and payload.";
156 return ErrorCode::kDownloadMetadataSignatureMissingError;
157 }
158
Sen Jiang9c89e842018-02-02 13:51:21 -0800159 brillo::Blob calculated_metadata_hash;
160 if (!HashCalculator::RawHashOfBytes(
161 payload.data(), metadata_size_, &calculated_metadata_hash)) {
162 LOG(ERROR) << "Unable to compute actual hash of manifest";
163 return ErrorCode::kDownloadMetadataSignatureVerificationError;
164 }
165
166 PayloadVerifier::PadRSA2048SHA256Hash(&calculated_metadata_hash);
167 if (calculated_metadata_hash.empty()) {
168 LOG(ERROR) << "Computed actual hash of metadata is empty.";
169 return ErrorCode::kDownloadMetadataSignatureVerificationError;
170 }
171
172 if (!metadata_signature_blob.empty()) {
173 brillo::Blob expected_metadata_hash;
Sen Jiang08c6da12019-01-07 18:28:56 -0800174 if (!PayloadVerifier::GetRawHashFromSignature(
175 metadata_signature_blob, pem_public_key, &expected_metadata_hash)) {
Sen Jiang9c89e842018-02-02 13:51:21 -0800176 LOG(ERROR) << "Unable to compute expected hash from metadata signature";
177 return ErrorCode::kDownloadMetadataSignatureError;
178 }
179 if (calculated_metadata_hash != expected_metadata_hash) {
180 LOG(ERROR) << "Manifest hash verification failed. Expected hash = ";
181 utils::HexDumpVector(expected_metadata_hash);
182 LOG(ERROR) << "Calculated hash = ";
183 utils::HexDumpVector(calculated_metadata_hash);
184 return ErrorCode::kDownloadMetadataSignatureMismatch;
185 }
186 } else {
187 if (!PayloadVerifier::VerifySignature(metadata_signature_protobuf_blob,
Sen Jiang08c6da12019-01-07 18:28:56 -0800188 pem_public_key,
Sen Jiang9c89e842018-02-02 13:51:21 -0800189 calculated_metadata_hash)) {
190 LOG(ERROR) << "Manifest hash verification failed.";
191 return ErrorCode::kDownloadMetadataSignatureMismatch;
192 }
193 }
194
195 // The autoupdate_CatchBadSignatures test checks for this string in
196 // log-files. Keep in sync.
197 LOG(INFO) << "Metadata hash signature matches value in Omaha response.";
198 return ErrorCode::kSuccess;
199}
200
Amin Hassani79821002019-05-06 17:40:49 -0700201bool PayloadMetadata::ParsePayloadFile(const string& payload_path,
202 DeltaArchiveManifest* manifest,
203 Signatures* metadata_signatures) {
204 brillo::Blob payload;
205 TEST_AND_RETURN_FALSE(
206 utils::ReadFileChunk(payload_path, 0, kMaxPayloadHeaderSize, &payload));
207 TEST_AND_RETURN_FALSE(ParsePayloadHeader(payload));
208
209 if (manifest != nullptr) {
210 TEST_AND_RETURN_FALSE(
211 utils::ReadFileChunk(payload_path,
212 kMaxPayloadHeaderSize,
213 GetMetadataSize() - kMaxPayloadHeaderSize,
214 &payload));
215 TEST_AND_RETURN_FALSE(GetManifest(payload, manifest));
216 }
217
Amin Hassani55c75412019-10-07 11:20:39 -0700218 if (metadata_signatures != nullptr) {
Amin Hassani79821002019-05-06 17:40:49 -0700219 payload.clear();
220 TEST_AND_RETURN_FALSE(utils::ReadFileChunk(
221 payload_path, GetMetadataSize(), GetMetadataSignatureSize(), &payload));
222 TEST_AND_RETURN_FALSE(
223 metadata_signatures->ParseFromArray(payload.data(), payload.size()));
224 }
225
226 return true;
227}
228
Sen Jiang9c89e842018-02-02 13:51:21 -0800229} // namespace chromeos_update_engine