diff options
| author | 2017-01-23 14:15:54 -0800 | |
|---|---|---|
| committer | 2017-01-23 19:06:41 -0800 | |
| commit | 7599f1366e8a08781f415e73f65cf270aae36868 (patch) | |
| tree | e325af936fa989f2fe2762453c7a5be2dacc2dc7 | |
| parent | ea617597c78e3a28304a85cfb3f790e1e36bca76 (diff) | |
Resolve Android security comments for Android ID migration.
- Use 32 byte key instead of 16 byte.
- Use HMAC-SHA256 instead of SHA256 for ssaid generation.
- Update HMAC with all package signatures.
- Use delimiter in between digest arguments.
This change will cause the ssaid of non-legacy installed apps (apps installed
post Android ID migration OTA) to change after an uninstall and reinstall sequence.
Bug: 34395671
Test: Unit tests, CTS tests, Manual tests
Change-Id: I19dec57947368ee5000c2c630b1e4030d46a4ab3
3 files changed, 39 insertions, 19 deletions
diff --git a/core/java/android/util/ByteStringUtils.java b/core/java/android/util/ByteStringUtils.java index 7103e6da0625..333208db5f79 100644 --- a/core/java/android/util/ByteStringUtils.java +++ b/core/java/android/util/ByteStringUtils.java @@ -33,7 +33,7 @@ public final class ByteStringUtils { * @param bytes Byte array to encode. * @return Hex encoded string representation of bytes. */ - public static String toString(byte[] bytes) { + public static String toHexString(byte[] bytes) { if (bytes == null || bytes.length == 0 || bytes.length % 2 != 0) { return null; } @@ -55,7 +55,7 @@ public final class ByteStringUtils { * @param str Hex encoded string to decode. * @return Decoded byte array representation of str. */ - public static byte[] toByteArray(String str) { + public static byte[] fromHexToByteArray(String str) { if (str == null || str.length() == 0 || str.length() % 2 != 0) { return null; } diff --git a/core/java/android/util/PackageUtils.java b/core/java/android/util/PackageUtils.java index 31819797ea17..0fe56f6efa21 100644 --- a/core/java/android/util/PackageUtils.java +++ b/core/java/android/util/PackageUtils.java @@ -80,6 +80,6 @@ public final class PackageUtils { messageDigest.update(data); - return ByteStringUtils.toString(messageDigest.digest()); + return ByteStringUtils.toHexString(messageDigest.digest()); } } diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java index a33ab162debf..cbbb8094d06d 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java @@ -78,16 +78,20 @@ import java.io.FileDescriptor; import java.io.FileNotFoundException; import java.io.PrintWriter; import java.nio.charset.StandardCharsets; -import java.security.MessageDigest; +import java.nio.ByteBuffer; +import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; import java.security.SecureRandom; import java.util.ArrayList; import java.util.Arrays; import java.util.HashSet; import java.util.List; +import java.util.Locale; import java.util.Map; import java.util.Set; import java.util.regex.Pattern; +import javax.crypto.Mac; +import javax.crypto.spec.SecretKeySpec; import static android.os.Process.ROOT_UID; import static android.os.Process.SYSTEM_UID; @@ -973,7 +977,7 @@ public class SettingsProvider extends ContentProvider { continue; } - // As of Android O (API 24), the SSAID is read from an app-specific entry in table + // As of Android O, the SSAID is read from an app-specific entry in table // SETTINGS_FILE_SSAID, unless accessed by a system process. final Setting setting; if (isNewSsaidSetting(name)) { @@ -1009,7 +1013,7 @@ public class SettingsProvider extends ContentProvider { // Get the value. synchronized (mLock) { - // As of Android O (API 24), the SSAID is read from an app-specific entry in table + // As of Android O, the SSAID is read from an app-specific entry in table // SETTINGS_FILE_SSAID, unless accessed by a system process. if (isNewSsaidSetting(name)) { return getSsaidSettingLocked(owningUserId); @@ -1895,12 +1899,12 @@ public class SettingsProvider extends ContentProvider { private void generateUserKeyLocked(int userId) { // Generate a random key for each user used for creating a new ssaid. - final byte[] keyBytes = new byte[16]; + final byte[] keyBytes = new byte[32]; final SecureRandom rand = new SecureRandom(); rand.nextBytes(keyBytes); // Convert to string for storage in settings table. - final String userKey = ByteStringUtils.toString(keyBytes); + final String userKey = ByteStringUtils.toHexString(keyBytes); // Store the key in the ssaid table. final SettingsState ssaidSettings = getSettingsLocked(SETTINGS_TYPE_SSAID, userId); @@ -1912,6 +1916,10 @@ public class SettingsProvider extends ContentProvider { } } + private byte[] getLengthPrefix(byte[] data) { + return ByteBuffer.allocate(4).putInt(data.length).array(); + } + public Setting generateSsaidLocked(String packageName, int userId) { final PackageInfo packageInfo; try { @@ -1936,25 +1944,37 @@ public class SettingsProvider extends ContentProvider { final String userKey = userKeySetting.getValue(); // Convert the user's key back to a byte array. - final byte[] keyBytes = ByteStringUtils.toByteArray(userKey); - if (keyBytes == null || keyBytes.length != 16) { + final byte[] keyBytes = ByteStringUtils.fromHexToByteArray(userKey); + + // Validate that the key is of expected length. + // Keys are currently 32 bytes, but were once 16 bytes during Android O development. + if (keyBytes == null || (keyBytes.length != 16 && keyBytes.length != 32)) { throw new IllegalStateException("User key invalid"); } - final MessageDigest md; + final Mac m; try { - // Hash package name and signature. - md = MessageDigest.getInstance("SHA-256"); + m = Mac.getInstance("HmacSHA256"); + m.init(new SecretKeySpec(keyBytes, m.getAlgorithm())); } catch (NoSuchAlgorithmException e) { - throw new IllegalStateException("HmacSHA256 is not available"); + throw new IllegalStateException("HmacSHA256 is not available", e); + } catch (InvalidKeyException e) { + throw new IllegalStateException("Key is corrupted", e); + } + + // Mac the package name and each of the signatures. + byte[] packageNameBytes = packageInfo.packageName.getBytes(StandardCharsets.UTF_8); + m.update(getLengthPrefix(packageNameBytes), 0, 4); + m.update(packageNameBytes); + for (int i = 0; i < packageInfo.signatures.length; i++) { + byte[] sig = packageInfo.signatures[i].toByteArray(); + m.update(getLengthPrefix(sig), 0, 4); + m.update(sig); } - md.update(keyBytes); - md.update(packageInfo.packageName.getBytes(StandardCharsets.UTF_8)); - md.update(packageInfo.signatures[0].toByteArray()); // Convert result to a string for storage in settings table. Only want first 64 bits. - final String ssaid = ByteStringUtils.toString(md.digest()).substring(0, 16) - .toLowerCase(); + final String ssaid = ByteStringUtils.toHexString(m.doFinal()).substring(0, 16) + .toLowerCase(Locale.US); // Save the ssaid in the ssaid table. final String uid = Integer.toString(packageInfo.applicationInfo.uid); |