1 //
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
28 namespace chromeos_update_engine {
29
30 const uint64_t PayloadMetadata::kDeltaVersionOffset = sizeof(kDeltaMagic);
31 const uint64_t PayloadMetadata::kDeltaVersionSize = 8;
32 const uint64_t PayloadMetadata::kDeltaManifestSizeOffset =
33 kDeltaVersionOffset + kDeltaVersionSize;
34 const uint64_t PayloadMetadata::kDeltaManifestSizeSize = 8;
35 const uint64_t PayloadMetadata::kDeltaMetadataSignatureSizeSize = 4;
36
GetMetadataSignatureSizeOffset(uint64_t * out_offset) const37 bool 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
GetManifestOffset(uint64_t * out_offset) const46 bool 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
ParsePayloadHeader(const brillo::Blob & payload,uint64_t supported_major_version,ErrorCode * error)62 MetadataParseResult PayloadMetadata::ParsePayloadHeader(
63 const brillo::Blob& payload,
64 uint64_t supported_major_version,
65 ErrorCode* error) {
66 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
87 if (major_payload_version_ != supported_major_version &&
88 major_payload_version_ != kChromeOSMajorPayloadVersion) {
89 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
112 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
119 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_);
133
134 if (metadata_size_ + metadata_signature_size_ < metadata_size_) {
135 // Overflow detected.
136 *error = ErrorCode::kDownloadInvalidMetadataSize;
137 return MetadataParseResult::kError;
138 }
139 }
140 return MetadataParseResult::kSuccess;
141 }
142
GetManifest(const brillo::Blob & payload,DeltaArchiveManifest * out_manifest) const143 bool PayloadMetadata::GetManifest(const brillo::Blob& payload,
144 DeltaArchiveManifest* out_manifest) const {
145 uint64_t manifest_offset;
146 if (!GetManifestOffset(&manifest_offset))
147 return false;
148 CHECK_GE(payload.size(), manifest_offset + manifest_size_);
149 return out_manifest->ParseFromArray(&payload[manifest_offset],
150 manifest_size_);
151 }
152
ValidateMetadataSignature(const brillo::Blob & payload,std::string metadata_signature,base::FilePath path_to_public_key) const153 ErrorCode PayloadMetadata::ValidateMetadataSignature(
154 const brillo::Blob& payload,
155 std::string metadata_signature,
156 base::FilePath path_to_public_key) const {
157 if (payload.size() < metadata_size_ + metadata_signature_size_)
158 return ErrorCode::kDownloadMetadataSignatureError;
159
160 brillo::Blob metadata_signature_blob, metadata_signature_protobuf_blob;
161 if (!metadata_signature.empty()) {
162 // Convert base64-encoded signature to raw bytes.
163 if (!brillo::data_encoding::Base64Decode(metadata_signature,
164 &metadata_signature_blob)) {
165 LOG(ERROR) << "Unable to decode base64 metadata signature: "
166 << metadata_signature;
167 return ErrorCode::kDownloadMetadataSignatureError;
168 }
169 } else if (major_payload_version_ == kBrilloMajorPayloadVersion) {
170 metadata_signature_protobuf_blob.assign(
171 payload.begin() + metadata_size_,
172 payload.begin() + metadata_size_ + metadata_signature_size_);
173 }
174
175 if (metadata_signature_blob.empty() &&
176 metadata_signature_protobuf_blob.empty()) {
177 LOG(ERROR) << "Missing mandatory metadata signature in both Omaha "
178 << "response and payload.";
179 return ErrorCode::kDownloadMetadataSignatureMissingError;
180 }
181
182 LOG(INFO) << "Verifying metadata hash signature using public key: "
183 << path_to_public_key.value();
184
185 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;
200 if (!PayloadVerifier::GetRawHashFromSignature(metadata_signature_blob,
201 path_to_public_key.value(),
202 &expected_metadata_hash)) {
203 LOG(ERROR) << "Unable to compute expected hash from metadata signature";
204 return ErrorCode::kDownloadMetadataSignatureError;
205 }
206 if (calculated_metadata_hash != expected_metadata_hash) {
207 LOG(ERROR) << "Manifest hash verification failed. Expected hash = ";
208 utils::HexDumpVector(expected_metadata_hash);
209 LOG(ERROR) << "Calculated hash = ";
210 utils::HexDumpVector(calculated_metadata_hash);
211 return ErrorCode::kDownloadMetadataSignatureMismatch;
212 }
213 } else {
214 if (!PayloadVerifier::VerifySignature(metadata_signature_protobuf_blob,
215 path_to_public_key.value(),
216 calculated_metadata_hash)) {
217 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
229