diff options
author | asargent <asargent@chromium.org> | 2015-07-27 14:22:32 -0700 |
---|---|---|
committer | Commit bot <commit-bot@chromium.org> | 2015-07-27 21:23:30 +0000 |
commit | 710893f766e00c8501d67e78a61cf6d882641f41 (patch) | |
tree | 05776b600180da3ac8a5ff911fe329e917806dcc /components/crx_file/crx_file.cc | |
parent | 3d7774c521d716ee91d2f41a1d2df53c1dbbe566 (diff) | |
download | chromium_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.cc | 146 |
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; |