WifiConfigStore: Encrypt credentials for networks (3/4)

Encrypt/Decrypt preSharedKey & enterprise config's password fields.
When deserializing, handle migration from older config store
version file.

Any encryption failure are silently ignored. Decryption failures are
however non-recoverable.

Bug: 140485110
Test: atest com.android.server.wifi
Test: Manual verification
- Store a PSK network config on older build
- Upgrade to build with this CL
- Ensured that the psk was read correctly on upgrade
- Ensured that the psk was encrypted when stored on disk after upgrade
Change-Id: Ic7673cb375c9e5447ff4074ed78321152573e1c3
Merged-In: Ic7673cb375c9e5447ff4074ed78321152573e1c3
diff --git a/service/java/com/android/server/wifi/WifiConfigStore.java b/service/java/com/android/server/wifi/WifiConfigStore.java
index dee52a7..350e8b5 100644
--- a/service/java/com/android/server/wifi/WifiConfigStore.java
+++ b/service/java/com/android/server/wifi/WifiConfigStore.java
@@ -170,12 +170,6 @@
                 put(STORE_FILE_USER_GENERAL, STORE_FILE_NAME_USER_GENERAL);
                 put(STORE_FILE_USER_NETWORK_SUGGESTIONS, STORE_FILE_NAME_USER_NETWORK_SUGGESTIONS);
             }};
-
-    @VisibleForTesting
-    public static final EncryptedData ZEROED_ENCRYPTED_DATA =
-            new EncryptedData(
-                    new byte[EncryptedData.ENCRYPTED_DATA_LENGTH],
-                    new byte[EncryptedData.IV_LENGTH]);
     /**
      * Handler instance to post alarm timeouts to
      */
diff --git a/service/java/com/android/server/wifi/util/EncryptedData.java b/service/java/com/android/server/wifi/util/EncryptedData.java
index 91342d3..baec204 100644
--- a/service/java/com/android/server/wifi/util/EncryptedData.java
+++ b/service/java/com/android/server/wifi/util/EncryptedData.java
@@ -18,21 +18,19 @@
 
 import com.android.internal.util.Preconditions;
 
+import java.util.Arrays;
+import java.util.Objects;
+
 /**
- * A class to store data created by {@link DataIntegrityChecker}.
+ * A class to store data created by {@link WifiConfigStoreEncryptionUtil}.
  */
 public class EncryptedData {
-    public static final int ENCRYPTED_DATA_LENGTH = 48;
-    public static final int IV_LENGTH = 12;
-
     private final byte[] mEncryptedData;
     private final byte[] mIv;
 
     public EncryptedData(byte[] encryptedData, byte[] iv) {
-        Preconditions.checkNotNull(encryptedData, iv);
-        Preconditions.checkState(encryptedData.length == ENCRYPTED_DATA_LENGTH,
-                "encryptedData.length=" + encryptedData.length);
-        Preconditions.checkState(iv.length == IV_LENGTH, "iv.length=" + iv.length);
+        Preconditions.checkNotNull(encryptedData);
+        Preconditions.checkNotNull(iv);
         mEncryptedData = encryptedData;
         mIv = iv;
     }
@@ -44,4 +42,17 @@
     public byte[] getIv() {
         return mIv;
     }
+
+    @Override
+    public boolean equals(Object other) {
+        if (!(other instanceof EncryptedData)) return false;
+        EncryptedData otherEncryptedData = (EncryptedData) other;
+        return Arrays.equals(this.mEncryptedData, otherEncryptedData.mEncryptedData)
+                && Arrays.equals(this.mIv, otherEncryptedData.mIv);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(Arrays.hashCode(mEncryptedData), Arrays.hashCode(mIv));
+    }
 }
diff --git a/service/java/com/android/server/wifi/util/XmlUtil.java b/service/java/com/android/server/wifi/util/XmlUtil.java
index 292c792..6128b0b 100644
--- a/service/java/com/android/server/wifi/util/XmlUtil.java
+++ b/service/java/com/android/server/wifi/util/XmlUtil.java
@@ -30,6 +30,7 @@
 import android.net.wifi.WifiConfiguration;
 import android.net.wifi.WifiConfiguration.NetworkSelectionStatus;
 import android.net.wifi.WifiEnterpriseConfig;
+import android.text.TextUtils;
 import android.util.Log;
 import android.util.Pair;
 
@@ -379,12 +380,42 @@
         }
 
         /**
+         * Write preshared key to the XML stream.
+         *
+         * If encryptionUtil is null or if encryption fails for some reason, the pre-shared
+         * key is stored in plaintext, else the encrypted psk is stored.
+         */
+        private static void writePreSharedKeyToXml(
+                XmlSerializer out, String preSharedKey,
+                @Nullable WifiConfigStoreEncryptionUtil encryptionUtil)
+                throws XmlPullParserException, IOException {
+            EncryptedData encryptedData = null;
+            if (encryptionUtil != null) {
+                if (preSharedKey != null) {
+                    encryptedData = encryptionUtil.encrypt(preSharedKey.getBytes());
+                    if (encryptedData == null) {
+                        // We silently fail encryption failures!
+                        Log.wtf(TAG, "Encryption of preSharedKey failed");
+                    }
+                }
+            }
+            if (encryptedData != null) {
+                XmlUtil.writeNextSectionStart(out, XML_TAG_PRE_SHARED_KEY);
+                EncryptedDataXmlUtil.writeToXml(out, encryptedData);
+                XmlUtil.writeNextSectionEnd(out, XML_TAG_PRE_SHARED_KEY);
+            } else {
+                XmlUtil.writeNextValue(out, XML_TAG_PRE_SHARED_KEY, preSharedKey);
+            }
+        }
+
+        /**
          * Write the Configuration data elements that are common for backup & config store to the
          * XML stream.
          *
          * @param out XmlSerializer instance pointing to the XML stream.
          * @param configuration WifiConfiguration object to be serialized.
-         * @param encryptionUtil Instance of {@link EncryptedDataXmlUtil}.
+         * @param encryptionUtil Instance of {@link EncryptedDataXmlUtil}. Backup/restore stores
+         *                       keys unencrypted.
          */
         public static void writeCommonElementsToXml(
                 XmlSerializer out, WifiConfiguration configuration,
@@ -393,7 +424,7 @@
             XmlUtil.writeNextValue(out, XML_TAG_CONFIG_KEY, configuration.configKey());
             XmlUtil.writeNextValue(out, XML_TAG_SSID, configuration.SSID);
             XmlUtil.writeNextValue(out, XML_TAG_BSSID, configuration.BSSID);
-            XmlUtil.writeNextValue(out, XML_TAG_PRE_SHARED_KEY, configuration.preSharedKey);
+            writePreSharedKeyToXml(out, configuration.preSharedKey, encryptionUtil);
             writeWepKeysToXml(out, configuration.wepKeys);
             XmlUtil.writeNextValue(out, XML_TAG_WEP_TX_KEY_INDEX, configuration.wepTxKeyIndex);
             XmlUtil.writeNextValue(out, XML_TAG_HIDDEN_SSID, configuration.hiddenSSID);
@@ -517,13 +548,13 @@
          *
          * @param in XmlPullParser instance pointing to the XML stream.
          * @param outerTagDepth depth of the outer tag in the XML document.
-         * @param areCredentialsEncrypted Whether credentials are encrypted or not.
+         * @param shouldExpectEncryptedCredentials Whether to expect encrypted credentials or not.
          * @param encryptionUtil Instance of {@link EncryptedDataXmlUtil}.
          * @return Pair<Config key, WifiConfiguration object> if parsing is successful,
          * null otherwise.
          */
         public static Pair<String, WifiConfiguration> parseFromXml(
-                XmlPullParser in, int outerTagDepth, boolean areCredentialsEncrypted,
+                XmlPullParser in, int outerTagDepth, boolean shouldExpectEncryptedCredentials,
                 @NonNull WifiConfigStoreEncryptionUtil encryptionUtil)
                 throws XmlPullParserException, IOException {
             WifiConfiguration configuration = new WifiConfiguration();
@@ -532,147 +563,175 @@
 
             // Loop through and parse out all the elements from the stream within this section.
             while (!XmlUtil.isNextSectionEnd(in, outerTagDepth)) {
-                String[] valueName = new String[1];
-                Object value = XmlUtil.readCurrentValue(in, valueName);
-                if (valueName[0] == null) {
-                    throw new XmlPullParserException("Missing value name");
-                }
-                switch (valueName[0]) {
-                    case XML_TAG_CONFIG_KEY:
-                        configKeyInData = (String) value;
-                        break;
-                    case XML_TAG_SSID:
-                        configuration.SSID = (String) value;
-                        break;
-                    case XML_TAG_BSSID:
-                        configuration.BSSID = (String) value;
-                        break;
-                    case XML_TAG_PRE_SHARED_KEY:
-                        configuration.preSharedKey = (String) value;
-                        break;
-                    case XML_TAG_WEP_KEYS:
-                        populateWepKeysFromXmlValue(value, configuration.wepKeys);
-                        break;
-                    case XML_TAG_WEP_TX_KEY_INDEX:
-                        configuration.wepTxKeyIndex = (int) value;
-                        break;
-                    case XML_TAG_HIDDEN_SSID:
-                        configuration.hiddenSSID = (boolean) value;
-                        break;
-                    case XML_TAG_REQUIRE_PMF:
-                        configuration.requirePMF = (boolean) value;
-                        break;
-                    case XML_TAG_ALLOWED_KEY_MGMT:
-                        byte[] allowedKeyMgmt = (byte[]) value;
-                        configuration.allowedKeyManagement = BitSet.valueOf(allowedKeyMgmt);
-                        break;
-                    case XML_TAG_ALLOWED_PROTOCOLS:
-                        byte[] allowedProtocols = (byte[]) value;
-                        configuration.allowedProtocols = BitSet.valueOf(allowedProtocols);
-                        break;
-                    case XML_TAG_ALLOWED_AUTH_ALGOS:
-                        byte[] allowedAuthAlgorithms = (byte[]) value;
-                        configuration.allowedAuthAlgorithms = BitSet.valueOf(allowedAuthAlgorithms);
-                        break;
-                    case XML_TAG_ALLOWED_GROUP_CIPHERS:
-                        byte[] allowedGroupCiphers = (byte[]) value;
-                        configuration.allowedGroupCiphers = BitSet.valueOf(allowedGroupCiphers);
-                        break;
-                    case XML_TAG_ALLOWED_PAIRWISE_CIPHERS:
-                        byte[] allowedPairwiseCiphers = (byte[]) value;
-                        configuration.allowedPairwiseCiphers =
-                                BitSet.valueOf(allowedPairwiseCiphers);
-                        break;
-                    case XML_TAG_ALLOWED_GROUP_MGMT_CIPHERS:
-                        byte[] allowedGroupMgmtCiphers = (byte[]) value;
-                        configuration.allowedGroupManagementCiphers =
-                                BitSet.valueOf(allowedGroupMgmtCiphers);
-                        break;
-                    case XML_TAG_ALLOWED_SUITE_B_CIPHERS:
-                        byte[] allowedSuiteBCiphers = (byte[]) value;
-                        configuration.allowedSuiteBCiphers =
-                                BitSet.valueOf(allowedSuiteBCiphers);
-                        break;
-                    case XML_TAG_SHARED:
-                        configuration.shared = (boolean) value;
-                        break;
-                    case XML_TAG_STATUS:
-                        int status = (int) value;
-                        // Any network which was CURRENT before reboot needs
-                        // to be restored to ENABLED.
-                        if (status == WifiConfiguration.Status.CURRENT) {
-                            status = WifiConfiguration.Status.ENABLED;
-                        }
-                        configuration.status = status;
-                        break;
-                    case XML_TAG_FQDN:
-                        configuration.FQDN = (String) value;
-                        break;
-                    case XML_TAG_PROVIDER_FRIENDLY_NAME:
-                        configuration.providerFriendlyName = (String) value;
-                        break;
-                    case XML_TAG_LINKED_NETWORKS_LIST:
-                        configuration.linkedConfigurations = (HashMap<String, Integer>) value;
-                        break;
-                    case XML_TAG_DEFAULT_GW_MAC_ADDRESS:
-                        configuration.defaultGwMacAddress = (String) value;
-                        break;
-                    case XML_TAG_VALIDATED_INTERNET_ACCESS:
-                        configuration.validatedInternetAccess = (boolean) value;
-                        break;
-                    case XML_TAG_NO_INTERNET_ACCESS_EXPECTED:
-                        configuration.noInternetAccessExpected = (boolean) value;
-                        break;
-                    case XML_TAG_USER_APPROVED:
-                        configuration.userApproved = (int) value;
-                        break;
-                    case XML_TAG_METERED_HINT:
-                        configuration.meteredHint = (boolean) value;
-                        break;
-                    case XML_TAG_METERED_OVERRIDE:
-                        configuration.meteredOverride = (int) value;
-                        break;
-                    case XML_TAG_USE_EXTERNAL_SCORES:
-                        configuration.useExternalScores = (boolean) value;
-                        break;
-                    case XML_TAG_NUM_ASSOCIATION:
-                        configuration.numAssociation = (int) value;
-                        break;
-                    case XML_TAG_CREATOR_UID:
-                        configuration.creatorUid = (int) value;
-                        break;
-                    case XML_TAG_CREATOR_NAME:
-                        configuration.creatorName = (String) value;
-                        break;
-                    case XML_TAG_CREATION_TIME:
-                        configuration.creationTime = (String) value;
-                        break;
-                    case XML_TAG_LAST_UPDATE_UID:
-                        configuration.lastUpdateUid = (int) value;
-                        break;
-                    case XML_TAG_LAST_UPDATE_NAME:
-                        configuration.lastUpdateName = (String) value;
-                        break;
-                    case XML_TAG_LAST_CONNECT_UID:
-                        configuration.lastConnectUid = (int) value;
-                        break;
-                    case XML_TAG_IS_LEGACY_PASSPOINT_CONFIG:
-                        configuration.isLegacyPasspointConfig = (boolean) value;
-                        break;
-                    case XML_TAG_ROAMING_CONSORTIUM_OIS:
-                        configuration.roamingConsortiumIds = (long[]) value;
-                        break;
-                    case XML_TAG_RANDOMIZED_MAC_ADDRESS:
-                        configuration.setRandomizedMacAddress(
-                                MacAddress.fromString((String) value));
-                        break;
-                    case XML_TAG_MAC_RANDOMIZATION_SETTING:
-                        configuration.macRandomizationSetting = (int) value;
-                        macRandomizationSettingExists = true;
-                        break;
-                    default:
-                        throw new XmlPullParserException(
-                                "Unknown value name found: " + valueName[0]);
+                if (in.getAttributeValue(null, "name") != null) {
+                    // Value elements.
+                    String[] valueName = new String[1];
+                    Object value = XmlUtil.readCurrentValue(in, valueName);
+                    if (valueName[0] == null) {
+                        throw new XmlPullParserException("Missing value name");
+                    }
+                    switch (valueName[0]) {
+                        case XML_TAG_CONFIG_KEY:
+                            configKeyInData = (String) value;
+                            break;
+                        case XML_TAG_SSID:
+                            configuration.SSID = (String) value;
+                            break;
+                        case XML_TAG_BSSID:
+                            configuration.BSSID = (String) value;
+                            break;
+                        case XML_TAG_PRE_SHARED_KEY:
+                            configuration.preSharedKey = (String) value;
+                            break;
+                        case XML_TAG_WEP_KEYS:
+                            populateWepKeysFromXmlValue(value, configuration.wepKeys);
+                            break;
+                        case XML_TAG_WEP_TX_KEY_INDEX:
+                            configuration.wepTxKeyIndex = (int) value;
+                            break;
+                        case XML_TAG_HIDDEN_SSID:
+                            configuration.hiddenSSID = (boolean) value;
+                            break;
+                        case XML_TAG_REQUIRE_PMF:
+                            configuration.requirePMF = (boolean) value;
+                            break;
+                        case XML_TAG_ALLOWED_KEY_MGMT:
+                            byte[] allowedKeyMgmt = (byte[]) value;
+                            configuration.allowedKeyManagement = BitSet.valueOf(allowedKeyMgmt);
+                            break;
+                        case XML_TAG_ALLOWED_PROTOCOLS:
+                            byte[] allowedProtocols = (byte[]) value;
+                            configuration.allowedProtocols = BitSet.valueOf(allowedProtocols);
+                            break;
+                        case XML_TAG_ALLOWED_AUTH_ALGOS:
+                            byte[] allowedAuthAlgorithms = (byte[]) value;
+                            configuration.allowedAuthAlgorithms = BitSet.valueOf(
+                                    allowedAuthAlgorithms);
+                            break;
+                        case XML_TAG_ALLOWED_GROUP_CIPHERS:
+                            byte[] allowedGroupCiphers = (byte[]) value;
+                            configuration.allowedGroupCiphers = BitSet.valueOf(allowedGroupCiphers);
+                            break;
+                        case XML_TAG_ALLOWED_PAIRWISE_CIPHERS:
+                            byte[] allowedPairwiseCiphers = (byte[]) value;
+                            configuration.allowedPairwiseCiphers =
+                                    BitSet.valueOf(allowedPairwiseCiphers);
+                            break;
+                        case XML_TAG_ALLOWED_GROUP_MGMT_CIPHERS:
+                            byte[] allowedGroupMgmtCiphers = (byte[]) value;
+                            configuration.allowedGroupManagementCiphers =
+                                    BitSet.valueOf(allowedGroupMgmtCiphers);
+                            break;
+                        case XML_TAG_ALLOWED_SUITE_B_CIPHERS:
+                            byte[] allowedSuiteBCiphers = (byte[]) value;
+                            configuration.allowedSuiteBCiphers =
+                                    BitSet.valueOf(allowedSuiteBCiphers);
+                            break;
+                        case XML_TAG_SHARED:
+                            configuration.shared = (boolean) value;
+                            break;
+                        case XML_TAG_STATUS:
+                            int status = (int) value;
+                            // Any network which was CURRENT before reboot needs
+                            // to be restored to ENABLED.
+                            if (status == WifiConfiguration.Status.CURRENT) {
+                                status = WifiConfiguration.Status.ENABLED;
+                            }
+                            configuration.status = status;
+                            break;
+                        case XML_TAG_FQDN:
+                            configuration.FQDN = (String) value;
+                            break;
+                        case XML_TAG_PROVIDER_FRIENDLY_NAME:
+                            configuration.providerFriendlyName = (String) value;
+                            break;
+                        case XML_TAG_LINKED_NETWORKS_LIST:
+                            configuration.linkedConfigurations = (HashMap<String, Integer>) value;
+                            break;
+                        case XML_TAG_DEFAULT_GW_MAC_ADDRESS:
+                            configuration.defaultGwMacAddress = (String) value;
+                            break;
+                        case XML_TAG_VALIDATED_INTERNET_ACCESS:
+                            configuration.validatedInternetAccess = (boolean) value;
+                            break;
+                        case XML_TAG_NO_INTERNET_ACCESS_EXPECTED:
+                            configuration.noInternetAccessExpected = (boolean) value;
+                            break;
+                        case XML_TAG_USER_APPROVED:
+                            configuration.userApproved = (int) value;
+                            break;
+                        case XML_TAG_METERED_HINT:
+                            configuration.meteredHint = (boolean) value;
+                            break;
+                        case XML_TAG_METERED_OVERRIDE:
+                            configuration.meteredOverride = (int) value;
+                            break;
+                        case XML_TAG_USE_EXTERNAL_SCORES:
+                            configuration.useExternalScores = (boolean) value;
+                            break;
+                        case XML_TAG_NUM_ASSOCIATION:
+                            configuration.numAssociation = (int) value;
+                            break;
+                        case XML_TAG_CREATOR_UID:
+                            configuration.creatorUid = (int) value;
+                            break;
+                        case XML_TAG_CREATOR_NAME:
+                            configuration.creatorName = (String) value;
+                            break;
+                        case XML_TAG_CREATION_TIME:
+                            configuration.creationTime = (String) value;
+                            break;
+                        case XML_TAG_LAST_UPDATE_UID:
+                            configuration.lastUpdateUid = (int) value;
+                            break;
+                        case XML_TAG_LAST_UPDATE_NAME:
+                            configuration.lastUpdateName = (String) value;
+                            break;
+                        case XML_TAG_LAST_CONNECT_UID:
+                            configuration.lastConnectUid = (int) value;
+                            break;
+                        case XML_TAG_IS_LEGACY_PASSPOINT_CONFIG:
+                            configuration.isLegacyPasspointConfig = (boolean) value;
+                            break;
+                        case XML_TAG_ROAMING_CONSORTIUM_OIS:
+                            configuration.roamingConsortiumIds = (long[]) value;
+                            break;
+                        case XML_TAG_RANDOMIZED_MAC_ADDRESS:
+                            configuration.setRandomizedMacAddress(
+                                    MacAddress.fromString((String) value));
+                            break;
+                        case XML_TAG_MAC_RANDOMIZATION_SETTING:
+                            configuration.macRandomizationSetting = (int) value;
+                            macRandomizationSettingExists = true;
+                            break;
+                        default:
+                            throw new XmlPullParserException(
+                                  "Unknown value name found: " + valueName[0]);
+                    }
+                } else {
+                    String tagName = in.getName();
+                    if (tagName == null) {
+                        throw new XmlPullParserException("Unexpected null tag found");
+                    }
+                    switch (tagName) {
+                        case XML_TAG_PRE_SHARED_KEY:
+                            if (!shouldExpectEncryptedCredentials) {
+                                throw new XmlPullParserException(
+                                        "Encrypted preSharedKey section not expected");
+                            }
+                            EncryptedData encryptedData =
+                                    EncryptedDataXmlUtil.parseFromXml(in, outerTagDepth + 1);
+                            byte[] preSharedKeyBytes = encryptionUtil.decrypt(encryptedData);
+                            if (preSharedKeyBytes == null) {
+                                Log.wtf(TAG, "Decryption of preSharedKey failed");
+                            } else {
+                                configuration.preSharedKey = new String(preSharedKeyBytes);
+                            }
+                            break;
+                        default:
+                            throw new XmlPullParserException(
+                                  "Unknown tag name found: " + tagName);
+                    }
                 }
             }
             if (!macRandomizationSettingExists) {
@@ -1028,6 +1087,35 @@
         public static final String XML_TAG_REALM = "Realm";
 
         /**
+         * Write password key to the XML stream.
+         *
+         * If encryptionUtil is null or if encryption fails for some reason, the password is stored
+         * in plaintext, else the encrypted psk is stored.
+         */
+        private static void writePasswordToXml(
+                XmlSerializer out, String password,
+                @NonNull WifiConfigStoreEncryptionUtil encryptionUtil)
+                throws XmlPullParserException, IOException {
+            EncryptedData encryptedData = null;
+            if (encryptionUtil != null) {
+                if (password != null) {
+                    encryptedData = encryptionUtil.encrypt(password.getBytes());
+                    if (encryptedData == null) {
+                        // We silently fail encryption failures!
+                        Log.wtf(TAG, "Encryption of password failed");
+                    }
+                }
+            }
+            if (encryptedData != null) {
+                XmlUtil.writeNextSectionStart(out, XML_TAG_PASSWORD);
+                EncryptedDataXmlUtil.writeToXml(out, encryptedData);
+                XmlUtil.writeNextSectionEnd(out, XML_TAG_PASSWORD);
+            } else {
+                XmlUtil.writeNextValue(out, XML_TAG_PASSWORD, password);
+            }
+        }
+
+        /**
          * Write the WifiEnterpriseConfig data elements from the provided config to the XML
          * stream.
          *
@@ -1042,8 +1130,9 @@
                     enterpriseConfig.getFieldValue(WifiEnterpriseConfig.IDENTITY_KEY));
             XmlUtil.writeNextValue(out, XML_TAG_ANON_IDENTITY,
                     enterpriseConfig.getFieldValue(WifiEnterpriseConfig.ANON_IDENTITY_KEY));
-            XmlUtil.writeNextValue(out, XML_TAG_PASSWORD,
-                    enterpriseConfig.getFieldValue(WifiEnterpriseConfig.PASSWORD_KEY));
+            writePasswordToXml(
+                    out, enterpriseConfig.getFieldValue(WifiEnterpriseConfig.PASSWORD_KEY),
+                    encryptionUtil);
             XmlUtil.writeNextValue(out, XML_TAG_CLIENT_CERT,
                     enterpriseConfig.getFieldValue(WifiEnterpriseConfig.CLIENT_CERT_KEY));
             XmlUtil.writeNextValue(out, XML_TAG_CA_CERT,
@@ -1073,87 +1162,123 @@
          *
          * @param in XmlPullParser instance pointing to the XML stream.
          * @param outerTagDepth depth of the outer tag in the XML document.
-         * @param areCredentialsEncrypted Whether credentials are encrypted or not.
+         * @param shouldExpectEncryptedCredentials Whether to expect encrypted credentials or not.
          * @param encryptionUtil Instance of {@link EncryptedDataXmlUtil}.
          * @return WifiEnterpriseConfig object if parsing is successful, null otherwise.
          */
         public static WifiEnterpriseConfig parseFromXml(XmlPullParser in, int outerTagDepth,
-                boolean areCredentialsEncrypted,
+                boolean shouldExpectEncryptedCredentials,
                 @NonNull WifiConfigStoreEncryptionUtil encryptionUtil)
                 throws XmlPullParserException, IOException {
             WifiEnterpriseConfig enterpriseConfig = new WifiEnterpriseConfig();
 
             // Loop through and parse out all the elements from the stream within this section.
-            while (!XmlUtil.isNextSectionEnd(in, outerTagDepth)) {
-                String[] valueName = new String[1];
-                Object value = XmlUtil.readCurrentValue(in, valueName);
-                if (valueName[0] == null) {
-                    throw new XmlPullParserException("Missing value name");
-                }
-                switch (valueName[0]) {
-                    case XML_TAG_IDENTITY:
-                        enterpriseConfig.setFieldValue(
-                                WifiEnterpriseConfig.IDENTITY_KEY, (String) value);
-                        break;
-                    case XML_TAG_ANON_IDENTITY:
-                        enterpriseConfig.setFieldValue(
-                                WifiEnterpriseConfig.ANON_IDENTITY_KEY, (String) value);
-                        break;
-                    case XML_TAG_PASSWORD:
-                        enterpriseConfig.setFieldValue(
-                                WifiEnterpriseConfig.PASSWORD_KEY, (String) value);
-                        break;
-                    case XML_TAG_CLIENT_CERT:
-                        enterpriseConfig.setFieldValue(
-                                WifiEnterpriseConfig.CLIENT_CERT_KEY, (String) value);
-                        break;
-                    case XML_TAG_CA_CERT:
-                        enterpriseConfig.setFieldValue(
-                                WifiEnterpriseConfig.CA_CERT_KEY, (String) value);
-                        break;
-                    case XML_TAG_SUBJECT_MATCH:
-                        enterpriseConfig.setFieldValue(
-                                WifiEnterpriseConfig.SUBJECT_MATCH_KEY, (String) value);
-                        break;
-                    case XML_TAG_ENGINE:
-                        enterpriseConfig.setFieldValue(
-                                WifiEnterpriseConfig.ENGINE_KEY, (String) value);
-                        break;
-                    case XML_TAG_ENGINE_ID:
-                        enterpriseConfig.setFieldValue(
-                                WifiEnterpriseConfig.ENGINE_ID_KEY, (String) value);
-                        break;
-                    case XML_TAG_PRIVATE_KEY_ID:
-                        enterpriseConfig.setFieldValue(
-                                WifiEnterpriseConfig.PRIVATE_KEY_ID_KEY, (String) value);
-                        break;
-                    case XML_TAG_ALT_SUBJECT_MATCH:
-                        enterpriseConfig.setFieldValue(
-                                WifiEnterpriseConfig.ALTSUBJECT_MATCH_KEY, (String) value);
-                        break;
-                    case XML_TAG_DOM_SUFFIX_MATCH:
-                        enterpriseConfig.setFieldValue(
-                                WifiEnterpriseConfig.DOM_SUFFIX_MATCH_KEY, (String) value);
-                        break;
-                    case XML_TAG_CA_PATH:
-                        enterpriseConfig.setFieldValue(
-                                WifiEnterpriseConfig.CA_PATH_KEY, (String) value);
-                        break;
-                    case XML_TAG_EAP_METHOD:
-                        enterpriseConfig.setEapMethod((int) value);
-                        break;
-                    case XML_TAG_PHASE2_METHOD:
-                        enterpriseConfig.setPhase2Method((int) value);
-                        break;
-                    case XML_TAG_PLMN:
-                        enterpriseConfig.setPlmn((String) value);
-                        break;
-                    case XML_TAG_REALM:
-                        enterpriseConfig.setRealm((String) value);
-                        break;
-                    default:
-                        throw new XmlPullParserException(
-                                "Unknown value name found: " + valueName[0]);
+            while (XmlUtils.nextElementWithin(in, outerTagDepth)) {
+                if (in.getAttributeValue(null, "name") != null) {
+                    // Value elements.
+                    String[] valueName = new String[1];
+                    Object value = XmlUtil.readCurrentValue(in, valueName);
+                    if (valueName[0] == null) {
+                        throw new XmlPullParserException("Missing value name");
+                    }
+                    switch (valueName[0]) {
+                        case XML_TAG_IDENTITY:
+                            enterpriseConfig.setFieldValue(
+                                    WifiEnterpriseConfig.IDENTITY_KEY, (String) value);
+                            break;
+                        case XML_TAG_ANON_IDENTITY:
+                            enterpriseConfig.setFieldValue(
+                                    WifiEnterpriseConfig.ANON_IDENTITY_KEY, (String) value);
+                            break;
+                        case XML_TAG_PASSWORD:
+                            enterpriseConfig.setFieldValue(
+                                    WifiEnterpriseConfig.PASSWORD_KEY, (String) value);
+                            if (shouldExpectEncryptedCredentials
+                                    && !TextUtils.isEmpty(enterpriseConfig.getFieldValue(
+                                            WifiEnterpriseConfig.PASSWORD_KEY))) {
+                                // Indicates that encryption of password failed when it was last
+                                // written.
+                                Log.e(TAG, "password value not expected");
+                            }
+                            break;
+                        case XML_TAG_CLIENT_CERT:
+                            enterpriseConfig.setFieldValue(
+                                    WifiEnterpriseConfig.CLIENT_CERT_KEY, (String) value);
+                            break;
+                        case XML_TAG_CA_CERT:
+                            enterpriseConfig.setFieldValue(
+                                    WifiEnterpriseConfig.CA_CERT_KEY, (String) value);
+                            break;
+                        case XML_TAG_SUBJECT_MATCH:
+                            enterpriseConfig.setFieldValue(
+                                    WifiEnterpriseConfig.SUBJECT_MATCH_KEY, (String) value);
+                            break;
+                        case XML_TAG_ENGINE:
+                            enterpriseConfig.setFieldValue(
+                                    WifiEnterpriseConfig.ENGINE_KEY, (String) value);
+                            break;
+                        case XML_TAG_ENGINE_ID:
+                            enterpriseConfig.setFieldValue(
+                                    WifiEnterpriseConfig.ENGINE_ID_KEY, (String) value);
+                            break;
+                        case XML_TAG_PRIVATE_KEY_ID:
+                            enterpriseConfig.setFieldValue(
+                                    WifiEnterpriseConfig.PRIVATE_KEY_ID_KEY, (String) value);
+                            break;
+                        case XML_TAG_ALT_SUBJECT_MATCH:
+                            enterpriseConfig.setFieldValue(
+                                    WifiEnterpriseConfig.ALTSUBJECT_MATCH_KEY, (String) value);
+                            break;
+                        case XML_TAG_DOM_SUFFIX_MATCH:
+                            enterpriseConfig.setFieldValue(
+                                    WifiEnterpriseConfig.DOM_SUFFIX_MATCH_KEY, (String) value);
+                            break;
+                        case XML_TAG_CA_PATH:
+                            enterpriseConfig.setFieldValue(
+                                    WifiEnterpriseConfig.CA_PATH_KEY, (String) value);
+                            break;
+                        case XML_TAG_EAP_METHOD:
+                            enterpriseConfig.setEapMethod((int) value);
+                            break;
+                        case XML_TAG_PHASE2_METHOD:
+                            enterpriseConfig.setPhase2Method((int) value);
+                            break;
+                        case XML_TAG_PLMN:
+                            enterpriseConfig.setPlmn((String) value);
+                            break;
+                        case XML_TAG_REALM:
+                            enterpriseConfig.setRealm((String) value);
+                            break;
+                        default:
+                            throw new XmlPullParserException(
+                                  "Unknown value name found: " + valueName[0]);
+                    }
+                } else {
+                    String tagName = in.getName();
+                    if (tagName == null) {
+                        throw new XmlPullParserException("Unexpected null tag found");
+                    }
+                    switch (tagName) {
+                        case XML_TAG_PASSWORD:
+                            if (!shouldExpectEncryptedCredentials) {
+                                throw new XmlPullParserException(
+                                        "encrypted password section not expected");
+                            }
+                            EncryptedData encryptedData =
+                                    EncryptedDataXmlUtil.parseFromXml(in, outerTagDepth + 1);
+                            byte[] passwordBytes = encryptionUtil.decrypt(encryptedData);
+                            if (passwordBytes == null) {
+                                Log.wtf(TAG, "Decryption of password failed");
+                            } else {
+                                enterpriseConfig.setFieldValue(
+                                        WifiEnterpriseConfig.PASSWORD_KEY,
+                                        new String(passwordBytes));
+                            }
+                            break;
+                        default:
+                            throw new XmlPullParserException(
+                                  "Unknown tag name found: " + tagName);
+                    }
                 }
             }
             return enterpriseConfig;
diff --git a/tests/wifitests/src/com/android/server/wifi/WifiConfigStoreTest.java b/tests/wifitests/src/com/android/server/wifi/WifiConfigStoreTest.java
index 9c16b12..efa2d43 100644
--- a/tests/wifitests/src/com/android/server/wifi/WifiConfigStoreTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/WifiConfigStoreTest.java
@@ -16,8 +16,6 @@
 
 package com.android.server.wifi;
 
-import static com.android.server.wifi.WifiConfigStore.ZEROED_ENCRYPTED_DATA;
-
 import static org.junit.Assert.*;
 import static org.mockito.Mockito.*;
 
@@ -189,7 +187,7 @@
         when(mContext.getPackageManager()).thenReturn(mPackageManager);
         when(mPackageManager.getNameForUid(anyInt())).thenReturn(TEST_CREATOR_NAME);
         when(mEncryptionUtil.encrypt(any(byte[].class)))
-                .thenReturn(ZEROED_ENCRYPTED_DATA);
+                .thenReturn(new EncryptedData(new byte[0], new byte[0]));
         when(mEncryptionUtil.decrypt(any(EncryptedData.class)))
                 .thenReturn(new byte[0]);
         mSharedStore = new MockStoreFile(WifiConfigStore.STORE_FILE_SHARED_GENERAL);
@@ -820,8 +818,8 @@
      */
     @Test
     public void testReadVersion2StoreFile() throws Exception {
-        byte[] encryptedData = new byte[EncryptedData.ENCRYPTED_DATA_LENGTH];
-        byte[] iv = new byte[EncryptedData.IV_LENGTH];
+        byte[] encryptedData = new byte[0];
+        byte[] iv = new byte[0];
         Random random = new Random();
         random.nextBytes(encryptedData);
         random.nextBytes(iv);
diff --git a/tests/wifitests/src/com/android/server/wifi/util/XmlUtilTest.java b/tests/wifitests/src/com/android/server/wifi/util/XmlUtilTest.java
index 126a80d..66534d2 100644
--- a/tests/wifitests/src/com/android/server/wifi/util/XmlUtilTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/util/XmlUtilTest.java
@@ -35,7 +35,11 @@
 import com.android.server.wifi.util.XmlUtil.WifiConfigurationXmlUtil;
 import com.android.server.wifi.util.XmlUtil.WifiEnterpriseConfigXmlUtil;
 
+import org.junit.Before;
 import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
 import org.xmlpull.v1.XmlSerializer;
@@ -73,6 +77,13 @@
     private static final int TEST_PHASE2_METHOD = WifiEnterpriseConfig.Phase2.MSCHAPV2;
     private final String mXmlDocHeader = "XmlUtilTest";
 
+    @Mock private WifiConfigStoreEncryptionUtil mWifiConfigStoreEncryptionUtil;
+
+    @Before
+    public void setUp() throws Exception {
+        MockitoAnnotations.initMocks(this);
+    }
+
     /**
      * Verify that a open WifiConfiguration is serialized & deserialized correctly.
      */
@@ -101,6 +112,21 @@
     }
 
     /**
+     * Verify that a psk WifiConfiguration is serialized & deserialized correctly.
+     */
+    @Test
+    public void testPskWifiConfigurationSerializeDeserializeWithEncryption()
+            throws IOException, XmlPullParserException {
+        WifiConfiguration pskNetwork = WifiConfigurationTestUtil.createPskNetwork();
+        EncryptedData encryptedData = new EncryptedData(new byte[0], new byte[0]);
+        when(mWifiConfigStoreEncryptionUtil.encrypt(pskNetwork.preSharedKey.getBytes()))
+                .thenReturn(encryptedData);
+        when(mWifiConfigStoreEncryptionUtil.decrypt(encryptedData))
+                .thenReturn(pskNetwork.preSharedKey.getBytes());
+        serializeDeserializeWifiConfiguration(pskNetwork);
+    }
+
+    /**
      * Verify that a psk hidden WifiConfiguration is serialized & deserialized correctly.
      */
     @Test
@@ -382,6 +408,36 @@
     }
 
     /**
+     * Verify that a WifiEnterpriseConfig object is serialized & deserialized correctly.
+     */
+    @Test
+    public void testWifiEnterpriseConfigSerializeDeserializeWithEncryption()
+            throws IOException, XmlPullParserException {
+        WifiEnterpriseConfig config = new WifiEnterpriseConfig();
+        config.setFieldValue(WifiEnterpriseConfig.IDENTITY_KEY, TEST_IDENTITY);
+        config.setFieldValue(WifiEnterpriseConfig.ANON_IDENTITY_KEY, TEST_ANON_IDENTITY);
+        config.setFieldValue(WifiEnterpriseConfig.PASSWORD_KEY, TEST_PASSWORD);
+        config.setFieldValue(WifiEnterpriseConfig.CLIENT_CERT_KEY, TEST_CLIENT_CERT);
+        config.setFieldValue(WifiEnterpriseConfig.CA_CERT_KEY, TEST_CA_CERT);
+        config.setFieldValue(WifiEnterpriseConfig.SUBJECT_MATCH_KEY, TEST_SUBJECT_MATCH);
+        config.setFieldValue(WifiEnterpriseConfig.ENGINE_KEY, TEST_ENGINE);
+        config.setFieldValue(WifiEnterpriseConfig.ENGINE_ID_KEY, TEST_ENGINE_ID);
+        config.setFieldValue(WifiEnterpriseConfig.PRIVATE_KEY_ID_KEY, TEST_PRIVATE_KEY_ID);
+        config.setFieldValue(WifiEnterpriseConfig.ALTSUBJECT_MATCH_KEY, TEST_ALTSUBJECT_MATCH);
+        config.setFieldValue(WifiEnterpriseConfig.DOM_SUFFIX_MATCH_KEY, TEST_DOM_SUFFIX_MATCH);
+        config.setFieldValue(WifiEnterpriseConfig.CA_PATH_KEY, TEST_CA_PATH);
+        config.setEapMethod(TEST_EAP_METHOD);
+        config.setPhase2Method(TEST_PHASE2_METHOD);
+
+        EncryptedData encryptedData = new EncryptedData(new byte[0], new byte[0]);
+        when(mWifiConfigStoreEncryptionUtil.encrypt(TEST_PASSWORD.getBytes()))
+                .thenReturn(encryptedData);
+        when(mWifiConfigStoreEncryptionUtil.decrypt(encryptedData))
+                .thenReturn(TEST_PASSWORD.getBytes());
+        serializeDeserializeWifiEnterpriseConfig(config);
+    }
+
+    /**
      * Verify that an illegal argument exception is thrown when trying to parse out a corrupted
      * WifiEnterpriseConfig.
      *
@@ -474,7 +530,7 @@
         out.setOutput(outputStream, StandardCharsets.UTF_8.name());
         XmlUtil.writeDocumentStart(out, mXmlDocHeader);
         WifiConfigurationXmlUtil.writeToXmlForConfigStore(
-                out, configuration, mock(WifiConfigStoreEncryptionUtil.class));
+                out, configuration, mWifiConfigStoreEncryptionUtil);
         XmlUtil.writeDocumentEnd(out, mXmlDocHeader);
         return outputStream.toByteArray();
     }
@@ -487,7 +543,9 @@
         in.setInput(inputStream, StandardCharsets.UTF_8.name());
         XmlUtil.gotoDocumentStart(in, mXmlDocHeader);
         return WifiConfigurationXmlUtil.parseFromXml(
-                in, in.getDepth(), false, mock(WifiConfigStoreEncryptionUtil.class));
+                in, in.getDepth(),
+                mWifiConfigStoreEncryptionUtil != null,
+                mWifiConfigStoreEncryptionUtil);
     }
 
     /**
@@ -596,7 +654,7 @@
         out.setOutput(outputStream, StandardCharsets.UTF_8.name());
         XmlUtil.writeDocumentStart(out, mXmlDocHeader);
         WifiEnterpriseConfigXmlUtil.writeToXml(
-                out, config, mock(WifiConfigStoreEncryptionUtil.class));
+                out, config, mWifiConfigStoreEncryptionUtil);
         XmlUtil.writeDocumentEnd(out, mXmlDocHeader);
         return outputStream.toByteArray();
     }
@@ -608,7 +666,8 @@
         in.setInput(inputStream, StandardCharsets.UTF_8.name());
         XmlUtil.gotoDocumentStart(in, mXmlDocHeader);
         return WifiEnterpriseConfigXmlUtil.parseFromXml(
-                in, in.getDepth(), false, mock(WifiConfigStoreEncryptionUtil.class));
+                in, in.getDepth(), mWifiConfigStoreEncryptionUtil != null,
+                mWifiConfigStoreEncryptionUtil);
     }
 
     private void serializeDeserializeWifiEnterpriseConfig(WifiEnterpriseConfig config)