blob: 8b3eb4e13c49c778d33dc75d61acc16860360c03 [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
Sen Jiang9b2f1782019-01-24 14:27:50 -080028using 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
39bool PayloadMetadata::GetMetadataSignatureSizeOffset(
40 uint64_t* out_offset) const {
41 if (GetMajorVersion() == kBrilloMajorPayloadVersion) {
42 *out_offset = kDeltaManifestSizeOffset + kDeltaManifestSizeSize;
43 return true;
44 }
45 return false;
46}
47
48bool PayloadMetadata::GetManifestOffset(uint64_t* out_offset) const {
49 // Actual manifest begins right after the manifest size field or
50 // metadata signature size field if major version >= 2.
51 if (major_payload_version_ == kChromeOSMajorPayloadVersion) {
52 *out_offset = kDeltaManifestSizeOffset + kDeltaManifestSizeSize;
53 return true;
54 }
55 if (major_payload_version_ == kBrilloMajorPayloadVersion) {
56 *out_offset = kDeltaManifestSizeOffset + kDeltaManifestSizeSize +
57 kDeltaMetadataSignatureSizeSize;
58 return true;
59 }
60 LOG(ERROR) << "Unknown major payload version: " << major_payload_version_;
61 return false;
62}
63
64MetadataParseResult PayloadMetadata::ParsePayloadHeader(
Sen Jiangf1236632018-05-11 16:03:23 -070065 const brillo::Blob& payload, ErrorCode* error) {
Sen Jiang9c89e842018-02-02 13:51:21 -080066 uint64_t manifest_offset;
67 // Ensure we have data to cover the major payload version.
68 if (payload.size() < kDeltaManifestSizeOffset)
69 return MetadataParseResult::kInsufficientData;
70
71 // Validate the magic string.
72 if (memcmp(payload.data(), kDeltaMagic, sizeof(kDeltaMagic)) != 0) {
73 LOG(ERROR) << "Bad payload format -- invalid delta magic.";
74 *error = ErrorCode::kDownloadInvalidMetadataMagicString;
75 return MetadataParseResult::kError;
76 }
77
78 // Extract the payload version from the metadata.
79 static_assert(sizeof(major_payload_version_) == kDeltaVersionSize,
80 "Major payload version size mismatch");
81 memcpy(&major_payload_version_,
82 &payload[kDeltaVersionOffset],
83 kDeltaVersionSize);
84 // Switch big endian to host.
85 major_payload_version_ = be64toh(major_payload_version_);
86
Sen Jiangf1236632018-05-11 16:03:23 -070087 if (major_payload_version_ < kMinSupportedMajorPayloadVersion ||
88 major_payload_version_ > kMaxSupportedMajorPayloadVersion) {
Sen Jiang9c89e842018-02-02 13:51:21 -080089 LOG(ERROR) << "Bad payload format -- unsupported payload version: "
90 << major_payload_version_;
91 *error = ErrorCode::kUnsupportedMajorPayloadVersion;
92 return MetadataParseResult::kError;
93 }
94
95 // Get the manifest offset now that we have payload version.
96 if (!GetManifestOffset(&manifest_offset)) {
97 *error = ErrorCode::kUnsupportedMajorPayloadVersion;
98 return MetadataParseResult::kError;
99 }
100 // Check again with the manifest offset.
101 if (payload.size() < manifest_offset)
102 return MetadataParseResult::kInsufficientData;
103
104 // Next, parse the manifest size.
105 static_assert(sizeof(manifest_size_) == kDeltaManifestSizeSize,
106 "manifest_size size mismatch");
107 memcpy(&manifest_size_,
108 &payload[kDeltaManifestSizeOffset],
109 kDeltaManifestSizeSize);
110 manifest_size_ = be64toh(manifest_size_); // switch big endian to host
111
Sen Jiang840a7ea2018-09-19 14:29:44 -0700112 metadata_size_ = manifest_offset + manifest_size_;
113 if (metadata_size_ < manifest_size_) {
114 // Overflow detected.
115 *error = ErrorCode::kDownloadInvalidMetadataSize;
116 return MetadataParseResult::kError;
117 }
118
Sen Jiang9c89e842018-02-02 13:51:21 -0800119 if (GetMajorVersion() == kBrilloMajorPayloadVersion) {
120 // Parse the metadata signature size.
121 static_assert(
122 sizeof(metadata_signature_size_) == kDeltaMetadataSignatureSizeSize,
123 "metadata_signature_size size mismatch");
124 uint64_t metadata_signature_size_offset;
125 if (!GetMetadataSignatureSizeOffset(&metadata_signature_size_offset)) {
126 *error = ErrorCode::kError;
127 return MetadataParseResult::kError;
128 }
129 memcpy(&metadata_signature_size_,
130 &payload[metadata_signature_size_offset],
131 kDeltaMetadataSignatureSizeSize);
132 metadata_signature_size_ = be32toh(metadata_signature_size_);
Sen Jiang840a7ea2018-09-19 14:29:44 -0700133
134 if (metadata_size_ + metadata_signature_size_ < metadata_size_) {
135 // Overflow detected.
136 *error = ErrorCode::kDownloadInvalidMetadataSize;
137 return MetadataParseResult::kError;
138 }
Sen Jiang9c89e842018-02-02 13:51:21 -0800139 }
Sen Jiang9c89e842018-02-02 13:51:21 -0800140 return MetadataParseResult::kSuccess;
141}
142
Sen Jiang44ac3ea2018-10-18 15:10:20 -0700143bool PayloadMetadata::ParsePayloadHeader(const brillo::Blob& payload) {
144 ErrorCode error;
145 return ParsePayloadHeader(payload, &error) == MetadataParseResult::kSuccess;
146}
147
Sen Jiang9c89e842018-02-02 13:51:21 -0800148bool PayloadMetadata::GetManifest(const brillo::Blob& payload,
149 DeltaArchiveManifest* out_manifest) const {
150 uint64_t manifest_offset;
151 if (!GetManifestOffset(&manifest_offset))
152 return false;
153 CHECK_GE(payload.size(), manifest_offset + manifest_size_);
154 return out_manifest->ParseFromArray(&payload[manifest_offset],
155 manifest_size_);
156}
157
158ErrorCode PayloadMetadata::ValidateMetadataSignature(
159 const brillo::Blob& payload,
Sen Jiang9b2f1782019-01-24 14:27:50 -0800160 const string& metadata_signature,
161 const string& pem_public_key) const {
Sen Jiang9c89e842018-02-02 13:51:21 -0800162 if (payload.size() < metadata_size_ + metadata_signature_size_)
163 return ErrorCode::kDownloadMetadataSignatureError;
164
Sen Jiang9b2f1782019-01-24 14:27:50 -0800165 // A single signature in raw bytes.
166 brillo::Blob metadata_signature_blob;
167 // The serialized Signatures protobuf message stored in major version >=2
168 // payload, it may contain multiple signatures.
169 string metadata_signature_protobuf;
Sen Jiang9c89e842018-02-02 13:51:21 -0800170 if (!metadata_signature.empty()) {
171 // Convert base64-encoded signature to raw bytes.
172 if (!brillo::data_encoding::Base64Decode(metadata_signature,
173 &metadata_signature_blob)) {
174 LOG(ERROR) << "Unable to decode base64 metadata signature: "
175 << metadata_signature;
176 return ErrorCode::kDownloadMetadataSignatureError;
177 }
178 } else if (major_payload_version_ == kBrilloMajorPayloadVersion) {
Sen Jiang9b2f1782019-01-24 14:27:50 -0800179 metadata_signature_protobuf.assign(
Sen Jiang9c89e842018-02-02 13:51:21 -0800180 payload.begin() + metadata_size_,
181 payload.begin() + metadata_size_ + metadata_signature_size_);
182 }
183
Sen Jiang9b2f1782019-01-24 14:27:50 -0800184 if (metadata_signature_blob.empty() && metadata_signature_protobuf.empty()) {
Sen Jiang9c89e842018-02-02 13:51:21 -0800185 LOG(ERROR) << "Missing mandatory metadata signature in both Omaha "
186 << "response and payload.";
187 return ErrorCode::kDownloadMetadataSignatureMissingError;
188 }
189
Sen Jiang9c89e842018-02-02 13:51:21 -0800190 brillo::Blob calculated_metadata_hash;
191 if (!HashCalculator::RawHashOfBytes(
192 payload.data(), metadata_size_, &calculated_metadata_hash)) {
193 LOG(ERROR) << "Unable to compute actual hash of manifest";
194 return ErrorCode::kDownloadMetadataSignatureVerificationError;
195 }
196
197 PayloadVerifier::PadRSA2048SHA256Hash(&calculated_metadata_hash);
198 if (calculated_metadata_hash.empty()) {
199 LOG(ERROR) << "Computed actual hash of metadata is empty.";
200 return ErrorCode::kDownloadMetadataSignatureVerificationError;
201 }
202
203 if (!metadata_signature_blob.empty()) {
204 brillo::Blob expected_metadata_hash;
Sen Jiang08c6da12019-01-07 18:28:56 -0800205 if (!PayloadVerifier::GetRawHashFromSignature(
206 metadata_signature_blob, pem_public_key, &expected_metadata_hash)) {
Sen Jiang9c89e842018-02-02 13:51:21 -0800207 LOG(ERROR) << "Unable to compute expected hash from metadata signature";
208 return ErrorCode::kDownloadMetadataSignatureError;
209 }
210 if (calculated_metadata_hash != expected_metadata_hash) {
211 LOG(ERROR) << "Manifest hash verification failed. Expected hash = ";
212 utils::HexDumpVector(expected_metadata_hash);
213 LOG(ERROR) << "Calculated hash = ";
214 utils::HexDumpVector(calculated_metadata_hash);
215 return ErrorCode::kDownloadMetadataSignatureMismatch;
216 }
217 } else {
Sen Jiang9b2f1782019-01-24 14:27:50 -0800218 if (!PayloadVerifier::VerifySignature(metadata_signature_protobuf,
Sen Jiang08c6da12019-01-07 18:28:56 -0800219 pem_public_key,
Sen Jiang9c89e842018-02-02 13:51:21 -0800220 calculated_metadata_hash)) {
221 LOG(ERROR) << "Manifest hash verification failed.";
222 return ErrorCode::kDownloadMetadataSignatureMismatch;
223 }
224 }
225
226 // The autoupdate_CatchBadSignatures test checks for this string in
227 // log-files. Keep in sync.
228 LOG(INFO) << "Metadata hash signature matches value in Omaha response.";
229 return ErrorCode::kSuccess;
230}
231
232} // namespace chromeos_update_engine