nigori.cc revision ddb351dbec246cf1fab5ec20d2d5520909041de1
1dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project// Copyright (c) 2011 The Chromium Authors. All rights reserved.
2dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project// Use of this source code is governed by a BSD-style license that can be
3dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project// found in the LICENSE file.
4dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project
5dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project#include "chrome/browser/sync/util/nigori.h"
6dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project
7dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project#if defined(OS_WIN)
8dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project#include <winsock2.h>  // for htonl
9dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project#else
10dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project#include <arpa/inet.h>
11dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project#endif
12dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project
13dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project#include <sstream>
14dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project#include <vector>
15dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project
16dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project#include "base/base64.h"
17dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project#include "base/logging.h"
18dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project#include "base/rand_util.h"
19dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project#include "base/string_util.h"
20dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project#include "crypto/encryptor.h"
21dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project#include "crypto/hmac.h"
22dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project
23dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Projectusing base::Base64Encode;
24dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Projectusing base::Base64Decode;
25dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Projectusing base::RandInt;
26dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Projectusing crypto::Encryptor;
27dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Projectusing crypto::HMAC;
28dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Projectusing crypto::SymmetricKey;
29dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project
30dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Projectnamespace browser_sync {
31dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project
32dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project// NigoriStream simplifies the concatenation operation of the Nigori protocol.
33dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Projectclass NigoriStream {
34dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project public:
35dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project  // Append the big-endian representation of the length of |value| with 32 bits,
36dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project  // followed by |value| itself to the stream.
37dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project  NigoriStream& operator<<(const std::string& value) {
38dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project    uint32 size = htonl(value.size());
39dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project    stream_.write((char *) &size, sizeof(uint32));
40dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project    stream_ << value;
41dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project    return *this;
42dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project  }
43dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project
44dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project  // Append the big-endian representation of the length of |type| with 32 bits,
45dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project  // followed by the big-endian representation of the value of |type|, with 32
46dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project  // bits, to the stream.
47dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project  NigoriStream& operator<<(const Nigori::Type type) {
48dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project    uint32 size = htonl(sizeof(uint32));
49dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project    stream_.write((char *) &size, sizeof(uint32));
50dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project    uint32 value = htonl(type);
51dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project    stream_.write((char *) &value, sizeof(uint32));
52dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project    return *this;
53dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project  }
54dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project
55dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project  std::string str() {
56dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project    return stream_.str();
57dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project  }
58dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project
59dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project private:
60dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project  std::ostringstream stream_;
61dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project};
62dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project
63dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project// static
64dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Projectconst char Nigori::kSaltSalt[] = "saltsalt";
65dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project
66dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source ProjectNigori::Nigori() {
67dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project}
68dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project
69dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source ProjectNigori::~Nigori() {
70dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project}
71dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project
72dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Projectbool Nigori::InitByDerivation(const std::string& hostname,
73dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project                              const std::string& username,
74dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project                              const std::string& password) {
75dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project  NigoriStream salt_password;
76dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project  salt_password << username << hostname;
77dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project
78dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project  // Suser = PBKDF2(Username || Servername, "saltsalt", Nsalt, 8)
79dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project  scoped_ptr<SymmetricKey> user_salt(SymmetricKey::DeriveKeyFromPassword(
80dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project      SymmetricKey::HMAC_SHA1, salt_password.str(),
81dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project      kSaltSalt,
82dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project      kSaltIterations,
83dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project      kSaltKeySizeInBits));
84dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project  DCHECK(user_salt.get());
85dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project
86dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project  std::string raw_user_salt;
87dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project  if (!user_salt->GetRawKey(&raw_user_salt))
88dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project    return false;
89dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project
90dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project  // Kuser = PBKDF2(P, Suser, Nuser, 16)
91dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project  user_key_.reset(SymmetricKey::DeriveKeyFromPassword(SymmetricKey::AES,
92dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project      password, raw_user_salt, kUserIterations, kDerivedKeySizeInBits));
93dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project  DCHECK(user_key_.get());
94dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project
95dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project  // Kenc = PBKDF2(P, Suser, Nenc, 16)
96dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project  encryption_key_.reset(SymmetricKey::DeriveKeyFromPassword(SymmetricKey::AES,
97dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project      password, raw_user_salt, kEncryptionIterations, kDerivedKeySizeInBits));
98dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project  DCHECK(encryption_key_.get());
99dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project
100dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project  // Kmac = PBKDF2(P, Suser, Nmac, 16)
101dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project  mac_key_.reset(SymmetricKey::DeriveKeyFromPassword(
102dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project      SymmetricKey::HMAC_SHA1, password, raw_user_salt, kSigningIterations,
103dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project      kDerivedKeySizeInBits));
104dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project  DCHECK(mac_key_.get());
105dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project
106dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project  return true;
107dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project}
108dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project
109dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Projectbool Nigori::InitByImport(const std::string& user_key,
110dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project                          const std::string& encryption_key,
111dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project                          const std::string& mac_key) {
112dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project  user_key_.reset(SymmetricKey::Import(SymmetricKey::AES, user_key));
113dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project  DCHECK(user_key_.get());
114dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project
115dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project  encryption_key_.reset(SymmetricKey::Import(SymmetricKey::AES,
116dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project                                             encryption_key));
117dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project  DCHECK(encryption_key_.get());
118dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project
119dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project  mac_key_.reset(SymmetricKey::Import(SymmetricKey::HMAC_SHA1, mac_key));
120dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project  DCHECK(mac_key_.get());
121dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project
122dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project  return user_key_.get() && encryption_key_.get() && mac_key_.get();
123dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project}
124dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project
125dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project// Permute[Kenc,Kmac](type || name)
126dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Projectbool Nigori::Permute(Type type, const std::string& name,
127dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project                     std::string* permuted) const {
128dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project  DCHECK_LT(0U, name.size());
129dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project
130dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project  NigoriStream plaintext;
131dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project  plaintext << type << name;
132
133  Encryptor encryptor;
134  if (!encryptor.Init(encryption_key_.get(), Encryptor::CBC,
135                      std::string(kIvSize, 0)))
136    return false;
137
138  std::string ciphertext;
139  if (!encryptor.Encrypt(plaintext.str(), &ciphertext))
140    return false;
141
142  std::string raw_mac_key;
143  if (!mac_key_->GetRawKey(&raw_mac_key))
144    return false;
145
146  HMAC hmac(HMAC::SHA256);
147  if (!hmac.Init(raw_mac_key))
148    return false;
149
150  std::vector<unsigned char> hash(kHashSize);
151  if (!hmac.Sign(ciphertext, &hash[0], hash.size()))
152    return false;
153
154  std::string output;
155  output.assign(ciphertext);
156  output.append(hash.begin(), hash.end());
157
158  return Base64Encode(output, permuted);
159}
160
161std::string GenerateRandomString(size_t size) {
162  // TODO(albertb): Use a secure random function.
163  std::string random(size, 0);
164  for (size_t i = 0; i < size; ++i)
165    random[i] = RandInt(0, 0xff);
166  return random;
167}
168
169// Enc[Kenc,Kmac](value)
170bool Nigori::Encrypt(const std::string& value, std::string* encrypted) const {
171  DCHECK_LT(0U, value.size());
172
173  std::string iv = GenerateRandomString(kIvSize);
174
175  Encryptor encryptor;
176  if (!encryptor.Init(encryption_key_.get(), Encryptor::CBC, iv))
177    return false;
178
179  std::string ciphertext;
180  if (!encryptor.Encrypt(value, &ciphertext))
181    return false;
182
183  std::string raw_mac_key;
184  if (!mac_key_->GetRawKey(&raw_mac_key))
185    return false;
186
187  HMAC hmac(HMAC::SHA256);
188  if (!hmac.Init(raw_mac_key))
189    return false;
190
191  std::vector<unsigned char> hash(kHashSize);
192  if (!hmac.Sign(ciphertext, &hash[0], hash.size()))
193    return false;
194
195  std::string output;
196  output.assign(iv);
197  output.append(ciphertext);
198  output.append(hash.begin(), hash.end());
199
200  return Base64Encode(output, encrypted);
201}
202
203bool Nigori::Decrypt(const std::string& encrypted, std::string* value) const {
204  std::string input;
205  if (!Base64Decode(encrypted, &input))
206    return false;
207
208  if (input.size() < kIvSize * 2 + kHashSize)
209    return false;
210
211  // The input is:
212  // * iv (16 bytes)
213  // * ciphertext (multiple of 16 bytes)
214  // * hash (32 bytes)
215  std::string iv(input.substr(0, kIvSize));
216  std::string ciphertext(input.substr(kIvSize,
217                                      input.size() - (kIvSize + kHashSize)));
218  std::string hash(input.substr(input.size() - kHashSize, kHashSize));
219
220  std::string raw_mac_key;
221  if (!mac_key_->GetRawKey(&raw_mac_key))
222    return false;
223
224  HMAC hmac(HMAC::SHA256);
225  if (!hmac.Init(raw_mac_key))
226    return false;
227
228  std::vector<unsigned char> expected(kHashSize);
229  if (!hmac.Sign(ciphertext, &expected[0], expected.size()))
230    return false;
231
232  if (hash.compare(0, hash.size(),
233                   reinterpret_cast<char *>(&expected[0]),
234                   expected.size()))
235    return false;
236
237  Encryptor encryptor;
238  if (!encryptor.Init(encryption_key_.get(), Encryptor::CBC, iv))
239    return false;
240
241  std::string plaintext;
242  if (!encryptor.Decrypt(ciphertext, value))
243    return false;
244
245  return true;
246}
247
248bool Nigori::ExportKeys(std::string* user_key,
249                        std::string* encryption_key,
250                        std::string* mac_key) const {
251  DCHECK(user_key);
252  DCHECK(encryption_key);
253  DCHECK(mac_key);
254
255  return user_key_->GetRawKey(user_key) &&
256      encryption_key_->GetRawKey(encryption_key) &&
257      mac_key_->GetRawKey(mac_key);
258}
259
260}  // namespace browser_sync
261