• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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