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