• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.
operator <<(const std::string & value)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.
operator <<(const Nigori::Type type)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 
str()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 
Nigori()61 Nigori::Nigori() {
62 }
63 
~Nigori()64 Nigori::~Nigori() {
65 }
66 
InitByDerivation(const std::string & hostname,const std::string & username,const std::string & password)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 
InitByImport(const std::string & user_key,const std::string & encryption_key,const std::string & mac_key)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)
Permute(Type type,const std::string & name,std::string * permuted) const121 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   Base64Encode(output, permuted);
154   return true;
155 }
156 
157 // Enc[Kenc,Kmac](value)
Encrypt(const std::string & value,std::string * encrypted) const158 bool Nigori::Encrypt(const std::string& value, std::string* encrypted) const {
159   if (0U >= value.size())
160     return false;
161 
162   std::string iv;
163   crypto::RandBytes(WriteInto(&iv, kIvSize + 1), kIvSize);
164 
165   Encryptor encryptor;
166   if (!encryptor.Init(encryption_key_.get(), Encryptor::CBC, iv))
167     return false;
168 
169   std::string ciphertext;
170   if (!encryptor.Encrypt(value, &ciphertext))
171     return false;
172 
173   std::string raw_mac_key;
174   if (!mac_key_->GetRawKey(&raw_mac_key))
175     return false;
176 
177   HMAC hmac(HMAC::SHA256);
178   if (!hmac.Init(raw_mac_key))
179     return false;
180 
181   std::vector<unsigned char> hash(kHashSize);
182   if (!hmac.Sign(ciphertext, &hash[0], hash.size()))
183     return false;
184 
185   std::string output;
186   output.assign(iv);
187   output.append(ciphertext);
188   output.append(hash.begin(), hash.end());
189 
190   Base64Encode(output, encrypted);
191   return true;
192 }
193 
Decrypt(const std::string & encrypted,std::string * value) const194 bool Nigori::Decrypt(const std::string& encrypted, std::string* value) const {
195   std::string input;
196   if (!Base64Decode(encrypted, &input))
197     return false;
198 
199   if (input.size() < kIvSize * 2 + kHashSize)
200     return false;
201 
202   // The input is:
203   // * iv (16 bytes)
204   // * ciphertext (multiple of 16 bytes)
205   // * hash (32 bytes)
206   std::string iv(input.substr(0, kIvSize));
207   std::string ciphertext(input.substr(kIvSize,
208                                       input.size() - (kIvSize + kHashSize)));
209   std::string hash(input.substr(input.size() - kHashSize, kHashSize));
210 
211   std::string raw_mac_key;
212   if (!mac_key_->GetRawKey(&raw_mac_key))
213     return false;
214 
215   HMAC hmac(HMAC::SHA256);
216   if (!hmac.Init(raw_mac_key))
217     return false;
218 
219   std::vector<unsigned char> expected(kHashSize);
220   if (!hmac.Sign(ciphertext, &expected[0], expected.size()))
221     return false;
222 
223   if (hash.compare(0, hash.size(),
224                    reinterpret_cast<char *>(&expected[0]),
225                    expected.size()))
226     return false;
227 
228   Encryptor encryptor;
229   if (!encryptor.Init(encryption_key_.get(), Encryptor::CBC, iv))
230     return false;
231 
232   if (!encryptor.Decrypt(ciphertext, value))
233     return false;
234 
235   return true;
236 }
237 
ExportKeys(std::string * user_key,std::string * encryption_key,std::string * mac_key) const238 bool Nigori::ExportKeys(std::string* user_key,
239                         std::string* encryption_key,
240                         std::string* mac_key) const {
241   DCHECK(user_key);
242   DCHECK(encryption_key);
243   DCHECK(mac_key);
244 
245   return user_key_->GetRawKey(user_key) &&
246       encryption_key_->GetRawKey(encryption_key) &&
247       mac_key_->GetRawKey(mac_key);
248 }
249 
250 }  // namespace syncer
251