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