OLD | NEW |
| (Empty) |
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. | |
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. | |
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 | |
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 | |
66 Nigori::Nigori() { | |
67 } | |
68 | |
69 Nigori::~Nigori() { | |
70 } | |
71 | |
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 user_key_.get() && encryption_key_.get() && mac_key_.get(); | |
107 } | |
108 | |
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) | |
126 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 | |
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) | |
170 bool Nigori::Encrypt(const std::string& value, std::string* encrypted) const { | |
171 if (0U >= value.size()) | |
172 return false; | |
173 | |
174 std::string iv = GenerateRandomString(kIvSize); | |
175 | |
176 Encryptor encryptor; | |
177 if (!encryptor.Init(encryption_key_.get(), Encryptor::CBC, iv)) | |
178 return false; | |
179 | |
180 std::string ciphertext; | |
181 if (!encryptor.Encrypt(value, &ciphertext)) | |
182 return false; | |
183 | |
184 std::string raw_mac_key; | |
185 if (!mac_key_->GetRawKey(&raw_mac_key)) | |
186 return false; | |
187 | |
188 HMAC hmac(HMAC::SHA256); | |
189 if (!hmac.Init(raw_mac_key)) | |
190 return false; | |
191 | |
192 std::vector<unsigned char> hash(kHashSize); | |
193 if (!hmac.Sign(ciphertext, &hash[0], hash.size())) | |
194 return false; | |
195 | |
196 std::string output; | |
197 output.assign(iv); | |
198 output.append(ciphertext); | |
199 output.append(hash.begin(), hash.end()); | |
200 | |
201 return Base64Encode(output, encrypted); | |
202 } | |
203 | |
204 bool Nigori::Decrypt(const std::string& encrypted, std::string* value) const { | |
205 std::string input; | |
206 if (!Base64Decode(encrypted, &input)) | |
207 return false; | |
208 | |
209 if (input.size() < kIvSize * 2 + kHashSize) | |
210 return false; | |
211 | |
212 // The input is: | |
213 // * iv (16 bytes) | |
214 // * ciphertext (multiple of 16 bytes) | |
215 // * hash (32 bytes) | |
216 std::string iv(input.substr(0, kIvSize)); | |
217 std::string ciphertext(input.substr(kIvSize, | |
218 input.size() - (kIvSize + kHashSize))); | |
219 std::string hash(input.substr(input.size() - kHashSize, kHashSize)); | |
220 | |
221 std::string raw_mac_key; | |
222 if (!mac_key_->GetRawKey(&raw_mac_key)) | |
223 return false; | |
224 | |
225 HMAC hmac(HMAC::SHA256); | |
226 if (!hmac.Init(raw_mac_key)) | |
227 return false; | |
228 | |
229 std::vector<unsigned char> expected(kHashSize); | |
230 if (!hmac.Sign(ciphertext, &expected[0], expected.size())) | |
231 return false; | |
232 | |
233 if (hash.compare(0, hash.size(), | |
234 reinterpret_cast<char *>(&expected[0]), | |
235 expected.size())) | |
236 return false; | |
237 | |
238 Encryptor encryptor; | |
239 if (!encryptor.Init(encryption_key_.get(), Encryptor::CBC, iv)) | |
240 return false; | |
241 | |
242 std::string plaintext; | |
243 if (!encryptor.Decrypt(ciphertext, value)) | |
244 return false; | |
245 | |
246 return true; | |
247 } | |
248 | |
249 bool Nigori::ExportKeys(std::string* user_key, | |
250 std::string* encryption_key, | |
251 std::string* mac_key) const { | |
252 DCHECK(user_key); | |
253 DCHECK(encryption_key); | |
254 DCHECK(mac_key); | |
255 | |
256 return user_key_->GetRawKey(user_key) && | |
257 encryption_key_->GetRawKey(encryption_key) && | |
258 mac_key_->GetRawKey(mac_key); | |
259 } | |
260 | |
261 } // namespace browser_sync | |
OLD | NEW |