• 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/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