summaryrefslogtreecommitdiff
path: root/chromium/components/crx_file
diff options
context:
space:
mode:
authorAllan Sandfeld Jensen <allan.jensen@qt.io>2017-07-17 13:57:45 +0200
committerAllan Sandfeld Jensen <allan.jensen@qt.io>2017-07-19 13:44:40 +0000
commit6ec7b8da05d21a3878bd21c691b41e675d74bb1c (patch)
treeb87f250bc19413750b9bb9cdbf2da20ef5014820 /chromium/components/crx_file
parentec02ee4181c49b61fce1c8fb99292dbb8139cc90 (diff)
downloadqtwebengine-chromium-6ec7b8da05d21a3878bd21c691b41e675d74bb1c.tar.gz
BASELINE: Update Chromium to 60.0.3112.70
Change-Id: I9911c2280a014d4632f254857876a395d4baed2d Reviewed-by: Alexandru Croitor <alexandru.croitor@qt.io>
Diffstat (limited to 'chromium/components/crx_file')
-rw-r--r--chromium/components/crx_file/BUILD.gn35
-rw-r--r--chromium/components/crx_file/OWNERS4
-rw-r--r--chromium/components/crx_file/crx2_file.cc79
-rw-r--r--chromium/components/crx_file/crx2_file.h76
-rw-r--r--chromium/components/crx_file/crx3.proto55
-rw-r--r--chromium/components/crx_file/crx_file.cc220
-rw-r--r--chromium/components/crx_file/crx_file.h118
-rw-r--r--chromium/components/crx_file/crx_verifier.cc308
-rw-r--r--chromium/components/crx_file/crx_verifier.h53
-rw-r--r--chromium/components/crx_file/crx_verifier_unittest.cc196
10 files changed, 802 insertions, 342 deletions
diff --git a/chromium/components/crx_file/BUILD.gn b/chromium/components/crx_file/BUILD.gn
index 8c5cceb5db2..412528e188e 100644
--- a/chromium/components/crx_file/BUILD.gn
+++ b/chromium/components/crx_file/BUILD.gn
@@ -2,10 +2,14 @@
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
+import("//third_party/protobuf/proto_library.gni")
+
static_library("crx_file") {
sources = [
- "crx_file.cc",
- "crx_file.h",
+ "crx2_file.cc",
+ "crx2_file.h",
+ "crx_verifier.cc",
+ "crx_verifier.h",
"id_util.cc",
"id_util.h",
]
@@ -14,17 +18,44 @@ static_library("crx_file") {
"//base",
"//crypto",
]
+
+ public_deps = [
+ ":crx3_proto",
+ ]
+}
+
+bundle_data("unit_tests_bundle_data") {
+ visibility = [ ":unit_tests" ]
+ testonly = true
+ sources = [
+ "//components/test/data/crx_file/unsigned.crx3",
+ "//components/test/data/crx_file/valid.crx2",
+ "//components/test/data/crx_file/valid_no_publisher.crx3",
+ "//components/test/data/crx_file/valid_publisher.crx3",
+ ]
+ outputs = [
+ "{{bundle_resources_dir}}/" +
+ "{{source_root_relative_dir}}/{{source_file_part}}",
+ ]
}
source_set("unit_tests") {
testonly = true
sources = [
+ "crx_verifier_unittest.cc",
"id_util_unittest.cc",
]
deps = [
":crx_file",
+ ":unit_tests_bundle_data",
"//base",
"//testing/gtest",
]
}
+
+proto_library("crx3_proto") {
+ sources = [
+ "crx3.proto",
+ ]
+}
diff --git a/chromium/components/crx_file/OWNERS b/chromium/components/crx_file/OWNERS
index 5a014d15275..8c5a6dbd3ba 100644
--- a/chromium/components/crx_file/OWNERS
+++ b/chromium/components/crx_file/OWNERS
@@ -1,2 +1,2 @@
-asargent@chromium.org
-rockot@chromium.org
+file://extensions/OWNERS
+
diff --git a/chromium/components/crx_file/crx2_file.cc b/chromium/components/crx_file/crx2_file.cc
new file mode 100644
index 00000000000..2c37bfe7979
--- /dev/null
+++ b/chromium/components/crx_file/crx2_file.cc
@@ -0,0 +1,79 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/crx_file/crx2_file.h"
+
+#include "base/base64.h"
+#include "base/files/file_path.h"
+#include "base/files/file_util.h"
+#include "base/files/scoped_file.h"
+#include "base/macros.h"
+#include "base/memory/ptr_util.h"
+#include "base/numerics/safe_math.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/string_util.h"
+#include "components/crx_file/id_util.h"
+#include "crypto/secure_hash.h"
+#include "crypto/sha2.h"
+#include "crypto/signature_verifier.h"
+
+namespace crx_file {
+
+namespace {
+
+// The current version of the crx format.
+static const uint32_t kCurrentVersion = 2;
+
+// The current version of the crx diff format.
+static const uint32_t kCurrentDiffVersion = 0;
+
+// The maximum size the crx parser will tolerate for a public key.
+static const uint32_t kMaxPublicKeySize = 1 << 16;
+
+// The maximum size the crx parser will tolerate for a signature.
+static const uint32_t kMaxSignatureSize = 1 << 16;
+
+} // namespace
+
+std::unique_ptr<Crx2File> Crx2File::Create(const uint32_t key_size,
+ const uint32_t signature_size,
+ Crx2File::Error* error) {
+ Crx2File::Header header;
+ memcpy(&header.magic, kCrx2FileHeaderMagic, kCrx2FileHeaderMagicSize);
+ header.version = kCurrentVersion;
+ header.key_size = key_size;
+ header.signature_size = signature_size;
+ if (HeaderIsValid(header, error))
+ return base::WrapUnique(new Crx2File(header));
+ return nullptr;
+}
+
+Crx2File::Crx2File(const Header& header) : header_(header) {}
+
+bool Crx2File::HeaderIsValid(const Crx2File::Header& header,
+ Crx2File::Error* error) {
+ bool valid = false;
+ bool diffCrx = false;
+ if (!strncmp(kCrxDiffFileHeaderMagic, header.magic, sizeof(header.magic)))
+ diffCrx = true;
+ if (strncmp(kCrx2FileHeaderMagic, header.magic, sizeof(header.magic)) &&
+ !diffCrx)
+ *error = kWrongMagic;
+ else if (header.version != kCurrentVersion &&
+ !(diffCrx && header.version == kCurrentDiffVersion))
+ *error = kInvalidVersion;
+ else if (header.key_size > kMaxPublicKeySize)
+ *error = kInvalidKeyTooLarge;
+ else if (header.key_size == 0)
+ *error = kInvalidKeyTooSmall;
+ else if (header.signature_size > kMaxSignatureSize)
+ *error = kInvalidSignatureTooLarge;
+ else if (header.signature_size == 0)
+ *error = kInvalidSignatureTooSmall;
+ else
+ valid = true;
+ return valid;
+}
+
+} // namespace crx_file
diff --git a/chromium/components/crx_file/crx2_file.h b/chromium/components/crx_file/crx2_file.h
new file mode 100644
index 00000000000..6fe1584116f
--- /dev/null
+++ b/chromium/components/crx_file/crx2_file.h
@@ -0,0 +1,76 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_CRX_FILE_CRX2_FILE_H_
+#define COMPONENTS_CRX_FILE_CRX2_FILE_H_
+
+#include <stddef.h>
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <memory>
+#include <string>
+#include <vector>
+
+namespace crx_file {
+
+// The magic string embedded in the header.
+constexpr char kCrx2FileHeaderMagic[] = "Cr24";
+constexpr char kCrxDiffFileHeaderMagic[] = "CrOD";
+constexpr int kCrx2FileHeaderMagicSize = 4;
+
+// CRX files have a header that includes a magic key, version number, and
+// some signature sizing information. Use Crx2File object to validate whether
+// the header is valid or not.
+class Crx2File {
+ public:
+ // This header is the first data at the beginning of an extension. Its
+ // contents are purposely 32-bit aligned so that it can just be slurped into
+ // a struct without manual parsing.
+ struct Header {
+ char magic[kCrx2FileHeaderMagicSize];
+ uint32_t version;
+ uint32_t key_size; // The size of the public key, in bytes.
+ uint32_t signature_size; // The size of the signature, in bytes.
+ // An ASN.1-encoded PublicKeyInfo structure follows.
+ // The signature follows.
+ };
+
+ enum Error {
+ kWrongMagic,
+ kInvalidVersion,
+ kInvalidKeyTooLarge,
+ kInvalidKeyTooSmall,
+ kInvalidSignatureTooLarge,
+ kInvalidSignatureTooSmall,
+ kMaxValue,
+ };
+
+ // Construct a new header for the given key and signature sizes.
+ // Returns null if erroneous values of |key_size| and/or
+ // |signature_size| are provided. |error| contains an error code with
+ // additional information.
+ // Use this constructor and then .header() to obtain the Header
+ // for writing out to a CRX file.
+ static std::unique_ptr<Crx2File> Create(const uint32_t key_size,
+ const uint32_t signature_size,
+ Error* error);
+
+ // Returns the header structure for writing out to a CRX file.
+ const Header& header() const { return header_; }
+
+ private:
+ Header header_;
+
+ // Constructor is private. Clients should use static factory methods above.
+ explicit Crx2File(const Header& header);
+
+ // Checks the |header| for validity and returns true if the values are valid.
+ // If false is returned, more detailed error code is returned in |error|.
+ static bool HeaderIsValid(const Header& header, Error* error);
+};
+
+} // namespace crx_file
+
+#endif // COMPONENTS_CRX_FILE_CRX2_FILE_H_
diff --git a/chromium/components/crx_file/crx3.proto b/chromium/components/crx_file/crx3.proto
new file mode 100644
index 00000000000..2ea91f1c937
--- /dev/null
+++ b/chromium/components/crx_file/crx3.proto
@@ -0,0 +1,55 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file
+
+syntax = "proto2";
+
+option optimize_for = LITE_RUNTIME;
+
+package crx_file;
+
+// A CRX₃ file is a binary file of the following format:
+// [4 octets]: "Cr24", a magic number.
+// [4 octets]: The version of the *.crx file format used (currently 3).
+// [4 octets]: N, little-endian, the length of the header section.
+// [N octets]: The header (the binary encoding of a CrxFileHeader).
+// [M octets]: The ZIP archive.
+// Clients should reject CRX₃ files that contain an N that is too large for the
+// client to safely handle in memory.
+
+message CrxFileHeader {
+ // PSS signature with RSA public key. The public key is formatted as a
+ // X.509 SubjectPublicKeyInfo block, as in CRX₂. In the common case of a
+ // developer key proof, the first 128 bits of the SHA-256 hash of the
+ // public key must equal the crx_id.
+ repeated AsymmetricKeyProof sha256_with_rsa = 2;
+
+ // ECDSA signature, using the NIST P-256 curve. Public key appears in
+ // named-curve format.
+ // The pinned algorithm will be this, at least on 2017-01-01.
+ repeated AsymmetricKeyProof sha256_with_ecdsa = 3;
+
+ // The binary form of a SignedData message. We do not use a nested
+ // SignedData message, as handlers of this message must verify the proofs
+ // on exactly these bytes, so it is convenient to parse in two steps.
+ //
+ // All proofs in this CrxFile message are on the value
+ // "CRX3 SignedData\x00" + signed_header_size + signed_header_data +
+ // archive, where "\x00" indicates an octet with value 0, "CRX3 SignedData"
+ // is encoded using UTF-8, signed_header_size is the size in octets of the
+ // contents of this field and is encoded using 4 octets in little-endian
+ // order, signed_header_data is exactly the content of this field, and
+ // archive is the remaining contents of the file following the header.
+ optional bytes signed_header_data = 10000;
+}
+
+message AsymmetricKeyProof {
+ optional bytes public_key = 1;
+ optional bytes signature = 2;
+}
+
+message SignedData {
+ // This is simple binary, not UTF-8 encoded mpdecimal; i.e. it is exactly
+ // 16 bytes long.
+ optional bytes crx_id = 1;
+}
diff --git a/chromium/components/crx_file/crx_file.cc b/chromium/components/crx_file/crx_file.cc
deleted file mode 100644
index c2b05f8e80b..00000000000
--- a/chromium/components/crx_file/crx_file.cc
+++ /dev/null
@@ -1,220 +0,0 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/crx_file/crx_file.h"
-
-#include "base/base64.h"
-#include "base/files/file_path.h"
-#include "base/files/file_util.h"
-#include "base/files/scoped_file.h"
-#include "base/macros.h"
-#include "base/memory/ptr_util.h"
-#include "base/numerics/safe_math.h"
-#include "base/strings/string_number_conversions.h"
-#include "base/strings/string_util.h"
-#include "components/crx_file/id_util.h"
-#include "crypto/secure_hash.h"
-#include "crypto/sha2.h"
-#include "crypto/signature_verifier.h"
-
-namespace crx_file {
-
-namespace {
-
-// The current version of the crx format.
-static const uint32_t kCurrentVersion = 2;
-
-// The current version of the crx diff format.
-static const uint32_t kCurrentDiffVersion = 0;
-
-// The maximum size the crx parser will tolerate for a public key.
-static const uint32_t kMaxPublicKeySize = 1 << 16;
-
-// The maximum size the crx parser will tolerate for a signature.
-static const uint32_t kMaxSignatureSize = 1 << 16;
-
-// Helper function to read bytes into a buffer while also updating a hash with
-// those bytes. Returns the number of bytes read.
-size_t ReadAndHash(void* ptr,
- size_t size,
- size_t nmemb,
- FILE* stream,
- crypto::SecureHash* hash) {
- size_t item_count = fread(ptr, size, nmemb, stream);
- base::CheckedNumeric<size_t> byte_count(item_count);
- byte_count *= size;
- if (!byte_count.IsValid())
- return 0;
- if (item_count > 0 && hash) {
- hash->Update(ptr, byte_count.ValueOrDie());
- }
- return byte_count.ValueOrDie();
-}
-
-// Helper function to finish computing a hash and return an error if the
-// result of the hash didn't meet an expected base64-encoded value.
-CrxFile::ValidateError FinalizeHash(const std::string& extension_id,
- crypto::SecureHash* hash,
- const std::string& expected_hash) {
- CHECK(hash != nullptr);
- uint8_t output[crypto::kSHA256Length] = {};
- hash->Finish(output, sizeof(output));
- std::string hash_base64 =
- base::ToLowerASCII(base::HexEncode(output, sizeof(output)));
- if (hash_base64 != expected_hash) {
- LOG(ERROR) << "Hash check failed for extension: " << extension_id
- << ", expected " << expected_hash << ", got " << hash_base64;
- return CrxFile::ValidateError::CRX_HASH_VERIFICATION_FAILED;
- } else {
- return CrxFile::ValidateError::NONE;
- }
-}
-
-} // namespace
-
-// The magic string embedded in the header.
-const char kCrxFileHeaderMagic[] = "Cr24";
-const char kCrxDiffFileHeaderMagic[] = "CrOD";
-
-std::unique_ptr<CrxFile> CrxFile::Parse(const CrxFile::Header& header,
- CrxFile::Error* error) {
- if (HeaderIsValid(header, error))
- return base::WrapUnique(new CrxFile(header));
- return nullptr;
-}
-
-std::unique_ptr<CrxFile> CrxFile::Create(const uint32_t key_size,
- const uint32_t signature_size,
- CrxFile::Error* error) {
- CrxFile::Header header;
- memcpy(&header.magic, kCrxFileHeaderMagic, kCrxFileHeaderMagicSize);
- header.version = kCurrentVersion;
- header.key_size = key_size;
- header.signature_size = signature_size;
- if (HeaderIsValid(header, error))
- return base::WrapUnique(new CrxFile(header));
- return nullptr;
-}
-
-bool CrxFile::HeaderIsDelta(const CrxFile::Header& header) {
- return !strncmp(kCrxDiffFileHeaderMagic, header.magic, sizeof(header.magic));
-}
-
-// static
-CrxFile::ValidateError CrxFile::ValidateSignature(
- const base::FilePath& crx_path,
- const std::string& expected_hash,
- std::string* public_key,
- std::string* extension_id,
- CrxFile::Header* header_out) {
- base::ScopedFILE file(base::OpenFile(crx_path, "rb"));
- std::unique_ptr<crypto::SecureHash> hash;
- if (!expected_hash.empty())
- hash = crypto::SecureHash::Create(crypto::SecureHash::SHA256);
-
- if (!file.get())
- return ValidateError::CRX_FILE_NOT_READABLE;
-
- CrxFile::Header header;
- size_t len = ReadAndHash(&header, sizeof(header), 1, file.get(), hash.get());
- if (len != sizeof(header))
- return ValidateError::CRX_HEADER_INVALID;
- if (header_out)
- *header_out = header;
-
- CrxFile::Error error;
- std::unique_ptr<CrxFile> crx(CrxFile::Parse(header, &error));
- if (!crx) {
- switch (error) {
- case CrxFile::kWrongMagic:
- return ValidateError::CRX_MAGIC_NUMBER_INVALID;
- case CrxFile::kInvalidVersion:
- return ValidateError::CRX_VERSION_NUMBER_INVALID;
-
- case CrxFile::kInvalidKeyTooLarge:
- case CrxFile::kInvalidSignatureTooLarge:
- return ValidateError::CRX_EXCESSIVELY_LARGE_KEY_OR_SIGNATURE;
-
- case CrxFile::kInvalidKeyTooSmall:
- return ValidateError::CRX_ZERO_KEY_LENGTH;
- case CrxFile::kInvalidSignatureTooSmall:
- return ValidateError::CRX_ZERO_SIGNATURE_LENGTH;
-
- default:
- return ValidateError::CRX_HEADER_INVALID;
- }
- }
-
- std::vector<uint8_t> key(header.key_size);
- len = ReadAndHash(&key.front(), sizeof(uint8_t), header.key_size, file.get(),
- hash.get());
- if (len != header.key_size)
- return ValidateError::CRX_PUBLIC_KEY_INVALID;
-
- std::vector<uint8_t> signature(header.signature_size);
- len = ReadAndHash(&signature.front(), sizeof(uint8_t), header.signature_size,
- file.get(), hash.get());
- if (len < header.signature_size)
- return ValidateError::CRX_SIGNATURE_INVALID;
-
- crypto::SignatureVerifier verifier;
- if (!verifier.VerifyInit(crypto::SignatureVerifier::RSA_PKCS1_SHA1,
- signature.data(), signature.size(), key.data(),
- key.size())) {
- // Signature verification initialization failed. This is most likely
- // caused by a public key in the wrong format (should encode algorithm).
- return ValidateError::CRX_SIGNATURE_VERIFICATION_INITIALIZATION_FAILED;
- }
-
- uint8_t buf[1 << 12] = {};
- while ((len = ReadAndHash(buf, sizeof(buf[0]), arraysize(buf), file.get(),
- hash.get())) > 0)
- verifier.VerifyUpdate(buf, len);
-
- if (!verifier.VerifyFinal())
- return ValidateError::CRX_SIGNATURE_VERIFICATION_FAILED;
-
- std::string public_key_bytes =
- std::string(reinterpret_cast<char*>(&key.front()), key.size());
- if (public_key)
- base::Base64Encode(public_key_bytes, public_key);
-
- std::string id = id_util::GenerateId(public_key_bytes);
- if (extension_id)
- *extension_id = id;
-
- if (!expected_hash.empty())
- return FinalizeHash(id, hash.get(), expected_hash);
-
- return ValidateError::NONE;
-}
-
-CrxFile::CrxFile(const Header& header) : header_(header) {}
-
-bool CrxFile::HeaderIsValid(const CrxFile::Header& header,
- CrxFile::Error* error) {
- bool valid = false;
- bool diffCrx = false;
- if (!strncmp(kCrxDiffFileHeaderMagic, header.magic, sizeof(header.magic)))
- diffCrx = true;
- if (strncmp(kCrxFileHeaderMagic, header.magic, sizeof(header.magic)) &&
- !diffCrx)
- *error = kWrongMagic;
- else if (header.version != kCurrentVersion
- && !(diffCrx && header.version == kCurrentDiffVersion))
- *error = kInvalidVersion;
- else if (header.key_size > kMaxPublicKeySize)
- *error = kInvalidKeyTooLarge;
- else if (header.key_size == 0)
- *error = kInvalidKeyTooSmall;
- else if (header.signature_size > kMaxSignatureSize)
- *error = kInvalidSignatureTooLarge;
- else if (header.signature_size == 0)
- *error = kInvalidSignatureTooSmall;
- else
- valid = true;
- return valid;
-}
-
-} // namespace crx_file
diff --git a/chromium/components/crx_file/crx_file.h b/chromium/components/crx_file/crx_file.h
deleted file mode 100644
index cf9bce8a81c..00000000000
--- a/chromium/components/crx_file/crx_file.h
+++ /dev/null
@@ -1,118 +0,0 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef COMPONENTS_CRX_FILE_CRX_FILE_H_
-#define COMPONENTS_CRX_FILE_CRX_FILE_H_
-
-#include <stddef.h>
-#include <stdint.h>
-#include <sys/types.h>
-
-#include <memory>
-#include <string>
-#include <vector>
-
-namespace base {
-class FilePath;
-}
-
-namespace crx_file {
-
-// CRX files have a header that includes a magic key, version number, and
-// some signature sizing information. Use CrxFile object to validate whether
-// the header is valid or not.
-class CrxFile {
- public:
- // The size of the magic character sequence at the beginning of each crx
- // file, in bytes. This should be a multiple of 4.
- static const size_t kCrxFileHeaderMagicSize = 4;
-
- // This header is the first data at the beginning of an extension. Its
- // contents are purposely 32-bit aligned so that it can just be slurped into
- // a struct without manual parsing.
- struct Header {
- char magic[kCrxFileHeaderMagicSize];
- uint32_t version;
- uint32_t key_size; // The size of the public key, in bytes.
- uint32_t signature_size; // The size of the signature, in bytes.
- // An ASN.1-encoded PublicKeyInfo structure follows.
- // The signature follows.
- };
-
- enum Error {
- kWrongMagic,
- kInvalidVersion,
- kInvalidKeyTooLarge,
- kInvalidKeyTooSmall,
- kInvalidSignatureTooLarge,
- kInvalidSignatureTooSmall,
- };
-
- // Construct a new CRX file header object with bytes of a header
- // read from a CRX file. If a null scoped_ptr is returned, |error|
- // contains an error code with additional information.
- static std::unique_ptr<CrxFile> Parse(const Header& header, Error* error);
-
- // Construct a new header for the given key and signature sizes.
- // Returns a null scoped_ptr if erroneous values of |key_size| and/or
- // |signature_size| are provided. |error| contains an error code with
- // additional information.
- // Use this constructor and then .header() to obtain the Header
- // for writing out to a CRX file.
- static std::unique_ptr<CrxFile> Create(const uint32_t key_size,
- const uint32_t signature_size,
- Error* error);
-
- // Returns the header structure for writing out to a CRX file.
- const Header& header() const { return header_; }
-
- // Checks a valid |header| to determine whether or not the CRX represents a
- // differential CRX.
- static bool HeaderIsDelta(const Header& header);
-
- enum class ValidateError {
- NONE,
- CRX_FILE_NOT_READABLE,
- CRX_HEADER_INVALID,
- CRX_MAGIC_NUMBER_INVALID,
- CRX_VERSION_NUMBER_INVALID,
- CRX_EXCESSIVELY_LARGE_KEY_OR_SIGNATURE,
- CRX_ZERO_KEY_LENGTH,
- CRX_ZERO_SIGNATURE_LENGTH,
- CRX_PUBLIC_KEY_INVALID,
- CRX_SIGNATURE_INVALID,
- CRX_SIGNATURE_VERIFICATION_INITIALIZATION_FAILED,
- CRX_SIGNATURE_VERIFICATION_FAILED,
- CRX_HASH_VERIFICATION_FAILED,
- };
-
- // Validates that the .crx file at |crx_path| is properly signed. If
- // |expected_hash| is non-empty, it should specify a hex-encoded SHA256 hash
- // for the entire .crx file which will be checked as we read it, and any
- // mismatch will cause a CRX_HASH_VERIFICATION_FAILED result. The
- // |public_key| argument can be provided to receive a copy of the
- // base64-encoded public key pem bytes extracted from the .crx. The
- // |extension_id| argument can be provided to receive the extension id (which
- // is computed as a hash of the public key provided in the .crx). The
- // |header| argument can be provided to receive a copy of the crx header.
- static ValidateError ValidateSignature(const base::FilePath& crx_path,
- const std::string& expected_hash,
- std::string* public_key,
- std::string* extension_id,
- CrxFile::Header* header);
-
- private:
- Header header_;
-
- // Constructor is private. Clients should use static factory methods above.
- explicit CrxFile(const Header& header);
-
- // Checks the |header| for validity and returns true if the values are valid.
- // If false is returned, more detailed error code is returned in |error|.
- static bool HeaderIsValid(const Header& header, Error* error);
-};
-
-} // namespace crx_file
-
-#endif // COMPONENTS_CRX_FILE_CRX_FILE_H_
diff --git a/chromium/components/crx_file/crx_verifier.cc b/chromium/components/crx_file/crx_verifier.cc
new file mode 100644
index 00000000000..0a26b77d6f9
--- /dev/null
+++ b/chromium/components/crx_file/crx_verifier.cc
@@ -0,0 +1,308 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/crx_file/crx_verifier.h"
+
+#include <cstring>
+#include <iterator>
+#include <memory>
+#include <set>
+#include <utility>
+
+#include "base/base64.h"
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/files/file.h"
+#include "base/files/file_path.h"
+#include "base/memory/ptr_util.h"
+#include "base/strings/string_number_conversions.h"
+#include "components/crx_file/crx2_file.h"
+#include "components/crx_file/crx3.pb.h"
+#include "components/crx_file/id_util.h"
+#include "crypto/secure_hash.h"
+#include "crypto/secure_util.h"
+#include "crypto/sha2.h"
+#include "crypto/signature_verifier.h"
+
+namespace crx_file {
+
+namespace {
+
+// The maximum size the Crx2 parser will tolerate for a public key.
+constexpr uint32_t kMaxPublicKeySize = 1 << 16;
+
+// The maximum size the Crx2 parser will tolerate for a signature.
+constexpr uint32_t kMaxSignatureSize = 1 << 16;
+
+// The maximum size the Crx3 parser will tolerate for a header.
+constexpr uint32_t kMaxHeaderSize = 1 << 18;
+
+// The context for Crx3 signing, encoded in UTF8.
+constexpr unsigned char kSignatureContext[] = u8"CRX3 SignedData";
+
+// The SHA256 hash of the "ecdsa_2017_public" Crx3 key.
+constexpr uint8_t kPublisherKeyHash[] = {
+ 0x61, 0xf7, 0xf2, 0xa6, 0xbf, 0xcf, 0x74, 0xcd, 0x0b, 0xc1, 0xfe,
+ 0x24, 0x97, 0xcc, 0x9b, 0x04, 0x25, 0x4c, 0x65, 0x8f, 0x79, 0xf2,
+ 0x14, 0x53, 0x92, 0x86, 0x7e, 0xa8, 0x36, 0x63, 0x67, 0xcf};
+
+using VerifierCollection =
+ std::vector<std::unique_ptr<crypto::SignatureVerifier>>;
+using RepeatedProof = google::protobuf::RepeatedPtrField<AsymmetricKeyProof>;
+
+int ReadAndHashBuffer(uint8_t* buffer,
+ int length,
+ base::File* file,
+ crypto::SecureHash* hash) {
+ static_assert(sizeof(char) == sizeof(uint8_t), "Unsupported char size.");
+ int read = file->ReadAtCurrentPos(reinterpret_cast<char*>(buffer), length);
+ hash->Update(buffer, read);
+ return read;
+}
+
+// Returns UINT32_MAX in the case of an unexpected EOF or read error, else
+// returns the read uint32.
+uint32_t ReadAndHashLittleEndianUInt32(base::File* file,
+ crypto::SecureHash* hash) {
+ uint8_t buffer[4] = {};
+ if (ReadAndHashBuffer(buffer, 4, file, hash) != 4)
+ return UINT32_MAX;
+ return buffer[3] << 24 | buffer[2] << 16 | buffer[1] << 8 | buffer[0];
+}
+
+// Read to the end of the file, updating the hash and all verifiers.
+bool ReadHashAndVerifyArchive(base::File* file,
+ crypto::SecureHash* hash,
+ const VerifierCollection& verifiers) {
+ uint8_t buffer[1 << 12] = {};
+ size_t len = 0;
+ while ((len = ReadAndHashBuffer(buffer, arraysize(buffer), file, hash)) > 0) {
+ for (auto& verifier : verifiers)
+ verifier->VerifyUpdate(buffer, len);
+ }
+ for (auto& verifier : verifiers) {
+ if (!verifier->VerifyFinal())
+ return false;
+ }
+ return true;
+}
+
+// The remaining contents of a Crx3 file are [header-size][header][archive].
+// [header] is an encoded protocol buffer and contains both a signed and
+// unsigned section. The unsigned section contains a set of key/signature pairs,
+// and the signed section is the encoding of another protocol buffer. All
+// signatures cover [prefix][signed-header-size][signed-header][archive].
+VerifierResult VerifyCrx3(
+ base::File* file,
+ crypto::SecureHash* hash,
+ const std::vector<std::vector<uint8_t>>& required_key_hashes,
+ std::string* public_key,
+ std::string* crx_id,
+ bool require_publisher_key) {
+ // Parse [header-size] and [header].
+ const uint32_t header_size = ReadAndHashLittleEndianUInt32(file, hash);
+ if (header_size > kMaxHeaderSize)
+ return VerifierResult::ERROR_HEADER_INVALID;
+ std::vector<uint8_t> header_bytes(header_size);
+ // Assuming kMaxHeaderSize can fit in an int, the following cast is safe.
+ if (ReadAndHashBuffer(header_bytes.data(), header_size, file, hash) !=
+ static_cast<int>(header_size))
+ return VerifierResult::ERROR_HEADER_INVALID;
+ CrxFileHeader header;
+ if (!header.ParseFromArray(header_bytes.data(), header_size))
+ return VerifierResult::ERROR_HEADER_INVALID;
+
+ // Parse [signed-header].
+ const std::string& signed_header_data_str = header.signed_header_data();
+ SignedData signed_header_data;
+ if (!signed_header_data.ParseFromString(signed_header_data_str))
+ return VerifierResult::ERROR_HEADER_INVALID;
+ const std::string& crx_id_encoded = signed_header_data.crx_id();
+ const std::string declared_crx_id = id_util::GenerateIdFromHex(
+ base::HexEncode(crx_id_encoded.data(), crx_id_encoded.size()));
+
+ // Create a little-endian representation of [signed-header-size].
+ const int signed_header_size = signed_header_data_str.size();
+ const uint8_t header_size_octets[] = {
+ signed_header_size, signed_header_size >> 8, signed_header_size >> 16,
+ signed_header_size >> 24};
+
+ // Create a set of all required key hashes.
+ std::set<std::vector<uint8_t>> required_key_set(required_key_hashes.begin(),
+ required_key_hashes.end());
+ if (require_publisher_key) {
+ required_key_set.emplace(std::begin(kPublisherKeyHash),
+ std::end(kPublisherKeyHash));
+ }
+
+ using ProofFetcher = const RepeatedProof& (CrxFileHeader::*)() const;
+ ProofFetcher rsa = &CrxFileHeader::sha256_with_rsa;
+ ProofFetcher ecdsa = &CrxFileHeader::sha256_with_ecdsa;
+
+ std::string public_key_bytes;
+ VerifierCollection verifiers;
+ verifiers.reserve(header.sha256_with_rsa_size() +
+ header.sha256_with_ecdsa_size());
+ const std::vector<
+ std::pair<ProofFetcher, crypto::SignatureVerifier::SignatureAlgorithm>>
+ proof_types = {
+ std::make_pair(rsa, crypto::SignatureVerifier::RSA_PKCS1_SHA256),
+ std::make_pair(ecdsa, crypto::SignatureVerifier::ECDSA_SHA256)};
+
+ // Initialize all verifiers and update them with
+ // [prefix][signed-header-size][signed-header].
+ // Clear any elements of required_key_set that are encountered, and watch for
+ // the developer key.
+ for (const auto& proof_type : proof_types) {
+ for (const auto& proof : (header.*proof_type.first)()) {
+ const std::string& key = proof.public_key();
+ const std::string& sig = proof.signature();
+ if (id_util::GenerateId(key) == declared_crx_id)
+ public_key_bytes = key;
+ std::vector<uint8_t> key_hash(crypto::kSHA256Length);
+ crypto::SHA256HashString(key, key_hash.data(), key_hash.size());
+ required_key_set.erase(key_hash);
+ auto v = base::MakeUnique<crypto::SignatureVerifier>();
+ static_assert(sizeof(unsigned char) == sizeof(uint8_t),
+ "Unsupported char size.");
+ if (!v->VerifyInit(
+ proof_type.second, reinterpret_cast<const uint8_t*>(sig.data()),
+ sig.size(), reinterpret_cast<const uint8_t*>(key.data()),
+ key.size()))
+ return VerifierResult::ERROR_SIGNATURE_INITIALIZATION_FAILED;
+ v->VerifyUpdate(kSignatureContext, arraysize(kSignatureContext));
+ v->VerifyUpdate(header_size_octets, arraysize(header_size_octets));
+ v->VerifyUpdate(
+ reinterpret_cast<const uint8_t*>(signed_header_data_str.data()),
+ signed_header_data_str.size());
+ verifiers.push_back(std::move(v));
+ }
+ }
+ if (public_key_bytes.empty() || !required_key_set.empty())
+ return VerifierResult::ERROR_REQUIRED_PROOF_MISSING;
+
+ // Update and finalize the verifiers with [archive].
+ if (!ReadHashAndVerifyArchive(file, hash, verifiers))
+ return VerifierResult::ERROR_SIGNATURE_VERIFICATION_FAILED;
+
+ base::Base64Encode(public_key_bytes, public_key);
+ *crx_id = declared_crx_id;
+ return VerifierResult::OK_FULL;
+}
+
+VerifierResult VerifyCrx2(
+ base::File* file,
+ crypto::SecureHash* hash,
+ const std::vector<std::vector<uint8_t>>& required_key_hashes,
+ std::string* public_key,
+ std::string* crx_id) {
+ const uint32_t key_size = ReadAndHashLittleEndianUInt32(file, hash);
+ if (key_size > kMaxPublicKeySize)
+ return VerifierResult::ERROR_HEADER_INVALID;
+ const uint32_t sig_size = ReadAndHashLittleEndianUInt32(file, hash);
+ if (sig_size > kMaxSignatureSize)
+ return VerifierResult::ERROR_HEADER_INVALID;
+ std::vector<uint8_t> key(key_size);
+ if (ReadAndHashBuffer(key.data(), key_size, file, hash) !=
+ static_cast<int>(key_size))
+ return VerifierResult::ERROR_HEADER_INVALID;
+ for (const auto& expected_hash : required_key_hashes) {
+ // In practice we expect zero or one key_hashes_ for Crx2 files.
+ std::vector<uint8_t> hash(crypto::kSHA256Length);
+ std::unique_ptr<crypto::SecureHash> sha256 =
+ crypto::SecureHash::Create(crypto::SecureHash::SHA256);
+ sha256->Update(key.data(), key.size());
+ sha256->Finish(hash.data(), hash.size());
+ if (hash != expected_hash)
+ return VerifierResult::ERROR_REQUIRED_PROOF_MISSING;
+ }
+
+ std::vector<uint8_t> sig(sig_size);
+ if (ReadAndHashBuffer(sig.data(), sig_size, file, hash) !=
+ static_cast<int>(sig_size))
+ return VerifierResult::ERROR_HEADER_INVALID;
+ std::vector<std::unique_ptr<crypto::SignatureVerifier>> verifiers;
+ verifiers.push_back(base::MakeUnique<crypto::SignatureVerifier>());
+ if (!verifiers[0]->VerifyInit(crypto::SignatureVerifier::RSA_PKCS1_SHA1,
+ sig.data(), sig.size(), key.data(),
+ key.size())) {
+ return VerifierResult::ERROR_SIGNATURE_INITIALIZATION_FAILED;
+ }
+
+ if (!ReadHashAndVerifyArchive(file, hash, verifiers))
+ return VerifierResult::ERROR_SIGNATURE_VERIFICATION_FAILED;
+
+ const std::string public_key_bytes(key.begin(), key.end());
+ base::Base64Encode(public_key_bytes, public_key);
+ *crx_id = id_util::GenerateId(public_key_bytes);
+ return VerifierResult::OK_FULL;
+}
+
+} // namespace
+
+VerifierResult Verify(
+ const base::FilePath& crx_path,
+ const VerifierFormat& format,
+ const std::vector<std::vector<uint8_t>>& required_key_hashes,
+ const std::vector<uint8_t>& required_file_hash,
+ std::string* public_key,
+ std::string* crx_id) {
+ std::string public_key_local;
+ std::string crx_id_local;
+ base::File file(crx_path, base::File::FLAG_OPEN | base::File::FLAG_READ);
+ if (!file.IsValid())
+ return VerifierResult::ERROR_FILE_NOT_READABLE;
+
+ std::unique_ptr<crypto::SecureHash> file_hash =
+ crypto::SecureHash::Create(crypto::SecureHash::SHA256);
+
+ // Magic number.
+ bool diff = false;
+ char buffer[kCrx2FileHeaderMagicSize] = {};
+ if (file.ReadAtCurrentPos(buffer, kCrx2FileHeaderMagicSize) !=
+ kCrx2FileHeaderMagicSize)
+ return VerifierResult::ERROR_HEADER_INVALID;
+ if (!strncmp(buffer, kCrxDiffFileHeaderMagic, kCrx2FileHeaderMagicSize))
+ diff = true;
+ else if (strncmp(buffer, kCrx2FileHeaderMagic, kCrx2FileHeaderMagicSize))
+ return VerifierResult::ERROR_HEADER_INVALID;
+ file_hash->Update(buffer, sizeof(buffer));
+
+ // Version number.
+ const uint32_t version =
+ ReadAndHashLittleEndianUInt32(&file, file_hash.get());
+ VerifierResult result;
+ if (format == VerifierFormat::CRX2_OR_CRX3 &&
+ (version == 2 || (diff && version == 0)))
+ result = VerifyCrx2(&file, file_hash.get(), required_key_hashes,
+ &public_key_local, &crx_id_local);
+ else if (version == 3)
+ result = VerifyCrx3(&file, file_hash.get(), required_key_hashes,
+ &public_key_local, &crx_id_local,
+ format == VerifierFormat::CRX3_WITH_PUBLISHER_PROOF);
+ else
+ result = VerifierResult::ERROR_HEADER_INVALID;
+ if (result != VerifierResult::OK_FULL)
+ return result;
+
+ // Finalize file hash.
+ uint8_t final_hash[crypto::kSHA256Length] = {};
+ file_hash->Finish(final_hash, sizeof(final_hash));
+ if (!required_file_hash.empty()) {
+ if (required_file_hash.size() != crypto::kSHA256Length)
+ return VerifierResult::ERROR_EXPECTED_HASH_INVALID;
+ if (!crypto::SecureMemEqual(final_hash, required_file_hash.data(),
+ crypto::kSHA256Length))
+ return VerifierResult::ERROR_FILE_HASH_FAILED;
+ }
+
+ // All is well. Set the out-params and return.
+ if (public_key)
+ *public_key = public_key_local;
+ if (crx_id)
+ *crx_id = crx_id_local;
+ return diff ? VerifierResult::OK_DELTA : VerifierResult::OK_FULL;
+}
+
+} // namespace crx_file
diff --git a/chromium/components/crx_file/crx_verifier.h b/chromium/components/crx_file/crx_verifier.h
new file mode 100644
index 00000000000..35cf1956a24
--- /dev/null
+++ b/chromium/components/crx_file/crx_verifier.h
@@ -0,0 +1,53 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_CRX_FILE_CRX_VERIFIER_H_
+#define COMPONENTS_CRX_FILE_CRX_VERIFIER_H_
+
+#include <stdint.h>
+#include <string>
+#include <vector>
+
+namespace base {
+class FilePath;
+} // namespace base
+
+namespace crx_file {
+
+enum class VerifierFormat {
+ CRX2_OR_CRX3, // Accept Crx2 or Crx3.
+ CRX3, // Accept only Crx3.
+ CRX3_WITH_PUBLISHER_PROOF, // Accept only Crx3 with a publisher proof.
+};
+
+enum class VerifierResult {
+ OK_FULL, // The file verifies as a correct full CRX file.
+ OK_DELTA, // The file verifies as a correct differential CRX file.
+ ERROR_FILE_NOT_READABLE, // Cannot open the CRX file.
+ ERROR_HEADER_INVALID, // Failed to parse or understand CRX header.
+ ERROR_EXPECTED_HASH_INVALID, // Expected hash is not well-formed.
+ ERROR_FILE_HASH_FAILED, // The file's actual hash != the expected hash.
+ ERROR_SIGNATURE_INITIALIZATION_FAILED, // A signature or key is malformed.
+ ERROR_SIGNATURE_VERIFICATION_FAILED, // A signature doesn't match.
+ ERROR_REQUIRED_PROOF_MISSING, // RequireKeyProof was unsatisfied.
+};
+
+// Verify the file at |crx_path| as a valid Crx of |format|. The Crx must be
+// well-formed, contain no invalid proofs, match the |required_file_hash| (if
+// non-empty), and contain a proof with each of the |required_key_hashes|.
+// If and only if this function returns OK_FULL or OK_DELTA, and only if
+// |public_key| / |crx_id| are non-null, they will be updated to contain the
+// public key (PEM format, without the header/footer) and crx id (encoded in
+// base16 using the characters [a-p]).
+VerifierResult Verify(
+ const base::FilePath& crx_path,
+ const VerifierFormat& format,
+ const std::vector<std::vector<uint8_t>>& required_key_hashes,
+ const std::vector<uint8_t>& required_file_hash,
+ std::string* public_key,
+ std::string* crx_id);
+
+} // namespace crx_file
+
+#endif // COMPONENTS_CRX_FILE_CRX_VERIFIER_H_
diff --git a/chromium/components/crx_file/crx_verifier_unittest.cc b/chromium/components/crx_file/crx_verifier_unittest.cc
new file mode 100644
index 00000000000..d5bd5a88296
--- /dev/null
+++ b/chromium/components/crx_file/crx_verifier_unittest.cc
@@ -0,0 +1,196 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/crx_file/crx_verifier.h"
+#include "base/base_paths.h"
+#include "base/files/file_path.h"
+#include "base/path_service.h"
+#include "base/strings/string_number_conversions.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+
+base::FilePath TestFile(const std::string& file) {
+ base::FilePath path;
+ PathService::Get(base::DIR_SOURCE_ROOT, &path);
+ return path.AppendASCII("components")
+ .AppendASCII("test")
+ .AppendASCII("data")
+ .AppendASCII("crx_file")
+ .AppendASCII(file);
+}
+
+constexpr char kOjjHash[] = "ojjgnpkioondelmggbekfhllhdaimnho";
+constexpr char kOjjKey[] =
+ "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA230uN7vYDEhdDlb4/"
+ "+pg2pfL8p0FFzCF/O146NB3D5dPKuLbnNphn0OUzOrDzR/Z1XLVDlDyiA6xnb+qeRp7H8n7Wk/"
+ "/gvVDNArZyForlVqWdaHLhl4dyZoNJwPKsggf30p/"
+ "MxCbNfy2rzFujzn2nguOrJKzWvNt0BFqssrBpzOQl69blBezE2ZYGOnYW8mPgQV29ekIgOfJk2"
+ "GgXoJBQQRRsjoPmUY7GDuEKudEB/"
+ "CmWh3+"
+ "mCsHBHFWbqtGhSN4YCAw3DYQzwdTcIVaIA8f2Uo4AZ4INKkrEPRL8o9mZDYtO2YHIQg8pMSRMa"
+ "6AawBNYi9tZScnmgl5L1qE6z5oIwIDAQAB";
+
+} // namespace
+
+namespace crx_file {
+
+using CrxVerifierTest = testing::Test;
+
+TEST_F(CrxVerifierTest, ValidFullCrx2) {
+ const std::vector<std::vector<uint8_t>> keys;
+ const std::vector<uint8_t> hash;
+ std::string public_key;
+ std::string crx_id;
+
+ EXPECT_EQ(VerifierResult::OK_FULL,
+ Verify(TestFile("valid.crx2"), VerifierFormat::CRX2_OR_CRX3, keys,
+ hash, &public_key, &crx_id));
+ EXPECT_EQ(std::string(kOjjHash), crx_id);
+ EXPECT_EQ(std::string(kOjjKey), public_key);
+}
+
+TEST_F(CrxVerifierTest, ValidFullCrx3) {
+ const std::vector<std::vector<uint8_t>> keys;
+ const std::vector<uint8_t> hash;
+ std::string public_key = "UNSET";
+ std::string crx_id = "UNSET";
+
+ EXPECT_EQ(VerifierResult::OK_FULL, Verify(TestFile("valid_no_publisher.crx3"),
+ VerifierFormat::CRX2_OR_CRX3, keys,
+ hash, &public_key, &crx_id));
+ EXPECT_EQ(std::string(kOjjHash), crx_id);
+ EXPECT_EQ(std::string(kOjjKey), public_key);
+
+ public_key = "UNSET";
+ crx_id = "UNSET";
+ EXPECT_EQ(VerifierResult::OK_FULL,
+ Verify(TestFile("valid_no_publisher.crx3"), VerifierFormat::CRX3,
+ keys, hash, &public_key, &crx_id));
+ EXPECT_EQ(std::string(kOjjHash), crx_id);
+ EXPECT_EQ(std::string(kOjjKey), public_key);
+}
+
+TEST_F(CrxVerifierTest, Crx3RejectsCrx2) {
+ const std::vector<std::vector<uint8_t>> keys;
+ const std::vector<uint8_t> hash;
+ std::string public_key = "UNSET";
+ std::string crx_id = "UNSET";
+
+ EXPECT_EQ(VerifierResult::ERROR_HEADER_INVALID,
+ Verify(TestFile("valid.crx2"), VerifierFormat::CRX3, keys, hash,
+ &public_key, &crx_id));
+ EXPECT_EQ("UNSET", crx_id);
+ EXPECT_EQ("UNSET", public_key);
+}
+
+TEST_F(CrxVerifierTest, VerifiesFileHash) {
+ const std::vector<std::vector<uint8_t>> keys;
+ std::vector<uint8_t> hash;
+ EXPECT_TRUE(base::HexStringToBytes(
+ "d033c510f9e4ee081ccb60ea2bf530dc2e5cb0e71085b55503c8b13b74515fe4",
+ &hash));
+ std::string public_key = "UNSET";
+ std::string crx_id = "UNSET";
+
+ EXPECT_EQ(VerifierResult::OK_FULL, Verify(TestFile("valid_no_publisher.crx3"),
+ VerifierFormat::CRX2_OR_CRX3, keys,
+ hash, &public_key, &crx_id));
+ EXPECT_EQ(std::string(kOjjHash), crx_id);
+ EXPECT_EQ(std::string(kOjjKey), public_key);
+
+ hash.clear();
+ EXPECT_TRUE(base::HexStringToBytes(std::string(32, '0'), &hash));
+ public_key = "UNSET";
+ crx_id = "UNSET";
+ EXPECT_EQ(VerifierResult::ERROR_EXPECTED_HASH_INVALID,
+ Verify(TestFile("valid_no_publisher.crx3"), VerifierFormat::CRX3,
+ keys, hash, &public_key, &crx_id));
+ EXPECT_EQ("UNSET", crx_id);
+ EXPECT_EQ("UNSET", public_key);
+
+ hash.clear();
+ EXPECT_TRUE(base::HexStringToBytes(std::string(64, '0'), &hash));
+ public_key = "UNSET";
+ crx_id = "UNSET";
+ EXPECT_EQ(VerifierResult::ERROR_FILE_HASH_FAILED,
+ Verify(TestFile("valid_no_publisher.crx3"), VerifierFormat::CRX3,
+ keys, hash, &public_key, &crx_id));
+ EXPECT_EQ("UNSET", crx_id);
+ EXPECT_EQ("UNSET", public_key);
+}
+
+TEST_F(CrxVerifierTest, ChecksRequiredKeyHashes) {
+ const std::vector<uint8_t> hash;
+
+ std::vector<uint8_t> good_key;
+ EXPECT_TRUE(base::HexStringToBytes(
+ "e996dfa8eed34bc6614a57bb7308cd7e519bcc690841e1969f7cb173ef16800a",
+ &good_key));
+ const std::vector<std::vector<uint8_t>> good_keys = {good_key};
+ std::string public_key = "UNSET";
+ std::string crx_id = "UNSET";
+ EXPECT_EQ(
+ VerifierResult::OK_FULL,
+ Verify(TestFile("valid_no_publisher.crx3"), VerifierFormat::CRX2_OR_CRX3,
+ good_keys, hash, &public_key, &crx_id));
+ EXPECT_EQ(std::string(kOjjHash), crx_id);
+ EXPECT_EQ(std::string(kOjjKey), public_key);
+
+ std::vector<uint8_t> bad_key;
+ EXPECT_TRUE(base::HexStringToBytes(std::string(64, '0'), &bad_key));
+ const std::vector<std::vector<uint8_t>> bad_keys = {bad_key};
+ public_key = "UNSET";
+ crx_id = "UNSET";
+ EXPECT_EQ(VerifierResult::ERROR_REQUIRED_PROOF_MISSING,
+ Verify(TestFile("valid_no_publisher.crx3"), VerifierFormat::CRX3,
+ bad_keys, hash, &public_key, &crx_id));
+ EXPECT_EQ("UNSET", crx_id);
+ EXPECT_EQ("UNSET", public_key);
+}
+
+TEST_F(CrxVerifierTest, ChecksPinnedKey) {
+ const std::vector<uint8_t> hash;
+ const std::vector<std::vector<uint8_t>> keys;
+ std::string public_key = "UNSET";
+ std::string crx_id = "UNSET";
+ EXPECT_EQ(VerifierResult::OK_FULL,
+ Verify(TestFile("valid_publisher.crx3"),
+ VerifierFormat::CRX3_WITH_PUBLISHER_PROOF, keys, hash,
+ &public_key, &crx_id));
+ EXPECT_EQ(std::string(kOjjHash), crx_id);
+ EXPECT_EQ(std::string(kOjjKey), public_key);
+
+ public_key = "UNSET";
+ crx_id = "UNSET";
+ EXPECT_EQ(VerifierResult::ERROR_REQUIRED_PROOF_MISSING,
+ Verify(TestFile("valid_no_publisher.crx3"),
+ VerifierFormat::CRX3_WITH_PUBLISHER_PROOF, keys, hash,
+ &public_key, &crx_id));
+ EXPECT_EQ("UNSET", crx_id);
+ EXPECT_EQ("UNSET", public_key);
+}
+
+TEST_F(CrxVerifierTest, NullptrSafe) {
+ const std::vector<uint8_t> hash;
+ const std::vector<std::vector<uint8_t>> keys;
+ EXPECT_EQ(VerifierResult::OK_FULL,
+ Verify(TestFile("valid_publisher.crx3"),
+ VerifierFormat::CRX3_WITH_PUBLISHER_PROOF, keys, hash,
+ nullptr, nullptr));
+}
+
+TEST_F(CrxVerifierTest, RequiresDeveloperKey) {
+ const std::vector<uint8_t> hash;
+ const std::vector<std::vector<uint8_t>> keys;
+ std::string public_key = "UNSET";
+ std::string crx_id = "UNSET";
+ EXPECT_EQ(VerifierResult::ERROR_REQUIRED_PROOF_MISSING,
+ Verify(TestFile("unsigned.crx3"), VerifierFormat::CRX2_OR_CRX3,
+ keys, hash, &public_key, &crx_id));
+ EXPECT_EQ("UNSET", crx_id);
+ EXPECT_EQ("UNSET", public_key);
+}
+
+} // namespace crx_file