summaryrefslogtreecommitdiffstats
path: root/sync/util/cryptographer.h
blob: c23495258e3957b889d46640ade9302a77f8b0ec (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
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
// Copyright (c) 2012 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 SYNC_UTIL_CRYPTOGRAPHER_H_
#define SYNC_UTIL_CRYPTOGRAPHER_H_
#pragma once

#include <map>
#include <string>

#include "base/gtest_prod_util.h"
#include "base/memory/linked_ptr.h"
#include "base/memory/scoped_ptr.h"
#include "base/observer_list.h"
#include "sync/internal_api/public/base/model_type.h"
#include "sync/protocol/encryption.pb.h"
#include "sync/protocol/nigori_specifics.pb.h"
#include "sync/util/nigori.h"

namespace syncer {

class Encryptor;

extern const char kNigoriTag[];

// The parameters used to initialize a Nigori instance.
struct KeyParams {
  std::string hostname;
  std::string username;
  std::string password;
};

// This class manages the Nigori objects used to encrypt and decrypt sensitive
// sync data (eg. passwords). Each Nigori object knows how to handle data
// protected with a particular passphrase.
//
// Whenever an update to the Nigori sync node is received from the server,
// SetPendingKeys should be called with the encrypted contents of that node.
// Most likely, an updated Nigori node means that a new passphrase has been set
// and that future node updates won't be decryptable. To remedy this, the user
// should be prompted for the new passphrase and DecryptPendingKeys be called.
//
// Whenever a update to an encrypted node is received from the server,
// CanDecrypt should be used to verify whether the Cryptographer can decrypt
// that node. If it cannot, then the application of that update should be
// delayed until after it can be decrypted.
class Cryptographer {
 public:
  // All Observer methods are done synchronously, so they're called
  // under a transaction (since all Cryptographer operations are done
  // under a transaction).
  class Observer {
   public:
    // Called when the set of encrypted types or the encrypt
    // everything flag has been changed.  Note that this doesn't
    // necessarily mean that encryption has completed for the given
    // types.
    //
    // |encrypted_types| will always be a superset of
    // SensitiveTypes().  If |encrypt_everything| is true,
    // |encrypted_types| will be the set of all known types.
    //
    // Until this function is called, observers can assume that the
    // set of encrypted types is SensitiveTypes() and that the encrypt
    // everything flag is false.
    virtual void OnEncryptedTypesChanged(
        syncer::ModelTypeSet encrypted_types,
        bool encrypt_everything) = 0;

   protected:
    virtual ~Observer();
  };

  // Does not take ownership of |encryptor|.
  explicit Cryptographer(Encryptor* encryptor);
  ~Cryptographer();

  // When update on cryptographer is called this enum tells if the
  // cryptographer was succesfully able to update using the nigori node or if
  // it needs a key to decrypt the nigori node.
  enum UpdateResult {
    SUCCESS,
    NEEDS_PASSPHRASE
  };

  // Manage observers.
  void AddObserver(Observer* observer);
  void RemoveObserver(Observer* observer);

  // |restored_bootstrap_token| can be provided via this method to bootstrap
  // Cryptographer instance into the ready state (is_ready will be true).
  // It must be a string that was previously built by the
  // GetSerializedBootstrapToken function.  It is possible that the token is no
  // longer valid (due to server key change), in which case the normal
  // decryption code paths will fail and the user will need to provide a new
  // passphrase.
  // It is an error to call this if is_ready() == true, though it is fair to
  // never call Bootstrap at all.
  void Bootstrap(const std::string& restored_bootstrap_token);

  // Returns whether we can decrypt |encrypted| using the keys we currently know
  // about.
  bool CanDecrypt(const sync_pb::EncryptedData& encrypted) const;

  // Returns whether |encrypted| can be decrypted using the default encryption
  // key.
  bool CanDecryptUsingDefaultKey(const sync_pb::EncryptedData& encrypted) const;

  // Encrypts |message| into |encrypted|. Does not overwrite |encrypted| if
  // |message| already matches the decrypted data within |encrypted| and
  // |encrypted| was encrypted with the current default key. This avoids
  // unnecessarily modifying |encrypted| if the change had no practical effect.
  // Returns true unless encryption fails or |message| isn't valid (e.g. a
  // required field isn't set).
  bool Encrypt(const ::google::protobuf::MessageLite& message,
               sync_pb::EncryptedData* encrypted) const;

  // Decrypts |encrypted| into |message|. Returns true unless decryption fails,
  // or |message| fails to parse the decrypted data.
  bool Decrypt(const sync_pb::EncryptedData& encrypted,
               ::google::protobuf::MessageLite* message) const;

  // Decrypts |encrypted| and returns plaintext decrypted data. If decryption
  // fails, returns empty string.
  std::string DecryptToString(const sync_pb::EncryptedData& encrypted) const;

  // Encrypts the set of currently known keys into |encrypted|. Returns true if
  // successful.
  bool GetKeys(sync_pb::EncryptedData* encrypted) const;

  // Creates a new Nigori instance using |params|. If successful, |params| will
  // become the default encryption key and be used for all future calls to
  // Encrypt.
  bool AddKey(const KeyParams& params);

  // Same as AddKey(..), but builds the new Nigori from a previously persisted
  // bootstrap token. This can be useful when consuming a bootstrap token
  // with a cryptographer that has already been initialized.
  bool AddKeyFromBootstrapToken(const std::string restored_bootstrap_token);

  // Makes a local copy of |encrypted| to later be decrypted by
  // DecryptPendingKeys. This should only be used if CanDecrypt(encrypted) ==
  // false.
  void SetPendingKeys(const sync_pb::EncryptedData& encrypted);

  // Makes |pending_keys_| available to callers that may want to cache its
  // value for later use on the UI thread. It is illegal to call this if the
  // cryptographer has no pending keys. Like other calls that access the
  // cryptographer, this method must be called from within a transaction.
  const sync_pb::EncryptedData& GetPendingKeys() const;

  // Attempts to decrypt the set of keys that was copied in the previous call to
  // SetPendingKeys using |params|. Returns true if the pending keys were
  // successfully decrypted and installed. If successful, the default key
  // is updated.
  bool DecryptPendingKeys(const KeyParams& params);

  bool is_initialized() const { return !nigoris_.empty() && default_nigori_; }

  // Returns whether this Cryptographer is ready to encrypt and decrypt data.
  bool is_ready() const { return is_initialized() &&
                          has_pending_keys() == false; }

  // Returns whether there is a pending set of keys that needs to be decrypted.
  bool has_pending_keys() const { return NULL != pending_keys_.get(); }

  // Obtain a token that can be provided on construction to a future
  // Cryptographer instance to bootstrap itself.  Returns false if such a token
  // can't be created (i.e. if this Cryptograhper doesn't have valid keys).
  bool GetBootstrapToken(std::string* token) const;

  // Update the cryptographer based on the contents of the nigori specifics.
  // This updates both the encryption keys and the set of encrypted types.
  // Returns NEEDS_PASSPHRASE if was unable to decrypt the pending keys,
  // SUCCESS otherwise.
  // Note: will not change the default key. If the nigori's keybag
  // is decryptable, all keys are added to the local keybag and the current
  // default is preserved. If the nigori's keybag is not decryptable, it is
  // stored in the |pending_keys_|.
  UpdateResult Update(const sync_pb::NigoriSpecifics& nigori);

  // The set of types that are always encrypted.
  static syncer::ModelTypeSet SensitiveTypes();

  // Reset our set of encrypted types based on the contents of the nigori
  // specifics.
  void UpdateEncryptedTypesFromNigori(const sync_pb::NigoriSpecifics& nigori);

  // Update the nigori to reflect the current set of encrypted types.
  void UpdateNigoriFromEncryptedTypes(sync_pb::NigoriSpecifics* nigori) const;

  // Setter/getter for whether all current and future datatypes should
  // be encrypted. Once set you cannot unset without reading from a
  // new nigori node.  set_encrypt_everything() emits a notification
  // the first time it's called.
  void set_encrypt_everything();
  bool encrypt_everything() const;

  // Return the set of encrypted types.
  syncer::ModelTypeSet GetEncryptedTypes() const;

  // Forwards to MergeEncryptedTypes.
  void MergeEncryptedTypesForTest(syncer::ModelTypeSet encrypted_types);

 private:
  FRIEND_TEST_ALL_PREFIXES(SyncCryptographerTest, PackUnpack);
  typedef std::map<std::string, linked_ptr<const Nigori> > NigoriMap;

  // Merges the given set of encrypted types with the existing set and emits a
  // notification if necessary.
  void MergeEncryptedTypes(syncer::ModelTypeSet encrypted_types);

  void EmitEncryptedTypesChangedNotification();

  // Decrypts |encrypted| and uses its contents to initialize Nigori instances.
  // Returns true unless decryption of |encrypted| fails. The caller is
  // responsible for checking that CanDecrypt(encrypted) == true.
  // Does not update the default nigori.
  void InstallKeys(const sync_pb::EncryptedData& encrypted);

  // Helper method to instantiate Nigori instances for each set of key
  // parameters in |bag|.
  // Does not update the default nigori.
  void InstallKeyBag(const sync_pb::NigoriKeyBag& bag);

  // Helper method to add a nigori as the new default nigori.
  bool AddKeyImpl(Nigori* nigori);

  // Functions to serialize + encrypt a Nigori object in an opaque format for
  // persistence by sync infrastructure.
  bool PackBootstrapToken(const Nigori* nigori, std::string* pack_into) const;
  Nigori* UnpackBootstrapToken(const std::string& token) const;

  Encryptor* const encryptor_;

  ObserverList<Observer> observers_;

  NigoriMap nigoris_;  // The Nigoris we know about, mapped by key name.
  NigoriMap::value_type* default_nigori_;  // The Nigori used for encryption.

  scoped_ptr<sync_pb::EncryptedData> pending_keys_;

  syncer::ModelTypeSet encrypted_types_;
  bool encrypt_everything_;

  DISALLOW_COPY_AND_ASSIGN(Cryptographer);
};

}  // namespace syncer

#endif  // SYNC_UTIL_CRYPTOGRAPHER_H_