blob: 09526460545675f91fe1090b3b2b3ddcd3053887 [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
40bool PayloadMetadata::GetMetadataSignatureSizeOffset(
41 uint64_t* out_offset) const {
42 if (GetMajorVersion() == kBrilloMajorPayloadVersion) {
43 *out_offset = kDeltaManifestSizeOffset + kDeltaManifestSizeSize;
44 return true;
45 }
46 return false;
47}
48
49bool PayloadMetadata::GetManifestOffset(uint64_t* out_offset) const {
50 // Actual manifest begins right after the manifest size field or
51 // metadata signature size field if major version >= 2.
52 if (major_payload_version_ == kChromeOSMajorPayloadVersion) {
53 *out_offset = kDeltaManifestSizeOffset + kDeltaManifestSizeSize;
54 return true;
55 }
56 if (major_payload_version_ == kBrilloMajorPayloadVersion) {
57 *out_offset = kDeltaManifestSizeOffset + kDeltaManifestSizeSize +
58 kDeltaMetadataSignatureSizeSize;
59 return true;
60 }
61 LOG(ERROR) << "Unknown major payload version: " << major_payload_version_;
62 return false;
63}
64
65MetadataParseResult PayloadMetadata::ParsePayloadHeader(
Sen Jiangf1236632018-05-11 16:03:23 -070066 const brillo::Blob& payload, ErrorCode* error) {
Sen Jiang9c89e842018-02-02 13:51:21 -080067 uint64_t manifest_offset;
68 // Ensure we have data to cover the major payload version.
69 if (payload.size() < kDeltaManifestSizeOffset)
70 return MetadataParseResult::kInsufficientData;
71
72 // Validate the magic string.
73 if (memcmp(payload.data(), kDeltaMagic, sizeof(kDeltaMagic)) != 0) {
74 LOG(ERROR) << "Bad payload format -- invalid delta magic.";
75 *error = ErrorCode::kDownloadInvalidMetadataMagicString;
76 return MetadataParseResult::kError;
77 }
78
79 // Extract the payload version from the metadata.
80 static_assert(sizeof(major_payload_version_) == kDeltaVersionSize,
81 "Major payload version size mismatch");
82 memcpy(&major_payload_version_,
83 &payload[kDeltaVersionOffset],
84 kDeltaVersionSize);
85 // Switch big endian to host.
86 major_payload_version_ = be64toh(major_payload_version_);
87
Sen Jiangf1236632018-05-11 16:03:23 -070088 if (major_payload_version_ < kMinSupportedMajorPayloadVersion ||
89 major_payload_version_ > kMaxSupportedMajorPayloadVersion) {
Sen Jiang9c89e842018-02-02 13:51:21 -080090 LOG(ERROR) << "Bad payload format -- unsupported payload version: "
91 << major_payload_version_;
92 *error = ErrorCode::kUnsupportedMajorPayloadVersion;
93 return MetadataParseResult::kError;
94 }
95
96 // Get the manifest offset now that we have payload version.
97 if (!GetManifestOffset(&manifest_offset)) {
98 *error = ErrorCode::kUnsupportedMajorPayloadVersion;
99 return MetadataParseResult::kError;
100 }
101 // Check again with the manifest offset.
102 if (payload.size() < manifest_offset)
103 return MetadataParseResult::kInsufficientData;
104
105 // Next, parse the manifest size.
106 static_assert(sizeof(manifest_size_) == kDeltaManifestSizeSize,
107 "manifest_size size mismatch");
108 memcpy(&manifest_size_,
109 &payload[kDeltaManifestSizeOffset],
110 kDeltaManifestSizeSize);
111 manifest_size_ = be64toh(manifest_size_); // switch big endian to host
112
Sen Jiang840a7ea2018-09-19 14:29:44 -0700113 metadata_size_ = manifest_offset + manifest_size_;
114 if (metadata_size_ < manifest_size_) {
115 // Overflow detected.
116 *error = ErrorCode::kDownloadInvalidMetadataSize;
117 return MetadataParseResult::kError;
118 }
119
Sen Jiang9c89e842018-02-02 13:51:21 -0800120 if (GetMajorVersion() == kBrilloMajorPayloadVersion) {
121 // Parse the metadata signature size.
122 static_assert(
123 sizeof(metadata_signature_size_) == kDeltaMetadataSignatureSizeSize,
124 "metadata_signature_size size mismatch");
125 uint64_t metadata_signature_size_offset;
126 if (!GetMetadataSignatureSizeOffset(&metadata_signature_size_offset)) {
127 *error = ErrorCode::kError;
128 return MetadataParseResult::kError;
129 }
130 memcpy(&metadata_signature_size_,
131 &payload[metadata_signature_size_offset],
132 kDeltaMetadataSignatureSizeSize);
133 metadata_signature_size_ = be32toh(metadata_signature_size_);
Sen Jiang840a7ea2018-09-19 14:29:44 -0700134
135 if (metadata_size_ + metadata_signature_size_ < metadata_size_) {
136 // Overflow detected.
137 *error = ErrorCode::kDownloadInvalidMetadataSize;
138 return MetadataParseResult::kError;
139 }
Sen Jiang9c89e842018-02-02 13:51:21 -0800140 }
Sen Jiang9c89e842018-02-02 13:51:21 -0800141 return MetadataParseResult::kSuccess;
142}
143
Sen Jiang44ac3ea2018-10-18 15:10:20 -0700144bool PayloadMetadata::ParsePayloadHeader(const brillo::Blob& payload) {
145 ErrorCode error;
146 return ParsePayloadHeader(payload, &error) == MetadataParseResult::kSuccess;
147}
148
Sen Jiang9c89e842018-02-02 13:51:21 -0800149bool PayloadMetadata::GetManifest(const brillo::Blob& payload,
150 DeltaArchiveManifest* out_manifest) const {
151 uint64_t manifest_offset;
152 if (!GetManifestOffset(&manifest_offset))
153 return false;
154 CHECK_GE(payload.size(), manifest_offset + manifest_size_);
155 return out_manifest->ParseFromArray(&payload[manifest_offset],
156 manifest_size_);
157}
158
159ErrorCode PayloadMetadata::ValidateMetadataSignature(
160 const brillo::Blob& payload,
Sen Jiang9b2f1782019-01-24 14:27:50 -0800161 const string& metadata_signature,
Tianjie Xu7a78d632019-10-08 16:32:39 -0700162 const PayloadVerifier& payload_verifier) const {
Sen Jiang9c89e842018-02-02 13:51:21 -0800163 if (payload.size() < metadata_size_ + metadata_signature_size_)
164 return ErrorCode::kDownloadMetadataSignatureError;
165
Sen Jiang9b2f1782019-01-24 14:27:50 -0800166 // A single signature in raw bytes.
167 brillo::Blob metadata_signature_blob;
168 // The serialized Signatures protobuf message stored in major version >=2
169 // payload, it may contain multiple signatures.
170 string metadata_signature_protobuf;
Sen Jiang9c89e842018-02-02 13:51:21 -0800171 if (!metadata_signature.empty()) {
172 // Convert base64-encoded signature to raw bytes.
173 if (!brillo::data_encoding::Base64Decode(metadata_signature,
174 &metadata_signature_blob)) {
175 LOG(ERROR) << "Unable to decode base64 metadata signature: "
176 << metadata_signature;
177 return ErrorCode::kDownloadMetadataSignatureError;
178 }
179 } else if (major_payload_version_ == kBrilloMajorPayloadVersion) {
Sen Jiang9b2f1782019-01-24 14:27:50 -0800180 metadata_signature_protobuf.assign(
Sen Jiang9c89e842018-02-02 13:51:21 -0800181 payload.begin() + metadata_size_,
182 payload.begin() + metadata_size_ + metadata_signature_size_);
183 }
184
Sen Jiang9b2f1782019-01-24 14:27:50 -0800185 if (metadata_signature_blob.empty() && metadata_signature_protobuf.empty()) {
Sen Jiang9c89e842018-02-02 13:51:21 -0800186 LOG(ERROR) << "Missing mandatory metadata signature in both Omaha "
187 << "response and payload.";
188 return ErrorCode::kDownloadMetadataSignatureMissingError;
189 }
190
xunchangcda3c032019-03-26 15:41:14 -0700191 brillo::Blob metadata_hash;
Sen Jiang9c89e842018-02-02 13:51:21 -0800192 if (!HashCalculator::RawHashOfBytes(
xunchangcda3c032019-03-26 15:41:14 -0700193 payload.data(), metadata_size_, &metadata_hash)) {
Sen Jiang9c89e842018-02-02 13:51:21 -0800194 LOG(ERROR) << "Unable to compute actual hash of manifest";
195 return ErrorCode::kDownloadMetadataSignatureVerificationError;
196 }
197
xunchangcda3c032019-03-26 15:41:14 -0700198 if (metadata_hash.size() != kSHA256Size) {
199 LOG(ERROR) << "Computed actual hash of metadata has incorrect size: "
200 << metadata_hash.size();
Sen Jiang9c89e842018-02-02 13:51:21 -0800201 return ErrorCode::kDownloadMetadataSignatureVerificationError;
202 }
203
204 if (!metadata_signature_blob.empty()) {
Tianjie Xu6cf830b2019-09-30 11:31:49 -0700205 brillo::Blob decrypted_signature;
Tianjie Xu7a78d632019-10-08 16:32:39 -0700206 if (!payload_verifier.VerifyRawSignature(
Tianjie Xu6cf830b2019-09-30 11:31:49 -0700207 metadata_signature_blob, metadata_hash, &decrypted_signature)) {
208 LOG(ERROR) << "Manifest hash verification failed. Decrypted hash = ";
209 utils::HexDumpVector(decrypted_signature);
210 LOG(ERROR) << "Calculated hash before padding = ";
211 utils::HexDumpVector(metadata_hash);
Sen Jiang9c89e842018-02-02 13:51:21 -0800212 return ErrorCode::kDownloadMetadataSignatureMismatch;
213 }
214 } else {
Tianjie Xu7a78d632019-10-08 16:32:39 -0700215 if (!payload_verifier.VerifySignature(metadata_signature_protobuf,
216 metadata_hash)) {
Sen Jiang9c89e842018-02-02 13:51:21 -0800217 LOG(ERROR) << "Manifest hash verification failed.";
218 return ErrorCode::kDownloadMetadataSignatureMismatch;
219 }
220 }
221
222 // The autoupdate_CatchBadSignatures test checks for this string in
223 // log-files. Keep in sync.
224 LOG(INFO) << "Metadata hash signature matches value in Omaha response.";
225 return ErrorCode::kSuccess;
226}
227
228} // namespace chromeos_update_engine