Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(81)

Side by Side Diff: crypto.cc

Issue 2645008: Update on feedback, update dbus API, add unit tests. TEST=manual,unit,BVT BUG=3628 323 (Closed) Base URL: ssh://git@chromiumos-git/cryptohome.git
Patch Set: Address second round of feedback. Created 10 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « crypto.h ('k') | crypto_unittest.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 // Copyright (c) 2009-2010 The Chromium OS 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 // Contains the implementation of class Crypto
6
7 #include "crypto.h"
8
9 #include <openssl/err.h>
10 #include <openssl/evp.h>
11 #include <openssl/rand.h>
12 #include <openssl/sha.h>
13
14 #include <base/file_util.h>
15 #include <base/logging.h>
16 #include <chromeos/utility.h>
17
18 #include "cryptohome_common.h"
19 #include "platform.h"
20 #include "username_passkey.h"
21
22 // Included last because it has conflicting defines
23 extern "C" {
24 #include <ecryptfs.h>
25 }
26
27 using std::string;
28
29 namespace cryptohome {
30
31 const std::string kDefaultEntropySource = "/dev/urandom";
32 const std::string kOpenSSLMagic = "Salted__";
33
34 Crypto::Crypto()
35 : entropy_source_(kDefaultEntropySource) {
36 }
37
38 Crypto::~Crypto() {
39 }
40
41 void Crypto::GetSecureRandom(unsigned char *rand, int length) const {
42 // TODO(fes): Get assistance from the TPM when it is available
43 // Seed the OpenSSL random number generator until it is happy
44 while (!RAND_status()) {
45 char buffer[256];
46 file_util::ReadFile(FilePath(entropy_source_), buffer, sizeof(buffer));
47 RAND_add(buffer, sizeof(buffer), sizeof(buffer));
48 }
49
50 // Have OpenSSL generate the random bytes
51 RAND_bytes(rand, length);
52 }
53
54 bool Crypto::UnwrapVaultKeyset(const chromeos::Blob& wrapped_keyset,
55 const chromeos::Blob& vault_wrapper,
56 VaultKeyset* vault_keyset) const {
57 unsigned int header_size = kOpenSSLMagic.length() + PKCS5_SALT_LEN;
58
59 if (wrapped_keyset.size() < header_size) {
60 LOG(ERROR) << "Master key file too short";
61 return false;
62 }
63
64 // Grab the salt used in converting the passkey to a key (OpenSSL
65 // passkey-encrypted files have the format:
66 // Salted__<8-byte-salt><ciphertext>)
67 unsigned char salt[PKCS5_SALT_LEN];
68 memcpy(salt, &wrapped_keyset[kOpenSSLMagic.length()], PKCS5_SALT_LEN);
69
70 unsigned char wrapper_key[EVP_MAX_KEY_LENGTH];
71 unsigned char iv[EVP_MAX_IV_LENGTH];
72
73 // Convert the passkey to a key encryption key
74 if (!EVP_BytesToKey(EVP_aes_256_cbc(), EVP_sha1(), salt, &vault_wrapper[0],
75 vault_wrapper.size(), 1, wrapper_key, iv)) {
76 LOG(ERROR) << "Failure converting bytes to key";
77 return false;
78 }
79
80 int cipher_text_size = wrapped_keyset.size() - header_size;
81 SecureBlob plain_text(cipher_text_size);
82
83 int final_size = 0;
84 int decrypt_size = plain_text.size();
85
86 // Do the actual decryption
87 EVP_CIPHER_CTX d_ctx;
88 EVP_CIPHER_CTX_init(&d_ctx);
89 EVP_DecryptInit_ex(&d_ctx, EVP_aes_256_ecb(), NULL, wrapper_key, iv);
90 if (!EVP_DecryptUpdate(&d_ctx, &plain_text[0], &decrypt_size,
91 &wrapped_keyset[header_size],
92 cipher_text_size)) {
93 LOG(ERROR) << "DecryptUpdate failed";
94 return false;
95 }
96 if (!EVP_DecryptFinal_ex(&d_ctx, &plain_text[decrypt_size], &final_size)) {
97 unsigned long err = ERR_get_error();
98 ERR_load_ERR_strings();
99 ERR_load_crypto_strings();
100
101 LOG(ERROR) << "DecryptFinal Error: " << err
102 << ": " << ERR_lib_error_string(err)
103 << ", " << ERR_func_error_string(err)
104 << ", " << ERR_reason_error_string(err);
105
106 return false;
107 }
108 final_size += decrypt_size;
109
110 plain_text.resize(final_size);
111
112 if (plain_text.size() != VaultKeyset::SerializedSize()) {
113 LOG(ERROR) << "Plain text was not the correct size: " << plain_text.size()
114 << ", expected: " << VaultKeyset::SerializedSize();
115 return false;
116 }
117
118 (*vault_keyset).AssignBuffer(plain_text);
119 return true;
120 }
121
122 bool Crypto::WrapVaultKeyset(const VaultKeyset& vault_keyset,
123 const SecureBlob& vault_wrapper,
124 const SecureBlob& vault_wrapper_salt,
125 SecureBlob* wrapped_keyset) const {
126 unsigned char wrapper_key[EVP_MAX_KEY_LENGTH];
127 unsigned char iv[EVP_MAX_IV_LENGTH];
128
129 if (vault_wrapper_salt.size() < PKCS5_SALT_LEN) {
130 LOG(ERROR) << "Vault wrapper salt was not the correct length";
131 return false;
132 }
133
134 // Convert the passkey wrapper to a key encryption key
135 if (!EVP_BytesToKey(EVP_aes_256_cbc(), EVP_sha1(), &vault_wrapper_salt[0],
136 &vault_wrapper[0], vault_wrapper.size(), 1,
137 wrapper_key, iv)) {
138 LOG(ERROR) << "Failure converting bytes to key";
139 return false;
140 }
141
142 SecureBlob keyset_blob;
143 if (!vault_keyset.ToBuffer(&keyset_blob)) {
144 LOG(ERROR) << "Failure serializing keyset to buffer";
145 return false;
146 }
147
148 // Store the salt and encrypt the master key
149 unsigned int header_size = kOpenSSLMagic.length() + PKCS5_SALT_LEN;
150 SecureBlob cipher_text(header_size
151 + keyset_blob.size()
152 + EVP_CIPHER_block_size(EVP_aes_256_ecb()));
153 memcpy(&cipher_text[0], kOpenSSLMagic.c_str(), kOpenSSLMagic.length());
154 memcpy(&cipher_text[kOpenSSLMagic.length()], vault_wrapper_salt.const_data(),
155 PKCS5_SALT_LEN);
156
157 int current_size = header_size;
158 int encrypt_size = 0;
159 EVP_CIPHER_CTX e_ctx;
160 EVP_CIPHER_CTX_init(&e_ctx);
161
162 // Encrypt the keyset
163 EVP_EncryptInit_ex(&e_ctx, EVP_aes_256_ecb(), NULL, wrapper_key, iv);
164 if (!EVP_EncryptUpdate(&e_ctx, &cipher_text[current_size], &encrypt_size,
165 &keyset_blob[0],
166 keyset_blob.size())) {
167 LOG(ERROR) << "EncryptUpdate failed";
168 chromeos::SecureMemset(wrapper_key, 0, sizeof(wrapper_key));
169 return false;
170 }
171 current_size += encrypt_size;
172 encrypt_size = 0;
173
174 // Finish the encryption
175 if (!EVP_EncryptFinal_ex(&e_ctx, &cipher_text[current_size], &encrypt_size)) {
176 LOG(ERROR) << "EncryptFinal failed";
177 chromeos::SecureMemset(wrapper_key, 0, sizeof(wrapper_key));
178 return false;
179 }
180 current_size += encrypt_size;
181 cipher_text.resize(current_size);
182
183 chromeos::SecureMemset(wrapper_key, 0, sizeof(wrapper_key));
184
185 wrapped_keyset->swap(cipher_text);
186 return true;
187 }
188
189 void Crypto::PasskeyToWrapper(const chromeos::Blob& passkey,
190 const chromeos::Blob& salt, int iters,
191 SecureBlob* wrapper) const {
192 // TODO(fes): Update this when TPM support is available, or use a memory-
193 // bound strengthening algorithm.
194 int update_length = passkey.size();
195 int holder_size = SHA_DIGEST_LENGTH;
196 if (update_length > SHA_DIGEST_LENGTH) {
197 holder_size = update_length;
198 }
199 SecureBlob holder(holder_size);
200 memcpy(&holder[0], &passkey[0], update_length);
201
202 // Repeatedly hash the user passkey and salt to generate the wrapper
203 for (int i = 0; i < iters; ++i) {
204 SHA_CTX ctx;
205 unsigned char md_value[SHA_DIGEST_LENGTH];
206
207 SHA1_Init(&ctx);
208 SHA1_Update(&ctx, &salt[0], salt.size());
209 SHA1_Update(&ctx, &holder[0], update_length);
210 SHA1_Final(md_value, &ctx);
211
212 memcpy(&holder[0], md_value, SHA_DIGEST_LENGTH);
213 update_length = SHA_DIGEST_LENGTH;
214 }
215
216 holder.resize(update_length);
217 SecureBlob local_wrapper(update_length * 2);
218 AsciiEncodeToBuffer(holder, static_cast<char*>(local_wrapper.data()),
219 local_wrapper.size());
220 wrapper->swap(local_wrapper);
221 }
222
223 bool Crypto::GetOrCreateSalt(const FilePath& path, int length, bool force,
224 SecureBlob* salt) const {
225 SecureBlob local_salt;
226 if (force || !file_util::PathExists(path)) {
227 // If this salt doesn't exist, automatically create it
228 local_salt.resize(length);
229 GetSecureRandom(static_cast<unsigned char*>(local_salt.data()),
230 local_salt.size());
231 int data_written = file_util::WriteFile(path,
232 static_cast<const char*>(local_salt.const_data()),
233 length);
234 if (data_written != length) {
235 LOG(ERROR) << "Could not write user salt";
236 return false;
237 }
238 } else {
239 // Otherwise just load the contents of the salt
240 int64 file_size;
241 if (!file_util::GetFileSize(path, &file_size)) {
242 LOG(ERROR) << "Could not get size of " << path.value();
243 return false;
244 }
245 if (file_size > INT_MAX) {
246 LOG(ERROR) << "File " << path.value() << " is too large: " << file_size;
247 return false;
248 }
249 local_salt.resize(file_size);
250 int data_read = file_util::ReadFile(path,
251 static_cast<char*>(local_salt.data()),
252 local_salt.size());
253 if (data_read != file_size) {
254 LOG(ERROR) << "Could not read entire file " << file_size;
255 return false;
256 }
257 }
258 salt->swap(local_salt);
259 return true;
260 }
261
262 bool Crypto::AddKeyset(const VaultKeyset& vault_keyset,
263 std::string* key_signature,
264 std::string* fnek_signature) const {
265 // Add the FEK
266 *key_signature = chromeos::AsciiEncode(vault_keyset.FEK_SIG());
267 if (!PushVaultKey(vault_keyset.FEK(), *key_signature,
268 vault_keyset.FEK_SALT())) {
269 LOG(ERROR) << "Couldn't add ecryptfs key to keyring";
270 return false;
271 }
272
273 // Add the FNEK
274 *fnek_signature = chromeos::AsciiEncode(vault_keyset.FNEK_SIG());
275 if (!PushVaultKey(vault_keyset.FNEK(), *fnek_signature,
276 vault_keyset.FNEK_SALT())) {
277 LOG(ERROR) << "Couldn't add ecryptfs fnek key to keyring";
278 return false;
279 }
280
281 return true;
282 }
283
284 void Crypto::ClearKeyset() const {
285 Platform::ClearUserKeyring();
286 }
287
288 bool Crypto::PushVaultKey(const SecureBlob& key, const std::string& key_sig,
289 const SecureBlob& salt) const {
290 DCHECK(key.size() == ECRYPTFS_MAX_KEY_BYTES);
291 DCHECK(key_sig.length() == (ECRYPTFS_SIG_SIZE * 2));
292 DCHECK(salt.size() == ECRYPTFS_SALT_SIZE);
293
294 struct ecryptfs_auth_tok auth_token;
295
296 generate_payload(&auth_token, const_cast<char*>(key_sig.c_str()),
297 const_cast<char*>(reinterpret_cast<const char*>(&salt[0])),
298 const_cast<char*>(reinterpret_cast<const char*>(&key[0])));
299
300 if (ecryptfs_add_auth_tok_to_keyring(&auth_token,
301 const_cast<char*>(key_sig.c_str())) < 0) {
302 LOG(ERROR) << "PushVaultKey failed";
303 }
304
305 return true;
306 }
307
308 void Crypto::PasswordToPasskey(const char *password,
309 const chromeos::Blob& salt,
310 SecureBlob* passkey) {
311 CHECK(password);
312
313 std::string ascii_salt = chromeos::AsciiEncode(salt);
314 // Convert a raw password to a password hash
315 SHA256_CTX sha_ctx;
316 SecureBlob md_value(SHA256_DIGEST_LENGTH);
317
318 SHA256_Init(&sha_ctx);
319 SHA256_Update(&sha_ctx,
320 reinterpret_cast<const unsigned char*>(ascii_salt.data()),
321 static_cast<unsigned int>(ascii_salt.length()));
322 SHA256_Update(&sha_ctx, password, strlen(password));
323 SHA256_Final(static_cast<unsigned char*>(md_value.data()), &sha_ctx);
324
325 md_value.resize(SHA256_DIGEST_LENGTH / 2);
326 SecureBlob local_passkey(SHA256_DIGEST_LENGTH);
327 AsciiEncodeToBuffer(md_value, static_cast<char*>(local_passkey.data()),
328 local_passkey.size());
329 passkey->swap(local_passkey);
330 }
331
332 void Crypto::AsciiEncodeToBuffer(const chromeos::Blob& blob, char* buffer,
333 int buffer_length) {
334 const char hex_chars[] = "0123456789abcdef";
335 int i = 0;
336 for (chromeos::Blob::const_iterator it = blob.begin();
337 it < blob.end() && (i + 1) < buffer_length; ++it) {
338 buffer[i++] = hex_chars[((*it) >> 4) & 0x0f];
339 buffer[i++] = hex_chars[(*it) & 0x0f];
340 }
341 if (i < buffer_length) {
342 buffer[i] = '\0';
343 }
344 }
345
346 } // namespace cryptohome
OLDNEW
« no previous file with comments | « crypto.h ('k') | crypto_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698