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
28namespace chromeos_update_engine {
29
30const uint64_t PayloadMetadata::kDeltaVersionOffset = sizeof(kDeltaMagic);
31const uint64_t PayloadMetadata::kDeltaVersionSize = 8;
32const uint64_t PayloadMetadata::kDeltaManifestSizeOffset =
33    kDeltaVersionOffset + kDeltaVersionSize;
34const uint64_t PayloadMetadata::kDeltaManifestSizeSize = 8;
35const uint64_t PayloadMetadata::kDeltaMetadataSignatureSizeSize = 4;
36
37bool 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
46bool 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
62MetadataParseResult 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  if (GetMajorVersion() == kBrilloMajorPayloadVersion) {
113    // Parse the metadata signature size.
114    static_assert(
115        sizeof(metadata_signature_size_) == kDeltaMetadataSignatureSizeSize,
116        "metadata_signature_size size mismatch");
117    uint64_t metadata_signature_size_offset;
118    if (!GetMetadataSignatureSizeOffset(&metadata_signature_size_offset)) {
119      *error = ErrorCode::kError;
120      return MetadataParseResult::kError;
121    }
122    memcpy(&metadata_signature_size_,
123           &payload[metadata_signature_size_offset],
124           kDeltaMetadataSignatureSizeSize);
125    metadata_signature_size_ = be32toh(metadata_signature_size_);
126  }
127  metadata_size_ = manifest_offset + manifest_size_;
128  return MetadataParseResult::kSuccess;
129}
130
131bool PayloadMetadata::GetManifest(const brillo::Blob& payload,
132                                  DeltaArchiveManifest* out_manifest) const {
133  uint64_t manifest_offset;
134  if (!GetManifestOffset(&manifest_offset))
135    return false;
136  CHECK_GE(payload.size(), manifest_offset + manifest_size_);
137  return out_manifest->ParseFromArray(&payload[manifest_offset],
138                                      manifest_size_);
139}
140
141ErrorCode PayloadMetadata::ValidateMetadataSignature(
142    const brillo::Blob& payload,
143    std::string metadata_signature,
144    base::FilePath path_to_public_key) const {
145  if (payload.size() < metadata_size_ + metadata_signature_size_)
146    return ErrorCode::kDownloadMetadataSignatureError;
147
148  brillo::Blob metadata_signature_blob, metadata_signature_protobuf_blob;
149  if (!metadata_signature.empty()) {
150    // Convert base64-encoded signature to raw bytes.
151    if (!brillo::data_encoding::Base64Decode(metadata_signature,
152                                             &metadata_signature_blob)) {
153      LOG(ERROR) << "Unable to decode base64 metadata signature: "
154                 << metadata_signature;
155      return ErrorCode::kDownloadMetadataSignatureError;
156    }
157  } else if (major_payload_version_ == kBrilloMajorPayloadVersion) {
158    metadata_signature_protobuf_blob.assign(
159        payload.begin() + metadata_size_,
160        payload.begin() + metadata_size_ + metadata_signature_size_);
161  }
162
163  if (metadata_signature_blob.empty() &&
164      metadata_signature_protobuf_blob.empty()) {
165    LOG(ERROR) << "Missing mandatory metadata signature in both Omaha "
166               << "response and payload.";
167    return ErrorCode::kDownloadMetadataSignatureMissingError;
168  }
169
170  LOG(INFO) << "Verifying metadata hash signature using public key: "
171            << path_to_public_key.value();
172
173  brillo::Blob calculated_metadata_hash;
174  if (!HashCalculator::RawHashOfBytes(
175          payload.data(), metadata_size_, &calculated_metadata_hash)) {
176    LOG(ERROR) << "Unable to compute actual hash of manifest";
177    return ErrorCode::kDownloadMetadataSignatureVerificationError;
178  }
179
180  PayloadVerifier::PadRSA2048SHA256Hash(&calculated_metadata_hash);
181  if (calculated_metadata_hash.empty()) {
182    LOG(ERROR) << "Computed actual hash of metadata is empty.";
183    return ErrorCode::kDownloadMetadataSignatureVerificationError;
184  }
185
186  if (!metadata_signature_blob.empty()) {
187    brillo::Blob expected_metadata_hash;
188    if (!PayloadVerifier::GetRawHashFromSignature(metadata_signature_blob,
189                                                  path_to_public_key.value(),
190                                                  &expected_metadata_hash)) {
191      LOG(ERROR) << "Unable to compute expected hash from metadata signature";
192      return ErrorCode::kDownloadMetadataSignatureError;
193    }
194    if (calculated_metadata_hash != expected_metadata_hash) {
195      LOG(ERROR) << "Manifest hash verification failed. Expected hash = ";
196      utils::HexDumpVector(expected_metadata_hash);
197      LOG(ERROR) << "Calculated hash = ";
198      utils::HexDumpVector(calculated_metadata_hash);
199      return ErrorCode::kDownloadMetadataSignatureMismatch;
200    }
201  } else {
202    if (!PayloadVerifier::VerifySignature(metadata_signature_protobuf_blob,
203                                          path_to_public_key.value(),
204                                          calculated_metadata_hash)) {
205      LOG(ERROR) << "Manifest hash verification failed.";
206      return ErrorCode::kDownloadMetadataSignatureMismatch;
207    }
208  }
209
210  // The autoupdate_CatchBadSignatures test checks for this string in
211  // log-files. Keep in sync.
212  LOG(INFO) << "Metadata hash signature matches value in Omaha response.";
213  return ErrorCode::kSuccess;
214}
215
216}  // namespace chromeos_update_engine
217