shill: EapCredentials: Promote to a class

Promote the EapCredentials structure to a sovereign class with
a small set of getters and setters where necessary.  Otherwise,
move most of the member manipulation (creating arguments for
wpa_supplicant and wimax_manager, Load() and Save()) interior
to the class.  This involves a straightforwarnd but lengthy
refactor of just about everything that uses EapCredentials.
The EapCredentials object is ref-counted to allow it to be
shared between some services.  For this reason, move the
"remote_certification" property out of EapCredentials into
Service since this property is strictly per-service.

BUG=chromium:227434
TEST=Unit tests, wired 802.1x and wifi EAP-TLS autotests

Change-Id: I167e71e1d9c934bc328a0e1d73f638e1ff50d238
Reviewed-on: https://gerrit.chromium.org/gerrit/47973
Reviewed-by: Ben Chan <benchan@chromium.org>
Commit-Queue: Paul Stewart <pstew@chromium.org>
Tested-by: Paul Stewart <pstew@chromium.org>
diff --git a/Makefile b/Makefile
index 6a0d566..d79442f 100644
--- a/Makefile
+++ b/Makefile
@@ -223,6 +223,7 @@
 	dhcpcd_proxy.o \
 	diagnostics_reporter.o \
 	dns_client.o \
+	eap_credentials.o \
 	eap_listener.o \
 	endpoint.o \
 	ephemeral_profile.o \
@@ -343,6 +344,7 @@
 	dhcp_provider_unittest.o \
 	diagnostics_reporter_unittest.o \
 	dns_client_unittest.o \
+	eap_credentials_unittest.o \
 	eap_listener_unittest.o \
 	error_unittest.o \
 	ethernet_unittest.o \
@@ -380,6 +382,7 @@
 	mock_dhcp_proxy.o \
 	mock_diagnostics_reporter.o \
 	mock_dns_client.o \
+	mock_eap_credentials.o \
 	mock_eap_listener.o \
 	mock_ethernet.o \
 	mock_ethernet_service.o \
diff --git a/eap_credentials.cc b/eap_credentials.cc
new file mode 100644
index 0000000..11f3fad
--- /dev/null
+++ b/eap_credentials.cc
@@ -0,0 +1,479 @@
+// Copyright (c) 2013 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/eap_credentials.h"
+
+#include <map>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include <chromeos/dbus/service_constants.h>
+
+#include "shill/certificate_file.h"
+#include "shill/logging.h"
+#include "shill/key_value_store.h"
+#include "shill/metrics.h"
+#include "shill/nss.h"
+#include "shill/property_accessor.h"
+#include "shill/property_store.h"
+#include "shill/service.h"
+#include "shill/store_interface.h"
+#include "shill/wpa_supplicant.h"
+
+using base::FilePath;
+using std::map;
+using std::string;
+using std::vector;
+
+using std::string;
+
+namespace shill {
+
+const char EapCredentials::kStorageEapAnonymousIdentity[] =
+    "EAP.AnonymousIdentity";
+const char EapCredentials::kStorageEapCACert[] = "EAP.CACert";
+const char EapCredentials::kStorageEapCACertID[] = "EAP.CACertID";
+const char EapCredentials::kStorageEapCACertNSS[] = "EAP.CACertNSS";
+const char EapCredentials::kStorageEapCACertPEM[] = "EAP.CACertPEM";
+const char EapCredentials::kStorageEapCertID[] = "EAP.CertID";
+const char EapCredentials::kStorageEapClientCert[] = "EAP.ClientCert";
+const char EapCredentials::kStorageEapEap[] = "EAP.EAP";
+const char EapCredentials::kStorageEapIdentity[] = "EAP.Identity";
+const char EapCredentials::kStorageEapInnerEap[] = "EAP.InnerEAP";
+const char EapCredentials::kStorageEapKeyID[] = "EAP.KeyID";
+const char EapCredentials::kStorageEapKeyManagement[] = "EAP.KeyMgmt";
+const char EapCredentials::kStorageEapPIN[] = "EAP.PIN";
+const char EapCredentials::kStorageEapPassword[] = "EAP.Password";
+const char EapCredentials::kStorageEapPrivateKey[] = "EAP.PrivateKey";
+const char EapCredentials::kStorageEapPrivateKeyPassword[] =
+    "EAP.PrivateKeyPassword";
+const char EapCredentials::kStorageEapSubjectMatch[] =
+    "EAP.SubjectMatch";
+const char EapCredentials::kStorageEapUseSystemCAs[] = "EAP.UseSystemCAs";
+
+EapCredentials::EapCredentials() : use_system_cas_(true) {}
+
+EapCredentials::~EapCredentials() {}
+
+// static
+void EapCredentials::PopulateSupplicantProperties(
+    CertificateFile *certificate_file,
+    NSS *nss,
+    const vector<char> nss_identifier,
+    map<string, DBus::Variant> *params) const {
+  string ca_cert = ca_cert_;
+  if (!ca_cert_pem_.empty()) {
+    FilePath certfile =
+        certificate_file->CreateDERFromString(ca_cert_pem_);
+    if (certfile.empty()) {
+      LOG(ERROR) << "Unable to extract PEM certificate.";
+    } else {
+      ca_cert = certfile.value();
+    }
+  } else if (!ca_cert_nss_.empty()) {
+    FilePath certfile = nss->GetDERCertfile(ca_cert_nss_, nss_identifier);
+    if (certfile.empty()) {
+      LOG(ERROR) << "Unable to extract DER certificate: " << ca_cert_nss_;
+    } else {
+      ca_cert = certfile.value();
+    }
+  }
+
+
+  typedef std::pair<const char *, const char *> KeyVal;
+  KeyVal init_propertyvals[] = {
+    // Authentication properties.
+    KeyVal(WPASupplicant::kNetworkPropertyEapAnonymousIdentity,
+           anonymous_identity_.c_str()),
+    KeyVal(WPASupplicant::kNetworkPropertyEapCertId, cert_id_.c_str()),
+    KeyVal(WPASupplicant::kNetworkPropertyEapClientCert,
+           client_cert_.c_str()),
+    KeyVal(WPASupplicant::kNetworkPropertyEapIdentity, identity_.c_str()),
+    KeyVal(WPASupplicant::kNetworkPropertyEapKeyId, key_id_.c_str()),
+    KeyVal(WPASupplicant::kNetworkPropertyEapCaPassword,
+           password_.c_str()),
+    KeyVal(WPASupplicant::kNetworkPropertyEapPrivateKey,
+           private_key_.c_str()),
+    KeyVal(WPASupplicant::kNetworkPropertyEapPrivateKeyPassword,
+           private_key_password_.c_str()),
+
+    // Non-authentication properties.
+    KeyVal(WPASupplicant::kNetworkPropertyEapCaCert, ca_cert.c_str()),
+    KeyVal(WPASupplicant::kNetworkPropertyEapCaCertId,
+           ca_cert_id_.c_str()),
+    KeyVal(WPASupplicant::kNetworkPropertyEapEap, eap_.c_str()),
+    KeyVal(WPASupplicant::kNetworkPropertyEapInnerEap,
+           inner_eap_.c_str()),
+    KeyVal(WPASupplicant::kNetworkPropertyEapSubjectMatch,
+           subject_match_.c_str())
+  };
+
+  vector<KeyVal> propertyvals(init_propertyvals,
+                              init_propertyvals + arraysize(init_propertyvals));
+  if (use_system_cas_) {
+    propertyvals.push_back(KeyVal(
+        WPASupplicant::kNetworkPropertyCaPath, WPASupplicant::kCaPath));
+  } else if (ca_cert.empty()) {
+    LOG(WARNING) << __func__
+                 << ": No certificate authorities are configured."
+                 << " Server certificates will be accepted"
+                 << " unconditionally.";
+  }
+
+  if (!cert_id_.empty() || !key_id_.empty() ||
+      !ca_cert_id_.empty()) {
+    propertyvals.push_back(KeyVal(
+        WPASupplicant::kNetworkPropertyEapPin, pin_.c_str()));
+    propertyvals.push_back(KeyVal(
+        WPASupplicant::kNetworkPropertyEngineId,
+        WPASupplicant::kEnginePKCS11));
+    // We can't use the propertyvals vector for this since this argument
+    // is a uint32, not a string.
+    (*params)[WPASupplicant::kNetworkPropertyEngine].writer().
+        append_uint32(WPASupplicant::kDefaultEngine);
+  }
+
+  vector<KeyVal>::iterator it;
+  for (it = propertyvals.begin(); it != propertyvals.end(); ++it) {
+    if (strlen((*it).second) > 0) {
+      (*params)[(*it).first].writer().append_string((*it).second);
+    }
+  }
+}
+
+// static
+void EapCredentials::PopulateWiMaxProperties(KeyValueStore *params) const {
+  if (!anonymous_identity_.empty()) {
+    params->SetString(wimax_manager::kEAPAnonymousIdentity,
+                      anonymous_identity_);
+  }
+  if (!identity_.empty()) {
+    params->SetString(wimax_manager::kEAPUserIdentity, identity_);
+  }
+  if (!password_.empty()) {
+    params->SetString(wimax_manager::kEAPUserPassword, password_);
+  }
+}
+
+void EapCredentials::InitPropertyStore(PropertyStore *store) {
+  // Authentication properties.
+  store->RegisterString(flimflam::kEapAnonymousIdentityProperty,
+                        &anonymous_identity_);
+  store->RegisterString(flimflam::kEAPCertIDProperty, &cert_id_);
+  store->RegisterString(flimflam::kEAPClientCertProperty, &client_cert_);
+  store->RegisterString(flimflam::kEapIdentityProperty, &identity_);
+  store->RegisterString(flimflam::kEAPKeyIDProperty, &key_id_);
+  HelpRegisterDerivedString(store,
+                            flimflam::kEapKeyMgmtProperty,
+                            &EapCredentials::GetKeyManagement,
+                            &EapCredentials::SetKeyManagement);
+  HelpRegisterWriteOnlyDerivedString(store,
+                                     flimflam::kEapPasswordProperty,
+                                     &EapCredentials::SetEapPassword,
+                                     NULL,
+                                     &password_);
+  store->RegisterString(flimflam::kEAPPINProperty, &pin_);
+  store->RegisterString(flimflam::kEapPrivateKeyProperty, &private_key_);
+  HelpRegisterWriteOnlyDerivedString(store,
+                                     flimflam::kEapPrivateKeyPasswordProperty,
+                                     &EapCredentials::SetEapPrivateKeyPassword,
+                                     NULL,
+                                     &private_key_password_);
+
+  // Non-authentication properties.
+  store->RegisterString(kEapCaCertPemProperty, &ca_cert_pem_);
+  store->RegisterString(flimflam::kEapCaCertIDProperty, &ca_cert_id_);
+  store->RegisterString(flimflam::kEapCaCertNssProperty, &ca_cert_nss_);
+  store->RegisterString(flimflam::kEapCaCertProperty, &ca_cert_);
+  store->RegisterString(flimflam::kEAPEAPProperty, &eap_);
+  store->RegisterString(flimflam::kEapPhase2AuthProperty, &inner_eap_);
+  store->RegisterString(kEapSubjectMatchProperty, &subject_match_);
+  store->RegisterBool(flimflam::kEapUseSystemCAsProperty, &use_system_cas_);
+}
+
+// static
+bool EapCredentials::IsEapAuthenticationProperty(const string property) {
+  return
+      property == flimflam::kEapAnonymousIdentityProperty ||
+      property == flimflam::kEAPCertIDProperty ||
+      property == flimflam::kEAPClientCertProperty ||
+      property == flimflam::kEapIdentityProperty ||
+      property == flimflam::kEAPKeyIDProperty ||
+      property == flimflam::kEapKeyMgmtProperty ||
+      property == flimflam::kEapPasswordProperty ||
+      property == flimflam::kEAPPINProperty ||
+      property == flimflam::kEapPrivateKeyProperty ||
+      property == flimflam::kEapPrivateKeyPasswordProperty;
+}
+
+bool EapCredentials::IsConnectable() const {
+  // Identity is required.
+  if (identity_.empty()) {
+    SLOG(Service, 2) << "Not connectable: Identity is empty.";
+    return false;
+  }
+
+  if (!client_cert_.empty() || !cert_id_.empty()) {
+    // If a client certificate is being used, we must have a private key.
+    if (private_key_.empty() && key_id_.empty()) {
+      SLOG(Service, 2)
+          << "Not connectable: Client certificate but no private key.";
+      return false;
+    }
+  }
+  if (!cert_id_.empty() || !key_id_.empty() ||
+      !ca_cert_id_.empty()) {
+    // If PKCS#11 data is needed, a PIN is required.
+    if (pin_.empty()) {
+      SLOG(Service, 2) << "Not connectable: PKCS#11 data but no PIN.";
+      return false;
+    }
+  }
+
+  // For EAP-TLS, a client certificate is required.
+  if (eap_.empty() || eap_ == "TLS") {
+    if ((!client_cert_.empty() || !cert_id_.empty()) &&
+        (!private_key_.empty() || !key_id_.empty())) {
+      SLOG(Service, 2) << "Connectable: EAP-TLS with a client cert and key.";
+      return true;
+    }
+  }
+
+  // For EAP types other than TLS (e.g. EAP-TTLS or EAP-PEAP, password is the
+  // minimum requirement), at least an identity + password is required.
+  if (eap_.empty() || eap_ != "TLS") {
+    if (!password_.empty()) {
+      SLOG(Service, 2) << "Connectable. !EAP-TLS and has a password.";
+      return true;
+    }
+  }
+
+  SLOG(Service, 2)
+      << "Not connectable: No suitable EAP configuration was found.";
+  return false;
+}
+
+bool EapCredentials::IsConnectableUsingPassphrase() const {
+  return !identity_.empty() && !password_.empty();
+}
+
+void EapCredentials::Load(StoreInterface *storage, const string &id) {
+  // Authentication properties.
+  storage->GetCryptedString(id,
+                            kStorageEapAnonymousIdentity,
+                            &anonymous_identity_);
+  storage->GetString(id, kStorageEapCertID, &cert_id_);
+  storage->GetString(id, kStorageEapClientCert, &client_cert_);
+  storage->GetCryptedString(id, kStorageEapIdentity, &identity_);
+  storage->GetString(id, kStorageEapKeyID, &key_id_);
+  string key_management;
+  storage->GetString(id, kStorageEapKeyManagement, &key_management);
+  SetKeyManagement(key_management, NULL);
+  storage->GetCryptedString(id, kStorageEapPassword, &password_);
+  storage->GetString(id, kStorageEapPIN, &pin_);
+  storage->GetString(id, kStorageEapPrivateKey, &private_key_);
+  storage->GetCryptedString(id,
+                            kStorageEapPrivateKeyPassword,
+                            &private_key_password_);
+
+  // Non-authentication properties.
+  storage->GetString(id, kStorageEapCACert, &ca_cert_);
+  storage->GetString(id, kStorageEapCACertID, &ca_cert_id_);
+  storage->GetString(id, kStorageEapCACertNSS, &ca_cert_nss_);
+  storage->GetString(id, kStorageEapCACertPEM, &ca_cert_pem_);
+  storage->GetString(id, kStorageEapEap, &eap_);
+  storage->GetString(id, kStorageEapInnerEap, &inner_eap_);
+  storage->GetString(id, kStorageEapSubjectMatch, &subject_match_);
+  storage->GetBool(id, kStorageEapUseSystemCAs, &use_system_cas_);
+}
+
+void EapCredentials::OutputConnectionMetrics(
+    Metrics *metrics, Technology::Identifier technology) const {
+  Metrics::EapOuterProtocol outer_protocol =
+      Metrics::EapOuterProtocolStringToEnum(eap_);
+  metrics->SendEnumToUMA(
+      metrics->GetFullMetricName(Metrics::kMetricNetworkEapOuterProtocol,
+                                 technology),
+      outer_protocol,
+      Metrics::kMetricNetworkEapOuterProtocolMax);
+
+  Metrics::EapInnerProtocol inner_protocol =
+      Metrics::EapInnerProtocolStringToEnum(inner_eap_);
+  metrics->SendEnumToUMA(
+      metrics->GetFullMetricName(Metrics::kMetricNetworkEapInnerProtocol,
+                                 technology),
+      inner_protocol,
+      Metrics::kMetricNetworkEapInnerProtocolMax);
+}
+
+void EapCredentials::Save(StoreInterface *storage, const string &id,
+                          bool save_credentials) const {
+  // Authentication properties.
+  Service::SaveString(storage,
+                      id,
+                      kStorageEapAnonymousIdentity,
+                      anonymous_identity_,
+                      true,
+                      save_credentials);
+  Service::SaveString(storage,
+                      id,
+                      kStorageEapCertID,
+                      cert_id_,
+                      false,
+                      save_credentials);
+  Service::SaveString(storage,
+                      id,
+                      kStorageEapClientCert,
+                      client_cert_,
+                      false,
+                      save_credentials);
+  Service::SaveString(storage,
+                      id,
+                      kStorageEapIdentity,
+                      identity_,
+                      true,
+                      save_credentials);
+  Service::SaveString(storage,
+                      id,
+                      kStorageEapKeyID,
+                      key_id_,
+                      false,
+                      save_credentials);
+  Service::SaveString(storage,
+                      id,
+                      kStorageEapKeyManagement,
+                      key_management_,
+                      false,
+                      true);
+  Service::SaveString(storage,
+                      id,
+                      kStorageEapPassword,
+                      password_,
+                      true,
+                      save_credentials);
+  Service::SaveString(storage,
+                      id,
+                      kStorageEapPIN,
+                      pin_,
+                      false,
+                      save_credentials);
+  Service::SaveString(storage,
+                      id,
+                      kStorageEapPrivateKey,
+                      private_key_,
+                      false,
+                      save_credentials);
+  Service::SaveString(storage,
+                      id,
+                      kStorageEapPrivateKeyPassword,
+                      private_key_password_,
+                      true,
+                      save_credentials);
+
+  // Non-authentication properties.
+  Service::SaveString(storage, id, kStorageEapCACert, ca_cert_, false, true);
+  Service::SaveString(storage,
+                      id,
+                      kStorageEapCACertID,
+                      ca_cert_id_,
+                      false,
+                      true);
+  Service::SaveString(storage,
+                      id,
+                      kStorageEapCACertNSS,
+                      ca_cert_nss_,
+                      false,
+                      true);
+  Service::SaveString(storage,
+                      id,
+                      kStorageEapCACertPEM,
+                      ca_cert_pem_,
+                      false,
+                      true);
+  Service::SaveString(storage, id, kStorageEapEap, eap_, false, true);
+  Service::SaveString(storage,
+                      id,
+                      kStorageEapInnerEap,
+                      inner_eap_,
+                      false,
+                      true);
+  Service::SaveString(storage,
+                      id,
+                      kStorageEapSubjectMatch,
+                      subject_match_,
+                      false,
+                      true);
+  storage->SetBool(id, kStorageEapUseSystemCAs, use_system_cas_);
+}
+
+void EapCredentials::Reset() {
+  // Authentication properties.
+  anonymous_identity_ = "";
+  cert_id_ = "";
+  client_cert_ = "";
+  identity_ = "";
+  key_id_ = "";
+  key_management_ = "";
+  password_ = "";
+  pin_ = "";
+  private_key_ = "";
+  private_key_password_ = "";
+
+  // Non-authentication properties.
+  ca_cert_ = "";
+  ca_cert_id_ = "";
+  ca_cert_nss_ = "";
+  ca_cert_pem_ = "";
+  eap_ = "";
+  inner_eap_ = "";
+  subject_match_ = "";
+  use_system_cas_ = true;
+}
+
+void EapCredentials::SetEapPassword(const string &password, Error */*error*/) {
+  password_ = password;
+}
+
+void EapCredentials::SetEapPrivateKeyPassword(const string &password,
+                                              Error */*error*/) {
+  private_key_password_ = password;
+}
+
+string EapCredentials::GetKeyManagement(Error */*error*/) {
+  return key_management_;
+}
+
+void EapCredentials::SetKeyManagement(const std::string &key_management,
+                                      Error */*error*/) {
+  if (!key_management.empty()) {
+    key_management_ = key_management;
+  }
+}
+
+void EapCredentials::HelpRegisterDerivedString(
+    PropertyStore *store,
+    const string &name,
+    string(EapCredentials::*get)(Error *),
+    void(EapCredentials::*set)(const string&, Error *)) {
+  store->RegisterDerivedString(
+      name,
+      StringAccessor(new CustomAccessor<EapCredentials, string>(
+          this, get, set)));
+}
+
+void EapCredentials::HelpRegisterWriteOnlyDerivedString(
+    PropertyStore *store,
+    const string &name,
+    void(EapCredentials::*set)(const string &, Error *),
+    void(EapCredentials::*clear)(Error *),
+    const string *default_value) {
+  store->RegisterDerivedString(
+      name,
+      StringAccessor(
+          new CustomWriteOnlyAccessor<EapCredentials, string>(
+              this, set, clear, default_value)));
+}
+
+}
diff --git a/eap_credentials.h b/eap_credentials.h
index 63190b0..ef4dcc7 100644
--- a/eap_credentials.h
+++ b/eap_credentials.h
@@ -5,54 +5,197 @@
 #ifndef SHILL_EAP_CREDENTIALS_H_
 #define SHILL_EAP_CREDENTIALS_H_
 
+#include <map>
 #include <string>
 #include <vector>
 
+#include <base/file_path.h>
+#include <dbus-c++/dbus.h>
+
+#include "shill/technology.h"
+
 namespace shill {
 
-struct EapCredentials {
-  EapCredentials() : use_system_cas(true) {}
-  // Who we identify ourselves as to the EAP authenticator.
-  std::string identity;
-  // The outer or only EAP authetnication type.
-  std::string eap;
-  // The inner EAP authentication type.
-  std::string inner_eap;
+class CertificateFile;
+class Error;
+class KeyValueStore;
+class Metrics;
+class NSS;
+class PropertyStore;
+class StoreInterface;
+
+class EapCredentials {
+ public:
+  // TODO(pstew): Storage constants shouldn't need to be public
+  // crosbug.com/25813
+  static const char kStorageEapAnonymousIdentity[];
+  static const char kStorageEapCACert[];
+  static const char kStorageEapCACertID[];
+  static const char kStorageEapCACertNSS[];
+  static const char kStorageEapCACertPEM[];
+  static const char kStorageEapCertID[];
+  static const char kStorageEapClientCert[];
+  static const char kStorageEapEap[];
+  static const char kStorageEapIdentity[];
+  static const char kStorageEapInnerEap[];
+  static const char kStorageEapKeyID[];
+  static const char kStorageEapKeyManagement[];
+  static const char kStorageEapPIN[];
+  static const char kStorageEapPassword[];
+  static const char kStorageEapPrivateKey[];
+  static const char kStorageEapPrivateKeyPassword[];
+  static const char kStorageEapSubjectMatch[];
+  static const char kStorageEapUseSystemCAs[];
+
+  EapCredentials();
+  virtual ~EapCredentials();
+
+  // Add property accessors to the EAP credential parameters in |this| to
+  // |store|.
+  void InitPropertyStore(PropertyStore *store);
+
+  // Returns true if |property| is used for authentication in EapCredentials.
+  static bool IsEapAuthenticationProperty(const std::string property);
+
+  // Returns true if a connection can be made with |this| credentials using
+  // either passphrase or certificates.
+  virtual bool IsConnectable() const;
+
+  // Returns true if a connection can be made with |this| credentials using
+  // only passphrase properties.
+  virtual bool IsConnectableUsingPassphrase() const;
+
+  // Loads EAP properties from |storage| in group |id|.
+  virtual void Load(StoreInterface *store, const std::string &id);
+
+  // Output metrics about this EAP connection to |metrics| with technology
+  // |technology|.
+  virtual void OutputConnectionMetrics(Metrics *metrics,
+                                       Technology::Identifier technology) const;
+
+  // Populate the wpa_supplicant DBus parameter map |params| with the
+  // credentials in |this|.  To do so, this function may use |certificate_file|
+  // or |nss| to export CA certificates to be passed to wpa_supplicant.
+  virtual void PopulateSupplicantProperties(
+      CertificateFile *certificate_file,
+      NSS *nss,
+      const std::vector<char> nss_identifier,
+      std::map<std::string, DBus::Variant> *params) const;
+
+  // Populate the WiMax connection parameters |params| with the
+  // credentials in |this|.
+  virtual void PopulateWiMaxProperties(
+      KeyValueStore *params) const;
+
+  // Save EAP properties to |storage| in group |id|.  If |save_credentials|
+  // is true, passwords and identities that are a part of the credentials are
+  // also saved.
+  virtual void Save(StoreInterface *store, const std::string &id,
+                    bool save_credentials) const;
+
+  // Restore EAP properties to their initial state.
+  virtual void Reset();
+
+  // Setter that guards against emptying the "Key Management" value.
+  virtual void SetKeyManagement(const std::string &key_management,
+                                Error *error);
+
+  // Getters and setters.
+  virtual const std::string &identity() const { return identity_; }
+  void set_identity(const std::string &identity) {
+    identity_ = identity;
+  }
+  virtual const std::string &key_management() const { return key_management_; }
+  virtual void set_password(const std::string &password) {
+    password_ = password;
+  }
+
+ private:
+  friend class EapCredentialsTest;
+
+  // Expose a property in |store|, with the name |name|.
+  //
+  // Reads of the property will be handled by invoking |get|.
+  // Writes to the property will be handled by invoking |set|.
+  void HelpRegisterDerivedString(
+      PropertyStore *store,
+      const std::string &name,
+      std::string(EapCredentials::*get)(Error *error),
+      void(EapCredentials::*set)(const std::string &value, Error *error));
+
+  // Expose a property in |store|, with the name |name|.
+  //
+  // Reads of the property will be handled by invoking |get|.
+  //
+  // Clearing the property will be handled by invoking |clear|, or
+  // calling |set| with |default_value| (whichever is non-NULL).  It
+  // is an error to call this method with both |clear| and
+  // |default_value| non-NULL.
+  void HelpRegisterWriteOnlyDerivedString(
+      PropertyStore *store,
+      const std::string &name,
+      void(EapCredentials::*set)(const std::string &value, Error *error),
+      void(EapCredentials::*clear)(Error *error),
+      const std::string *default_value);
+
+  // Assigns |value| to |key| in |storage| if |value| is non-empty and |save| is
+  // true. Otherwise, removes |key| from |storage|. If |crypted| is true, the
+  // value is encrypted.
+  static void SaveString(StoreInterface *storage,
+                         const std::string &id,
+                         const std::string &key,
+                         const std::string &value,
+                         bool crypted,
+                         bool save);
+
+  // Setters for write-only RPC properties.
+  void SetEapPassword(const std::string &password, Error *error);
+  void SetEapPrivateKeyPassword(const std::string &password, Error *error);
+
+  // RPC getter for key_management_.
+  std::string GetKeyManagement(Error *error);
+
   // When there is an inner EAP type, use this identity for the outer.
-  std::string anonymous_identity;
-  // Filename of the client certificate.
-  std::string client_cert;
+  std::string anonymous_identity_;
   // Locator for the client certificate within the security token.
-  std::string cert_id;
-  // Filename of the client private key.
-  std::string private_key;
-  // Password for decrypting the client private key file.
-  std::string private_key_password;
+  std::string cert_id_;
+  // Filename of the client certificate.
+  std::string client_cert_;
+  // Who we identify ourselves as to the EAP authenticator.
+  std::string identity_;
   // Locator for the client private key within the security token.
-  std::string key_id;
-  // Filename of the certificate authority (CA) certificate.
-  std::string ca_cert;
-  // Locator for the CA certificate within the security token.
-  std::string ca_cert_id;
-  // Locator for the CA certificate within the user NSS database.
-  std::string ca_cert_nss;
-  // Raw PEM contents of the CA certificate.
-  std::string ca_cert_pem;
-  // If true, use the system-wide CA database to authenticate the remote.
-  bool use_system_cas;
-  // PIN code for accessing the security token.
-  std::string pin;
-  // Password to use for EAP methods which require one.
-  std::string password;
+  std::string key_id_;
   // Key management algorithm to use after EAP succeeds.
-  std::string key_management;
+  std::string key_management_;
+  // Password to use for EAP methods which require one.
+  std::string password_;
+  // PIN code for accessing the security token.
+  std::string pin_;
+  // Filename of the client private key.
+  std::string private_key_;
+  // Password for decrypting the client private key file.
+  std::string private_key_password_;
+
+  // Filename of the certificate authority (CA) certificate.
+  std::string ca_cert_;
+  // Locator for the CA certificate within the security token.
+  std::string ca_cert_id_;
+  // Locator for the CA certificate within the user NSS database.
+  std::string ca_cert_nss_;
+  // Raw PEM contents of the CA certificate.
+  std::string ca_cert_pem_;
+  // The outer or only EAP authetnication type.
+  std::string eap_;
+  // The inner EAP authentication type.
+  std::string inner_eap_;
   // If non-empty, string to match remote subject against before connecting.
-  std::string subject_match;
-  // List of subject names reported by remote entity during TLS setup.
-  std::vector<std::string> remote_certification;
+  std::string subject_match_;
+  // If true, use the system-wide CA database to authenticate the remote.
+  bool use_system_cas_;
+
+  DISALLOW_COPY_AND_ASSIGN(EapCredentials);
 };
 
 }  // namespace shill
 
 #endif  // SHILL_EAP_CREDENTIALS_H_
-
diff --git a/eap_credentials_unittest.cc b/eap_credentials_unittest.cc
new file mode 100644
index 0000000..ed05e00
--- /dev/null
+++ b/eap_credentials_unittest.cc
@@ -0,0 +1,433 @@
+// Copyright (c) 2013 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/eap_credentials.h"
+
+#include <base/stl_util.h>
+#include <chromeos/dbus/service_constants.h>
+#include <gtest/gtest.h>
+
+#include "shill/key_value_store.h"
+#include "shill/mock_certificate_file.h"
+#include "shill/mock_event_dispatcher.h"
+#include "shill/mock_log.h"
+#include "shill/mock_metrics.h"
+#include "shill/mock_nss.h"
+#include "shill/mock_property_store.h"
+#include "shill/mock_store.h"
+#include "shill/technology.h"
+#include "shill/wpa_supplicant.h"
+
+using base::FilePath;
+using std::map;
+using std::string;
+using std::vector;
+using testing::_;
+using testing::AnyNumber;
+using testing::DoAll;
+using testing::HasSubstr;
+using testing::Mock;
+using testing::Return;
+using testing::SetArgumentPointee;
+
+namespace shill {
+
+class EapCredentialsTest : public testing::Test {
+ public:
+  EapCredentialsTest() {}
+  virtual ~EapCredentialsTest() {}
+
+ protected:
+  void PopulateSupplicantProperties() {
+    eap_.PopulateSupplicantProperties(&certificate_file_, &nss_,
+                                      nss_identifier_, &params_);
+  }
+
+  void SetAnonymousIdentity(const string &anonymous_identity) {
+    eap_.anonymous_identity_ = anonymous_identity;
+  }
+  void SetCACertNSS(const string &ca_cert_nss) {
+    eap_.ca_cert_nss_ = ca_cert_nss;
+  }
+  void SetCACertPEM(const string &ca_cert_pem) {
+    eap_.ca_cert_pem_ = ca_cert_pem;
+  }
+  void SetClientCert(const string &client_cert) {
+    eap_.client_cert_ = client_cert;
+  }
+  void SetCertId(const string &cert_id) {
+    eap_.cert_id_ = cert_id;
+  }
+  void SetEap(const string &eap) {
+    eap_.eap_ = eap;
+  }
+  void SetIdentity(const string &identity) {
+    eap_.identity_ = identity;
+  }
+  void SetInnerEap(const string &inner_eap) {
+    eap_.inner_eap_ = inner_eap;
+  }
+  void SetKeyId(const string &key_id) {
+    eap_.key_id_ = key_id;
+  }
+  const string &GetPassword() {
+    return eap_.password_;
+  }
+  void SetPassword(const string &password) {
+    eap_.password_ = password;
+  }
+  void SetPrivateKey(const string &private_key) {
+    eap_.private_key_ = private_key;
+  }
+  void SetPin(const string &pin) {
+    eap_.pin_ = pin;
+  }
+  void SetUseSystemCAs(bool use_system_cas) {
+    eap_.use_system_cas_ = use_system_cas;
+  }
+  bool IsReset() {
+    return
+        eap_.anonymous_identity_.empty() &&
+        eap_.cert_id_.empty() &&
+        eap_.client_cert_.empty() &&
+        eap_.identity_.empty() &&
+        eap_.key_id_.empty() &&
+        eap_.key_management_.empty() &&
+        eap_.password_.empty() &&
+        eap_.pin_.empty() &&
+        eap_.private_key_.empty() &&
+        eap_.private_key_password_.empty() &&
+        eap_.ca_cert_.empty() &&
+        eap_.ca_cert_id_.empty() &&
+        eap_.ca_cert_nss_.empty() &&
+        eap_.ca_cert_pem_.empty() &&
+        eap_.eap_.empty() &&
+        eap_.inner_eap_.empty() &&
+        eap_.subject_match_.empty() &&
+        eap_.use_system_cas_;
+  }
+
+  EapCredentials eap_;
+  MockCertificateFile certificate_file_;
+  MockNSS nss_;
+  vector<char> nss_identifier_;
+  map<string, ::DBus::Variant> params_;
+};
+
+TEST_F(EapCredentialsTest, PropertyStore) {
+  PropertyStore store;
+  eap_.InitPropertyStore(&store);
+  const string kIdentity("Cross-Eyed Mary");
+  Error error;
+  EXPECT_TRUE(store.SetStringProperty(flimflam::kEapIdentityProperty,
+                                      kIdentity, &error));
+  EXPECT_EQ(kIdentity, eap_.identity());
+}
+
+TEST_F(EapCredentialsTest, Connectable) {
+  // Empty EAP credentials should not make a 802.1x network connectable.
+  EXPECT_FALSE(eap_.IsConnectable());
+
+  // Identity alone is not enough.
+  SetIdentity("Steel Monkey");
+  EXPECT_FALSE(eap_.IsConnectable());
+
+  // Set a password.
+  SetPassword("Angry Tapir");
+
+  // Empty "EAP" parameter is treated like "not EAP-TLS", and connectable.
+  EXPECT_TRUE(eap_.IsConnectable());
+
+  // Some other non-TLS EAP type.
+  SetEap("DodgeBall");
+  EXPECT_TRUE(eap_.IsConnectable());
+
+  // EAP-TLS requires certificate parameters, and cares not for passwords.
+  SetEap("TLS");
+  EXPECT_FALSE(eap_.IsConnectable());
+
+  // Clearing the password won't help.
+  SetPassword("");
+  EXPECT_FALSE(eap_.IsConnectable());
+
+  // A client cert by itself doesn't help.
+  SetClientCert("client-cert");
+  EXPECT_FALSE(eap_.IsConnectable());
+
+  // A client cert and key will, however.
+  SetPrivateKey("client-cert");
+  EXPECT_TRUE(eap_.IsConnectable());
+
+  // A key-id (and cert) doesn't work.
+  SetKeyId("client-key-id");
+  EXPECT_FALSE(eap_.IsConnectable());
+
+  // We need a PIN for the key id in addition.
+  SetPin("pin");
+  EXPECT_TRUE(eap_.IsConnectable());
+
+  // If we clear the "EAP" property, we just assume these valid certificate
+  // credentials are the ones to be used.
+  SetEap("");
+  EXPECT_TRUE(eap_.IsConnectable());
+
+  // Check that clearing the certificate parameter breaks us again.
+  SetClientCert("");
+  EXPECT_FALSE(eap_.IsConnectable());
+
+  // Setting the cert-id will fix things.
+  SetCertId("client-cert-id");
+  EXPECT_TRUE(eap_.IsConnectable());
+}
+
+TEST_F(EapCredentialsTest, ConnectableUsingPassphrase) {
+  EXPECT_FALSE(eap_.IsConnectableUsingPassphrase());
+
+  // No password.
+  SetIdentity("TestIdentity");
+  EXPECT_FALSE(eap_.IsConnectableUsingPassphrase());
+
+  // Success.
+  SetPassword("TestPassword");
+  EXPECT_TRUE(eap_.IsConnectableUsingPassphrase());
+
+  // Clear identity.
+  SetIdentity("");
+  EXPECT_FALSE(eap_.IsConnectableUsingPassphrase());
+}
+
+TEST_F(EapCredentialsTest, IsEapAuthenticationProperty) {
+  EXPECT_TRUE(EapCredentials::IsEapAuthenticationProperty(
+      flimflam::kEapAnonymousIdentityProperty));
+  EXPECT_TRUE(EapCredentials::IsEapAuthenticationProperty(
+      flimflam::kEAPCertIDProperty));
+  EXPECT_TRUE(EapCredentials::IsEapAuthenticationProperty(
+      flimflam::kEAPClientCertProperty));
+  EXPECT_TRUE(EapCredentials::IsEapAuthenticationProperty(
+      flimflam::kEapIdentityProperty));
+  EXPECT_TRUE(EapCredentials::IsEapAuthenticationProperty(
+      flimflam::kEAPKeyIDProperty));
+  EXPECT_TRUE(EapCredentials::IsEapAuthenticationProperty(
+      flimflam::kEapKeyMgmtProperty));
+  EXPECT_TRUE(EapCredentials::IsEapAuthenticationProperty(
+      flimflam::kEapPasswordProperty));
+  EXPECT_TRUE(EapCredentials::IsEapAuthenticationProperty(
+      flimflam::kEAPPINProperty));
+  EXPECT_TRUE(EapCredentials::IsEapAuthenticationProperty(
+      flimflam::kEapPrivateKeyProperty));
+  EXPECT_TRUE(EapCredentials::IsEapAuthenticationProperty(
+      flimflam::kEapPrivateKeyPasswordProperty));
+
+  // It's easier to test that this function returns TRUE in every situation
+  // that it should, than to test all the cases it should return FALSE in.
+  EXPECT_FALSE(EapCredentials::IsEapAuthenticationProperty(
+      flimflam::kEapCaCertProperty));
+  EXPECT_FALSE(EapCredentials::IsEapAuthenticationProperty(
+      flimflam::kEapCaCertIDProperty));
+  EXPECT_FALSE(EapCredentials::IsEapAuthenticationProperty(
+      flimflam::kEapCaCertNssProperty));
+  EXPECT_FALSE(EapCredentials::IsEapAuthenticationProperty(
+      kEapCaCertPemProperty));
+  EXPECT_FALSE(EapCredentials::IsEapAuthenticationProperty(
+      flimflam::kEAPEAPProperty));
+  EXPECT_FALSE(EapCredentials::IsEapAuthenticationProperty(
+      flimflam::kEapPhase2AuthProperty));
+  EXPECT_FALSE(EapCredentials::IsEapAuthenticationProperty(
+      flimflam::kEapUseSystemCAsProperty));
+  EXPECT_FALSE(EapCredentials::IsEapAuthenticationProperty(
+      kEapRemoteCertificationProperty));
+  EXPECT_FALSE(EapCredentials::IsEapAuthenticationProperty(
+      kEapSubjectMatchProperty));
+}
+
+TEST_F(EapCredentialsTest, LoadAndSave) {
+  MockStore store;
+  // For the values we're not testing...
+  EXPECT_CALL(store, GetCryptedString(_, _, _)).WillRepeatedly(Return(false));
+  EXPECT_CALL(store, GetString(_, _, _)).WillRepeatedly(Return(false));
+
+  const string kId("storage-id");
+  const string kIdentity("Purple Onion");
+  EXPECT_CALL(store, GetCryptedString(
+      kId, EapCredentials::kStorageEapIdentity, _))
+      .WillOnce(DoAll(SetArgumentPointee<2>(kIdentity), Return(true)));
+  const string kManagement("Shave and a Haircut");
+  EXPECT_CALL(store, GetString(
+      kId, EapCredentials::kStorageEapKeyManagement, _))
+      .WillOnce(DoAll(SetArgumentPointee<2>(kManagement), Return(true)));
+  const string kPassword("Two Bits");
+  EXPECT_CALL(store, GetCryptedString(
+      kId, EapCredentials::kStorageEapPassword, _))
+      .WillOnce(DoAll(SetArgumentPointee<2>(kPassword), Return(true)));
+
+  eap_.Load(&store, kId);
+  Mock::VerifyAndClearExpectations(&store);
+
+  EXPECT_EQ(kIdentity, eap_.identity());
+  EXPECT_EQ(kManagement, eap_.key_management());
+  EXPECT_EQ(kPassword, GetPassword());
+
+  // Authentication properties are deleted from the store if they are empty,
+  // so we expect the fields that we haven't set to be deleted.
+  EXPECT_CALL(store, DeleteKey(_, _)).Times(AnyNumber());
+  EXPECT_CALL(store, SetCryptedString(_, _, _)).Times(0);
+  EXPECT_CALL(store, DeleteKey(kId, EapCredentials::kStorageEapIdentity));
+  EXPECT_CALL(store, SetString(
+      kId, EapCredentials::kStorageEapKeyManagement, kManagement));
+  EXPECT_CALL(store, DeleteKey(kId, EapCredentials::kStorageEapPassword));
+  eap_.Save(&store, kId, false);
+  Mock::VerifyAndClearExpectations(&store);
+
+  // Authentication properties are deleted from the store if they are empty,
+  // so we expect the fields that we haven't set to be deleted.
+  EXPECT_CALL(store, DeleteKey(_, _)).Times(AnyNumber());
+  EXPECT_CALL(store, SetCryptedString(
+      kId, EapCredentials::kStorageEapIdentity, kIdentity));
+  EXPECT_CALL(store, SetString(
+      kId, EapCredentials::kStorageEapKeyManagement, kManagement));
+  EXPECT_CALL(store, SetCryptedString(
+      kId, EapCredentials::kStorageEapPassword, kPassword));
+  eap_.Save(&store, kId, true);
+}
+
+TEST_F(EapCredentialsTest, OutputConnectionMetrics) {
+  Error unused_error;
+  SetEap(flimflam::kEapMethodPEAP);
+  SetInnerEap(flimflam::kEapPhase2AuthPEAPMSCHAPV2);
+
+  MockEventDispatcher dispatcher;
+  MockMetrics metrics(&dispatcher);
+  EXPECT_CALL(metrics, SendEnumToUMA("Network.Shill.Wifi.EapOuterProtocol",
+                                     Metrics::kEapOuterProtocolPeap,
+                                     Metrics::kEapOuterProtocolMax));
+  EXPECT_CALL(metrics, SendEnumToUMA("Network.Shill.Wifi.EapInnerProtocol",
+                                     Metrics::kEapInnerProtocolPeapMschapv2,
+                                     Metrics::kEapInnerProtocolMax));
+  eap_.OutputConnectionMetrics(&metrics, Technology::kWifi);
+}
+
+TEST_F(EapCredentialsTest, PopulateSupplicantProperties) {
+  SetIdentity("testidentity");
+  SetPin("xxxx");
+  PopulateSupplicantProperties();
+  // Test that only non-empty 802.1x properties are populated.
+  EXPECT_TRUE(ContainsKey(params_, WPASupplicant::kNetworkPropertyEapIdentity));
+  EXPECT_FALSE(ContainsKey(params_, WPASupplicant::kNetworkPropertyEapKeyId));
+  EXPECT_FALSE(ContainsKey(params_, WPASupplicant::kNetworkPropertyEapCaCert));
+
+  // Test that CA path is set by default.
+  EXPECT_TRUE(ContainsKey(params_, WPASupplicant::kNetworkPropertyCaPath));
+
+  // Test that hardware-backed security arguments are not set, since
+  // neither key-id nor cert-id were set.
+  EXPECT_FALSE(ContainsKey(params_, WPASupplicant::kNetworkPropertyEapPin));
+  EXPECT_FALSE(ContainsKey(params_, WPASupplicant::kNetworkPropertyEngine));
+  EXPECT_FALSE(ContainsKey(params_, WPASupplicant::kNetworkPropertyEngineId));
+}
+
+TEST_F(EapCredentialsTest, PopulateSupplicantPropertiesNoSystemCAs) {
+  SetIdentity("testidentity");
+  SetUseSystemCAs(false);
+  PopulateSupplicantProperties();
+  // Test that CA path is not set if use_system_cas is explicitly false.
+  EXPECT_FALSE(ContainsKey(params_, WPASupplicant::kNetworkPropertyCaPath));
+}
+
+TEST_F(EapCredentialsTest, PopulateSupplicantPropertiesUsingHardwareAuth) {
+  SetIdentity("testidentity");
+  SetKeyId("key_id");
+  SetPin("xxxx");
+  PopulateSupplicantProperties();
+  // Test that EAP engine parameters set if key_id is set.
+  EXPECT_TRUE(ContainsKey(params_, WPASupplicant::kNetworkPropertyEapPin));
+  EXPECT_TRUE(ContainsKey(params_, WPASupplicant::kNetworkPropertyEapKeyId));
+  EXPECT_TRUE(ContainsKey(params_, WPASupplicant::kNetworkPropertyEngine));
+  EXPECT_TRUE(ContainsKey(params_, WPASupplicant::kNetworkPropertyEngineId));
+}
+
+TEST_F(EapCredentialsTest, PopulateSupplicantPropertiesNSS) {
+  const string kNSSNickname("nss_nickname");
+  SetCACertNSS(kNSSNickname);
+  const string kNSSCertfile("/tmp/nss-cert");
+  FilePath nss_cert(kNSSCertfile);
+  nss_identifier_ = vector<char>(1, 'a');
+  EXPECT_CALL(nss_, GetDERCertfile(kNSSNickname, nss_identifier_))
+      .WillOnce(Return(nss_cert));
+  PopulateSupplicantProperties();
+  EXPECT_TRUE(ContainsKey(params_, WPASupplicant::kNetworkPropertyEapCaCert));
+  if (ContainsKey(params_, WPASupplicant::kNetworkPropertyEapCaCert)) {
+    EXPECT_EQ(kNSSCertfile, params_[WPASupplicant::kNetworkPropertyEapCaCert]
+              .reader().get_string());
+  }
+}
+
+TEST_F(EapCredentialsTest, PopulateSupplicantPropertiesPEM) {
+  const string kPemCert("-pem-certificate-here-");
+  SetCACertPEM(kPemCert);
+  const string kPEMCertfile("/tmp/pem-cert");
+  FilePath pem_cert(kPEMCertfile);
+  EXPECT_CALL(certificate_file_, CreateDERFromString(kPemCert))
+      .WillOnce(Return(pem_cert));
+
+  PopulateSupplicantProperties();
+  EXPECT_TRUE(ContainsKey(params_, WPASupplicant::kNetworkPropertyEapCaCert));
+  if (ContainsKey(params_, WPASupplicant::kNetworkPropertyEapCaCert)) {
+    EXPECT_EQ(kPEMCertfile, params_[WPASupplicant::kNetworkPropertyEapCaCert]
+              .reader().get_string());
+  }
+}
+
+TEST_F(EapCredentialsTest, PopulateWiMaxProperties) {
+  {
+    KeyValueStore parameters;
+    eap_.PopulateWiMaxProperties(&parameters);
+
+    EXPECT_FALSE(parameters.ContainsString(
+        wimax_manager::kEAPAnonymousIdentity));
+    EXPECT_FALSE(parameters.ContainsString(
+        wimax_manager::kEAPUserIdentity));
+    EXPECT_FALSE(parameters.ContainsString(
+        wimax_manager::kEAPUserPassword));
+  }
+
+  const string kAnonymousIdentity("TestAnonymousIdentity");
+  SetAnonymousIdentity(kAnonymousIdentity);
+  const string kIdentity("TestUserIdentity");
+  SetIdentity(kIdentity);
+  const string kPassword("TestPassword");
+  SetPassword(kPassword);
+
+  {
+    KeyValueStore parameters;
+    eap_.PopulateWiMaxProperties(&parameters);
+    EXPECT_EQ(kAnonymousIdentity, parameters.LookupString(
+        wimax_manager::kEAPAnonymousIdentity, ""));
+    EXPECT_EQ(kIdentity, parameters.LookupString(
+        wimax_manager::kEAPUserIdentity, ""));
+    EXPECT_EQ(kPassword, parameters.LookupString(
+        wimax_manager::kEAPUserPassword, ""));
+  }
+}
+
+TEST_F(EapCredentialsTest, Reset) {
+  EXPECT_TRUE(IsReset());
+  SetAnonymousIdentity("foo");
+  SetCACertNSS("foo");
+  SetCACertPEM("foo");
+  SetClientCert("foo");
+  SetCertId("foo");
+  SetEap("foo");
+  SetIdentity("foo");
+  SetInnerEap("foo");
+  SetKeyId("foo");
+  SetPassword("foo");
+  SetPrivateKey("foo");
+  SetPin("foo");
+  SetUseSystemCAs(false);
+  EXPECT_FALSE(IsReset());
+  eap_.Reset();
+  EXPECT_TRUE(IsReset());
+}
+
+}  // namespace shill
diff --git a/ethernet.cc b/ethernet.cc
index 589b127..430c259 100644
--- a/ethernet.cc
+++ b/ethernet.cc
@@ -20,6 +20,7 @@
 #include "shill/control_interface.h"
 #include "shill/device.h"
 #include "shill/device_info.h"
+#include "shill/eap_credentials.h"
 #include "shill/eap_listener.h"
 #include "shill/ethernet_service.h"
 #include "shill/event_dispatcher.h"
@@ -260,8 +261,8 @@
 bool Ethernet::StartEapAuthentication() {
   map<string, DBus::Variant> params;
   vector<char> nss_identifier(link_name().begin(), link_name().end());
-  WPASupplicant::Populate8021xProperties(
-      service_->eap(), &certificate_file_, nss_, nss_identifier, &params);
+  service_->eap()->PopulateSupplicantProperties(
+      &certificate_file_, nss_, nss_identifier, &params);
   params[WPASupplicant::kNetworkPropertyEapKeyManagement].writer().
       append_string(WPASupplicant::kKeyManagementIeee8021X);
   params[WPASupplicant::kNetworkPropertyEapolFlags].writer().
diff --git a/ethernet_service.cc b/ethernet_service.cc
index 57e0bcf..1404375 100644
--- a/ethernet_service.cc
+++ b/ethernet_service.cc
@@ -17,6 +17,7 @@
 #include "shill/control_interface.h"
 #include "shill/device.h"
 #include "shill/device_info.h"
+#include "shill/eap_credentials.h"
 #include "shill/ethernet.h"
 #include "shill/event_dispatcher.h"
 #include "shill/manager.h"
@@ -41,6 +42,7 @@
   set_auto_connect(true);
   set_friendly_name("Ethernet");
   SetStrength(kStrengthMax);
+  SetEapCredentials(new EapCredentials());
 }
 
 EthernetService::~EthernetService() { }
diff --git a/metrics_unittest.cc b/metrics_unittest.cc
index 8a4cfe2..9c10f95 100644
--- a/metrics_unittest.cc
+++ b/metrics_unittest.cc
@@ -11,6 +11,7 @@
 #include <metrics/timer_mock.h>
 
 #include "shill/mock_control.h"
+#include "shill/mock_eap_credentials.h"
 #include "shill/mock_event_dispatcher.h"
 #include "shill/mock_glib.h"
 #include "shill/mock_manager.h"
@@ -49,13 +50,14 @@
                                           ssid_,
                                           flimflam::kModeManaged,
                                           flimflam::kSecurityNone,
-                                          false)) {
-  }
+                                          false)),
+        eap_(new MockEapCredentials()) {}
 
   virtual ~MetricsTest() {}
 
   virtual void SetUp() {
     metrics_.set_library(&library_);
+    wifi_service_->eap_.reset(eap_);  // Passes ownership.
     metrics_.collect_bootstats_ = false;
   }
 
@@ -90,6 +92,7 @@
   scoped_refptr<MockService> service_;
   const std::vector<uint8_t> ssid_;
   scoped_refptr<MockWiFiService> wifi_service_;
+  MockEapCredentials *eap_;  // Owned by |wifi_service_|.
 };
 
 TEST_F(MetricsTest, TimeToConfig) {
@@ -205,20 +208,11 @@
                         Metrics::kWiFiNetworkPhyMode11a,
                         Metrics::kWiFiSecurity8021x,
                         -kStrength);
-  EXPECT_CALL(library_, SendEnumToUMA("Network.Shill.Wifi.EapOuterProtocol",
-                                      Metrics::kEapOuterProtocolPeap,
-                                      Metrics::kEapOuterProtocolMax));
-  EXPECT_CALL(library_, SendEnumToUMA("Network.Shill.Wifi.EapInnerProtocol",
-                                      Metrics::kEapInnerProtocolPeapMschapv2,
-                                      Metrics::kEapInnerProtocolMax));
   wifi_service_->frequency_ = 2412;
   wifi_service_->physical_mode_ = Metrics::kWiFiNetworkPhyMode11a;
   wifi_service_->security_ = flimflam::kSecurity8021x;
   wifi_service_->raw_signal_strength_ = kStrength;
-  EapCredentials eap;
-  eap.eap = flimflam::kEapMethodPEAP;
-  eap.inner_eap = flimflam::kEapPhase2AuthPEAPMSCHAPV2;
-  wifi_service_->set_eap(eap);
+  EXPECT_CALL(*eap_, OutputConnectionMetrics(&metrics_, Technology::kWifi));
   metrics_.RegisterService(wifi_service_);
   metrics_.NotifyServiceStateChanged(wifi_service_, Service::kStateConnected);
 }
diff --git a/mock_eap_credentials.cc b/mock_eap_credentials.cc
new file mode 100644
index 0000000..456cdf8
--- /dev/null
+++ b/mock_eap_credentials.cc
@@ -0,0 +1,18 @@
+// Copyright (c) 2013 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/mock_eap_credentials.h"
+
+#include <gtest/gtest.h>
+
+namespace shill {
+
+MockEapCredentials::MockEapCredentials() : EapCredentials() {
+  ON_CALL(*this, key_management())
+      .WillByDefault(testing::ReturnRef(kDefaultKeyManagement));
+}
+
+MockEapCredentials::~MockEapCredentials() {}
+
+}  // namespace shill
diff --git a/mock_eap_credentials.h b/mock_eap_credentials.h
new file mode 100644
index 0000000..3d14d68
--- /dev/null
+++ b/mock_eap_credentials.h
@@ -0,0 +1,47 @@
+// Copyright (c) 2013 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.
+
+#ifndef SHILL_MOCK_EAP_CREDENTIALS_H_
+#define SHILL_MOCK_EAP_CREDENTIALS_H_
+
+#include "shill/eap_credentials.h"
+
+#include <gmock/gmock.h>
+
+namespace shill {
+
+class MockEapCredentials : public EapCredentials {
+ public:
+  MockEapCredentials();
+  virtual ~MockEapCredentials();
+
+  MOCK_CONST_METHOD0(IsConnectable, bool());
+  MOCK_CONST_METHOD0(IsConnectableUsingPassphrase, bool());
+  MOCK_METHOD2(Load, void(StoreInterface *store, const std::string &id));
+  MOCK_CONST_METHOD2(OutputConnectionMetrics,
+                     void(Metrics *metrics, Technology::Identifier technology));
+  MOCK_CONST_METHOD4(PopulateSupplicantProperties, void(
+      CertificateFile *certificate_file,
+      NSS *nss,
+      const std::vector<char> nss_identifier,
+      std::map<std::string, DBus::Variant> *params));
+  MOCK_CONST_METHOD1(PopulateWiMaxProperties, void(KeyValueStore *params));
+  MOCK_CONST_METHOD3(Save, void(
+      StoreInterface *store, const std::string &id, bool save_credentials));
+  MOCK_METHOD0(Reset, void());
+  MOCK_METHOD2(SetKeyManagement, void(const std::string &key_management,
+                                      Error *error));
+  MOCK_CONST_METHOD0(identity, const std::string &());
+  MOCK_CONST_METHOD0(key_management, const std::string &());
+  MOCK_METHOD1(set_password, void(const std::string &password));
+
+ private:
+  std::string kDefaultKeyManagement;
+
+  DISALLOW_COPY_AND_ASSIGN(MockEapCredentials);
+};
+
+}  // namespace shill
+
+#endif  // SHILL_MOCK_EAP_CREDENTIALS_H_
diff --git a/profile_dbus_property_exporter.cc b/profile_dbus_property_exporter.cc
index 613a47b..a9881ef 100644
--- a/profile_dbus_property_exporter.cc
+++ b/profile_dbus_property_exporter.cc
@@ -11,6 +11,7 @@
 #include <dbus-c++/dbus.h>
 
 #include "shill/dbus_adaptor.h"
+#include "shill/eap_credentials.h"
 #include "shill/error.h"
 #include "shill/service.h"
 #include "shill/store_interface.h"
@@ -66,11 +67,11 @@
 bool ProfileDBusPropertyExporter::LoadEapServiceProperties(
     PropertyList *properties,
     Error */*error*/) {
-  LoadString(properties, WiFiService::kStorageEapCACertID,
+  LoadString(properties, EapCredentials::kStorageEapCACertID,
              flimflam::kEapCaCertIDProperty);
-  LoadString(properties, WiFiService::kStorageEapCertID,
+  LoadString(properties, EapCredentials::kStorageEapCertID,
              flimflam::kEAPCertIDProperty);
-  LoadString(properties, WiFiService::kStorageEapKeyID,
+  LoadString(properties, EapCredentials::kStorageEapKeyID,
              flimflam::kEAPKeyIDProperty);
   return true;
 }
diff --git a/profile_dbus_property_exporter_unittest.cc b/profile_dbus_property_exporter_unittest.cc
index 24cee52..2c1d6b3 100644
--- a/profile_dbus_property_exporter_unittest.cc
+++ b/profile_dbus_property_exporter_unittest.cc
@@ -10,6 +10,7 @@
 #include <chromeos/dbus/service_constants.h>
 #include <gtest/gtest.h>
 
+#include "shill/eap_credentials.h"
 #include "shill/error.h"
 #include "shill/mock_store.h"
 #include "shill/service.h"
@@ -165,11 +166,11 @@
   const string security("unbreakablecrypto");
   ExpectStringProperty(WiFiService::kStorageSecurity, security);
   const string ca_cert_id("ca-cert-id");
-  ExpectStringProperty(WiFiService::kStorageEapCACertID, ca_cert_id);
+  ExpectStringProperty(EapCredentials::kStorageEapCACertID, ca_cert_id);
   const string cert_id("cert-id");
-  ExpectStringProperty(WiFiService::kStorageEapCertID, cert_id);
+  ExpectStringProperty(EapCredentials::kStorageEapCertID, cert_id);
   const string key_id("key-id");
-  ExpectStringProperty(WiFiService::kStorageEapKeyID, key_id);
+  ExpectStringProperty(EapCredentials::kStorageEapKeyID, key_id);
 
 
   ProfileDBusPropertyExporter::PropertyList props;
diff --git a/service.cc b/service.cc
index 3508ad1..9d12995 100644
--- a/service.cc
+++ b/service.cc
@@ -19,6 +19,7 @@
 #include "shill/connection.h"
 #include "shill/control_interface.h"
 #include "shill/diagnostics_reporter.h"
+#include "shill/eap_credentials.h"
 #include "shill/error.h"
 #include "shill/http_proxy.h"
 #include "shill/logging.h"
@@ -72,23 +73,6 @@
 
 const char Service::kStorageAutoConnect[] = "AutoConnect";
 const char Service::kStorageCheckPortal[] = "CheckPortal";
-const char Service::kStorageEapAnonymousIdentity[] = "EAP.AnonymousIdentity";
-const char Service::kStorageEapCACert[] = "EAP.CACert";
-const char Service::kStorageEapCACertID[] = "EAP.CACertID";
-const char Service::kStorageEapCACertNSS[] = "EAP.CACertNSS";
-const char Service::kStorageEapCACertPEM[] = "EAP.CACertPEM";
-const char Service::kStorageEapCertID[] = "EAP.CertID";
-const char Service::kStorageEapClientCert[] = "EAP.ClientCert";
-const char Service::kStorageEapEap[] = "EAP.EAP";
-const char Service::kStorageEapIdentity[] = "EAP.Identity";
-const char Service::kStorageEapInnerEap[] = "EAP.InnerEAP";
-const char Service::kStorageEapKeyID[] = "EAP.KeyID";
-const char Service::kStorageEapKeyManagement[] = "EAP.KeyMgmt";
-const char Service::kStorageEapPIN[] = "EAP.PIN";
-const char Service::kStorageEapPassword[] = "EAP.Password";
-const char Service::kStorageEapPrivateKey[] = "EAP.PrivateKey";
-const char Service::kStorageEapPrivateKeyPassword[] = "EAP.PrivateKeyPassword";
-const char Service::kStorageEapUseSystemCAs[] = "EAP.UseSystemCAs";
 const char Service::kStorageError[] = "Error";
 const char Service::kStorageFavorite[] = "Favorite";
 const char Service::kStorageGUID[] = "GUID";
@@ -172,37 +156,10 @@
   HelpRegisterDerivedRpcIdentifier(flimflam::kDeviceProperty,
                                    &Service::GetDeviceRpcId,
                                    NULL);
+  store_.RegisterConstStrings(kEapRemoteCertificationProperty,
+                              &remote_certification_);
   store_.RegisterString(flimflam::kGuidProperty, &guid_);
 
-  store_.RegisterString(flimflam::kEapIdentityProperty, &eap_.identity);
-  store_.RegisterString(flimflam::kEAPEAPProperty, &eap_.eap);
-  store_.RegisterString(flimflam::kEapPhase2AuthProperty, &eap_.inner_eap);
-  store_.RegisterString(flimflam::kEapAnonymousIdentityProperty,
-                        &eap_.anonymous_identity);
-  store_.RegisterString(flimflam::kEAPClientCertProperty, &eap_.client_cert);
-  store_.RegisterString(flimflam::kEAPCertIDProperty, &eap_.cert_id);
-  store_.RegisterString(flimflam::kEapPrivateKeyProperty, &eap_.private_key);
-  HelpRegisterWriteOnlyDerivedString(flimflam::kEapPrivateKeyPasswordProperty,
-                                     &Service::SetEAPPrivateKeyPassword,
-                                     NULL,
-                                     &eap_.private_key_password);
-  store_.RegisterString(flimflam::kEAPKeyIDProperty, &eap_.key_id);
-  store_.RegisterString(flimflam::kEapCaCertProperty, &eap_.ca_cert);
-  store_.RegisterString(flimflam::kEapCaCertIDProperty, &eap_.ca_cert_id);
-  store_.RegisterString(flimflam::kEapCaCertNssProperty, &eap_.ca_cert_nss);
-  store_.RegisterString(kEapCaCertPemProperty, &eap_.ca_cert_pem);
-  store_.RegisterString(flimflam::kEAPPINProperty, &eap_.pin);
-  HelpRegisterWriteOnlyDerivedString(flimflam::kEapPasswordProperty,
-                                     &Service::SetEAPPassword,
-                                     NULL,
-                                     &eap_.password);
-  store_.RegisterString(flimflam::kEapKeyMgmtProperty, &eap_.key_management);
-  store_.RegisterBool(flimflam::kEapUseSystemCAsProperty, &eap_.use_system_cas);
-  store_.RegisterConstStrings(shill::kEapRemoteCertificationProperty,
-                              &eap_.remote_certification);
-  store_.RegisterString(shill::kEapSubjectMatchProperty,
-                        &eap_.subject_match);
-
   // TODO(ers): in flimflam clearing Error has the side-effect of
   // setting the service state to IDLE. Is this important? I could
   // see an autotest depending on it.
@@ -442,9 +399,14 @@
   storage->GetString(id, kStorageProxyConfig, &proxy_config_);
   storage->GetBool(id, kStorageSaveCredentials, &save_credentials_);
   storage->GetString(id, kStorageUIData, &ui_data_);
-  LoadEapCredentials(storage, id);
+
   static_ip_parameters_.Load(storage, id);
 
+  if (mutable_eap()) {
+    mutable_eap()->Load(storage, id);
+    OnEapCredentialsChanged();
+  }
+
   explicitly_disconnected_ = false;
   favorite_ = true;
 
@@ -462,8 +424,11 @@
   proxy_config_ = "";
   save_credentials_ = true;
   ui_data_ = "";
+  if (mutable_eap()) {
+    mutable_eap()->Reset();
+  }
+  ClearEAPCertification();
 
-  UnloadEapCredentials();
   Error error;  // Ignored.
   Disconnect(&error);
   return false;
@@ -497,8 +462,10 @@
   storage->SetBool(id, kStorageSaveCredentials, save_credentials_);
   SaveString(storage, id, kStorageUIData, ui_data_, false, true);
 
-  SaveEapCredentials(storage, id);
   static_ip_parameters_.Save(storage, id);
+  if (eap()) {
+    eap()->Save(storage, id, save_credentials_);
+  }
   return true;
 }
 
@@ -644,52 +611,7 @@
 }
 
 bool Service::Is8021xConnectable() const {
-  // We mirror all the flimflam checks (see service.c:is_connectable()).
-
-  // Identity is required.
-  if (eap_.identity.empty()) {
-    SLOG(Service, 2) << "Not connectable: Identity is empty.";
-    return false;
-  }
-
-  if (!eap_.client_cert.empty() || !eap_.cert_id.empty()) {
-    // If a client certificate is being used, we must have a private key.
-    if (eap_.private_key.empty() && eap_.key_id.empty()) {
-      SLOG(Service, 2)
-          << "Not connectable. Client certificate but no private key.";
-      return false;
-    }
-  }
-  if (!eap_.cert_id.empty() || !eap_.key_id.empty() ||
-      !eap_.ca_cert_id.empty()) {
-    // If PKCS#11 data is needed, a PIN is required.
-    if (eap_.pin.empty()) {
-      SLOG(Service, 2) << "Not connectable. PKCS#11 data but no PIN.";
-      return false;
-    }
-  }
-
-  // For EAP-TLS, a client certificate is required.
-  if (eap_.eap.empty() || eap_.eap == "TLS") {
-    if ((!eap_.client_cert.empty() || !eap_.cert_id.empty()) &&
-        (!eap_.private_key.empty() || !eap_.key_id.empty())) {
-      SLOG(Service, 2) << "Connectable. EAP-TLS with a client cert and key.";
-      return true;
-    }
-  }
-
-  // For EAP types other than TLS (e.g. EAP-TTLS or EAP-PEAP, password is the
-  // minimum requirement), at least an identity + password is required.
-  if (eap_.eap.empty() || eap_.eap != "TLS") {
-    if (!eap_.password.empty()) {
-      SLOG(Service, 2) << "Connectable. !EAP-TLS and has a password.";
-      return true;
-    }
-  }
-
-  SLOG(Service, 2)
-      << "Not connectable. No suitable EAP configuration was found.";
-  return false;
+  return eap() && eap()->IsConnectable();
 }
 
 bool Service::AddEAPCertification(const string &name, size_t depth) {
@@ -701,13 +623,13 @@
     return false;
   }
 
-  if (depth >= eap_.remote_certification.size()) {
-    eap_.remote_certification.resize(depth + 1);
-  } else if (name == eap_.remote_certification[depth]) {
+  if (depth >= remote_certification_.size()) {
+    remote_certification_.resize(depth + 1);
+  } else if (name == remote_certification_[depth]) {
     return true;
   }
 
-  eap_.remote_certification[depth] = name;
+  remote_certification_[depth] = name;
   LOG(INFO) << "Received certification for "
             << name
             << " at depth "
@@ -716,13 +638,15 @@
 }
 
 void Service::ClearEAPCertification() {
-  eap_.remote_certification.clear();
+  remote_certification_.clear();
 }
 
-void Service::set_eap(const EapCredentials &eap) {
-  eap_ = eap;
-  // Note: Connectability can only be updated by a subclass of Service
-  // with knowledge of whether the service actually uses 802.1x credentials.
+void Service::SetEapCredentials(EapCredentials *eap) {
+  // This operation must be done at most once for the lifetime of the service.
+  CHECK(eap && !eap_);
+
+  eap_.reset(eap);
+  eap_->InitPropertyStore(mutable_store());
 }
 
 // static
@@ -996,18 +920,8 @@
 }
 
 void Service::OnPropertyChanged(const string &property) {
-  if (Is8021x() &&
-      (property == flimflam::kEAPCertIDProperty ||
-       property == flimflam::kEAPClientCertProperty ||
-       property == flimflam::kEAPKeyIDProperty ||
-       property == flimflam::kEAPPINProperty ||
-       property == flimflam::kEapCaCertIDProperty ||
-       property == flimflam::kEapIdentityProperty ||
-       property == flimflam::kEapKeyMgmtProperty ||
-       property == flimflam::kEapPasswordProperty ||
-       property == flimflam::kEapPrivateKeyProperty)) {
-    // This notifies subclassess that EAP parameters have been changed.
-    set_eap(eap_);
+  if (Is8021x() && EapCredentials::IsEapAuthenticationProperty(property)) {
+    OnEapCredentialsChanged();
   }
   SaveToProfile();
   if ((property == flimflam::kCheckPortalProperty ||
@@ -1175,18 +1089,7 @@
       StringsAccessor(new CustomAccessor<Service, Strings>(this, get, NULL)));
 }
 
-void Service::HelpRegisterWriteOnlyDerivedString(
-    const string &name,
-    void(Service::*set)(const string &, Error *),
-    void(Service::*clear)(Error *),
-    const string *default_value) {
-  store_.RegisterDerivedString(
-      name,
-      StringAccessor(
-          new CustomWriteOnlyAccessor<Service, string>(
-              this, set, clear, default_value)));
-}
-
+// static
 void Service::SaveString(StoreInterface *storage,
                          const string &id,
                          const string &key,
@@ -1204,95 +1107,18 @@
   storage->SetString(id, key, value);
 }
 
-void Service::LoadEapCredentials(StoreInterface *storage, const string &id) {
-  EapCredentials eap;
-  storage->GetCryptedString(id, kStorageEapIdentity, &eap.identity);
-  storage->GetString(id, kStorageEapEap, &eap.eap);
-  storage->GetString(id, kStorageEapInnerEap, &eap.inner_eap);
-  storage->GetCryptedString(id,
-                            kStorageEapAnonymousIdentity,
-                            &eap.anonymous_identity);
-  storage->GetString(id, kStorageEapClientCert, &eap.client_cert);
-  storage->GetString(id, kStorageEapCertID, &eap.cert_id);
-  storage->GetString(id, kStorageEapPrivateKey, &eap.private_key);
-  storage->GetCryptedString(id,
-                            kStorageEapPrivateKeyPassword,
-                            &eap.private_key_password);
-  storage->GetString(id, kStorageEapKeyID, &eap.key_id);
-  storage->GetString(id, kStorageEapCACert, &eap.ca_cert);
-  storage->GetString(id, kStorageEapCACertID, &eap.ca_cert_id);
-  storage->GetString(id, kStorageEapCACertNSS, &eap.ca_cert_nss);
-  storage->GetString(id, kStorageEapCACertPEM, &eap.ca_cert_pem);
-  storage->GetBool(id, kStorageEapUseSystemCAs, &eap.use_system_cas);
-  storage->GetString(id, kStorageEapPIN, &eap.pin);
-  storage->GetCryptedString(id, kStorageEapPassword, &eap.password);
-  storage->GetString(id, kStorageEapKeyManagement, &eap.key_management);
-  set_eap(eap);
-}
-
-void Service::SaveEapCredentials(StoreInterface *storage, const string &id) {
-  bool save = save_credentials_;
-  SaveString(storage, id, kStorageEapIdentity, eap_.identity, true, save);
-  SaveString(storage, id, kStorageEapEap, eap_.eap, false, true);
-  SaveString(storage, id, kStorageEapInnerEap, eap_.inner_eap, false, true);
-  SaveString(storage,
-             id,
-             kStorageEapAnonymousIdentity,
-             eap_.anonymous_identity,
-             true,
-             save);
-  SaveString(storage, id, kStorageEapClientCert, eap_.client_cert, false, save);
-  SaveString(storage, id, kStorageEapCertID, eap_.cert_id, false, save);
-  SaveString(storage, id, kStorageEapPrivateKey, eap_.private_key, false, save);
-  SaveString(storage,
-             id,
-             kStorageEapPrivateKeyPassword,
-             eap_.private_key_password,
-             true,
-             save);
-  SaveString(storage, id, kStorageEapKeyID, eap_.key_id, false, save);
-  SaveString(storage, id, kStorageEapCACert, eap_.ca_cert, false, true);
-  SaveString(storage, id, kStorageEapCACertID, eap_.ca_cert_id, false, true);
-  SaveString(storage, id, kStorageEapCACertNSS, eap_.ca_cert_nss, false, true);
-  SaveString(storage, id, kStorageEapCACertPEM, eap_.ca_cert_pem, false, true);
-  storage->SetBool(id, kStorageEapUseSystemCAs, eap_.use_system_cas);
-  SaveString(storage, id, kStorageEapPIN, eap_.pin, false, save);
-  SaveString(storage, id, kStorageEapPassword, eap_.password, true, save);
-  SaveString(storage,
-             id,
-             kStorageEapKeyManagement,
-             eap_.key_management,
-             false,
-             true);
-}
-
-void Service::UnloadEapCredentials() {
-  eap_.identity = "";
-  eap_.eap = "";
-  eap_.inner_eap = "";
-  eap_.anonymous_identity = "";
-  eap_.client_cert = "";
-  eap_.cert_id = "";
-  eap_.private_key = "";
-  eap_.private_key_password = "";
-  eap_.key_id = "";
-  eap_.ca_cert = "";
-  eap_.ca_cert_id = "";
-  eap_.use_system_cas = true;
-  eap_.pin = "";
-  eap_.password = "";
-}
-
 void Service::IgnoreParameterForConfigure(const string &parameter) {
   parameters_ignored_for_configure_.insert(parameter);
 }
 
 const string &Service::GetEAPKeyManagement() const {
-  return eap_.key_management;
+  CHECK(eap());
+  return eap()->key_management();
 }
 
 void Service::SetEAPKeyManagement(const string &key_management) {
-  eap_.key_management = key_management;
+  CHECK(mutable_eap());
+  mutable_eap()->SetKeyManagement(key_management, NULL);
 }
 
 bool Service::GetAutoConnect(Error */*error*/) {
@@ -1329,15 +1155,6 @@
   check_portal_ = check_portal;
 }
 
-void Service::SetEAPPassword(const string &password, Error */*error*/) {
-  eap_.password = password;
-}
-
-void Service::SetEAPPrivateKeyPassword(const string &password,
-                                       Error */*error*/) {
-  eap_.private_key_password = password;
-}
-
 void Service::SetSecurity(CryptoAlgorithm crypto_algorithm, bool key_rotation,
                           bool endpoint_auth) {
   crypto_algorithm_ = crypto_algorithm;
diff --git a/service.h b/service.h
index 48b47ea..9ab30cb 100644
--- a/service.h
+++ b/service.h
@@ -22,7 +22,6 @@
 #include "shill/adaptor_interfaces.h"
 #include "shill/accessor_interface.h"
 #include "shill/callbacks.h"
-#include "shill/eap_credentials.h"
 #include "shill/property_store.h"
 #include "shill/refptr_types.h"
 #include "shill/shill_time.h"
@@ -37,6 +36,7 @@
 
 class ControlInterface;
 class DiagnosticsReporter;
+class EapCredentials;
 class Endpoint;
 class Error;
 class EventDispatcher;
@@ -66,23 +66,6 @@
   // crosbug.com/25813
   static const char kStorageAutoConnect[];
   static const char kStorageCheckPortal[];
-  static const char kStorageEapAnonymousIdentity[];
-  static const char kStorageEapCACert[];
-  static const char kStorageEapCACertID[];
-  static const char kStorageEapCACertNSS[];
-  static const char kStorageEapCACertPEM[];
-  static const char kStorageEapCertID[];
-  static const char kStorageEapClientCert[];
-  static const char kStorageEapEap[];
-  static const char kStorageEapIdentity[];
-  static const char kStorageEapInnerEap[];
-  static const char kStorageEapKeyID[];
-  static const char kStorageEapKeyManagement[];
-  static const char kStorageEapPIN[];
-  static const char kStorageEapPassword[];
-  static const char kStorageEapPrivateKey[];
-  static const char kStorageEapPrivateKeyPassword[];
-  static const char kStorageEapUseSystemCAs[];
   static const char kStorageError[];
   static const char kStorageFavorite[];
   static const char kStorageGUID[];
@@ -354,8 +337,8 @@
   virtual Technology::Identifier technology() const { return technology_; }
   std::string GetTechnologyString() const;
 
-  const EapCredentials &eap() const { return eap_; }
-  virtual void set_eap(const EapCredentials &eap);
+  const EapCredentials *eap() const { return eap_.get(); }
+  void SetEapCredentials(EapCredentials *eap);
 
   bool save_credentials() const { return save_credentials_; }
   void set_save_credentials(bool save) { save_credentials_ = save; }
@@ -407,9 +390,16 @@
   // the RPC adaptor.
   virtual void OnPropertyChanged(const std::string &property);
 
+  // Notification that occurs when an EAP credential property has been
+  // changed.  Some service subclasses can choose to respond to this
+  // event.
+  virtual void OnEapCredentialsChanged() {}
+
   // Called by the manager once after a resume.
   virtual void OnAfterResume();
 
+  EapCredentials *mutable_eap() { return eap_.get(); }
+
   PropertyStore *mutable_store() { return &store_; }
   const PropertyStore &store() const { return store_; }
   StaticIPParameters *mutable_static_ip_parameters() {
@@ -419,6 +409,16 @@
     return static_ip_parameters_;
   }
 
+  // Assigns |value| to |key| in |storage| if |value| is non-empty and |save| is
+  // true. Otherwise, removes |key| from |storage|. If |crypted| is true, the
+  // value is encrypted.
+  static void SaveString(StoreInterface *storage,
+                         const std::string &id,
+                         const std::string &key,
+                         const std::string &value,
+                         bool crypted,
+                         bool save);
+
  protected:
   friend class base::RefCounted<Service>;
 
@@ -484,18 +484,6 @@
 
   ServiceAdaptorInterface *adaptor() const { return adaptor_.get(); }
 
-  // Assigns |value| to |key| in |storage| if |value| is non-empty and |save| is
-  // true. Otherwise, removes |key| from |storage|. If |crypted| is true, the
-  // value is encrypted.
-  void SaveString(StoreInterface *storage,
-                  const std::string &id,
-                  const std::string &key,
-                  const std::string &value,
-                  bool crypted,
-                  bool save);
-
-  void LoadEapCredentials(StoreInterface *storage, const std::string &id);
-  void SaveEapCredentials(StoreInterface *storage, const std::string &id);
   void UnloadEapCredentials();
 
   // Ignore |parameter| when performing a Configure() operation.
@@ -512,8 +500,6 @@
   EventDispatcher *dispatcher() const { return dispatcher_; }
   const std::string &GetEAPKeyManagement() const;
   virtual void SetEAPKeyManagement(const std::string &key_management);
-  void SetEAPPassword(const std::string &password, Error *error);
-  void SetEAPPrivateKeyPassword(const std::string &password, Error *error);
   Manager *manager() const { return manager_; }
   Metrics *metrics() const { return metrics_; }
 
@@ -548,8 +534,8 @@
   FRIEND_TEST(ServiceTest, CalculateState);
   FRIEND_TEST(ServiceTest, CalculateTechnology);
   FRIEND_TEST(ServiceTest, Certification);
+  FRIEND_TEST(ServiceTest, ConfigureEapStringProperty);
   FRIEND_TEST(ServiceTest, ConfigureIgnoredProperty);
-  FRIEND_TEST(ServiceTest, ConfigureStringProperty);
   FRIEND_TEST(ServiceTest, Constructor);
   FRIEND_TEST(ServiceTest, GetIPConfigRpcIdentifier);
   FRIEND_TEST(ServiceTest, GetProperties);
@@ -682,7 +668,7 @@
   std::string ui_data_;
   std::string guid_;
   bool save_credentials_;
-  EapCredentials eap_;  // Only saved if |save_credentials_| is true.
+  scoped_ptr<EapCredentials> eap_;
   Technology::Identifier technology_;
   // The time of the most recent failure. Value is 0 if the service is
   // not currently failed.
@@ -710,6 +696,9 @@
   // to PII concerns.
   std::string friendly_name_;
 
+  // List of subject names reported by remote entity during TLS setup.
+  std::vector<std::string> remote_certification_;
+
   scoped_ptr<ServiceAdaptorInterface> adaptor_;
   scoped_ptr<HTTPProxy> http_proxy_;
   ConnectionRefPtr connection_;
diff --git a/service_unittest.cc b/service_unittest.cc
index d16397a..eab8380 100644
--- a/service_unittest.cc
+++ b/service_unittest.cc
@@ -23,6 +23,7 @@
 #include "shill/mock_connection.h"
 #include "shill/mock_device_info.h"
 #include "shill/mock_diagnostics_reporter.h"
+#include "shill/mock_eap_credentials.h"
 #include "shill/mock_event_dispatcher.h"
 #include "shill/mock_log.h"
 #include "shill/mock_manager.h"
@@ -70,10 +71,12 @@
                                        metrics(),
                                        &mock_manager_)),
         storage_id_(ServiceUnderTest::kStorageId),
-        power_manager_(new MockPowerManager(NULL, &proxy_factory_)) {
+        power_manager_(new MockPowerManager(NULL, &proxy_factory_)),
+        eap_(new MockEapCredentials()) {
     service_->time_ = &time_;
     DefaultValue<Timestamp>::Set(Timestamp());
     service_->diagnostics_reporter_ = &diagnostics_reporter_;
+    service_->eap_.reset(eap_);  // Passes ownership.
     mock_manager_.running_ = true;
     mock_manager_.set_power_manager(power_manager_);  // Passes ownership.
   }
@@ -182,6 +185,7 @@
   string storage_id_;
   TestProxyFactory proxy_factory_;
   MockPowerManager *power_manager_;  // Owned by |mock_manager_|.
+  MockEapCredentials *eap_;  // Owned by |service_|.
 };
 
 class AllMockServiceTest : public testing::Test {
@@ -295,6 +299,22 @@
   {
     ::DBus::Error error;
     EXPECT_TRUE(DBusAdaptor::SetProperty(service_->mutable_store(),
+                                         flimflam::kGuidProperty,
+                                         PropertyStoreTest::kStringV,
+                                         &error));
+  }
+  // Ensure that EAP properties cannot be set on services with no EAP
+  // credentials.  Use service2_ here since we're have some code in
+  // ServiceTest::SetUp() that fiddles with service_->eap_.
+  {
+    ::DBus::Error error;
+    EXPECT_FALSE(DBusAdaptor::SetProperty(service2_->mutable_store(),
+                                          flimflam::kEAPEAPProperty,
+                                          PropertyStoreTest::kStringV,
+                                          &error));
+    EXPECT_EQ(invalid_prop(), error.name());
+    service2_->SetEapCredentials(new EapCredentials());
+    EXPECT_TRUE(DBusAdaptor::SetProperty(service2_->mutable_store(),
                                          flimflam::kEAPEAPProperty,
                                          PropertyStoreTest::kStringV,
                                          &error));
@@ -354,6 +374,7 @@
   EXPECT_CALL(storage, GetBool(storage_id_, _, _)).Times(AnyNumber());
   EXPECT_CALL(storage,
               GetBool(storage_id_, Service::kStorageSaveCredentials, _));
+  EXPECT_CALL(*eap_, Load(&storage, storage_id_));
   EXPECT_TRUE(service_->Load(&storage));
 }
 
@@ -410,6 +431,7 @@
               SetBool(storage_id_,
                       Service::kStorageSaveCredentials,
                       service_->save_credentials()));
+  EXPECT_CALL(*eap_, Save(&storage, storage_id_, true));
   EXPECT_TRUE(service_->Save(&storage));
 }
 
@@ -426,6 +448,7 @@
   EXPECT_FALSE(service_->explicitly_disconnected_);
   service_->explicitly_disconnected_ = true;
   EXPECT_FALSE(service_->has_ever_connected_);
+  EXPECT_CALL(*eap_, Load(&storage, storage_id_));
   ASSERT_TRUE(service_->Load(&storage));
   // TODO(pstew): Only two string properties in the service are tested as
   // a sentinel that properties are being set and reset at the right times.
@@ -438,6 +461,7 @@
   EXPECT_FALSE(service_->explicitly_disconnected_);
   EXPECT_TRUE(service_->has_ever_connected_);
   service_->explicitly_disconnected_ = true;
+  EXPECT_CALL(*eap_, Reset());
   service_->Unload();
   EXPECT_EQ(string(""), service_->ui_data_);
   EXPECT_EQ(string(""), service_->guid_);
@@ -589,6 +613,7 @@
   // again.
   NiceMock<MockStore> storage;
   EXPECT_CALL(storage, ContainsGroup(storage_id_)).WillOnce(Return(true));
+  EXPECT_CALL(*eap_, Load(&storage, storage_id_));
   EXPECT_TRUE(service_->Load(&storage));
   EXPECT_TRUE(service_->IsAutoConnectable(&reason));
 
@@ -741,16 +766,35 @@
 }
 
 TEST_F(ServiceTest, ConfigureStringProperty) {
-  const string kEAPManagement0 = "management_zero";
-  const string kEAPManagement1 = "management_one";
-  service_->SetEAPKeyManagement(kEAPManagement0);
-  ASSERT_EQ(kEAPManagement0, service_->GetEAPKeyManagement());
+  const string kGuid0 = "guid_zero";
+  const string kGuid1 = "guid_one";
+  service_->set_guid(kGuid0);
+  ASSERT_EQ(kGuid0, service_->guid());
   KeyValueStore args;
-  args.SetString(flimflam::kEapKeyMgmtProperty, kEAPManagement1);
+  args.SetString(flimflam::kGuidProperty, kGuid1);
   Error error;
   service_->Configure(args, &error);
   EXPECT_TRUE(error.IsSuccess());
-  EXPECT_EQ(kEAPManagement1, service_->GetEAPKeyManagement());
+  EXPECT_EQ(kGuid1, service_->guid());
+}
+
+TEST_F(ServiceTest, ConfigureEapStringProperty) {
+  MockEapCredentials *eap = new MockEapCredentials();
+  service2_->SetEapCredentials(eap);  // Passes ownership.
+
+  const string kEAPManagement0 = "management_zero";
+  const string kEAPManagement1 = "management_one";
+  service2_->SetEAPKeyManagement(kEAPManagement0);
+
+  EXPECT_CALL(*eap, key_management())
+      .WillOnce(ReturnRef(kEAPManagement0));
+  ASSERT_EQ(kEAPManagement0, service2_->GetEAPKeyManagement());
+  KeyValueStore args;
+  EXPECT_CALL(*eap, SetKeyManagement(kEAPManagement1, _));
+  args.SetString(flimflam::kEapKeyMgmtProperty, kEAPManagement1);
+  Error error;
+  service2_->Configure(args, &error);
+  EXPECT_TRUE(error.IsSuccess());
 }
 
 TEST_F(ServiceTest, ConfigureIntProperty) {
@@ -998,13 +1042,13 @@
   EXPECT_TRUE(service_->connectable());
 }
 
-// Make sure a property is registered as a write only property
-// by reading and comparing all string properties returned on the store.
-// Subtle: We need to convert the test argument back and forth between
-// string and ::DBus::Variant because this is the parameter type that
-// our supeclass (PropertyStoreTest) is declared with.
-class ReadOnlyServicePropertyTest : public ServiceTest {};
-TEST_P(ReadOnlyServicePropertyTest, PropertyWriteOnly) {
+class WriteOnlyServicePropertyTest : public ServiceTest {};
+TEST_P(WriteOnlyServicePropertyTest, PropertyWriteOnly) {
+  // Use a real EapCredentials instance since the base Service class
+  // contains no write-only properties.
+  EapCredentials eap;
+  eap.InitPropertyStore(service_->mutable_store());
+
   string property(GetParam().reader().get_string());
   Error error;
   EXPECT_FALSE(service_->store().GetStringProperty(property, NULL, &error));
@@ -1012,8 +1056,8 @@
 }
 
 INSTANTIATE_TEST_CASE_P(
-    ReadOnlyServicePropertyTestInstance,
-    ReadOnlyServicePropertyTest,
+    WriteOnlyServicePropertyTestInstance,
+    WriteOnlyServicePropertyTest,
     Values(
         DBusAdaptor::StringToVariant(flimflam::kEapPrivateKeyPasswordProperty),
         DBusAdaptor::StringToVariant(flimflam::kEapPasswordProperty)));
@@ -1058,15 +1102,15 @@
   mock_device_info.reset();
 }
 
-class ServiceWithMockSetEap : public ServiceUnderTest {
+class ServiceWithMockOnEapCredentialsChanged : public ServiceUnderTest {
  public:
-  ServiceWithMockSetEap(ControlInterface *control_interface,
-                        EventDispatcher *dispatcher,
-                        Metrics *metrics,
-                        Manager *manager)
+  ServiceWithMockOnEapCredentialsChanged(ControlInterface *control_interface,
+                                         EventDispatcher *dispatcher,
+                                         Metrics *metrics,
+                                         Manager *manager)
       : ServiceUnderTest(control_interface, dispatcher, metrics, manager),
         is_8021x_(false) {}
-  MOCK_METHOD1(set_eap, void(const EapCredentials &eap));
+  MOCK_METHOD0(OnEapCredentialsChanged, void());
   virtual bool Is8021x() const { return is_8021x_; }
   void set_is_8021x(bool is_8021x) { is_8021x_ = is_8021x; }
 
@@ -1075,32 +1119,32 @@
 };
 
 TEST_F(ServiceTest, SetEAPCredentialsOverRPC) {
-  scoped_refptr<ServiceWithMockSetEap> service(
-      new ServiceWithMockSetEap(control_interface(),
-                                dispatcher(),
-                                metrics(),
-                                &mock_manager_));
+  scoped_refptr<ServiceWithMockOnEapCredentialsChanged> service(
+      new ServiceWithMockOnEapCredentialsChanged(control_interface(),
+                                                 dispatcher(),
+                                                 metrics(),
+                                                 &mock_manager_));
   string eap_credential_properties[] = {
+      flimflam::kEapAnonymousIdentityProperty,
       flimflam::kEAPCertIDProperty,
       flimflam::kEAPClientCertProperty,
-      flimflam::kEAPKeyIDProperty,
-      flimflam::kEAPPINProperty,
-      flimflam::kEapCaCertIDProperty,
       flimflam::kEapIdentityProperty,
+      flimflam::kEAPKeyIDProperty,
       flimflam::kEapPasswordProperty,
-      flimflam::kEapPrivateKeyProperty
+      flimflam::kEAPPINProperty,
+      flimflam::kEapPrivateKeyProperty,
+      flimflam::kEapPrivateKeyPasswordProperty
   };
   string eap_non_credential_properties[] = {
+      flimflam::kEapCaCertIDProperty,
+      flimflam::kEapCaCertNssProperty,
       flimflam::kEAPEAPProperty,
       flimflam::kEapPhase2AuthProperty,
-      flimflam::kEapAnonymousIdentityProperty,
-      flimflam::kEapPrivateKeyPasswordProperty,
-      flimflam::kEapCaCertNssProperty,
       flimflam::kEapUseSystemCAsProperty
   };
   // While this is not an 802.1x-based service, none of these property
   // changes should cause a call to set_eap().
-  EXPECT_CALL(*service, set_eap(_)).Times(0);
+  EXPECT_CALL(*service, OnEapCredentialsChanged()).Times(0);
   for (size_t i = 0; i < arraysize(eap_credential_properties); ++i)
     service->OnPropertyChanged(eap_credential_properties[i]);
   for (size_t i = 0; i < arraysize(eap_non_credential_properties); ++i)
@@ -1112,7 +1156,7 @@
   // When this is an 802.1x-based service, set_eap should be called for
   // all credential-carrying properties.
   for (size_t i = 0; i < arraysize(eap_credential_properties); ++i) {
-    EXPECT_CALL(*service, set_eap(_)).Times(1);
+    EXPECT_CALL(*service, OnEapCredentialsChanged()).Times(1);
     service->OnPropertyChanged(eap_credential_properties[i]);
     Mock::VerifyAndClearExpectations(service.get());
   }
@@ -1120,17 +1164,17 @@
   // The key management property is a special case.  While not strictly
   // a credential, it can change which credentials are used.  Therefore it
   // should also trigger a call to set_eap();
-  EXPECT_CALL(*service, set_eap(_)).Times(1);
+  EXPECT_CALL(*service, OnEapCredentialsChanged()).Times(1);
   service->OnPropertyChanged(flimflam::kEapKeyMgmtProperty);
   Mock::VerifyAndClearExpectations(service.get());
 
-  EXPECT_CALL(*service, set_eap(_)).Times(0);
+  EXPECT_CALL(*service, OnEapCredentialsChanged()).Times(0);
   for (size_t i = 0; i < arraysize(eap_non_credential_properties); ++i)
     service->OnPropertyChanged(eap_non_credential_properties[i]);
 }
 
 TEST_F(ServiceTest, Certification) {
-  EXPECT_FALSE(service_->eap_.remote_certification.size());
+  EXPECT_FALSE(service_->remote_certification_.size());
 
   ScopedMockLog log;
   EXPECT_CALL(log, Log(logging::LOG_WARNING, _,
@@ -1140,7 +1184,7 @@
       kSubject, Service::kEAPMaxCertificationElements));
   EXPECT_FALSE(service_->AddEAPCertification(
       kSubject, Service::kEAPMaxCertificationElements + 1));
-  EXPECT_FALSE(service_->eap_.remote_certification.size());
+  EXPECT_FALSE(service_->remote_certification_.size());
   Mock::VerifyAndClearExpectations(&log);
 
   EXPECT_CALL(log,
@@ -1150,11 +1194,11 @@
       kSubject, Service::kEAPMaxCertificationElements - 1));
   Mock::VerifyAndClearExpectations(&log);
   EXPECT_EQ(Service::kEAPMaxCertificationElements,
-      service_->eap_.remote_certification.size());
+      service_->remote_certification_.size());
   for (size_t i = 0; i < Service::kEAPMaxCertificationElements - 1; ++i) {
-      EXPECT_TRUE(service_->eap_.remote_certification[i].empty());
+      EXPECT_TRUE(service_->remote_certification_[i].empty());
   }
-  EXPECT_EQ(kSubject, service_->eap_.remote_certification[
+  EXPECT_EQ(kSubject, service_->remote_certification_[
       Service::kEAPMaxCertificationElements - 1]);
 
   // Re-adding the same name in the same position should not generate a log.
@@ -1168,6 +1212,9 @@
       .Times(1);
   EXPECT_TRUE(service_->AddEAPCertification(
       kSubject + "x", Service::kEAPMaxCertificationElements - 1));
+
+  service_->ClearEAPCertification();
+  EXPECT_TRUE(service_->remote_certification_.empty());
 }
 
 TEST_F(ServiceTest, NoteDisconnectEventIdle) {
diff --git a/wifi_service.cc b/wifi_service.cc
index 3e5ed3f..22196a4 100644
--- a/wifi_service.cc
+++ b/wifi_service.cc
@@ -19,6 +19,7 @@
 #include "shill/certificate_file.h"
 #include "shill/control_interface.h"
 #include "shill/device.h"
+#include "shill/eap_credentials.h"
 #include "shill/error.h"
 #include "shill/event_dispatcher.h"
 #include "shill/ieee80211.h"
@@ -105,6 +106,8 @@
   }
   set_friendly_name(ssid_string);
 
+  SetEapCredentials(new EapCredentials());
+
   // TODO(quiche): determine if it is okay to set EAP.KeyManagement for
   // a service that is not 802.1x.
   if (Is8021x()) {
@@ -366,21 +369,7 @@
       Metrics::kMetricNetworkSecurityMax);
 
   if (Is8021x()) {
-    Metrics::EapOuterProtocol outer_protocol =
-        Metrics::EapOuterProtocolStringToEnum(eap().eap);
-    metrics()->SendEnumToUMA(
-        metrics()->GetFullMetricName(Metrics::kMetricNetworkEapOuterProtocol,
-                                     technology()),
-        outer_protocol,
-        Metrics::kMetricNetworkEapOuterProtocolMax);
-
-    Metrics::EapInnerProtocol inner_protocol =
-        Metrics::EapInnerProtocolStringToEnum(eap().inner_eap);
-    metrics()->SendEnumToUMA(
-        metrics()->GetFullMetricName(Metrics::kMetricNetworkEapInnerProtocol,
-                                     technology()),
-        inner_protocol,
-        Metrics::kMetricNetworkEapInnerProtocolMax);
+    eap()->OutputConnectionMetrics(metrics(), technology());
   }
 
   // We invert the sign of the signal strength value, since UMA histograms
@@ -492,8 +481,8 @@
     if (GetEAPKeyManagement().empty())
       SetEAPKeyManagement("WPA-EAP");
     vector<char> nss_identifier(ssid_.begin(), ssid_.end());
-    WPASupplicant::Populate8021xProperties(
-        eap(), certificate_file_.get(), nss_, nss_identifier, &params);
+    eap()->PopulateSupplicantProperties(
+        certificate_file_.get(), nss_, nss_identifier, &params);
     ClearEAPCertification();
   } else if (security_ == flimflam::kSecurityPsk ||
              security_ == flimflam::kSecurityRsn ||
@@ -988,14 +977,7 @@
   }
 }
 
-void WiFiService::set_eap(const EapCredentials &new_eap) {
-  EapCredentials modified_eap = new_eap;
-
-  // An empty key_management field is invalid.  Prevent it, if possible.
-  if (modified_eap.key_management.empty()) {
-    modified_eap.key_management = eap().key_management;
-  }
-  Service::set_eap(modified_eap);
+void WiFiService::OnEapCredentialsChanged() {
   ClearCachedCredentials();
   UpdateConnectable();
 }
diff --git a/wifi_service.h b/wifi_service.h
index 9f62608..bf30b13 100644
--- a/wifi_service.h
+++ b/wifi_service.h
@@ -114,7 +114,7 @@
 
   // Override from parent Service class to correctly update connectability
   // when the EAP credentials change for 802.1x networks.
-  void set_eap(const EapCredentials &eap);
+  void OnEapCredentialsChanged();
 
   // Override from parent Service class to register hidden services once they
   // have been configured.
diff --git a/wifi_service_unittest.cc b/wifi_service_unittest.cc
index 1d02151..bd89b81 100644
--- a/wifi_service_unittest.cc
+++ b/wifi_service_unittest.cc
@@ -22,6 +22,7 @@
 #include "shill/mock_adaptors.h"
 #include "shill/mock_certificate_file.h"
 #include "shill/mock_control.h"
+#include "shill/mock_eap_credentials.h"
 #include "shill/mock_log.h"
 #include "shill/mock_nss.h"
 #include "shill/mock_profile.h"
@@ -47,6 +48,7 @@
 using ::testing::Mock;
 using ::testing::NiceMock;
 using ::testing::Return;
+using ::testing::ReturnRef;
 using ::testing::SetArgumentPointee;
 using ::testing::StrEq;
 using ::testing::StrNe;
@@ -72,15 +74,27 @@
  protected:
   static const char fake_mac[];
 
+  MockEapCredentials *SetMockEap(
+      const WiFiServiceRefPtr &service) {
+    MockEapCredentials *eap = new MockEapCredentials();
+    service->eap_.reset(eap);  // Passes ownership.
+    return eap;
+  }
   bool CheckConnectable(const std::string &security, const char *passphrase,
-                        EapCredentials *eap) {
+                        bool is_1x_connectable) {
     Error error;
     WiFiServiceRefPtr service = MakeSimpleService(security);
     if (passphrase)
       service->SetPassphrase(passphrase, &error);
-    if (eap) {
-      service->set_eap(*eap);
+    MockEapCredentials *eap = SetMockEap(service);
+    EXPECT_CALL(*eap, IsConnectable())
+        .WillRepeatedly(Return(is_1x_connectable));
+    const string kKeyManagement8021x(WPASupplicant::kKeyManagementIeee8021X);
+    if (security == flimflam::kSecurityWep && is_1x_connectable) {
+      EXPECT_CALL(*eap, key_management())
+          .WillRepeatedly(ReturnRef(kKeyManagement8021x));
     }
+    service->OnEapCredentialsChanged();
     return service->connectable();
   }
   WiFiEndpoint *MakeEndpoint(const string &ssid, const string &bssid,
@@ -468,14 +482,25 @@
 
 TEST_F(WiFiServiceTest, ConnectTask8021x) {
   WiFiServiceRefPtr service = MakeServiceWithWiFi(flimflam::kSecurity8021x);
-  EapCredentials eap;
-  eap.identity = "identity";
-  eap.password = "mumble";
-  service->set_eap(eap);
+  service->mutable_eap()->set_identity("identity");
+  service->mutable_eap()->set_password("mumble");
+  service->OnEapCredentialsChanged();
   EXPECT_CALL(*wifi(), ConnectTo(service.get(), EAPSecurityArgs()));
   service->Connect(NULL, "in test");
 }
 
+TEST_F(WiFiServiceTest, ConnectTask8021xWithMockEap) {
+  WiFiServiceRefPtr service = MakeServiceWithWiFi(flimflam::kSecurity8021x);
+  MockEapCredentials *eap = SetMockEap(service);
+  EXPECT_CALL(*eap, IsConnectable()).WillOnce(Return(true));
+  service->OnEapCredentialsChanged();
+  EXPECT_CALL(*eap, PopulateSupplicantProperties(_, _, _, _));
+  // The mocked function does not actually set EAP parameters so we cannot
+  // expect them to be set.
+  EXPECT_CALL(*wifi(), ConnectTo(service.get(), _));
+  service->Connect(NULL, "in test");
+}
+
 TEST_F(WiFiServiceTest, ConnectTaskAdHocFrequency) {
   vector<uint8_t> ssid(1, 'a');
   WiFiEndpointRefPtr endpoint_nofreq =
@@ -619,11 +644,10 @@
 TEST_F(WiFiServiceTest, ConnectTaskDynamicWEP) {
   WiFiServiceRefPtr wifi_service = MakeServiceWithWiFi(flimflam::kSecurityWep);
 
-  EapCredentials eap;
-  eap.key_management = "IEEE8021X";
-  eap.identity = "something";
-  eap.password = "mumble";
-  wifi_service->set_eap(eap);
+  wifi_service->mutable_eap()->SetKeyManagement("IEEE8021X", NULL);
+  wifi_service->mutable_eap()->set_identity("something");
+  wifi_service->mutable_eap()->set_password("mumble");
+  wifi_service->OnEapCredentialsChanged();
   EXPECT_CALL(*wifi(),
               ConnectTo(wifi_service.get(), DynamicWEPArgs()));
   wifi_service->Connect(NULL, "in test");
@@ -684,7 +708,7 @@
     // Any change to EAP parameters (including a null one) will trigger cache
     // removal.  This is a lot less granular than the passphrase checks above.
     EXPECT_CALL(*wifi(), ClearCachedCredentials(wifi_service.get()));
-    wifi_service->set_eap(EapCredentials());
+    wifi_service->OnEapCredentialsChanged();
     Mock::VerifyAndClearExpectations(wifi());
   }
 }
@@ -1068,52 +1092,33 @@
 
 TEST_F(WiFiServiceTest, Connectable) {
   // Open network should be connectable.
-  EXPECT_TRUE(CheckConnectable(flimflam::kSecurityNone, NULL, NULL));
+  EXPECT_TRUE(CheckConnectable(flimflam::kSecurityNone, NULL, false));
 
   // Open network should remain connectable if we try to set a password on it.
-  EXPECT_TRUE(CheckConnectable(flimflam::kSecurityNone, "abcde", NULL));
+  EXPECT_TRUE(CheckConnectable(flimflam::kSecurityNone, "abcde", false));
 
   // WEP network with passphrase set should be connectable.
-  EXPECT_TRUE(CheckConnectable(flimflam::kSecurityWep, "abcde", NULL));
+  EXPECT_TRUE(CheckConnectable(flimflam::kSecurityWep, "abcde", false));
 
   // WEP network without passphrase set should NOT be connectable.
-  EXPECT_FALSE(CheckConnectable(flimflam::kSecurityWep, NULL, NULL));
+  EXPECT_FALSE(CheckConnectable(flimflam::kSecurityWep, NULL, false));
 
   // A bad passphrase should not make a WEP network connectable.
-  EXPECT_FALSE(CheckConnectable(flimflam::kSecurityWep, "a", NULL));
+  EXPECT_FALSE(CheckConnectable(flimflam::kSecurityWep, "a", false));
 
   // Similar to WEP, for WPA.
-  EXPECT_TRUE(CheckConnectable(flimflam::kSecurityWpa, "abcdefgh", NULL));
-  EXPECT_FALSE(CheckConnectable(flimflam::kSecurityWpa, NULL, NULL));
-  EXPECT_FALSE(CheckConnectable(flimflam::kSecurityWpa, "a", NULL));
+  EXPECT_TRUE(CheckConnectable(flimflam::kSecurityWpa, "abcdefgh", false));
+  EXPECT_FALSE(CheckConnectable(flimflam::kSecurityWpa, NULL, false));
+  EXPECT_FALSE(CheckConnectable(flimflam::kSecurityWpa, "a", false));
 
-  // Unconfigured 802.1x should NOT be connectable.
-  EXPECT_FALSE(CheckConnectable(flimflam::kSecurity8021x, NULL, NULL));
+  // 802.1x without connectable EAP credentials should NOT be connectable.
+  EXPECT_FALSE(CheckConnectable(flimflam::kSecurity8021x, NULL, false));
 
-  EapCredentials eap;
-  // Empty EAP credentials should not make a 802.1x network connectable.
-  EXPECT_FALSE(CheckConnectable(flimflam::kSecurity8021x, NULL, &eap));
+  // 802.1x with connectable EAP credentials should be connectable.
+  EXPECT_TRUE(CheckConnectable(flimflam::kSecurity8021x, NULL, true));
 
-  eap.identity = "something";
-  // If client certificate is being used, a private key must exist.
-  eap.client_cert = "some client cert";
-  EXPECT_FALSE(CheckConnectable(flimflam::kSecurity8021x, NULL, &eap));
-  eap.private_key = "some private key";
-  EXPECT_TRUE(CheckConnectable(flimflam::kSecurity8021x, NULL, &eap));
-
-  // Identity is always required.
-  eap.identity.clear();
-  EXPECT_FALSE(CheckConnectable(flimflam::kSecurity8021x, NULL, &eap));
-
-  eap.identity = "something";
-  // For non EAP-TLS types, a password is required.
-  eap.eap = "Non-TLS";
-  EXPECT_FALSE(CheckConnectable(flimflam::kSecurity8021x, NULL, &eap));
-  eap.password = "some password";
-  EXPECT_TRUE(CheckConnectable(flimflam::kSecurity8021x, NULL, &eap));
   // Dynamic WEP + 802.1X should be connectable under the same conditions.
-  eap.key_management = "IEEE8021X";
-  EXPECT_TRUE(CheckConnectable(flimflam::kSecurityWep, NULL, &eap));
+  EXPECT_TRUE(CheckConnectable(flimflam::kSecurityWep, NULL, true));
 }
 
 TEST_F(WiFiServiceTest, IsAutoConnectable) {
diff --git a/wimax_provider_unittest.cc b/wimax_provider_unittest.cc
index 93997eb..01c768b 100644
--- a/wimax_provider_unittest.cc
+++ b/wimax_provider_unittest.cc
@@ -10,6 +10,7 @@
 #include <chromeos/dbus/service_constants.h>
 #include <gtest/gtest.h>
 
+#include "shill/eap_credentials.h"
 #include "shill/glib.h"
 #include "shill/key_file_store.h"
 #include "shill/mock_dbus_manager.h"
@@ -515,12 +516,12 @@
 
   // GetService should create a service with only identifying parameters set.
   EXPECT_EQ(kName, GetServiceFriendlyName(service));
-  EXPECT_EQ("", service->eap().identity);
+  EXPECT_EQ("", service->eap()->identity());
 
   // After configuring the service, other parameters should be set.
   service->Configure(args, &e);
   EXPECT_TRUE(e.IsSuccess());
-  EXPECT_EQ(kIdentity, service->eap().identity);
+  EXPECT_EQ(kIdentity, service->eap()->identity());
 }
 
 TEST_F(WiMaxProviderTest, SelectCarrier) {
diff --git a/wimax_service.cc b/wimax_service.cc
index f59c336..4b75461 100644
--- a/wimax_service.cc
+++ b/wimax_service.cc
@@ -11,6 +11,7 @@
 #include <base/stringprintf.h>
 #include <chromeos/dbus/service_constants.h>
 
+#include "shill/eap_credentials.h"
 #include "shill/key_value_store.h"
 #include "shill/logging.h"
 #include "shill/manager.h"
@@ -40,6 +41,8 @@
   store->RegisterBool(flimflam::kPassphraseRequiredProperty, &need_passphrase_);
   store->RegisterConstString(kNetworkIdProperty, &network_id_);
 
+  SetEapCredentials(new EapCredentials());
+
   IgnoreParameterForConfigure(kNetworkIdProperty);
 
   // Initialize a default storage identifier based on the service's unique
@@ -52,16 +55,7 @@
 
 void WiMaxService::GetConnectParameters(KeyValueStore *parameters) const {
   CHECK(parameters);
-  if (!eap().anonymous_identity.empty()) {
-    parameters->SetString(wimax_manager::kEAPAnonymousIdentity,
-                          eap().anonymous_identity);
-  }
-  if (!eap().identity.empty()) {
-    parameters->SetString(wimax_manager::kEAPUserIdentity, eap().identity);
-  }
-  if (!eap().password.empty()) {
-    parameters->SetString(wimax_manager::kEAPUserPassword, eap().password);
-  }
+  eap()->PopulateWiMaxProperties(parameters);
 }
 
 RpcIdentifier WiMaxService::GetNetworkObjectPath() const {
@@ -204,9 +198,8 @@
   return true;
 }
 
-void WiMaxService::set_eap(const EapCredentials &eap) {
-  Service::set_eap(eap);
-  need_passphrase_ = eap.identity.empty() || eap.password.empty();
+void WiMaxService::OnEapCredentialsChanged() {
+  need_passphrase_ = !eap()->IsConnectableUsingPassphrase();
   UpdateConnectable();
 }
 
@@ -226,6 +219,7 @@
   }
   const string id = GetStorageIdentifier();
   storage->SetString(id, kStorageNetworkId, network_id_);
+
   return true;
 }
 
@@ -267,10 +261,8 @@
 }
 
 void WiMaxService::ClearPassphrase() {
-  EapCredentials creds = eap();
-  creds.password.clear();
-  // Updates the service credentials and connectability status.
-  set_eap(creds);
+  mutable_eap()->set_password("");
+  OnEapCredentialsChanged();
 }
 
 }  // namespace shill
diff --git a/wimax_service.h b/wimax_service.h
index 7bf6612..a63314d 100644
--- a/wimax_service.h
+++ b/wimax_service.h
@@ -70,7 +70,7 @@
   virtual void Disconnect(Error *error);
   virtual std::string GetStorageIdentifier() const;
   virtual bool Is8021x() const;
-  virtual void set_eap(const EapCredentials &eap);
+  virtual void OnEapCredentialsChanged();
   virtual bool Save(StoreInterface *storage);
   virtual bool Unload();
   virtual void SetState(ConnectState state);
@@ -80,7 +80,7 @@
   FRIEND_TEST(WiMaxServiceTest, GetDeviceRpcId);
   FRIEND_TEST(WiMaxServiceTest, IsAutoConnectable);
   FRIEND_TEST(WiMaxServiceTest, OnSignalStrengthChanged);
-  FRIEND_TEST(WiMaxServiceTest, SetEAP);
+  FRIEND_TEST(WiMaxServiceTest, Connectable);
   FRIEND_TEST(WiMaxServiceTest, SetState);
   FRIEND_TEST(WiMaxServiceTest, StartStop);
 
diff --git a/wimax_service_unittest.cc b/wimax_service_unittest.cc
index 81e2c8b..76ce419 100644
--- a/wimax_service_unittest.cc
+++ b/wimax_service_unittest.cc
@@ -11,6 +11,7 @@
 #include "shill/error.h"
 #include "shill/nice_mock_control.h"
 #include "shill/mock_adaptors.h"
+#include "shill/mock_eap_credentials.h"
 #include "shill/mock_manager.h"
 #include "shill/mock_metrics.h"
 #include "shill/mock_store.h"
@@ -20,6 +21,7 @@
 
 using std::string;
 using testing::_;
+using testing::Mock;
 using testing::NiceMock;
 using testing::Return;
 using wimax_manager::kEAPAnonymousIdentity;
@@ -48,10 +50,12 @@
         device_(new MockWiMax(&control_, NULL, &metrics_, &manager_,
                               kTestLinkName, kTestAddress, kTestInterfaceIndex,
                               kTestPath)),
-        service_(new WiMaxService(&control_, NULL, &metrics_, &manager_)) {
+        service_(new WiMaxService(&control_, NULL, &metrics_, &manager_)),
+        eap_(new MockEapCredentials()) {
     service_->set_friendly_name(kTestName);
     service_->set_network_id(kTestNetworkId);
     service_->InitStorageIdentifier();
+    service_->eap_.reset(eap_);  // Passes ownership.
   }
 
   virtual ~WiMaxServiceTest() {}
@@ -76,32 +80,13 @@
   NiceMock<MockMetrics> metrics_;
   scoped_refptr<MockWiMax> device_;
   WiMaxServiceRefPtr service_;
+  MockEapCredentials *eap_;  // Owned by |service_|.
 };
 
 TEST_F(WiMaxServiceTest, GetConnectParameters) {
-  {
-    KeyValueStore parameters;
-    service_->GetConnectParameters(&parameters);
-
-    EXPECT_FALSE(parameters.ContainsString(kEAPAnonymousIdentity));
-    EXPECT_FALSE(parameters.ContainsString(kEAPUserIdentity));
-    EXPECT_FALSE(parameters.ContainsString(kEAPUserPassword));
-  }
-  {
-    EapCredentials eap;
-    eap.anonymous_identity = "TestAnonymousIdentity";
-    eap.identity = "TestUserIdentity";
-    eap.password = "TestPassword";
-    service_->set_eap(eap);
-
-    KeyValueStore parameters;
-    service_->GetConnectParameters(&parameters);
-
-    EXPECT_EQ(eap.anonymous_identity,
-              parameters.LookupString(kEAPAnonymousIdentity, ""));
-    EXPECT_EQ(eap.identity, parameters.LookupString(kEAPUserIdentity, ""));
-    EXPECT_EQ(eap.password, parameters.LookupString(kEAPUserPassword, ""));
-  }
+  KeyValueStore parameters;
+  EXPECT_CALL(*eap_, PopulateWiMaxProperties(&parameters));
+  service_->GetConnectParameters(&parameters);
 }
 
 TEST_F(WiMaxServiceTest, GetDeviceRpcId) {
@@ -152,38 +137,41 @@
   EXPECT_FALSE(service_->proxy_.get());
 }
 
-TEST_F(WiMaxServiceTest, SetEAP) {
-  ServiceRefPtr base_service = service_;
-  EXPECT_TRUE(base_service->Is8021x());
+TEST_F(WiMaxServiceTest, Connectable) {
+  EXPECT_TRUE(service_->Is8021x());
   EXPECT_TRUE(service_->need_passphrase_);
-  EXPECT_FALSE(base_service->connectable());
+  EXPECT_FALSE(service_->connectable());
 
-  // No password.
-  EapCredentials eap;
-  eap.identity = "TestIdentity";
-  base_service->set_eap(eap);
+  EXPECT_CALL(*eap_, IsConnectableUsingPassphrase())
+      .WillOnce(Return(false))
+      .WillRepeatedly(Return(true));
+
+  // No WiMaxCredentials.
+  service_->OnEapCredentialsChanged();
   EXPECT_TRUE(service_->need_passphrase_);
-  EXPECT_FALSE(base_service->connectable());
+  EXPECT_FALSE(service_->connectable());
 
-  // Not started.
-  eap.password = "TestPassword";
-  base_service->set_eap(eap);
+  // Not started (no proxy).
+  service_->OnEapCredentialsChanged();
   EXPECT_FALSE(service_->need_passphrase_);
-  EXPECT_FALSE(base_service->connectable());
+  EXPECT_FALSE(service_->connectable());
 
   // Connectable.
   service_->proxy_.reset(proxy_.release());
   ExpectUpdateService();
-  base_service->set_eap(eap);
+  service_->OnEapCredentialsChanged();
   EXPECT_FALSE(service_->need_passphrase_);
-  EXPECT_TRUE(base_service->connectable());
+  EXPECT_TRUE(service_->connectable());
 
-  // Reset password.
+  // Reset WimaxConnectable state.
+  Mock::VerifyAndClearExpectations(eap_);
+  EXPECT_CALL(*eap_, set_password(""));
+  EXPECT_CALL(*eap_, IsConnectableUsingPassphrase())
+      .WillRepeatedly(Return(false));
   ExpectUpdateService();
   service_->ClearPassphrase();
   EXPECT_TRUE(service_->need_passphrase_);
-  EXPECT_FALSE(base_service->connectable());
-  EXPECT_TRUE(base_service->eap().password.empty());
+  EXPECT_FALSE(service_->connectable());
 }
 
 TEST_F(WiMaxServiceTest, ConvertIdentifierToNetworkId) {
@@ -258,31 +246,33 @@
   EXPECT_CALL(manager_, wimax_provider())
       .Times(2)
       .WillRepeatedly(Return(&provider));
-  EapCredentials eap;
-  eap.identity = "TestUserIdentity";
-  service_->set_eap(eap);
+  EXPECT_CALL(*eap_, Reset());
+  EXPECT_CALL(*eap_, set_password(""));
+  EXPECT_CALL(*eap_, IsConnectableUsingPassphrase())
+      .WillRepeatedly(Return(false));
   EXPECT_CALL(provider, OnServiceUnloaded(_)).WillOnce(Return(false));
   EXPECT_FALSE(service_->Unload());
-  EXPECT_TRUE(service_->eap().identity.empty());
-  eap.identity = "TestUserIdentity";
-  service_->set_eap(eap);
+  Mock::VerifyAndClearExpectations(eap_);
+
+  EXPECT_CALL(*eap_, Reset());
+  EXPECT_CALL(*eap_, set_password(""));
+  EXPECT_CALL(*eap_, IsConnectableUsingPassphrase())
+      .WillRepeatedly(Return(false));
   EXPECT_CALL(provider, OnServiceUnloaded(_)).WillOnce(Return(true));
   EXPECT_TRUE(service_->Unload());
-  EXPECT_TRUE(service_->eap().identity.empty());
 }
 
 TEST_F(WiMaxServiceTest, SetState) {
   service_->device_ = device_;
-  ServiceRefPtr base_service = service_;
   EXPECT_EQ(Service::kStateIdle, service_->state());
 
   EXPECT_CALL(manager_, UpdateService(_));
-  base_service->SetState(Service::kStateAssociating);
+  service_->SetState(Service::kStateAssociating);
   EXPECT_EQ(Service::kStateAssociating, service_->state());
   EXPECT_TRUE(service_->device_);
 
   EXPECT_CALL(manager_, UpdateService(_));
-  base_service->SetState(Service::kStateFailure);
+  service_->SetState(Service::kStateFailure);
   EXPECT_EQ(Service::kStateFailure, service_->state());
   EXPECT_FALSE(service_->device_);
 }
diff --git a/wpa_supplicant.cc b/wpa_supplicant.cc
index 4f0edf2..3be81f1 100644
--- a/wpa_supplicant.cc
+++ b/wpa_supplicant.cc
@@ -144,88 +144,6 @@
     SHIMDIR "/wpa_supplicant.conf";
 
 // static
-void WPASupplicant::Populate8021xProperties(
-    const EapCredentials &eap, CertificateFile *certificate_file,
-    NSS *nss, const vector<char> nss_identifier,
-    map<string, DBus::Variant> *params) {
-  string ca_cert = eap.ca_cert;
-  if (!eap.ca_cert_pem.empty()) {
-    FilePath certfile =
-        certificate_file->CreateDERFromString(eap.ca_cert_pem);
-    if (certfile.empty()) {
-      LOG(ERROR) << "Unable to extract PEM certificate.";
-    } else {
-      ca_cert = certfile.value();
-    }
-  } else if (!eap.ca_cert_nss.empty()) {
-    FilePath certfile = nss->GetDERCertfile(eap.ca_cert_nss, nss_identifier);
-    if (certfile.empty()) {
-      LOG(ERROR) << "Unable to extract DER certificate: " << eap.ca_cert_nss;
-    } else {
-      ca_cert = certfile.value();
-    }
-  }
-
-
-  typedef std::pair<const char *, const char *> KeyVal;
-  KeyVal init_propertyvals[] = {
-    KeyVal(WPASupplicant::kNetworkPropertyEapIdentity, eap.identity.c_str()),
-    KeyVal(WPASupplicant::kNetworkPropertyEapEap, eap.eap.c_str()),
-    KeyVal(WPASupplicant::kNetworkPropertyEapInnerEap,
-           eap.inner_eap.c_str()),
-    KeyVal(WPASupplicant::kNetworkPropertyEapAnonymousIdentity,
-           eap.anonymous_identity.c_str()),
-    KeyVal(WPASupplicant::kNetworkPropertyEapClientCert,
-           eap.client_cert.c_str()),
-    KeyVal(WPASupplicant::kNetworkPropertyEapPrivateKey,
-           eap.private_key.c_str()),
-    KeyVal(WPASupplicant::kNetworkPropertyEapPrivateKeyPassword,
-           eap.private_key_password.c_str()),
-    KeyVal(WPASupplicant::kNetworkPropertyEapCaCert, ca_cert.c_str()),
-    KeyVal(WPASupplicant::kNetworkPropertyEapCaPassword,
-           eap.password.c_str()),
-    KeyVal(WPASupplicant::kNetworkPropertyEapCertId, eap.cert_id.c_str()),
-    KeyVal(WPASupplicant::kNetworkPropertyEapKeyId, eap.key_id.c_str()),
-    KeyVal(WPASupplicant::kNetworkPropertyEapCaCertId,
-           eap.ca_cert_id.c_str()),
-    KeyVal(WPASupplicant::kNetworkPropertyEapSubjectMatch,
-           eap.subject_match.c_str())
-  };
-
-  vector<KeyVal> propertyvals(init_propertyvals,
-                              init_propertyvals + arraysize(init_propertyvals));
-  if (eap.use_system_cas) {
-    propertyvals.push_back(KeyVal(
-        WPASupplicant::kNetworkPropertyCaPath, WPASupplicant::kCaPath));
-  } else if (ca_cert.empty()) {
-    LOG(WARNING) << __func__
-                 << ": No certificate authorities are configured."
-                 << " Server certificates will be accepted"
-                 << " unconditionally.";
-  }
-
-  if (!eap.cert_id.empty() || !eap.key_id.empty() ||
-      !eap.ca_cert_id.empty()) {
-    propertyvals.push_back(KeyVal(
-        WPASupplicant::kNetworkPropertyEapPin, eap.pin.c_str()));
-    propertyvals.push_back(KeyVal(
-        WPASupplicant::kNetworkPropertyEngineId,
-        WPASupplicant::kEnginePKCS11));
-    // We can't use the propertyvals vector for this since this argument
-    // is a uint32, not a string.
-    (*params)[WPASupplicant::kNetworkPropertyEngine].writer().
-        append_uint32(WPASupplicant::kDefaultEngine);
-  }
-
-  vector<KeyVal>::iterator it;
-  for (it = propertyvals.begin(); it != propertyvals.end(); ++it) {
-    if (strlen((*it).second) > 0) {
-      (*params)[(*it).first].writer().append_string((*it).second);
-    }
-  }
-}
-
-// static
 bool WPASupplicant::ExtractRemoteCertification(
       const std::map<std::string, DBus::Variant> &properties,
       std::string *subject, uint32 *depth) {
diff --git a/wpa_supplicant.h b/wpa_supplicant.h
index 78260e9..90728d0 100644
--- a/wpa_supplicant.h
+++ b/wpa_supplicant.h
@@ -14,10 +14,6 @@
 
 namespace shill {
 
-class CertificateFile;
-struct EapCredentials;
-class NSS;
-
 class WPASupplicant {
  public:
   static const char kBSSPropertyBSSID[];
@@ -137,14 +133,6 @@
 
   static const char kSupplicantConfPath[];
 
-  // Populate the wpa_supplicant DBus parameter map |params| with the
-  // credentials in |eap|.  To do so, this function may use |certificate_file|
-  // or |nss| to export CA certificates to be passed to wpa_supplicant.
-  static void Populate8021xProperties(
-      const EapCredentials &eap, CertificateFile *certificate_file,
-      NSS *nss, const std::vector<char> nss_identifier,
-      std::map<std::string, DBus::Variant> *params);
-
   // Retrieve the |subject| and |depth| of an a remote certifying entity,
   // as contained the the |properties| to a Certification event from
   // wpa_supplicant.  Returns true if an |subject| and |depth| were
diff --git a/wpa_supplicant_unittest.cc b/wpa_supplicant_unittest.cc
index de8b681..d0b0cb5 100644
--- a/wpa_supplicant_unittest.cc
+++ b/wpa_supplicant_unittest.cc
@@ -4,23 +4,14 @@
 
 #include "shill/wpa_supplicant.h"
 
-#include <base/file_path.h>
-#include <base/stl_util.h>
-#include <gmock/gmock.h>
 #include <gtest/gtest.h>
 
-#include "shill/eap_credentials.h"
-#include "shill/mock_certificate_file.h"
 #include "shill/mock_log.h"
-#include "shill/mock_nss.h"
 
-using base::FilePath;
 using std::map;
 using std::string;
-using std::vector;
 using testing::_;
 using testing::EndsWith;
-using testing::Return;
 
 namespace shill {
 
@@ -30,95 +21,18 @@
   virtual ~WPASupplicantTest() {}
 
  protected:
-  typedef std::map<std::string, DBus::Variant> PropertyMap;
+  typedef map<string, DBus::Variant> PropertyMap;
 
-  void Populate() {
-    WPASupplicant::Populate8021xProperties(eap_, &certificate_file_,
-                                           &nss_, nss_identifier_, &params_);
-  }
-
-  EapCredentials eap_;
-  MockCertificateFile certificate_file_;
-  MockNSS nss_;
-  vector<char> nss_identifier_;
-  map<string, ::DBus::Variant> params_;
+  PropertyMap property_map_;
 };
 
-TEST_F(WPASupplicantTest, Populate8021x) {
-  eap_.identity = "testidentity";
-  eap_.pin = "xxxx";
-  Populate();
-  // Test that only non-empty 802.1x properties are populated.
-  EXPECT_TRUE(ContainsKey(params_, WPASupplicant::kNetworkPropertyEapIdentity));
-  EXPECT_FALSE(ContainsKey(params_, WPASupplicant::kNetworkPropertyEapKeyId));
-  EXPECT_FALSE(ContainsKey(params_, WPASupplicant::kNetworkPropertyEapCaCert));
-
-  // Test that CA path is set by default.
-  EXPECT_TRUE(ContainsKey(params_, WPASupplicant::kNetworkPropertyCaPath));
-
-  // Test that hardware-backed security arguments are not set.
-  EXPECT_FALSE(ContainsKey(params_, WPASupplicant::kNetworkPropertyEapPin));
-  EXPECT_FALSE(ContainsKey(params_, WPASupplicant::kNetworkPropertyEngine));
-  EXPECT_FALSE(ContainsKey(params_, WPASupplicant::kNetworkPropertyEngineId));
-}
-
-TEST_F(WPASupplicantTest, Populate8021xNoSystemCAs) {
-  eap_.identity = "testidentity";
-  eap_.use_system_cas = false;
-  Populate();
-  // Test that CA path is not set if use_system_cas is explicitly false.
-  EXPECT_FALSE(ContainsKey(params_, WPASupplicant::kNetworkPropertyCaPath));
-}
-
-TEST_F(WPASupplicantTest, Populate8021xUsingHardwareAuth) {
-  eap_.identity = "testidentity";
-  eap_.key_id = "key_id";
-  eap_.pin = "xxxx";
-  Populate();
-  // Test that EAP engine parameters set if key_id is set.
-  EXPECT_TRUE(ContainsKey(params_, WPASupplicant::kNetworkPropertyEapPin));
-  EXPECT_TRUE(ContainsKey(params_, WPASupplicant::kNetworkPropertyEapKeyId));
-  EXPECT_TRUE(ContainsKey(params_, WPASupplicant::kNetworkPropertyEngine));
-  EXPECT_TRUE(ContainsKey(params_, WPASupplicant::kNetworkPropertyEngineId));
-}
-
-TEST_F(WPASupplicantTest, Populate8021xNSS) {
-  eap_.ca_cert_nss = "nss_nickname";
-  const string kNSSCertfile("/tmp/nss-cert");
-  FilePath nss_cert(kNSSCertfile);
-  nss_identifier_ = vector<char>(1, 'a');
-  EXPECT_CALL(nss_, GetDERCertfile(eap_.ca_cert_nss, nss_identifier_))
-      .WillOnce(Return(nss_cert));
-  Populate();
-  EXPECT_TRUE(ContainsKey(params_, WPASupplicant::kNetworkPropertyEapCaCert));
-  if (ContainsKey(params_, WPASupplicant::kNetworkPropertyEapCaCert)) {
-    EXPECT_EQ(kNSSCertfile, params_[WPASupplicant::kNetworkPropertyEapCaCert]
-              .reader().get_string());
-  }
-}
-
-TEST_F(WPASupplicantTest, Populate8021xPEM) {
-  eap_.ca_cert_pem = "-pem-certificate-here-";
-  const string kPEMCertfile("/tmp/pem-cert");
-  FilePath pem_cert(kPEMCertfile);
-  EXPECT_CALL(certificate_file_, CreateDERFromString(eap_.ca_cert_pem))
-      .WillOnce(Return(pem_cert));
-
-  Populate();
-  EXPECT_TRUE(ContainsKey(params_, WPASupplicant::kNetworkPropertyEapCaCert));
-  if (ContainsKey(params_, WPASupplicant::kNetworkPropertyEapCaCert)) {
-    EXPECT_EQ(kPEMCertfile, params_[WPASupplicant::kNetworkPropertyEapCaCert]
-              .reader().get_string());
-  }
-}
-
 TEST_F(WPASupplicantTest, ExtractRemoteCertificationEmpty) {
   string subject;
   uint32 depth = 0;
   ScopedMockLog log;
   EXPECT_CALL(log, Log(logging::LOG_ERROR, _, EndsWith("no depth parameter.")));
   EXPECT_FALSE(WPASupplicant::ExtractRemoteCertification(
-      PropertyMap(), &subject, &depth));
+      property_map_, &subject, &depth));
   EXPECT_EQ("", subject);
   EXPECT_EQ(0, depth);
 }
@@ -127,14 +41,13 @@
   string subject;
   const uint32 kDepthValue = 100;
   uint32 depth = kDepthValue - 1;
-  PropertyMap property_map;
-  property_map[WPASupplicant::kInterfacePropertyDepth]
+  property_map_[WPASupplicant::kInterfacePropertyDepth]
       .writer().append_uint32(kDepthValue);
   ScopedMockLog log;
   EXPECT_CALL(log,
               Log(logging::LOG_ERROR, _, EndsWith("no subject parameter.")));
   EXPECT_FALSE(WPASupplicant::ExtractRemoteCertification(
-      property_map, &subject, &depth));
+      property_map_, &subject, &depth));
   EXPECT_EQ("", subject);
   EXPECT_NE(kDepthValue, depth);
 }
@@ -143,13 +56,12 @@
   const char kSubjectName[] = "subject-name";
   string subject;
   uint32 depth = 0;
-  PropertyMap property_map;
-  property_map[WPASupplicant::kInterfacePropertySubject]
+  property_map_[WPASupplicant::kInterfacePropertySubject]
       .writer().append_string(kSubjectName);
   ScopedMockLog log;
   EXPECT_CALL(log, Log(logging::LOG_ERROR, _, EndsWith("no depth parameter.")));
   EXPECT_FALSE(WPASupplicant::ExtractRemoteCertification(
-      property_map, &subject, &depth));
+      property_map_, &subject, &depth));
   EXPECT_EQ("", subject);
   EXPECT_EQ(0, depth);
 }
@@ -159,13 +71,12 @@
   string subject;
   const uint32 kDepthValue = 100;
   uint32 depth = 0;
-  PropertyMap property_map;
-  property_map[WPASupplicant::kInterfacePropertySubject]
+  property_map_[WPASupplicant::kInterfacePropertySubject]
       .writer().append_string(kSubjectName);
-  property_map[WPASupplicant::kInterfacePropertyDepth]
+  property_map_[WPASupplicant::kInterfacePropertyDepth]
       .writer().append_uint32(kDepthValue);
   EXPECT_TRUE(WPASupplicant::ExtractRemoteCertification(
-      property_map, &subject, &depth));
+      property_map_, &subject, &depth));
   EXPECT_EQ(kSubjectName, subject);
   EXPECT_EQ(kDepthValue, depth);
 }