| OLD | NEW |
| (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 |
| OLD | NEW |