1 // Copyright 2013 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 "chrome/browser/profile_resetter/jtl_interpreter.h"
6
7 #include <numeric>
8
9 #include "base/memory/scoped_vector.h"
10 #include "base/strings/string_number_conversions.h"
11 #include "base/strings/string_util.h"
12 #include "chrome/browser/profile_resetter/jtl_foundation.h"
13 #include "crypto/hmac.h"
14 #include "crypto/sha2.h"
15 #include "net/base/registry_controlled_domains/registry_controlled_domain.h"
16 #include "url/gurl.h"
17
18 namespace {
19
20 class ExecutionContext;
21
22 // An operation in an interpreted program.
23 class Operation {
24 public:
~Operation()25 virtual ~Operation() {}
26 // Executes the operation on the specified context and instructs the context
27 // to continue execution with the next instruction if appropriate.
28 // Returns true if we should continue with any potential backtracking that
29 // needs to be done.
30 virtual bool Execute(ExecutionContext* context) = 0;
31 };
32
33 // An execution context of operations.
34 class ExecutionContext {
35 public:
36 // |input| is the root of a dictionary that stores the information the
37 // sentence is evaluated on.
ExecutionContext(const jtl_foundation::Hasher * hasher,const std::vector<Operation * > & sentence,const base::DictionaryValue * input,base::DictionaryValue * working_memory)38 ExecutionContext(const jtl_foundation::Hasher* hasher,
39 const std::vector<Operation*>& sentence,
40 const base::DictionaryValue* input,
41 base::DictionaryValue* working_memory)
42 : hasher_(hasher),
43 sentence_(sentence),
44 next_instruction_index_(0u),
45 working_memory_(working_memory),
46 error_(false) {
47 stack_.push_back(input);
48 }
~ExecutionContext()49 ~ExecutionContext() {}
50
51 // Returns true in case of success.
ContinueExecution()52 bool ContinueExecution() {
53 if (error_ || stack_.empty()) {
54 error_ = true;
55 return false;
56 }
57 if (next_instruction_index_ >= sentence_.size())
58 return true;
59
60 Operation* op = sentence_[next_instruction_index_];
61 next_instruction_index_++;
62 bool continue_traversal = op->Execute(this);
63 next_instruction_index_--;
64 return continue_traversal;
65 }
66
GetHash(const std::string & input)67 std::string GetHash(const std::string& input) {
68 return hasher_->GetHash(input);
69 }
70
71 // Calculates the |hash| of a string, integer or double |value|, and returns
72 // true. Returns false otherwise.
GetValueHash(const base::Value & value,std::string * hash)73 bool GetValueHash(const base::Value& value, std::string* hash) {
74 DCHECK(hash);
75 std::string value_as_string;
76 int tmp_int = 0;
77 double tmp_double = 0.0;
78 if (value.GetAsInteger(&tmp_int))
79 value_as_string = base::IntToString(tmp_int);
80 else if (value.GetAsDouble(&tmp_double))
81 value_as_string = base::DoubleToString(tmp_double);
82 else if (!value.GetAsString(&value_as_string))
83 return false;
84 *hash = GetHash(value_as_string);
85 return true;
86 }
87
current_node() const88 const base::Value* current_node() const { return stack_.back(); }
stack()89 std::vector<const base::Value*>* stack() { return &stack_; }
working_memory()90 base::DictionaryValue* working_memory() { return working_memory_; }
error() const91 bool error() const { return error_; }
92
93 private:
94 // A hasher used to hash node names in a dictionary.
95 const jtl_foundation::Hasher* hasher_;
96 // The sentence to be executed.
97 const std::vector<Operation*> sentence_;
98 // Position in |sentence_|.
99 size_t next_instruction_index_;
100 // A stack of Values, indicating a navigation path from the root node of
101 // |input| (see constructor) to the current node on which the
102 // sentence_[next_instruction_index_] is evaluated.
103 std::vector<const base::Value*> stack_;
104 // Memory into which values can be stored by the program.
105 base::DictionaryValue* working_memory_;
106 // Whether a runtime error occurred.
107 bool error_;
108 DISALLOW_COPY_AND_ASSIGN(ExecutionContext);
109 };
110
111 class NavigateOperation : public Operation {
112 public:
NavigateOperation(const std::string & hashed_key)113 explicit NavigateOperation(const std::string& hashed_key)
114 : hashed_key_(hashed_key) {}
~NavigateOperation()115 virtual ~NavigateOperation() {}
Execute(ExecutionContext * context)116 virtual bool Execute(ExecutionContext* context) OVERRIDE {
117 const base::DictionaryValue* dict = NULL;
118 if (!context->current_node()->GetAsDictionary(&dict)) {
119 // Just ignore this node gracefully as this navigation is a dead end.
120 // If this NavigateOperation occurred after a NavigateAny operation, those
121 // may still be fulfillable, so we allow continuing the execution of the
122 // sentence on other nodes.
123 return true;
124 }
125 for (base::DictionaryValue::Iterator i(*dict); !i.IsAtEnd(); i.Advance()) {
126 if (context->GetHash(i.key()) != hashed_key_)
127 continue;
128 context->stack()->push_back(&i.value());
129 bool continue_traversal = context->ContinueExecution();
130 context->stack()->pop_back();
131 if (!continue_traversal)
132 return false;
133 }
134 return true;
135 }
136
137 private:
138 std::string hashed_key_;
139 DISALLOW_COPY_AND_ASSIGN(NavigateOperation);
140 };
141
142 class NavigateAnyOperation : public Operation {
143 public:
NavigateAnyOperation()144 NavigateAnyOperation() {}
~NavigateAnyOperation()145 virtual ~NavigateAnyOperation() {}
Execute(ExecutionContext * context)146 virtual bool Execute(ExecutionContext* context) OVERRIDE {
147 const base::DictionaryValue* dict = NULL;
148 const base::ListValue* list = NULL;
149 if (context->current_node()->GetAsDictionary(&dict)) {
150 for (base::DictionaryValue::Iterator i(*dict);
151 !i.IsAtEnd(); i.Advance()) {
152 context->stack()->push_back(&i.value());
153 bool continue_traversal = context->ContinueExecution();
154 context->stack()->pop_back();
155 if (!continue_traversal)
156 return false;
157 }
158 } else if (context->current_node()->GetAsList(&list)) {
159 for (base::ListValue::const_iterator i = list->begin();
160 i != list->end(); ++i) {
161 context->stack()->push_back(*i);
162 bool continue_traversal = context->ContinueExecution();
163 context->stack()->pop_back();
164 if (!continue_traversal)
165 return false;
166 }
167 } else {
168 // Do nothing, just ignore this node.
169 }
170 return true;
171 }
172
173 private:
174 DISALLOW_COPY_AND_ASSIGN(NavigateAnyOperation);
175 };
176
177 class NavigateBackOperation : public Operation {
178 public:
NavigateBackOperation()179 NavigateBackOperation() {}
~NavigateBackOperation()180 virtual ~NavigateBackOperation() {}
Execute(ExecutionContext * context)181 virtual bool Execute(ExecutionContext* context) OVERRIDE {
182 const base::Value* current_node = context->current_node();
183 context->stack()->pop_back();
184 bool continue_traversal = context->ContinueExecution();
185 context->stack()->push_back(current_node);
186 return continue_traversal;
187 }
188
189 private:
190 DISALLOW_COPY_AND_ASSIGN(NavigateBackOperation);
191 };
192
193 class StoreValue : public Operation {
194 public:
StoreValue(const std::string & hashed_name,scoped_ptr<base::Value> value)195 StoreValue(const std::string& hashed_name, scoped_ptr<base::Value> value)
196 : hashed_name_(hashed_name),
197 value_(value.Pass()) {
198 DCHECK(base::IsStringUTF8(hashed_name));
199 DCHECK(value_);
200 }
~StoreValue()201 virtual ~StoreValue() {}
Execute(ExecutionContext * context)202 virtual bool Execute(ExecutionContext* context) OVERRIDE {
203 context->working_memory()->Set(hashed_name_, value_->DeepCopy());
204 return context->ContinueExecution();
205 }
206
207 private:
208 std::string hashed_name_;
209 scoped_ptr<base::Value> value_;
210 DISALLOW_COPY_AND_ASSIGN(StoreValue);
211 };
212
213 class CompareStoredValue : public Operation {
214 public:
CompareStoredValue(const std::string & hashed_name,scoped_ptr<base::Value> value,scoped_ptr<base::Value> default_value)215 CompareStoredValue(const std::string& hashed_name,
216 scoped_ptr<base::Value> value,
217 scoped_ptr<base::Value> default_value)
218 : hashed_name_(hashed_name),
219 value_(value.Pass()),
220 default_value_(default_value.Pass()) {
221 DCHECK(base::IsStringUTF8(hashed_name));
222 DCHECK(value_);
223 DCHECK(default_value_);
224 }
~CompareStoredValue()225 virtual ~CompareStoredValue() {}
Execute(ExecutionContext * context)226 virtual bool Execute(ExecutionContext* context) OVERRIDE {
227 const base::Value* actual_value = NULL;
228 if (!context->working_memory()->Get(hashed_name_, &actual_value))
229 actual_value = default_value_.get();
230 if (!value_->Equals(actual_value))
231 return true;
232 return context->ContinueExecution();
233 }
234
235 private:
236 std::string hashed_name_;
237 scoped_ptr<base::Value> value_;
238 scoped_ptr<base::Value> default_value_;
239 DISALLOW_COPY_AND_ASSIGN(CompareStoredValue);
240 };
241
242 template<bool ExpectedTypeIsBooleanNotHashable>
243 class StoreNodeValue : public Operation {
244 public:
StoreNodeValue(const std::string & hashed_name)245 explicit StoreNodeValue(const std::string& hashed_name)
246 : hashed_name_(hashed_name) {
247 DCHECK(base::IsStringUTF8(hashed_name));
248 }
~StoreNodeValue()249 virtual ~StoreNodeValue() {}
Execute(ExecutionContext * context)250 virtual bool Execute(ExecutionContext* context) OVERRIDE {
251 scoped_ptr<base::Value> value;
252 if (ExpectedTypeIsBooleanNotHashable) {
253 if (!context->current_node()->IsType(base::Value::TYPE_BOOLEAN))
254 return true;
255 value.reset(context->current_node()->DeepCopy());
256 } else {
257 std::string hash;
258 if (!context->GetValueHash(*context->current_node(), &hash))
259 return true;
260 value.reset(new base::StringValue(hash));
261 }
262 context->working_memory()->Set(hashed_name_, value.release());
263 return context->ContinueExecution();
264 }
265
266 private:
267 std::string hashed_name_;
268 DISALLOW_COPY_AND_ASSIGN(StoreNodeValue);
269 };
270
271 // Stores the hash of the registerable domain name -- as in, the portion of the
272 // domain that is registerable, as opposed to controlled by a registrar; without
273 // subdomains -- of the URL represented by the current node into working memory.
274 class StoreNodeRegisterableDomain : public Operation {
275 public:
StoreNodeRegisterableDomain(const std::string & hashed_name)276 explicit StoreNodeRegisterableDomain(const std::string& hashed_name)
277 : hashed_name_(hashed_name) {
278 DCHECK(base::IsStringUTF8(hashed_name));
279 }
~StoreNodeRegisterableDomain()280 virtual ~StoreNodeRegisterableDomain() {}
Execute(ExecutionContext * context)281 virtual bool Execute(ExecutionContext* context) OVERRIDE {
282 std::string possibly_invalid_url;
283 std::string domain;
284 if (!context->current_node()->GetAsString(&possibly_invalid_url) ||
285 !GetRegisterableDomain(possibly_invalid_url, &domain))
286 return true;
287 context->working_memory()->Set(
288 hashed_name_, new base::StringValue(context->GetHash(domain)));
289 return context->ContinueExecution();
290 }
291
292 private:
293 // If |possibly_invalid_url| is a valid URL having a registerable domain name
294 // part, outputs that in |registerable_domain| and returns true. Otherwise,
295 // returns false.
GetRegisterableDomain(const std::string & possibly_invalid_url,std::string * registerable_domain)296 static bool GetRegisterableDomain(const std::string& possibly_invalid_url,
297 std::string* registerable_domain) {
298 namespace domains = net::registry_controlled_domains;
299 DCHECK(registerable_domain);
300 GURL url(possibly_invalid_url);
301 if (!url.is_valid())
302 return false;
303 std::string registry_plus_one = domains::GetDomainAndRegistry(
304 url.host(), domains::INCLUDE_PRIVATE_REGISTRIES);
305 size_t registry_length = domains::GetRegistryLength(
306 url.host(),
307 domains::INCLUDE_UNKNOWN_REGISTRIES,
308 domains::INCLUDE_PRIVATE_REGISTRIES);
309 // Fail unless (1.) the URL has a host part; and (2.) that host part is a
310 // well-formed domain name consisting of at least one subcomponent; followed
311 // by either a recognized registry identifier, or exactly one subcomponent,
312 // which is then assumed to be the unknown registry identifier.
313 if (registry_length == std::string::npos || registry_length == 0)
314 return false;
315 DCHECK_LT(registry_length, registry_plus_one.size());
316 // Subtract one to cut off the dot separating the SLD and the registry.
317 registerable_domain->assign(
318 registry_plus_one, 0, registry_plus_one.size() - registry_length - 1);
319 return true;
320 }
321
322 std::string hashed_name_;
323 DISALLOW_COPY_AND_ASSIGN(StoreNodeRegisterableDomain);
324 };
325
326 class CompareNodeBool : public Operation {
327 public:
CompareNodeBool(bool value)328 explicit CompareNodeBool(bool value) : value_(value) {}
~CompareNodeBool()329 virtual ~CompareNodeBool() {}
Execute(ExecutionContext * context)330 virtual bool Execute(ExecutionContext* context) OVERRIDE {
331 bool actual_value = false;
332 if (!context->current_node()->GetAsBoolean(&actual_value))
333 return true;
334 if (actual_value != value_)
335 return true;
336 return context->ContinueExecution();
337 }
338
339 private:
340 bool value_;
341 DISALLOW_COPY_AND_ASSIGN(CompareNodeBool);
342 };
343
344 class CompareNodeHash : public Operation {
345 public:
CompareNodeHash(const std::string & hashed_value)346 explicit CompareNodeHash(const std::string& hashed_value)
347 : hashed_value_(hashed_value) {}
~CompareNodeHash()348 virtual ~CompareNodeHash() {}
Execute(ExecutionContext * context)349 virtual bool Execute(ExecutionContext* context) OVERRIDE {
350 std::string actual_hash;
351 if (!context->GetValueHash(*context->current_node(), &actual_hash) ||
352 actual_hash != hashed_value_)
353 return true;
354 return context->ContinueExecution();
355 }
356
357 private:
358 std::string hashed_value_;
359 DISALLOW_COPY_AND_ASSIGN(CompareNodeHash);
360 };
361
362 class CompareNodeHashNot : public Operation {
363 public:
CompareNodeHashNot(const std::string & hashed_value)364 explicit CompareNodeHashNot(const std::string& hashed_value)
365 : hashed_value_(hashed_value) {}
~CompareNodeHashNot()366 virtual ~CompareNodeHashNot() {}
Execute(ExecutionContext * context)367 virtual bool Execute(ExecutionContext* context) OVERRIDE {
368 std::string actual_hash;
369 if (context->GetValueHash(*context->current_node(), &actual_hash) &&
370 actual_hash == hashed_value_)
371 return true;
372 return context->ContinueExecution();
373 }
374
375 private:
376 std::string hashed_value_;
377 DISALLOW_COPY_AND_ASSIGN(CompareNodeHashNot);
378 };
379
380 template<bool ExpectedTypeIsBooleanNotHashable>
381 class CompareNodeToStored : public Operation {
382 public:
CompareNodeToStored(const std::string & hashed_name)383 explicit CompareNodeToStored(const std::string& hashed_name)
384 : hashed_name_(hashed_name) {}
~CompareNodeToStored()385 virtual ~CompareNodeToStored() {}
Execute(ExecutionContext * context)386 virtual bool Execute(ExecutionContext* context) OVERRIDE {
387 const base::Value* stored_value = NULL;
388 if (!context->working_memory()->Get(hashed_name_, &stored_value))
389 return true;
390 if (ExpectedTypeIsBooleanNotHashable) {
391 if (!context->current_node()->IsType(base::Value::TYPE_BOOLEAN) ||
392 !context->current_node()->Equals(stored_value))
393 return true;
394 } else {
395 std::string actual_hash;
396 std::string stored_hash;
397 if (!context->GetValueHash(*context->current_node(), &actual_hash) ||
398 !stored_value->GetAsString(&stored_hash) ||
399 actual_hash != stored_hash)
400 return true;
401 }
402 return context->ContinueExecution();
403 }
404
405 private:
406 std::string hashed_name_;
407 DISALLOW_COPY_AND_ASSIGN(CompareNodeToStored);
408 };
409
410 class CompareNodeSubstring : public Operation {
411 public:
CompareNodeSubstring(const std::string & hashed_pattern,size_t pattern_length,uint32 pattern_sum)412 explicit CompareNodeSubstring(const std::string& hashed_pattern,
413 size_t pattern_length,
414 uint32 pattern_sum)
415 : hashed_pattern_(hashed_pattern),
416 pattern_length_(pattern_length),
417 pattern_sum_(pattern_sum) {
418 DCHECK(pattern_length_);
419 }
~CompareNodeSubstring()420 virtual ~CompareNodeSubstring() {}
Execute(ExecutionContext * context)421 virtual bool Execute(ExecutionContext* context) OVERRIDE {
422 std::string value_as_string;
423 if (!context->current_node()->GetAsString(&value_as_string) ||
424 !pattern_length_ || value_as_string.size() < pattern_length_)
425 return true;
426 // Go over the string with a sliding window. Meanwhile, maintain the sum in
427 // an incremental fashion, and only calculate the SHA-256 hash when the sum
428 // checks out so as to improve performance.
429 std::string::const_iterator window_begin = value_as_string.begin();
430 std::string::const_iterator window_end = window_begin + pattern_length_ - 1;
431 uint32 window_sum =
432 std::accumulate(window_begin, window_end, static_cast<uint32>(0u));
433 while (window_end != value_as_string.end()) {
434 window_sum += *window_end++;
435 if (window_sum == pattern_sum_ && context->GetHash(std::string(
436 window_begin, window_end)) == hashed_pattern_)
437 return context->ContinueExecution();
438 window_sum -= *window_begin++;
439 }
440 return true;
441 }
442
443 private:
444 std::string hashed_pattern_;
445 size_t pattern_length_;
446 uint32 pattern_sum_;
447 DISALLOW_COPY_AND_ASSIGN(CompareNodeSubstring);
448 };
449
450 class StopExecutingSentenceOperation : public Operation {
451 public:
StopExecutingSentenceOperation()452 StopExecutingSentenceOperation() {}
~StopExecutingSentenceOperation()453 virtual ~StopExecutingSentenceOperation() {}
Execute(ExecutionContext * context)454 virtual bool Execute(ExecutionContext* context) OVERRIDE {
455 return false;
456 }
457
458 private:
459 DISALLOW_COPY_AND_ASSIGN(StopExecutingSentenceOperation);
460 };
461
462 class Parser {
463 public:
Parser(const std::string & program)464 explicit Parser(const std::string& program)
465 : program_(program),
466 next_instruction_index_(0u) {}
~Parser()467 ~Parser() {}
ParseNextSentence(ScopedVector<Operation> * output)468 bool ParseNextSentence(ScopedVector<Operation>* output) {
469 ScopedVector<Operation> operators;
470 bool sentence_ended = false;
471 while (next_instruction_index_ < program_.size() && !sentence_ended) {
472 uint8 op_code = 0;
473 if (!ReadOpCode(&op_code))
474 return false;
475 switch (static_cast<jtl_foundation::OpCodes>(op_code)) {
476 case jtl_foundation::NAVIGATE: {
477 std::string hashed_key;
478 if (!ReadHash(&hashed_key))
479 return false;
480 operators.push_back(new NavigateOperation(hashed_key));
481 break;
482 }
483 case jtl_foundation::NAVIGATE_ANY:
484 operators.push_back(new NavigateAnyOperation);
485 break;
486 case jtl_foundation::NAVIGATE_BACK:
487 operators.push_back(new NavigateBackOperation);
488 break;
489 case jtl_foundation::STORE_BOOL: {
490 std::string hashed_name;
491 if (!ReadHash(&hashed_name) || !base::IsStringUTF8(hashed_name))
492 return false;
493 bool value = false;
494 if (!ReadBool(&value))
495 return false;
496 operators.push_back(new StoreValue(
497 hashed_name,
498 scoped_ptr<base::Value>(new base::FundamentalValue(value))));
499 break;
500 }
501 case jtl_foundation::COMPARE_STORED_BOOL: {
502 std::string hashed_name;
503 if (!ReadHash(&hashed_name) || !base::IsStringUTF8(hashed_name))
504 return false;
505 bool value = false;
506 if (!ReadBool(&value))
507 return false;
508 bool default_value = false;
509 if (!ReadBool(&default_value))
510 return false;
511 operators.push_back(new CompareStoredValue(
512 hashed_name,
513 scoped_ptr<base::Value>(new base::FundamentalValue(value)),
514 scoped_ptr<base::Value>(
515 new base::FundamentalValue(default_value))));
516 break;
517 }
518 case jtl_foundation::STORE_HASH: {
519 std::string hashed_name;
520 if (!ReadHash(&hashed_name) || !base::IsStringUTF8(hashed_name))
521 return false;
522 std::string hashed_value;
523 if (!ReadHash(&hashed_value))
524 return false;
525 operators.push_back(new StoreValue(
526 hashed_name,
527 scoped_ptr<base::Value>(new base::StringValue(hashed_value))));
528 break;
529 }
530 case jtl_foundation::COMPARE_STORED_HASH: {
531 std::string hashed_name;
532 if (!ReadHash(&hashed_name) || !base::IsStringUTF8(hashed_name))
533 return false;
534 std::string hashed_value;
535 if (!ReadHash(&hashed_value))
536 return false;
537 std::string hashed_default_value;
538 if (!ReadHash(&hashed_default_value))
539 return false;
540 operators.push_back(new CompareStoredValue(
541 hashed_name,
542 scoped_ptr<base::Value>(new base::StringValue(hashed_value)),
543 scoped_ptr<base::Value>(
544 new base::StringValue(hashed_default_value))));
545 break;
546 }
547 case jtl_foundation::STORE_NODE_BOOL: {
548 std::string hashed_name;
549 if (!ReadHash(&hashed_name) || !base::IsStringUTF8(hashed_name))
550 return false;
551 operators.push_back(new StoreNodeValue<true>(hashed_name));
552 break;
553 }
554 case jtl_foundation::STORE_NODE_HASH: {
555 std::string hashed_name;
556 if (!ReadHash(&hashed_name) || !base::IsStringUTF8(hashed_name))
557 return false;
558 operators.push_back(new StoreNodeValue<false>(hashed_name));
559 break;
560 }
561 case jtl_foundation::STORE_NODE_REGISTERABLE_DOMAIN_HASH: {
562 std::string hashed_name;
563 if (!ReadHash(&hashed_name) || !base::IsStringUTF8(hashed_name))
564 return false;
565 operators.push_back(new StoreNodeRegisterableDomain(hashed_name));
566 break;
567 }
568 case jtl_foundation::COMPARE_NODE_BOOL: {
569 bool value = false;
570 if (!ReadBool(&value))
571 return false;
572 operators.push_back(new CompareNodeBool(value));
573 break;
574 }
575 case jtl_foundation::COMPARE_NODE_HASH: {
576 std::string hashed_value;
577 if (!ReadHash(&hashed_value))
578 return false;
579 operators.push_back(new CompareNodeHash(hashed_value));
580 break;
581 }
582 case jtl_foundation::COMPARE_NODE_HASH_NOT: {
583 std::string hashed_value;
584 if (!ReadHash(&hashed_value))
585 return false;
586 operators.push_back(new CompareNodeHashNot(hashed_value));
587 break;
588 }
589 case jtl_foundation::COMPARE_NODE_TO_STORED_BOOL: {
590 std::string hashed_name;
591 if (!ReadHash(&hashed_name) || !base::IsStringUTF8(hashed_name))
592 return false;
593 operators.push_back(new CompareNodeToStored<true>(hashed_name));
594 break;
595 }
596 case jtl_foundation::COMPARE_NODE_TO_STORED_HASH: {
597 std::string hashed_name;
598 if (!ReadHash(&hashed_name) || !base::IsStringUTF8(hashed_name))
599 return false;
600 operators.push_back(new CompareNodeToStored<false>(hashed_name));
601 break;
602 }
603 case jtl_foundation::COMPARE_NODE_SUBSTRING: {
604 std::string hashed_pattern;
605 uint32 pattern_length = 0, pattern_sum = 0;
606 if (!ReadHash(&hashed_pattern))
607 return false;
608 if (!ReadUint32(&pattern_length) || pattern_length == 0)
609 return false;
610 if (!ReadUint32(&pattern_sum))
611 return false;
612 operators.push_back(new CompareNodeSubstring(
613 hashed_pattern, pattern_length, pattern_sum));
614 break;
615 }
616 case jtl_foundation::STOP_EXECUTING_SENTENCE:
617 operators.push_back(new StopExecutingSentenceOperation);
618 break;
619 case jtl_foundation::END_OF_SENTENCE:
620 sentence_ended = true;
621 break;
622 default:
623 return false;
624 }
625 }
626 output->swap(operators);
627 return true;
628 }
629
HasNextSentence() const630 bool HasNextSentence() const {
631 return next_instruction_index_ < program_.size();
632 }
633
634 private:
635 // Reads an uint8 and returns whether this operation was successful.
ReadUint8(uint8 * out)636 bool ReadUint8(uint8* out) {
637 DCHECK(out);
638 if (next_instruction_index_ + 1u > program_.size())
639 return false;
640 *out = static_cast<uint8>(program_[next_instruction_index_]);
641 ++next_instruction_index_;
642 return true;
643 }
644
645 // Reads an uint32 and returns whether this operation was successful.
ReadUint32(uint32 * out)646 bool ReadUint32(uint32* out) {
647 DCHECK(out);
648 if (next_instruction_index_ + 4u > program_.size())
649 return false;
650 *out = 0u;
651 for (int i = 0; i < 4; ++i) {
652 *out >>= 8;
653 *out |= static_cast<uint8>(program_[next_instruction_index_]) << 24;
654 ++next_instruction_index_;
655 }
656 return true;
657 }
658
659 // Reads an operator code and returns whether this operation was successful.
ReadOpCode(uint8 * out)660 bool ReadOpCode(uint8* out) { return ReadUint8(out); }
661
ReadHash(std::string * out)662 bool ReadHash(std::string* out) {
663 DCHECK(out);
664 if (next_instruction_index_ + jtl_foundation::kHashSizeInBytes >
665 program_.size())
666 return false;
667 *out = program_.substr(next_instruction_index_,
668 jtl_foundation::kHashSizeInBytes);
669 next_instruction_index_ += jtl_foundation::kHashSizeInBytes;
670 DCHECK(jtl_foundation::Hasher::IsHash(*out));
671 return true;
672 }
673
ReadBool(bool * out)674 bool ReadBool(bool* out) {
675 DCHECK(out);
676 uint8 value = 0;
677 if (!ReadUint8(&value))
678 return false;
679 if (value == 0)
680 *out = false;
681 else if (value == 1)
682 *out = true;
683 else
684 return false;
685 return true;
686 }
687
688 std::string program_;
689 size_t next_instruction_index_;
690 DISALLOW_COPY_AND_ASSIGN(Parser);
691 };
692
693 } // namespace
694
JtlInterpreter(const std::string & hasher_seed,const std::string & program,const base::DictionaryValue * input)695 JtlInterpreter::JtlInterpreter(
696 const std::string& hasher_seed,
697 const std::string& program,
698 const base::DictionaryValue* input)
699 : hasher_seed_(hasher_seed),
700 program_(program),
701 input_(input),
702 working_memory_(new base::DictionaryValue),
703 result_(OK) {
704 DCHECK(input->IsType(base::Value::TYPE_DICTIONARY));
705 }
706
~JtlInterpreter()707 JtlInterpreter::~JtlInterpreter() {}
708
Execute()709 void JtlInterpreter::Execute() {
710 jtl_foundation::Hasher hasher(hasher_seed_);
711 Parser parser(program_);
712 while (parser.HasNextSentence()) {
713 ScopedVector<Operation> sentence;
714 if (!parser.ParseNextSentence(&sentence)) {
715 result_ = PARSE_ERROR;
716 return;
717 }
718 ExecutionContext context(
719 &hasher, sentence.get(), input_, working_memory_.get());
720 context.ContinueExecution();
721 if (context.error()) {
722 result_ = RUNTIME_ERROR;
723 return;
724 }
725 }
726 }
727
GetOutputBoolean(const std::string & unhashed_key,bool * output) const728 bool JtlInterpreter::GetOutputBoolean(const std::string& unhashed_key,
729 bool* output) const {
730 std::string hashed_key =
731 jtl_foundation::Hasher(hasher_seed_).GetHash(unhashed_key);
732 return working_memory_->GetBoolean(hashed_key, output);
733 }
734
GetOutputString(const std::string & unhashed_key,std::string * output) const735 bool JtlInterpreter::GetOutputString(const std::string& unhashed_key,
736 std::string* output) const {
737 std::string hashed_key =
738 jtl_foundation::Hasher(hasher_seed_).GetHash(unhashed_key);
739 return working_memory_->GetString(hashed_key, output);
740 }
741
CalculateProgramChecksum() const742 int JtlInterpreter::CalculateProgramChecksum() const {
743 uint8 digest[3] = {};
744 crypto::SHA256HashString(program_, digest, arraysize(digest));
745 return static_cast<uint32>(digest[0]) << 16 |
746 static_cast<uint32>(digest[1]) << 8 |
747 static_cast<uint32>(digest[2]);
748 }
749