summaryrefslogtreecommitdiffstats
path: root/components/crx_file/crx_file.cc
diff options
context:
space:
mode:
authorasargent <asargent@chromium.org>2015-07-27 14:22:32 -0700
committerCommit bot <commit-bot@chromium.org>2015-07-27 21:23:30 +0000
commit710893f766e00c8501d67e78a61cf6d882641f41 (patch)
tree05776b600180da3ac8a5ff911fe329e917806dcc /components/crx_file/crx_file.cc
parent3d7774c521d716ee91d2f41a1d2df53c1dbbe566 (diff)
downloadchromium_src-710893f766e00c8501d67e78a61cf6d882641f41.zip
chromium_src-710893f766e00c8501d67e78a61cf6d882641f41.tar.gz
chromium_src-710893f766e00c8501d67e78a61cf6d882641f41.tar.bz2
Move ValidateSignature logic for crx files to the CrxFile class
We were using the same logic with two different implementations in both extensions/ and components/update_client, so this lets us share the same code. BUG=490418 Review URL: https://codereview.chromium.org/1235593007 Cr-Commit-Position: refs/heads/master@{#340566}
Diffstat (limited to 'components/crx_file/crx_file.cc')
-rw-r--r--components/crx_file/crx_file.cc146
1 files changed, 143 insertions, 3 deletions
diff --git a/components/crx_file/crx_file.cc b/components/crx_file/crx_file.cc
index 391d549..b081fec 100644
--- a/components/crx_file/crx_file.cc
+++ b/components/crx_file/crx_file.cc
@@ -4,6 +4,20 @@
#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/memory/scoped_ptr.h"
+#include "base/numerics/safe_math.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/string_util.h"
+#include "components/crx_file/constants.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 {
@@ -20,6 +34,43 @@ static const uint32 kMaxPublicKeySize = 1 << 16;
// The maximum size the crx parser will tolerate for a signature.
static const uint32 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 output[crypto::kSHA256Length] = {};
+ hash->Finish(output, sizeof(output));
+ std::string hash_base64 =
+ base::StringToLowerASCII(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.
@@ -46,13 +97,102 @@ scoped_ptr<CrxFile> CrxFile::Create(const uint32 key_size,
return scoped_ptr<CrxFile>();
}
-CrxFile::CrxFile(const Header& header) : header_(header) {
-}
-
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"));
+ scoped_ptr<crypto::SecureHash> hash;
+ if (!expected_hash.empty())
+ hash.reset(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;
+ scoped_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> key(header.key_size);
+ len = ReadAndHash(&key.front(), sizeof(uint8), header.key_size, file.get(),
+ hash.get());
+ if (len != header.key_size)
+ return ValidateError::CRX_PUBLIC_KEY_INVALID;
+
+ std::vector<uint8> signature(header.signature_size);
+ len = ReadAndHash(&signature.front(), sizeof(uint8), header.signature_size,
+ file.get(), hash.get());
+ if (len < header.signature_size)
+ return ValidateError::CRX_SIGNATURE_INVALID;
+
+ crypto::SignatureVerifier verifier;
+ if (!verifier.VerifyInit(
+ crx_file::kSignatureAlgorithm, sizeof(crx_file::kSignatureAlgorithm),
+ &signature.front(), static_cast<int>(signature.size()), &key.front(),
+ static_cast<int>(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 buf[1 << 12] = {};
+ while ((len = ReadAndHash(buf, sizeof(buf[0]), arraysize(buf), file.get(),
+ hash.get())) > 0)
+ verifier.VerifyUpdate(buf, static_cast<int>(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;