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.
5 #include "extensions/browser/computed_hashes.h"
7 #include "base/base64.h"
8 #include "base/file_util.h"
9 #include "base/files/file_path.h"
10 #include "base/json/json_reader.h"
11 #include "base/json/json_writer.h"
12 #include "base/stl_util.h"
13 #include "base/values.h"
14 #include "crypto/secure_hash.h"
15 #include "crypto/sha2.h"
18 const char kBlockHashesKey
[] = "block_hashes";
19 const char kBlockSizeKey
[] = "block_size";
20 const char kFileHashesKey
[] = "file_hashes";
21 const char kPathKey
[] = "path";
22 const char kVersionKey
[] = "version";
23 const int kVersion
= 2;
26 namespace extensions
{
28 ComputedHashes::Reader::Reader() {
31 ComputedHashes::Reader::~Reader() {
34 bool ComputedHashes::Reader::InitFromFile(const base::FilePath
& path
) {
36 if (!base::ReadFileToString(path
, &contents
))
39 base::DictionaryValue
* top_dictionary
= NULL
;
40 scoped_ptr
<base::Value
> value(base::JSONReader::Read(contents
));
41 if (!value
.get() || !value
->GetAsDictionary(&top_dictionary
))
44 // For now we don't support forwards or backwards compatability in the
45 // format, so we return false on version mismatch.
47 if (!top_dictionary
->GetInteger(kVersionKey
, &version
) || version
!= kVersion
)
50 base::ListValue
* all_hashes
= NULL
;
51 if (!top_dictionary
->GetList(kFileHashesKey
, &all_hashes
))
54 for (size_t i
= 0; i
< all_hashes
->GetSize(); i
++) {
55 base::DictionaryValue
* dictionary
= NULL
;
56 if (!all_hashes
->GetDictionary(i
, &dictionary
))
59 std::string relative_path_utf8
;
60 if (!dictionary
->GetString(kPathKey
, &relative_path_utf8
))
64 if (!dictionary
->GetInteger(kBlockSizeKey
, &block_size
))
66 if (block_size
<= 0 || ((block_size
% 1024) != 0)) {
67 LOG(ERROR
) << "Invalid block size: " << block_size
;
72 base::ListValue
* hashes_list
= NULL
;
73 if (!dictionary
->GetList(kBlockHashesKey
, &hashes_list
))
76 base::FilePath relative_path
=
77 base::FilePath::FromUTF8Unsafe(relative_path_utf8
);
78 relative_path
= relative_path
.NormalizePathSeparatorsTo('/');
80 data_
[relative_path
] = HashInfo(block_size
, std::vector
<std::string
>());
81 std::vector
<std::string
>* hashes
= &(data_
[relative_path
].second
);
83 for (size_t j
= 0; j
< hashes_list
->GetSize(); j
++) {
85 if (!hashes_list
->GetString(j
, &encoded
))
88 hashes
->push_back(std::string());
89 std::string
* decoded
= &hashes
->back();
90 if (!base::Base64Decode(encoded
, decoded
)) {
99 bool ComputedHashes::Reader::GetHashes(const base::FilePath
& relative_path
,
101 std::vector
<std::string
>* hashes
) {
102 base::FilePath path
= relative_path
.NormalizePathSeparatorsTo('/');
103 std::map
<base::FilePath
, HashInfo
>::iterator i
= data_
.find(path
);
104 if (i
== data_
.end())
106 HashInfo
& info
= i
->second
;
107 *block_size
= info
.first
;
108 *hashes
= info
.second
;
112 ComputedHashes::Writer::Writer() : file_list_(new base::ListValue
) {
115 ComputedHashes::Writer::~Writer() {
118 void ComputedHashes::Writer::AddHashes(const base::FilePath
& relative_path
,
120 const std::vector
<std::string
>& hashes
) {
121 base::DictionaryValue
* dict
= new base::DictionaryValue();
122 base::ListValue
* block_hashes
= new base::ListValue();
123 file_list_
->Append(dict
);
124 dict
->SetString(kPathKey
,
125 relative_path
.NormalizePathSeparatorsTo('/').AsUTF8Unsafe());
126 dict
->SetInteger(kBlockSizeKey
, block_size
);
127 dict
->Set(kBlockHashesKey
, block_hashes
);
129 for (std::vector
<std::string
>::const_iterator i
= hashes
.begin();
133 base::Base64Encode(*i
, &encoded
);
134 block_hashes
->AppendString(encoded
);
138 bool ComputedHashes::Writer::WriteToFile(const base::FilePath
& path
) {
140 base::DictionaryValue top_dictionary
;
141 top_dictionary
.SetInteger(kVersionKey
, kVersion
);
142 top_dictionary
.Set(kFileHashesKey
, file_list_
.release());
144 if (!base::JSONWriter::Write(&top_dictionary
, &json
))
146 int written
= base::WriteFile(path
, json
.data(), json
.size());
147 if (static_cast<unsigned>(written
) != json
.size()) {
148 LOG(ERROR
) << "Error writing " << path
.AsUTF8Unsafe()
149 << " ; write result:" << written
<< " expected:" << json
.size();
155 void ComputedHashes::ComputeHashesForContent(const std::string
& contents
,
157 std::vector
<std::string
>* hashes
) {
159 // Even when the contents is empty, we want to output at least one hash
160 // block (the hash of the empty string).
162 const char* block_start
= contents
.data() + offset
;
163 DCHECK(offset
<= contents
.size());
164 size_t bytes_to_read
= std::min(contents
.size() - offset
, block_size
);
165 scoped_ptr
<crypto::SecureHash
> hash(
166 crypto::SecureHash::Create(crypto::SecureHash::SHA256
));
167 hash
->Update(block_start
, bytes_to_read
);
169 hashes
->push_back(std::string());
170 std::string
* buffer
= &(hashes
->back());
171 buffer
->resize(crypto::kSHA256Length
);
172 hash
->Finish(string_as_array(buffer
), buffer
->size());
174 // If |contents| is empty, then we want to just exit here.
175 if (bytes_to_read
== 0)
178 offset
+= bytes_to_read
;
179 } while (offset
< contents
.size());
182 } // namespace extensions