Chromium Code Reviews| Index: chrome/browser/chromeos/cros/onc_network_parser.cc |
| diff --git a/chrome/browser/chromeos/cros/onc_network_parser.cc b/chrome/browser/chromeos/cros/onc_network_parser.cc |
| index 69ba0da80e4878f12978c52fb60bb23e3fee8943..bf0151acdac3b7cfe843886d065b84ab6e48f229 100644 |
| --- a/chrome/browser/chromeos/cros/onc_network_parser.cc |
| +++ b/chrome/browser/chromeos/cros/onc_network_parser.cc |
| @@ -17,6 +17,9 @@ |
| #include "chrome/browser/chromeos/cros/network_library.h" |
| #include "chrome/browser/chromeos/cros/onc_constants.h" |
| #include "chrome/common/net/x509_certificate_model.h" |
| +#include "crypto/scoped_nss_types.h" |
| +#include "crypto/symmetric_key.h" |
| +#include "crypto/encryptor.h" |
| #include "grit/generated_resources.h" |
| #include "net/base/cert_database.h" |
| #include "net/base/crypto_module.h" |
| @@ -194,11 +197,65 @@ std::string ConvertValueToString(const base::Value& value) { |
| return value_json; |
| } |
| +bool VerifyHMAC(const std::string& hmac, |
| + const std::string& plaintext, |
| + const crypto::SymmetricKey* key) { |
|
Ryan Sleevi
2011/12/24 07:34:08
See crypto::HMAC::Verify()
Same implementation, b
|
| + SECItem empty_parameters; |
| + empty_parameters.type = siBuffer; |
| + empty_parameters.data = 0; |
| + empty_parameters.len = 0; |
| + |
| + SECStatus status = SECSuccess; |
| + crypto::ScopedPK11Context context(PK11_CreateContextBySymKey( |
| + CKM_SHA_1_HMAC, |
| + CKA_SIGN, |
| + key->key(), |
| + &empty_parameters)); |
| + if (!context.get()) { |
| + LOG(WARNING) << "PK11_CreateContextBySymKey failed"; |
| + return false; |
| + } |
| + |
| + status = PK11_DigestBegin(context.get()); |
| + if (status != SECSuccess) { |
| + LOG(WARNING) << "PK11_DigestBegin failed"; |
| + return false; |
| + } |
| + |
| + status = PK11_DigestOp( |
| + context.get(), |
| + reinterpret_cast<const unsigned char*>(plaintext.c_str()), |
| + plaintext.size()); |
| + |
| + if (status != SECSuccess) { |
| + LOG(WARNING) << "PK11_DigestOp failed"; |
| + return false; |
| + } |
| + |
| + // SHA1 HMACs are 160 bits. |
| + char digest[20]; |
| + unsigned int length = 0; |
| + status = PK11_DigestFinal(context.get(), |
| + reinterpret_cast<unsigned char*>(&digest[0]), |
| + &length, |
| + 20); |
| + if (status != SECSuccess) { |
| + LOG(WARNING) << "PK11_DigestFinal failed"; |
| + return false; |
| + } |
| + |
| + if (std::string(digest, length) == hmac) |
|
Ryan Sleevi
2011/12/24 07:34:08
See above comment. This comparison is not a consta
|
| + return true; |
| + |
| + return false; |
|
Charlie Lee
2011/12/22 20:55:17
should add a log warning here for why this would f
|
| +} |
| + |
| } // namespace |
| // -------------------- OncNetworkParser -------------------- |
| OncNetworkParser::OncNetworkParser(const std::string& onc_blob, |
| + const std::string& passphrase, |
| NetworkUIData::ONCSource onc_source) |
| : NetworkParser(get_onc_mapper()), |
| onc_source_(onc_source), |
| @@ -213,6 +270,12 @@ OncNetworkParser::OncNetworkParser(const std::string& onc_blob, |
| LOG(WARNING) << "OncNetworkParser received bad ONC file: " << parse_error_; |
| } else { |
| root_dict_.reset(static_cast<DictionaryValue*>(root.release())); |
| + |
| + // Check and see if this is an encrypted ONC file. If so, decrypt it. |
| + std::string ciphertext_test; |
| + if (root_dict_->GetString("Ciphertext", &ciphertext_test)) |
| + root_dict_.reset(Decrypt(passphrase, root_dict_.get())); |
| + |
| // At least one of NetworkConfigurations or Certificates is required. |
| bool has_network_configurations = |
| root_dict_->GetList("NetworkConfigurations", &network_configs_); |
| @@ -239,6 +302,103 @@ const EnumMapper<PropertyIndex>* OncNetworkParser::property_mapper() { |
| return get_onc_mapper(); |
| } |
| +base::DictionaryValue* OncNetworkParser::Decrypt( |
| + const std::string& passphrase, |
| + base::DictionaryValue* root) { |
| + std::string salt; |
| + int iterations; |
| + const int key_size_in_bits = 256; |
| + std::string initial_vector_str; |
| + std::string ciphertext; |
| + std::string cipher; |
| + std::string hmac_method; |
| + |
| + if (!root->GetString("Salt", &salt) || |
| + !root->GetInteger("Iterations", &iterations) || |
| + !root->GetString("Cipher", &cipher) || |
| + !root->GetString("HMAC", &hmac_method) || |
| + !root->GetString("IV", &initial_vector_str) || |
| + !root->GetString("Ciphertext", &ciphertext)) { |
| + parse_error_ = l10n_util::GetStringUTF8( |
| + IDS_NETWORK_CONFIG_ERROR_ENCRYPTED_ONC_MALFORMED); |
| + return NULL; |
| + } |
| + |
| + if (hmac_method != "SHA1" || cipher != "AES256") { |
| + parse_error_ = l10n_util::GetStringUTF8( |
| + IDS_NETWORK_CONFIG_ERROR_ENCRYPTED_ONC_UNSUPPORTED_ENCRYPTION); |
| + return NULL; |
| + } |
| + |
| + if (!base::Base64Decode(salt, &salt)) { |
| + parse_error_ = l10n_util::GetStringUTF8( |
| + IDS_NETWORK_CONFIG_ERROR_ENCRYPTED_ONC_UNABLE_TO_DECODE); |
| + return NULL; |
| + } |
| + |
| + scoped_ptr<crypto::SymmetricKey> key( |
| + crypto::SymmetricKey::DeriveKeyFromPassword(crypto::SymmetricKey::AES, |
| + passphrase, |
| + salt, |
| + iterations, |
| + key_size_in_bits)); |
| + |
| + crypto::Encryptor decryptor; |
| + std::string initial_vector; |
| + std::string plaintext; |
| + if (!base::Base64Decode(initial_vector_str, &initial_vector)) { |
| + parse_error_ = l10n_util::GetStringUTF8( |
| + IDS_NETWORK_CONFIG_ERROR_ENCRYPTED_ONC_UNABLE_TO_DECODE); |
| + return NULL; |
| + } |
| + if (!decryptor.Init(key.get(), crypto::Encryptor::CBC, initial_vector)) { |
| + parse_error_ = l10n_util::GetStringUTF8( |
| + IDS_NETWORK_CONFIG_ERROR_ENCRYPTED_ONC_UNABLE_TO_DECRYPT); |
| + return NULL; |
| + } |
| + if (!base::Base64Decode(ciphertext, &ciphertext)) { |
| + parse_error_ = l10n_util::GetStringUTF8( |
| + IDS_NETWORK_CONFIG_ERROR_ENCRYPTED_ONC_UNABLE_TO_DECODE); |
| + return NULL; |
| + } |
| + if (!decryptor.Decrypt(ciphertext, &plaintext)) { |
| + parse_error_ = l10n_util::GetStringUTF8( |
| + IDS_NETWORK_CONFIG_ERROR_ENCRYPTED_ONC_UNABLE_TO_DECRYPT); |
| + return NULL; |
| + } |
| + |
| + // Split out the HMAC. |
|
Ryan Sleevi
2011/12/24 07:34:08
As a drive-by, if it's not too late, wouldn't this
|
| + std::string::size_type split = plaintext.find_last_of('|'); |
| + std::string hmac = plaintext.substr(split + 1, plaintext.size() - split - 1); |
| + plaintext = plaintext.substr(0, split); |
| + if (!base::Base64Decode(hmac, &hmac)) { |
| + parse_error_ = l10n_util::GetStringUTF8( |
| + IDS_NETWORK_CONFIG_ERROR_ENCRYPTED_ONC_UNABLE_TO_DECODE); |
| + return NULL; |
| + } |
| + |
| + // Verify the HMAC. If it fails, then we have failed to decrypt (even though |
| + // we decrypted *something*, it's been tampered with). |
| + if (!VerifyHMAC(hmac, plaintext, key.get())) { |
| + parse_error_ = l10n_util::GetStringUTF8( |
| + IDS_NETWORK_CONFIG_ERROR_ENCRYPTED_ONC_HMAC_CHECK_FAILED); |
|
Ryan Sleevi
2011/12/24 07:34:08
The fact that HMAC errors are distinguished from e
|
| + return NULL; |
| + } |
| + |
| + // Now we've decrypted it, let's deserialize the decrypted data. |
| + JSONStringValueSerializer deserializer(plaintext); |
| + deserializer.set_allow_trailing_comma(true); |
| + scoped_ptr<base::Value> new_root(deserializer.Deserialize(NULL, |
| + &parse_error_)); |
| + if (!new_root.get() || new_root->GetType() != base::Value::TYPE_DICTIONARY) { |
| + if (parse_error_.empty()) |
|
Charlie Lee
2011/12/22 20:55:17
when would this not be empty?
Greg Spencer (Chromium)
2012/01/03 21:54:22
When the Deserialize call above places an error in
|
| + parse_error_ = l10n_util::GetStringUTF8( |
| + IDS_NETWORK_CONFIG_ERROR_NETWORK_PROP_DICT_MALFORMED); |
| + return NULL; |
| + } |
| + return static_cast<base::DictionaryValue*>(new_root.release()); |
| +} |
| + |
| int OncNetworkParser::GetNetworkConfigsSize() const { |
| return network_configs_ ? network_configs_->GetSize() : 0; |
| } |