1// Copyright 2014 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "extensions/browser/verified_contents.h"
6
7#include "base/base64.h"
8#include "base/file_util.h"
9#include "base/json/json_reader.h"
10#include "base/strings/string_util.h"
11#include "base/values.h"
12#include "crypto/signature_verifier.h"
13#include "extensions/common/extension.h"
14
15using base::DictionaryValue;
16using base::ListValue;
17using base::Value;
18
19namespace {
20
21// Note: this structure is an ASN.1 which encodes the algorithm used with its
22// parameters.  The signature algorithm is "RSA256" aka "RSASSA-PKCS-v1_5 using
23// SHA-256 hash algorithm". This is defined in PKCS #1 (RFC 3447).
24// It is encoding: { OID sha256WithRSAEncryption      PARAMETERS NULL }
25const uint8 kSignatureAlgorithm[15] = {0x30, 0x0d, 0x06, 0x09, 0x2a,
26                                       0x86, 0x48, 0x86, 0xf7, 0x0d,
27                                       0x01, 0x01, 0x0b, 0x05, 0x00};
28
29const char kBlockSizeKey[] = "block_size";
30const char kContentHashesKey[] = "content_hashes";
31const char kDescriptionKey[] = "description";
32const char kFilesKey[] = "files";
33const char kFormatKey[] = "format";
34const char kHashBlockSizeKey[] = "hash_block_size";
35const char kHeaderKidKey[] = "header.kid";
36const char kItemIdKey[] = "item_id";
37const char kItemVersionKey[] = "item_version";
38const char kPathKey[] = "path";
39const char kPayloadKey[] = "payload";
40const char kProtectedKey[] = "protected";
41const char kRootHashKey[] = "root_hash";
42const char kSignatureKey[] = "signature";
43const char kSignaturesKey[] = "signatures";
44const char kSignedContentKey[] = "signed_content";
45const char kTreeHashPerFile[] = "treehash per file";
46const char kTreeHash[] = "treehash";
47const char kWebstoreKId[] = "webstore";
48
49// Helper function to iterate over a list of dictionaries, returning the
50// dictionary that has |key| -> |value| in it, if any, or NULL.
51DictionaryValue* FindDictionaryWithValue(const ListValue* list,
52                                         std::string key,
53                                         std::string value) {
54  for (ListValue::const_iterator i = list->begin(); i != list->end(); ++i) {
55    if (!(*i)->IsType(Value::TYPE_DICTIONARY))
56      continue;
57    DictionaryValue* dictionary = static_cast<DictionaryValue*>(*i);
58    std::string found_value;
59    if (dictionary->GetString(key, &found_value) && found_value == value)
60      return dictionary;
61  }
62  return NULL;
63}
64
65}  // namespace
66
67namespace extensions {
68
69// static
70bool VerifiedContents::FixupBase64Encoding(std::string* input) {
71  for (std::string::iterator i = input->begin(); i != input->end(); ++i) {
72    if (*i == '-')
73      *i = '+';
74    else if (*i == '_')
75      *i = '/';
76  }
77  switch (input->size() % 4) {
78    case 0:
79      break;
80    case 2:
81      input->append("==");
82      break;
83    case 3:
84      input->append("=");
85      break;
86    default:
87      return false;
88  }
89  return true;
90}
91
92VerifiedContents::VerifiedContents(const uint8* public_key, int public_key_size)
93    : public_key_(public_key),
94      public_key_size_(public_key_size),
95      valid_signature_(false),  // Guilty until proven innocent.
96      block_size_(0) {
97}
98
99VerifiedContents::~VerifiedContents() {
100}
101
102// The format of the payload json is:
103// {
104//   "item_id": "<extension id>",
105//   "item_version": "<extension version>",
106//   "content_hashes": [
107//     {
108//       "block_size": 4096,
109//       "hash_block_size": 4096,
110//       "format": "treehash",
111//       "files": [
112//         {
113//           "path": "foo/bar",
114//           "root_hash": "<base64url encoded bytes>"
115//         },
116//         ...
117//       ]
118//     }
119//   ]
120// }
121bool VerifiedContents::InitFrom(const base::FilePath& path,
122                                bool ignore_invalid_signature) {
123  std::string payload;
124  if (!GetPayload(path, &payload, ignore_invalid_signature))
125    return false;
126
127  scoped_ptr<base::Value> value(base::JSONReader::Read(payload));
128  if (!value.get() || !value->IsType(Value::TYPE_DICTIONARY))
129    return false;
130  DictionaryValue* dictionary = static_cast<DictionaryValue*>(value.get());
131
132  std::string item_id;
133  if (!dictionary->GetString(kItemIdKey, &item_id) ||
134      !Extension::IdIsValid(item_id))
135    return false;
136  extension_id_ = item_id;
137
138  std::string version_string;
139  if (!dictionary->GetString(kItemVersionKey, &version_string))
140    return false;
141  version_ = base::Version(version_string);
142  if (!version_.IsValid())
143    return false;
144
145  ListValue* hashes_list = NULL;
146  if (!dictionary->GetList(kContentHashesKey, &hashes_list))
147    return false;
148
149  for (size_t i = 0; i < hashes_list->GetSize(); i++) {
150    DictionaryValue* hashes = NULL;
151    if (!hashes_list->GetDictionary(i, &hashes))
152      return false;
153    std::string format;
154    if (!hashes->GetString(kFormatKey, &format) || format != kTreeHash)
155      continue;
156
157    int block_size = 0;
158    int hash_block_size = 0;
159    if (!hashes->GetInteger(kBlockSizeKey, &block_size) ||
160        !hashes->GetInteger(kHashBlockSizeKey, &hash_block_size))
161      return false;
162    block_size_ = block_size;
163
164    // We don't support using a different block_size and hash_block_size at
165    // the moment.
166    if (block_size_ != hash_block_size)
167      return false;
168
169    ListValue* files = NULL;
170    if (!hashes->GetList(kFilesKey, &files))
171      return false;
172
173    for (size_t j = 0; j < files->GetSize(); j++) {
174      DictionaryValue* data = NULL;
175      if (!files->GetDictionary(j, &data))
176        return false;
177      std::string file_path_string;
178      std::string encoded_root_hash;
179      std::string root_hash;
180      if (!data->GetString(kPathKey, &file_path_string) ||
181          !base::IsStringUTF8(file_path_string) ||
182          !data->GetString(kRootHashKey, &encoded_root_hash) ||
183          !FixupBase64Encoding(&encoded_root_hash) ||
184          !base::Base64Decode(encoded_root_hash, &root_hash))
185        return false;
186      base::FilePath file_path =
187          base::FilePath::FromUTF8Unsafe(file_path_string);
188#if defined(FILE_PATH_USES_WIN_SEPARATORS)
189      file_path = file_path.NormalizePathSeparators();
190#endif  // defined(FILE_PATH_USES_WIN_SEPARATORS)
191      root_hashes_[file_path] = std::string();
192      root_hashes_[file_path].swap(root_hash);
193    }
194
195    break;
196  }
197  return true;
198}
199
200const std::string* VerifiedContents::GetTreeHashRoot(
201    const base::FilePath& relative_path) {
202  std::map<base::FilePath, std::string>::const_iterator i =
203      root_hashes_.find(relative_path);
204  if (i == root_hashes_.end())
205    return NULL;
206  return &i->second;
207}
208
209// We're loosely following the "JSON Web Signature" draft spec for signing
210// a JSON payload:
211//
212//   http://tools.ietf.org/html/draft-ietf-jose-json-web-signature-26
213//
214// The idea is that you have some JSON that you want to sign, so you
215// base64-encode that and put it as the "payload" field in a containing
216// dictionary. There might be signatures of it done with multiple
217// algorithms/parameters, so the payload is followed by a list of one or more
218// signature sections. Each signature section specifies the
219// algorithm/parameters in a JSON object which is base64url encoded into one
220// string and put into a "protected" field in the signature. Then the encoded
221// "payload" and "protected" strings are concatenated with a "." in between
222// them and those bytes are signed and the resulting signature is base64url
223// encoded and placed in the "signature" field. To allow for extensibility, we
224// wrap this, so we can include additional kinds of payloads in the future. E.g.
225// [
226//   {
227//     "description": "treehash per file",
228//     "signed_content": {
229//       "payload": "<base64url encoded JSON to sign>",
230//       "signatures": [
231//         {
232//           "protected": "<base64url encoded JSON with algorithm/parameters>",
233//           "header": {
234//             <object with metadata about this signature, eg a key identifier>
235//           }
236//           "signature":
237//              "<base64url encoded signature over payload || . || protected>"
238//         },
239//         ... <zero or more additional signatures> ...
240//       ]
241//     }
242//   }
243// ]
244// There might be both a signature generated with a webstore private key and a
245// signature generated with the extension's private key - for now we only
246// verify the webstore one (since the id is in the payload, so we can trust
247// that it is for a given extension), but in the future we may validate using
248// the extension's key too (eg for non-webstore hosted extensions such as
249// enterprise installs).
250bool VerifiedContents::GetPayload(const base::FilePath& path,
251                                  std::string* payload,
252                                  bool ignore_invalid_signature) {
253  std::string contents;
254  if (!base::ReadFileToString(path, &contents))
255    return false;
256  scoped_ptr<base::Value> value(base::JSONReader::Read(contents));
257  if (!value.get() || !value->IsType(Value::TYPE_LIST))
258    return false;
259  ListValue* top_list = static_cast<ListValue*>(value.get());
260
261  // Find the "treehash per file" signed content, e.g.
262  // [
263  //   {
264  //     "description": "treehash per file",
265  //     "signed_content": {
266  //       "signatures": [ ... ],
267  //       "payload": "..."
268  //     }
269  //   }
270  // ]
271  DictionaryValue* dictionary =
272      FindDictionaryWithValue(top_list, kDescriptionKey, kTreeHashPerFile);
273  DictionaryValue* signed_content = NULL;
274  if (!dictionary ||
275      !dictionary->GetDictionaryWithoutPathExpansion(kSignedContentKey,
276                                                     &signed_content)) {
277    return false;
278  }
279
280  ListValue* signatures = NULL;
281  if (!signed_content->GetList(kSignaturesKey, &signatures))
282    return false;
283
284  DictionaryValue* signature_dict =
285      FindDictionaryWithValue(signatures, kHeaderKidKey, kWebstoreKId);
286  if (!signature_dict)
287    return false;
288
289  std::string protected_value;
290  std::string encoded_signature;
291  std::string decoded_signature;
292  if (!signature_dict->GetString(kProtectedKey, &protected_value) ||
293      !signature_dict->GetString(kSignatureKey, &encoded_signature) ||
294      !FixupBase64Encoding(&encoded_signature) ||
295      !base::Base64Decode(encoded_signature, &decoded_signature))
296    return false;
297
298  std::string encoded_payload;
299  if (!signed_content->GetString(kPayloadKey, &encoded_payload))
300    return false;
301
302  valid_signature_ =
303      VerifySignature(protected_value, encoded_payload, decoded_signature);
304  if (!valid_signature_ && !ignore_invalid_signature)
305    return false;
306
307  if (!FixupBase64Encoding(&encoded_payload) ||
308      !base::Base64Decode(encoded_payload, payload))
309    return false;
310
311  return true;
312}
313
314bool VerifiedContents::VerifySignature(const std::string& protected_value,
315                                       const std::string& payload,
316                                       const std::string& signature_bytes) {
317  crypto::SignatureVerifier signature_verifier;
318  if (!signature_verifier.VerifyInit(
319          kSignatureAlgorithm,
320          sizeof(kSignatureAlgorithm),
321          reinterpret_cast<const uint8*>(signature_bytes.data()),
322          signature_bytes.size(),
323          public_key_,
324          public_key_size_)) {
325    VLOG(1) << "Could not verify signature - VerifyInit failure";
326    return false;
327  }
328
329  signature_verifier.VerifyUpdate(
330      reinterpret_cast<const uint8*>(protected_value.data()),
331      protected_value.size());
332
333  std::string dot(".");
334  signature_verifier.VerifyUpdate(reinterpret_cast<const uint8*>(dot.data()),
335                                  dot.size());
336
337  signature_verifier.VerifyUpdate(
338      reinterpret_cast<const uint8*>(payload.data()), payload.size());
339
340  if (!signature_verifier.VerifyFinal()) {
341    VLOG(1) << "Could not verify signature - VerifyFinal failure";
342    return false;
343  }
344  return true;
345}
346
347}  // namespace extensions
348