Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(754)

Side by Side Diff: extensions/browser/verified_contents.cc

Issue 278593005: Extension content verification: new class for parsing data from store (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Created 6 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
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_number_conversions.h"
11 #include "base/values.h"
12 #include "crypto/signature_verifier.h"
13 #include "extensions/common/extension.h"
14
15 using base::DictionaryValue;
16 using base::ListValue;
17 using base::Value;
18
19 namespace {
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 }
25 const uint8 kSignatureAlgorithm[15] = {0x30, 0x0d, 0x06, 0x09, 0x2a,
26 0x86, 0x48, 0x86, 0xf7, 0x0d,
27 0x01, 0x01, 0x0b, 0x05, 0x00};
28
29 const char kBlockSizeKey[] = "block_size";
30 const char kContentHashesKey[] = "content_hashes";
31 const char kFilesKey[] = "files";
32 const char kFormatKey[] = "format";
33 const char kHashBlockSizeKey[] = "hash_block_size";
34 const char kHeaderKidKey[] = "header.kid";
35 const char kItemIdKey[] = "item_id";
36 const char kItemVersionKey[] = "item_version";
37 const char kPathKey[] = "path";
38 const char kPayloadKey[] = "payload";
39 const char kProtectedKey[] = "protected";
40 const char kRootHashKey[] = "root_hash";
41 const char kSignatureKey[] = "signature";
42 const char kSignaturesKey[] = "signatures";
43 const char kTreeHash[] = "treehash";
44 const char kWebstoreKId[] = "webstore";
45
46 // This function fixes up a string in base64url encoding to be in standard
47 // base64.
48 //
49 // The JSON signing spec we're following uses "base64url" encoding (RFC 4648
50 // section 5 without padding). The slight differences from regular base64
51 // encoding are:
52 // 1. uses '_' instead of '/'
53 // 2. uses '-' instead of '+'
54 // 3. omits trailing '=' padding
55 bool FixupBase64Encoding(std::string* input) {
56 for (std::string::iterator i = input->begin(); i != input->end(); ++i) {
57 if (*i == '-')
58 *i = '+';
59 else if (*i == '_')
60 *i = '/';
61 }
62 switch (input->size() % 4) {
63 case 0:
64 break;
65 case 2:
66 input->append("==");
67 break;
68 case 3:
69 input->append("=");
70 break;
71 default:
72 return false;
73 }
74 return true;
75 }
76
77 } // namespace
78
79 namespace extensions {
80
81 VerifiedContents::VerifiedContents(const uint8* public_key, int public_key_size)
82 : public_key_(public_key),
83 public_key_size_(public_key_size),
84 valid_signature_(false), // Guilty until proven innocent.
85 block_size_(0) {
86 }
87
88 VerifiedContents::~VerifiedContents() {
89 }
90
91 // The format of the payload json is:
92 // {
93 // "content_hashes": [
94 // {
95 // "block_size": 4096,
96 // "hash_block_size": 4096,
97 // "format": "treehash",
98 // "files": [
99 // {
100 // "path": "foo/bar",
101 // "root_hash": "<hex encoded bytes>"
102 // },
103 // ...
104 // ]
105 // }
106 // ]
107 // }
108 bool VerifiedContents::InitFrom(const base::FilePath& path,
109 bool ignore_invalid_signature) {
110 std::string payload;
111 if (!GetPayload(path, &payload, ignore_invalid_signature))
112 return false;
113
114 scoped_ptr<base::Value> value(base::JSONReader::Read(payload));
115 if (!value.get() || !value->IsType(Value::TYPE_DICTIONARY))
116 return false;
117 DictionaryValue* dictionary = static_cast<DictionaryValue*>(value.get());
118
119 std::string item_id;
120 if (!dictionary->GetString(kItemIdKey, &item_id) ||
121 !Extension::IdIsValid(item_id))
122 return false;
123 extension_id_ = item_id;
124
125 std::string version_string;
126 if (!dictionary->GetString(kItemVersionKey, &version_string))
127 return false;
128 version_ = base::Version(version_string);
129 if (!version_.IsValid())
130 return false;
131
132 ListValue* hashes_list = NULL;
133 if (!dictionary->GetList(kContentHashesKey, &hashes_list))
134 return false;
135
136 for (size_t i = 0; i < hashes_list->GetSize(); i++) {
137 DictionaryValue* hashes = NULL;
138 if (!hashes_list->GetDictionary(i, &hashes))
139 return false;
140 std::string format;
141 if (!hashes->GetString(kFormatKey, &format) || format != kTreeHash)
142 continue;
143
144 int block_size = 0;
145 int hash_block_size = 0;
146 if (!hashes->GetInteger(kBlockSizeKey, &block_size) ||
147 !hashes->GetInteger(kHashBlockSizeKey, &hash_block_size))
148 return false;
149 block_size_ = block_size;
150
151 // We don't support using a different block_size and hash_block_size at
152 // the moment.
153 if (block_size_ != hash_block_size)
154 return false;
155
156 ListValue* files = NULL;
157 if (!hashes->GetList(kFilesKey, &files))
158 return false;
159
160 for (size_t j = 0; j < files->GetSize(); j++) {
161 DictionaryValue* data = NULL;
162 if (!files->GetDictionary(j, &data))
163 return false;
164 std::string file_path_string;
165 std::string encoded_root_hash;
166 std::vector<uint8> root_hash;
167 if (!data->GetString(kPathKey, &file_path_string) ||
168 !data->GetString(kRootHashKey, &encoded_root_hash) ||
169 !base::HexStringToBytes(encoded_root_hash, &root_hash))
170 return false;
171 base::FilePath file_path(file_path_string);
172 root_hashes_[file_path] = std::string();
173 root_hashes_[file_path].append(root_hash.begin(), root_hash.end());
174 }
175
176 break;
177 }
178 return true;
179 }
180
181 const std::string* VerifiedContents::GetTreeHashRoot(
182 const base::FilePath& relative_path) {
183 std::map<base::FilePath, std::string>::const_iterator i =
184 root_hashes_.find(relative_path);
185 if (i == root_hashes_.end())
186 return NULL;
187 return &i->second;
188 }
189
190 // We're loosely following the "JSON Web Signature" draft spec for signing
191 // a JSON payload:
192 //
193 // http://tools.ietf.org/html/draft-ietf-jose-json-web-signature-26
194 //
195 // The idea is that you have some JSON that you want to sign, so you
196 // base64-encode that and put it as the "payload" field in a containing
197 // dictionary. There might be signatures of it done with multiple
198 // alrogirhtms/parameters, so the payload is followed by a list of one or more
199 // signature sections. Each signature section specifies the
200 // algorithm/parameters in a JSON object which is base64url encoded into one
201 // string and put into a "protected" field in the signature. Then the encoded
202 // "payload" and "protected" strings are concatenated with a "." in between
203 // them and those bytes are signed and the resulting signature is base64url
204 // encoded and placed in the "signature" field. E.g.
205 // {
206 // "payload": "<base64url encoded JSON to sign>",
207 // "signatures": [
208 // {
209 // "protected": "<base64url encoded JSON with algorithm/parameters>",
210 // "header": {
211 // <object with metadata about this signature, eg a key identifier>
212 // }
213 // "signature":
214 // "<base64url encoded signature done over payload || . || protected>"
215 // },
216 // ... <zero or more additional signatures> ...
217 // ]
218 // }
219 //
220 // There might be both a signature generated with a webstore private key and a
221 // signature generated with the extension's private key - for now we only
222 // verify the webstore one (since the id is in the payload, so we can trust
223 // that it is for a given extension), but in the future we may validate using
224 // the extension's key too (eg for non-webstore hosted extensions such as
225 // enterprise installs).
226 bool VerifiedContents::GetPayload(const base::FilePath& path,
227 std::string* payload,
228 bool ignore_invalid_signature) {
229 std::string contents;
230 if (!base::ReadFileToString(path, &contents))
231 return false;
232 scoped_ptr<base::Value> value(base::JSONReader::Read(contents));
233 if (!value.get() || !value->IsType(Value::TYPE_DICTIONARY))
234 return false;
235 DictionaryValue* dictionary = static_cast<DictionaryValue*>(value.get());
236
237 ListValue* signatures = NULL;
238 if (!dictionary->GetList(kSignaturesKey, &signatures))
239 return false;
240
241 std::string protected_value;
242 std::string decoded_signature;
243 for (size_t i = 0; i < signatures->GetSize(); i++) {
244 DictionaryValue* signature_dict = NULL;
245 if (!signatures->GetDictionary(i, &signature_dict))
246 return false;
247 std::string kid;
248 if (!signature_dict->GetString(kHeaderKidKey, &kid))
249 continue;
250 if (kid == std::string(kWebstoreKId)) {
251 std::string encoded_signature;
252 if (!signature_dict->GetString(kProtectedKey, &protected_value) ||
253 !signature_dict->GetString(kSignatureKey, &encoded_signature) ||
254 !FixupBase64Encoding(&encoded_signature) ||
255 !base::Base64Decode(encoded_signature, &decoded_signature))
256 return false;
257 break;
258 }
259 }
260 std::string encoded_payload;
261 if (!dictionary->GetString(kPayloadKey, &encoded_payload))
262 return false;
263
264 valid_signature_ =
265 VerifySignature(protected_value, encoded_payload, decoded_signature);
266 if (!valid_signature_ && !ignore_invalid_signature)
267 return false;
268
269 if (!FixupBase64Encoding(&encoded_payload) ||
270 !base::Base64Decode(encoded_payload, payload))
271 return false;
272
273 return true;
274 }
275
276 bool VerifiedContents::VerifySignature(const std::string& protected_value,
277 const std::string& payload,
278 const std::string& signature_bytes) {
279 crypto::SignatureVerifier signature_verifier;
280 if (!signature_verifier.VerifyInit(
281 kSignatureAlgorithm,
282 sizeof(kSignatureAlgorithm),
283 reinterpret_cast<const uint8*>(signature_bytes.data()),
284 signature_bytes.size(),
285 public_key_,
286 public_key_size_)) {
287 VLOG(1) << "Could not verify signature - VerifyInit failure";
288 return false;
289 }
290
291 signature_verifier.VerifyUpdate(
292 reinterpret_cast<const uint8*>(protected_value.data()),
293 protected_value.size());
294
295 std::string dot(".");
296 signature_verifier.VerifyUpdate(reinterpret_cast<const uint8*>(dot.data()),
297 dot.size());
298
299 signature_verifier.VerifyUpdate(
300 reinterpret_cast<const uint8*>(payload.data()), payload.size());
301
302 if (!signature_verifier.VerifyFinal()) {
303 VLOG(1) << "Could not verify signature - VerifyFinal failure";
304 return false;
305 }
306 return true;
307 }
308
309 } // namespace extensions
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698