| // Copyright (c) 2011 The Chromium OS Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "shill/crypto_des_cbc.h" |
| |
| #include <rpc/des_crypt.h> |
| |
| #include <base/file_util.h> |
| #include <base/string_util.h> |
| |
| #include "shill/glib.h" |
| |
| using std::string; |
| using std::vector; |
| |
| namespace shill { |
| |
| const int CryptoDESCBC::kBlockSize = 8; |
| const char CryptoDESCBC::kID[] = "des-cbc"; |
| const char CryptoDESCBC::kSentinel[] = "[ok]"; |
| const char CryptoDESCBC::kVersion2Prefix[] = "02:"; |
| |
| CryptoDESCBC::CryptoDESCBC(GLib *glib) : glib_(glib) {} |
| |
| string CryptoDESCBC::GetID() { |
| return kID; |
| } |
| |
| bool CryptoDESCBC::Encrypt(const string &plaintext, string *ciphertext) { |
| CHECK_EQ(kBlockSize, key_.size()); |
| CHECK_EQ(kBlockSize, iv_.size()); |
| |
| // Prepare the data to be encrypted by concatenating |plaintext| and |
| // |kSentinel|, and appending nulls to a size multiple of |kBlockSize|. |
| vector<char> data(plaintext.begin(), plaintext.end()); |
| data.insert(data.end(), kSentinel, kSentinel + strlen(kSentinel)); |
| // +1 to encrypt at least one null terminator. +(kBlockSize - 1) to pad to a |
| // full block, if necessary. |
| int blocks = (data.size() + 1 + (kBlockSize - 1)) / kBlockSize; |
| data.resize(blocks * kBlockSize, '\0'); |
| |
| // The IV is modified in place. |
| vector<char> iv = iv_; |
| int rv = |
| cbc_crypt(key_.data(), data.data(), data.size(), DES_ENCRYPT, iv.data()); |
| if (DES_FAILED(rv)) { |
| LOG(ERROR) << "DES-CBC encryption failed."; |
| return false; |
| } |
| gchar *b64_ciphertext = |
| glib_->Base64Encode(reinterpret_cast<guchar *>(data.data()), data.size()); |
| if (!b64_ciphertext) { |
| LOG(ERROR) << "Unable to base64-encode DES-CBC ciphertext."; |
| return false; |
| } |
| *ciphertext = string(kVersion2Prefix) + b64_ciphertext; |
| glib_->Free(b64_ciphertext); |
| return true; |
| } |
| |
| bool CryptoDESCBC::Decrypt(const string &ciphertext, string *plaintext) { |
| CHECK_EQ(kBlockSize, key_.size()); |
| CHECK_EQ(kBlockSize, iv_.size()); |
| int version = 1; |
| string b64_ciphertext = ciphertext; |
| if (StartsWithASCII(ciphertext, kVersion2Prefix, true)) { |
| version = 2; |
| b64_ciphertext.erase(0, strlen(kVersion2Prefix)); |
| } |
| |
| gsize data_len = 0; |
| guchar *gdata = glib_->Base64Decode(b64_ciphertext.c_str(), &data_len); |
| if (!gdata) { |
| LOG(ERROR) << "Unable to base64-decode DEC-CBC ciphertext."; |
| return false; |
| } |
| |
| vector<char> data(gdata, gdata + data_len); |
| glib_->Free(gdata); |
| if (data.empty() || (data.size() % kBlockSize != 0)) { |
| LOG(ERROR) << "Invalid DES-CBC ciphertext size: " << data.size(); |
| return false; |
| } |
| |
| // The IV is modified in place. |
| vector<char> iv = iv_; |
| int rv = |
| cbc_crypt(key_.data(), data.data(), data.size(), DES_DECRYPT, iv.data()); |
| if (DES_FAILED(rv)) { |
| LOG(ERROR) << "DES-CBC decryption failed."; |
| return false; |
| } |
| if (data.back() != '\0') { |
| LOG(ERROR) << "DEC-CBC decryption resulted in invalid plain text."; |
| return false; |
| } |
| string text = data.data(); |
| if (version == 2) { |
| if (!EndsWith(text, kSentinel, true)) { |
| LOG(ERROR) << "DES-CBC decrypted text missing sentinel -- bad key?"; |
| return false; |
| } |
| text.erase(text.size() - strlen(kSentinel), strlen(kSentinel)); |
| } |
| *plaintext = text; |
| return true; |
| } |
| |
| bool CryptoDESCBC::LoadKeyMatter(const FilePath &path) { |
| key_.clear(); |
| iv_.clear(); |
| string matter; |
| // TODO(petkov): This mimics current flimflam behavior. Fix it so that it |
| // doesn't read the whole file. |
| if (!file_util::ReadFileToString(path, &matter)) { |
| LOG(ERROR) << "Unable to load key matter from " << path.value(); |
| return false; |
| } |
| if (matter.size() < 2 * kBlockSize) { |
| LOG(ERROR) << "Key matter data not enough " << matter.size() << " < " |
| << 2 * kBlockSize; |
| return false; |
| } |
| string::const_iterator matter_start = |
| matter.begin() + (matter.size() - 2 * kBlockSize); |
| key_.assign(matter_start + kBlockSize, matter_start + 2 * kBlockSize); |
| iv_.assign(matter_start, matter_start + kBlockSize); |
| return true; |
| } |
| |
| } // namespace shill |