• 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 <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(uint64_t * out_offset) const40 bool 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 
GetManifestOffset(uint64_t * out_offset) const49 bool 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 
ParsePayloadHeader(const brillo::Blob & payload,ErrorCode * error)65 MetadataParseResult PayloadMetadata::ParsePayloadHeader(
66     const brillo::Blob& payload, ErrorCode* error) {
67   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 
88   if (major_payload_version_ < kMinSupportedMajorPayloadVersion ||
89       major_payload_version_ > kMaxSupportedMajorPayloadVersion) {
90     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 
113   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 
120   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_);
134 
135     if (metadata_size_ + metadata_signature_size_ < metadata_size_) {
136       // Overflow detected.
137       *error = ErrorCode::kDownloadInvalidMetadataSize;
138       return MetadataParseResult::kError;
139     }
140   }
141   return MetadataParseResult::kSuccess;
142 }
143 
ParsePayloadHeader(const brillo::Blob & payload)144 bool PayloadMetadata::ParsePayloadHeader(const brillo::Blob& payload) {
145   ErrorCode error;
146   return ParsePayloadHeader(payload, &error) == MetadataParseResult::kSuccess;
147 }
148 
GetManifest(const brillo::Blob & payload,DeltaArchiveManifest * out_manifest) const149 bool 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 
ValidateMetadataSignature(const brillo::Blob & payload,const string & metadata_signature,const string & pem_public_key) const159 ErrorCode PayloadMetadata::ValidateMetadataSignature(
160     const brillo::Blob& payload,
161     const string& metadata_signature,
162     const string& pem_public_key) const {
163   if (payload.size() < metadata_size_ + metadata_signature_size_)
164     return ErrorCode::kDownloadMetadataSignatureError;
165 
166   // 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;
171   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) {
180     metadata_signature_protobuf.assign(
181         payload.begin() + metadata_size_,
182         payload.begin() + metadata_size_ + metadata_signature_size_);
183   }
184 
185   if (metadata_signature_blob.empty() && metadata_signature_protobuf.empty()) {
186     LOG(ERROR) << "Missing mandatory metadata signature in both Omaha "
187                << "response and payload.";
188     return ErrorCode::kDownloadMetadataSignatureMissingError;
189   }
190 
191   brillo::Blob metadata_hash;
192   if (!HashCalculator::RawHashOfBytes(
193           payload.data(), metadata_size_, &metadata_hash)) {
194     LOG(ERROR) << "Unable to compute actual hash of manifest";
195     return ErrorCode::kDownloadMetadataSignatureVerificationError;
196   }
197 
198   if (metadata_hash.size() != kSHA256Size) {
199     LOG(ERROR) << "Computed actual hash of metadata has incorrect size: "
200                << metadata_hash.size();
201     return ErrorCode::kDownloadMetadataSignatureVerificationError;
202   }
203 
204   if (!metadata_signature_blob.empty()) {
205     brillo::Blob expected_metadata_hash;
206     if (!PayloadVerifier::GetRawHashFromSignature(
207             metadata_signature_blob, pem_public_key, &expected_metadata_hash)) {
208       LOG(ERROR) << "Unable to compute expected hash from metadata signature";
209       return ErrorCode::kDownloadMetadataSignatureError;
210     }
211 
212     brillo::Blob padded_metadata_hash = metadata_hash;
213     if (!PayloadVerifier::PadRSASHA256Hash(&padded_metadata_hash,
214                                            expected_metadata_hash.size())) {
215       LOG(ERROR) << "Failed to pad the SHA256 hash to "
216                  << expected_metadata_hash.size() << " bytes.";
217       return ErrorCode::kDownloadMetadataSignatureVerificationError;
218     }
219 
220     if (padded_metadata_hash != expected_metadata_hash) {
221       LOG(ERROR) << "Manifest hash verification failed. Expected hash = ";
222       utils::HexDumpVector(expected_metadata_hash);
223       LOG(ERROR) << "Calculated hash = ";
224       utils::HexDumpVector(padded_metadata_hash);
225       return ErrorCode::kDownloadMetadataSignatureMismatch;
226     }
227   } else {
228     if (!PayloadVerifier::VerifySignature(
229             metadata_signature_protobuf, pem_public_key, metadata_hash)) {
230       LOG(ERROR) << "Manifest hash verification failed.";
231       return ErrorCode::kDownloadMetadataSignatureMismatch;
232     }
233   }
234 
235   // The autoupdate_CatchBadSignatures test checks for this string in
236   // log-files. Keep in sync.
237   LOG(INFO) << "Metadata hash signature matches value in Omaha response.";
238   return ErrorCode::kSuccess;
239 }
240 
241 }  // namespace chromeos_update_engine
242