• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (c) 2011 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 #else
10 #include <arpa/inet.h>
11 #endif
12 
13 #include <sstream>
14 #include <vector>
15 
16 #include "base/base64.h"
17 #include "base/logging.h"
18 #include "base/rand_util.h"
19 #include "base/string_util.h"
20 #include "crypto/encryptor.h"
21 #include "crypto/hmac.h"
22 
23 using base::Base64Encode;
24 using base::Base64Decode;
25 using base::RandInt;
26 using crypto::Encryptor;
27 using crypto::HMAC;
28 using crypto::SymmetricKey;
29 
30 namespace browser_sync {
31 
32 // NigoriStream simplifies the concatenation operation of the Nigori protocol.
33 class NigoriStream {
34  public:
35   // Append the big-endian representation of the length of |value| with 32 bits,
36   // followed by |value| itself to the stream.
operator <<(const std::string & value)37   NigoriStream& operator<<(const std::string& value) {
38     uint32 size = htonl(value.size());
39     stream_.write((char *) &size, sizeof(uint32));
40     stream_ << value;
41     return *this;
42   }
43 
44   // Append the big-endian representation of the length of |type| with 32 bits,
45   // followed by the big-endian representation of the value of |type|, with 32
46   // bits, to the stream.
operator <<(const Nigori::Type type)47   NigoriStream& operator<<(const Nigori::Type type) {
48     uint32 size = htonl(sizeof(uint32));
49     stream_.write((char *) &size, sizeof(uint32));
50     uint32 value = htonl(type);
51     stream_.write((char *) &value, sizeof(uint32));
52     return *this;
53   }
54 
str()55   std::string str() {
56     return stream_.str();
57   }
58 
59  private:
60   std::ostringstream stream_;
61 };
62 
63 // static
64 const char Nigori::kSaltSalt[] = "saltsalt";
65 
Nigori()66 Nigori::Nigori() {
67 }
68 
~Nigori()69 Nigori::~Nigori() {
70 }
71 
InitByDerivation(const std::string & hostname,const std::string & username,const std::string & password)72 bool Nigori::InitByDerivation(const std::string& hostname,
73                               const std::string& username,
74                               const std::string& password) {
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   if (!user_salt->GetRawKey(&raw_user_salt))
88     return false;
89 
90   // Kuser = PBKDF2(P, Suser, Nuser, 16)
91   user_key_.reset(SymmetricKey::DeriveKeyFromPassword(SymmetricKey::AES,
92       password, raw_user_salt, kUserIterations, kDerivedKeySizeInBits));
93   DCHECK(user_key_.get());
94 
95   // Kenc = PBKDF2(P, Suser, Nenc, 16)
96   encryption_key_.reset(SymmetricKey::DeriveKeyFromPassword(SymmetricKey::AES,
97       password, raw_user_salt, kEncryptionIterations, kDerivedKeySizeInBits));
98   DCHECK(encryption_key_.get());
99 
100   // Kmac = PBKDF2(P, Suser, Nmac, 16)
101   mac_key_.reset(SymmetricKey::DeriveKeyFromPassword(
102       SymmetricKey::HMAC_SHA1, password, raw_user_salt, kSigningIterations,
103       kDerivedKeySizeInBits));
104   DCHECK(mac_key_.get());
105 
106   return true;
107 }
108 
InitByImport(const std::string & user_key,const std::string & encryption_key,const std::string & mac_key)109 bool Nigori::InitByImport(const std::string& user_key,
110                           const std::string& encryption_key,
111                           const std::string& mac_key) {
112   user_key_.reset(SymmetricKey::Import(SymmetricKey::AES, user_key));
113   DCHECK(user_key_.get());
114 
115   encryption_key_.reset(SymmetricKey::Import(SymmetricKey::AES,
116                                              encryption_key));
117   DCHECK(encryption_key_.get());
118 
119   mac_key_.reset(SymmetricKey::Import(SymmetricKey::HMAC_SHA1, mac_key));
120   DCHECK(mac_key_.get());
121 
122   return user_key_.get() && encryption_key_.get() && mac_key_.get();
123 }
124 
125 // Permute[Kenc,Kmac](type || name)
Permute(Type type,const std::string & name,std::string * permuted) const126 bool Nigori::Permute(Type type, const std::string& name,
127                      std::string* permuted) const {
128   DCHECK_LT(0U, name.size());
129 
130   NigoriStream plaintext;
131   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 
GenerateRandomString(size_t size)161 std::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)
Encrypt(const std::string & value,std::string * encrypted) const170 bool 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 
Decrypt(const std::string & encrypted,std::string * value) const203 bool 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 
ExportKeys(std::string * user_key,std::string * encryption_key,std::string * mac_key) const248 bool 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