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
18namespace {
19
20class ExecutionContext;
21
22// An operation in an interpreted program.
23class Operation {
24 public:
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.
34class ExecutionContext {
35 public:
36  // |input| is the root of a dictionary that stores the information the
37  // sentence is evaluated on.
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  }
49  ~ExecutionContext() {}
50
51  // Returns true in case of success.
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
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.
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
88  const base::Value* current_node() const { return stack_.back(); }
89  std::vector<const base::Value*>* stack() { return &stack_; }
90  base::DictionaryValue* working_memory() { return working_memory_; }
91  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
111class NavigateOperation : public Operation {
112 public:
113  explicit NavigateOperation(const std::string& hashed_key)
114      : hashed_key_(hashed_key) {}
115  virtual ~NavigateOperation() {}
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
142class NavigateAnyOperation : public Operation {
143 public:
144  NavigateAnyOperation() {}
145  virtual ~NavigateAnyOperation() {}
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
177class NavigateBackOperation : public Operation {
178 public:
179  NavigateBackOperation() {}
180  virtual ~NavigateBackOperation() {}
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
193class StoreValue : public Operation {
194 public:
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  }
201  virtual ~StoreValue() {}
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
213class CompareStoredValue : public Operation {
214 public:
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  }
225  virtual ~CompareStoredValue() {}
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
242template<bool ExpectedTypeIsBooleanNotHashable>
243class StoreNodeValue : public Operation {
244 public:
245  explicit StoreNodeValue(const std::string& hashed_name)
246      : hashed_name_(hashed_name) {
247    DCHECK(base::IsStringUTF8(hashed_name));
248  }
249  virtual ~StoreNodeValue() {}
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.
274class StoreNodeRegisterableDomain : public Operation {
275 public:
276  explicit StoreNodeRegisterableDomain(const std::string& hashed_name)
277      : hashed_name_(hashed_name) {
278    DCHECK(base::IsStringUTF8(hashed_name));
279  }
280  virtual ~StoreNodeRegisterableDomain() {}
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.
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
326class CompareNodeBool : public Operation {
327 public:
328  explicit CompareNodeBool(bool value) : value_(value) {}
329  virtual ~CompareNodeBool() {}
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
344class CompareNodeHash : public Operation {
345 public:
346  explicit CompareNodeHash(const std::string& hashed_value)
347      : hashed_value_(hashed_value) {}
348  virtual ~CompareNodeHash() {}
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
362class CompareNodeHashNot : public Operation {
363 public:
364  explicit CompareNodeHashNot(const std::string& hashed_value)
365      : hashed_value_(hashed_value) {}
366  virtual ~CompareNodeHashNot() {}
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
380template<bool ExpectedTypeIsBooleanNotHashable>
381class CompareNodeToStored : public Operation {
382 public:
383  explicit CompareNodeToStored(const std::string& hashed_name)
384      : hashed_name_(hashed_name) {}
385  virtual ~CompareNodeToStored() {}
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
410class CompareNodeSubstring : public Operation {
411 public:
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  }
420  virtual ~CompareNodeSubstring() {}
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
450class StopExecutingSentenceOperation : public Operation {
451 public:
452  StopExecutingSentenceOperation() {}
453  virtual ~StopExecutingSentenceOperation() {}
454  virtual bool Execute(ExecutionContext* context) OVERRIDE {
455    return false;
456  }
457
458 private:
459  DISALLOW_COPY_AND_ASSIGN(StopExecutingSentenceOperation);
460};
461
462class Parser {
463 public:
464  explicit Parser(const std::string& program)
465      : program_(program),
466        next_instruction_index_(0u) {}
467  ~Parser() {}
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
630  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.
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.
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.
660  bool ReadOpCode(uint8* out) { return ReadUint8(out); }
661
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
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
695JtlInterpreter::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
707JtlInterpreter::~JtlInterpreter() {}
708
709void 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
728bool 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
735bool 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
742int 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