Home | History | Annotate | Download | only in payload_consumer
      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 
     37 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 
     46 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 
     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   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 
    131 bool 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 
    141 ErrorCode 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