// Copyright 2014 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "extensions/browser/verified_contents.h" #include <stddef.h> #include <algorithm> #include <string_view> #include "base/base64url.h" #include "base/containers/contains.h" #include "base/files/file_util.h" #include "base/json/json_reader.h" #include "base/logging.h" #include "base/memory/ptr_util.h" #include "base/strings/string_util.h" #include "base/timer/elapsed_timer.h" #include "base/values.h" #include "build/build_config.h" #include "components/crx_file/id_util.h" #include "crypto/signature_verifier.h" #include "extensions/browser/content_verifier/content_verifier_utils.h" #include "extensions/common/extension.h" namespace { const char kBlockSizeKey[] = …; const char kContentHashesKey[] = …; const char kDescriptionKey[] = …; const char kFilesKey[] = …; const char kFormatKey[] = …; const char kHashBlockSizeKey[] = …; const char kHeaderKidKey[] = …; const char kItemIdKey[] = …; const char kItemVersionKey[] = …; const char kPathKey[] = …; const char kPayloadKey[] = …; const char kProtectedKey[] = …; const char kRootHashKey[] = …; const char kSignatureKey[] = …; const char kSignaturesKey[] = …; const char kSignedContentKey[] = …; const char kTreeHashPerFile[] = …; const char kTreeHash[] = …; const char kWebstoreKId[] = …; // Helper function to iterate over a list of dictionaries, returning the // dictionary that has |key| -> |value| in it, if any, or null. const base::Value::Dict* FindDictionaryWithValue(const base::Value::List& list, const std::string& key, const std::string& value) { … } } // namespace namespace extensions { VerifiedContents::VerifiedContents(base::span<const uint8_t> public_key) : … { … } VerifiedContents::~VerifiedContents() { … } // The format of the payload json is: // { // "item_id": "<extension id>", // "item_version": "<extension version>", // "content_hashes": [ // { // "block_size": 4096, // "hash_block_size": 4096, // "format": "treehash", // "files": [ // { // "path": "foo/bar", // "root_hash": "<base64url encoded bytes>" // }, // ... // ] // } // ] // } // static. std::unique_ptr<VerifiedContents> VerifiedContents::CreateFromFile( base::span<const uint8_t> public_key, const base::FilePath& path) { … } std::unique_ptr<VerifiedContents> VerifiedContents::Create( base::span<const uint8_t> public_key, std::string_view contents) { … } bool VerifiedContents::HasTreeHashRoot( const base::FilePath& relative_path) const { … } bool VerifiedContents::TreeHashRootEquals(const base::FilePath& relative_path, const std::string& expected) const { … } // We're loosely following the "JSON Web Signature" draft spec for signing // a JSON payload: // // http://tools.ietf.org/html/draft-ietf-jose-json-web-signature-26 // // The idea is that you have some JSON that you want to sign, so you // base64-encode that and put it as the "payload" field in a containing // dictionary. There might be signatures of it done with multiple // algorithms/parameters, so the payload is followed by a list of one or more // signature sections. Each signature section specifies the // algorithm/parameters in a JSON object which is base64url encoded into one // string and put into a "protected" field in the signature. Then the encoded // "payload" and "protected" strings are concatenated with a "." in between // them and those bytes are signed and the resulting signature is base64url // encoded and placed in the "signature" field. To allow for extensibility, we // wrap this, so we can include additional kinds of payloads in the future. E.g. // [ // { // "description": "treehash per file", // "signed_content": { // "payload": "<base64url encoded JSON to sign>", // "signatures": [ // { // "protected": "<base64url encoded JSON with algorithm/parameters>", // "header": { // <object with metadata about this signature, eg a key identifier> // } // "signature": // "<base64url encoded signature over payload || . || protected>" // }, // ... <zero or more additional signatures> ... // ] // } // } // ] // There might be both a signature generated with a webstore private key and a // signature generated with the extension's private key - for now we only // verify the webstore one (since the id is in the payload, so we can trust // that it is for a given extension), but in the future we may validate using // the extension's key too (eg for non-webstore hosted extensions such as // enterprise installs). bool VerifiedContents::GetPayload(std::string_view contents, std::string* payload) { … } bool VerifiedContents::VerifySignature(const std::string& protected_value, const std::string& payload, const std::string& signature_bytes) { … } bool VerifiedContents::TreeHashRootEqualsForCanonicalPath( const content_verifier_utils::CanonicalRelativePath& canonical_relative_path, const std::string& expected) const { … } } // namespace extensions