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