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 = [ ...@@ -11,4 +11,5 @@ include_rules = [
"+courgette", "+courgette",
"+third_party/crashpad", "+third_party/crashpad",
"+third_party/zlib/google", "+third_party/zlib/google",
"+third_party/boringssl/src/include",
] ]
...@@ -40,5 +40,8 @@ source_set("unittest") { ...@@ -40,5 +40,8 @@ source_set("unittest") {
"//base", "//base",
"//base/test:test_support", "//base/test:test_support",
"//testing/gtest", "//testing/gtest",
"//third_party/zlib/google:compression_utils",
] ]
data = [ "../test/data/signed.exe.gz" ]
} }
This diff is collapsed.
...@@ -5,68 +5,54 @@ ...@@ -5,68 +5,54 @@
#ifndef CHROME_UPDATER_TOOLS_CERTIFICATE_TAG_H_ #ifndef CHROME_UPDATER_TOOLS_CERTIFICATE_TAG_H_
#define CHROME_UPDATER_TOOLS_CERTIFICATE_TAG_H_ #define CHROME_UPDATER_TOOLS_CERTIFICATE_TAG_H_
#include <cstdint> #include "base/containers/span.h"
#include <memory> #include "base/optional.h"
#include <tuple>
#include <vector>
namespace base {
class Time;
}
namespace updater { namespace updater {
namespace tools { namespace tools {
struct SignedData; // 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
// Binary represents a PE binary. // contain arbitrary data without invalidating any Authenticode signature.
struct Binary { class Binary {
Binary(); public:
Binary(const Binary&);
~Binary(); ~Binary();
Binary(const Binary&) = delete;
Binary& operator=(const Binary&) = delete;
// The full file. // Parse a signed, Windows PE binary. Note that the returned structure
std::vector<uint8_t> contents; // contains pointers into the given data.
static base::Optional<Binary> Parse(base::span<const uint8_t> binary);
// The offset to the attribute certificates table. // tag returns the embedded tag, if any.
size_t attr_cert_offset = 0; const base::Optional<base::span<const uint8_t>>& tag() const;
// The offset to the size of the attribute certificates table. // SetTag returns an updated version of the binary that contains the given
size_t cert_size_offset = 0; // 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. private:
std::vector<uint8_t> asn1_data; Binary();
// The appended tag, if any.
std::vector<uint8_t> appended_tag;
// The parsed, SignedData structure. // ParseTag attempts to parse out the tag. It returns false on parse error or
SignedData* signed_data = nullptr; // 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 tools
} // namespace updater } // namespace updater
......
...@@ -3,27 +3,65 @@ ...@@ -3,27 +3,65 @@
// found in the LICENSE file. // found in the LICENSE file.
#include "chrome/updater/tools/certificate_tag.h" #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 "testing/gtest/include/gtest/gtest.h"
#include "third_party/zlib/google/compression_utils.h"
namespace updater { namespace updater {
namespace tools { namespace tools {
TEST(UpdaterCertificateTagTests, ParseUnixTime) { TEST(CertificateTag, RoundTrip) {
base::Time time; base::FilePath source_path;
EXPECT_TRUE(ParseUnixTime("Mon Jan 1 10:00:00 UTC 2013", &time)); ASSERT_TRUE(base::PathService::Get(base::DIR_SOURCE_ROOT, &source_path));
base::Time::Exploded exploded; const base::FilePath exe_path = source_path.AppendASCII("chrome")
time.UTCExplode(&exploded); .AppendASCII("updater")
time.UTCExplode(&exploded); .AppendASCII("test")
EXPECT_EQ(2013, exploded.year); .AppendASCII("data")
EXPECT_EQ(1, exploded.month); .AppendASCII("signed.exe.gz");
EXPECT_EQ(2, exploded.day_of_week); std::string exe;
EXPECT_EQ(10, exploded.hour); ASSERT_TRUE(base::ReadFileToString(exe_path, &exe));
EXPECT_EQ(0, exploded.minute); ASSERT_TRUE(compression::GzipUncompress(exe, &exe));
EXPECT_EQ(0, exploded.second); const base::span<const uint8_t> exe_span(
reinterpret_cast<const uint8_t*>(exe.data()), exe.size());
// Tests parsing an invalid date string.
EXPECT_FALSE(ParseUnixTime("Mon Jan 101 10:00:00 UTC 2013", &time)); 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 } // 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