diff options
author | Roshan Pius <rpius@google.com> | 2019-10-22 13:42:45 -0700 |
---|---|---|
committer | Roshan Pius <rpius@google.com> | 2019-11-14 11:18:00 -0800 |
commit | a161d62d9baac95a087cb3252d51c1fd101d24fa (patch) | |
tree | 4213efe8d35f12e1bd993bc35931b322a4da8f74 /service | |
parent | bc8fa0c163c40afa999ac71cc78687bb845131ab (diff) |
WifiConfigStore: Encrypt credentials for networks (1/4)
Changes in the CL:
a) Introduced a new config store version.
The new version gets rid of the integrity computation & adds support for
encryption of credential data.
b) Changed DataIntegrityChecker to WifiConfigStoreEncryptionUtil to
help the different config store modules to encrypt/decrypt their
credential data to be stored.
c) Pass the version & the new EncryptionUtil to all the config store
modules. The version is needed for the modules to handle upgrades.
d) Use the WIFI_UID to store encryption key in keystore (to help ease
migrate keys if we move to a separate process in R)
Actual encryption of credential data/handling of upgrades will be
added in the next CL.
Bug: 140485110
Test: atest com.android.server.wifi
Change-Id: I522b11ef2ffbdbf0ff19ae4f2643023df3843e5e
Merged-In: I522b11ef2ffbdbf0ff19ae4f2643023df3843e5e
Diffstat (limited to 'service')
-rw-r--r-- | service/java/com/android/server/wifi/DeletedEphemeralSsidsStoreData.java | 10 | ||||
-rw-r--r-- | service/java/com/android/server/wifi/NetworkListStoreData.java | 9 | ||||
-rw-r--r-- | service/java/com/android/server/wifi/NetworkRequestStoreData.java | 9 | ||||
-rw-r--r-- | service/java/com/android/server/wifi/NetworkSuggestionStoreData.java | 9 | ||||
-rw-r--r-- | service/java/com/android/server/wifi/RandomizedMacStoreData.java | 10 | ||||
-rw-r--r-- | service/java/com/android/server/wifi/SsidSetStoreData.java | 9 | ||||
-rw-r--r-- | service/java/com/android/server/wifi/WakeupConfigStoreData.java | 9 | ||||
-rw-r--r-- | service/java/com/android/server/wifi/WifiConfigStore.java | 209 | ||||
-rw-r--r-- | service/java/com/android/server/wifi/hotspot2/PasspointConfigSharedStoreData.java | 10 | ||||
-rw-r--r-- | service/java/com/android/server/wifi/hotspot2/PasspointConfigUserStoreData.java | 9 | ||||
-rw-r--r-- | service/java/com/android/server/wifi/util/WifiConfigStoreEncryptionUtil.java (renamed from service/java/com/android/server/wifi/util/DataIntegrityChecker.java) | 139 |
11 files changed, 156 insertions, 276 deletions
diff --git a/service/java/com/android/server/wifi/DeletedEphemeralSsidsStoreData.java b/service/java/com/android/server/wifi/DeletedEphemeralSsidsStoreData.java index 0c064884c..3575ff254 100644 --- a/service/java/com/android/server/wifi/DeletedEphemeralSsidsStoreData.java +++ b/service/java/com/android/server/wifi/DeletedEphemeralSsidsStoreData.java @@ -16,6 +16,9 @@ package com.android.server.wifi; +import android.annotation.NonNull; + +import com.android.server.wifi.util.WifiConfigStoreEncryptionUtil; import com.android.server.wifi.util.XmlUtil; import org.xmlpull.v1.XmlPullParser; @@ -44,7 +47,8 @@ public class DeletedEphemeralSsidsStoreData implements WifiConfigStore.StoreData } @Override - public void serializeData(XmlSerializer out) + public void serializeData(XmlSerializer out, + @NonNull WifiConfigStoreEncryptionUtil encryptionUtil) throws XmlPullParserException, IOException { if (mSsidToTimeMap != null) { XmlUtil.writeNextValue(out, XML_TAG_SSID_LIST, mSsidToTimeMap); @@ -52,7 +56,9 @@ public class DeletedEphemeralSsidsStoreData implements WifiConfigStore.StoreData } @Override - public void deserializeData(XmlPullParser in, int outerTagDepth) + public void deserializeData(XmlPullParser in, int outerTagDepth, + @WifiConfigStore.Version int version, + @NonNull WifiConfigStoreEncryptionUtil encryptionUtil) throws XmlPullParserException, IOException { // Ignore empty reads. if (in == null) { diff --git a/service/java/com/android/server/wifi/NetworkListStoreData.java b/service/java/com/android/server/wifi/NetworkListStoreData.java index 696647185..981e97c7e 100644 --- a/service/java/com/android/server/wifi/NetworkListStoreData.java +++ b/service/java/com/android/server/wifi/NetworkListStoreData.java @@ -16,6 +16,7 @@ package com.android.server.wifi; +import android.annotation.NonNull; import android.content.Context; import android.net.IpConfiguration; import android.net.wifi.WifiConfiguration; @@ -25,6 +26,7 @@ import android.os.Process; import android.util.Log; import android.util.Pair; +import com.android.server.wifi.util.WifiConfigStoreEncryptionUtil; import com.android.server.wifi.util.XmlUtil; import com.android.server.wifi.util.XmlUtil.IpConfigurationXmlUtil; import com.android.server.wifi.util.XmlUtil.NetworkSelectionStatusXmlUtil; @@ -66,13 +68,16 @@ public abstract class NetworkListStoreData implements WifiConfigStore.StoreData } @Override - public void serializeData(XmlSerializer out) + public void serializeData(XmlSerializer out, + @NonNull WifiConfigStoreEncryptionUtil encryptionUtil) throws XmlPullParserException, IOException { serializeNetworkList(out, mConfigurations); } @Override - public void deserializeData(XmlPullParser in, int outerTagDepth) + public void deserializeData(XmlPullParser in, int outerTagDepth, + @WifiConfigStore.Version int version, + @NonNull WifiConfigStoreEncryptionUtil encryptionUtil) throws XmlPullParserException, IOException { // Ignore empty reads. if (in == null) { diff --git a/service/java/com/android/server/wifi/NetworkRequestStoreData.java b/service/java/com/android/server/wifi/NetworkRequestStoreData.java index 8d1244f05..3a5143f56 100644 --- a/service/java/com/android/server/wifi/NetworkRequestStoreData.java +++ b/service/java/com/android/server/wifi/NetworkRequestStoreData.java @@ -16,10 +16,12 @@ package com.android.server.wifi; +import android.annotation.NonNull; import android.net.MacAddress; import android.util.Log; import com.android.server.wifi.WifiNetworkFactory.AccessPoint; +import com.android.server.wifi.util.WifiConfigStoreEncryptionUtil; import com.android.server.wifi.util.XmlUtil; import com.android.server.wifi.util.XmlUtil.WifiConfigurationXmlUtil; @@ -87,13 +89,16 @@ public class NetworkRequestStoreData implements WifiConfigStore.StoreData { } @Override - public void serializeData(XmlSerializer out) + public void serializeData(XmlSerializer out, + @NonNull WifiConfigStoreEncryptionUtil encryptionUtil) throws XmlPullParserException, IOException { serializeApprovedAccessPointsMap(out, mDataSource.toSerialize()); } @Override - public void deserializeData(XmlPullParser in, int outerTagDepth) + public void deserializeData(XmlPullParser in, int outerTagDepth, + @WifiConfigStore.Version int version, + @NonNull WifiConfigStoreEncryptionUtil encryptionUtil) throws XmlPullParserException, IOException { // Ignore empty reads. if (in == null) { diff --git a/service/java/com/android/server/wifi/NetworkSuggestionStoreData.java b/service/java/com/android/server/wifi/NetworkSuggestionStoreData.java index 9627a9daa..e9503b7e4 100644 --- a/service/java/com/android/server/wifi/NetworkSuggestionStoreData.java +++ b/service/java/com/android/server/wifi/NetworkSuggestionStoreData.java @@ -16,6 +16,7 @@ package com.android.server.wifi; +import android.annotation.NonNull; import android.net.wifi.WifiConfiguration; import android.net.wifi.WifiEnterpriseConfig; import android.net.wifi.WifiNetworkSuggestion; @@ -26,6 +27,7 @@ import android.util.Pair; import com.android.internal.util.XmlUtils; import com.android.server.wifi.WifiNetworkSuggestionsManager.ExtendedWifiNetworkSuggestion; import com.android.server.wifi.WifiNetworkSuggestionsManager.PerAppInfo; +import com.android.server.wifi.util.WifiConfigStoreEncryptionUtil; import com.android.server.wifi.util.XmlUtil; import com.android.server.wifi.util.XmlUtil.WifiConfigurationXmlUtil; @@ -98,13 +100,16 @@ public class NetworkSuggestionStoreData implements WifiConfigStore.StoreData { } @Override - public void serializeData(XmlSerializer out) + public void serializeData(XmlSerializer out, + @NonNull WifiConfigStoreEncryptionUtil encryptionUtil) throws XmlPullParserException, IOException { serializeNetworkSuggestionsMap(out, mDataSource.toSerialize()); } @Override - public void deserializeData(XmlPullParser in, int outerTagDepth) + public void deserializeData(XmlPullParser in, int outerTagDepth, + @WifiConfigStore.Version int version, + @NonNull WifiConfigStoreEncryptionUtil encryptionUtil) throws XmlPullParserException, IOException { // Ignore empty reads. if (in == null) { diff --git a/service/java/com/android/server/wifi/RandomizedMacStoreData.java b/service/java/com/android/server/wifi/RandomizedMacStoreData.java index 1e4d972ef..8e47ee7bf 100644 --- a/service/java/com/android/server/wifi/RandomizedMacStoreData.java +++ b/service/java/com/android/server/wifi/RandomizedMacStoreData.java @@ -16,6 +16,9 @@ package com.android.server.wifi; +import android.annotation.NonNull; + +import com.android.server.wifi.util.WifiConfigStoreEncryptionUtil; import com.android.server.wifi.util.XmlUtil; import org.xmlpull.v1.XmlPullParser; @@ -40,7 +43,8 @@ public class RandomizedMacStoreData implements WifiConfigStore.StoreData { RandomizedMacStoreData() {} @Override - public void serializeData(XmlSerializer out) + public void serializeData(XmlSerializer out, + @NonNull WifiConfigStoreEncryptionUtil encryptionUtil) throws XmlPullParserException, IOException { if (mMacMapping != null) { XmlUtil.writeNextValue(out, XML_TAG_MAC_MAP, mMacMapping); @@ -48,7 +52,9 @@ public class RandomizedMacStoreData implements WifiConfigStore.StoreData { } @Override - public void deserializeData(XmlPullParser in, int outerTagDepth) + public void deserializeData(XmlPullParser in, int outerTagDepth, + @WifiConfigStore.Version int version, + @NonNull WifiConfigStoreEncryptionUtil encryptionUtil) throws XmlPullParserException, IOException { // Ignore empty reads. if (in == null) { diff --git a/service/java/com/android/server/wifi/SsidSetStoreData.java b/service/java/com/android/server/wifi/SsidSetStoreData.java index 7d1b99340..1339dae38 100644 --- a/service/java/com/android/server/wifi/SsidSetStoreData.java +++ b/service/java/com/android/server/wifi/SsidSetStoreData.java @@ -16,8 +16,10 @@ package com.android.server.wifi; +import android.annotation.NonNull; import android.text.TextUtils; +import com.android.server.wifi.util.WifiConfigStoreEncryptionUtil; import com.android.server.wifi.util.XmlUtil; import org.xmlpull.v1.XmlPullParser; @@ -74,7 +76,8 @@ public class SsidSetStoreData implements WifiConfigStore.StoreData { } @Override - public void serializeData(XmlSerializer out) + public void serializeData(XmlSerializer out, + @NonNull WifiConfigStoreEncryptionUtil encryptionUtil) throws XmlPullParserException, IOException { Set<String> ssidSet = mDataSource.getSsids(); if (ssidSet != null && !ssidSet.isEmpty()) { @@ -83,7 +86,9 @@ public class SsidSetStoreData implements WifiConfigStore.StoreData { } @Override - public void deserializeData(XmlPullParser in, int outerTagDepth) + public void deserializeData(XmlPullParser in, int outerTagDepth, + @WifiConfigStore.Version int version, + @NonNull WifiConfigStoreEncryptionUtil encryptionUtil) throws XmlPullParserException, IOException { // Ignore empty reads. if (in == null) { diff --git a/service/java/com/android/server/wifi/WakeupConfigStoreData.java b/service/java/com/android/server/wifi/WakeupConfigStoreData.java index d191ee3d6..1d146a0e1 100644 --- a/service/java/com/android/server/wifi/WakeupConfigStoreData.java +++ b/service/java/com/android/server/wifi/WakeupConfigStoreData.java @@ -16,10 +16,12 @@ package com.android.server.wifi; +import android.annotation.NonNull; import android.util.ArraySet; import android.util.Log; import com.android.server.wifi.WifiConfigStore.StoreData; +import com.android.server.wifi.util.WifiConfigStoreEncryptionUtil; import com.android.server.wifi.util.XmlUtil; import org.xmlpull.v1.XmlPullParser; @@ -94,7 +96,8 @@ public class WakeupConfigStoreData implements StoreData { } @Override - public void serializeData(XmlSerializer out) + public void serializeData(XmlSerializer out, + @NonNull WifiConfigStoreEncryptionUtil encryptionUtil) throws XmlPullParserException, IOException { writeFeatureState(out); @@ -141,7 +144,9 @@ public class WakeupConfigStoreData implements StoreData { } @Override - public void deserializeData(XmlPullParser in, int outerTagDepth) + public void deserializeData(XmlPullParser in, int outerTagDepth, + @WifiConfigStore.Version int version, + @NonNull WifiConfigStoreEncryptionUtil encryptionUtil) throws XmlPullParserException, IOException { if (!mHasBeenRead) { Log.d(TAG, "WifiWake user data has been read"); diff --git a/service/java/com/android/server/wifi/WifiConfigStore.java b/service/java/com/android/server/wifi/WifiConfigStore.java index e189d00e1..efe4a4c8d 100644 --- a/service/java/com/android/server/wifi/WifiConfigStore.java +++ b/service/java/com/android/server/wifi/WifiConfigStore.java @@ -35,8 +35,8 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.internal.os.AtomicFile; import com.android.internal.util.FastXmlSerializer; import com.android.internal.util.Preconditions; -import com.android.server.wifi.util.DataIntegrityChecker; import com.android.server.wifi.util.EncryptedData; +import com.android.server.wifi.util.WifiConfigStoreEncryptionUtil; import com.android.server.wifi.util.XmlUtil; import org.xmlpull.v1.XmlPullParser; @@ -53,7 +53,6 @@ import java.io.IOException; import java.io.PrintWriter; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; -import java.nio.ByteBuffer; import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Arrays; @@ -106,17 +105,33 @@ public class WifiConfigStore { /** * Current config store data version. This will be incremented for any additions. */ - private static final int CURRENT_CONFIG_STORE_DATA_VERSION = 2; + private static final int CURRENT_CONFIG_STORE_DATA_VERSION = 3; /** This list of older versions will be used to restore data from older config store. */ /** * First version of the config store data format. */ - private static final int INITIAL_CONFIG_STORE_DATA_VERSION = 1; + public static final int INITIAL_CONFIG_STORE_DATA_VERSION = 1; /** * Second version of the config store data format, introduced: * - Integrity info. */ - private static final int INTEGRITY_CONFIG_STORE_DATA_VERSION = 2; + public static final int INTEGRITY_CONFIG_STORE_DATA_VERSION = 2; + /** + * Third version of the config store data format, + * introduced: + * - Encryption of credentials + * removed: + * - Integrity info. + */ + public static final int ENCRYPT_CREDENTIALS_CONFIG_STORE_DATA_VERSION = 3; + + @IntDef(suffix = { "_VERSION" }, value = { + INITIAL_CONFIG_STORE_DATA_VERSION, + INTEGRITY_CONFIG_STORE_DATA_VERSION, + ENCRYPT_CREDENTIALS_CONFIG_STORE_DATA_VERSION + }) + @Retention(RetentionPolicy.SOURCE) + public @interface Version { } /** * Alarm tag to use for starting alarms for buffering file writes. @@ -291,8 +306,9 @@ public class WifiConfigStore { } } File file = new File(storeDir, STORE_ID_TO_FILE_NAME.get(fileId)); - DataIntegrityChecker dataIntegrityChecker = new DataIntegrityChecker(file.getName()); - return new StoreFile(file, fileId, dataIntegrityChecker); + WifiConfigStoreEncryptionUtil encryptionUtil = + new WifiConfigStoreEncryptionUtil(file.getName()); + return new StoreFile(file, fileId, encryptionUtil); } /** @@ -427,88 +443,18 @@ public class WifiConfigStore { final ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); out.setOutput(outputStream, StandardCharsets.UTF_8.name()); - // To compute integrity, write zeroes in the integrity fields. Once the integrity is - // computed, go back and modfiy the XML fields in place with the computed values. - writeDocumentMetadata(out, ZEROED_ENCRYPTED_DATA); + // First XML header. + XmlUtil.writeDocumentStart(out, XML_TAG_DOCUMENT_HEADER); + // Next version. + XmlUtil.writeNextValue(out, XML_TAG_VERSION, CURRENT_CONFIG_STORE_DATA_VERSION); for (StoreData storeData : storeDataList) { String tag = storeData.getName(); XmlUtil.writeNextSectionStart(out, tag); - storeData.serializeData(out); + storeData.serializeData(out, storeFile.getEncryptionUtil()); XmlUtil.writeNextSectionEnd(out, tag); } XmlUtil.writeDocumentEnd(out, XML_TAG_DOCUMENT_HEADER); - - byte[] outBytes = outputStream.toByteArray(); - EncryptedData encryptedData = storeFile.computeIntegrity(outBytes); - if (encryptedData == null) { - // should never happen, this is a fatal failure. Abort file write. - Log.wtf(TAG, "Failed to compute integrity, failing write"); - return null; - } - return rewriteDocumentMetadataRawBytes(outBytes, encryptedData); - } - - /** - * Helper method to write the metadata at the start of every config store file. - * The metadata consists of: - * a) Version - * b) Integrity data computed on the data contents. - */ - private void writeDocumentMetadata(XmlSerializer out, EncryptedData encryptedData) - throws XmlPullParserException, IOException { - // First XML header. - XmlUtil.writeDocumentStart(out, XML_TAG_DOCUMENT_HEADER); - // Next version. - XmlUtil.writeNextValue(out, XML_TAG_VERSION, CURRENT_CONFIG_STORE_DATA_VERSION); - - // Next integrity data. - XmlUtil.writeNextSectionStart(out, XML_TAG_HEADER_INTEGRITY); - XmlUtil.writeNextValue(out, XML_TAG_INTEGRITY_ENCRYPTED_DATA, - encryptedData.getEncryptedData()); - XmlUtil.writeNextValue(out, XML_TAG_INTEGRITY_IV, encryptedData.getIv()); - XmlUtil.writeNextSectionEnd(out, XML_TAG_HEADER_INTEGRITY); - } - - /** - * Helper method to generate the raw bytes containing the the metadata at the start of every - * config store file. - * - * NOTE: This does not create a fully formed XML document (the start tag is not closed for - * example). This only creates the top portion of the XML which contains the modified - * integrity data & version along with the XML prolog (metadata): - * <?xml version='1.0' encoding='utf-8' standalone='yes' ?> - * <WifiConfigStoreData> - * <int name="Version" value="2" /> - * <Integrity> - * <byte-array name="EncryptedData" num="48">!EncryptedData!</byte-array> - * <byte-array name="IV" num="12">!IV!</byte-array> - * </Integrity> - */ - private byte[] generateDocumentMetadataRawBytes(EncryptedData encryptedData) - throws XmlPullParserException, IOException { - final XmlSerializer outXml = new FastXmlSerializer(); - final ByteArrayOutputStream outStream = new ByteArrayOutputStream(); - outXml.setOutput(outStream, StandardCharsets.UTF_8.name()); - writeDocumentMetadata(outXml, encryptedData); - outXml.endDocument(); - return outStream.toByteArray(); - } - - /** - * Helper method to rewrite the existing document metadata in the incoming raw bytes in - * |inBytes| with the new document metadata created with the provided |encryptedData|. - * - * NOTE: This assumes that the metadata in existing XML inside |inBytes| aligns exactly - * with the new metadata created. So, a simple in place rewrite of the first few bytes ( - * corresponding to metadata section of the document) from |inBytes| will preserve the overall - * document structure. - */ - private byte[] rewriteDocumentMetadataRawBytes(byte[] inBytes, EncryptedData encryptedData) - throws XmlPullParserException, IOException { - byte[] replaceMetadataBytes = generateDocumentMetadataRawBytes(encryptedData); - ByteBuffer outByteBuffer = ByteBuffer.wrap(inBytes); - outByteBuffer.put(replaceMetadataBytes); - return outByteBuffer.array(); + return outputStream.toByteArray(); } /** @@ -629,10 +575,11 @@ public class WifiConfigStore { } // Inform all the provided store data clients that there is nothing in the store for them. - private void indicateNoDataForStoreDatas(Collection<StoreData> storeDataSet) + private void indicateNoDataForStoreDatas(Collection<StoreData> storeDataSet, + @Version int version, @NonNull WifiConfigStoreEncryptionUtil encryptionUtil) throws XmlPullParserException, IOException { for (StoreData storeData : storeDataSet) { - storeData.deserializeData(null, 0); + storeData.deserializeData(null, 0, version, encryptionUtil); } } @@ -654,7 +601,8 @@ public class WifiConfigStore { throws XmlPullParserException, IOException { List<StoreData> storeDataList = retrieveStoreDataListForStoreFile(storeFile); if (dataBytes == null) { - indicateNoDataForStoreDatas(storeDataList); + indicateNoDataForStoreDatas(storeDataList, -1 /* unknown */, + storeFile.getEncryptionUtil()); return; } final XmlPullParser in = Xml.newPullParser(); @@ -665,21 +613,10 @@ public class WifiConfigStore { int rootTagDepth = in.getDepth() + 1; XmlUtil.gotoDocumentStart(in, XML_TAG_DOCUMENT_HEADER); - int version = parseVersionFromXml(in); - // Version 2 onwards contains integrity data, so check the integrity of the file. - if (version >= INTEGRITY_CONFIG_STORE_DATA_VERSION) { - EncryptedData integrityData = parseIntegrityDataFromXml(in, rootTagDepth); - // To compute integrity, write zeroes in the integrity fields. - dataBytes = rewriteDocumentMetadataRawBytes(dataBytes, ZEROED_ENCRYPTED_DATA); - if (!storeFile.checkIntegrity(dataBytes, integrityData)) { - Log.wtf(TAG, "Integrity mismatch, discarding data from " + storeFile.mFileName); - return; - } - } else { - // When integrity checking is introduced. The existing data will have no related - // integrity file for validation. Thus, we will assume the existing data is correct. - // Integrity will be computed for the next write. - Log.d(TAG, "No integrity data to check; thus vacously true"); + @Version int version = parseVersionFromXml(in); + // Version 2 contains the now unused integrity data, parse & then discard the information. + if (version == INTEGRITY_CONFIG_STORE_DATA_VERSION) { + parseAndDiscardIntegrityDataFromXml(in, rootTagDepth); } String[] headerName = new String[1]; @@ -695,14 +632,15 @@ public class WifiConfigStore { throw new XmlPullParserException("Unknown store data: " + headerName[0] + ". List of store data: " + storeDataList); } - storeData.deserializeData(in, rootTagDepth + 1); + storeData.deserializeData(in, rootTagDepth + 1, version, + storeFile.getEncryptionUtil()); storeDatasInvoked.add(storeData); } // Inform all the other registered store data clients that there is nothing in the store // for them. Set<StoreData> storeDatasNotInvoked = new HashSet<>(storeDataList); storeDatasNotInvoked.removeAll(storeDatasInvoked); - indicateNoDataForStoreDatas(storeDatasNotInvoked); + indicateNoDataForStoreDatas(storeDatasNotInvoked, version, storeFile.getEncryptionUtil()); } /** @@ -712,7 +650,7 @@ public class WifiConfigStore { * @param in XmlPullParser instance pointing to the XML stream. * @return version number retrieved from the Xml stream. */ - private static int parseVersionFromXml(XmlPullParser in) + private static @Version int parseVersionFromXml(XmlPullParser in) throws XmlPullParserException, IOException { int version = (int) XmlUtil.readNextValueWithName(in, XML_TAG_VERSION); if (version < INITIAL_CONFIG_STORE_DATA_VERSION @@ -723,22 +661,16 @@ public class WifiConfigStore { } /** - * Parse the integrity data structure from the XML stream. - * This is used for both the shared and user config store data. + * Parse the integrity data structure from the XML stream and discard it. * * @param in XmlPullParser instance pointing to the XML stream. * @param outerTagDepth Outer tag depth. - * @return Instance of {@link EncryptedData} retrieved from the Xml stream. */ - private static @NonNull EncryptedData parseIntegrityDataFromXml( - XmlPullParser in, int outerTagDepth) + private static void parseAndDiscardIntegrityDataFromXml(XmlPullParser in, int outerTagDepth) throws XmlPullParserException, IOException { XmlUtil.gotoNextSectionWithName(in, XML_TAG_HEADER_INTEGRITY, outerTagDepth); - byte[] encryptedData = - (byte[]) XmlUtil.readNextValueWithName(in, XML_TAG_INTEGRITY_ENCRYPTED_DATA); - byte[] iv = - (byte[]) XmlUtil.readNextValueWithName(in, XML_TAG_INTEGRITY_IV); - return new EncryptedData(encryptedData, iv); + XmlUtil.readNextValueWithName(in, XML_TAG_INTEGRITY_ENCRYPTED_DATA); + XmlUtil.readNextValueWithName(in, XML_TAG_INTEGRITY_IV); } /** @@ -790,14 +722,14 @@ public class WifiConfigStore { /** * Integrity checking for the store file. */ - private final DataIntegrityChecker mDataIntegrityChecker; + private final WifiConfigStoreEncryptionUtil mEncryptionUtil; public StoreFile(File file, @StoreFileId int fileId, - @NonNull DataIntegrityChecker dataIntegrityChecker) { + @NonNull WifiConfigStoreEncryptionUtil encryptionUtil) { mAtomicFile = new AtomicFile(file); mFileName = file.getAbsolutePath(); mFileId = fileId; - mDataIntegrityChecker = dataIntegrityChecker; + mEncryptionUtil = encryptionUtil; } /** @@ -810,6 +742,13 @@ public class WifiConfigStore { } /** + * @return Returns the encryption util used for this store file. + */ + public @NonNull WifiConfigStoreEncryptionUtil getEncryptionUtil() { + return mEncryptionUtil; + } + + /** * Read the entire raw data from the store file and return in a byte array. * * @return raw data read from the file or null if the file is not found or the data has @@ -862,33 +801,6 @@ public class WifiConfigStore { // Reset the pending write data after write. mWriteData = null; } - - /** - * Compute integrity of |dataWithZeroedIntegrityFields| to be written to the file. - * - * @param dataWithZeroedIntegrityFields raw data to be written to the file with the - * integrity fields zeroed out for integrity - * calculation. - * @return Instance of {@link EncryptedData} holding the encrypted integrity data for the - * raw data to be written to the file. - */ - public EncryptedData computeIntegrity(byte[] dataWithZeroedIntegrityFields) { - return mDataIntegrityChecker.compute(dataWithZeroedIntegrityFields); - } - - /** - * Check integrity of |dataWithZeroedIntegrityFields| read from the file with the integrity - * data parsed from the file. - * @param dataWithZeroedIntegrityFields raw data read from the file with the integrity - * fields zeroed out for integrity calculation. - * @param parsedEncryptedData Instance of {@link EncryptedData} parsed from the integrity - * fields in the raw data. - * @return true if the integrity matches, false otherwise. - */ - public boolean checkIntegrity(byte[] dataWithZeroedIntegrityFields, - EncryptedData parsedEncryptedData) { - return mDataIntegrityChecker.isOk(dataWithZeroedIntegrityFields, parsedEncryptedData); - } } /** @@ -908,8 +820,9 @@ public class WifiConfigStore { * Serialize a XML data block to the output stream. * * @param out The output stream to serialize the data to + * @param encryptionUtil Utility to help encrypt any credential data. */ - void serializeData(XmlSerializer out) + void serializeData(XmlSerializer out, @NonNull WifiConfigStoreEncryptionUtil encryptionUtil) throws XmlPullParserException, IOException; /** @@ -918,10 +831,14 @@ public class WifiConfigStore { * @param in The input stream to read the data from. This could be null if there is * nothing in the store. * @param outerTagDepth The depth of the outer tag in the XML document + * @param version Version of config store file. + * @param encryptionUtil Utility to help decrypt any credential data. + * * Note: This will be invoked every time a store file is read, even if there is nothing * in the store for them. */ - void deserializeData(@Nullable XmlPullParser in, int outerTagDepth) + void deserializeData(@Nullable XmlPullParser in, int outerTagDepth, @Version int version, + @NonNull WifiConfigStoreEncryptionUtil encryptionUtil) throws XmlPullParserException, IOException; /** diff --git a/service/java/com/android/server/wifi/hotspot2/PasspointConfigSharedStoreData.java b/service/java/com/android/server/wifi/hotspot2/PasspointConfigSharedStoreData.java index 419ea7993..9abccb94b 100644 --- a/service/java/com/android/server/wifi/hotspot2/PasspointConfigSharedStoreData.java +++ b/service/java/com/android/server/wifi/hotspot2/PasspointConfigSharedStoreData.java @@ -16,7 +16,10 @@ package com.android.server.wifi.hotspot2; +import android.annotation.NonNull; + import com.android.server.wifi.WifiConfigStore; +import com.android.server.wifi.util.WifiConfigStoreEncryptionUtil; import com.android.server.wifi.util.XmlUtil; import org.xmlpull.v1.XmlPullParser; @@ -72,13 +75,16 @@ public class PasspointConfigSharedStoreData implements WifiConfigStore.StoreData } @Override - public void serializeData(XmlSerializer out) + public void serializeData(XmlSerializer out, + @NonNull WifiConfigStoreEncryptionUtil encryptionUtil) throws XmlPullParserException, IOException { serializeShareData(out); } @Override - public void deserializeData(XmlPullParser in, int outerTagDepth) + public void deserializeData(XmlPullParser in, int outerTagDepth, + @WifiConfigStore.Version int version, + @NonNull WifiConfigStoreEncryptionUtil encryptionUtil) throws XmlPullParserException, IOException { // Ignore empty reads. if (in == null) { diff --git a/service/java/com/android/server/wifi/hotspot2/PasspointConfigUserStoreData.java b/service/java/com/android/server/wifi/hotspot2/PasspointConfigUserStoreData.java index 0114cfb21..1001b1189 100644 --- a/service/java/com/android/server/wifi/hotspot2/PasspointConfigUserStoreData.java +++ b/service/java/com/android/server/wifi/hotspot2/PasspointConfigUserStoreData.java @@ -16,6 +16,7 @@ package com.android.server.wifi.hotspot2; +import android.annotation.NonNull; import android.net.wifi.hotspot2.PasspointConfiguration; import android.text.TextUtils; @@ -23,6 +24,7 @@ import com.android.internal.util.XmlUtils; import com.android.server.wifi.SIMAccessor; import com.android.server.wifi.WifiConfigStore; import com.android.server.wifi.WifiKeyStore; +import com.android.server.wifi.util.WifiConfigStoreEncryptionUtil; import com.android.server.wifi.util.XmlUtil; import org.xmlpull.v1.XmlPullParser; @@ -103,13 +105,16 @@ public class PasspointConfigUserStoreData implements WifiConfigStore.StoreData { } @Override - public void serializeData(XmlSerializer out) + public void serializeData(XmlSerializer out, + @NonNull WifiConfigStoreEncryptionUtil encryptionUtil) throws XmlPullParserException, IOException { serializeUserData(out); } @Override - public void deserializeData(XmlPullParser in, int outerTagDepth) + public void deserializeData(XmlPullParser in, int outerTagDepth, + @WifiConfigStore.Version int version, + @NonNull WifiConfigStoreEncryptionUtil encryptionUtil) throws XmlPullParserException, IOException { // Ignore empty reads. if (in == null) { diff --git a/service/java/com/android/server/wifi/util/DataIntegrityChecker.java b/service/java/com/android/server/wifi/util/WifiConfigStoreEncryptionUtil.java index 6f03a4861..2f9b08f2b 100644 --- a/service/java/com/android/server/wifi/util/DataIntegrityChecker.java +++ b/service/java/com/android/server/wifi/util/WifiConfigStoreEncryptionUtil.java @@ -17,22 +17,22 @@ package com.android.server.wifi.util; import android.annotation.NonNull; +import android.annotation.Nullable; +import android.os.Process; import android.os.SystemProperties; +import android.security.keystore.AndroidKeyStoreProvider; import android.security.keystore.KeyGenParameterSpec; import android.security.keystore.KeyProperties; import android.text.TextUtils; import android.util.Log; -import java.io.IOException; import java.security.InvalidAlgorithmParameterException; import java.security.InvalidKeyException; import java.security.KeyStore; import java.security.KeyStoreException; -import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.security.NoSuchProviderException; import java.security.UnrecoverableEntryException; -import java.security.cert.CertificateException; import javax.crypto.BadPaddingException; import javax.crypto.Cipher; @@ -43,34 +43,26 @@ import javax.crypto.SecretKey; import javax.crypto.spec.GCMParameterSpec; /** - * Tools to provide integrity checking of byte arrays based on NIAP Common Criteria Protection - * Profile <a href="https://www.niap-ccevs.org/MMO/PP/-417-/#FCS_STG_EXT.3.1">FCS_STG_EXT.3.1</a>. + * Tools to help encrypt/decrypt */ -public class DataIntegrityChecker { - private static final String TAG = "DataIntegrityChecker"; +public class WifiConfigStoreEncryptionUtil { + private static final String TAG = "WifiConfigStoreEncryptionUtil"; - private static final String ALIAS_SUFFIX = ".data-integrity-checker-key"; + private static final String ALIAS_SUFFIX = ".data-encryption-key"; private static final String CIPHER_ALGORITHM = "AES/GCM/NoPadding"; - private static final String DIGEST_ALGORITHM = "SHA-256"; private static final int GCM_TAG_LENGTH = 128; private static final String KEY_STORE = "AndroidKeyStore"; - /** - * When KEYSTORE_FAILURE_RETURN_VALUE is true, all cryptographic operation failures will not - * enforce security and {@link #isOk(byte[], EncryptedData)} always return true. - */ - private static final boolean KEYSTORE_FAILURE_RETURN_VALUE = true; - private final String mDataFileName; /** - * Construct a new integrity checker to update and check if/when a data file was altered - * outside expected conditions. + * Construct a new util to help {@link com.android.server.wifi.WifiConfigStore.StoreData} + * modules to encrypt/decrypt credential data written/read from this config store file. * - * @param dataFileName The full path of the data file for which integrity check is performed. + * @param dataFileName The full path of the data file. * @throws NullPointerException When data file is empty string. */ - public DataIntegrityChecker(@NonNull String dataFileName) { + public WifiConfigStoreEncryptionUtil(@NonNull String dataFileName) { if (TextUtils.isEmpty(dataFileName)) { throw new NullPointerException("dataFileName must not be null or the empty " + "string"); @@ -83,80 +75,16 @@ public class DataIntegrityChecker { } /** - * Computes a digest of a byte array, encrypt it, and store the result - * - * Call this method immediately before storing the byte array - * - * @param data The data desired to ensure integrity - * @return Instance of {@link EncryptedData} containing the encrypted integrity data. - */ - public EncryptedData compute(byte[] data) { - if (data == null || data.length < 1) { - reportException(new Exception("No data to compute"), "No data to compute."); - return null; - } - byte[] digest = getDigest(data); - if (digest == null || digest.length < 1) { - reportException(new Exception("digest null in compute"), - "digest null in compute"); - return null; - } - EncryptedData integrityData = encrypt(digest, getKeyAlias()); - if (integrityData == null) { - reportException(new Exception("integrityData null in compute"), - "integrityData null in compute"); - } - return integrityData; - } - - - /** - * Check the integrity of a given byte array - * - * Call this method immediately before trusting the byte array. This method will return false - * when the integrity data calculated on the byte array does not match the encrypted integrity - * data provided to compare or if there is an underlying issue with the cryptographic functions - * or the key store. + * Encrypt the provided data blob. * - * @param data The data to check if its been altered. - * @param integrityData Encrypted integrity data to be used for comparison. - * @return true if the integrity data computed on |data| matches the provided |integrityData|. + * @param data Data blob to be encrypted. + * @return Instance of {@link EncryptedData} containing the encrypted info. */ - public boolean isOk(@NonNull byte[] data, @NonNull EncryptedData integrityData) { - if (data == null || data.length < 1) { - return KEYSTORE_FAILURE_RETURN_VALUE; - } - byte[] currentDigest = getDigest(data); - if (currentDigest == null || currentDigest.length < 1) { - reportException(new Exception("current digest null"), "current digest null"); - return KEYSTORE_FAILURE_RETURN_VALUE; - } - if (integrityData == null) { - reportException(new Exception("integrityData null in isOk"), - "integrityData null in isOk"); - return KEYSTORE_FAILURE_RETURN_VALUE; - } - byte[] storedDigest = decrypt(integrityData, getKeyAlias()); - if (storedDigest == null) { - return KEYSTORE_FAILURE_RETURN_VALUE; - } - return constantTimeEquals(storedDigest, currentDigest); - } - - private byte[] getDigest(byte[] data) { - try { - return MessageDigest.getInstance(DIGEST_ALGORITHM).digest(data); - } catch (NoSuchAlgorithmException e) { - reportException(e, "getDigest could not find algorithm: " + DIGEST_ALGORITHM); - return null; - } - } - - private EncryptedData encrypt(byte[] data, String keyAlias) { + public @Nullable EncryptedData encrypt(byte[] data) { EncryptedData encryptedData = null; try { Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM); - SecretKey secretKeyReference = getOrCreateSecretKey(keyAlias); + SecretKey secretKeyReference = getOrCreateSecretKey(getKeyAlias()); if (secretKeyReference != null) { cipher.init(Cipher.ENCRYPT_MODE, secretKeyReference); encryptedData = new EncryptedData(cipher.doFinal(data), cipher.getIV()); @@ -178,12 +106,18 @@ public class DataIntegrityChecker { return encryptedData; } - private byte[] decrypt(EncryptedData encryptedData, String keyAlias) { + /** + * Decrypt the original data blob from the provided {@link EncryptedData}. + * + * @param encryptedData Instance of {@link EncryptedData} containing the encrypted info. + * @return Original data blob that was encrypted. + */ + public @Nullable byte[] decrypt(@NonNull EncryptedData encryptedData) { byte[] decryptedData = null; try { Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM); GCMParameterSpec spec = new GCMParameterSpec(GCM_TAG_LENGTH, encryptedData.getIv()); - SecretKey secretKeyReference = getOrCreateSecretKey(keyAlias); + SecretKey secretKeyReference = getOrCreateSecretKey(getKeyAlias()); if (secretKeyReference != null) { cipher.init(Cipher.DECRYPT_MODE, secretKeyReference, spec); decryptedData = cipher.doFinal(encryptedData.getEncryptedData()); @@ -207,8 +141,7 @@ public class DataIntegrityChecker { private SecretKey getOrCreateSecretKey(String keyAlias) { SecretKey secretKey = null; try { - KeyStore keyStore = KeyStore.getInstance(KEY_STORE); - keyStore.load(null); + KeyStore keyStore = AndroidKeyStoreProvider.getKeyStoreForUid(Process.WIFI_UID); if (keyStore.containsAlias(keyAlias)) { // The key exists in key store. Get the key. KeyStore.SecretKeyEntry secretKeyEntry = (KeyStore.SecretKeyEntry) keyStore .getEntry(keyAlias, null); @@ -227,17 +160,14 @@ public class DataIntegrityChecker { KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT) .setBlockModes(KeyProperties.BLOCK_MODE_GCM) .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE) + .setUid(Process.WIFI_UID) .build(); keyGenerator.init(keyGenParameterSpec); secretKey = keyGenerator.generateKey(); } - } catch (CertificateException e) { - reportException(e, "getOrCreateSecretKey had a certificate exception."); } catch (InvalidAlgorithmParameterException e) { reportException(e, "getOrCreateSecretKey had an invalid algorithm parameter"); - } catch (IOException e) { - reportException(e, "getOrCreateSecretKey had an IO exception."); } catch (KeyStoreException e) { reportException(e, "getOrCreateSecretKey cannot find the keystore: " + KEY_STORE); } catch (NoSuchAlgorithmException e) { @@ -250,22 +180,6 @@ public class DataIntegrityChecker { return secretKey; } - private boolean constantTimeEquals(byte[] a, byte[] b) { - if (a == null && b == null) { - return true; - } - - if (a == null || b == null || a.length != b.length) { - return false; - } - - byte differenceAccumulator = 0; - for (int i = 0; i < a.length; ++i) { - differenceAccumulator |= a[i] ^ b[i]; - } - return (differenceAccumulator == 0); - } - /* TODO(b/128526030): Remove this error reporting code upon resolving the bug. */ private static final boolean REQUEST_BUG_REPORT = false; private void reportException(Exception exception, String error) { @@ -275,4 +189,5 @@ public class DataIntegrityChecker { SystemProperties.set("ctl.start", "bugreport"); } } + } |