Skip to content

Commit

Permalink
[Crypto] Initial support for OpenPGP clients
Browse files Browse the repository at this point in the history
The default OpenPGP API doesn't work in AndroidX, therefore needed a workaround. There's an alternative library written in Kotlin (https://github.com/android-password-store/openpgp-ktx) but it inherits the same problem: it relies on Android preference library (an API is supposed to be independent of such a library). This workaround uses a single Dialog fragment to merge the two preferences and therefore, we have at least some control on how we are going to work with it. But I have to admit, the original OpenPGP library is not what a library should look like (neither is mine as a consequence). Related issue: open-keychain/open-keychain#2432
  • Loading branch information
MuntashirAkon committed Sep 20, 2020
1 parent 24f2655 commit db0deb2
Show file tree
Hide file tree
Showing 22 changed files with 1,941 additions and 0 deletions.
1 change: 1 addition & 0 deletions .idea/gradle.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
compileOnly project(path: ':HiddenApi')
implementation project(path: ':libApkParser')
implementation project(path: ':libOpenPGP')
implementation 'com.google.android.material:material:1.3.0-alpha02'
implementation 'androidx.appcompat:appcompat:1.3.0-alpha02'
implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.2.0-alpha01'
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
/*
* Copyright (C) 2020 Muntashir Al-Islam
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/

package io.github.muntashirakon.AppManager.settings;

import android.app.Dialog;
import android.app.PendingIntent;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.ServiceInfo;
import android.os.Bundle;

import com.google.android.material.dialog.MaterialAlertDialogBuilder;

import org.openintents.openpgp.IOpenPgpService2;
import org.openintents.openpgp.OpenPgpError;
import org.openintents.openpgp.util.OpenPgpApi;
import org.openintents.openpgp.util.OpenPgpServiceConnection;
import org.openintents.openpgp.util.OpenPgpUtils;

import java.util.List;
import java.util.Objects;

import androidx.activity.result.ActivityResultLauncher;
import androidx.activity.result.IntentSenderRequest;
import androidx.activity.result.contract.ActivityResultContracts;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.DialogFragment;
import androidx.fragment.app.FragmentActivity;
import io.github.muntashirakon.AppManager.AppManager;
import io.github.muntashirakon.AppManager.R;
import io.github.muntashirakon.AppManager.logs.Log;
import io.github.muntashirakon.AppManager.utils.AppPref;
import io.github.muntashirakon.AppManager.utils.ArrayUtils;

public class OpenPgpKeySelectionDialogFragment extends DialogFragment {
public static final String TAG = "OpenPgpKeySelectionDialogFragment";

private String mOpenPgpProvider;
private OpenPgpServiceConnection mServiceConnection;
private FragmentActivity activity;

private static final int NO_KEY = 0;

private ActivityResultLauncher<IntentSenderRequest> keyIdResultLauncher;

@NonNull
@Override
public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) {
activity = requireActivity();
// This must be registered using an activity context since the dialog won't exist
keyIdResultLauncher = activity.registerForActivityResult(
new ActivityResultContracts.StartIntentSenderForResult(),
result -> {
if (result.getData() != null)
getUserId(result.getData());
});
mOpenPgpProvider = (String) AppPref.get(AppPref.PrefKey.PREF_OPEN_PGP_PACKAGE_STR);
List<ServiceInfo> serviceInfoList = OpenPgpUtils.getPgpClientServices(activity);
CharSequence[] packageLabels = new String[serviceInfoList.size()];
String[] packageNames = new String[serviceInfoList.size()];
ServiceInfo serviceInfo;
PackageManager pm = activity.getPackageManager();
for (int i = 0; i < packageLabels.length; ++i) {
serviceInfo = serviceInfoList.get(i);
packageLabels[i] = serviceInfo.loadLabel(pm);
packageNames[i] = serviceInfo.packageName;
}
int choice = ArrayUtils.indexOf(packageNames, mOpenPgpProvider);
return new MaterialAlertDialogBuilder(activity)
.setTitle(R.string.open_pgp_provider)
.setSingleChoiceItems(packageLabels, choice, (dialog, which) -> {
mOpenPgpProvider = packageNames[which];
AppPref.set(AppPref.PrefKey.PREF_OPEN_PGP_PACKAGE_STR, mOpenPgpProvider);
})
.setNegativeButton(R.string.cancel, null)
.setPositiveButton(R.string.ok, (dialog, which) -> chooseKey())
.create();
}

private void chooseKey() {
// bind to service
mServiceConnection = new OpenPgpServiceConnection(AppManager.getContext(), mOpenPgpProvider,
new OpenPgpServiceConnection.OnBound() {
@Override
public void onBound(IOpenPgpService2 service) {
getUserId(new Intent());
}

@Override
public void onError(Exception e) {
Log.e(OpenPgpApi.TAG, "exception on binding!", e);
}
}
);
mServiceConnection.bindToService();
}

private void getUserId(@NonNull Intent data) {
data.setAction(OpenPgpApi.ACTION_GET_SIGN_KEY_ID);
data.putExtra(OpenPgpApi.EXTRA_USER_ID, "");
OpenPgpApi api = new OpenPgpApi(activity, mServiceConnection.getService());
api.executeApiAsync(data, null, null, new OpenPgpCallback());
}

private class OpenPgpCallback implements OpenPgpApi.IOpenPgpCallback {

private OpenPgpCallback() {
}

@Override
public void onReturn(@NonNull Intent result) {
switch (result.getIntExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_ERROR)) {
case OpenPgpApi.RESULT_CODE_SUCCESS: {
long keyId = result.getLongExtra(OpenPgpApi.EXTRA_SIGN_KEY_ID, NO_KEY);
AppPref.set(AppPref.PrefKey.PREF_OPEN_PGP_USER_ID_LONG, keyId);
break;
}
case OpenPgpApi.RESULT_CODE_USER_INTERACTION_REQUIRED: {
PendingIntent pi = Objects.requireNonNull(result.getParcelableExtra(OpenPgpApi.RESULT_INTENT));
keyIdResultLauncher.launch(new IntentSenderRequest.Builder(pi).build());
break;
}
case OpenPgpApi.RESULT_CODE_ERROR: {
OpenPgpError error = result.getParcelableExtra(OpenPgpApi.RESULT_ERROR);
if (error != null)
Log.e(OpenPgpApi.TAG, "RESULT_CODE_ERROR: " + error.getMessage());
break;
}
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,10 @@ protected void onCreate(Bundle savedInstanceState) {
})
.setNegativeButton(R.string.no, null)
.show());
// OpenPGP Provider
findViewById(R.id.open_pgp_provider).setOnClickListener(v ->
new OpenPgpKeySelectionDialogFragment().show(getSupportFragmentManager(),
OpenPgpKeySelectionDialogFragment.TAG));
// About
findViewById(R.id.about_view).setOnClickListener(v -> {
@SuppressLint("InflateParams")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,8 @@ public enum PrefKey {
PREF_LAST_VERSION_CODE_LONG,
PREF_MAIN_WINDOW_FILTER_FLAGS_INT,
PREF_MAIN_WINDOW_SORT_ORDER_INT,
PREF_OPEN_PGP_PACKAGE_STR,
PREF_OPEN_PGP_USER_ID_LONG,
PREF_PERMISSIONS_SORT_ORDER_INT,
PREF_ROOT_MODE_ENABLED_BOOL,
PREF_SHOW_DISCLAIMER_BOOL,
Expand Down Expand Up @@ -246,6 +248,7 @@ private Object getDefaultValue(@NonNull PrefKey key) {
case PREF_SHOW_DISCLAIMER_BOOL:
return true;
case PREF_LAST_VERSION_CODE_LONG:
case PREF_OPEN_PGP_USER_ID_LONG:
return 0L;
case PREF_APP_THEME_INT:
return AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM;
Expand All @@ -259,6 +262,8 @@ private Object getDefaultValue(@NonNull PrefKey key) {
case PREF_COMPONENTS_SORT_ORDER_INT:
case PREF_PERMISSIONS_SORT_ORDER_INT:
return AppDetailsFragment.SORT_BY_NAME;
case PREF_OPEN_PGP_PACKAGE_STR:
return "";
}
throw new IllegalArgumentException("Pref key not found.");
}
Expand Down
28 changes: 28 additions & 0 deletions app/src/main/res/layout/activity_settings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -276,6 +276,34 @@

</androidx.appcompat.widget.LinearLayoutCompat>

<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="@color/ten_percent_black" />

<androidx.appcompat.widget.LinearLayoutCompat
android:id="@+id/open_pgp_provider"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="@dimen/padding_medium"
android:background="?selectableItemBackground"
android:orientation="vertical">

<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textColor="?android:attr/textColorPrimary"
android:text="@string/open_pgp_provider"
android:textSize="@dimen/title_font" />

<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/pref_open_pgp_provider_msg"
android:textSize="@dimen/subtitle_font" />

</androidx.appcompat.widget.LinearLayoutCompat>

<View
android:layout_width="match_parent"
android:layout_height="1dp"
Expand Down
2 changes: 2 additions & 0 deletions app/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -618,4 +618,6 @@
<string name="ok">OK</string>
<string name="cancel">Cancel</string>
<string name="systemless_app">Systemless App</string>
<string name="open_pgp_provider">OpenPGP Provider</string>
<string name="pref_open_pgp_provider_msg">Adding an OpenPGP provider enables you to encrypt sensitive info such as app data backups</string>
</resources>
1 change: 1 addition & 0 deletions libOpenPGP/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/build
49 changes: 49 additions & 0 deletions libOpenPGP/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
/*
* Copyright (C) 2020 Muntashir Al-Islam
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/

apply plugin: 'com.android.library'

android {
compileSdkVersion 29

defaultConfig {
minSdkVersion 21
targetSdkVersion 29
versionCode 11
versionName "11"
}

buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
preRelease {
minifyEnabled false
versionNameSuffix '-PRE'
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}

dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'androidx.annotation:annotation:1.1.0'
}

sourceCompatibility = "1.8"
targetCompatibility = "1.8"
18 changes: 18 additions & 0 deletions libOpenPGP/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<!--
~ Copyright (C) 2020 Muntashir Al-Islam
~
~ This program is free software: you can redistribute it and/or modify
~ it under the terms of the GNU General Public License as published by
~ the Free Software Foundation, either version 3 of the License, or
~ (at your option) any later version.
~
~ This program is distributed in the hope that it will be useful,
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
~ GNU General Public License for more details.
~
~ You should have received a copy of the GNU General Public License
~ along with this program. If not, see <https://www.gnu.org/licenses/>.
-->

<manifest package="org.openintents.openpgp" />
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/*
* Copyright (C) 2014-2015 Dominik Schürmann <[email protected]>
*
* 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.openintents.openpgp;

interface IOpenPgpService {

/**
* do NOT use this, data returned from the service through "output" may be truncated
* @deprecated
*/
Intent execute(in Intent data, in ParcelFileDescriptor input, in ParcelFileDescriptor output);

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/*
* Copyright (C) 2015 Dominik Schürmann <[email protected]>
*
* 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.openintents.openpgp;

interface IOpenPgpService2 {

/**
* see org.openintents.openpgp.util.OpenPgpApi for documentation
*/
ParcelFileDescriptor createOutputPipe(in int pipeId);

/**
* see org.openintents.openpgp.util.OpenPgpApi for documentation
*/
Intent execute(in Intent data, in ParcelFileDescriptor input, int pipeId);
}
Loading

0 comments on commit db0deb2

Please sign in to comment.