From 0e14dcb290d5b3c7acae1da5a64e611e293c7f06 Mon Sep 17 00:00:00 2001 From: Thialfihar Date: Fri, 23 Apr 2010 17:00:52 +0000 Subject: [PATCH] layout adjustments, force portrait layout for EncryptFileActivity for now, (En|De)cryptFileActivity can now handle symmetric algorithms --- AndroidManifest.xml | 1 + res/layout/encrypt_file.xml | 365 +++++++++--------- res/values/strings.xml | 4 +- src/org/thialfihar/android/apg/Apg.java | 142 +++++-- .../apg/AskForSecretKeyPassPhrase.java | 60 ++- .../android/apg/DecryptFileActivity.java | 14 +- .../android/apg/DecryptMessageActivity.java | 2 +- .../android/apg/EncryptFileActivity.java | 2 +- .../thialfihar/android/apg/MainActivity.java | 2 +- 9 files changed, 337 insertions(+), 255 deletions(-) diff --git a/AndroidManifest.xml b/AndroidManifest.xml index b9dd47fd0..1475080c2 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -74,6 +74,7 @@ diff --git a/res/layout/encrypt_file.xml b/res/layout/encrypt_file.xml index 0c388fbe7..2945cf89f 100644 --- a/res/layout/encrypt_file.xml +++ b/res/layout/encrypt_file.xml @@ -21,204 +21,213 @@ android:orientation="vertical" android:paddingTop="5dip"> - - - - - - - - - - - - - - - - - - - + android:orientation="horizontal" + android:paddingLeft="5dip"> - + android:text="@string/label_file"/> + + + + + + + + + + + + + + + + + + + + + android:layout_gravity="right|center_vertical"/> + + + + + + + + + + + + android:layout_height="fill_parent" + android:orientation="vertical"> + + + + + + + + + + + + + + + + + android:layout_height="wrap_content"/> + + + + + + + + + + + + + + + + + + + + + + + + + + - - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + Save Cancel - Symmetric - Asymmetric + Use Pass Phrase + Use Public Key About Add GMail Account diff --git a/src/org/thialfihar/android/apg/Apg.java b/src/org/thialfihar/android/apg/Apg.java index a5daca0cb..79e0a8758 100644 --- a/src/org/thialfihar/android/apg/Apg.java +++ b/src/org/thialfihar/android/apg/Apg.java @@ -41,6 +41,7 @@ import java.util.Comparator; import java.util.Date; import java.util.GregorianCalendar; import java.util.HashMap; +import java.util.Iterator; import java.util.Vector; import java.util.regex.Pattern; @@ -66,6 +67,7 @@ import org.bouncycastle2.openpgp.PGPLiteralDataGenerator; import org.bouncycastle2.openpgp.PGPObjectFactory; import org.bouncycastle2.openpgp.PGPOnePassSignature; import org.bouncycastle2.openpgp.PGPOnePassSignatureList; +import org.bouncycastle2.openpgp.PGPPBEEncryptedData; import org.bouncycastle2.openpgp.PGPPrivateKey; import org.bouncycastle2.openpgp.PGPPublicKey; import org.bouncycastle2.openpgp.PGPPublicKeyEncryptedData; @@ -1361,24 +1363,60 @@ public class Apg { throw new GeneralException("data not valid encryption data"); } + // TODO: currently we always only look at the first known key // find the secret key PGPSecretKey secretKey = null; - for (PGPPublicKeyEncryptedData pbe : - new IterableIterator(enc.getEncryptedDataObjects())) { - secretKey = findSecretKey(pbe.getKeyID()); - if (secretKey != null) { - break; + Iterator it = enc.getEncryptedDataObjects(); + while (it.hasNext()) { + Object obj = it.next(); + if (obj instanceof PGPPublicKeyEncryptedData) { + PGPPublicKeyEncryptedData pbe = (PGPPublicKeyEncryptedData) obj; + secretKey = findSecretKey(pbe.getKeyID()); + if (secretKey != null) { + break; + } } } + if (secretKey == null) { - throw new GeneralException("couldn't find a secret key to decrypt"); + return 0; } return secretKey.getKeyID(); } + public static boolean hasSymmetricEncryption(InputStream inStream) + throws GeneralException, IOException { + InputStream in = PGPUtil.getDecoderStream(inStream); + PGPObjectFactory pgpF = new PGPObjectFactory(in); + PGPEncryptedDataList enc; + Object o = pgpF.nextObject(); + + // the first object might be a PGP marker packet. + if (o instanceof PGPEncryptedDataList) { + enc = (PGPEncryptedDataList) o; + } else { + enc = (PGPEncryptedDataList) pgpF.nextObject(); + } + + if (enc == null) { + throw new GeneralException("data not valid encryption data"); + } + + Iterator it = enc.getEncryptedDataObjects(); + while (it.hasNext()) { + Object obj = it.next(); + if (obj instanceof PGPPBEEncryptedData) { + return true; + } + } + + return false; + } + public static Bundle decrypt(InputStream inStream, OutputStream outStream, - String passPhrase, ProgressDialogUpdater progress) + String passPhrase, ProgressDialogUpdater progress, + boolean assumeSymmetric) throws IOException, GeneralException, PGPException, SignatureException { Bundle returnData = new Bundle(); InputStream in = PGPUtil.getDecoderStream(inStream); @@ -1399,32 +1437,66 @@ public class Apg { throw new GeneralException("data not valid encryption data"); } - progress.setProgress("finding key...", 10, 100); - // find the secret key - PGPPublicKeyEncryptedData pbe = null; - PGPSecretKey secretKey = null; - for (PGPPublicKeyEncryptedData encData : - new IterableIterator(enc.getEncryptedDataObjects())) { - secretKey = findSecretKey(encData.getKeyID()); - if (secretKey != null) { - pbe = encData; - break; + InputStream clear = null; + PGPEncryptedData encryptedData = null; + + // TODO: currently we always only look at the first known key or symmetric encryption, + // there might be more... + if (assumeSymmetric) { + PGPPBEEncryptedData pbe = null; + Iterator it = enc.getEncryptedDataObjects(); + // find secret key + while (it.hasNext()) { + Object obj = it.next(); + if (obj instanceof PGPPBEEncryptedData) { + pbe = (PGPPBEEncryptedData) obj; + break; + } } - } - if (secretKey == null) { - throw new GeneralException("couldn't find a secret key to decrypt"); + + if (pbe == null) { + throw new GeneralException("couldn't find a packet with symmetric encryption"); + } + + progress.setProgress("decrypting data...", 20, 100); + clear = pbe.getDataStream(passPhrase.toCharArray(), new BouncyCastleProvider()); + encryptedData = pbe; + } else { + progress.setProgress("finding key...", 10, 100); + PGPPublicKeyEncryptedData pbe = null; + PGPSecretKey secretKey = null; + Iterator it = enc.getEncryptedDataObjects(); + // find secret key + while (it.hasNext()) { + Object obj = it.next(); + if (obj instanceof PGPPublicKeyEncryptedData) { + PGPPublicKeyEncryptedData encData = (PGPPublicKeyEncryptedData) obj; + secretKey = findSecretKey(encData.getKeyID()); + if (secretKey != null) { + pbe = encData; + break; + } + } + } + + if (secretKey == null) { + throw new GeneralException("couldn't find a secret key to decrypt"); + } + + progress.setProgress("extracting key...", 20, 100); + PGPPrivateKey privateKey = null; + try { + privateKey = secretKey.extractPrivateKey(passPhrase.toCharArray(), + new BouncyCastleProvider()); + } catch (PGPException e) { + throw new PGPException("wrong pass phrase"); + } + + progress.setProgress("decrypting data...", 30, 100); + clear = pbe.getDataStream(privateKey, new BouncyCastleProvider()); + encryptedData = pbe; } - progress.setProgress("extracting key...", 20, 100); - PGPPrivateKey privateKey = null; - try { - privateKey = secretKey.extractPrivateKey(passPhrase.toCharArray(), - new BouncyCastleProvider()); - } catch (PGPException e) { - throw new PGPException("wrong pass phrase"); - } - progress.setProgress("decrypting data...", 30, 100); - InputStream clear = pbe.getDataStream(privateKey, new BouncyCastleProvider()); PGPObjectFactory plainFact = new PGPObjectFactory(clear); Object dataChunk = plainFact.nextObject(); PGPOnePassSignature signature = null; @@ -1511,15 +1583,15 @@ public class Apg { } // TODO: add integrity somewhere - if (pbe.isIntegrityProtected()) { + if (encryptedData.isIntegrityProtected()) { progress.setProgress("verifying integrity...", 90, 100); - if (!pbe.verify()) { - System.err.println("message failed integrity check"); + if (encryptedData.verify()) { + // passed } else { - System.err.println("message integrity check passed"); + // failed } } else { - System.err.println("no message integrity check"); + // no integrity check } progress.setProgress("done.", 100, 100); diff --git a/src/org/thialfihar/android/apg/AskForSecretKeyPassPhrase.java b/src/org/thialfihar/android/apg/AskForSecretKeyPassPhrase.java index 06e9c6d0a..a6fe557c4 100644 --- a/src/org/thialfihar/android/apg/AskForSecretKeyPassPhrase.java +++ b/src/org/thialfihar/android/apg/AskForSecretKeyPassPhrase.java @@ -26,9 +26,6 @@ import android.app.Dialog; import android.content.DialogInterface; import android.text.InputType; import android.text.method.PasswordTransformationMethod; -import android.view.KeyEvent; -import android.view.View; -import android.view.View.OnKeyListener; import android.view.ViewGroup.LayoutParams; import android.widget.EditText; import android.widget.LinearLayout; @@ -43,36 +40,25 @@ public class AskForSecretKeyPassPhrase { PassPhraseCallbackInterface callback) { AlertDialog.Builder alert = new AlertDialog.Builder(context); - final PGPSecretKey secretKey = - Apg.getMasterKey(Apg.findSecretKeyRing(secretKeyId)); - if (secretKey == null) { - return null; - } - - String userId = Apg.getMainUserIdSafe(context, secretKey); - alert.setTitle(R.string.title_authentification); - alert.setMessage("Pass phrase for " + userId); + + final PGPSecretKey secretKey; + + if (secretKeyId == 0) { + secretKey = null; + alert.setMessage("Pass phrase"); + } else { + secretKey = Apg.getMasterKey(Apg.findSecretKeyRing(secretKeyId)); + if (secretKey == null) { + return null; + } + String userId = Apg.getMainUserIdSafe(context, secretKey); + alert.setMessage("Pass phrase for " + userId); + } final EditText input = new EditText(context); input.setInputType(InputType.TYPE_TEXT_VARIATION_PASSWORD); input.setTransformationMethod(new PasswordTransformationMethod()); - input.setOnKeyListener(new OnKeyListener() { - public boolean onKey(View v, int keyCode, KeyEvent event) { - // If the event is a key-down event on the "enter" button - if (event.getAction() == KeyEvent.ACTION_DOWN && - keyCode == KeyEvent.KEYCODE_ENTER) { - try { - ((AlertDialog) v.getParent()).getButton(AlertDialog.BUTTON_POSITIVE) - .performClick(); - } catch (ClassCastException e) { - // don't do anything if we're not in that dialog - } - return true; - } - return false; - } - }); // 5dip padding int padding = (int) (10 * context.getResources().getDisplayMetrics().densityDpi / 160); LinearLayout layout = new LinearLayout(context); @@ -91,14 +77,16 @@ public class AskForSecretKeyPassPhrase { public void onClick(DialogInterface dialog, int id) { activity.removeDialog(Id.dialog.pass_phrase); String passPhrase = "" + input.getText(); - try { - secretKey.extractPrivateKey(passPhrase.toCharArray(), - new BouncyCastleProvider()); - } catch (PGPException e) { - Toast.makeText(activity, - R.string.wrong_pass_phrase, - Toast.LENGTH_SHORT).show(); - return; + if (secretKey != null) { + try { + secretKey.extractPrivateKey(passPhrase.toCharArray(), + new BouncyCastleProvider()); + } catch (PGPException e) { + Toast.makeText(activity, + R.string.wrong_pass_phrase, + Toast.LENGTH_SHORT).show(); + return; + } } cb.passPhraseCallback(passPhrase); } diff --git a/src/org/thialfihar/android/apg/DecryptFileActivity.java b/src/org/thialfihar/android/apg/DecryptFileActivity.java index 47be98344..8e21eae11 100644 --- a/src/org/thialfihar/android/apg/DecryptFileActivity.java +++ b/src/org/thialfihar/android/apg/DecryptFileActivity.java @@ -59,6 +59,8 @@ public class DecryptFileActivity extends BaseActivity { private String mInputFilename = null; private String mOutputFilename = null; + private boolean mAssumeSymmetricEncryption = false; + @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -128,6 +130,16 @@ public class DecryptFileActivity extends BaseActivity { try { InputStream in = new FileInputStream(mInputFilename); setSecretKeyId(Apg.getDecryptionKeyId(in)); + if (getSecretKeyId() == 0) { + // reopen the file to check whether there's symmetric encryption data in there + in = new FileInputStream(mInputFilename); + if (!Apg.hasSymmetricEncryption(in)) { + throw new Apg.GeneralException("no suitable keys found"); + } + mAssumeSymmetricEncryption = true; + } else { + mAssumeSymmetricEncryption = false; + } showDialog(Id.dialog.pass_phrase); } catch (FileNotFoundException e) { error = "file not found: " + e.getLocalizedMessage(); @@ -168,7 +180,7 @@ public class DecryptFileActivity extends BaseActivity { InputStream in = new FileInputStream(mInputFilename); ByteArrayOutputStream out = new ByteArrayOutputStream(); - data = Apg.decrypt(in, out, Apg.getPassPhrase(), this); + data = Apg.decrypt(in, out, Apg.getPassPhrase(), this, mAssumeSymmetricEncryption); out.close(); OutputStream fileOut = new FileOutputStream(mOutputFilename); diff --git a/src/org/thialfihar/android/apg/DecryptMessageActivity.java b/src/org/thialfihar/android/apg/DecryptMessageActivity.java index 3f2a74987..d3a84eff5 100644 --- a/src/org/thialfihar/android/apg/DecryptMessageActivity.java +++ b/src/org/thialfihar/android/apg/DecryptMessageActivity.java @@ -204,7 +204,7 @@ public class DecryptMessageActivity extends BaseActivity { if (mSignedOnly) { data = Apg.verifyText(in, out, this); } else { - data = Apg.decrypt(in, out, Apg.getPassPhrase(), this); + data = Apg.decrypt(in, out, Apg.getPassPhrase(), this, false); } out.close(); diff --git a/src/org/thialfihar/android/apg/EncryptFileActivity.java b/src/org/thialfihar/android/apg/EncryptFileActivity.java index ce2cf9031..3f7db2b72 100644 --- a/src/org/thialfihar/android/apg/EncryptFileActivity.java +++ b/src/org/thialfihar/android/apg/EncryptFileActivity.java @@ -101,7 +101,7 @@ public class EncryptFileActivity extends BaseActivity { TabSpec ts2 = mTabHost.newTabSpec(TAB_SYMMETRIC); ts2.setIndicator(getString(R.string.tab_symmetric), - getResources().getDrawable(R.drawable.encrypted)); + getResources().getDrawable(R.drawable.key)); ts2.setContent(R.id.tab_symmetric); mTabHost.addTab(ts2); diff --git a/src/org/thialfihar/android/apg/MainActivity.java b/src/org/thialfihar/android/apg/MainActivity.java index d6b04ce92..5562d6a7a 100644 --- a/src/org/thialfihar/android/apg/MainActivity.java +++ b/src/org/thialfihar/android/apg/MainActivity.java @@ -224,7 +224,7 @@ public class MainActivity extends BaseActivity { new SpannableString("Read the warnings!\n\n" + "Changes:\n" + " * OI File Manager support\n" + - " * file encryption\n" + + " * file encryption/decryption\n" + "\n" + "WARNING: be careful editing your existing keys, as they " + "WILL be stripped of certificates right now.\n" +