completly new PasswordCacheService (more energy efficient), reworked Password dialogs

This commit is contained in:
Dominik 2012-09-11 19:56:54 +02:00
parent be49597882
commit 4b8400685a
19 changed files with 370 additions and 354 deletions

View File

@ -1,5 +1,6 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<!-- <!--
Copyright (C) 2012 Dominik Schürmann <dominik@dominikschuermann.de>
Copyright (C) 2010 Thialfihar <thi@thialfihar.org> Copyright (C) 2010 Thialfihar <thi@thialfihar.org>
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
@ -49,7 +50,7 @@
android:theme="@style/Theme.Sherlock.Light.ForceOverflow" > android:theme="@style/Theme.Sherlock.Light.ForceOverflow" >
<activity <activity
android:name=".ui.MainActivity" android:name=".ui.MainActivity"
android:configChanges="keyboardHidden|orientation|keyboard" android:configChanges="orientation|screenSize|keyboardHidden|keyboard"
android:label="@string/app_name" > android:label="@string/app_name" >
<intent-filter> <intent-filter>
<action android:name="android.intent.action.MAIN" /> <action android:name="android.intent.action.MAIN" />
@ -59,7 +60,7 @@
</activity> </activity>
<activity <activity
android:name=".ui.PublicKeyListActivity" android:name=".ui.PublicKeyListActivity"
android:configChanges="keyboardHidden|orientation|keyboard" android:configChanges="orientation|screenSize|keyboardHidden|keyboard"
android:label="@string/title_managePublicKeys" android:label="@string/title_managePublicKeys"
android:launchMode="singleTop" android:launchMode="singleTop"
android:uiOptions="splitActionBarWhenNarrow" > android:uiOptions="splitActionBarWhenNarrow" >
@ -73,7 +74,7 @@
</activity> </activity>
<activity <activity
android:name=".ui.SecretKeyListActivity" android:name=".ui.SecretKeyListActivity"
android:configChanges="keyboardHidden|orientation|keyboard" android:configChanges="orientation|screenSize|keyboardHidden|keyboard"
android:label="@string/title_manageSecretKeys" android:label="@string/title_manageSecretKeys"
android:launchMode="singleTop" android:launchMode="singleTop"
android:uiOptions="splitActionBarWhenNarrow" > android:uiOptions="splitActionBarWhenNarrow" >
@ -87,7 +88,7 @@
</activity> </activity>
<activity <activity
android:name=".ui.EditKeyActivity" android:name=".ui.EditKeyActivity"
android:configChanges="keyboardHidden|orientation|keyboard" android:configChanges="orientation|screenSize|keyboardHidden|keyboard"
android:label="@string/title_editKey" android:label="@string/title_editKey"
android:uiOptions="splitActionBarWhenNarrow" android:uiOptions="splitActionBarWhenNarrow"
android:windowSoftInputMode="stateHidden" > android:windowSoftInputMode="stateHidden" >
@ -100,7 +101,7 @@
</activity> </activity>
<activity <activity
android:name=".ui.SelectPublicKeyListActivity" android:name=".ui.SelectPublicKeyListActivity"
android:configChanges="keyboardHidden|orientation|keyboard" android:configChanges="orientation|screenSize|keyboardHidden|keyboard"
android:label="@string/title_selectRecipients" android:label="@string/title_selectRecipients"
android:launchMode="singleTop" android:launchMode="singleTop"
android:uiOptions="splitActionBarWhenNarrow" > android:uiOptions="splitActionBarWhenNarrow" >
@ -119,7 +120,7 @@
</activity> </activity>
<activity <activity
android:name=".ui.SelectSecretKeyListActivity" android:name=".ui.SelectSecretKeyListActivity"
android:configChanges="keyboardHidden|orientation|keyboard" android:configChanges="orientation|screenSize|keyboardHidden|keyboard"
android:label="@string/title_selectSignature" android:label="@string/title_selectSignature"
android:launchMode="singleTop" > android:launchMode="singleTop" >
<intent-filter> <intent-filter>
@ -137,7 +138,7 @@
</activity> </activity>
<activity <activity
android:name=".ui.EncryptActivity" android:name=".ui.EncryptActivity"
android:configChanges="keyboardHidden|orientation|keyboard" android:configChanges="orientation|screenSize|keyboardHidden|keyboard"
android:label="@string/title_encrypt" android:label="@string/title_encrypt"
android:uiOptions="splitActionBarWhenNarrow" android:uiOptions="splitActionBarWhenNarrow"
android:windowSoftInputMode="stateHidden" > android:windowSoftInputMode="stateHidden" >
@ -154,7 +155,7 @@
</activity> </activity>
<activity <activity
android:name=".ui.DecryptActivity" android:name=".ui.DecryptActivity"
android:configChanges="keyboardHidden|orientation|keyboard" android:configChanges="orientation|screenSize|keyboardHidden|keyboard"
android:label="@string/title_decrypt" android:label="@string/title_decrypt"
android:uiOptions="splitActionBarWhenNarrow" android:uiOptions="splitActionBarWhenNarrow"
android:windowSoftInputMode="stateHidden" > android:windowSoftInputMode="stateHidden" >
@ -170,7 +171,7 @@
</activity> </activity>
<activity <activity
android:name=".deprecated.GeneralActivity" android:name=".deprecated.GeneralActivity"
android:configChanges="keyboardHidden|orientation|keyboard" android:configChanges="orientation|screenSize|keyboardHidden|keyboard"
android:label="@string/app_name" android:label="@string/app_name"
android:theme="@android:style/Theme.Dialog" > android:theme="@android:style/Theme.Dialog" >
<intent-filter> <intent-filter>
@ -202,39 +203,39 @@
</activity> </activity>
<activity <activity
android:name=".ui.MailListActivity" android:name=".ui.MailListActivity"
android:configChanges="keyboardHidden|orientation|keyboard" android:configChanges="orientation|screenSize|keyboardHidden|keyboard"
android:label="@string/title_mailInbox" /> android:label="@string/title_mailInbox" />
<activity <activity
android:name=".ui.KeyServerQueryActivity" android:name=".ui.KeyServerQueryActivity"
android:configChanges="keyboardHidden|orientation|keyboard" android:configChanges="orientation|screenSize|keyboardHidden|keyboard"
android:label="@string/title_keyServerQuery" /> android:label="@string/title_keyServerQuery" />
<activity <activity
android:name=".ui.KeyServerUploadActivity" android:name=".ui.KeyServerUploadActivity"
android:configChanges="keyboardHidden|orientation|keyboard" android:configChanges="orientation|screenSize|keyboardHidden|keyboard"
android:label="@string/title_sendKey" /> android:label="@string/title_sendKey" />
<activity <activity
android:name=".ui.PreferencesActivity" android:name=".ui.PreferencesActivity"
android:configChanges="keyboardHidden|orientation|keyboard" android:configChanges="orientation|screenSize|keyboardHidden|keyboard"
android:label="@string/title_preferences" /> android:label="@string/title_preferences" />
<activity <activity
android:name=".ui.PreferencesKeyServerActivity" android:name=".ui.PreferencesKeyServerActivity"
android:configChanges="keyboardHidden|orientation|keyboard" android:configChanges="orientation|screenSize|keyboardHidden|keyboard"
android:label="@string/title_keyServerPreference" android:label="@string/title_keyServerPreference"
android:uiOptions="splitActionBarWhenNarrow" android:uiOptions="splitActionBarWhenNarrow"
android:windowSoftInputMode="stateHidden" /> android:windowSoftInputMode="stateHidden" />
<activity <activity
android:name=".ui.SignKeyActivity" android:name=".ui.SignKeyActivity"
android:configChanges="keyboardHidden|orientation|keyboard" android:configChanges="orientation|screenSize|keyboardHidden|keyboard"
android:label="@string/title_signKey" /> android:label="@string/title_signKey" />
<activity <activity
android:name=".ui.ImportFromQRCodeActivity" android:name=".ui.ImportFromQRCodeActivity"
android:configChanges="keyboardHidden|orientation|keyboard" android:configChanges="orientation|screenSize|keyboardHidden|keyboard"
android:label="@string/title_importFromQRCode" /> android:label="@string/title_importFromQRCode" />
<activity <activity
android:name=".ui.HelpActivity" android:name=".ui.HelpActivity"
android:label="@string/title_help" /> android:label="@string/title_help" />
<service android:name=".service.password.PassphraseCacheService" /> <service android:name=".service.PassphraseCacheService" />
<service android:name=".service.ApgService" /> <service android:name=".service.ApgService" />
<!-- TODO: need to be moved into new service model --> <!-- TODO: need to be moved into new service model -->

View File

@ -33,6 +33,7 @@
android:id="@+id/passphrase_passphrase" android:id="@+id/passphrase_passphrase"
android:layout_width="fill_parent" android:layout_width="fill_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:imeOptions="actionDone"
android:inputType="textPassword" android:inputType="textPassword"
android:padding="4dp" /> android:padding="4dp" />

View File

@ -54,6 +54,7 @@
android:id="@+id/passphrase_passphrase_again" android:id="@+id/passphrase_passphrase_again"
android:layout_width="fill_parent" android:layout_width="fill_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:imeOptions="actionDone"
android:inputType="textPassword" android:inputType="textPassword"
android:padding="4dp" /> android:padding="4dp" />
</TableRow> </TableRow>

View File

@ -38,7 +38,8 @@ public class ApgApplication extends Application {
super.onCreate(); super.onCreate();
/* Start passphrase cache service */ /* Start passphrase cache service */
PassphraseCacheService.startCacheService(this); // TODO: not needed anymore!
// PassphraseCacheService.startCacheService(this);
// TODO: Do it better than this! // TODO: Do it better than this!
// this initializes the database to be used in PGPMain // this initializes the database to be used in PGPMain

View File

@ -1,156 +0,0 @@
/*
* Copyright (C) 2010 Thialfihar <thi@thialfihar.org>
*
* 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.thialfihar.android.apg.deprecated;
import org.spongycastle.jce.provider.BouncyCastleProvider;
import org.spongycastle.openpgp.PGPException;
import org.spongycastle.openpgp.PGPPrivateKey;
import org.spongycastle.openpgp.PGPSecretKey;
import org.thialfihar.android.apg.Id;
import org.thialfihar.android.apg.R;
import org.thialfihar.android.apg.helper.PGPHelper;
import org.thialfihar.android.apg.helper.PGPMain;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.DialogInterface.OnClickListener;
import org.thialfihar.android.apg.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;
/**
* TODO:
*
* - Use new PassphraseDialogFragment!
*
*
*/
public class AskForPassphrase {
public static interface PassPhraseCallbackInterface {
void passPhraseCallback(long keyId, String passPhrase);
}
public static Dialog createDialog(Activity context, long secretKeyId,
PassPhraseCallbackInterface callback) {
AlertDialog.Builder alert = new AlertDialog.Builder(context);
alert.setTitle(R.string.title_authentication);
final PGPSecretKey secretKey;
final Activity activity = context;
if (secretKeyId == Id.key.symmetric || secretKeyId == Id.key.none) {
secretKey = null;
alert.setMessage(context.getString(R.string.passPhraseForSymmetricEncryption));
} else {
secretKey = PGPHelper.getMasterKey(PGPMain.getSecretKeyRing(secretKeyId));
if (secretKey == null) {
alert.setTitle(R.string.title_keyNotFound);
alert.setMessage(context.getString(R.string.keyNotFound, secretKeyId));
alert.setPositiveButton(android.R.string.ok, new OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
activity.removeDialog(Id.dialog.pass_phrase);
}
});
alert.setCancelable(false);
return alert.create();
}
String userId = PGPHelper.getMainUserIdSafe(context, secretKey);
alert.setMessage(context.getString(R.string.passPhraseFor, userId));
}
LayoutInflater inflater = (LayoutInflater) context
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View view = inflater.inflate(R.layout.passphrase, null);
final EditText input = (EditText) view.findViewById(R.id.passphrase_passphrase);
final TextView labelNotUsed = (TextView) view
.findViewById(R.id.passphrase_label_passphrase_again);
labelNotUsed.setVisibility(View.GONE);
final EditText inputNotUsed = (EditText) view
.findViewById(R.id.passphrase_passphrase_again);
inputNotUsed.setVisibility(View.GONE);
alert.setView(view);
final PassPhraseCallbackInterface cb = callback;
alert.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
activity.removeDialog(Id.dialog.pass_phrase);
String passPhrase = input.getText().toString();
long keyId;
if (secretKey != null) {
try {
PGPPrivateKey testKey = secretKey.extractPrivateKey(
passPhrase.toCharArray(), new BouncyCastleProvider());
if (testKey == null) {
Toast.makeText(activity, R.string.error_couldNotExtractPrivateKey,
Toast.LENGTH_SHORT).show();
return;
}
} catch (PGPException e) {
Toast.makeText(activity, R.string.wrongPassPhrase, Toast.LENGTH_SHORT)
.show();
return;
}
keyId = secretKey.getKeyID();
} else {
keyId = Id.key.symmetric;
}
// cache again
PGPMain.setCachedPassPhrase(keyId, passPhrase);
// return by callback
cb.passPhraseCallback(keyId, passPhrase);
}
});
alert.setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
activity.removeDialog(Id.dialog.pass_phrase);
}
});
// check if the key has no passphrase
if (secretKey != null) {
try {
Log.d("APG", "check if key has no passphrase...");
PGPPrivateKey testKey = secretKey.extractPrivateKey("".toCharArray(),
new BouncyCastleProvider());
if (testKey != null) {
Log.d("APG", "Key has no passphrase!");
// cache null
PGPMain.setCachedPassPhrase(secretKey.getKeyID(), null);
// return by callback
cb.passPhraseCallback(secretKey.getKeyID(), null);
return null;
}
} catch (PGPException e) {
}
}
return alert.create();
}
}

View File

@ -42,7 +42,7 @@ import android.os.Handler;
import android.os.Message; import android.os.Message;
public class BaseActivity extends SherlockFragmentActivity implements Runnable, public class BaseActivity extends SherlockFragmentActivity implements Runnable,
ProgressDialogUpdater, AskForPassphrase.PassPhraseCallbackInterface { ProgressDialogUpdater {
private ProgressDialog mProgressDialog = null; private ProgressDialog mProgressDialog = null;
// private PausableThread mRunningThread = null; // private PausableThread mRunningThread = null;
@ -363,11 +363,11 @@ public class BaseActivity extends SherlockFragmentActivity implements Runnable,
// //
// Toast.makeText(this, message, Toast.LENGTH_SHORT).show(); // Toast.makeText(this, message, Toast.LENGTH_SHORT).show();
// } // }
//
public void passPhraseCallback(long keyId, String passPhrase) { // public void passPhraseCallback(long keyId, String passPhrase) {
// TODO: Not needed anymore, now implemented in AskForSecretKeyPass // // TODO: Not needed anymore, now implemented in AskForSecretKeyPass
PGPMain.setCachedPassPhrase(keyId, passPhrase); // PGPMain.setCachedPassPhrase(keyId, passPhrase);
} // }
// public void sendMessage(Message msg) { // public void sendMessage(Message msg) {
// mHandler.sendMessage(msg); // mHandler.sendMessage(msg);

View File

@ -78,7 +78,6 @@ import org.thialfihar.android.apg.provider.KeyRings;
import org.thialfihar.android.apg.provider.Keys; import org.thialfihar.android.apg.provider.Keys;
import org.thialfihar.android.apg.provider.UserIds; import org.thialfihar.android.apg.provider.UserIds;
import org.thialfihar.android.apg.service.ApgService; import org.thialfihar.android.apg.service.ApgService;
import org.thialfihar.android.apg.service.CachedPassphrase;
import org.thialfihar.android.apg.util.HkpKeyServer; import org.thialfihar.android.apg.util.HkpKeyServer;
import org.thialfihar.android.apg.util.InputData; import org.thialfihar.android.apg.util.InputData;
import org.thialfihar.android.apg.util.PositionAwareInputStream; import org.thialfihar.android.apg.util.PositionAwareInputStream;
@ -121,9 +120,7 @@ import java.security.Security;
import java.security.SignatureException; import java.security.SignatureException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Date; import java.util.Date;
import java.util.HashMap;
import java.util.Iterator; import java.util.Iterator;
import java.util.Map;
import java.util.Vector; import java.util.Vector;
import java.util.regex.Pattern; import java.util.regex.Pattern;
@ -185,7 +182,6 @@ public class PGPMain {
".*?(-----BEGIN PGP PUBLIC KEY BLOCK-----.*?-----END PGP PUBLIC KEY BLOCK-----).*", ".*?(-----BEGIN PGP PUBLIC KEY BLOCK-----.*?-----END PGP PUBLIC KEY BLOCK-----).*",
Pattern.DOTALL); Pattern.DOTALL);
private static HashMap<Long, CachedPassphrase> mPassPhraseCache = new HashMap<Long, CachedPassphrase>();
private static String mEditPassPhrase = null; private static String mEditPassPhrase = null;
private static Database mDatabase = null; private static Database mDatabase = null;
@ -224,58 +220,6 @@ public class PGPMain {
return mEditPassPhrase; return mEditPassPhrase;
} }
public static void setCachedPassPhrase(long keyId, String passPhrase) {
mPassPhraseCache.put(keyId, new CachedPassphrase(new Date().getTime(), passPhrase));
}
public static String getCachedPassPhrase(long keyId) {
long realId = keyId;
if (realId != Id.key.symmetric) {
PGPSecretKeyRing keyRing = getSecretKeyRing(keyId);
if (keyRing == null) {
return null;
}
PGPSecretKey masterKey = PGPHelper.getMasterKey(keyRing);
if (masterKey == null) {
return null;
}
realId = masterKey.getKeyID();
}
CachedPassphrase cpp = mPassPhraseCache.get(realId);
if (cpp == null) {
return null;
}
// set it again to reset the cache life cycle
setCachedPassPhrase(realId, cpp.passPhrase);
return cpp.passPhrase;
}
public static int cleanUpCache(int ttl, int initialDelay) {
int delay = initialDelay;
long realTtl = ttl * 1000;
long now = new Date().getTime();
Vector<Long> oldKeys = new Vector<Long>();
for (Map.Entry<Long, CachedPassphrase> pair : mPassPhraseCache.entrySet()) {
long lived = now - pair.getValue().timestamp;
if (lived >= realTtl) {
oldKeys.add(pair.getKey());
} else {
// see, whether the remaining time for this cache entry improves our
// check delay
long nextCheck = realTtl - lived + 1000;
if (nextCheck < delay) {
delay = (int) nextCheck;
}
}
}
for (long keyId : oldKeys) {
mPassPhraseCache.remove(keyId);
}
return delay;
}
/** /**
* Creates new secret key. The returned PGPSecretKeyRing contains only one newly generated key * Creates new secret key. The returned PGPSecretKeyRing contains only one newly generated key
* when this key is the new masterkey. If a masterkey is supplied in the parameters * when this key is the new masterkey. If a masterkey is supplied in the parameters
@ -1268,11 +1212,10 @@ public class PGPMain {
progress.setProgress(R.string.progress_done, 100, 100); progress.setProgress(R.string.progress_done, 100, 100);
} }
public static PGPPublicKeyRing signKey(Context context, long masterKeyId, long pubKeyId) public static PGPPublicKeyRing signKey(Context context, long masterKeyId, long pubKeyId,
throws GeneralException, NoSuchAlgorithmException, NoSuchProviderException, String passphrase) throws GeneralException, NoSuchAlgorithmException,
PGPException, SignatureException { NoSuchProviderException, PGPException, SignatureException {
String signaturePassPhrase = PGPMain.getCachedPassPhrase(masterKeyId); if (passphrase == null || passphrase.length() <= 0) {
if (signaturePassPhrase == null || signaturePassPhrase.length() <= 0) {
throw new GeneralException("Unable to obtain passphrase"); throw new GeneralException("Unable to obtain passphrase");
} else { } else {
PGPPublicKeyRing pubring = PGPMain.getPublicKeyRing(pubKeyId); PGPPublicKeyRing pubring = PGPMain.getPublicKeyRing(pubKeyId);
@ -1283,7 +1226,7 @@ public class PGPMain {
} }
PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder().setProvider( PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder().setProvider(
BOUNCY_CASTLE_PROVIDER_NAME).build(signaturePassPhrase.toCharArray()); BOUNCY_CASTLE_PROVIDER_NAME).build(passphrase.toCharArray());
PGPPrivateKey signaturePrivateKey = signingKey.extractPrivateKey(keyDecryptor); PGPPrivateKey signaturePrivateKey = signingKey.extractPrivateKey(keyDecryptor);
if (signaturePrivateKey == null) { if (signaturePrivateKey == null) {
throw new GeneralException( throw new GeneralException(

View File

@ -70,6 +70,25 @@ public class ApgService extends IntentService implements ProgressDialogUpdater {
public static final String EXTRA_ACTION = "action"; public static final String EXTRA_ACTION = "action";
public static final String EXTRA_DATA = "data"; public static final String EXTRA_DATA = "data";
/* possible EXTRA_ACTIONs */
public static final int ACTION_ENCRYPT_SIGN = 10;
public static final int ACTION_DECRYPT_VERIFY = 20;
public static final int ACTION_SAVE_KEYRING = 30;
public static final int ACTION_GENERATE_KEY = 31;
public static final int ACTION_GENERATE_DEFAULT_RSA_KEYS = 32;
public static final int ACTION_DELETE_FILE_SECURELY = 40;
public static final int ACTION_IMPORT_KEY = 50;
public static final int ACTION_EXPORT_KEY = 51;
public static final int ACTION_UPLOAD_KEY = 60;
public static final int ACTION_QUERY_KEY = 61;
public static final int ACTION_SIGN_KEY = 70;
/* keys for data bundle */ /* keys for data bundle */
// encrypt, decrypt, import export // encrypt, decrypt, import export
@ -143,25 +162,6 @@ public class ApgService extends IntentService implements ProgressDialogUpdater {
public static final String SIGN_KEY_MASTER_KEY_ID = "signKeyMasterKeyId"; public static final String SIGN_KEY_MASTER_KEY_ID = "signKeyMasterKeyId";
public static final String SIGN_KEY_PUB_KEY_ID = "signKeyPubKeyId"; public static final String SIGN_KEY_PUB_KEY_ID = "signKeyPubKeyId";
/* possible EXTRA_ACTIONs */
public static final int ACTION_ENCRYPT_SIGN = 10;
public static final int ACTION_DECRYPT_VERIFY = 20;
public static final int ACTION_SAVE_KEYRING = 30;
public static final int ACTION_GENERATE_KEY = 31;
public static final int ACTION_GENERATE_DEFAULT_RSA_KEYS = 32;
public static final int ACTION_DELETE_FILE_SECURELY = 40;
public static final int ACTION_IMPORT_KEY = 50;
public static final int ACTION_EXPORT_KEY = 51;
public static final int ACTION_UPLOAD_KEY = 60;
public static final int ACTION_QUERY_KEY = 61;
public static final int ACTION_SIGN_KEY = 70;
/* possible data keys as result send over messenger */ /* possible data keys as result send over messenger */
// keys // keys
public static final String RESULT_NEW_KEY = "newKey"; public static final String RESULT_NEW_KEY = "newKey";
@ -320,19 +320,21 @@ public class ApgService extends IntentService implements ProgressDialogUpdater {
if (generateSignature) { if (generateSignature) {
Log.d(Constants.TAG, "generating signature..."); Log.d(Constants.TAG, "generating signature...");
PGPMain.generateSignature(this, inputData, outStream, useAsciiArmour, false, PGPMain.generateSignature(this, inputData, outStream, useAsciiArmour, false,
secretKeyId, PGPMain.getCachedPassPhrase(secretKeyId), Preferences secretKeyId, PassphraseCacheService.getCachedPassphrase(this,
.getPreferences(this).getDefaultHashAlgorithm(), Preferences secretKeyId), Preferences.getPreferences(this)
.getPreferences(this).getForceV3Signatures(), this); .getDefaultHashAlgorithm(), Preferences.getPreferences(this)
.getForceV3Signatures(), this);
} else if (signOnly) { } else if (signOnly) {
Log.d(Constants.TAG, "sign only..."); Log.d(Constants.TAG, "sign only...");
PGPMain.signText(this, inputData, outStream, secretKeyId, PGPMain PGPMain.signText(this, inputData, outStream, secretKeyId,
.getCachedPassPhrase(secretKeyId), Preferences.getPreferences(this) PassphraseCacheService.getCachedPassphrase(this, secretKeyId),
.getDefaultHashAlgorithm(), Preferences.getPreferences(this) Preferences.getPreferences(this).getDefaultHashAlgorithm(), Preferences
.getForceV3Signatures(), this); .getPreferences(this).getForceV3Signatures(), this);
} else { } else {
Log.d(Constants.TAG, "encrypt..."); Log.d(Constants.TAG, "encrypt...");
PGPMain.encrypt(this, inputData, outStream, useAsciiArmour, encryptionKeyIds, PGPMain.encrypt(this, inputData, outStream, useAsciiArmour, encryptionKeyIds,
signatureKeyId, PGPMain.getCachedPassPhrase(signatureKeyId), this, signatureKeyId,
PassphraseCacheService.getCachedPassphrase(this, signatureKeyId), this,
Preferences.getPreferences(this).getDefaultEncryptionAlgorithm(), Preferences.getPreferences(this).getDefaultEncryptionAlgorithm(),
Preferences.getPreferences(this).getDefaultHashAlgorithm(), Preferences.getPreferences(this).getDefaultHashAlgorithm(),
compressionId, Preferences.getPreferences(this).getForceV3Signatures(), compressionId, Preferences.getPreferences(this).getForceV3Signatures(),
@ -478,7 +480,7 @@ public class ApgService extends IntentService implements ProgressDialogUpdater {
this); this);
} else { } else {
resultData = PGPMain.decrypt(this, inputData, outStream, resultData = PGPMain.decrypt(this, inputData, outStream,
PGPMain.getCachedPassPhrase(secretKeyId), this, PassphraseCacheService.getCachedPassphrase(this, secretKeyId), this,
assumeSymmetricEncryption); assumeSymmetricEncryption);
} }
@ -539,7 +541,7 @@ public class ApgService extends IntentService implements ProgressDialogUpdater {
/* Operation */ /* Operation */
PGPMain.buildSecretKey(this, userIds, keys, keysUsages, masterKeyId, oldPassPhrase, PGPMain.buildSecretKey(this, userIds, keys, keysUsages, masterKeyId, oldPassPhrase,
newPassPhrase, this); newPassPhrase, this);
PGPMain.setCachedPassPhrase(masterKeyId, newPassPhrase); PassphraseCacheService.addCachedPassphrase(this, masterKeyId, newPassPhrase);
/* Output */ /* Output */
sendMessageToHandler(ApgServiceHandler.MESSAGE_OKAY); sendMessageToHandler(ApgServiceHandler.MESSAGE_OKAY);
@ -798,7 +800,11 @@ public class ApgService extends IntentService implements ProgressDialogUpdater {
long pubKeyId = data.getLong(SIGN_KEY_PUB_KEY_ID); long pubKeyId = data.getLong(SIGN_KEY_PUB_KEY_ID);
/* Operation */ /* Operation */
PGPPublicKeyRing signedPubKeyRing = PGPMain.signKey(this, masterKeyId, pubKeyId); String signaturePassPhrase = PassphraseCacheService.getCachedPassphrase(this,
masterKeyId);
PGPPublicKeyRing signedPubKeyRing = PGPMain.signKey(this, masterKeyId, pubKeyId,
signaturePassPhrase);
// store the signed key in our local cache // store the signed key in our local cache
int retval = PGPMain.storeKeyRingInCache(signedPubKeyRing); int retval = PGPMain.storeKeyRingInCache(signedPubKeyRing);

View File

@ -15,19 +15,27 @@
package org.thialfihar.android.apg.service; package org.thialfihar.android.apg.service;
public class CachedPassphrase { public class CachedPassphrase {
public final long timestamp; private final long timestamp;
public final String passPhrase; private final String passphrase;
public CachedPassphrase(long timestamp, String passPhrase) { public CachedPassphrase(long timestamp, String passPhrase) {
super(); super();
this.timestamp = timestamp; this.timestamp = timestamp;
this.passPhrase = passPhrase; this.passphrase = passPhrase;
}
public long getTimestamp() {
return timestamp;
}
public String getPassphrase() {
return passphrase;
} }
@Override @Override
public int hashCode() { public int hashCode() {
int hc1 = (int) (this.timestamp & 0xffffffff); int hc1 = (int) (this.timestamp & 0xffffffff);
int hc2 = (this.passPhrase == null ? 0 : this.passPhrase.hashCode()); int hc2 = (this.passphrase == null ? 0 : this.passphrase.hashCode());
return (hc1 + hc2) * hc2 + hc1; return (hc1 + hc2) * hc2 + hc1;
} }
@ -42,12 +50,12 @@ public class CachedPassphrase {
return false; return false;
} }
if (passPhrase != o.passPhrase) { if (passphrase != o.passphrase) {
if (passPhrase == null || o.passPhrase == null) { if (passphrase == null || o.passphrase == null) {
return false; return false;
} }
if (!passPhrase.equals(o.passPhrase)) { if (!passphrase.equals(o.passphrase)) {
return false; return false;
} }
} }

View File

@ -1,4 +1,6 @@
/* /*
* Copyright (C) 2012 Dominik Schürmann <dominik@dominikschuermann.de>
*
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at * You may obtain a copy of the License at
@ -14,90 +16,211 @@
package org.thialfihar.android.apg.service; package org.thialfihar.android.apg.service;
import java.util.HashMap;
import org.spongycastle.openpgp.PGPSecretKey;
import org.spongycastle.openpgp.PGPSecretKeyRing;
import org.thialfihar.android.apg.Constants;
import org.thialfihar.android.apg.Id;
import org.thialfihar.android.apg.helper.PGPHelper;
import org.thialfihar.android.apg.helper.PGPMain; import org.thialfihar.android.apg.helper.PGPMain;
import org.thialfihar.android.apg.helper.Preferences; import org.thialfihar.android.apg.helper.Preferences;
import android.app.AlarmManager;
import android.app.PendingIntent;
import android.app.Service; import android.app.Service;
import android.content.BroadcastReceiver;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.content.IntentFilter;
import android.os.Binder; import android.os.Binder;
import android.os.Handler;
import android.os.IBinder; import android.os.IBinder;
import android.support.v4.content.LocalBroadcastManager;
import android.util.Log;
public class PassphraseCacheService extends Service { public class PassphraseCacheService extends Service {
private final IBinder mBinder = new LocalBinder(); public static final String BROADCAST_ACTION_PASSPHRASE_CACHE_SERVICE = Constants.INTENT_PREFIX
+ "PASSPHRASE_CACHE_SERVICE";
public static final String EXTRA_TTL = "ttl"; public static final String EXTRA_TTL = "ttl";
public static final String EXTRA_KEY_ID = "keyId";
public static final String EXTRA_PASSPHRASE = "passphrase";
private static final int REQUEST_ID = 0;
private static final long DEFAULT_TTL = 15;
private BroadcastReceiver mIntentReceiver;
// TODO: This is static to be easily retrieved by getCachedPassphrase()
// To avoid static we would need a messenger from the service back to the activity?
private static HashMap<Long, CachedPassphrase> mPassphraseCache = new HashMap<Long, CachedPassphrase>();
/**
* This caches a new passphrase by sending a new command to the service. An android service is
* only run once. Thus when it is already started new commands just add new BroadcastReceivers
* for cached passphrases
*
* @param context
* @param keyId
* @param passphrase
*/
public static void addCachedPassphrase(Context context, long keyId, String passphrase) {
Log.d(Constants.TAG, "cacheNewPassphrase() for " + keyId);
public static void startCacheService(Context context) {
Intent intent = new Intent(context, PassphraseCacheService.class); Intent intent = new Intent(context, PassphraseCacheService.class);
intent.putExtra(PassphraseCacheService.EXTRA_TTL, Preferences.getPreferences(context).getPassPhraseCacheTtl()); intent.putExtra(EXTRA_TTL, Preferences.getPreferences(context).getPassPhraseCacheTtl());
intent.putExtra(EXTRA_PASSPHRASE, passphrase);
intent.putExtra(EXTRA_KEY_ID, keyId);
context.startService(intent); context.startService(intent);
} }
private int mPassPhraseCacheTtl = 15; /**
private Handler mCacheHandler = new Handler(); * Gets a cached passphrase from memory
private Runnable mCacheTask = new Runnable() { *
public void run() { * @param context
// check every ttl/2 seconds, which shouldn't be heavy on the device (even if ttl = 15), * @param keyId
// and makes sure the longest a pass phrase survives in the cache is 1.5 * ttl * @return
int delay = mPassPhraseCacheTtl * 1000 / 2; */
// also make sure the delay is not longer than one minute public static String getCachedPassphrase(Context context, long keyId) {
if (delay > 60000) { // try to get real key id
delay = 60000; long realId = keyId;
if (realId != Id.key.symmetric) {
PGPSecretKeyRing keyRing = PGPMain.getSecretKeyRing(keyId);
if (keyRing == null) {
return null;
} }
PGPSecretKey masterKey = PGPHelper.getMasterKey(keyRing);
delay = PGPMain.cleanUpCache(mPassPhraseCacheTtl, delay); if (masterKey == null) {
// don't check too often, even if we were close return null;
if (delay < 5000) {
delay = 5000;
} }
realId = masterKey.getKeyID();
mCacheHandler.postDelayed(this, delay);
} }
};
static private boolean mIsRunning = false; // get cached passphrase
CachedPassphrase cpp = mPassphraseCache.get(realId);
if (cpp == null) {
return null;
}
// set it again to reset the cache life cycle
addCachedPassphrase(context, realId, cpp.getPassphrase());
return cpp.getPassphrase();
}
/**
* Register BroadcastReceiver that is unregistered when service is destroyed. This
* BroadcastReceiver hears on intents with ACTION_PASSPHRASE_CACHE_SERVICE to timeout
* passphrases in memory.
*/
private void registerReceiver() {
if (mIntentReceiver == null) {
mIntentReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (action.equals(BROADCAST_ACTION_PASSPHRASE_CACHE_SERVICE)) {
long keyId = intent.getLongExtra(EXTRA_KEY_ID, -1);
timeout(context, keyId);
}
}
};
IntentFilter filter = new IntentFilter();
filter.addAction(BROADCAST_ACTION_PASSPHRASE_CACHE_SERVICE);
LocalBroadcastManager.getInstance(this).registerReceiver(mIntentReceiver, filter);
}
}
/**
* Build pending intent that is executed by alarm manager when one passphrase times out
*
* @param context
* @param keyId
* @return
*/
private static PendingIntent buildIntent(Context context, long keyId) {
Intent intent = new Intent(BROADCAST_ACTION_PASSPHRASE_CACHE_SERVICE);
intent.putExtra(EXTRA_KEY_ID, keyId);
PendingIntent sender = PendingIntent.getBroadcast(context, REQUEST_ID, intent,
PendingIntent.FLAG_CANCEL_CURRENT);
return sender;
}
@Override @Override
public void onCreate() { public void onCreate() {
super.onCreate(); Log.d(Constants.TAG, "PassphraseCacheService created!");
}
mIsRunning = true; /**
* Executed when service is started by intent
*/
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.d(Constants.TAG, "PassphraseCacheService started");
// register broadcastreceiver
registerReceiver();
if (intent != null) {
long ttl = intent.getLongExtra(EXTRA_TTL, DEFAULT_TTL);
long keyId = intent.getLongExtra(EXTRA_KEY_ID, -1);
String passphrase = intent.getStringExtra(EXTRA_PASSPHRASE);
Log.d(Constants.TAG, "received intent with keyId: " + keyId + ", ttl: " + ttl);
// add keyId and passphrase to memory
mPassphraseCache.put(keyId,
new CachedPassphrase(System.currentTimeMillis(), passphrase));
// register new alarm with keyId for this passphrase
long triggerTime = System.currentTimeMillis() + ttl;
AlarmManager am = (AlarmManager) this.getSystemService(Context.ALARM_SERVICE);
am.set(AlarmManager.RTC_WAKEUP, triggerTime, buildIntent(this, keyId));
}
return START_STICKY;
}
/**
* Called when one specific passphrase for keyId timed out
*
* @param context
* @param keyId
*/
private void timeout(Context context, long keyId) {
Log.d(Constants.TAG, "Timeout of " + keyId);
// remove passphrase corresponding to keyId from memory
mPassphraseCache.remove(keyId);
// stop whole service if no cached passphrases remaining
if (mPassphraseCache.isEmpty()) {
Log.d(Constants.TAG, "No passphrases remaining in memory, stopping service!");
stopSelf();
}
} }
@Override @Override
public void onDestroy() { public void onDestroy() {
super.onDestroy(); Log.d(Constants.TAG, "PassphraseCacheService destroyed!");
mIsRunning = false;
LocalBroadcastManager.getInstance(this).unregisterReceiver(mIntentReceiver);
} }
@Override public class PassphraseCacheBinder extends Binder {
public void onStart(Intent intent, int startId) { public PassphraseCacheService getService() {
super.onStart(intent, startId);
if (intent != null) {
mPassPhraseCacheTtl = intent.getIntExtra(EXTRA_TTL, 15);
}
if (mPassPhraseCacheTtl < 15) {
mPassPhraseCacheTtl = 15;
}
mCacheHandler.removeCallbacks(mCacheTask);
mCacheHandler.postDelayed(mCacheTask, 1000);
}
static public boolean isRunning() {
return mIsRunning;
}
public class LocalBinder extends Binder {
PassphraseCacheService getService() {
return PassphraseCacheService.this; return PassphraseCacheService.this;
} }
} }
private final IBinder mBinder = new PassphraseCacheBinder();
@Override @Override
public IBinder onBind(Intent intent) { public IBinder onBind(Intent intent) {
return mBinder; return mBinder;
} }
} }

View File

@ -25,6 +25,7 @@ import org.thialfihar.android.apg.helper.PGPHelper;
import org.thialfihar.android.apg.helper.PGPMain; import org.thialfihar.android.apg.helper.PGPMain;
import org.thialfihar.android.apg.service.ApgServiceHandler; import org.thialfihar.android.apg.service.ApgServiceHandler;
import org.thialfihar.android.apg.service.ApgService; import org.thialfihar.android.apg.service.ApgService;
import org.thialfihar.android.apg.service.PassphraseCacheService;
import org.thialfihar.android.apg.ui.dialog.DeleteFileDialogFragment; import org.thialfihar.android.apg.ui.dialog.DeleteFileDialogFragment;
import org.thialfihar.android.apg.ui.dialog.FileDialogFragment; import org.thialfihar.android.apg.ui.dialog.FileDialogFragment;
import org.thialfihar.android.apg.ui.dialog.LookupUnknownKeyDialogFragment; import org.thialfihar.android.apg.ui.dialog.LookupUnknownKeyDialogFragment;
@ -513,7 +514,7 @@ public class DecryptActivity extends SherlockFragmentActivity {
// if we need a symmetric passphrase or a passphrase to use a secret key ask for it // if we need a symmetric passphrase or a passphrase to use a secret key ask for it
if (getSecretKeyId() == Id.key.symmetric if (getSecretKeyId() == Id.key.symmetric
|| PGPMain.getCachedPassPhrase(getSecretKeyId()) == null) { || PassphraseCacheService.getCachedPassphrase(this, getSecretKeyId()) == null) {
showPassphraseDialog(); showPassphraseDialog();
} else { } else {
if (mDecryptTarget == Id.target.file) { if (mDecryptTarget == Id.target.file) {
@ -548,7 +549,7 @@ public class DecryptActivity extends SherlockFragmentActivity {
Messenger messenger = new Messenger(returnHandler); Messenger messenger = new Messenger(returnHandler);
try { try {
PassphraseDialogFragment passphraseDialog = PassphraseDialogFragment.newInstance( PassphraseDialogFragment passphraseDialog = PassphraseDialogFragment.newInstance(this,
messenger, mSecretKeyId); messenger, mSecretKeyId);
passphraseDialog.show(getSupportFragmentManager(), "passphraseDialog"); passphraseDialog.show(getSupportFragmentManager(), "passphraseDialog");

View File

@ -30,6 +30,7 @@ import org.thialfihar.android.apg.helper.PGPMain;
import org.thialfihar.android.apg.helper.Preferences; import org.thialfihar.android.apg.helper.Preferences;
import org.thialfihar.android.apg.service.ApgServiceHandler; import org.thialfihar.android.apg.service.ApgServiceHandler;
import org.thialfihar.android.apg.service.ApgService; import org.thialfihar.android.apg.service.ApgService;
import org.thialfihar.android.apg.service.PassphraseCacheService;
import org.thialfihar.android.apg.ui.dialog.DeleteFileDialogFragment; import org.thialfihar.android.apg.ui.dialog.DeleteFileDialogFragment;
import org.thialfihar.android.apg.ui.dialog.FileDialogFragment; import org.thialfihar.android.apg.ui.dialog.FileDialogFragment;
import org.thialfihar.android.apg.ui.dialog.PassphraseDialogFragment; import org.thialfihar.android.apg.ui.dialog.PassphraseDialogFragment;
@ -659,7 +660,8 @@ public class EncryptActivity extends SherlockFragmentActivity {
return; return;
} }
if (getSecretKeyId() != 0 && PGPMain.getCachedPassPhrase(getSecretKeyId()) == null) { if (getSecretKeyId() != 0
&& PassphraseCacheService.getCachedPassphrase(this, getSecretKeyId()) == null) {
showPassphraseDialog(); showPassphraseDialog();
return; return;
@ -697,7 +699,7 @@ public class EncryptActivity extends SherlockFragmentActivity {
try { try {
PassphraseDialogFragment passphraseDialog = PassphraseDialogFragment.newInstance( PassphraseDialogFragment passphraseDialog = PassphraseDialogFragment.newInstance(
messenger, mSecretKeyId); EncryptActivity.this, messenger, mSecretKeyId);
passphraseDialog.show(getSupportFragmentManager(), "passphraseDialog"); passphraseDialog.show(getSupportFragmentManager(), "passphraseDialog");
} catch (PGPMain.GeneralException e) { } catch (PGPMain.GeneralException e) {

View File

@ -69,7 +69,8 @@ public class PreferencesActivity extends SherlockPreferenceActivity {
mPreferences.setPassPhraseCacheTtl(Integer.parseInt(newValue.toString())); mPreferences.setPassPhraseCacheTtl(Integer.parseInt(newValue.toString()));
// restart cache service with new ttl // restart cache service with new ttl
PassphraseCacheService.startCacheService(PreferencesActivity.this); // TODO: not needed anymore!
// PassphraseCacheService.startCacheService(PreferencesActivity.this);
return false; return false;
} }
}); });

View File

@ -21,6 +21,7 @@ import org.thialfihar.android.apg.Constants;
import org.thialfihar.android.apg.Id; import org.thialfihar.android.apg.Id;
import org.thialfihar.android.apg.helper.PGPHelper; import org.thialfihar.android.apg.helper.PGPHelper;
import org.thialfihar.android.apg.helper.PGPMain; import org.thialfihar.android.apg.helper.PGPMain;
import org.thialfihar.android.apg.service.PassphraseCacheService;
import org.thialfihar.android.apg.ui.dialog.PassphraseDialogFragment; import org.thialfihar.android.apg.ui.dialog.PassphraseDialogFragment;
import org.thialfihar.android.apg.util.Log; import org.thialfihar.android.apg.util.Log;
@ -137,7 +138,7 @@ public class SecretKeyListActivity extends KeyListActivity implements OnChildCli
public void checkPassPhraseAndEdit() { public void checkPassPhraseAndEdit() {
long keyId = ((KeyListAdapter) mList.getExpandableListAdapter()).getGroupId(mSelectedItem); long keyId = ((KeyListAdapter) mList.getExpandableListAdapter()).getGroupId(mSelectedItem);
String passPhrase = PGPMain.getCachedPassPhrase(keyId); String passPhrase = PassphraseCacheService.getCachedPassphrase(this, keyId);
if (passPhrase == null) { if (passPhrase == null) {
showPassphraseDialog(keyId); showPassphraseDialog(keyId);
} else { } else {
@ -152,7 +153,8 @@ public class SecretKeyListActivity extends KeyListActivity implements OnChildCli
@Override @Override
public void handleMessage(Message message) { public void handleMessage(Message message) {
if (message.what == PassphraseDialogFragment.MESSAGE_OKAY) { if (message.what == PassphraseDialogFragment.MESSAGE_OKAY) {
String passPhrase = PGPMain.getCachedPassPhrase(secretKeyId); String passPhrase = PassphraseCacheService.getCachedPassphrase(
SecretKeyListActivity.this, secretKeyId);
PGPMain.setEditPassPhrase(passPhrase); PGPMain.setEditPassPhrase(passPhrase);
editKey(); editKey();
} }
@ -164,7 +166,7 @@ public class SecretKeyListActivity extends KeyListActivity implements OnChildCli
try { try {
PassphraseDialogFragment passphraseDialog = PassphraseDialogFragment.newInstance( PassphraseDialogFragment passphraseDialog = PassphraseDialogFragment.newInstance(
messenger, secretKeyId); SecretKeyListActivity.this, messenger, secretKeyId);
passphraseDialog.show(getSupportFragmentManager(), "passphraseDialog"); passphraseDialog.show(getSupportFragmentManager(), "passphraseDialog");
} catch (PGPMain.GeneralException e) { } catch (PGPMain.GeneralException e) {

View File

@ -27,6 +27,7 @@ import org.thialfihar.android.apg.helper.PGPMain;
import org.thialfihar.android.apg.helper.Preferences; import org.thialfihar.android.apg.helper.Preferences;
import org.thialfihar.android.apg.service.ApgService; import org.thialfihar.android.apg.service.ApgService;
import org.thialfihar.android.apg.service.ApgServiceHandler; import org.thialfihar.android.apg.service.ApgServiceHandler;
import org.thialfihar.android.apg.service.PassphraseCacheService;
import org.thialfihar.android.apg.ui.dialog.PassphraseDialogFragment; import org.thialfihar.android.apg.ui.dialog.PassphraseDialogFragment;
import com.actionbarsherlock.app.ActionBar; import com.actionbarsherlock.app.ActionBar;
@ -141,7 +142,7 @@ public class SignKeyActivity extends SherlockFragmentActivity {
Messenger messenger = new Messenger(returnHandler); Messenger messenger = new Messenger(returnHandler);
try { try {
PassphraseDialogFragment passphraseDialog = PassphraseDialogFragment.newInstance( PassphraseDialogFragment passphraseDialog = PassphraseDialogFragment.newInstance(this,
messenger, secretKeyId); messenger, secretKeyId);
passphraseDialog.show(getSupportFragmentManager(), "passphraseDialog"); passphraseDialog.show(getSupportFragmentManager(), "passphraseDialog");
@ -175,7 +176,7 @@ public class SignKeyActivity extends SherlockFragmentActivity {
/* /*
* get the user's passphrase for this key (if required) * get the user's passphrase for this key (if required)
*/ */
String passphrase = PGPMain.getCachedPassPhrase(mMasterKeyId); String passphrase = PassphraseCacheService.getCachedPassphrase(this, mMasterKeyId);
if (passphrase == null) { if (passphrase == null) {
showPassphraseDialog(mMasterKeyId); showPassphraseDialog(mMasterKeyId);
return; // bail out; need to wait until the user has entered the passphrase return; // bail out; need to wait until the user has entered the passphrase

View File

@ -31,6 +31,7 @@ import org.thialfihar.android.apg.R;
import android.app.Activity; import android.app.Activity;
import android.app.AlertDialog; import android.app.AlertDialog;
import android.app.Dialog; import android.app.Dialog;
import android.content.Context;
import android.content.DialogInterface; import android.content.DialogInterface;
import android.content.DialogInterface.OnClickListener; import android.content.DialogInterface.OnClickListener;
import android.os.Bundle; import android.os.Bundle;
@ -38,13 +39,22 @@ import android.os.Message;
import android.os.Messenger; import android.os.Messenger;
import android.os.RemoteException; import android.os.RemoteException;
import android.support.v4.app.DialogFragment; import android.support.v4.app.DialogFragment;
import org.thialfihar.android.apg.service.PassphraseCacheService;
import org.thialfihar.android.apg.util.Log; import org.thialfihar.android.apg.util.Log;
import android.view.KeyEvent;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.view.WindowManager.LayoutParams;
import android.view.inputmethod.EditorInfo;
import android.widget.Button;
import android.widget.EditText; import android.widget.EditText;
import android.widget.TextView;
import android.widget.TextView.OnEditorActionListener;
import android.widget.Toast; import android.widget.Toast;
public class PassphraseDialogFragment extends DialogFragment { public class PassphraseDialogFragment extends DialogFragment implements OnEditorActionListener {
private Messenger mMessenger; private Messenger mMessenger;
@ -53,6 +63,8 @@ public class PassphraseDialogFragment extends DialogFragment {
public static final int MESSAGE_OKAY = 1; public static final int MESSAGE_OKAY = 1;
private EditText mPassphraseEditText;
/** /**
* Creates new instance of this dialog fragment * Creates new instance of this dialog fragment
* *
@ -63,11 +75,11 @@ public class PassphraseDialogFragment extends DialogFragment {
* @return * @return
* @throws GeneralException * @throws GeneralException
*/ */
public static PassphraseDialogFragment newInstance(Messenger messenger, long secretKeyId) public static PassphraseDialogFragment newInstance(Context context, Messenger messenger,
throws GeneralException { long secretKeyId) throws GeneralException {
// check if secret key has a passphrase // check if secret key has a passphrase
if (!(secretKeyId == Id.key.symmetric || secretKeyId == Id.key.none)) { if (!(secretKeyId == Id.key.symmetric || secretKeyId == Id.key.none)) {
if (!hasPassphrase(secretKeyId)) { if (!hasPassphrase(context, secretKeyId)) {
throw new PGPMain.GeneralException("No passphrase! No passphrase dialog needed!"); throw new PGPMain.GeneralException("No passphrase! No passphrase dialog needed!");
} }
} }
@ -88,7 +100,7 @@ public class PassphraseDialogFragment extends DialogFragment {
* @param secretKeyId * @param secretKeyId
* @return true if it has a passphrase * @return true if it has a passphrase
*/ */
private static boolean hasPassphrase(long secretKeyId) { private static boolean hasPassphrase(Context context, long secretKeyId) {
// check if the key has no passphrase // check if the key has no passphrase
try { try {
PGPSecretKey secretKey = PGPHelper.getMasterKey(PGPMain.getSecretKeyRing(secretKeyId)); PGPSecretKey secretKey = PGPHelper.getMasterKey(PGPMain.getSecretKeyRing(secretKeyId));
@ -101,7 +113,7 @@ public class PassphraseDialogFragment extends DialogFragment {
Log.d(Constants.TAG, "Key has no passphrase! Caches empty passphrase!"); Log.d(Constants.TAG, "Key has no passphrase! Caches empty passphrase!");
// cache empty passphrase // cache empty passphrase
PGPMain.setCachedPassPhrase(secretKey.getKeyID(), ""); PassphraseCacheService.addCachedPassphrase(context, secretKey.getKeyID(), "");
return false; return false;
} }
@ -112,6 +124,11 @@ public class PassphraseDialogFragment extends DialogFragment {
return true; return true;
} }
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
/** /**
* Creates dialog * Creates dialog
*/ */
@ -154,13 +171,13 @@ public class PassphraseDialogFragment extends DialogFragment {
View view = inflater.inflate(R.layout.passphrase, null); View view = inflater.inflate(R.layout.passphrase, null);
alert.setView(view); alert.setView(view);
final EditText input = (EditText) view.findViewById(R.id.passphrase_passphrase); mPassphraseEditText = (EditText) view.findViewById(R.id.passphrase_passphrase);
alert.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { alert.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) { public void onClick(DialogInterface dialog, int id) {
dismiss(); dismiss();
String passPhrase = input.getText().toString(); String passPhrase = mPassphraseEditText.getText().toString();
long keyId; long keyId;
if (secretKey != null) { if (secretKey != null) {
try { try {
@ -185,7 +202,7 @@ public class PassphraseDialogFragment extends DialogFragment {
// cache the new passphrase // cache the new passphrase
Log.d(Constants.TAG, "Everything okay! Caching entered passphrase"); Log.d(Constants.TAG, "Everything okay! Caching entered passphrase");
PGPMain.setCachedPassPhrase(keyId, passPhrase); PassphraseCacheService.addCachedPassphrase(activity, keyId, passPhrase);
sendMessageToHandler(MESSAGE_OKAY); sendMessageToHandler(MESSAGE_OKAY);
} }
@ -200,6 +217,32 @@ public class PassphraseDialogFragment extends DialogFragment {
return alert.create(); return alert.create();
} }
@Override
public void onActivityCreated(Bundle arg0) {
super.onActivityCreated(arg0);
// request focus and open soft keyboard
mPassphraseEditText.requestFocus();
getDialog().getWindow().setSoftInputMode(LayoutParams.SOFT_INPUT_STATE_VISIBLE);
mPassphraseEditText.setOnEditorActionListener(this);
}
/**
* Associate the "done" button on the soft keyboard with the okay button in the view
*/
@Override
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
if (EditorInfo.IME_ACTION_DONE == actionId) {
AlertDialog dialog = ((AlertDialog) getDialog());
Button bt = dialog.getButton(AlertDialog.BUTTON_POSITIVE);
bt.performClick();
return true;
}
return false;
}
/** /**
* Send message back to handler which is initialized in a activity * Send message back to handler which is initialized in a activity
* *
@ -218,4 +261,5 @@ public class PassphraseDialogFragment extends DialogFragment {
Log.w(Constants.TAG, "Messenger is null!", e); Log.w(Constants.TAG, "Messenger is null!", e);
} }
} }
} }

View File

@ -29,12 +29,19 @@ import android.os.Messenger;
import android.os.RemoteException; import android.os.RemoteException;
import android.support.v4.app.DialogFragment; import android.support.v4.app.DialogFragment;
import org.thialfihar.android.apg.util.Log; import org.thialfihar.android.apg.util.Log;
import android.view.KeyEvent;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.view.WindowManager.LayoutParams;
import android.view.inputmethod.EditorInfo;
import android.widget.Button;
import android.widget.EditText; import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast; import android.widget.Toast;
import android.widget.TextView.OnEditorActionListener;
public class SetPassphraseDialogFragment extends DialogFragment { public class SetPassphraseDialogFragment extends DialogFragment implements OnEditorActionListener {
private Messenger mMessenger; private Messenger mMessenger;
private static final String ARG_MESSENGER = "messenger"; private static final String ARG_MESSENGER = "messenger";
@ -44,6 +51,9 @@ public class SetPassphraseDialogFragment extends DialogFragment {
public static final String MESSAGE_NEW_PASSPHRASE = "new_passphrase"; public static final String MESSAGE_NEW_PASSPHRASE = "new_passphrase";
private EditText mPassphraseEditText;
private EditText mPassphraseAgainEditText;
/** /**
* Creates new instance of this dialog fragment * Creates new instance of this dialog fragment
* *
@ -81,17 +91,17 @@ public class SetPassphraseDialogFragment extends DialogFragment {
LayoutInflater inflater = activity.getLayoutInflater(); LayoutInflater inflater = activity.getLayoutInflater();
View view = inflater.inflate(R.layout.passphrase_repeat, null); View view = inflater.inflate(R.layout.passphrase_repeat, null);
final EditText input1 = (EditText) view.findViewById(R.id.passphrase_passphrase);
final EditText input2 = (EditText) view.findViewById(R.id.passphrase_passphrase_again);
alert.setView(view); alert.setView(view);
mPassphraseEditText = (EditText) view.findViewById(R.id.passphrase_passphrase);
mPassphraseAgainEditText = (EditText) view.findViewById(R.id.passphrase_passphrase_again);
alert.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { alert.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) { public void onClick(DialogInterface dialog, int id) {
dismiss(); dismiss();
String passPhrase1 = input1.getText().toString(); String passPhrase1 = mPassphraseEditText.getText().toString();
String passPhrase2 = input2.getText().toString(); String passPhrase2 = mPassphraseAgainEditText.getText().toString();
if (!passPhrase1.equals(passPhrase2)) { if (!passPhrase1.equals(passPhrase2)) {
Toast.makeText( Toast.makeText(
activity, activity,
@ -127,6 +137,32 @@ public class SetPassphraseDialogFragment extends DialogFragment {
return alert.create(); return alert.create();
} }
@Override
public void onActivityCreated(Bundle arg0) {
super.onActivityCreated(arg0);
// request focus and open soft keyboard
mPassphraseEditText.requestFocus();
getDialog().getWindow().setSoftInputMode(LayoutParams.SOFT_INPUT_STATE_VISIBLE);
mPassphraseAgainEditText.setOnEditorActionListener(this);
}
/**
* Associate the "done" button on the soft keyboard with the okay button in the view
*/
@Override
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
if (EditorInfo.IME_ACTION_DONE == actionId) {
AlertDialog dialog = ((AlertDialog) getDialog());
Button bt = dialog.getButton(AlertDialog.BUTTON_POSITIVE);
bt.performClick();
return true;
}
return false;
}
/** /**
* Send message back to handler which is initialized in a activity * Send message back to handler which is initialized in a activity
* *

View File

@ -23,6 +23,7 @@ import org.thialfihar.android.apg.helper.PGPMain;
import org.thialfihar.android.apg.helper.PGPConversionHelper; import org.thialfihar.android.apg.helper.PGPConversionHelper;
import org.thialfihar.android.apg.service.ApgServiceHandler; import org.thialfihar.android.apg.service.ApgServiceHandler;
import org.thialfihar.android.apg.service.ApgService; import org.thialfihar.android.apg.service.ApgService;
import org.thialfihar.android.apg.service.PassphraseCacheService;
import org.thialfihar.android.apg.ui.dialog.ProgressDialogFragment; import org.thialfihar.android.apg.ui.dialog.ProgressDialogFragment;
import org.thialfihar.android.apg.ui.widget.Editor.EditorListener; import org.thialfihar.android.apg.ui.widget.Editor.EditorListener;
import org.thialfihar.android.apg.util.Choice; import org.thialfihar.android.apg.util.Choice;
@ -259,7 +260,8 @@ public class SectionView extends LinearLayout implements OnClickListener, Editor
String passPhrase; String passPhrase;
if (mEditors.getChildCount() > 0) { if (mEditors.getChildCount() > 0) {
PGPSecretKey masterKey = ((KeyEditor) mEditors.getChildAt(0)).getValue(); PGPSecretKey masterKey = ((KeyEditor) mEditors.getChildAt(0)).getValue();
passPhrase = PGPMain.getCachedPassPhrase(masterKey.getKeyID()); passPhrase = PassphraseCacheService
.getCachedPassphrase(mActivity, masterKey.getKeyID());
data.putByteArray(ApgService.MASTER_KEY, data.putByteArray(ApgService.MASTER_KEY,
PGPConversionHelper.PGPSecretKeyToBytes(masterKey)); PGPConversionHelper.PGPSecretKeyToBytes(masterKey));

View File

@ -20,7 +20,6 @@ import java.io.Serializable;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Date; import java.util.Date;
import java.util.List; import java.util.List;
import java.util.Vector;
import android.os.Parcel; import android.os.Parcel;
import android.os.Parcelable; import android.os.Parcelable;