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 #include <sstream> | |
8 #include <vector> | |
9 | |
10 #include "base/base64.h" | |
11 #include "base/logging.h" | |
12 #include "base/rand_util.h" | |
13 #include "base/string_util.h" | |
14 #include "base/sys_byteorder.h" | |
15 #include "crypto/encryptor.h" | |
16 #include "crypto/hmac.h" | |
17 | |
18 using base::Base64Encode; | |
19 using base::Base64Decode; | |
20 using base::RandInt; | |
21 using crypto::Encryptor; | |
22 using crypto::HMAC; | |
23 using crypto::SymmetricKey; | |
24 | |
25 namespace browser_sync { | |
26 | |
27 // NigoriStream simplifies the concatenation operation of the Nigori protocol. | |
28 class 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 = htonl(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 = htonl(sizeof(uint32)); | |
44 stream_.write((char *) &size, sizeof(uint32)); | |
45 uint32 value = htonl(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 | |
59 const char Nigori::kSaltSalt[] = "saltsalt"; | |
60 | |
61 Nigori::Nigori() { | |
62 } | |
63 | |
64 Nigori::~Nigori() { | |
65 } | |
66 | |
67 bool 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 | |
104 bool 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) | |
121 bool 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 return Base64Encode(output, permuted); | |
154 } | |
155 | |
156 std::string GenerateRandomString(size_t size) { | |
157 // TODO(albertb): Use a secure random function. | |
158 std::string random(size, 0); | |
159 for (size_t i = 0; i < size; ++i) | |
160 random[i] = RandInt(0, 0xff); | |
161 return random; | |
162 } | |
163 | |
164 // Enc[Kenc,Kmac](value) | |
165 bool Nigori::Encrypt(const std::string& value, std::string* encrypted) const { | |
166 if (0U >= value.size()) | |
167 return false; | |
168 | |
169 std::string iv = GenerateRandomString(kIvSize); | |
170 | |
171 Encryptor encryptor; | |
172 if (!encryptor.Init(encryption_key_.get(), Encryptor::CBC, iv)) | |
173 return false; | |
174 | |
175 std::string ciphertext; | |
176 if (!encryptor.Encrypt(value, &ciphertext)) | |
177 return false; | |
178 | |
179 std::string raw_mac_key; | |
180 if (!mac_key_->GetRawKey(&raw_mac_key)) | |
181 return false; | |
182 | |
183 HMAC hmac(HMAC::SHA256); | |
184 if (!hmac.Init(raw_mac_key)) | |
185 return false; | |
186 | |
187 std::vector<unsigned char> hash(kHashSize); | |
188 if (!hmac.Sign(ciphertext, &hash[0], hash.size())) | |
189 return false; | |
190 | |
191 std::string output; | |
192 output.assign(iv); | |
193 output.append(ciphertext); | |
194 output.append(hash.begin(), hash.end()); | |
195 | |
196 return Base64Encode(output, encrypted); | |
197 } | |
198 | |
199 bool Nigori::Decrypt(const std::string& encrypted, std::string* value) const { | |
200 std::string input; | |
201 if (!Base64Decode(encrypted, &input)) | |
202 return false; | |
203 | |
204 if (input.size() < kIvSize * 2 + kHashSize) | |
205 return false; | |
206 | |
207 // The input is: | |
208 // * iv (16 bytes) | |
209 // * ciphertext (multiple of 16 bytes) | |
210 // * hash (32 bytes) | |
211 std::string iv(input.substr(0, kIvSize)); | |
212 std::string ciphertext(input.substr(kIvSize, | |
213 input.size() - (kIvSize + kHashSize))); | |
214 std::string hash(input.substr(input.size() - kHashSize, kHashSize)); | |
215 | |
216 std::string raw_mac_key; | |
217 if (!mac_key_->GetRawKey(&raw_mac_key)) | |
218 return false; | |
219 | |
220 HMAC hmac(HMAC::SHA256); | |
221 if (!hmac.Init(raw_mac_key)) | |
222 return false; | |
223 | |
224 std::vector<unsigned char> expected(kHashSize); | |
225 if (!hmac.Sign(ciphertext, &expected[0], expected.size())) | |
226 return false; | |
227 | |
228 if (hash.compare(0, hash.size(), | |
229 reinterpret_cast<char *>(&expected[0]), | |
230 expected.size())) | |
231 return false; | |
232 | |
233 Encryptor encryptor; | |
234 if (!encryptor.Init(encryption_key_.get(), Encryptor::CBC, iv)) | |
235 return false; | |
236 | |
237 std::string plaintext; | |
238 if (!encryptor.Decrypt(ciphertext, value)) | |
239 return false; | |
240 | |
241 return true; | |
242 } | |
243 | |
244 bool Nigori::ExportKeys(std::string* user_key, | |
245 std::string* encryption_key, | |
246 std::string* mac_key) const { | |
247 DCHECK(user_key); | |
248 DCHECK(encryption_key); | |
249 DCHECK(mac_key); | |
250 | |
251 return user_key_->GetRawKey(user_key) && | |
252 encryption_key_->GetRawKey(encryption_key) && | |
253 mac_key_->GetRawKey(mac_key); | |
254 } | |
255 | |
256 } // namespace browser_sync | |
OLD | NEW |