blob: b631c87c19428c28c994f59126a92c286901f5fc [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
28namespace chromeos_update_engine {
29
30const uint64_t PayloadMetadata::kDeltaVersionOffset = sizeof(kDeltaMagic);
31const uint64_t PayloadMetadata::kDeltaVersionSize = 8;
32const uint64_t PayloadMetadata::kDeltaManifestSizeOffset =
33 kDeltaVersionOffset + kDeltaVersionSize;
34const uint64_t PayloadMetadata::kDeltaManifestSizeSize = 8;
35const uint64_t PayloadMetadata::kDeltaMetadataSignatureSizeSize = 4;
36
37bool PayloadMetadata::GetMetadataSignatureSizeOffset(
38 uint64_t* out_offset) const {
39 if (GetMajorVersion() == kBrilloMajorPayloadVersion) {
40 *out_offset = kDeltaManifestSizeOffset + kDeltaManifestSizeSize;
41 return true;
42 }
43 return false;
44}
45
46bool PayloadMetadata::GetManifestOffset(uint64_t* out_offset) const {
47 // Actual manifest begins right after the manifest size field or
48 // metadata signature size field if major version >= 2.
49 if (major_payload_version_ == kChromeOSMajorPayloadVersion) {
50 *out_offset = kDeltaManifestSizeOffset + kDeltaManifestSizeSize;
51 return true;
52 }
53 if (major_payload_version_ == kBrilloMajorPayloadVersion) {
54 *out_offset = kDeltaManifestSizeOffset + kDeltaManifestSizeSize +
55 kDeltaMetadataSignatureSizeSize;
56 return true;
57 }
58 LOG(ERROR) << "Unknown major payload version: " << major_payload_version_;
59 return false;
60}
61
62MetadataParseResult PayloadMetadata::ParsePayloadHeader(
Sen Jiangf1236632018-05-11 16:03:23 -070063 const brillo::Blob& payload, ErrorCode* error) {
Sen Jiang9c89e842018-02-02 13:51:21 -080064 uint64_t manifest_offset;
65 // Ensure we have data to cover the major payload version.
66 if (payload.size() < kDeltaManifestSizeOffset)
67 return MetadataParseResult::kInsufficientData;
68
69 // Validate the magic string.
70 if (memcmp(payload.data(), kDeltaMagic, sizeof(kDeltaMagic)) != 0) {
71 LOG(ERROR) << "Bad payload format -- invalid delta magic.";
72 *error = ErrorCode::kDownloadInvalidMetadataMagicString;
73 return MetadataParseResult::kError;
74 }
75
76 // Extract the payload version from the metadata.
77 static_assert(sizeof(major_payload_version_) == kDeltaVersionSize,
78 "Major payload version size mismatch");
79 memcpy(&major_payload_version_,
80 &payload[kDeltaVersionOffset],
81 kDeltaVersionSize);
82 // Switch big endian to host.
83 major_payload_version_ = be64toh(major_payload_version_);
84
Sen Jiangf1236632018-05-11 16:03:23 -070085 if (major_payload_version_ < kMinSupportedMajorPayloadVersion ||
86 major_payload_version_ > kMaxSupportedMajorPayloadVersion) {
Sen Jiang9c89e842018-02-02 13:51:21 -080087 LOG(ERROR) << "Bad payload format -- unsupported payload version: "
88 << major_payload_version_;
89 *error = ErrorCode::kUnsupportedMajorPayloadVersion;
90 return MetadataParseResult::kError;
91 }
92
93 // Get the manifest offset now that we have payload version.
94 if (!GetManifestOffset(&manifest_offset)) {
95 *error = ErrorCode::kUnsupportedMajorPayloadVersion;
96 return MetadataParseResult::kError;
97 }
98 // Check again with the manifest offset.
99 if (payload.size() < manifest_offset)
100 return MetadataParseResult::kInsufficientData;
101
102 // Next, parse the manifest size.
103 static_assert(sizeof(manifest_size_) == kDeltaManifestSizeSize,
104 "manifest_size size mismatch");
105 memcpy(&manifest_size_,
106 &payload[kDeltaManifestSizeOffset],
107 kDeltaManifestSizeSize);
108 manifest_size_ = be64toh(manifest_size_); // switch big endian to host
109
Sen Jiang840a7ea2018-09-19 14:29:44 -0700110 metadata_size_ = manifest_offset + manifest_size_;
111 if (metadata_size_ < manifest_size_) {
112 // Overflow detected.
113 *error = ErrorCode::kDownloadInvalidMetadataSize;
114 return MetadataParseResult::kError;
115 }
116
Sen Jiang9c89e842018-02-02 13:51:21 -0800117 if (GetMajorVersion() == kBrilloMajorPayloadVersion) {
118 // Parse the metadata signature size.
119 static_assert(
120 sizeof(metadata_signature_size_) == kDeltaMetadataSignatureSizeSize,
121 "metadata_signature_size size mismatch");
122 uint64_t metadata_signature_size_offset;
123 if (!GetMetadataSignatureSizeOffset(&metadata_signature_size_offset)) {
124 *error = ErrorCode::kError;
125 return MetadataParseResult::kError;
126 }
127 memcpy(&metadata_signature_size_,
128 &payload[metadata_signature_size_offset],
129 kDeltaMetadataSignatureSizeSize);
130 metadata_signature_size_ = be32toh(metadata_signature_size_);
Sen Jiang840a7ea2018-09-19 14:29:44 -0700131
132 if (metadata_size_ + metadata_signature_size_ < metadata_size_) {
133 // Overflow detected.
134 *error = ErrorCode::kDownloadInvalidMetadataSize;
135 return MetadataParseResult::kError;
136 }
Sen Jiang9c89e842018-02-02 13:51:21 -0800137 }
Sen Jiang9c89e842018-02-02 13:51:21 -0800138 return MetadataParseResult::kSuccess;
139}
140
Sen Jiang44ac3ea2018-10-18 15:10:20 -0700141bool PayloadMetadata::ParsePayloadHeader(const brillo::Blob& payload) {
142 ErrorCode error;
143 return ParsePayloadHeader(payload, &error) == MetadataParseResult::kSuccess;
144}
145
Sen Jiang9c89e842018-02-02 13:51:21 -0800146bool PayloadMetadata::GetManifest(const brillo::Blob& payload,
147 DeltaArchiveManifest* out_manifest) const {
148 uint64_t manifest_offset;
149 if (!GetManifestOffset(&manifest_offset))
150 return false;
151 CHECK_GE(payload.size(), manifest_offset + manifest_size_);
152 return out_manifest->ParseFromArray(&payload[manifest_offset],
153 manifest_size_);
154}
155
156ErrorCode PayloadMetadata::ValidateMetadataSignature(
157 const brillo::Blob& payload,
Sen Jiang08c6da12019-01-07 18:28:56 -0800158 const std::string& metadata_signature,
159 const std::string& pem_public_key) const {
Sen Jiang9c89e842018-02-02 13:51:21 -0800160 if (payload.size() < metadata_size_ + metadata_signature_size_)
161 return ErrorCode::kDownloadMetadataSignatureError;
162
163 brillo::Blob metadata_signature_blob, metadata_signature_protobuf_blob;
164 if (!metadata_signature.empty()) {
165 // Convert base64-encoded signature to raw bytes.
166 if (!brillo::data_encoding::Base64Decode(metadata_signature,
167 &metadata_signature_blob)) {
168 LOG(ERROR) << "Unable to decode base64 metadata signature: "
169 << metadata_signature;
170 return ErrorCode::kDownloadMetadataSignatureError;
171 }
172 } else if (major_payload_version_ == kBrilloMajorPayloadVersion) {
173 metadata_signature_protobuf_blob.assign(
174 payload.begin() + metadata_size_,
175 payload.begin() + metadata_size_ + metadata_signature_size_);
176 }
177
178 if (metadata_signature_blob.empty() &&
179 metadata_signature_protobuf_blob.empty()) {
180 LOG(ERROR) << "Missing mandatory metadata signature in both Omaha "
181 << "response and payload.";
182 return ErrorCode::kDownloadMetadataSignatureMissingError;
183 }
184
Sen Jiang9c89e842018-02-02 13:51:21 -0800185 brillo::Blob calculated_metadata_hash;
186 if (!HashCalculator::RawHashOfBytes(
187 payload.data(), metadata_size_, &calculated_metadata_hash)) {
188 LOG(ERROR) << "Unable to compute actual hash of manifest";
189 return ErrorCode::kDownloadMetadataSignatureVerificationError;
190 }
191
192 PayloadVerifier::PadRSA2048SHA256Hash(&calculated_metadata_hash);
193 if (calculated_metadata_hash.empty()) {
194 LOG(ERROR) << "Computed actual hash of metadata is empty.";
195 return ErrorCode::kDownloadMetadataSignatureVerificationError;
196 }
197
198 if (!metadata_signature_blob.empty()) {
199 brillo::Blob expected_metadata_hash;
Sen Jiang08c6da12019-01-07 18:28:56 -0800200 if (!PayloadVerifier::GetRawHashFromSignature(
201 metadata_signature_blob, pem_public_key, &expected_metadata_hash)) {
Sen Jiang9c89e842018-02-02 13:51:21 -0800202 LOG(ERROR) << "Unable to compute expected hash from metadata signature";
203 return ErrorCode::kDownloadMetadataSignatureError;
204 }
205 if (calculated_metadata_hash != expected_metadata_hash) {
206 LOG(ERROR) << "Manifest hash verification failed. Expected hash = ";
207 utils::HexDumpVector(expected_metadata_hash);
208 LOG(ERROR) << "Calculated hash = ";
209 utils::HexDumpVector(calculated_metadata_hash);
210 return ErrorCode::kDownloadMetadataSignatureMismatch;
211 }
212 } else {
213 if (!PayloadVerifier::VerifySignature(metadata_signature_protobuf_blob,
Sen Jiang08c6da12019-01-07 18:28:56 -0800214 pem_public_key,
Sen Jiang9c89e842018-02-02 13:51:21 -0800215 calculated_metadata_hash)) {
216 LOG(ERROR) << "Manifest hash verification failed.";
217 return ErrorCode::kDownloadMetadataSignatureMismatch;
218 }
219 }
220
221 // The autoupdate_CatchBadSignatures test checks for this string in
222 // log-files. Keep in sync.
223 LOG(INFO) << "Metadata hash signature matches value in Omaha response.";
224 return ErrorCode::kSuccess;
225}
226
227} // namespace chromeos_update_engine