blob: 01f3b62bde673072e20100e9bda14c2451bde62f [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
xunchangcda3c032019-03-26 15:41:14 -070023#include "update_engine/common/constants.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
Sen Jiang9b2f1782019-01-24 14:27:50 -080029using 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 Hassani822d4852020-03-10 01:50:42 +000040uint64_t PayloadMetadata::GetMetadataSignatureSizeOffset() const {
41 return kDeltaManifestSizeOffset + kDeltaManifestSizeSize;
Sen Jiang9c89e842018-02-02 13:51:21 -080042}
43
Amin Hassani822d4852020-03-10 01:50:42 +000044uint64_t PayloadMetadata::GetManifestOffset() const {
45 // Actual manifest begins right after the metadata signature size field.
46 return kDeltaManifestSizeOffset + kDeltaManifestSizeSize +
47 kDeltaMetadataSignatureSizeSize;
Sen Jiang9c89e842018-02-02 13:51:21 -080048}
49
50MetadataParseResult PayloadMetadata::ParsePayloadHeader(
Sen Jiangf1236632018-05-11 16:03:23 -070051 const brillo::Blob& payload, ErrorCode* error) {
Sen Jiang9c89e842018-02-02 13:51:21 -080052 // Ensure we have data to cover the major payload version.
53 if (payload.size() < kDeltaManifestSizeOffset)
54 return MetadataParseResult::kInsufficientData;
55
56 // Validate the magic string.
57 if (memcmp(payload.data(), kDeltaMagic, sizeof(kDeltaMagic)) != 0) {
58 LOG(ERROR) << "Bad payload format -- invalid delta magic.";
59 *error = ErrorCode::kDownloadInvalidMetadataMagicString;
60 return MetadataParseResult::kError;
61 }
62
Amin Hassani822d4852020-03-10 01:50:42 +000063 uint64_t manifest_offset = GetManifestOffset();
64 // Check again with the manifest offset.
65 if (payload.size() < manifest_offset)
66 return MetadataParseResult::kInsufficientData;
67
Sen Jiang9c89e842018-02-02 13:51:21 -080068 // Extract the payload version from the metadata.
69 static_assert(sizeof(major_payload_version_) == kDeltaVersionSize,
70 "Major payload version size mismatch");
71 memcpy(&major_payload_version_,
72 &payload[kDeltaVersionOffset],
73 kDeltaVersionSize);
74 // Switch big endian to host.
75 major_payload_version_ = be64toh(major_payload_version_);
76
Sen Jiangf1236632018-05-11 16:03:23 -070077 if (major_payload_version_ < kMinSupportedMajorPayloadVersion ||
78 major_payload_version_ > kMaxSupportedMajorPayloadVersion) {
Sen Jiang9c89e842018-02-02 13:51:21 -080079 LOG(ERROR) << "Bad payload format -- unsupported payload version: "
80 << major_payload_version_;
81 *error = ErrorCode::kUnsupportedMajorPayloadVersion;
82 return MetadataParseResult::kError;
83 }
84
Sen Jiang9c89e842018-02-02 13:51:21 -080085 // Next, parse the manifest size.
86 static_assert(sizeof(manifest_size_) == kDeltaManifestSizeSize,
87 "manifest_size size mismatch");
88 memcpy(&manifest_size_,
89 &payload[kDeltaManifestSizeOffset],
90 kDeltaManifestSizeSize);
91 manifest_size_ = be64toh(manifest_size_); // switch big endian to host
92
Sen Jiang840a7ea2018-09-19 14:29:44 -070093 metadata_size_ = manifest_offset + manifest_size_;
94 if (metadata_size_ < manifest_size_) {
95 // Overflow detected.
Andrew3a7dc262019-12-19 11:38:08 -080096 LOG(ERROR) << "Overflow detected on manifest size.";
Sen Jiang840a7ea2018-09-19 14:29:44 -070097 *error = ErrorCode::kDownloadInvalidMetadataSize;
98 return MetadataParseResult::kError;
99 }
100
Amin Hassani822d4852020-03-10 01:50:42 +0000101 // Parse the metadata signature size.
102 static_assert(
103 sizeof(metadata_signature_size_) == kDeltaMetadataSignatureSizeSize,
104 "metadata_signature_size size mismatch");
105 uint64_t metadata_signature_size_offset = GetMetadataSignatureSizeOffset();
106 memcpy(&metadata_signature_size_,
107 &payload[metadata_signature_size_offset],
108 kDeltaMetadataSignatureSizeSize);
109 metadata_signature_size_ = be32toh(metadata_signature_size_);
Sen Jiang840a7ea2018-09-19 14:29:44 -0700110
Amin Hassani822d4852020-03-10 01:50:42 +0000111 if (metadata_size_ + metadata_signature_size_ < metadata_size_) {
112 // Overflow detected.
113 LOG(ERROR) << "Overflow detected on metadata and signature size.";
114 *error = ErrorCode::kDownloadInvalidMetadataSize;
115 return MetadataParseResult::kError;
Sen Jiang9c89e842018-02-02 13:51:21 -0800116 }
Sen Jiang9c89e842018-02-02 13:51:21 -0800117 return MetadataParseResult::kSuccess;
118}
119
Sen Jiang44ac3ea2018-10-18 15:10:20 -0700120bool PayloadMetadata::ParsePayloadHeader(const brillo::Blob& payload) {
121 ErrorCode error;
122 return ParsePayloadHeader(payload, &error) == MetadataParseResult::kSuccess;
123}
124
Sen Jiang9c89e842018-02-02 13:51:21 -0800125bool PayloadMetadata::GetManifest(const brillo::Blob& payload,
126 DeltaArchiveManifest* out_manifest) const {
Amin Hassani822d4852020-03-10 01:50:42 +0000127 uint64_t manifest_offset = GetManifestOffset();
Sen Jiang9c89e842018-02-02 13:51:21 -0800128 CHECK_GE(payload.size(), manifest_offset + manifest_size_);
129 return out_manifest->ParseFromArray(&payload[manifest_offset],
130 manifest_size_);
131}
132
133ErrorCode PayloadMetadata::ValidateMetadataSignature(
134 const brillo::Blob& payload,
Sen Jiang9b2f1782019-01-24 14:27:50 -0800135 const string& metadata_signature,
Tianjie Xu7a78d632019-10-08 16:32:39 -0700136 const PayloadVerifier& payload_verifier) const {
Sen Jiang9c89e842018-02-02 13:51:21 -0800137 if (payload.size() < metadata_size_ + metadata_signature_size_)
138 return ErrorCode::kDownloadMetadataSignatureError;
139
Sen Jiang9b2f1782019-01-24 14:27:50 -0800140 // A single signature in raw bytes.
141 brillo::Blob metadata_signature_blob;
142 // The serialized Signatures protobuf message stored in major version >=2
143 // payload, it may contain multiple signatures.
144 string metadata_signature_protobuf;
Sen Jiang9c89e842018-02-02 13:51:21 -0800145 if (!metadata_signature.empty()) {
146 // Convert base64-encoded signature to raw bytes.
147 if (!brillo::data_encoding::Base64Decode(metadata_signature,
148 &metadata_signature_blob)) {
149 LOG(ERROR) << "Unable to decode base64 metadata signature: "
150 << metadata_signature;
151 return ErrorCode::kDownloadMetadataSignatureError;
152 }
Amin Hassani822d4852020-03-10 01:50:42 +0000153 } else {
Sen Jiang9b2f1782019-01-24 14:27:50 -0800154 metadata_signature_protobuf.assign(
Sen Jiang9c89e842018-02-02 13:51:21 -0800155 payload.begin() + metadata_size_,
156 payload.begin() + metadata_size_ + metadata_signature_size_);
157 }
158
Sen Jiang9b2f1782019-01-24 14:27:50 -0800159 if (metadata_signature_blob.empty() && metadata_signature_protobuf.empty()) {
Sen Jiang9c89e842018-02-02 13:51:21 -0800160 LOG(ERROR) << "Missing mandatory metadata signature in both Omaha "
161 << "response and payload.";
162 return ErrorCode::kDownloadMetadataSignatureMissingError;
163 }
164
xunchangcda3c032019-03-26 15:41:14 -0700165 brillo::Blob metadata_hash;
Sen Jiang9c89e842018-02-02 13:51:21 -0800166 if (!HashCalculator::RawHashOfBytes(
xunchangcda3c032019-03-26 15:41:14 -0700167 payload.data(), metadata_size_, &metadata_hash)) {
Sen Jiang9c89e842018-02-02 13:51:21 -0800168 LOG(ERROR) << "Unable to compute actual hash of manifest";
169 return ErrorCode::kDownloadMetadataSignatureVerificationError;
170 }
171
xunchangcda3c032019-03-26 15:41:14 -0700172 if (metadata_hash.size() != kSHA256Size) {
173 LOG(ERROR) << "Computed actual hash of metadata has incorrect size: "
174 << metadata_hash.size();
Sen Jiang9c89e842018-02-02 13:51:21 -0800175 return ErrorCode::kDownloadMetadataSignatureVerificationError;
176 }
177
178 if (!metadata_signature_blob.empty()) {
Tianjie Xu6cf830b2019-09-30 11:31:49 -0700179 brillo::Blob decrypted_signature;
Tianjie Xu7a78d632019-10-08 16:32:39 -0700180 if (!payload_verifier.VerifyRawSignature(
Tianjie Xu6cf830b2019-09-30 11:31:49 -0700181 metadata_signature_blob, metadata_hash, &decrypted_signature)) {
182 LOG(ERROR) << "Manifest hash verification failed. Decrypted hash = ";
183 utils::HexDumpVector(decrypted_signature);
184 LOG(ERROR) << "Calculated hash before padding = ";
185 utils::HexDumpVector(metadata_hash);
Sen Jiang9c89e842018-02-02 13:51:21 -0800186 return ErrorCode::kDownloadMetadataSignatureMismatch;
187 }
188 } else {
Tianjie Xu7a78d632019-10-08 16:32:39 -0700189 if (!payload_verifier.VerifySignature(metadata_signature_protobuf,
190 metadata_hash)) {
Sen Jiang9c89e842018-02-02 13:51:21 -0800191 LOG(ERROR) << "Manifest hash verification failed.";
192 return ErrorCode::kDownloadMetadataSignatureMismatch;
193 }
194 }
195
196 // The autoupdate_CatchBadSignatures test checks for this string in
197 // log-files. Keep in sync.
198 LOG(INFO) << "Metadata hash signature matches value in Omaha response.";
199 return ErrorCode::kSuccess;
200}
201
Amin Hassani79821002019-05-06 17:40:49 -0700202bool PayloadMetadata::ParsePayloadFile(const string& payload_path,
203 DeltaArchiveManifest* manifest,
204 Signatures* metadata_signatures) {
205 brillo::Blob payload;
206 TEST_AND_RETURN_FALSE(
207 utils::ReadFileChunk(payload_path, 0, kMaxPayloadHeaderSize, &payload));
Amin Hassani822d4852020-03-10 01:50:42 +0000208 TEST_AND_RETURN_FALSE(ParsePayloadHeader(payload));
Amin Hassani79821002019-05-06 17:40:49 -0700209
210 if (manifest != nullptr) {
211 TEST_AND_RETURN_FALSE(
212 utils::ReadFileChunk(payload_path,
213 kMaxPayloadHeaderSize,
214 GetMetadataSize() - kMaxPayloadHeaderSize,
215 &payload));
216 TEST_AND_RETURN_FALSE(GetManifest(payload, manifest));
217 }
218
Amin Hassani822d4852020-03-10 01:50:42 +0000219 if (metadata_signatures != nullptr) {
Amin Hassani79821002019-05-06 17:40:49 -0700220 payload.clear();
221 TEST_AND_RETURN_FALSE(utils::ReadFileChunk(
222 payload_path, GetMetadataSize(), GetMetadataSignatureSize(), &payload));
223 TEST_AND_RETURN_FALSE(
224 metadata_signatures->ParseFromArray(payload.data(), payload.size()));
225 }
226
227 return true;
228}
229
Sen Jiang9c89e842018-02-02 13:51:21 -0800230} // namespace chromeos_update_engine