// // Copyright (C) 2018 The Android Open Source Project // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // #include "update_engine/payload_consumer/payload_metadata.h" #include <endian.h> #include <brillo/data_encoding.h> #include "update_engine/common/hash_calculator.h" #include "update_engine/common/utils.h" #include "update_engine/payload_consumer/payload_constants.h" #include "update_engine/payload_consumer/payload_verifier.h" namespace chromeos_update_engine { const uint64_t PayloadMetadata::kDeltaVersionOffset = sizeof(kDeltaMagic); const uint64_t PayloadMetadata::kDeltaVersionSize = 8; const uint64_t PayloadMetadata::kDeltaManifestSizeOffset = kDeltaVersionOffset + kDeltaVersionSize; const uint64_t PayloadMetadata::kDeltaManifestSizeSize = 8; const uint64_t PayloadMetadata::kDeltaMetadataSignatureSizeSize = 4; bool PayloadMetadata::GetMetadataSignatureSizeOffset( uint64_t* out_offset) const { if (GetMajorVersion() == kBrilloMajorPayloadVersion) { *out_offset = kDeltaManifestSizeOffset + kDeltaManifestSizeSize; return true; } return false; } bool PayloadMetadata::GetManifestOffset(uint64_t* out_offset) const { // Actual manifest begins right after the manifest size field or // metadata signature size field if major version >= 2. if (major_payload_version_ == kChromeOSMajorPayloadVersion) { *out_offset = kDeltaManifestSizeOffset + kDeltaManifestSizeSize; return true; } if (major_payload_version_ == kBrilloMajorPayloadVersion) { *out_offset = kDeltaManifestSizeOffset + kDeltaManifestSizeSize + kDeltaMetadataSignatureSizeSize; return true; } LOG(ERROR) << "Unknown major payload version: " << major_payload_version_; return false; } MetadataParseResult PayloadMetadata::ParsePayloadHeader( const brillo::Blob& payload, uint64_t supported_major_version, ErrorCode* error) { uint64_t manifest_offset; // Ensure we have data to cover the major payload version. if (payload.size() < kDeltaManifestSizeOffset) return MetadataParseResult::kInsufficientData; // Validate the magic string. if (memcmp(payload.data(), kDeltaMagic, sizeof(kDeltaMagic)) != 0) { LOG(ERROR) << "Bad payload format -- invalid delta magic."; *error = ErrorCode::kDownloadInvalidMetadataMagicString; return MetadataParseResult::kError; } // Extract the payload version from the metadata. static_assert(sizeof(major_payload_version_) == kDeltaVersionSize, "Major payload version size mismatch"); memcpy(&major_payload_version_, &payload[kDeltaVersionOffset], kDeltaVersionSize); // Switch big endian to host. major_payload_version_ = be64toh(major_payload_version_); if (major_payload_version_ != supported_major_version && major_payload_version_ != kChromeOSMajorPayloadVersion) { LOG(ERROR) << "Bad payload format -- unsupported payload version: " << major_payload_version_; *error = ErrorCode::kUnsupportedMajorPayloadVersion; return MetadataParseResult::kError; } // Get the manifest offset now that we have payload version. if (!GetManifestOffset(&manifest_offset)) { *error = ErrorCode::kUnsupportedMajorPayloadVersion; return MetadataParseResult::kError; } // Check again with the manifest offset. if (payload.size() < manifest_offset) return MetadataParseResult::kInsufficientData; // Next, parse the manifest size. static_assert(sizeof(manifest_size_) == kDeltaManifestSizeSize, "manifest_size size mismatch"); memcpy(&manifest_size_, &payload[kDeltaManifestSizeOffset], kDeltaManifestSizeSize); manifest_size_ = be64toh(manifest_size_); // switch big endian to host if (GetMajorVersion() == kBrilloMajorPayloadVersion) { // Parse the metadata signature size. static_assert( sizeof(metadata_signature_size_) == kDeltaMetadataSignatureSizeSize, "metadata_signature_size size mismatch"); uint64_t metadata_signature_size_offset; if (!GetMetadataSignatureSizeOffset(&metadata_signature_size_offset)) { *error = ErrorCode::kError; return MetadataParseResult::kError; } memcpy(&metadata_signature_size_, &payload[metadata_signature_size_offset], kDeltaMetadataSignatureSizeSize); metadata_signature_size_ = be32toh(metadata_signature_size_); } metadata_size_ = manifest_offset + manifest_size_; return MetadataParseResult::kSuccess; } bool PayloadMetadata::GetManifest(const brillo::Blob& payload, DeltaArchiveManifest* out_manifest) const { uint64_t manifest_offset; if (!GetManifestOffset(&manifest_offset)) return false; CHECK_GE(payload.size(), manifest_offset + manifest_size_); return out_manifest->ParseFromArray(&payload[manifest_offset], manifest_size_); } ErrorCode PayloadMetadata::ValidateMetadataSignature( const brillo::Blob& payload, std::string metadata_signature, base::FilePath path_to_public_key) const { if (payload.size() < metadata_size_ + metadata_signature_size_) return ErrorCode::kDownloadMetadataSignatureError; brillo::Blob metadata_signature_blob, metadata_signature_protobuf_blob; if (!metadata_signature.empty()) { // Convert base64-encoded signature to raw bytes. if (!brillo::data_encoding::Base64Decode(metadata_signature, &metadata_signature_blob)) { LOG(ERROR) << "Unable to decode base64 metadata signature: " << metadata_signature; return ErrorCode::kDownloadMetadataSignatureError; } } else if (major_payload_version_ == kBrilloMajorPayloadVersion) { metadata_signature_protobuf_blob.assign( payload.begin() + metadata_size_, payload.begin() + metadata_size_ + metadata_signature_size_); } if (metadata_signature_blob.empty() && metadata_signature_protobuf_blob.empty()) { LOG(ERROR) << "Missing mandatory metadata signature in both Omaha " << "response and payload."; return ErrorCode::kDownloadMetadataSignatureMissingError; } LOG(INFO) << "Verifying metadata hash signature using public key: " << path_to_public_key.value(); brillo::Blob calculated_metadata_hash; if (!HashCalculator::RawHashOfBytes( payload.data(), metadata_size_, &calculated_metadata_hash)) { LOG(ERROR) << "Unable to compute actual hash of manifest"; return ErrorCode::kDownloadMetadataSignatureVerificationError; } PayloadVerifier::PadRSA2048SHA256Hash(&calculated_metadata_hash); if (calculated_metadata_hash.empty()) { LOG(ERROR) << "Computed actual hash of metadata is empty."; return ErrorCode::kDownloadMetadataSignatureVerificationError; } if (!metadata_signature_blob.empty()) { brillo::Blob expected_metadata_hash; if (!PayloadVerifier::GetRawHashFromSignature(metadata_signature_blob, path_to_public_key.value(), &expected_metadata_hash)) { LOG(ERROR) << "Unable to compute expected hash from metadata signature"; return ErrorCode::kDownloadMetadataSignatureError; } if (calculated_metadata_hash != expected_metadata_hash) { LOG(ERROR) << "Manifest hash verification failed. Expected hash = "; utils::HexDumpVector(expected_metadata_hash); LOG(ERROR) << "Calculated hash = "; utils::HexDumpVector(calculated_metadata_hash); return ErrorCode::kDownloadMetadataSignatureMismatch; } } else { if (!PayloadVerifier::VerifySignature(metadata_signature_protobuf_blob, path_to_public_key.value(), calculated_metadata_hash)) { LOG(ERROR) << "Manifest hash verification failed."; return ErrorCode::kDownloadMetadataSignatureMismatch; } } // The autoupdate_CatchBadSignatures test checks for this string in // log-files. Keep in sync. LOG(INFO) << "Metadata hash signature matches value in Omaha response."; return ErrorCode::kSuccess; } } // namespace chromeos_update_engine