summaryrefslogtreecommitdiffstats
path: root/extensions/browser/content_verify_job.h
blob: 30e46e1d145d20e0bc98a33d7eac4aef7abe7806 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef EXTENSIONS_BROWSER_CONTENT_VERIFY_JOB_H_
#define EXTENSIONS_BROWSER_CONTENT_VERIFY_JOB_H_

#include <stdint.h>

#include <string>

#include "base/callback.h"
#include "base/macros.h"
#include "base/memory/ref_counted.h"
#include "base/memory/scoped_ptr.h"
#include "base/threading/thread_checker.h"

namespace base {
class FilePath;
}

namespace crypto {
class SecureHash;
}

namespace extensions {

class ContentHashReader;

// Objects of this class are responsible for verifying that the actual content
// read from an extension file matches an expected set of hashes. This class
// can be created on any thread but the rest of the methods should be called
// from only one thread.
class ContentVerifyJob : public base::RefCountedThreadSafe<ContentVerifyJob> {
 public:
  enum FailureReason {
    // No failure.
    NONE,

    // Failed because there were no expected hashes at all (eg they haven't
    // been fetched yet).
    MISSING_ALL_HASHES,

    // Failed because this file wasn't found in the list of expected hashes.
    NO_HASHES_FOR_FILE,

    // Some of the content read did not match the expected hash.
    HASH_MISMATCH,

    FAILURE_REASON_MAX
  };
  typedef base::Callback<void(FailureReason)> FailureCallback;

  // The |failure_callback| will be called at most once if there was a failure.
  ContentVerifyJob(ContentHashReader* hash_reader,
                   const FailureCallback& failure_callback);

  // This begins the process of getting expected hashes, so it should be called
  // as early as possible.
  void Start();

  // Call this to add more bytes to verify. If at any point the read bytes
  // don't match the expected hashes, this will dispatch the failure
  // callback. The failure callback will only be run once even if more bytes
  // are read. Make sure to call DoneReading so that any final bytes that were
  // read that didn't align exactly on a block size boundary get their hash
  // checked as well.
  void BytesRead(int count, const char* data);

  // Call once when finished adding bytes via BytesRead.
  void DoneReading();

  class TestDelegate {
   public:
    // These methods will be called inside BytesRead/DoneReading respectively.
    // If either return something other than NONE, then the failure callback
    // will be dispatched with that reason.
    virtual FailureReason BytesRead(const std::string& extension_id,
                                    int count,
                                    const char* data) = 0;
    virtual FailureReason DoneReading(const std::string& extension_id) = 0;
  };

  class TestObserver {
   public:
    virtual void JobStarted(const std::string& extension_id,
                            const base::FilePath& relative_path) = 0;

    virtual void JobFinished(const std::string& extension_id,
                             const base::FilePath& relative_path,
                             bool failed) = 0;
  };

  static void SetDelegateForTests(TestDelegate* delegate);
  static void SetObserverForTests(TestObserver* observer);

 private:
  DISALLOW_COPY_AND_ASSIGN(ContentVerifyJob);

  virtual ~ContentVerifyJob();
  friend class base::RefCountedThreadSafe<ContentVerifyJob>;

  // Called each time we're done adding bytes for the current block, and are
  // ready to finish the hash operation for those bytes and make sure it
  // matches what was expected for that block. Returns true if everything is
  // still ok so far, or false if a mismatch was detected.
  bool FinishBlock();

  // Dispatches the failure callback with the given reason.
  void DispatchFailureCallback(FailureReason reason);

  // Called when our ContentHashReader has finished initializing.
  void OnHashesReady(bool success);

  // Indicates whether the caller has told us they are done calling BytesRead.
  bool done_reading_;

  // Set to true once hash_reader_ has read its expected hashes.
  bool hashes_ready_;

  // While we're waiting for the callback from the ContentHashReader, we need
  // to queue up bytes any bytes that are read.
  std::string queue_;

  // The total bytes we've read.
  int64_t total_bytes_read_;

  // The index of the block we're currently on.
  int current_block_;

  // The hash we're building up for the bytes of |current_block_|.
  scoped_ptr<crypto::SecureHash> current_hash_;

  // The number of bytes we've already input into |current_hash_|.
  int current_hash_byte_count_;

  scoped_refptr<ContentHashReader> hash_reader_;

  base::TimeDelta time_spent_;

  // Called once if verification fails.
  FailureCallback failure_callback_;

  // Set to true if we detected a mismatch and called the failure callback.
  bool failed_;

  // For ensuring methods on called on the right thread.
  base::ThreadChecker thread_checker_;
};

}  // namespace extensions

#endif  // EXTENSIONS_BROWSER_CONTENT_VERIFY_JOB_H_