Loading cmds/statsd/src/atoms.proto +42 −0 Original line number Diff line number Diff line Loading @@ -181,6 +181,7 @@ message Atom { DocsUISearchTypeReported docs_ui_search_type_reported = 120; DataStallEvent data_stall_event = 121; RescuePartyResetReported rescue_party_reset_reported = 122; SignedConfigReported signed_config_reported = 123; } // Pulled events will start at field 10000. Loading Loading @@ -3859,3 +3860,44 @@ message RescuePartyResetReported { // The rescue level of this reset. A value of 0 indicates missing or unknown level information. optional int32 rescue_level = 1; } /** * Logs when signed config is received from an APK, and if that config was applied successfully. * Logged from: * frameworks/base/services/core/java/com/android/server/signedconfig/SignedConfigService.java */ message SignedConfigReported { enum Type { UNKNOWN_TYPE = 0; GLOBAL_SETTINGS = 1; } optional Type type = 1; // The final status of the signed config received. enum Status { UNKNOWN_STATUS = 0; APPLIED = 1; BASE64_FAILURE_CONFIG = 2; BASE64_FAILURE_SIGNATURE = 3; SECURITY_EXCEPTION = 4; INVALID_CONFIG = 5; OLD_CONFIG = 6; SIGNATURE_CHECK_FAILED = 7; NOT_APPLICABLE = 8; } optional Status status = 2; // The version of the signed config processed. optional int32 version = 3; // The package name that the config was extracted from. optional string from_package = 4; enum Key { NO_KEY = 0; DEBUG = 1; PRODUCTION = 2; } // Which key was used to verify the config. optional Key verified_with = 5; } services/core/java/com/android/server/signedconfig/GlobalSettingsConfigApplicator.java +11 −2 Original line number Diff line number Diff line Loading @@ -23,6 +23,7 @@ import android.provider.Settings; import android.util.ArrayMap; import android.util.ArraySet; import android.util.Slog; import android.util.StatsLog; import java.security.GeneralSecurityException; import java.util.Arrays; Loading Loading @@ -66,12 +67,14 @@ class GlobalSettingsConfigApplicator { private final Context mContext; private final String mSourcePackage; private final SignedConfigEvent mEvent; private final SignatureVerifier mVerifier; GlobalSettingsConfigApplicator(Context context, String sourcePackage) { GlobalSettingsConfigApplicator(Context context, String sourcePackage, SignedConfigEvent event) { mContext = context; mSourcePackage = sourcePackage; mVerifier = new SignatureVerifier(); mEvent = event; mVerifier = new SignatureVerifier(mEvent); } private boolean checkSignature(String data, String signature) { Loading @@ -79,6 +82,7 @@ class GlobalSettingsConfigApplicator { return mVerifier.verifySignature(data, signature); } catch (GeneralSecurityException e) { Slog.e(TAG, "Failed to verify signature", e); mEvent.status = StatsLog.SIGNED_CONFIG_REPORTED__STATUS__SECURITY_EXCEPTION; return false; } } Loading Loading @@ -109,14 +113,17 @@ class GlobalSettingsConfigApplicator { SignedConfig config; try { config = SignedConfig.parse(configStr, ALLOWED_KEYS, KEY_VALUE_MAPPERS); mEvent.version = config.version; } catch (InvalidConfigException e) { Slog.e(TAG, "Failed to parse global settings from package " + mSourcePackage, e); mEvent.status = StatsLog.SIGNED_CONFIG_REPORTED__STATUS__INVALID_CONFIG; return; } int currentVersion = getCurrentConfigVersion(); if (currentVersion >= config.version) { Slog.i(TAG, "Global settings from package " + mSourcePackage + " is older than existing: " + config.version + "<=" + currentVersion); mEvent.status = StatsLog.SIGNED_CONFIG_REPORTED__STATUS__OLD_CONFIG; return; } // We have new config! Loading @@ -126,10 +133,12 @@ class GlobalSettingsConfigApplicator { config.getMatchingConfig(Build.VERSION.SDK_INT); if (matchedConfig == null) { Slog.i(TAG, "Settings is not applicable to current SDK version; ignoring"); mEvent.status = StatsLog.SIGNED_CONFIG_REPORTED__STATUS__NOT_APPLICABLE; return; } Slog.i(TAG, "Updating global settings to version " + config.version); updateCurrentConfig(config.version, matchedConfig.values); mEvent.status = StatsLog.SIGNED_CONFIG_REPORTED__STATUS__APPLIED; } } services/core/java/com/android/server/signedconfig/SignatureVerifier.java +7 −1 Original line number Diff line number Diff line Loading @@ -18,6 +18,7 @@ package com.android.server.signedconfig; import android.os.Build; import android.util.Slog; import android.util.StatsLog; import java.nio.charset.StandardCharsets; import java.security.InvalidKeyException; Loading @@ -43,9 +44,11 @@ public class SignatureVerifier { "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEaAn2XVifsLTHg616nTsOMVmlhBoECGbTEBTKKvdd2hO60" + "pj1pnU8SMkhYfaNxZuKgw9LNvOwlFwStboIYeZ3lQ=="; private final SignedConfigEvent mEvent; private final PublicKey mDebugKey; public SignatureVerifier() { public SignatureVerifier(SignedConfigEvent event) { mEvent = event; mDebugKey = createKey(DEBUG_KEY); } Loading Loading @@ -80,6 +83,7 @@ public class SignatureVerifier { try { signature = Base64.getDecoder().decode(base64Signature); } catch (IllegalArgumentException e) { mEvent.status = StatsLog.SIGNED_CONFIG_REPORTED__STATUS__BASE64_FAILURE_SIGNATURE; Slog.e(TAG, "Failed to base64 decode signature"); return false; } Loading @@ -94,6 +98,7 @@ public class SignatureVerifier { verifier.update(data); if (verifier.verify(signature)) { Slog.i(TAG, "Verified config using debug key"); mEvent.verifiedWith = StatsLog.SIGNED_CONFIG_REPORTED__VERIFIED_WITH__DEBUG; return true; } else { if (DBG) Slog.i(TAG, "Config verification failed using debug key"); Loading @@ -104,6 +109,7 @@ public class SignatureVerifier { } // TODO verify production key. Slog.w(TAG, "NO PRODUCTION KEY YET, FAILING VERIFICATION"); mEvent.status = StatsLog.SIGNED_CONFIG_REPORTED__STATUS__SIGNATURE_CHECK_FAILED; return false; } } services/core/java/com/android/server/signedconfig/SignedConfigEvent.java 0 → 100644 +39 −0 Original line number Diff line number Diff line /* * Copyright (C) 2019 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.server.signedconfig; import android.util.StatsLog; /** * Helper class to allow a SignedConfigReported event to be built up in stages. */ public class SignedConfigEvent { public int type = StatsLog.SIGNED_CONFIG_REPORTED__TYPE__UNKNOWN_TYPE; public int status = StatsLog.SIGNED_CONFIG_REPORTED__STATUS__UNKNOWN_STATUS; public int version = 0; public String fromPackage = null; public int verifiedWith = StatsLog.SIGNED_CONFIG_REPORTED__VERIFIED_WITH__NO_KEY; /** * Write this event to statslog. */ public void send() { StatsLog.write(StatsLog.SIGNED_CONFIG_REPORTED, type, status, version, fromPackage, verifiedWith); } } services/core/java/com/android/server/signedconfig/SignedConfigService.java +23 −13 Original line number Diff line number Diff line Loading @@ -26,6 +26,7 @@ import android.content.pm.PackageManagerInternal; import android.net.Uri; import android.os.Bundle; import android.util.Slog; import android.util.StatsLog; import com.android.server.LocalServices; Loading Loading @@ -82,21 +83,30 @@ public class SignedConfigService { } if (metaData.containsKey(KEY_GLOBAL_SETTINGS) && metaData.containsKey(KEY_GLOBAL_SETTINGS_SIGNATURE)) { SignedConfigEvent event = new SignedConfigEvent(); try { event.type = StatsLog.SIGNED_CONFIG_REPORTED__TYPE__GLOBAL_SETTINGS; event.fromPackage = packageName; String config = metaData.getString(KEY_GLOBAL_SETTINGS); String signature = metaData.getString(KEY_GLOBAL_SETTINGS_SIGNATURE); try { // Base64 encoding is standard (not URL safe) encoding: RFC4648 config = new String(Base64.getDecoder().decode(config), StandardCharsets.UTF_8); } catch (IllegalArgumentException iae) { Slog.e(TAG, "Failed to base64 decode global settings config from " + packageName); Slog.e(TAG, "Failed to base64 decode global settings config from " + packageName); event.status = StatsLog.SIGNED_CONFIG_REPORTED__STATUS__BASE64_FAILURE_CONFIG; return; } if (DBG) { Slog.d(TAG, "Got global settings config: " + config); Slog.d(TAG, "Got global settings signature: " + signature); } new GlobalSettingsConfigApplicator(mContext, packageName).applyConfig( new GlobalSettingsConfigApplicator(mContext, packageName, event).applyConfig( config, signature); } finally { event.send(); } } else { if (DBG) Slog.d(TAG, "Package has no global settings config/signature."); } Loading Loading
cmds/statsd/src/atoms.proto +42 −0 Original line number Diff line number Diff line Loading @@ -181,6 +181,7 @@ message Atom { DocsUISearchTypeReported docs_ui_search_type_reported = 120; DataStallEvent data_stall_event = 121; RescuePartyResetReported rescue_party_reset_reported = 122; SignedConfigReported signed_config_reported = 123; } // Pulled events will start at field 10000. Loading Loading @@ -3859,3 +3860,44 @@ message RescuePartyResetReported { // The rescue level of this reset. A value of 0 indicates missing or unknown level information. optional int32 rescue_level = 1; } /** * Logs when signed config is received from an APK, and if that config was applied successfully. * Logged from: * frameworks/base/services/core/java/com/android/server/signedconfig/SignedConfigService.java */ message SignedConfigReported { enum Type { UNKNOWN_TYPE = 0; GLOBAL_SETTINGS = 1; } optional Type type = 1; // The final status of the signed config received. enum Status { UNKNOWN_STATUS = 0; APPLIED = 1; BASE64_FAILURE_CONFIG = 2; BASE64_FAILURE_SIGNATURE = 3; SECURITY_EXCEPTION = 4; INVALID_CONFIG = 5; OLD_CONFIG = 6; SIGNATURE_CHECK_FAILED = 7; NOT_APPLICABLE = 8; } optional Status status = 2; // The version of the signed config processed. optional int32 version = 3; // The package name that the config was extracted from. optional string from_package = 4; enum Key { NO_KEY = 0; DEBUG = 1; PRODUCTION = 2; } // Which key was used to verify the config. optional Key verified_with = 5; }
services/core/java/com/android/server/signedconfig/GlobalSettingsConfigApplicator.java +11 −2 Original line number Diff line number Diff line Loading @@ -23,6 +23,7 @@ import android.provider.Settings; import android.util.ArrayMap; import android.util.ArraySet; import android.util.Slog; import android.util.StatsLog; import java.security.GeneralSecurityException; import java.util.Arrays; Loading Loading @@ -66,12 +67,14 @@ class GlobalSettingsConfigApplicator { private final Context mContext; private final String mSourcePackage; private final SignedConfigEvent mEvent; private final SignatureVerifier mVerifier; GlobalSettingsConfigApplicator(Context context, String sourcePackage) { GlobalSettingsConfigApplicator(Context context, String sourcePackage, SignedConfigEvent event) { mContext = context; mSourcePackage = sourcePackage; mVerifier = new SignatureVerifier(); mEvent = event; mVerifier = new SignatureVerifier(mEvent); } private boolean checkSignature(String data, String signature) { Loading @@ -79,6 +82,7 @@ class GlobalSettingsConfigApplicator { return mVerifier.verifySignature(data, signature); } catch (GeneralSecurityException e) { Slog.e(TAG, "Failed to verify signature", e); mEvent.status = StatsLog.SIGNED_CONFIG_REPORTED__STATUS__SECURITY_EXCEPTION; return false; } } Loading Loading @@ -109,14 +113,17 @@ class GlobalSettingsConfigApplicator { SignedConfig config; try { config = SignedConfig.parse(configStr, ALLOWED_KEYS, KEY_VALUE_MAPPERS); mEvent.version = config.version; } catch (InvalidConfigException e) { Slog.e(TAG, "Failed to parse global settings from package " + mSourcePackage, e); mEvent.status = StatsLog.SIGNED_CONFIG_REPORTED__STATUS__INVALID_CONFIG; return; } int currentVersion = getCurrentConfigVersion(); if (currentVersion >= config.version) { Slog.i(TAG, "Global settings from package " + mSourcePackage + " is older than existing: " + config.version + "<=" + currentVersion); mEvent.status = StatsLog.SIGNED_CONFIG_REPORTED__STATUS__OLD_CONFIG; return; } // We have new config! Loading @@ -126,10 +133,12 @@ class GlobalSettingsConfigApplicator { config.getMatchingConfig(Build.VERSION.SDK_INT); if (matchedConfig == null) { Slog.i(TAG, "Settings is not applicable to current SDK version; ignoring"); mEvent.status = StatsLog.SIGNED_CONFIG_REPORTED__STATUS__NOT_APPLICABLE; return; } Slog.i(TAG, "Updating global settings to version " + config.version); updateCurrentConfig(config.version, matchedConfig.values); mEvent.status = StatsLog.SIGNED_CONFIG_REPORTED__STATUS__APPLIED; } }
services/core/java/com/android/server/signedconfig/SignatureVerifier.java +7 −1 Original line number Diff line number Diff line Loading @@ -18,6 +18,7 @@ package com.android.server.signedconfig; import android.os.Build; import android.util.Slog; import android.util.StatsLog; import java.nio.charset.StandardCharsets; import java.security.InvalidKeyException; Loading @@ -43,9 +44,11 @@ public class SignatureVerifier { "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEaAn2XVifsLTHg616nTsOMVmlhBoECGbTEBTKKvdd2hO60" + "pj1pnU8SMkhYfaNxZuKgw9LNvOwlFwStboIYeZ3lQ=="; private final SignedConfigEvent mEvent; private final PublicKey mDebugKey; public SignatureVerifier() { public SignatureVerifier(SignedConfigEvent event) { mEvent = event; mDebugKey = createKey(DEBUG_KEY); } Loading Loading @@ -80,6 +83,7 @@ public class SignatureVerifier { try { signature = Base64.getDecoder().decode(base64Signature); } catch (IllegalArgumentException e) { mEvent.status = StatsLog.SIGNED_CONFIG_REPORTED__STATUS__BASE64_FAILURE_SIGNATURE; Slog.e(TAG, "Failed to base64 decode signature"); return false; } Loading @@ -94,6 +98,7 @@ public class SignatureVerifier { verifier.update(data); if (verifier.verify(signature)) { Slog.i(TAG, "Verified config using debug key"); mEvent.verifiedWith = StatsLog.SIGNED_CONFIG_REPORTED__VERIFIED_WITH__DEBUG; return true; } else { if (DBG) Slog.i(TAG, "Config verification failed using debug key"); Loading @@ -104,6 +109,7 @@ public class SignatureVerifier { } // TODO verify production key. Slog.w(TAG, "NO PRODUCTION KEY YET, FAILING VERIFICATION"); mEvent.status = StatsLog.SIGNED_CONFIG_REPORTED__STATUS__SIGNATURE_CHECK_FAILED; return false; } }
services/core/java/com/android/server/signedconfig/SignedConfigEvent.java 0 → 100644 +39 −0 Original line number Diff line number Diff line /* * Copyright (C) 2019 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.server.signedconfig; import android.util.StatsLog; /** * Helper class to allow a SignedConfigReported event to be built up in stages. */ public class SignedConfigEvent { public int type = StatsLog.SIGNED_CONFIG_REPORTED__TYPE__UNKNOWN_TYPE; public int status = StatsLog.SIGNED_CONFIG_REPORTED__STATUS__UNKNOWN_STATUS; public int version = 0; public String fromPackage = null; public int verifiedWith = StatsLog.SIGNED_CONFIG_REPORTED__VERIFIED_WITH__NO_KEY; /** * Write this event to statslog. */ public void send() { StatsLog.write(StatsLog.SIGNED_CONFIG_REPORTED, type, status, version, fromPackage, verifiedWith); } }
services/core/java/com/android/server/signedconfig/SignedConfigService.java +23 −13 Original line number Diff line number Diff line Loading @@ -26,6 +26,7 @@ import android.content.pm.PackageManagerInternal; import android.net.Uri; import android.os.Bundle; import android.util.Slog; import android.util.StatsLog; import com.android.server.LocalServices; Loading Loading @@ -82,21 +83,30 @@ public class SignedConfigService { } if (metaData.containsKey(KEY_GLOBAL_SETTINGS) && metaData.containsKey(KEY_GLOBAL_SETTINGS_SIGNATURE)) { SignedConfigEvent event = new SignedConfigEvent(); try { event.type = StatsLog.SIGNED_CONFIG_REPORTED__TYPE__GLOBAL_SETTINGS; event.fromPackage = packageName; String config = metaData.getString(KEY_GLOBAL_SETTINGS); String signature = metaData.getString(KEY_GLOBAL_SETTINGS_SIGNATURE); try { // Base64 encoding is standard (not URL safe) encoding: RFC4648 config = new String(Base64.getDecoder().decode(config), StandardCharsets.UTF_8); } catch (IllegalArgumentException iae) { Slog.e(TAG, "Failed to base64 decode global settings config from " + packageName); Slog.e(TAG, "Failed to base64 decode global settings config from " + packageName); event.status = StatsLog.SIGNED_CONFIG_REPORTED__STATUS__BASE64_FAILURE_CONFIG; return; } if (DBG) { Slog.d(TAG, "Got global settings config: " + config); Slog.d(TAG, "Got global settings signature: " + signature); } new GlobalSettingsConfigApplicator(mContext, packageName).applyConfig( new GlobalSettingsConfigApplicator(mContext, packageName, event).applyConfig( config, signature); } finally { event.send(); } } else { if (DBG) Slog.d(TAG, "Package has no global settings config/signature."); } Loading