- add sources.
[platform/framework/web/crosswalk.git] / src / sync / util / nigori.cc
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
19 using base::Base64Encode;
20 using base::Base64Decode;
21 using crypto::Encryptor;
22 using crypto::HMAC;
23 using crypto::SymmetricKey;
24
25 namespace syncer {
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 = 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
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 // Enc[Kenc,Kmac](value)
157 bool Nigori::Encrypt(const std::string& value, std::string* encrypted) const {
158   if (0U >= value.size())
159     return false;
160
161   std::string iv;
162   crypto::RandBytes(WriteInto(&iv, kIvSize + 1), kIvSize);
163
164   Encryptor encryptor;
165   if (!encryptor.Init(encryption_key_.get(), Encryptor::CBC, iv))
166     return false;
167
168   std::string ciphertext;
169   if (!encryptor.Encrypt(value, &ciphertext))
170     return false;
171
172   std::string raw_mac_key;
173   if (!mac_key_->GetRawKey(&raw_mac_key))
174     return false;
175
176   HMAC hmac(HMAC::SHA256);
177   if (!hmac.Init(raw_mac_key))
178     return false;
179
180   std::vector<unsigned char> hash(kHashSize);
181   if (!hmac.Sign(ciphertext, &hash[0], hash.size()))
182     return false;
183
184   std::string output;
185   output.assign(iv);
186   output.append(ciphertext);
187   output.append(hash.begin(), hash.end());
188
189   return Base64Encode(output, encrypted);
190 }
191
192 bool Nigori::Decrypt(const std::string& encrypted, std::string* value) const {
193   std::string input;
194   if (!Base64Decode(encrypted, &input))
195     return false;
196
197   if (input.size() < kIvSize * 2 + kHashSize)
198     return false;
199
200   // The input is:
201   // * iv (16 bytes)
202   // * ciphertext (multiple of 16 bytes)
203   // * hash (32 bytes)
204   std::string iv(input.substr(0, kIvSize));
205   std::string ciphertext(input.substr(kIvSize,
206                                       input.size() - (kIvSize + kHashSize)));
207   std::string hash(input.substr(input.size() - kHashSize, kHashSize));
208
209   std::string raw_mac_key;
210   if (!mac_key_->GetRawKey(&raw_mac_key))
211     return false;
212
213   HMAC hmac(HMAC::SHA256);
214   if (!hmac.Init(raw_mac_key))
215     return false;
216
217   std::vector<unsigned char> expected(kHashSize);
218   if (!hmac.Sign(ciphertext, &expected[0], expected.size()))
219     return false;
220
221   if (hash.compare(0, hash.size(),
222                    reinterpret_cast<char *>(&expected[0]),
223                    expected.size()))
224     return false;
225
226   Encryptor encryptor;
227   if (!encryptor.Init(encryption_key_.get(), Encryptor::CBC, iv))
228     return false;
229
230   if (!encryptor.Decrypt(ciphertext, value))
231     return false;
232
233   return true;
234 }
235
236 bool Nigori::ExportKeys(std::string* user_key,
237                         std::string* encryption_key,
238                         std::string* mac_key) const {
239   DCHECK(user_key);
240   DCHECK(encryption_key);
241   DCHECK(mac_key);
242
243   return user_key_->GetRawKey(user_key) &&
244       encryption_key_->GetRawKey(encryption_key) &&
245       mac_key_->GetRawKey(mac_key);
246 }
247
248 }  // namespace syncer