• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 <base/strings/stringprintf.h>
22 #include <brillo/data_encoding.h>
23 
24 #include "update_engine/common/constants.h"
25 #include "update_engine/common/hash_calculator.h"
26 #include "update_engine/common/utils.h"
27 #include "update_engine/payload_consumer/payload_constants.h"
28 #include "update_engine/payload_consumer/payload_verifier.h"
29 
30 using std::string;
31 
32 namespace chromeos_update_engine {
33 
34 const uint64_t PayloadMetadata::kDeltaVersionOffset = sizeof(kDeltaMagic);
35 const uint64_t PayloadMetadata::kDeltaVersionSize = 8;
36 const uint64_t PayloadMetadata::kDeltaManifestSizeOffset =
37     kDeltaVersionOffset + kDeltaVersionSize;
38 const uint64_t PayloadMetadata::kDeltaManifestSizeSize = 8;
39 const uint64_t PayloadMetadata::kDeltaMetadataSignatureSizeSize = 4;
40 
GetMetadataSignatureSizeOffset() const41 uint64_t PayloadMetadata::GetMetadataSignatureSizeOffset() const {
42   return kDeltaManifestSizeOffset + kDeltaManifestSizeSize;
43 }
44 
GetManifestOffset() const45 uint64_t PayloadMetadata::GetManifestOffset() const {
46   // Actual manifest begins right after the metadata signature size field.
47   return kDeltaManifestSizeOffset + kDeltaManifestSizeSize +
48          kDeltaMetadataSignatureSizeSize;
49 }
50 
ParsePayloadHeader(const brillo::Blob & payload,ErrorCode * error)51 MetadataParseResult PayloadMetadata::ParsePayloadHeader(
52     const brillo::Blob& payload, ErrorCode* error) {
53   return ParsePayloadHeader(payload.data(), payload.size(), error);
54 }
55 
ParsePayloadHeader(const unsigned char * payload,size_t size,ErrorCode * error)56 MetadataParseResult PayloadMetadata::ParsePayloadHeader(
57     const unsigned char* payload, size_t size, ErrorCode* error) {
58   // Ensure we have data to cover the major payload version.
59   if (size < kDeltaManifestSizeOffset)
60     return MetadataParseResult::kInsufficientData;
61 
62   // Validate the magic string.
63   if (memcmp(payload, kDeltaMagic, sizeof(kDeltaMagic)) != 0) {
64     LOG(ERROR) << "Bad payload format -- invalid delta magic: "
65                << base::StringPrintf("%02x%02x%02x%02x",
66                                      payload[0],
67                                      payload[1],
68                                      payload[2],
69                                      payload[3])
70                << " Expected: "
71                << base::StringPrintf("%02x%02x%02x%02x",
72                                      kDeltaMagic[0],
73                                      kDeltaMagic[1],
74                                      kDeltaMagic[2],
75                                      kDeltaMagic[3]);
76     *error = ErrorCode::kDownloadInvalidMetadataMagicString;
77     return MetadataParseResult::kError;
78   }
79 
80   uint64_t manifest_offset = GetManifestOffset();
81   // Check again with the manifest offset.
82   if (size < manifest_offset)
83     return MetadataParseResult::kInsufficientData;
84 
85   // Extract the payload version from the metadata.
86   static_assert(sizeof(major_payload_version_) == kDeltaVersionSize,
87                 "Major payload version size mismatch");
88   memcpy(&major_payload_version_,
89          &payload[kDeltaVersionOffset],
90          kDeltaVersionSize);
91   // Switch big endian to host.
92   major_payload_version_ = be64toh(major_payload_version_);
93 
94   if (major_payload_version_ < kMinSupportedMajorPayloadVersion ||
95       major_payload_version_ > kMaxSupportedMajorPayloadVersion) {
96     LOG(ERROR) << "Bad payload format -- unsupported payload version: "
97                << major_payload_version_;
98     *error = ErrorCode::kUnsupportedMajorPayloadVersion;
99     return MetadataParseResult::kError;
100   }
101 
102   // Next, parse the manifest size.
103   static_assert(sizeof(manifest_size_) == kDeltaManifestSizeSize,
104                 "manifest_size size mismatch");
105   memcpy(&manifest_size_,
106          &payload[kDeltaManifestSizeOffset],
107          kDeltaManifestSizeSize);
108   manifest_size_ = be64toh(manifest_size_);  // switch big endian to host
109 
110   metadata_size_ = manifest_offset + manifest_size_;
111   if (metadata_size_ < manifest_size_) {
112     // Overflow detected.
113     LOG(ERROR) << "Overflow detected on manifest size.";
114     *error = ErrorCode::kDownloadInvalidMetadataSize;
115     return MetadataParseResult::kError;
116   }
117 
118   // Parse the metadata signature size.
119   static_assert(
120       sizeof(metadata_signature_size_) == kDeltaMetadataSignatureSizeSize,
121       "metadata_signature_size size mismatch");
122   uint64_t metadata_signature_size_offset = GetMetadataSignatureSizeOffset();
123   memcpy(&metadata_signature_size_,
124          &payload[metadata_signature_size_offset],
125          kDeltaMetadataSignatureSizeSize);
126   metadata_signature_size_ = be32toh(metadata_signature_size_);
127 
128   if (metadata_size_ + metadata_signature_size_ < metadata_size_) {
129     // Overflow detected.
130     LOG(ERROR) << "Overflow detected on metadata and signature size.";
131     *error = ErrorCode::kDownloadInvalidMetadataSize;
132     return MetadataParseResult::kError;
133   }
134   return MetadataParseResult::kSuccess;
135 }
136 
ParsePayloadHeader(const brillo::Blob & payload)137 bool PayloadMetadata::ParsePayloadHeader(const brillo::Blob& payload) {
138   ErrorCode error;
139   return ParsePayloadHeader(payload, &error) == MetadataParseResult::kSuccess;
140 }
141 
GetManifest(const brillo::Blob & payload,DeltaArchiveManifest * out_manifest) const142 bool PayloadMetadata::GetManifest(const brillo::Blob& payload,
143                                   DeltaArchiveManifest* out_manifest) const {
144   return GetManifest(payload.data(), payload.size(), out_manifest);
145 }
146 
GetManifest(const unsigned char * payload,size_t size,DeltaArchiveManifest * out_manifest) const147 bool PayloadMetadata::GetManifest(const unsigned char* payload,
148                                   size_t size,
149                                   DeltaArchiveManifest* out_manifest) const {
150   uint64_t manifest_offset = GetManifestOffset();
151   CHECK_GE(size, manifest_offset + manifest_size_);
152   return out_manifest->ParseFromArray(&payload[manifest_offset],
153                                       manifest_size_);
154 }
155 
ValidateMetadataSignature(const brillo::Blob & payload,const string & metadata_signature,const PayloadVerifier & payload_verifier) const156 ErrorCode PayloadMetadata::ValidateMetadataSignature(
157     const brillo::Blob& payload,
158     const string& metadata_signature,
159     const PayloadVerifier& payload_verifier) const {
160   if (payload.size() < metadata_size_ + metadata_signature_size_)
161     return ErrorCode::kDownloadMetadataSignatureError;
162 
163   // A single signature in raw bytes.
164   brillo::Blob metadata_signature_blob;
165   // The serialized Signatures protobuf message stored in major version >=2
166   // payload, it may contain multiple signatures.
167   string metadata_signature_protobuf;
168   if (!metadata_signature.empty()) {
169     // Convert base64-encoded signature to raw bytes.
170     if (!brillo::data_encoding::Base64Decode(metadata_signature,
171                                              &metadata_signature_blob)) {
172       LOG(ERROR) << "Unable to decode base64 metadata signature: "
173                  << metadata_signature;
174       return ErrorCode::kDownloadMetadataSignatureError;
175     }
176   } else {
177     metadata_signature_protobuf.assign(
178         payload.begin() + metadata_size_,
179         payload.begin() + metadata_size_ + metadata_signature_size_);
180   }
181 
182   if (metadata_signature_blob.empty() && metadata_signature_protobuf.empty()) {
183     LOG(ERROR) << "Missing mandatory metadata signature in both Omaha "
184                << "response and payload.";
185     return ErrorCode::kDownloadMetadataSignatureMissingError;
186   }
187 
188   brillo::Blob metadata_hash;
189   if (!HashCalculator::RawHashOfBytes(
190           payload.data(), metadata_size_, &metadata_hash)) {
191     LOG(ERROR) << "Unable to compute actual hash of manifest";
192     return ErrorCode::kDownloadMetadataSignatureVerificationError;
193   }
194 
195   if (metadata_hash.size() != kSHA256Size) {
196     LOG(ERROR) << "Computed actual hash of metadata has incorrect size: "
197                << metadata_hash.size();
198     return ErrorCode::kDownloadMetadataSignatureVerificationError;
199   }
200 
201   if (!metadata_signature_blob.empty()) {
202     brillo::Blob decrypted_signature;
203     if (!payload_verifier.VerifyRawSignature(
204             metadata_signature_blob, metadata_hash, &decrypted_signature)) {
205       LOG(ERROR) << "Manifest hash verification failed. Decrypted hash = ";
206       utils::HexDumpVector(decrypted_signature);
207       LOG(ERROR) << "Calculated hash before padding = ";
208       utils::HexDumpVector(metadata_hash);
209       return ErrorCode::kDownloadMetadataSignatureMismatch;
210     }
211   } else {
212     if (!payload_verifier.VerifySignature(metadata_signature_protobuf,
213                                           metadata_hash)) {
214       LOG(ERROR) << "Manifest hash verification failed.";
215       return ErrorCode::kDownloadMetadataSignatureMismatch;
216     }
217   }
218 
219   // The autoupdate_CatchBadSignatures test checks for this string in
220   // log-files. Keep in sync.
221   LOG(INFO) << "Metadata hash signature matches value in Omaha response.";
222   return ErrorCode::kSuccess;
223 }
224 
ParsePayloadFile(const string & payload_path,DeltaArchiveManifest * manifest,Signatures * metadata_signatures)225 bool PayloadMetadata::ParsePayloadFile(const string& payload_path,
226                                        DeltaArchiveManifest* manifest,
227                                        Signatures* metadata_signatures) {
228   brillo::Blob payload;
229   TEST_AND_RETURN_FALSE(
230       utils::ReadFileChunk(payload_path, 0, kMaxPayloadHeaderSize, &payload));
231   TEST_AND_RETURN_FALSE(ParsePayloadHeader(payload));
232 
233   if (manifest != nullptr) {
234     TEST_AND_RETURN_FALSE(
235         utils::ReadFileChunk(payload_path,
236                              kMaxPayloadHeaderSize,
237                              GetMetadataSize() - kMaxPayloadHeaderSize,
238                              &payload));
239     TEST_AND_RETURN_FALSE(GetManifest(payload, manifest));
240   }
241 
242   if (metadata_signatures != nullptr) {
243     payload.clear();
244     TEST_AND_RETURN_FALSE(utils::ReadFileChunk(
245         payload_path, GetMetadataSize(), GetMetadataSignatureSize(), &payload));
246     TEST_AND_RETURN_FALSE(
247         metadata_signatures->ParseFromArray(payload.data(), payload.size()));
248   }
249 
250   return true;
251 }
252 
253 }  // namespace chromeos_update_engine
254