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/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"
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 Base64Encode(output
, permuted
);
157 // Enc[Kenc,Kmac](value)
158 bool Nigori::Encrypt(const std::string
& value
, std::string
* encrypted
) const {
159 if (0U >= value
.size())
163 crypto::RandBytes(base::WriteInto(&iv
, kIvSize
+ 1), kIvSize
);
166 if (!encryptor
.Init(encryption_key_
.get(), Encryptor::CBC
, iv
))
169 std::string ciphertext
;
170 if (!encryptor
.Encrypt(value
, &ciphertext
))
173 std::string raw_mac_key
;
174 if (!mac_key_
->GetRawKey(&raw_mac_key
))
177 HMAC
hmac(HMAC::SHA256
);
178 if (!hmac
.Init(raw_mac_key
))
181 std::vector
<unsigned char> hash(kHashSize
);
182 if (!hmac
.Sign(ciphertext
, &hash
[0], hash
.size()))
187 output
.append(ciphertext
);
188 output
.append(hash
.begin(), hash
.end());
190 Base64Encode(output
, encrypted
);
194 bool Nigori::Decrypt(const std::string
& encrypted
, std::string
* value
) const {
196 if (!Base64Decode(encrypted
, &input
))
199 if (input
.size() < kIvSize
* 2 + kHashSize
)
204 // * ciphertext (multiple of 16 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
));
211 std::string raw_mac_key
;
212 if (!mac_key_
->GetRawKey(&raw_mac_key
))
215 HMAC
hmac(HMAC::SHA256
);
216 if (!hmac
.Init(raw_mac_key
))
219 std::vector
<unsigned char> expected(kHashSize
);
220 if (!hmac
.Sign(ciphertext
, &expected
[0], expected
.size()))
223 if (hash
.compare(0, hash
.size(),
224 reinterpret_cast<char *>(&expected
[0]),
229 if (!encryptor
.Init(encryption_key_
.get(), Encryptor::CBC
, iv
))
232 if (!encryptor
.Decrypt(ciphertext
, value
))
238 bool Nigori::ExportKeys(std::string
* user_key
,
239 std::string
* encryption_key
,
240 std::string
* mac_key
) const {
242 DCHECK(encryption_key
);
245 return user_key_
->GetRawKey(user_key
) &&
246 encryption_key_
->GetRawKey(encryption_key
) &&
247 mac_key_
->GetRawKey(mac_key
);
250 } // namespace syncer