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