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/constants.h"
24 #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 
29 using std::string;
30 
31 namespace chromeos_update_engine {
32 
33 const uint64_t PayloadMetadata::kDeltaVersionOffset = sizeof(kDeltaMagic);
34 const uint64_t PayloadMetadata::kDeltaVersionSize = 8;
35 const uint64_t PayloadMetadata::kDeltaManifestSizeOffset =
36     kDeltaVersionOffset + kDeltaVersionSize;
37 const uint64_t PayloadMetadata::kDeltaManifestSizeSize = 8;
38 const uint64_t PayloadMetadata::kDeltaMetadataSignatureSizeSize = 4;
39 
GetMetadataSignatureSizeOffset() const40 uint64_t PayloadMetadata::GetMetadataSignatureSizeOffset() const {
41   return kDeltaManifestSizeOffset + kDeltaManifestSizeSize;
42 }
43 
GetManifestOffset() const44 uint64_t PayloadMetadata::GetManifestOffset() const {
45   // Actual manifest begins right after the metadata signature size field.
46   return kDeltaManifestSizeOffset + kDeltaManifestSizeSize +
47          kDeltaMetadataSignatureSizeSize;
48 }
49 
ParsePayloadHeader(const brillo::Blob & payload,ErrorCode * error)50 MetadataParseResult PayloadMetadata::ParsePayloadHeader(
51     const brillo::Blob& payload, ErrorCode* error) {
52   // 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 
63   uint64_t manifest_offset = GetManifestOffset();
64   // Check again with the manifest offset.
65   if (payload.size() < manifest_offset)
66     return MetadataParseResult::kInsufficientData;
67 
68   // 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 
77   if (major_payload_version_ < kMinSupportedMajorPayloadVersion ||
78       major_payload_version_ > kMaxSupportedMajorPayloadVersion) {
79     LOG(ERROR) << "Bad payload format -- unsupported payload version: "
80                << major_payload_version_;
81     *error = ErrorCode::kUnsupportedMajorPayloadVersion;
82     return MetadataParseResult::kError;
83   }
84 
85   // 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 
93   metadata_size_ = manifest_offset + manifest_size_;
94   if (metadata_size_ < manifest_size_) {
95     // Overflow detected.
96     LOG(ERROR) << "Overflow detected on manifest size.";
97     *error = ErrorCode::kDownloadInvalidMetadataSize;
98     return MetadataParseResult::kError;
99   }
100 
101   // 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_);
110 
111   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;
116   }
117   return MetadataParseResult::kSuccess;
118 }
119 
ParsePayloadHeader(const brillo::Blob & payload)120 bool PayloadMetadata::ParsePayloadHeader(const brillo::Blob& payload) {
121   ErrorCode error;
122   return ParsePayloadHeader(payload, &error) == MetadataParseResult::kSuccess;
123 }
124 
GetManifest(const brillo::Blob & payload,DeltaArchiveManifest * out_manifest) const125 bool PayloadMetadata::GetManifest(const brillo::Blob& payload,
126                                   DeltaArchiveManifest* out_manifest) const {
127   uint64_t manifest_offset = GetManifestOffset();
128   CHECK_GE(payload.size(), manifest_offset + manifest_size_);
129   return out_manifest->ParseFromArray(&payload[manifest_offset],
130                                       manifest_size_);
131 }
132 
ValidateMetadataSignature(const brillo::Blob & payload,const string & metadata_signature,const PayloadVerifier & payload_verifier) const133 ErrorCode PayloadMetadata::ValidateMetadataSignature(
134     const brillo::Blob& payload,
135     const string& metadata_signature,
136     const PayloadVerifier& payload_verifier) const {
137   if (payload.size() < metadata_size_ + metadata_signature_size_)
138     return ErrorCode::kDownloadMetadataSignatureError;
139 
140   // 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;
145   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     }
153   } else {
154     metadata_signature_protobuf.assign(
155         payload.begin() + metadata_size_,
156         payload.begin() + metadata_size_ + metadata_signature_size_);
157   }
158 
159   if (metadata_signature_blob.empty() && metadata_signature_protobuf.empty()) {
160     LOG(ERROR) << "Missing mandatory metadata signature in both Omaha "
161                << "response and payload.";
162     return ErrorCode::kDownloadMetadataSignatureMissingError;
163   }
164 
165   brillo::Blob metadata_hash;
166   if (!HashCalculator::RawHashOfBytes(
167           payload.data(), metadata_size_, &metadata_hash)) {
168     LOG(ERROR) << "Unable to compute actual hash of manifest";
169     return ErrorCode::kDownloadMetadataSignatureVerificationError;
170   }
171 
172   if (metadata_hash.size() != kSHA256Size) {
173     LOG(ERROR) << "Computed actual hash of metadata has incorrect size: "
174                << metadata_hash.size();
175     return ErrorCode::kDownloadMetadataSignatureVerificationError;
176   }
177 
178   if (!metadata_signature_blob.empty()) {
179     brillo::Blob decrypted_signature;
180     if (!payload_verifier.VerifyRawSignature(
181             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);
186       return ErrorCode::kDownloadMetadataSignatureMismatch;
187     }
188   } else {
189     if (!payload_verifier.VerifySignature(metadata_signature_protobuf,
190                                           metadata_hash)) {
191       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 
ParsePayloadFile(const string & payload_path,DeltaArchiveManifest * manifest,Signatures * metadata_signatures)202 bool 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));
208   TEST_AND_RETURN_FALSE(ParsePayloadHeader(payload));
209 
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 
219   if (metadata_signatures != nullptr) {
220     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 
230 }  // namespace chromeos_update_engine
231