Make SafetyNet configurable

This commit is contained in:
Marvin W 2017-02-08 14:13:34 +01:00
parent d991888b89
commit 170d5e4524
No known key found for this signature in database
GPG Key ID: 072E9235DB996F2A
11 changed files with 300 additions and 19 deletions

2
extern/UnifiedNlp vendored

@ -1 +1 @@
Subproject commit ea09d3ff063c8ad32db492343bd640c3ac53bf2c
Subproject commit c161d40f1bfe1c185b67d8120e4d51339d31e280

View File

@ -383,8 +383,8 @@
<!-- Chimera spoof -->
<provider
android:authorities="com.google.android.gms.chimera"
android:name="org.microg.gms.ChimeraSpoofProvider"
android:authorities="com.google.android.gms.chimera"
android:exported="true"/>
<!-- microG custom UI -->
@ -426,6 +426,11 @@
android:label="@string/service_name_mcs"
android:theme="@style/Theme.AppCompat.Settings"/>
<activity
android:name="org.microg.gms.ui.SafetyNetFragment$AsActivity"
android:label="@string/service_name_snet"
android:theme="@style/Theme.AppCompat.Settings"/>
<activity
android:name="org.microg.gms.ui.SelfCheckFragment$AsActivity"
android:label="@string/self_check_title"

View File

@ -53,7 +53,6 @@ import okio.ByteString;
public class Attestation {
private static final String TAG = "GmsSafetyNetAttest";
private static final String ATTEST_URL = "https://www.googleapis.com/androidcheck/v1/attestations/attest?alt=PROTO&key=AIzaSyDqVnJBjE5ymo--oBJt3On7HQx9xNm1RHA";
private Context context;
private String packageName;
@ -144,14 +143,11 @@ public class Attestation {
if (payload == null) {
throw new IllegalStateException("missing payload");
}
if (droidGaurdResult == null || droidGaurdResult.isEmpty()) {
throw new IllegalStateException("missing droidGuard");
}
return attest(new AttestRequest(ByteString.of(payload), droidGaurdResult)).result;
}
private AttestResponse attest(AttestRequest request) throws IOException {
HttpURLConnection connection = (HttpURLConnection) new URL(ATTEST_URL).openConnection();
HttpURLConnection connection = (HttpURLConnection) new URL(SafetyNetPrefs.get(context).getServiceUrl()).openConnection();
connection.setRequestMethod("POST");
connection.setDoInput(true);
connection.setDoOutput(true);
@ -180,9 +176,13 @@ public class Attestation {
}
InputStream is = connection.getInputStream();
AttestResponse response = new Wire().parseFrom(new GZIPInputStream(is), AttestResponse.class);
is.close();
return response;
byte[] bytes = Utils.readStreamToEnd(new GZIPInputStream(is));
try {
return new Wire().parseFrom(bytes, AttestResponse.class);
} catch (IOException e) {
Log.d(TAG, Base64.encodeToString(bytes, 0));
throw e;
}
}

View File

@ -56,6 +56,12 @@ public class SafetyNetClientServiceImpl extends ISafetyNetService.Stub {
return;
}
if (!SafetyNetPrefs.get(context).isEnabled()) {
Log.d(TAG, "ignoring SafetyNet request, it's disabled");
callbacks.onAttestationData(Status.CANCELED, null);
return;
}
new Thread(new Runnable() {
@Override
public void run() {
@ -65,10 +71,11 @@ public class SafetyNetClientServiceImpl extends ISafetyNetService.Stub {
RemoteDroidGuardConnector conn = new RemoteDroidGuardConnector(context);
Bundle bundle = new Bundle();
bundle.putString("contentBinding", attestation.getPayloadHashBase64());
RemoteDroidGuardConnector.Result dg = conn.guard("attest", Long.toString(LastCheckinInfo.read(context).androidId),
bundle, Utils.getDeviceIdentifier(context).meid, Utils.getPhoneInfo(context).imsi);
if (dg != null && dg.getStatusCode() == 0 && dg.getResult() != null) {
attestation.setDroidGaurdResult(Base64.encodeToString(dg.getResult(), Base64.NO_WRAP + Base64.NO_PADDING + Base64.URL_SAFE));
RemoteDroidGuardConnector.Result dg = conn.guard("attest", Long.toString(LastCheckinInfo.read(context).androidId), bundle);
if (!SafetyNetPrefs.get(context).isOfficial() || dg != null && dg.getStatusCode() == 0 && dg.getResult() != null) {
if (dg != null && dg.getStatusCode() == 0 && dg.getResult() != null) {
attestation.setDroidGaurdResult(Base64.encodeToString(dg.getResult(), Base64.NO_WRAP + Base64.NO_PADDING + Base64.URL_SAFE));
}
AttestationData data = new AttestationData(attestation.attest());
callbacks.onAttestationData(Status.SUCCESS, data);
} else {

View File

@ -0,0 +1,80 @@
/*
* Copyright (C) 2017 microG Project Team
*
* 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 org.microg.gms.snet;
import android.content.Context;
import android.content.SharedPreferences;
import android.preference.PreferenceManager;
public class SafetyNetPrefs implements SharedPreferences.OnSharedPreferenceChangeListener {
private static final String OFFICIAL_ATTEST_URL = "https://www.googleapis.com/androidcheck/v1/attestations/attest?alt=PROTO&key=AIzaSyDqVnJBjE5ymo--oBJt3On7HQx9xNm1RHA";
public static final String PREF_SNET_DISABLED = "snet_disabled";
public static final String PREF_SNET_OFFICIAL = "snet_official";
public static final String PREF_SNET_THIRD_PARTY = "snet_third_party";
public static final String PREF_SNET_CUSTOM_URL = "snet_custom_url";
private static SafetyNetPrefs INSTANCE;
public static SafetyNetPrefs get(Context context) {
if (INSTANCE == null) {
if (context == null) return new SafetyNetPrefs(null);
INSTANCE = new SafetyNetPrefs(context.getApplicationContext());
}
return INSTANCE;
}
private boolean disabled;
private boolean official;
private boolean thirdParty;
private String customUrl;
private SharedPreferences defaultPreferences;
private SafetyNetPrefs(Context context) {
if (context != null) {
defaultPreferences = PreferenceManager.getDefaultSharedPreferences(context);
defaultPreferences.registerOnSharedPreferenceChangeListener(this);
update();
}
}
public void update() {
disabled = defaultPreferences.getBoolean(PREF_SNET_DISABLED, true);
official = defaultPreferences.getBoolean(PREF_SNET_OFFICIAL, false);
thirdParty = defaultPreferences.getBoolean(PREF_SNET_THIRD_PARTY, false);
customUrl = defaultPreferences.getString(PREF_SNET_CUSTOM_URL, null);
}
@Override
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String s) {
update();
}
public boolean isEnabled() {
return !disabled && (official || thirdParty);
}
public boolean isOfficial() {
return official;
}
public String getServiceUrl() {
if (official) return OFFICIAL_ATTEST_URL;
return customUrl;
}
}

View File

@ -0,0 +1,84 @@
/*
* Copyright (C) 2017 microG Project Team
*
* 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 org.microg.gms.ui;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.support.v7.preference.Preference;
import com.google.android.gms.R;
import org.microg.tools.ui.AbstractSettingsActivity;
import org.microg.tools.ui.RadioButtonPreference;
import org.microg.tools.ui.ResourceSettingsFragment;
import static org.microg.gms.snet.SafetyNetPrefs.PREF_SNET_DISABLED;
import static org.microg.gms.snet.SafetyNetPrefs.PREF_SNET_OFFICIAL;
import static org.microg.gms.snet.SafetyNetPrefs.PREF_SNET_THIRD_PARTY;
public class SafetyNetFragment extends ResourceSettingsFragment {
public SafetyNetFragment() {
preferencesResource = R.xml.preferences_snet;
}
private RadioButtonPreference radioDisabled;
private RadioButtonPreference radioOfficial;
private RadioButtonPreference radioThirdParty;
@Override
public void onCreatePreferencesFix(@Nullable Bundle savedInstanceState, String rootKey) {
super.onCreatePreferencesFix(savedInstanceState, rootKey);
radioDisabled = (RadioButtonPreference) findPreference(PREF_SNET_DISABLED);
radioOfficial = (RadioButtonPreference) findPreference(PREF_SNET_OFFICIAL);
radioThirdParty = (RadioButtonPreference) findPreference(PREF_SNET_THIRD_PARTY);
}
@Override
public boolean onPreferenceTreeClick(Preference preference) {
if (preference == radioDisabled) {
radioDisabled.setChecked(true);
radioOfficial.setChecked(false);
radioThirdParty.setChecked(false);
return true;
} else if (preference == radioOfficial) {
radioDisabled.setChecked(false);
radioOfficial.setChecked(true);
radioThirdParty.setChecked(false);
return true;
} else if (preference == radioThirdParty) {
radioDisabled.setChecked(false);
radioOfficial.setChecked(false);
radioThirdParty.setChecked(true);
return true;
}
return super.onPreferenceTreeClick(preference);
}
public static class AsActivity extends AbstractSettingsActivity {
public AsActivity() {
showHomeAsUp = true;
}
@Override
protected Fragment getFragment() {
return new SafetyNetFragment();
}
}
}

View File

@ -19,12 +19,12 @@ package org.microg.gms.ui;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.support.v7.preference.PreferenceManager;
import com.google.android.gms.R;
import org.microg.gms.gcm.GcmDatabase;
import org.microg.gms.gcm.GcmPrefs;
import org.microg.gms.snet.SafetyNetPrefs;
import org.microg.tools.ui.AbstractDashboardActivity;
import org.microg.tools.ui.ResourceSettingsFragment;
@ -45,6 +45,7 @@ public class SettingsActivity extends AbstractDashboardActivity {
public static final String PREF_ABOUT = "pref_about";
public static final String PREF_GCM = "pref_gcm";
public static final String PREF_SNET = "pref_snet";
public FragmentImpl() {
preferencesResource = R.xml.preferences_start;
@ -53,16 +54,26 @@ public class SettingsActivity extends AbstractDashboardActivity {
@Override
public void onCreatePreferencesFix(@Nullable Bundle savedInstanceState, String rootKey) {
super.onCreatePreferencesFix(savedInstanceState, rootKey);
PreferenceManager prefs = getPreferenceManager();
prefs.findPreference(PREF_ABOUT).setSummary(getString(R.string.about_version_str, AboutFragment.getSelfVersion(getContext())));
updateDetails();
}
@Override
public void onResume() {
super.onResume();
updateDetails();
}
private void updateDetails() {
findPreference(PREF_ABOUT).setSummary(getString(R.string.about_version_str, AboutFragment.getSelfVersion(getContext())));
if (GcmPrefs.get(getContext()).isGcmEnabled()) {
GcmDatabase database = new GcmDatabase(getContext());
int regCount = database.getRegistrationList().size();
database.close();
prefs.findPreference(PREF_GCM).setSummary(getString(R.string.v7_preference_on) + " / " + getContext().getString(R.string.gcm_registered_apps_counter, regCount));
findPreference(PREF_GCM).setSummary(getString(R.string.v7_preference_on) + " / " + getContext().getString(R.string.gcm_registered_apps_counter, regCount));
} else {
prefs.findPreference(PREF_GCM).setSummary(R.string.v7_preference_off);
findPreference(PREF_GCM).setSummary(R.string.v7_preference_off);
}
findPreference(PREF_SNET).setSummary(SafetyNetPrefs.get(getContext()).isEnabled() ? R.string.service_status_enabled : R.string.service_status_disabled);
}
}
}

View File

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="#000000"
android:pathData="M4,3C2.89,3 2,3.89 2,5V15A2,2 0 0,0 4,17H12V22L15,19L18,22V17H20A2,2 0 0,0
22,15V8L22,6V5A2,2 0 0,0
20,3H16V3H4M12,5L15,7L18,5V8.5L21,10L18,11.5V15L15,13L12,15V11.5L9,10L12,8.5V5M4,5H9V7H4V5M4,9H7V11H4V9M4,13H9V15H4V13Z" />
</vector>

View File

@ -55,6 +55,10 @@ This can take a couple of minutes."</string>
<string name="service_name_checkin">Google device registration</string>
<string name="service_name_mcs">Google Cloud Messaging</string>
<string name="service_name_snet">Google SafetyNet</string>
<string name="service_status_disabled">Disabled</string>
<string name="service_status_enabled">Enabled</string>
<string name="pref_checkin_enable_summary">Registers your device to Google services and creates a unique device identifier. microG strips identifying bits other than your Google account name from registration data.</string>
<string name="pref_gcm_enable_mcs_summary">Google Cloud Messaging is a push notification provider used by many third-party applications. To use it you must enable device registration.</string>
@ -138,4 +142,20 @@ This can take a couple of minutes."</string>
<string name="network_type_wifi">Wi-Fi</string>
<string name="network_type_roaming">Roaming</string>
<string name="network_type_other">Other networks</string>
<string name="snet_intro">Google SafetyNet is a device certification system, ensuring that the device is properly secured and compatible with Android CTS. Some applications use SafetyNet for security reasons or as a prerequisite for tamper-protection.\n\nmicroG GmsCore contains a free implementation of SafetyNet, but the official server requires SafetyNet requests to be signed using the proprietary DroidGuard system. A sandboxed version of DroidGuard is available as a separate “DroidGuard Helper” app.</string>
<string name="prefcat_configuration">Configuration</string>
<string name="pref_snet_status_disabled_title">Disabled</string>
<string name="pref_snet_status_disabled_summary">Completely disable SafetyNet</string>
<string name="pref_snet_status_official_title">Use official server</string>
<string name="pref_snet_status_official_summary">Requires an unrooted system, device registration enabled and microG DroidGuard Helper installed</string>
<string name="pref_snet_status_third_party_title">Use third-party server</string>
<string name="pref_snet_status_third_party_summary">Third-party servers might be able to reply to SafetyNet requests without DroidGuard signature</string>
<string name="pref_snet_custom_url_title">Custom server URL</string>
<string name="pref_snet_custom_url_summary">Full URL of the third-party server answering SafetyNet attestation requests</string>
<string name="prefcat_test">Test</string>
<string name="pref_snet_testdrive_title">Try SafetyNet attestation</string>
</resources>

View File

@ -0,0 +1,53 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (C) 2017 microG Project Team
~
~ 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.
-->
<PreferenceScreen
xmlns:android="http://schemas.android.com/apk/res/android">
<Preference
android:selectable="false"
android:summary="@string/snet_intro"/>
<PreferenceCategory android:title="@string/prefcat_configuration">
<org.microg.tools.ui.RadioButtonPreference
android:defaultValue="true"
android:key="snet_disabled"
android:summary="@string/pref_snet_status_disabled_summary"
android:title="@string/pref_snet_status_disabled_title"/>
<org.microg.tools.ui.RadioButtonPreference
android:checked="false"
android:key="snet_official"
android:summary="@string/pref_snet_status_official_summary"
android:title="@string/pref_snet_status_official_title"/>
<org.microg.tools.ui.RadioButtonPreference
android:checked="false"
android:key="snet_third_party"
android:summary="@string/pref_snet_status_third_party_summary"
android:title="@string/pref_snet_status_third_party_title"/>
<EditTextPreference
android:dependency="snet_third_party"
android:hint="https://example.com/server?key=123"
android:key="snet_custom_url"
android:summary="@string/pref_snet_custom_url_summary"
android:title="@string/pref_snet_custom_url_title"/>
</PreferenceCategory>
<PreferenceCategory android:title="@string/prefcat_test">
<Preference
android:enabled="false"
android:key="pref_snet_run_attest"
android:summary="Not yet available"
android:title="@string/pref_snet_testdrive_title"/>
</PreferenceCategory>
</PreferenceScreen>

View File

@ -41,6 +41,14 @@
android:targetClass="org.microg.gms.ui.GcmFragment$AsActivity"
android:targetPackage="com.google.android.gms"/>
</org.microg.tools.ui.TintIconPreference>
<org.microg.tools.ui.TintIconPreference
android:icon="@drawable/certificate"
android:key="pref_snet"
android:title="@string/service_name_snet">
<intent
android:targetClass="org.microg.gms.ui.SafetyNetFragment$AsActivity"
android:targetPackage="com.google.android.gms"/>
</org.microg.tools.ui.TintIconPreference>
<org.microg.tools.ui.TintIconPreference
android:icon="@drawable/location_marker"
android:title="@string/nlp_settings_label">