Enforce private key for applications, verify signed-only texts without passphrase input, better internal decrypt and verify method

This commit is contained in:
Dominik Schürmann 2014-03-04 20:53:44 +01:00
parent 89a4c38cc0
commit 06f9134eb1
6 changed files with 266 additions and 162 deletions

View File

@ -38,18 +38,34 @@ public class OpenPgpSignatureResult implements Parcelable {
return status; return status;
} }
public void setStatus(int status) {
this.status = status;
}
public boolean isSignatureOnly() { public boolean isSignatureOnly() {
return signatureOnly; return signatureOnly;
} }
public void setSignatureOnly(boolean signatureOnly) {
this.signatureOnly = signatureOnly;
}
public String getUserId() { public String getUserId() {
return userId; return userId;
} }
public void setUserId(String userId) {
this.userId = userId;
}
public long getKeyId() { public long getKeyId() {
return keyId; return keyId;
} }
public void setKeyId(long keyId) {
this.keyId = keyId;
}
public OpenPgpSignatureResult() { public OpenPgpSignatureResult() {
} }

View File

@ -18,8 +18,8 @@
package org.sufficientlysecure.keychain.pgp; package org.sufficientlysecure.keychain.pgp;
import android.content.Context; import android.content.Context;
import android.os.Bundle;
import org.openintents.openpgp.OpenPgpSignatureResult;
import org.spongycastle.bcpg.ArmoredInputStream; import org.spongycastle.bcpg.ArmoredInputStream;
import org.spongycastle.bcpg.SignatureSubpacketTags; import org.spongycastle.bcpg.SignatureSubpacketTags;
import org.spongycastle.openpgp.PGPCompressedData; import org.spongycastle.openpgp.PGPCompressedData;
@ -36,6 +36,7 @@ import org.spongycastle.openpgp.PGPPublicKey;
import org.spongycastle.openpgp.PGPPublicKeyEncryptedData; import org.spongycastle.openpgp.PGPPublicKeyEncryptedData;
import org.spongycastle.openpgp.PGPPublicKeyRing; import org.spongycastle.openpgp.PGPPublicKeyRing;
import org.spongycastle.openpgp.PGPSecretKey; import org.spongycastle.openpgp.PGPSecretKey;
import org.spongycastle.openpgp.PGPSecretKeyRing;
import org.spongycastle.openpgp.PGPSignature; import org.spongycastle.openpgp.PGPSignature;
import org.spongycastle.openpgp.PGPSignatureList; import org.spongycastle.openpgp.PGPSignatureList;
import org.spongycastle.openpgp.PGPSignatureSubpacketVector; import org.spongycastle.openpgp.PGPSignatureSubpacketVector;
@ -53,7 +54,7 @@ import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException; import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
import org.sufficientlysecure.keychain.provider.ProviderHelper; import org.sufficientlysecure.keychain.provider.ProviderHelper;
import org.sufficientlysecure.keychain.service.KeychainIntentService; import org.sufficientlysecure.keychain.service.PassphraseCacheService;
import org.sufficientlysecure.keychain.util.InputData; import org.sufficientlysecure.keychain.util.InputData;
import org.sufficientlysecure.keychain.util.Log; import org.sufficientlysecure.keychain.util.Log;
import org.sufficientlysecure.keychain.util.ProgressDialogUpdater; import org.sufficientlysecure.keychain.util.ProgressDialogUpdater;
@ -75,9 +76,10 @@ public class PgpDecryptVerify {
private InputData data; private InputData data;
private OutputStream outStream; private OutputStream outStream;
private ProgressDialogUpdater progress; private ProgressDialogUpdater progressDialogUpdater;
boolean assumeSymmetric; private boolean assumeSymmetric;
String passphrase; private String passphrase;
private long enforcedKeyId;
private PgpDecryptVerify(Builder builder) { private PgpDecryptVerify(Builder builder) {
// private Constructor can only be called from Builder // private Constructor can only be called from Builder
@ -85,9 +87,10 @@ public class PgpDecryptVerify {
this.data = builder.data; this.data = builder.data;
this.outStream = builder.outStream; this.outStream = builder.outStream;
this.progress = builder.progress; this.progressDialogUpdater = builder.progressDialogUpdater;
this.assumeSymmetric = builder.assumeSymmetric; this.assumeSymmetric = builder.assumeSymmetric;
this.passphrase = builder.passphrase; this.passphrase = builder.passphrase;
this.enforcedKeyId = builder.enforcedKeyId;
} }
public static class Builder { public static class Builder {
@ -97,9 +100,10 @@ public class PgpDecryptVerify {
private OutputStream outStream; private OutputStream outStream;
// optional // optional
private ProgressDialogUpdater progress = null; private ProgressDialogUpdater progressDialogUpdater = null;
private boolean assumeSymmetric = false; private boolean assumeSymmetric = false;
private String passphrase = ""; private String passphrase = "";
private long enforcedKeyId = 0;
public Builder(Context context, InputData data, OutputStream outStream) { public Builder(Context context, InputData data, OutputStream outStream) {
this.context = context; this.context = context;
@ -107,8 +111,8 @@ public class PgpDecryptVerify {
this.outStream = outStream; this.outStream = outStream;
} }
public Builder progress(ProgressDialogUpdater progress) { public Builder progressDialogUpdater(ProgressDialogUpdater progressDialogUpdater) {
this.progress = progress; this.progressDialogUpdater = progressDialogUpdater;
return this; return this;
} }
@ -122,20 +126,32 @@ public class PgpDecryptVerify {
return this; return this;
} }
/**
* Allow this key id alone for decryption.
* This means only ciphertexts encrypted for this private key can be decrypted.
*
* @param enforcedKeyId
* @return
*/
public Builder enforcedKeyId(long enforcedKeyId) {
this.enforcedKeyId = enforcedKeyId;
return this;
}
public PgpDecryptVerify build() { public PgpDecryptVerify build() {
return new PgpDecryptVerify(this); return new PgpDecryptVerify(this);
} }
} }
public void updateProgress(int message, int current, int total) { public void updateProgress(int message, int current, int total) {
if (progress != null) { if (progressDialogUpdater != null) {
progress.setProgress(message, current, total); progressDialogUpdater.setProgress(message, current, total);
} }
} }
public void updateProgress(int current, int total) { public void updateProgress(int current, int total) {
if (progress != null) { if (progressDialogUpdater != null) {
progress.setProgress(current, total); progressDialogUpdater.setProgress(current, total);
} }
} }
@ -177,9 +193,8 @@ public class PgpDecryptVerify {
* @throws PGPException * @throws PGPException
* @throws SignatureException * @throws SignatureException
*/ */
public Bundle execute() public PgpDecryptVerifyResult execute()
throws IOException, PgpGeneralException, PGPException, SignatureException { throws IOException, PgpGeneralException, PGPException, SignatureException {
// automatically works with ascii armor input and binary // automatically works with ascii armor input and binary
InputStream in = PGPUtil.getDecoderStream(data.getInputStream()); InputStream in = PGPUtil.getDecoderStream(data.getInputStream());
if (in instanceof ArmoredInputStream) { if (in instanceof ArmoredInputStream) {
@ -207,9 +222,9 @@ public class PgpDecryptVerify {
* @throws PGPException * @throws PGPException
* @throws SignatureException * @throws SignatureException
*/ */
private Bundle decryptVerify(InputStream in) private PgpDecryptVerifyResult decryptVerify(InputStream in)
throws IOException, PgpGeneralException, PGPException, SignatureException { throws IOException, PgpGeneralException, PGPException, SignatureException {
Bundle returnData = new Bundle(); PgpDecryptVerifyResult returnData = new PgpDecryptVerifyResult();
PGPObjectFactory pgpF = new PGPObjectFactory(in); PGPObjectFactory pgpF = new PGPObjectFactory(in);
PGPEncryptedDataList enc; PGPEncryptedDataList enc;
@ -277,9 +292,38 @@ public class PgpDecryptVerify {
PGPPublicKeyEncryptedData encData = (PGPPublicKeyEncryptedData) obj; PGPPublicKeyEncryptedData encData = (PGPPublicKeyEncryptedData) obj;
secretKey = ProviderHelper.getPGPSecretKeyByKeyId(context, encData.getKeyID()); secretKey = ProviderHelper.getPGPSecretKeyByKeyId(context, encData.getKeyID());
if (secretKey != null) { if (secretKey != null) {
// secret key exists in database
// allow only a specific key for decryption?
if (enforcedKeyId != 0) {
// TODO: improve this code! get master key directly!
PGPSecretKeyRing secretKeyRing = ProviderHelper.getPGPSecretKeyRingByKeyId(context, encData.getKeyID());
long masterKeyId = PgpKeyHelper.getMasterKey(secretKeyRing).getKeyID();
Log.d(Constants.TAG, "encData.getKeyID():" + encData.getKeyID());
Log.d(Constants.TAG, "enforcedKeyId: " + enforcedKeyId);
Log.d(Constants.TAG, "masterKeyId: " + masterKeyId);
if (enforcedKeyId != masterKeyId) {
throw new PgpGeneralException(context.getString(R.string.error_no_secret_key_found));
}
}
pbe = encData; pbe = encData;
// passphrase handling...
if (passphrase == null) {
// try to get cached passphrase
passphrase = PassphraseCacheService.getCachedPassphrase(context, encData.getKeyID());
}
// if passphrase was not cached, return here!
if (passphrase == null) {
returnData.setKeyPassphraseNeeded(true);
return returnData;
}
break; break;
} }
} }
} }
@ -317,6 +361,7 @@ public class PgpDecryptVerify {
PGPObjectFactory plainFact = new PGPObjectFactory(clear); PGPObjectFactory plainFact = new PGPObjectFactory(clear);
Object dataChunk = plainFact.nextObject(); Object dataChunk = plainFact.nextObject();
PGPOnePassSignature signature = null; PGPOnePassSignature signature = null;
OpenPgpSignatureResult signatureResult = null;
PGPPublicKey signatureKey = null; PGPPublicKey signatureKey = null;
int signatureIndex = -1; int signatureIndex = -1;
@ -334,7 +379,7 @@ public class PgpDecryptVerify {
if (dataChunk instanceof PGPOnePassSignatureList) { if (dataChunk instanceof PGPOnePassSignatureList) {
updateProgress(R.string.progress_processing_signature, currentProgress, 100); updateProgress(R.string.progress_processing_signature, currentProgress, 100);
returnData.putBoolean(KeychainIntentService.RESULT_SIGNATURE, true); signatureResult = new OpenPgpSignatureResult();
PGPOnePassSignatureList sigList = (PGPOnePassSignatureList) dataChunk; PGPOnePassSignatureList sigList = (PGPOnePassSignatureList) dataChunk;
for (int i = 0; i < sigList.size(); ++i) { for (int i = 0; i < sigList.size(); ++i) {
signature = sigList.get(i); signature = sigList.get(i);
@ -354,12 +399,12 @@ public class PgpDecryptVerify {
if (signKeyRing != null) { if (signKeyRing != null) {
userId = PgpKeyHelper.getMainUserId(PgpKeyHelper.getMasterKey(signKeyRing)); userId = PgpKeyHelper.getMainUserId(PgpKeyHelper.getMasterKey(signKeyRing));
} }
returnData.putString(KeychainIntentService.RESULT_SIGNATURE_USER_ID, userId); signatureResult.setUserId(userId);
break; break;
} }
} }
returnData.putLong(KeychainIntentService.RESULT_SIGNATURE_KEY_ID, signatureKeyId); signatureResult.setKeyId(signatureKeyId);
if (signature != null) { if (signature != null) {
JcaPGPContentVerifierBuilderProvider contentVerifierBuilderProvider = new JcaPGPContentVerifierBuilderProvider() JcaPGPContentVerifierBuilderProvider contentVerifierBuilderProvider = new JcaPGPContentVerifierBuilderProvider()
@ -367,7 +412,7 @@ public class PgpDecryptVerify {
signature.init(contentVerifierBuilderProvider, signatureKey); signature.init(contentVerifierBuilderProvider, signatureKey);
} else { } else {
returnData.putBoolean(KeychainIntentService.RESULT_SIGNATURE_UNKNOWN, true); signatureResult.setStatus(OpenPgpSignatureResult.SIGNATURE_UNKNOWN_PUB_KEY);
} }
dataChunk = plainFact.nextObject(); dataChunk = plainFact.nextObject();
@ -395,25 +440,24 @@ public class PgpDecryptVerify {
} }
int n; int n;
// TODO: progress calculation is broken here! Try to rework it based on commented code! // TODO: progressDialogUpdater calculation is broken here! Try to rework it based on commented code!
// int progress = 0; // int progressDialogUpdater = 0;
long startPos = data.getStreamPosition(); long startPos = data.getStreamPosition();
while ((n = dataIn.read(buffer)) > 0) { while ((n = dataIn.read(buffer)) > 0) {
outStream.write(buffer, 0, n); outStream.write(buffer, 0, n);
// progress += n; // progressDialogUpdater += n;
if (signature != null) { if (signature != null) {
try { try {
signature.update(buffer, 0, n); signature.update(buffer, 0, n);
} catch (SignatureException e) { } catch (SignatureException e) {
returnData signatureResult.setStatus(OpenPgpSignatureResult.SIGNATURE_ERROR);
.putBoolean(KeychainIntentService.RESULT_SIGNATURE_SUCCESS, false);
signature = null; signature = null;
} }
} }
// TODO: dead code?! // TODO: dead code?!
// unknown size, but try to at least have a moving, slowing down progress bar // unknown size, but try to at least have a moving, slowing down progressDialogUpdater bar
// currentProgress = startProgress + (endProgress - startProgress) * progress // currentProgress = startProgress + (endProgress - startProgress) * progressDialogUpdater
// / (progress + 100000); // / (progressDialogUpdater + 100000);
if (data.getSize() - startPos == 0) { if (data.getSize() - startPos == 0) {
currentProgress = endProgress; currentProgress = endProgress;
} else { } else {
@ -430,17 +474,20 @@ public class PgpDecryptVerify {
PGPSignature messageSignature = signatureList.get(signatureIndex); PGPSignature messageSignature = signatureList.get(signatureIndex);
// these are not cleartext signatures! // these are not cleartext signatures!
returnData.putBoolean(KeychainIntentService.RESULT_CLEARTEXT_SIGNATURE_ONLY, false); // TODO: what about binary signatures?
signatureResult.setSignatureOnly(false);
//Now check binding signatures //Now check binding signatures
boolean keyBinding_isok = verifyKeyBinding(context, messageSignature, signatureKey); boolean keyBinding_isok = verifyKeyBinding(context, messageSignature, signatureKey);
boolean sig_isok = signature.verify(messageSignature); boolean sig_isok = signature.verify(messageSignature);
returnData.putBoolean(KeychainIntentService.RESULT_SIGNATURE_SUCCESS, keyBinding_isok & sig_isok); // TODO: implement CERTIFIED!
if (keyBinding_isok & sig_isok) {
signatureResult.setStatus(OpenPgpSignatureResult.SIGNATURE_SUCCESS_UNCERTIFIED);
}
} }
} }
// TODO: test if this integrity really check works!
if (encryptedData.isIntegrityProtected()) { if (encryptedData.isIntegrityProtected()) {
updateProgress(R.string.progress_verifying_integrity, 95, 100); updateProgress(R.string.progress_verifying_integrity, 95, 100);
@ -455,9 +502,12 @@ public class PgpDecryptVerify {
} else { } else {
// no integrity check // no integrity check
Log.e(Constants.TAG, "Encrypted data was not integrity protected!"); Log.e(Constants.TAG, "Encrypted data was not integrity protected!");
// TODO: inform user?
} }
updateProgress(R.string.progress_done, 100, 100); updateProgress(R.string.progress_done, 100, 100);
returnData.setSignatureResult(signatureResult);
return returnData; return returnData;
} }
@ -474,11 +524,12 @@ public class PgpDecryptVerify {
* @throws PGPException * @throws PGPException
* @throws SignatureException * @throws SignatureException
*/ */
private Bundle verifyCleartextSignature(ArmoredInputStream aIn) private PgpDecryptVerifyResult verifyCleartextSignature(ArmoredInputStream aIn)
throws IOException, PgpGeneralException, PGPException, SignatureException { throws IOException, PgpGeneralException, PGPException, SignatureException {
Bundle returnData = new Bundle(); PgpDecryptVerifyResult returnData = new PgpDecryptVerifyResult();
OpenPgpSignatureResult signatureResult = new OpenPgpSignatureResult();
// cleartext signatures are never encrypted ;) // cleartext signatures are never encrypted ;)
returnData.putBoolean(KeychainIntentService.RESULT_CLEARTEXT_SIGNATURE_ONLY, true); signatureResult.setSignatureOnly(true);
ByteArrayOutputStream out = new ByteArrayOutputStream(); ByteArrayOutputStream out = new ByteArrayOutputStream();
@ -504,8 +555,6 @@ public class PgpDecryptVerify {
byte[] clearText = out.toByteArray(); byte[] clearText = out.toByteArray();
outStream.write(clearText); outStream.write(clearText);
returnData.putBoolean(KeychainIntentService.RESULT_SIGNATURE, true);
updateProgress(R.string.progress_processing_signature, 60, 100); updateProgress(R.string.progress_processing_signature, 60, 100);
PGPObjectFactory pgpFact = new PGPObjectFactory(aIn); PGPObjectFactory pgpFact = new PGPObjectFactory(aIn);
@ -533,15 +582,15 @@ public class PgpDecryptVerify {
if (signKeyRing != null) { if (signKeyRing != null) {
userId = PgpKeyHelper.getMainUserId(PgpKeyHelper.getMasterKey(signKeyRing)); userId = PgpKeyHelper.getMainUserId(PgpKeyHelper.getMasterKey(signKeyRing));
} }
returnData.putString(KeychainIntentService.RESULT_SIGNATURE_USER_ID, userId); signatureResult.setUserId(userId);
break; break;
} }
} }
returnData.putLong(KeychainIntentService.RESULT_SIGNATURE_KEY_ID, signatureKeyId); signatureResult.setKeyId(signatureKeyId);
if (signature == null) { if (signature == null) {
returnData.putBoolean(KeychainIntentService.RESULT_SIGNATURE_UNKNOWN, true); signatureResult.setStatus(OpenPgpSignatureResult.SIGNATURE_UNKNOWN_PUB_KEY);
updateProgress(R.string.progress_done, 100, 100); updateProgress(R.string.progress_done, 100, 100);
return returnData; return returnData;
} }
@ -574,9 +623,15 @@ public class PgpDecryptVerify {
//Now check binding signatures //Now check binding signatures
boolean keyBinding_isok = verifyKeyBinding(context, signature, signatureKey); boolean keyBinding_isok = verifyKeyBinding(context, signature, signatureKey);
returnData.putBoolean(KeychainIntentService.RESULT_SIGNATURE_SUCCESS, sig_isok & keyBinding_isok); if (sig_isok & keyBinding_isok) {
signatureResult.setStatus(OpenPgpSignatureResult.SIGNATURE_SUCCESS_UNCERTIFIED);
}
// TODO: what about SIGNATURE_SUCCESS_CERTIFIED and SIGNATURE_ERROR????
updateProgress(R.string.progress_done, 100, 100); updateProgress(R.string.progress_done, 100, 100);
returnData.setSignatureResult(signatureResult);
return returnData; return returnData;
} }

View File

@ -0,0 +1,88 @@
/*
* Copyright (C) 2014 Dominik Schürmann <dominik@dominikschuermann.de>
*
* 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 <http://www.gnu.org/licenses/>.
*/
package org.sufficientlysecure.keychain.pgp;
import android.os.Parcel;
import android.os.Parcelable;
import org.openintents.openpgp.OpenPgpSignatureResult;
public class PgpDecryptVerifyResult implements Parcelable {
boolean symmetricPassphraseNeeded;
boolean keyPassphraseNeeded;
OpenPgpSignatureResult signatureResult;
public boolean isSymmetricPassphraseNeeded() {
return symmetricPassphraseNeeded;
}
public void setSymmetricPassphraseNeeded(boolean symmetricPassphraseNeeded) {
this.symmetricPassphraseNeeded = symmetricPassphraseNeeded;
}
public boolean isKeyPassphraseNeeded() {
return keyPassphraseNeeded;
}
public void setKeyPassphraseNeeded(boolean keyPassphraseNeeded) {
this.keyPassphraseNeeded = keyPassphraseNeeded;
}
public OpenPgpSignatureResult getSignatureResult() {
return signatureResult;
}
public void setSignatureResult(OpenPgpSignatureResult signatureResult) {
this.signatureResult = signatureResult;
}
public PgpDecryptVerifyResult() {
}
public PgpDecryptVerifyResult(PgpDecryptVerifyResult b) {
this.symmetricPassphraseNeeded = b.symmetricPassphraseNeeded;
this.keyPassphraseNeeded = b.keyPassphraseNeeded;
this.signatureResult = b.signatureResult;
}
public int describeContents() {
return 0;
}
public void writeToParcel(Parcel dest, int flags) {
dest.writeByte((byte) (symmetricPassphraseNeeded ? 1 : 0));
dest.writeByte((byte) (keyPassphraseNeeded ? 1 : 0));
dest.writeParcelable(signatureResult, 0);
}
public static final Creator<PgpDecryptVerifyResult> CREATOR = new Creator<PgpDecryptVerifyResult>() {
public PgpDecryptVerifyResult createFromParcel(final Parcel source) {
PgpDecryptVerifyResult vr = new PgpDecryptVerifyResult();
vr.symmetricPassphraseNeeded = source.readByte() == 1;
vr.keyPassphraseNeeded = source.readByte() == 1;
vr.signatureResult = source.readParcelable(OpenPgpSignatureResult.class.getClassLoader());
return vr;
}
public PgpDecryptVerifyResult[] newArray(final int size) {
return new PgpDecryptVerifyResult[size];
}
};
}

View File

@ -44,6 +44,7 @@ import org.sufficientlysecure.keychain.helper.OtherHelper;
import org.sufficientlysecure.keychain.helper.Preferences; import org.sufficientlysecure.keychain.helper.Preferences;
import org.sufficientlysecure.keychain.pgp.PgpConversionHelper; import org.sufficientlysecure.keychain.pgp.PgpConversionHelper;
import org.sufficientlysecure.keychain.pgp.PgpDecryptVerify; import org.sufficientlysecure.keychain.pgp.PgpDecryptVerify;
import org.sufficientlysecure.keychain.pgp.PgpDecryptVerifyResult;
import org.sufficientlysecure.keychain.pgp.PgpHelper; import org.sufficientlysecure.keychain.pgp.PgpHelper;
import org.sufficientlysecure.keychain.pgp.PgpImportExport; import org.sufficientlysecure.keychain.pgp.PgpImportExport;
import org.sufficientlysecure.keychain.pgp.PgpKeyOperation; import org.sufficientlysecure.keychain.pgp.PgpKeyOperation;
@ -181,13 +182,7 @@ public class KeychainIntentService extends IntentService implements ProgressDial
// decrypt/verify // decrypt/verify
public static final String RESULT_DECRYPTED_STRING = "decrypted_message"; public static final String RESULT_DECRYPTED_STRING = "decrypted_message";
public static final String RESULT_DECRYPTED_BYTES = "decrypted_data"; public static final String RESULT_DECRYPTED_BYTES = "decrypted_data";
public static final String RESULT_SIGNATURE = "signature"; public static final String RESULT_DECRYPT_VERIFY_RESULT = "signature";
public static final String RESULT_SIGNATURE_KEY_ID = "signature_key_id";
public static final String RESULT_SIGNATURE_USER_ID = "signature_user_id";
public static final String RESULT_CLEARTEXT_SIGNATURE_ONLY = "signature_only";
public static final String RESULT_SIGNATURE_SUCCESS = "signature_success";
public static final String RESULT_SIGNATURE_UNKNOWN = "signature_unknown";
// import // import
public static final String RESULT_IMPORT_ADDED = "added"; public static final String RESULT_IMPORT_ADDED = "added";
@ -489,15 +484,17 @@ public class KeychainIntentService extends IntentService implements ProgressDial
// verifyText and decrypt returning additional resultData values for the // verifyText and decrypt returning additional resultData values for the
// verification of signatures // verification of signatures
PgpDecryptVerify.Builder builder = new PgpDecryptVerify.Builder(this, inputData, outStream); PgpDecryptVerify.Builder builder = new PgpDecryptVerify.Builder(this, inputData, outStream);
builder.progress(this); builder.progressDialogUpdater(this);
builder.assumeSymmetric(assumeSymmetricEncryption) builder.assumeSymmetric(assumeSymmetricEncryption)
.passphrase(PassphraseCacheService.getCachedPassphrase(this, secretKeyId)); .passphrase(PassphraseCacheService.getCachedPassphrase(this, secretKeyId));
resultData = builder.build().execute(); PgpDecryptVerifyResult decryptVerifyResult = builder.build().execute();
outStream.close(); outStream.close();
resultData.putParcelable(RESULT_DECRYPT_VERIFY_RESULT, decryptVerifyResult);
/* Output */ /* Output */
switch (target) { switch (target) {
@ -867,10 +864,10 @@ public class KeychainIntentService extends IntentService implements ProgressDial
} }
/** /**
* Set progress of ProgressDialog by sending message to handler on UI thread * Set progressDialogUpdater of ProgressDialog by sending message to handler on UI thread
*/ */
public void setProgress(String message, int progress, int max) { public void setProgress(String message, int progress, int max) {
Log.d(Constants.TAG, "Send message by setProgress with progress=" + progress + ", max=" Log.d(Constants.TAG, "Send message by setProgress with progressDialogUpdater=" + progress + ", max="
+ max); + max);
Bundle data = new Bundle(); Bundle data = new Bundle();

View File

@ -21,7 +21,6 @@ import android.app.PendingIntent;
import android.content.Intent; import android.content.Intent;
import android.database.Cursor; import android.database.Cursor;
import android.net.Uri; import android.net.Uri;
import android.os.Bundle;
import android.os.IBinder; import android.os.IBinder;
import android.os.ParcelFileDescriptor; import android.os.ParcelFileDescriptor;
@ -33,9 +32,10 @@ import org.spongycastle.util.Arrays;
import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.Id; import org.sufficientlysecure.keychain.Id;
import org.sufficientlysecure.keychain.pgp.PgpDecryptVerify; import org.sufficientlysecure.keychain.pgp.PgpDecryptVerify;
import org.sufficientlysecure.keychain.pgp.PgpDecryptVerifyResult;
import org.sufficientlysecure.keychain.pgp.PgpSignEncrypt; import org.sufficientlysecure.keychain.pgp.PgpSignEncrypt;
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
import org.sufficientlysecure.keychain.provider.KeychainContract; import org.sufficientlysecure.keychain.provider.KeychainContract;
import org.sufficientlysecure.keychain.service.KeychainIntentService;
import org.sufficientlysecure.keychain.service.PassphraseCacheService; import org.sufficientlysecure.keychain.service.PassphraseCacheService;
import org.sufficientlysecure.keychain.util.InputData; import org.sufficientlysecure.keychain.util.InputData;
import org.sufficientlysecure.keychain.util.Log; import org.sufficientlysecure.keychain.util.Log;
@ -284,98 +284,29 @@ public class OpenPgpService extends RemoteService {
Intent result = new Intent(); Intent result = new Intent();
try { try {
// TODO: String passphrase = data.getStringExtra(OpenPgpApi.EXTRA_PASSPHRASE);
// fix the mess: http://stackoverflow.com/questions/148130/how-do-i-peek-at-the-first-two-bytes-in-an-inputstream
// should we allow to decrypt everything under every key id or only the one set?
// TODO: instead of trying to get the passphrase before
// pause stream when passphrase is missing and then resume
// TODO: put this code into PgpDecryptVerify class
// TODO: This allows to decrypt messages with ALL secret keys, not only the one for the
// app, Fix this?
// String passphrase = null;
// if (!signedOnly) {
// // BEGIN Get key
// // TODO: this input stream is consumed after PgpMain.getDecryptionKeyId()... do it
// // better!
// InputStream inputStream2 = new ByteArrayInputStream(inputBytes);
//
// // TODO: duplicates functions from DecryptActivity!
// long secretKeyId;
// try {
// if (inputStream2.markSupported()) {
// // should probably set this to the max size of two
// // pgpF objects, if it even needs to be anything other
// // than 0.
// inputStream2.mark(200);
// }
// secretKeyId = PgpHelper.getDecryptionKeyId(this, inputStream2);
// if (secretKeyId == Id.key.none) {
// throw new PgpGeneralException(getString(R.string.error_no_secret_key_found));
// }
// } catch (NoAsymmetricEncryptionException e) {
// if (inputStream2.markSupported()) {
// inputStream2.reset();
// }
// secretKeyId = Id.key.symmetric;
// if (!PgpDecryptVerify.hasSymmetricEncryption(this, inputStream2)) {
// throw new PgpGeneralException(
// getString(R.string.error_no_known_encryption_found));
// }
// // we do not support symmetric decryption from the API!
// throw new Exception("Symmetric decryption is not supported!");
// }
//
// Log.d(Constants.TAG, "secretKeyId " + secretKeyId);
// NOTE: currently this only gets the passphrase for the key set for this client
String passphrase;
if (data.hasExtra(OpenPgpApi.EXTRA_PASSPHRASE)) {
passphrase = data.getStringExtra(OpenPgpApi.EXTRA_PASSPHRASE);
} else {
passphrase = PassphraseCacheService.getCachedPassphrase(getContext(), appSettings.getKeyId());
}
if (passphrase == null) {
// get PendingIntent for passphrase input, add it to given params and return to client
Intent passphraseBundle = getPassphraseBundleIntent(data, appSettings.getKeyId());
return passphraseBundle;
}
long inputLength = is.available(); long inputLength = is.available();
InputData inputData = new InputData(is, inputLength); InputData inputData = new InputData(is, inputLength);
Bundle outputBundle;
PgpDecryptVerify.Builder builder = new PgpDecryptVerify.Builder(this, inputData, os); PgpDecryptVerify.Builder builder = new PgpDecryptVerify.Builder(this, inputData, os);
builder.assumeSymmetric(false) // no support for symmetric encryption
builder.assumeSymmetric(false) .enforcedKeyId(appSettings.getKeyId()) // allow only the private key for this app for decryption
.passphrase(passphrase); .passphrase(passphrase);
// TODO: this also decrypts with other secret keys that have no passphrase!!! // TODO: currently does not support binary signed-only content
outputBundle = builder.build().execute(); PgpDecryptVerifyResult decryptVerifyResult = builder.build().execute();
//TODO: instead of using all these wrapping use OpenPgpSignatureResult directly if (decryptVerifyResult.isKeyPassphraseNeeded()) {
// in DecryptVerify class and then in DecryptActivity // get PendingIntent for passphrase input, add it to given params and return to client
boolean signature = outputBundle.getBoolean(KeychainIntentService.RESULT_SIGNATURE, false); Intent passphraseBundle = getPassphraseBundleIntent(data, appSettings.getKeyId());
if (signature) { return passphraseBundle;
long signatureKeyId = outputBundle } else if (decryptVerifyResult.isSymmetricPassphraseNeeded()) {
.getLong(KeychainIntentService.RESULT_SIGNATURE_KEY_ID, 0); throw new PgpGeneralException("Decryption of symmetric content not supported by API!");
String signatureUserId = outputBundle }
.getString(KeychainIntentService.RESULT_SIGNATURE_USER_ID);
boolean signatureSuccess = outputBundle
.getBoolean(KeychainIntentService.RESULT_SIGNATURE_SUCCESS, false);
boolean signatureUnknown = outputBundle
.getBoolean(KeychainIntentService.RESULT_SIGNATURE_UNKNOWN, false);
boolean signatureOnly = outputBundle
.getBoolean(KeychainIntentService.RESULT_CLEARTEXT_SIGNATURE_ONLY, false);
// TODO: SIGNATURE_SUCCESS_CERTIFIED is currently not implemented
int signatureStatus = OpenPgpSignatureResult.SIGNATURE_ERROR;
if (signatureSuccess) {
signatureStatus = OpenPgpSignatureResult.SIGNATURE_SUCCESS_UNCERTIFIED;
} else if (signatureUnknown) {
signatureStatus = OpenPgpSignatureResult.SIGNATURE_UNKNOWN_PUB_KEY;
OpenPgpSignatureResult signatureResult = decryptVerifyResult.getSignatureResult();
if (signatureResult != null) {
if (signatureResult.getStatus() == OpenPgpSignatureResult.SIGNATURE_UNKNOWN_PUB_KEY) {
// If signature is unknown we return an additional PendingIntent // If signature is unknown we return an additional PendingIntent
// to retrieve the missing key // to retrieve the missing key
// TODO!!! // TODO!!!
@ -390,11 +321,9 @@ public class OpenPgpService extends RemoteService {
result.putExtra(OpenPgpApi.RESULT_INTENT, pi); result.putExtra(OpenPgpApi.RESULT_INTENT, pi);
} }
result.putExtra(OpenPgpApi.RESULT_SIGNATURE, signatureResult);
OpenPgpSignatureResult sigResult = new OpenPgpSignatureResult(signatureStatus,
signatureUserId, signatureOnly, signatureKeyId);
result.putExtra(OpenPgpApi.RESULT_SIGNATURE, sigResult);
} }
} finally { } finally {
is.close(); is.close();
os.close(); os.close();

View File

@ -25,6 +25,7 @@ import java.io.FileNotFoundException;
import java.io.InputStream; import java.io.InputStream;
import java.util.regex.Matcher; import java.util.regex.Matcher;
import org.openintents.openpgp.OpenPgpSignatureResult;
import org.spongycastle.openpgp.PGPPublicKeyRing; import org.spongycastle.openpgp.PGPPublicKeyRing;
import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.Id; import org.sufficientlysecure.keychain.Id;
@ -32,6 +33,7 @@ import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.compatibility.ClipboardReflection; import org.sufficientlysecure.keychain.compatibility.ClipboardReflection;
import org.sufficientlysecure.keychain.helper.ActionBarHelper; import org.sufficientlysecure.keychain.helper.ActionBarHelper;
import org.sufficientlysecure.keychain.helper.FileHelper; import org.sufficientlysecure.keychain.helper.FileHelper;
import org.sufficientlysecure.keychain.pgp.PgpDecryptVerifyResult;
import org.sufficientlysecure.keychain.pgp.PgpHelper; import org.sufficientlysecure.keychain.pgp.PgpHelper;
import org.sufficientlysecure.keychain.pgp.PgpKeyHelper; import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;
import org.sufficientlysecure.keychain.pgp.PgpDecryptVerify; import org.sufficientlysecure.keychain.pgp.PgpDecryptVerify;
@ -690,11 +692,15 @@ public class DecryptActivity extends DrawerActivity {
} }
if (returnData.getBoolean(KeychainIntentService.RESULT_SIGNATURE)) { PgpDecryptVerifyResult decryptVerifyResult =
String userId = returnData returnData.getParcelable(KeychainIntentService.RESULT_DECRYPT_VERIFY_RESULT);
.getString(KeychainIntentService.RESULT_SIGNATURE_USER_ID);
mSignatureKeyId = returnData OpenPgpSignatureResult signatureResult = decryptVerifyResult.getSignatureResult();
.getLong(KeychainIntentService.RESULT_SIGNATURE_KEY_ID);
if (signatureResult != null) {
String userId = signatureResult.getUserId();
mSignatureKeyId = signatureResult.getKeyId();
mUserIdRest.setText("id: " mUserIdRest.setText("id: "
+ PgpKeyHelper.convertKeyIdToHex(mSignatureKeyId)); + PgpKeyHelper.convertKeyIdToHex(mSignatureKeyId));
if (userId == null) { if (userId == null) {
@ -707,19 +713,32 @@ public class DecryptActivity extends DrawerActivity {
} }
mUserId.setText(userId); mUserId.setText(userId);
if (returnData.getBoolean(KeychainIntentService.RESULT_SIGNATURE_SUCCESS)) { switch (signatureResult.getStatus()) {
case OpenPgpSignatureResult.SIGNATURE_SUCCESS_UNCERTIFIED: {
mSignatureStatusImage.setImageResource(R.drawable.overlay_ok); mSignatureStatusImage.setImageResource(R.drawable.overlay_ok);
mLookupKey.setVisibility(View.GONE); mLookupKey.setVisibility(View.GONE);
} else if (returnData break;
.getBoolean(KeychainIntentService.RESULT_SIGNATURE_UNKNOWN)) { }
// TODO!
// case OpenPgpSignatureResult.SIGNATURE_SUCCESS_CERTIFIED: {
// break;
// }
case OpenPgpSignatureResult.SIGNATURE_UNKNOWN_PUB_KEY: {
mSignatureStatusImage.setImageResource(R.drawable.overlay_error); mSignatureStatusImage.setImageResource(R.drawable.overlay_error);
mLookupKey.setVisibility(View.VISIBLE); mLookupKey.setVisibility(View.VISIBLE);
AppMsg.makeText(DecryptActivity.this, AppMsg.makeText(DecryptActivity.this,
R.string.unknown_signature, R.string.unknown_signature,
AppMsg.STYLE_ALERT).show(); AppMsg.STYLE_ALERT).show();
} else { break;
}
default: {
mSignatureStatusImage.setImageResource(R.drawable.overlay_error); mSignatureStatusImage.setImageResource(R.drawable.overlay_error);
mLookupKey.setVisibility(View.GONE); mLookupKey.setVisibility(View.GONE);
break;
}
} }
mSignatureLayout.setVisibility(View.VISIBLE); mSignatureLayout.setVisibility(View.VISIBLE);
} }
@ -733,7 +752,7 @@ public class DecryptActivity extends DrawerActivity {
Messenger messenger = new Messenger(saveHandler); Messenger messenger = new Messenger(saveHandler);
intent.putExtra(KeychainIntentService.EXTRA_MESSENGER, messenger); intent.putExtra(KeychainIntentService.EXTRA_MESSENGER, messenger);
// show progress dialog // show progressDialogUpdater dialog
saveHandler.showProgressDialog(this); saveHandler.showProgressDialog(this);
// start service with intent // start service with intent