Parse metadata signature size in payload version 2.

This patch only parse the field and skip the signature without verifying it.

Bug: 23946683
TEST=unit test added.

Change-Id: I53e049c35f8c21d325aeb415ac9a2daf980fcda1
diff --git a/delta_performer.cc b/delta_performer.cc
index c8a00d3..4025b1f 100644
--- a/delta_performer.cc
+++ b/delta_performer.cc
@@ -56,8 +56,13 @@
 
 namespace chromeos_update_engine {
 
+const uint64_t DeltaPerformer::kDeltaVersionOffset = sizeof(kDeltaMagic);
 const uint64_t DeltaPerformer::kDeltaVersionSize = 8;
+const uint64_t DeltaPerformer::kDeltaManifestSizeOffset =
+    kDeltaVersionOffset + kDeltaVersionSize;
 const uint64_t DeltaPerformer::kDeltaManifestSizeSize = 8;
+const uint64_t DeltaPerformer::kDeltaMetadataSignatureSizeSize = 4;
+const uint64_t DeltaPerformer::kMaxPayloadHeaderSize = 24;
 const uint64_t DeltaPerformer::kSupportedMajorPayloadVersion = 1;
 const uint64_t DeltaPerformer::kSupportedMinorPayloadVersion = 2;
 
@@ -327,25 +332,39 @@
 
 }  // namespace
 
-uint64_t DeltaPerformer::GetVersionOffset() {
-  // Manifest size is stored right after the magic string and the version.
-  return strlen(kDeltaMagic);
+bool DeltaPerformer::GetMetadataSignatureSizeOffset(
+    uint64_t* out_offset) const {
+  if (GetMajorVersion() == kBrilloMajorPayloadVersion) {
+    *out_offset = kDeltaManifestSizeOffset + kDeltaManifestSizeSize;
+    return true;
+  }
+  return false;
 }
 
-uint64_t DeltaPerformer::GetManifestSizeOffset() {
-  // Manifest size is stored right after the magic string and the version.
-  return strlen(kDeltaMagic) + kDeltaVersionSize;
-}
-
-uint64_t DeltaPerformer::GetManifestOffset() {
-  // Actual manifest begins right after the manifest size field.
-  return GetManifestSizeOffset() + kDeltaManifestSizeSize;
+bool DeltaPerformer::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;
 }
 
 uint64_t DeltaPerformer::GetMetadataSize() const {
   return metadata_size_;
 }
 
+uint64_t DeltaPerformer::GetMajorVersion() const {
+  return major_payload_version_;
+}
+
 uint32_t DeltaPerformer::GetMinorVersion() const {
   if (manifest_.has_minor_version()) {
     return manifest_.minor_version();
@@ -363,56 +382,82 @@
   return true;
 }
 
+bool DeltaPerformer::IsHeaderParsed() const {
+  return metadata_size_ != 0;
+}
 
 DeltaPerformer::MetadataParseResult DeltaPerformer::ParsePayloadMetadata(
     const chromeos::Blob& payload, ErrorCode* error) {
   *error = ErrorCode::kSuccess;
-  const uint64_t manifest_offset = GetManifestOffset();
-  uint64_t manifest_size = (metadata_size_ ?
-                            metadata_size_ - manifest_offset : 0);
+  uint64_t manifest_offset;
 
-  if (!manifest_size) {
-    // Ensure we have data to cover the payload header.
-    if (payload.size() < manifest_offset)
+  if (!IsHeaderParsed()) {
+    // Ensure we have data to cover the major payload version.
+    if (payload.size() < kDeltaManifestSizeOffset)
       return kMetadataParseInsufficientData;
 
     // Validate the magic string.
-    if (memcmp(payload.data(), kDeltaMagic, strlen(kDeltaMagic)) != 0) {
+    if (memcmp(payload.data(), kDeltaMagic, sizeof(kDeltaMagic)) != 0) {
       LOG(ERROR) << "Bad payload format -- invalid delta magic.";
       *error = ErrorCode::kDownloadInvalidMetadataMagicString;
       return kMetadataParseError;
     }
 
     // Extract the payload version from the metadata.
-    uint64_t major_payload_version;
-    COMPILE_ASSERT(sizeof(major_payload_version) == kDeltaVersionSize,
+    COMPILE_ASSERT(sizeof(major_payload_version_) == kDeltaVersionSize,
                    major_payload_version_size_mismatch);
-    memcpy(&major_payload_version,
-           &payload[GetVersionOffset()],
+    memcpy(&major_payload_version_,
+           &payload[kDeltaVersionOffset],
            kDeltaVersionSize);
     // switch big endian to host
-    major_payload_version = be64toh(major_payload_version);
+    major_payload_version_ = be64toh(major_payload_version_);
 
-    if (major_payload_version != kSupportedMajorPayloadVersion) {
+    if (major_payload_version_ != supported_major_version_) {
       LOG(ERROR) << "Bad payload format -- unsupported payload version: "
-          << major_payload_version;
+          << major_payload_version_;
       *error = ErrorCode::kUnsupportedMajorPayloadVersion;
       return kMetadataParseError;
     }
 
+    // Get the manifest offset now that we have payload version.
+    if (!GetManifestOffset(&manifest_offset)) {
+      *error = ErrorCode::kUnsupportedMajorPayloadVersion;
+      return kMetadataParseError;
+    }
+    // Check again with the manifest offset.
+    if (payload.size() < manifest_offset)
+      return kMetadataParseInsufficientData;
+
     // Next, parse the manifest size.
-    COMPILE_ASSERT(sizeof(manifest_size) == kDeltaManifestSizeSize,
+    COMPILE_ASSERT(sizeof(manifest_size_) == kDeltaManifestSizeSize,
                    manifest_size_size_mismatch);
-    memcpy(&manifest_size,
-           &payload[GetManifestSizeOffset()],
+    memcpy(&manifest_size_,
+           &payload[kDeltaManifestSizeOffset],
            kDeltaManifestSizeSize);
-    manifest_size = be64toh(manifest_size);  // switch big endian to host
+    manifest_size_ = be64toh(manifest_size_);  // switch big endian to host
+
+    uint32_t metadata_signature_size = 0;
+    if (GetMajorVersion() == kBrilloMajorPayloadVersion) {
+      // Parse the metadata signature size.
+      COMPILE_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 kMetadataParseError;
+      }
+      memcpy(&metadata_signature_size,
+             &payload[metadata_signature_size_offset],
+             kDeltaMetadataSignatureSizeSize);
+      metadata_signature_size = be32toh(metadata_signature_size);
+    }
 
     // If the metadata size is present in install plan, check for it immediately
     // even before waiting for that many number of bytes to be downloaded in the
     // payload. This will prevent any attack which relies on us downloading data
     // beyond the expected metadata size.
-    metadata_size_ = manifest_offset + manifest_size;
+    metadata_size_ = manifest_offset + manifest_size_ + metadata_signature_size;
     if (install_plan_->hash_checks_mandatory) {
       if (install_plan_->metadata_size != metadata_size_) {
         LOG(ERROR) << "Mandatory metadata size in Omaha response ("
@@ -460,8 +505,12 @@
     *error = ErrorCode::kSuccess;
   }
 
+  if (!GetManifestOffset(&manifest_offset)) {
+    *error = ErrorCode::kUnsupportedMajorPayloadVersion;
+    return kMetadataParseError;
+  }
   // The payload metadata is deemed valid, it's safe to parse the protobuf.
-  if (!manifest_.ParseFromArray(&payload[manifest_offset], manifest_size)) {
+  if (!manifest_.ParseFromArray(&payload[manifest_offset], manifest_size_)) {
     LOG(ERROR) << "Unable to parse manifest in update file.";
     *error = ErrorCode::kDownloadManifestParseError;
     return kMetadataParseError;
@@ -485,11 +534,11 @@
   UpdateOverallProgress(false, "Completed ");
 
   while (!manifest_valid_) {
-    // Read data up to the needed limit; this is either the payload header size,
-    // or the full metadata size (once it becomes known).
-    const bool do_read_header = !metadata_size_;
+    // Read data up to the needed limit; this is either maximium payload header
+    // size, or the full metadata size (once it becomes known).
+    const bool do_read_header = !IsHeaderParsed();
     CopyDataToBuffer(&c_bytes, &count,
-                     (do_read_header ? GetManifestOffset() :
+                     (do_read_header ? kMaxPayloadHeaderSize :
                       metadata_size_));
 
     MetadataParseResult result = ParsePayloadMetadata(buffer_, error);
@@ -497,7 +546,7 @@
       return false;
     if (result == kMetadataParseInsufficientData) {
       // If we just processed the header, make an attempt on the manifest.
-      if (do_read_header && metadata_size_)
+      if (do_read_header && IsHeaderParsed())
         continue;
 
       return true;