Commit a81714f8 authored by Adam Langley's avatar Adam Langley Committed by Commit Bot

certificate_tag: flesh out.

With this change, the code should function. Only the superfluous
certificate mode of operation is supported.

Change-Id: I5e280e19c70169a0934f0815874cca68d2e61979
BUG: 1031734
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2013626Reviewed-by: default avatarSorin Jianu <sorin@chromium.org>
Commit-Queue: Sorin Jianu <sorin@chromium.org>
Auto-Submit: Adam Langley <agl@chromium.org>
Cr-Commit-Position: refs/heads/master@{#738803}
parent e4b16bb5
......@@ -11,4 +11,5 @@ include_rules = [
"+courgette",
"+third_party/crashpad",
"+third_party/zlib/google",
"+third_party/boringssl/src/include",
]
......@@ -40,5 +40,8 @@ source_set("unittest") {
"//base",
"//base/test:test_support",
"//testing/gtest",
"//third_party/zlib/google:compression_utils",
]
data = [ "../test/data/signed.exe.gz" ]
}
This diff is collapsed.
......@@ -5,68 +5,54 @@
#ifndef CHROME_UPDATER_TOOLS_CERTIFICATE_TAG_H_
#define CHROME_UPDATER_TOOLS_CERTIFICATE_TAG_H_
#include <cstdint>
#include <memory>
#include <tuple>
#include <vector>
namespace base {
class Time;
}
#include "base/containers/span.h"
#include "base/optional.h"
namespace updater {
namespace tools {
struct SignedData;
// Binary represents a PE binary.
struct Binary {
Binary();
// Binary represents a Windows PE binary and provides functions to extract and
// set data outside of the signed area (called a "tag"). This allows a binary to
// contain arbitrary data without invalidating any Authenticode signature.
class Binary {
public:
Binary(const Binary&);
~Binary();
Binary(const Binary&) = delete;
Binary& operator=(const Binary&) = delete;
// The full file.
std::vector<uint8_t> contents;
// Parse a signed, Windows PE binary. Note that the returned structure
// contains pointers into the given data.
static base::Optional<Binary> Parse(base::span<const uint8_t> binary);
// The offset to the attribute certificates table.
size_t attr_cert_offset = 0;
// tag returns the embedded tag, if any.
const base::Optional<base::span<const uint8_t>>& tag() const;
// The offset to the size of the attribute certificates table.
size_t cert_size_offset = 0;
// SetTag returns an updated version of the binary that contains the given
// tag, or |nullopt| on error. If the binary already contains a tag then it
// will be replaced.
base::Optional<std::vector<uint8_t>> SetTag(
base::span<const uint8_t> tag) const;
// The PKCS#7, SignedData in DER form.
std::vector<uint8_t> asn1_data;
// The appended tag, if any.
std::vector<uint8_t> appended_tag;
private:
Binary();
// The parsed, SignedData structure.
SignedData* signed_data = nullptr;
// ParseTag attempts to parse out the tag. It returns false on parse error or
// true on success. If successful, it sets |tag_|.
bool ParseTag();
// binary_ contains the whole input binary.
base::span<const uint8_t> binary_;
// content_info_ contains the |WIN_CERTIFICATE| structure.
base::span<const uint8_t> content_info_;
// tag_ contains the embedded tag, or |nullopt| if there isn't one.
base::Optional<base::span<const uint8_t>> tag_;
// attr_cert_offset_ is the offset in the file where the |WIN_CERTIFICATE|
// structure appears. (This is the last structure in the file.)
size_t attr_cert_offset_;
// certs_size_offset_ is the offset in the file where the u32le size of the
// |WIN_CERTIFICATE| structure is embedded in an |IMAGE_DATA_DIRECTORY|.
size_t certs_size_offset_;
};
bool ParseUnixTime(const char* time_string, base::Time* parsed_time);
std::tuple<std::unique_ptr<Binary>, int /*err*/> NewBinary(
std::vector<uint8_t> contents);
std::vector<uint8_t> BuildBinary(const Binary& bin,
const std::vector<uint8_t>& asn1_data,
const std::vector<uint8_t>& tag);
int ProcessAttributeCertificates(
const std::vector<uint8_t>& attribute_certificates,
std::vector<uint8_t>* asn1_data,
std::vector<uint8_t>* appended_tag);
std::vector<uint8_t> AppendedTag(const Binary& bin);
std::vector<uint8_t> RemoveAppendedTag(const Binary& bin);
std::vector<uint8_t> SetAppendedTag(const Binary& bin,
const std::vector<uint8_t>& tag_contents);
// SetSuperfluousCertTag returns a PE binary based on bin, but where the
// superfluous certificate contains the given tag data.
std::vector<uint8_t> SetSuperfluousCertTag(const Binary& bin,
const std::vector<uint8_t>& tag);
} // namespace tools
} // namespace updater
......
......@@ -3,27 +3,65 @@
// found in the LICENSE file.
#include "chrome/updater/tools/certificate_tag.h"
#include "base/time/time.h"
#include "base/base_paths.h"
#include "base/files/file.h"
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/path_service.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/zlib/google/compression_utils.h"
namespace updater {
namespace tools {
TEST(UpdaterCertificateTagTests, ParseUnixTime) {
base::Time time;
EXPECT_TRUE(ParseUnixTime("Mon Jan 1 10:00:00 UTC 2013", &time));
base::Time::Exploded exploded;
time.UTCExplode(&exploded);
time.UTCExplode(&exploded);
EXPECT_EQ(2013, exploded.year);
EXPECT_EQ(1, exploded.month);
EXPECT_EQ(2, exploded.day_of_week);
EXPECT_EQ(10, exploded.hour);
EXPECT_EQ(0, exploded.minute);
EXPECT_EQ(0, exploded.second);
// Tests parsing an invalid date string.
EXPECT_FALSE(ParseUnixTime("Mon Jan 101 10:00:00 UTC 2013", &time));
TEST(CertificateTag, RoundTrip) {
base::FilePath source_path;
ASSERT_TRUE(base::PathService::Get(base::DIR_SOURCE_ROOT, &source_path));
const base::FilePath exe_path = source_path.AppendASCII("chrome")
.AppendASCII("updater")
.AppendASCII("test")
.AppendASCII("data")
.AppendASCII("signed.exe.gz");
std::string exe;
ASSERT_TRUE(base::ReadFileToString(exe_path, &exe));
ASSERT_TRUE(compression::GzipUncompress(exe, &exe));
const base::span<const uint8_t> exe_span(
reinterpret_cast<const uint8_t*>(exe.data()), exe.size());
base::Optional<Binary> bin(Binary::Parse(exe_span));
ASSERT_TRUE(bin);
// Binary should be untagged on disk.
base::Optional<base::span<const uint8_t>> orig_tag(bin->tag());
EXPECT_FALSE(orig_tag);
static const uint8_t kTag[] = {1, 2, 3, 4, 5};
base::Optional<std::vector<uint8_t>> updated_exe(bin->SetTag(kTag));
ASSERT_TRUE(updated_exe);
base::Optional<Binary> bin2(Binary::Parse(*updated_exe));
ASSERT_TRUE(bin2);
base::Optional<base::span<const uint8_t>> parsed_tag(bin2->tag());
ASSERT_TRUE(parsed_tag);
EXPECT_TRUE(parsed_tag->size() == sizeof(kTag) &&
memcmp(kTag, parsed_tag->data(), sizeof(kTag)) == 0);
// Update an existing tag.
static const uint8_t kTag2[] = {1, 2, 3, 4, 6};
base::Optional<std::vector<uint8_t>> updated_again_exe(bin2->SetTag(kTag2));
ASSERT_TRUE(updated_again_exe);
base::Optional<Binary> bin3(Binary::Parse(*updated_again_exe));
ASSERT_TRUE(bin3);
base::Optional<base::span<const uint8_t>> parsed_tag2(bin3->tag());
ASSERT_TRUE(parsed_tag2);
EXPECT_TRUE(parsed_tag2->size() == sizeof(kTag2) &&
memcmp(kTag2, parsed_tag2->data(), sizeof(kTag2)) == 0);
// Updating an existing tag with a tag of the same size should not have grown
// the binary, i.e. the old tag should have been erased first.
EXPECT_EQ(sizeof(kTag), sizeof(kTag2));
EXPECT_EQ(updated_exe->size(), updated_again_exe->size());
}
} // namespace tools
......
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment