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.
5 #include "sync/util/nigori.h"
10 #include "base/base64.h"
11 #include "base/logging.h"
12 #include "base/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"
19 using base::Base64Encode
;
20 using base::Base64Decode
;
21 using crypto::Encryptor
;
23 using crypto::SymmetricKey
;
27 // NigoriStream simplifies the concatenation operation of the Nigori protocol.
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
));
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
));
55 std::ostringstream stream_
;
59 const char Nigori::kSaltSalt
[] = "saltsalt";
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
;
73 // Suser = PBKDF2(Username || Servername, "saltsalt", Nsalt, 8)
74 scoped_ptr
<SymmetricKey
> user_salt(SymmetricKey::DeriveKeyFromPassword(
75 SymmetricKey::HMAC_SHA1
, salt_password
.str(),
79 DCHECK(user_salt
.get());
81 std::string raw_user_salt
;
82 if (!user_salt
->GetRawKey(&raw_user_salt
))
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());
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());
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());
101 return user_key_
.get() && encryption_key_
.get() && mac_key_
.get();
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());
110 encryption_key_
.reset(SymmetricKey::Import(SymmetricKey::AES
,
112 DCHECK(encryption_key_
.get());
114 mac_key_
.reset(SymmetricKey::Import(SymmetricKey::HMAC_SHA1
, mac_key
));
115 DCHECK(mac_key_
.get());
117 return user_key_
.get() && encryption_key_
.get() && mac_key_
.get();
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());
125 NigoriStream plaintext
;
126 plaintext
<< type
<< name
;
129 if (!encryptor
.Init(encryption_key_
.get(), Encryptor::CBC
,
130 std::string(kIvSize
, 0)))
133 std::string ciphertext
;
134 if (!encryptor
.Encrypt(plaintext
.str(), &ciphertext
))
137 std::string raw_mac_key
;
138 if (!mac_key_
->GetRawKey(&raw_mac_key
))
141 HMAC
hmac(HMAC::SHA256
);
142 if (!hmac
.Init(raw_mac_key
))
145 std::vector
<unsigned char> hash(kHashSize
);
146 if (!hmac
.Sign(ciphertext
, &hash
[0], hash
.size()))
150 output
.assign(ciphertext
);
151 output
.append(hash
.begin(), hash
.end());
153 return Base64Encode(output
, permuted
);
156 // Enc[Kenc,Kmac](value)
157 bool Nigori::Encrypt(const std::string
& value
, std::string
* encrypted
) const {
158 if (0U >= value
.size())
162 crypto::RandBytes(WriteInto(&iv
, kIvSize
+ 1), kIvSize
);
165 if (!encryptor
.Init(encryption_key_
.get(), Encryptor::CBC
, iv
))
168 std::string ciphertext
;
169 if (!encryptor
.Encrypt(value
, &ciphertext
))
172 std::string raw_mac_key
;
173 if (!mac_key_
->GetRawKey(&raw_mac_key
))
176 HMAC
hmac(HMAC::SHA256
);
177 if (!hmac
.Init(raw_mac_key
))
180 std::vector
<unsigned char> hash(kHashSize
);
181 if (!hmac
.Sign(ciphertext
, &hash
[0], hash
.size()))
186 output
.append(ciphertext
);
187 output
.append(hash
.begin(), hash
.end());
189 return Base64Encode(output
, encrypted
);
192 bool Nigori::Decrypt(const std::string
& encrypted
, std::string
* value
) const {
194 if (!Base64Decode(encrypted
, &input
))
197 if (input
.size() < kIvSize
* 2 + kHashSize
)
202 // * ciphertext (multiple of 16 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
));
209 std::string raw_mac_key
;
210 if (!mac_key_
->GetRawKey(&raw_mac_key
))
213 HMAC
hmac(HMAC::SHA256
);
214 if (!hmac
.Init(raw_mac_key
))
217 std::vector
<unsigned char> expected(kHashSize
);
218 if (!hmac
.Sign(ciphertext
, &expected
[0], expected
.size()))
221 if (hash
.compare(0, hash
.size(),
222 reinterpret_cast<char *>(&expected
[0]),
227 if (!encryptor
.Init(encryption_key_
.get(), Encryptor::CBC
, iv
))
230 std::string plaintext
;
231 if (!encryptor
.Decrypt(ciphertext
, value
))
237 bool Nigori::ExportKeys(std::string
* user_key
,
238 std::string
* encryption_key
,
239 std::string
* mac_key
) const {
241 DCHECK(encryption_key
);
244 return user_key_
->GetRawKey(user_key
) &&
245 encryption_key_
->GetRawKey(encryption_key
) &&
246 mac_key_
->GetRawKey(mac_key
);
249 } // namespace syncer