From 1c4b8c193d35f2226d621aa448e6775ff49fa2c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dominik=20Sch=C3=BCrmann?= Date: Thu, 8 Jan 2015 14:48:13 +0100 Subject: [PATCH 1/6] Experimental API support for detached signatures (not tested) --- .../operations/results/SignEncryptResult.java | 16 ++++ .../keychain/pgp/CanonicalizedSecretKey.java | 2 +- .../keychain/pgp/PgpSignEncrypt.java | 94 +++++++++++++++---- .../keychain/remote/OpenPgpService.java | 47 +++++----- .../service/KeychainIntentService.java | 3 +- extern/openpgp-api-lib | 2 +- 6 files changed, 119 insertions(+), 45 deletions(-) diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/SignEncryptResult.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/SignEncryptResult.java index 57daf3430..c336f8502 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/SignEncryptResult.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/SignEncryptResult.java @@ -37,6 +37,7 @@ public class SignEncryptResult extends OperationResult { int mNfcAlgo; Date mNfcTimestamp; String mNfcPassphrase; + byte[] mDetachedSignature; public long getKeyIdPassphraseNeeded() { return mKeyIdPassphraseNeeded; @@ -54,6 +55,10 @@ public class SignEncryptResult extends OperationResult { mNfcPassphrase = passphrase; } + public void setDetachedSignature(byte[] detachedSignature) { + mDetachedSignature = detachedSignature; + } + public long getNfcKeyId() { return mNfcKeyId; } @@ -74,6 +79,10 @@ public class SignEncryptResult extends OperationResult { return mNfcPassphrase; } + public byte[] getDetachedSignature() { + return mDetachedSignature; + } + public boolean isPending() { return (mResult & RESULT_PENDING) == RESULT_PENDING; } @@ -87,6 +96,7 @@ public class SignEncryptResult extends OperationResult { mNfcHash = source.readInt() != 0 ? source.createByteArray() : null; mNfcAlgo = source.readInt(); mNfcTimestamp = source.readInt() != 0 ? new Date(source.readLong()) : null; + mDetachedSignature = source.readInt() != 0 ? source.createByteArray() : null; } public int describeContents() { @@ -108,6 +118,12 @@ public class SignEncryptResult extends OperationResult { } else { dest.writeInt(0); } + if (mDetachedSignature != null) { + dest.writeInt(1); + dest.writeByteArray(mDetachedSignature); + } else { + dest.writeInt(0); + } } public static final Creator CREATOR = new Creator() { diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedSecretKey.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedSecretKey.java index 6ccadac2e..cffb09420 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedSecretKey.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedSecretKey.java @@ -247,7 +247,7 @@ public class CanonicalizedSecretKey extends CanonicalizedPublicKey { int signatureType; if (cleartext) { - // for sign-only ascii text + // for sign-only ascii text (cleartext signature) signatureType = PGPSignature.CANONICAL_TEXT_DOCUMENT; } else { signatureType = PGPSignature.BINARY_DOCUMENT; diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpSignEncrypt.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpSignEncrypt.java index 3c3bcc890..5151667ed 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpSignEncrypt.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpSignEncrypt.java @@ -50,6 +50,7 @@ import org.sufficientlysecure.keychain.util.Log; import org.sufficientlysecure.keychain.util.ProgressScaler; import java.io.BufferedReader; +import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; @@ -78,7 +79,8 @@ public class PgpSignEncrypt extends BaseOperation { private int mSignatureHashAlgorithm; private String mSignaturePassphrase; private long mAdditionalEncryptId; - private boolean mCleartextInput; + private boolean mCleartextSignature; + private boolean mDetachedSignature; private String mOriginalFilename; private boolean mFailOnMissingEncryptionKeyIds; @@ -113,7 +115,8 @@ public class PgpSignEncrypt extends BaseOperation { this.mSignatureHashAlgorithm = builder.mSignatureHashAlgorithm; this.mSignaturePassphrase = builder.mSignaturePassphrase; this.mAdditionalEncryptId = builder.mAdditionalEncryptId; - this.mCleartextInput = builder.mCleartextInput; + this.mCleartextSignature = builder.mCleartextSignature; + this.mDetachedSignature = builder.mDetachedSignature; this.mNfcSignedHash = builder.mNfcSignedHash; this.mNfcCreationTimestamp = builder.mNfcCreationTimestamp; this.mOriginalFilename = builder.mOriginalFilename; @@ -140,7 +143,8 @@ public class PgpSignEncrypt extends BaseOperation { private int mSignatureHashAlgorithm = 0; private String mSignaturePassphrase = null; private long mAdditionalEncryptId = Constants.key.none; - private boolean mCleartextInput = false; + private boolean mCleartextSignature = false; + private boolean mDetachedSignature = false; private String mOriginalFilename = ""; private byte[] mNfcSignedHash = null; private Date mNfcCreationTimestamp = null; @@ -222,14 +226,13 @@ public class PgpSignEncrypt extends BaseOperation { return this; } - /** - * TODO: test this option! - * - * @param cleartextInput - * @return - */ - public Builder setCleartextInput(boolean cleartextInput) { - mCleartextInput = cleartextInput; + public Builder setCleartextSignature(boolean cleartextSignature) { + mCleartextSignature = cleartextSignature; + return this; + } + + public Builder setDetachedSignature(boolean detachedSignature) { + mDetachedSignature = detachedSignature; return this; } @@ -408,7 +411,7 @@ public class PgpSignEncrypt extends BaseOperation { updateProgress(R.string.progress_preparing_signature, 4, 100); try { - boolean cleartext = mCleartextInput && mEnableAsciiArmorOutput && !enableEncryption; + boolean cleartext = mCleartextSignature && mEnableAsciiArmorOutput && !enableEncryption; signatureGenerator = signingKey.getSignatureGenerator( mSignatureHashAlgorithm, cleartext, mNfcSignedHash, mNfcCreationTimestamp); } catch (PgpGeneralException e) { @@ -424,6 +427,9 @@ public class PgpSignEncrypt extends BaseOperation { OutputStream encryptionOut = null; BCPGOutputStream bcpgOut; + ByteArrayOutputStream detachedByteOut = null; + BCPGOutputStream detachedBcpgOut = null; + try { if (enableEncryption) { @@ -452,7 +458,7 @@ public class PgpSignEncrypt extends BaseOperation { PGPLiteralDataGenerator literalGen = new PGPLiteralDataGenerator(); char literalDataFormatTag; - if (mCleartextInput) { + if (mCleartextSignature) { literalDataFormatTag = PGPLiteralData.UTF8; } else { literalDataFormatTag = PGPLiteralData.BINARY; @@ -482,7 +488,7 @@ public class PgpSignEncrypt extends BaseOperation { literalGen.close(); indent -= 1; - } else if (enableSignature && mCleartextInput && mEnableAsciiArmorOutput) { + } else if (enableSignature && mCleartextSignature && mEnableAsciiArmorOutput) { /* cleartext signature: sign-only of ascii text */ updateProgress(R.string.progress_signing, 8, 100); @@ -517,11 +523,44 @@ public class PgpSignEncrypt extends BaseOperation { armorOut.endClearText(); pOut = new BCPGOutputStream(armorOut); - } else if (enableSignature && !mCleartextInput) { + } else if (enableSignature && mDetachedSignature) { + /* detached signature */ + + updateProgress(R.string.progress_signing, 8, 100); + log.add(LogType.MSG_SE_SIGNING, indent); + + InputStream in = mData.getInputStream(); + + // handle output stream separately for detached signatures + detachedByteOut = new ByteArrayOutputStream(); + OutputStream detachedOut = detachedByteOut; + if (mEnableAsciiArmorOutput) { + detachedOut = new ArmoredOutputStream(detachedOut); + } + detachedBcpgOut = new BCPGOutputStream(detachedOut); + + long alreadyWritten = 0; + int length; + byte[] buffer = new byte[1 << 16]; + while ((length = in.read(buffer)) > 0) { + // pipe input stream directly into output stream, no changes to data + mOutStream.write(buffer, 0, length); + + signatureGenerator.update(buffer, 0, length); + + alreadyWritten += length; + if (mData.getSize() > 0) { + long progress = 100 * alreadyWritten / mData.getSize(); + progressScaler.setProgress((int) progress, 100); + } + } + + pOut = null; + } else if (enableSignature && !mCleartextSignature && !mDetachedSignature) { /* sign-only binary (files/data stream) */ updateProgress(R.string.progress_signing, 8, 100); - log.add(LogType.MSG_SE_ENCRYPTING, indent); + log.add(LogType.MSG_SE_SIGNING, indent); InputStream in = mData.getInputStream(); @@ -556,13 +595,18 @@ public class PgpSignEncrypt extends BaseOperation { literalGen.close(); } else { pOut = null; + // TODO: Is this log right? log.add(LogType.MSG_SE_CLEARSIGN_ONLY, indent); } if (enableSignature) { updateProgress(R.string.progress_generating_signature, 95, 100); try { - signatureGenerator.generate().encode(pOut); + if (detachedBcpgOut != null) { + signatureGenerator.generate().encode(detachedBcpgOut); + } else { + signatureGenerator.generate().encode(pOut); + } } catch (NfcSyncPGPContentSignerBuilder.NfcInteractionNeeded e) { // this secret key diverts to a OpenPGP card, throw exception with hash that will be signed log.add(LogType.MSG_SE_PENDING_NFC, indent); @@ -607,10 +651,22 @@ public class PgpSignEncrypt extends BaseOperation { updateProgress(R.string.progress_done, 100, 100); log.add(LogType.MSG_SE_OK, indent); - return new SignEncryptResult(SignEncryptResult.RESULT_OK, log); - + SignEncryptResult result = new SignEncryptResult(SignEncryptResult.RESULT_OK, log); + if (detachedByteOut != null) { + try { + detachedByteOut.flush(); + detachedByteOut.close(); + } catch (IOException e) { + // silently catch + } + result.setDetachedSignature(detachedByteOut.toByteArray()); + } + return result; } + /** + * Remove whitespaces on line endings + */ private static void processLine(final String pLine, final ArmoredOutputStream pArmoredOutput, final PGPSignatureGenerator pSignatureGenerator) throws IOException, SignatureException { diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/OpenPgpService.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/OpenPgpService.java index a5813567a..478013f55 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/OpenPgpService.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/OpenPgpService.java @@ -222,9 +222,10 @@ public class OpenPgpService extends RemoteService { } private Intent signImpl(Intent data, ParcelFileDescriptor input, - ParcelFileDescriptor output, AccountSettings accSettings) { + ParcelFileDescriptor output, AccountSettings accSettings, + boolean cleartextSign) { try { - boolean asciiArmor = data.getBooleanExtra(OpenPgpApi.EXTRA_REQUEST_ASCII_ARMOR, true); + boolean asciiArmor = cleartextSign || data.getBooleanExtra(OpenPgpApi.EXTRA_REQUEST_ASCII_ARMOR, true); byte[] nfcSignedHash = data.getByteArrayExtra(OpenPgpApi.EXTRA_NFC_SIGNED_HASH); if (nfcSignedHash != null) { @@ -284,6 +285,8 @@ public class OpenPgpService extends RemoteService { inputData, os ); builder.setEnableAsciiArmorOutput(asciiArmor) + .setCleartextSignature(cleartextSign) + .setDetachedSignature(!cleartextSign) .setVersionHeader(PgpHelper.getVersionForHeader(this)) .setSignatureHashAlgorithm(accSettings.getHashAlgorithm()) .setSignatureMasterKeyId(accSettings.getKeyId()) @@ -291,9 +294,6 @@ public class OpenPgpService extends RemoteService { .setSignaturePassphrase(passphrase) .setNfcState(nfcSignedHash, nfcCreationDate); - // TODO: currently always assume cleartext input, no sign-only of binary currently! - builder.setCleartextInput(true); - // execute PGP operation! SignEncryptResult pgpResult = builder.build().execute(); @@ -313,20 +313,20 @@ public class OpenPgpService extends RemoteService { "Encountered unhandled type of pending action not supported by API!"); } } else if (pgpResult.success()) { - // see end of method + Intent result = new Intent(); + if (!cleartextSign) { + result.putExtra(OpenPgpApi.RESULT_DETACHED_SIGNATURE, pgpResult.getDetachedSignature()); + } + result.putExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_SUCCESS); + return result; } else { LogEntryParcel errorMsg = pgpResult.getLog().getLast(); throw new Exception(getString(errorMsg.mType.getMsgId())); } - } finally { is.close(); os.close(); } - - Intent result = new Intent(); - result.putExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_SUCCESS); - return result; } catch (Exception e) { Log.d(Constants.TAG, "signImpl", e); Intent result = new Intent(); @@ -444,7 +444,9 @@ public class OpenPgpService extends RemoteService { "Encountered unhandled type of pending action not supported by API!"); } } else if (pgpResult.success()) { - // see end of method + Intent result = new Intent(); + result.putExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_SUCCESS); + return result; } else { LogEntryParcel errorMsg = pgpResult.getLog().getLast(); throw new Exception(getString(errorMsg.mType.getMsgId())); @@ -454,10 +456,6 @@ public class OpenPgpService extends RemoteService { is.close(); os.close(); } - - Intent result = new Intent(); - result.putExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_SUCCESS); - return result; } catch (Exception e) { Log.d(Constants.TAG, "encryptAndSignImpl", e); Intent result = new Intent(); @@ -482,7 +480,6 @@ public class OpenPgpService extends RemoteService { os = new ParcelFileDescriptor.AutoCloseOutputStream(output); } - Intent result = new Intent(); try { String passphrase = data.getStringExtra(OpenPgpApi.EXTRA_PASSPHRASE); long inputLength = is.available(); @@ -522,6 +519,7 @@ public class OpenPgpService extends RemoteService { "Encountered unhandled type of pending action not supported by API!"); } } else if (pgpResult.success()) { + Intent result = new Intent(); OpenPgpSignatureResult signatureResult = pgpResult.getSignatureResult(); if (signatureResult != null) { @@ -557,6 +555,9 @@ public class OpenPgpService extends RemoteService { result.putExtra(OpenPgpApi.RESULT_METADATA, metadata); } } + + result.putExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_SUCCESS); + return result; } else { LogEntryParcel errorMsg = pgpResult.getLog().getLast(); throw new Exception(getString(errorMsg.mType.getMsgId())); @@ -567,9 +568,6 @@ public class OpenPgpService extends RemoteService { os.close(); } } - - result.putExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_SUCCESS); - return result; } catch (Exception e) { Log.d(Constants.TAG, "decryptAndVerifyImpl", e); Intent result = new Intent(); @@ -718,8 +716,13 @@ public class OpenPgpService extends RemoteService { } String action = data.getAction(); - if (OpenPgpApi.ACTION_SIGN.equals(action)) { - return signImpl(data, input, output, accSettings); + if (OpenPgpApi.ACTION_CLEARTEXT_SIGN.equals(action)) { + return signImpl(data, input, output, accSettings, true); + } else if (OpenPgpApi.ACTION_SIGN.equals(action)) { + // DEPRECATED: same as ACTION_CLEARTEXT_SIGN + return signImpl(data, input, output, accSettings, true); + } else if (OpenPgpApi.ACTION_DETACHED_SIGN.equals(action)) { + return signImpl(data, input, output, accSettings, false); } else if (OpenPgpApi.ACTION_ENCRYPT.equals(action)) { return encryptAndSignImpl(data, input, output, accSettings, false); } else if (OpenPgpApi.ACTION_SIGN_AND_ENCRYPT.equals(action)) { diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java index 479810203..2dc057941 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java @@ -33,7 +33,6 @@ import org.sufficientlysecure.keychain.operations.EditKeyOperation; import org.sufficientlysecure.keychain.operations.results.DeleteResult; import org.sufficientlysecure.keychain.operations.results.EditKeyResult; import org.sufficientlysecure.keychain.operations.results.ExportResult; -import org.sufficientlysecure.keychain.operations.results.PgpEditKeyResult; import org.sufficientlysecure.keychain.pgp.exception.PgpKeyNotFoundException; import org.sufficientlysecure.keychain.operations.results.CertifyResult; import org.sufficientlysecure.keychain.util.FileHelper; @@ -474,7 +473,7 @@ public class KeychainIntentService extends IntentService implements Progressable // this assumes that the bytes are cleartext (valid for current implementation!) if (source == IO_BYTES) { - builder.setCleartextInput(true); + builder.setCleartextSignature(true); } SignEncryptResult result = builder.build().execute(); diff --git a/extern/openpgp-api-lib b/extern/openpgp-api-lib index 89b0a3bd1..f712a26ab 160000 --- a/extern/openpgp-api-lib +++ b/extern/openpgp-api-lib @@ -1 +1 @@ -Subproject commit 89b0a3bd140dd178595c031beaa27747575d7ac8 +Subproject commit f712a26ab68eb0f978722cfa69a7e9b5d05c80ca From a87a45aa9abd00bb78c42ab11c8da007b892369d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dominik=20Sch=C3=BCrmann?= Date: Thu, 8 Jan 2015 14:57:09 +0100 Subject: [PATCH 2/6] No output stream for detached signatures, makes no sense to pipe it through --- .../org/sufficientlysecure/keychain/pgp/PgpSignEncrypt.java | 3 +-- extern/openpgp-api-lib | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpSignEncrypt.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpSignEncrypt.java index 5151667ed..3c6c86338 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpSignEncrypt.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpSignEncrypt.java @@ -543,8 +543,7 @@ public class PgpSignEncrypt extends BaseOperation { int length; byte[] buffer = new byte[1 << 16]; while ((length = in.read(buffer)) > 0) { - // pipe input stream directly into output stream, no changes to data - mOutStream.write(buffer, 0, length); + // no output stream is written, no changed to original data! signatureGenerator.update(buffer, 0, length); diff --git a/extern/openpgp-api-lib b/extern/openpgp-api-lib index f712a26ab..76d7b17f1 160000 --- a/extern/openpgp-api-lib +++ b/extern/openpgp-api-lib @@ -1 +1 @@ -Subproject commit f712a26ab68eb0f978722cfa69a7e9b5d05c80ca +Subproject commit 76d7b17f114ef180fdbe6852d8897e8b9a4bca8b From b73ad7d87e7ddef03c17b4983f526c0613403df6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dominik=20Sch=C3=BCrmann?= Date: Mon, 12 Jan 2015 15:37:41 +0100 Subject: [PATCH 3/6] Update safeslinger lib --- extern/safeslinger-exchange | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extern/safeslinger-exchange b/extern/safeslinger-exchange index 80beccbe1..96f7c8935 160000 --- a/extern/safeslinger-exchange +++ b/extern/safeslinger-exchange @@ -1 +1 @@ -Subproject commit 80beccbe1b95437040b201a37f40cdb50053c06a +Subproject commit 96f7c893565e3a8badd740b2035beea87d8bffb3 From fc786280fdd7187f1828a4c7fa4d719de902a374 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dominik=20Sch=C3=BCrmann?= Date: Mon, 26 Jan 2015 17:33:40 +0100 Subject: [PATCH 4/6] Fixes for detached signatures --- .../keychain/pgp/PgpSignEncrypt.java | 35 ++++++++++++++----- .../keychain/remote/OpenPgpService.java | 12 +++++-- 2 files changed, 36 insertions(+), 11 deletions(-) diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpSignEncrypt.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpSignEncrypt.java index 3c6c86338..5282deca4 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpSignEncrypt.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpSignEncrypt.java @@ -428,6 +428,7 @@ public class PgpSignEncrypt extends BaseOperation { BCPGOutputStream bcpgOut; ByteArrayOutputStream detachedByteOut = null; + ArmoredOutputStream detachedArmorOut = null; BCPGOutputStream detachedBcpgOut = null; try { @@ -535,7 +536,12 @@ public class PgpSignEncrypt extends BaseOperation { detachedByteOut = new ByteArrayOutputStream(); OutputStream detachedOut = detachedByteOut; if (mEnableAsciiArmorOutput) { - detachedOut = new ArmoredOutputStream(detachedOut); + detachedArmorOut = new ArmoredOutputStream(detachedOut); + if (mVersionHeader != null) { + detachedArmorOut.setHeader("Version", mVersionHeader); + } + + detachedOut = detachedArmorOut; } detachedBcpgOut = new BCPGOutputStream(detachedOut); @@ -614,27 +620,38 @@ public class PgpSignEncrypt extends BaseOperation { // Note that the checked key here is the master key, not the signing key // (although these are always the same on Yubikeys) result.setNfcData(mSignatureSubKeyId, e.hashToSign, e.hashAlgo, e.creationTimestamp, mSignaturePassphrase); - Log.d(Constants.TAG, "e.hashToSign"+ Hex.toHexString(e.hashToSign)); + Log.d(Constants.TAG, "e.hashToSign" + Hex.toHexString(e.hashToSign)); return result; } } // closing outputs // NOTE: closing needs to be done in the correct order! - // TODO: closing bcpgOut and pOut??? - if (enableEncryption) { - if (enableCompression) { + if (encryptionOut != null) { + if (compressGen != null) { compressGen.close(); } encryptionOut.close(); } - if (mEnableAsciiArmorOutput) { + // Note: Closing ArmoredOutputStream does not close the underlying stream + if (armorOut != null) { armorOut.close(); } - - out.close(); - mOutStream.close(); + // Note: Closing ArmoredOutputStream does not close the underlying stream + if (detachedArmorOut != null) { + detachedArmorOut.close(); + } + // Also closes detachedBcpgOut + if (detachedByteOut != null) { + detachedByteOut.close(); + } + if (out != null) { + out.close(); + } + if (mOutStream != null) { + mOutStream.close(); + } } catch (SignatureException e) { log.add(LogType.MSG_SE_ERROR_SIG, indent); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/OpenPgpService.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/OpenPgpService.java index f2af43b6f..6c36e1ab8 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/OpenPgpService.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/OpenPgpService.java @@ -244,7 +244,12 @@ public class OpenPgpService extends RemoteService { // Get Input- and OutputStream from ParcelFileDescriptor InputStream is = new ParcelFileDescriptor.AutoCloseInputStream(input); - OutputStream os = new ParcelFileDescriptor.AutoCloseOutputStream(output); + OutputStream os = null; + if (cleartextSign) { + // output stream only needed for cleartext signatures, + // detached signatures are returned as extra + os = new ParcelFileDescriptor.AutoCloseOutputStream(output); + } try { long inputLength = is.available(); InputData inputData = new InputData(is, inputLength); @@ -325,7 +330,9 @@ public class OpenPgpService extends RemoteService { } } finally { is.close(); - os.close(); + if (os != null) { + os.close(); + } } } catch (Exception e) { Log.d(Constants.TAG, "signImpl", e); @@ -720,6 +727,7 @@ public class OpenPgpService extends RemoteService { return signImpl(data, input, output, accSettings, true); } else if (OpenPgpApi.ACTION_SIGN.equals(action)) { // DEPRECATED: same as ACTION_CLEARTEXT_SIGN + Log.w(Constants.TAG, "You are using a deprecated API call, please use ACTION_CLEARTEXT_SIGN instead of ACTION_SIGN!"); return signImpl(data, input, output, accSettings, true); } else if (OpenPgpApi.ACTION_DETACHED_SIGN.equals(action)) { return signImpl(data, input, output, accSettings, false); From fd29d27e61e531378f0c37c78028fb2e86989dea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dominik=20Sch=C3=BCrmann?= Date: Tue, 27 Jan 2015 09:37:07 +0100 Subject: [PATCH 5/6] Temporary nav drawer fixes --- .../keychain/ui/DecryptActivity.java | 4 +-- .../keychain/ui/EncryptActivity.java | 2 +- .../keychain/ui/EncryptFilesActivity.java | 8 +++--- .../keychain/ui/EncryptTextActivity.java | 8 +++--- .../keychain/ui/NavDrawerActivity.java | 23 ++++++++++++------ OpenKeychain/src/main/res/drawable/mat2.jpg | Bin 11737 -> 0 bytes .../src/main/res/layout/custom_drawer.xml | 16 ++++++++++++ OpenKeychain/src/main/res/values/strings.xml | 1 + OpenKeychain/src/main/res/values/themes.xml | 12 +++++++-- 9 files changed, 54 insertions(+), 20 deletions(-) delete mode 100644 OpenKeychain/src/main/res/drawable/mat2.jpg create mode 100644 OpenKeychain/src/main/res/layout/custom_drawer.xml diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptActivity.java index a84461a92..21377bcd2 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptActivity.java @@ -34,13 +34,13 @@ import org.sufficientlysecure.keychain.ui.util.SubtleAttentionSeeker; import java.util.regex.Matcher; -public class DecryptActivity extends DrawerActivity { +public class DecryptActivity extends BaseActivity { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - activateDrawerNavigation(savedInstanceState); +// activateDrawerNavigation(savedInstanceState); View actionFile = findViewById(R.id.decrypt_files); View actionFromClipboard = findViewById(R.id.decrypt_from_clipboard); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptActivity.java index 11780e761..611078f95 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptActivity.java @@ -14,7 +14,7 @@ import org.sufficientlysecure.keychain.operations.results.SignEncryptResult; import java.util.Date; -public abstract class EncryptActivity extends NavDrawerActivity { +public abstract class EncryptActivity extends BaseActivity { public static final int REQUEST_CODE_PASSPHRASE = 0x00008001; public static final int REQUEST_CODE_NFC = 0x00008002; diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptFilesActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptFilesActivity.java index 6a5eaa26c..63708ae8e 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptFilesActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptFilesActivity.java @@ -325,10 +325,10 @@ public class EncryptFilesActivity extends EncryptActivity implements EncryptActi mUseArmor = Preferences.getPreferences(this).getDefaultAsciiArmor(); } -// @Override -// protected void initLayout() { -// setContentView(R.layout.encrypt_files_activity); -// } + @Override + protected void initLayout() { + setContentView(R.layout.encrypt_files_activity); + } @Override public boolean onCreateOptionsMenu(Menu menu) { diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptTextActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptTextActivity.java index f9faf683d..b4c12996e 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptTextActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptTextActivity.java @@ -303,10 +303,10 @@ public class EncryptTextActivity extends EncryptActivity implements EncryptActiv updateModeFragment(); } -// @Override -// protected void initLayout() { -// setContentView(R.layout.encrypt_text_activity); -// } + @Override + protected void initLayout() { + setContentView(R.layout.encrypt_text_activity); + } @Override public boolean onCreateOptionsMenu(Menu menu) { diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/NavDrawerActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/NavDrawerActivity.java index 092334ac3..5381b1ab3 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/NavDrawerActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/NavDrawerActivity.java @@ -19,9 +19,12 @@ package org.sufficientlysecure.keychain.ui; import android.content.Intent; import android.os.Bundle; +import android.view.LayoutInflater; +import android.view.View; import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.remote.ui.AppsListActivity; +import org.sufficientlysecure.keychain.remote.ui.AppsListFragment; import it.neokree.materialnavigationdrawer.MaterialNavigationDrawer; @@ -29,19 +32,25 @@ public abstract class NavDrawerActivity extends MaterialNavigationDrawer { @Override public void init(Bundle savedInstanceState) { + // don't open drawer on first run + disableLearningPattern(); + +// addMultiPaneSupport(); // set the header image - this.setDrawerHeaderImage(R.drawable.mat2); + // create and set the header + View view = LayoutInflater.from(this).inflate(R.layout.custom_drawer, null); + setDrawerHeaderCustom(view); // create sections - this.addSection(newSection(getString(R.string.app_name), R.drawable.ic_vpn_key_black_24dp, new KeyListFragment())); + addSection(newSection(getString(R.string.title_keys), R.drawable.ic_vpn_key_black_24dp, new KeyListFragment())); - this.addSection(newSection(getString(R.string.title_encrypt_text), R.drawable.ic_lock_outline_black_24dp, new Intent(this, EncryptTextActivity.class))); - this.addSection(newSection(getString(R.string.title_encrypt_files), R.drawable.ic_lock_outline_black_24dp, new Intent(this, EncryptFilesActivity.class))); - this.addSection(newSection(getString(R.string.title_decrypt), R.drawable.ic_lock_open_black_24dp, new Intent(this, DecryptActivity.class))); - this.addSection(newSection(getString(R.string.title_api_registered_apps), R.drawable.ic_apps_black_24dp, new Intent(this, AppsListActivity.class))); + addSection(newSection(getString(R.string.title_encrypt_text), R.drawable.ic_lock_outline_black_24dp, new Intent(this, EncryptTextActivity.class))); + addSection(newSection(getString(R.string.title_encrypt_files), R.drawable.ic_lock_outline_black_24dp, new Intent(this, EncryptFilesActivity.class))); + addSection(newSection(getString(R.string.title_decrypt), R.drawable.ic_lock_open_black_24dp, new Intent(this, DecryptActivity.class))); + addSection(newSection(getString(R.string.title_api_registered_apps), R.drawable.ic_apps_black_24dp, new AppsListFragment())); // create bottom section - this.addBottomSection(newSection(getString(R.string.menu_preferences), R.drawable.ic_settings_black_24dp, new Intent(this, SettingsActivity.class))); + addBottomSection(newSection(getString(R.string.menu_preferences), R.drawable.ic_settings_black_24dp, new Intent(this, SettingsActivity.class))); } } diff --git a/OpenKeychain/src/main/res/drawable/mat2.jpg b/OpenKeychain/src/main/res/drawable/mat2.jpg deleted file mode 100644 index e35902ec7d5afe05efc174df434696ffd0dd4e43..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 11737 zcmbVR2S60b(w^OgCFdL@3rG|Y$t;Lu0m(t~g5;dDBFP0oP=Z8Bl0lIq5|k){l0iU_ zoRI}VkoXUG@6>ny-8;s%N^YtE<26J^gSx4G^m+C@TOE2n0|9|A5n3d;?`U zIkW3p8VbtSAGP3LzkbB3*)oUK5evp&08Ia!_IHV{X7ds%}R zb`8W4-geeLARYoSo1>SL9f^xmKsite)B+8_YoG(@2Ksy17JxMX4SWR-!9<1y zA%KuUXdsM`^AK)`AVd@*4UvajgJ?kvA?6Sph%>|s5(v2iiH0OVQX$!pBFHny3&?9o z7o;CD2AP4ZK(-+VP#6>+N)9~-Wry-ZMWM1#Rj3Zs1Zo3ygZe|mps~;tXb!X#S_6Fz z?S>9Rr=e@mT^InvgHghmVZ1O=m>f(4W(2zlbB6`PP_QIe4y+tj59@>t!lq&Cux}V} z3^EKR3|tu|u(wu#2&qum`Z0un%#Fan9q2 z<7nVm;rQXi;pE}e3-$dt&RD2eDfQ7_RtF%B_1u^h1laWHWj@eATn z;$0Fl5&;qo5+{-vl46oBl4VjXQg%{#QY+Fh(p=J3(m66187r9_8ItS{SsqzC*#bG7 zoP%7M+@3s|yo~%kIhul$;u3`(g)c=KMI*&DC5)1tQkl|`GLEv6a)k1L>Kv6Ul{M8} zs&c9Usy%8tY8h&4Y7}(^^)U4T4HJz#jRQ?QO)bqNEe0(YttPDxZ6<9y?K&Mfodg|{ z4n%5DF2h z6WY4OeaZS#=A{q9WWs8~k-{y)$CpJeyI(H9yduITVlI*{GAc?gdR;U|^o@#o}9Q`kld@Qu&at!QCHu~lgjJLr^-($uqfCl zlqjGTg%tx7UnyZKsVc=QjVhm0Mk*I6qg6yyf>hd6@l~}|Q&nfLab0u2R<8zCQ&D@M z_EG)3y0dz%2B4v=@jzqpI>&Xl>n}Con%6bcG#9l5v;ws{waK*2w2QUB>Ri!@)0xoa z)b-MB(Ie6`)+^H6)4!^ppg(QEZxCeAZAfcqXIN*1Wu#~H#Ax@1{EfsL3&z665ym4X z945XdU8c0Aj;4)fL}nId&&@H-_05aTk1RASax8W&l`YdP(MUODGIGsI#_FNfinWw= zg7wl(shbHmmu;kM9@wndB5adv*X`u&Qth_vmF%&zF;FM2}zB>Ks5fpfuhp;lpAkw{TdF@14d3ADtk zWTn)&w6E-HSzS4Ad3FV5Mf6kPsn^rhXXejFDpf06o{K&&uR33qUQJews)5w_)u3x_ zYp3fB>-t})ylAPHsIPv>_p+dYxgo8QqA{)sw<)aYwAsIT_m%6b_18A9=UU8KCRz&08qx3%4uyK8!cd#c|FzpH+K`F%~VNN-)A zSYLg=WPj5DV&L`Q)xnM-<)Q9j_2K>z-H{KY#-r08tUfG{IgV|OdygMZ-1><55j9CX znKVT|mGkMsr;6#z(~UD%XL@F}XD8;6b8GV+^G6F|iv){FON>iJ%Yw@dD+()pt2b5` z)?C*P*CRGaHqt+Hey-Y-+3Z0Zpcl5>w@$ZXzR-Lr*txXRvU`1Z>Z{Y&!#&hK&3@rG z(Qlmx`UgvgK1WzbDaRL%>rYfqKAt+Ao}SJCDe}H{HvvFR4d4RV05$-JzyTPDAmB~{ zfRO$RgUkX#3vz|uIffpD?mISxaQ@JbwEK-zobeFwnZFfq|MD9z`TZ0281mzZkD-5P zLxIPzKQM&ptljAZAO}Dpkh6a%_zxBsChV-hF)(15Sa5J51y@=uY&;w+Y+P(O9ESi0 z7Y`2~A0G>cfRF&65QKPV4>@ZK`>ib|9yS~sTpRz(a{3A&!GRbD|+Nm)f( zM^{fDq<}~(>zg*VcJ>~gUfw>we*SmD!XqN1?xGSNJWNbVPI;7?lbe_Sq@b{GLx~u9Qt_!(?+bQtG0k5*> z;-ZA(@ut(f&2e8-^?LPI-H~dAwp#p_v0@63yi3Vo@H&F(6aYALP}9=3;PnjsQspWy z^5vbeul!7+EZpOpt2Ws~suvEPU2P-$Q`o6e!a`Po+3H;g90g*mWqKtS&Ky zXzzY4J(gzNxMx5kh>Tov7L~qpbf7P9G-G#MyDjD5u9%2Pu4$4NPOd2ff`6+FjPm6A zptiMi-)MuS%5smTYUE2>a z>f+EcF5E#^4e1MA@^liltFfSiD@dfrE*)*@E$1{UwaXjQi3D9iaXw+nL2&|qs<77O z9MqxQ&Y$J~?|X4-(_D@;UXDwA7)qDr5xm#L4(XFbQ-k0Hf4MCl_rJkA>qU#H3lpihiJK zagsO9JA=MUtriaTF1yby(vIWDLWmaCN3%&hiJRVYu}z6t=&V=#TgkIVMy7Ca-3UIH51tn? zS$j(|rEix+(?#hpOq%<7dMQMnNHbx8=!o=)uc%$S+oy zbx?eQL(-N)FD)v-NW zY9*5fBybNSGl;b4#1ZX{42iab8&VnTCmvH1p@Z2S@Et^u487B9dbEd%f+7>``=KK# zf&&Hsc`!;S1b?Qgshh7sq1<2EI1(f`lSQ%8bZH>zuqC->?`w=JTX-!HMD3;BEBDS; zzoXveYj0~Aa^nQIA+y74?dbv=Lu^3=Lz{l(k-%}eqwvkx?%5d4<-IJQjk{TuDsIEi zdkpv99**`{x>9gDIONWG5Ym-Js{4inBT%P+(3tHLS#gsq3FGyubsn(|Q4L%061#xA;HTzyuA(`nSUMOd(qYd-Lxl1sqDTY&@RLtP5AQI-It6?hY$ij99X?4@1^3qzwbjH9D4x6>{s8$Z zkqmn$y7V?UAxPB4eyb!}^z*~J59xQy1B}rs*YI9pzkbtM=KG%pn^s@D) zntN~kaX=O_$hIOy`rOHYG|Rqy|JZ&J+o)$=3xs=i5%UzVl-p~H`-VDz&71=51Vpn= z(Hb*&^XUZ#Zwwq$8Tb0Or5o<7nIUcvXx*ub-!hC)i|RgJ;qI)TIDs^Sc}r~mulT?Z znU;j3mKq>i_%VuEa+GM+#LT0HzFadqbB}k9G#E212h%F0 z@XvDx^t_yjCVg7n)4G^)^k@~NOywc@4%Y!)hzx)ah-;c=h;q4UDysTe$dl_v{ikg8 zs}t+zXOx8GwFc3Ua&~PRFN{Wft|E}H$gb%vAlj!u3{sL2lYA zW?dttYR!C`VsZY6Pu0WkQ?@=Fnbi1cGb`jfG4_bA@yJySO9gKPJPEHD^UYF4?X`}& znvP!+OlX;TcxdG*-I@B$Ocy;Q9-2@RA)NU5(^@lLHPUZ7pv&>&Q$fXheZ3nj40LOw zeW-6orrIw4k7kf{0YuRa=#%-Eo+0x=@$!BgEt}(=s#|jM$hrMNxuFkXq+};|B?81w z0sS3kvFX&^d}~j=dw00;LkVRWIvP|SE~t$w?f00%o8W0i0!Q- zaJvT_)AWY%2Z?rk3Dy0Dycr|S-qzDj;HjS+O5ONRobyNA{LV%H8xH!t?PY@`s|im0 z3qzW9#;->j=qOg7h5Jc0+K=8k;n`1{{uoX8h<~CgZ~DW>1f=FEaBZU0W+i^ZD3M?? z#wY6PTS5m^6K;leG5s9=RjCZy0!E>a($s;0XmEO*yKgs`#(RDD=?i9SNBo4yygMts zg%lI|9pj%uD?3!BUO!wgRL9M1Bk4OFMWQ#qW{(EK*t~ zyM8BgREH7N$vRi!^%ZwQ*IB%zPBH+)Qd=G?}R9vv=Vx zdrhE5iA~5m!zSuV5z@ww-(H<(+1qtl`8MJfSDk!e)naA4^HFMQP|DNiN)Iw}9~{qp z92(Lz&C%1lx~~tOZtgNB1|Y%%r5KX)xx_E5)p?!L1YAgk;82y*q@om^|Xw^R!9pa z;eEteJ|}awE#NrkfY)YJ@bKOpqtuMP+OX+Sf?1~uf7>eDh3;Tfx<-Vz;8c zdJ}H6rb+xZJFePKvCZtZ+o!9uwv;-Io^zN%ujx_Sf>WGr7QDD8M+k}&8$OK=^~(i! zhl4qKcJyz|o&sUD2N>lC{*66y83o6dr@)q3r1WrCBA1f6EEX0khW;n>=eRUsV#_Kg zCi=w)nU*=*Ib^Sj@?CJPu^R$e0`RNVcK?P<2aU0w$2MnabTpOji%o}B z*2KzD>&if5JOR%N?tK_~VOUEw99gr)9@d@jQ?HwOvBOcq?;%lh2BwKK^LsV!rl=wR~KMXc31w=}A~ zhcq41v?|2;&^C9>e0{Xgfq6F~5&(D0!1x`k;uHD+omA&Wc%C=ZGTrO$zDw)jr|6eQQ` z{>{J2#RGfu6yT-G(%x%k2wHL8-RE<{8&jbt%&pJ5BBB3WkwU$`!L%{X^m0+DNq+_y zY4%Uwx$8a6aM7P&VrTo76^o5^d;b)W)83EM<=Nmb|BMiI zyN1*yc<<10!WM|3dY3JJ_5$Y$7rsbMkm5*FTp_>b2bVc+chuM>YT%yX%zS~08iIA` z)#5H%P%A7h{77vY+=AgNb*u{UFwOPIxJu_19{HBqA98e<;aD~)2FPQi?@)mJhFBWv zN7}`nl4X6Lewq*IQn((}xnO3O66v~RZ|ZWdFOL*Ng=UDENOz767STS_x>FB2p_=FC3H4W)J9po1MtjzU zm_IdbVK@odN_+?M_hf&{q-hTYD*U zh?yOVFUG2mj#Y`)i<*P|{rQ^bed*;NmA=Fu2;QkV4tcZF9Nt7qYm(3{_$9V(%_KwK z#=gdTHGW4EmDGGmah77We$XAHOV=%bn?Nzy9n@C-TKzKA!R+Y7Ck2k}{kn1L+%3Tz z`wUK8i^^3swqqU<>O_O!v6AW5teTE28Q{<(X;%u5m181`MTJCtB`>?6%=tNBOiiU~ zvD8mLjF-=gyNhEa-pvtoBcWu+Zl`02(1wYyAa+AaC|W~HrbEL&D5#wFL9UvD`exDB z&+5v?pqrL|Wp(+3YeRP0$u+^q(K|B&!=}t;^@Ofwc+UC}xy^VL=i|bz*Hb*(X6KG5 zVppeRm<^G^#oA0K6tfJ%`{zCKR5o)WUP-nxuk0X?;+D*;#IcM;-w21#GD5+8Ch{gb zuCTm^(juyR;oTLz?Ug-7#Y8s-R{n%hxtZu_J0}j?@;To;xd?q~mPckvM_2dB&aH}O zLYQdATO-V}H%xhFH^Y-p=)42G98#zrgnt`dl{i*hF!M`tlft(y8r|dAM{Iw~DzHvh zjOZcN3amULG(}-Aq7K3a^%eWV|8)5Foy?Hy-s8UZz;MW-y87_TWso;``XFvCJ}=R` zWWK64#;cq@Ec!y#%9|1Mh;n?fl>er2o?0cp_vcR$Zr?^n7y$I_a;g?-ou%m4a@cfo z>;C#iHVy+z7^bNN?(Xs&36st;95Yz%5i&nz_Po&z&uzuoK-DzWi)|8)yyTk396jIr zB^%<&JHwXxD9wI;RAjc%T#@e9kO)%c`ci@-VvfMY&Ae=+Va}*oB)I^Kn}mB^!{Zns z{$VS)rj>=gt&*Ojw&)1a@}dEmA3!5bF_c`B)a|0Z;aB zQr?F@c=f#dyc1L4Y;Hw(XkCv`Q{la8uU3py7~w*}^ux z1xRn8KF%C}n5L;Bs}YkJ29l=2hOhUTm{U&HB?&}-mfeG`AV==&C8dGAvC9?OTYInc zD*f07sMa^L>SVX$LiUMfI;;K4i}H+@Yo*nT@LG;5MO|D2Mwe-veIj}Wu6rljG#`=e zq{AH|dyxKGqqSPqG2)j%7{#cn*s zRkVNO`|g$l!4+C7`&@#Ukwm}}wU6A!dH3$iMUbVi{hrCd6LdDVK1>tKU8k~M;U!bP z5ucYaqjIww4wiKUrWUi42E82j8_klIKHok;^bHT>bL~=ye(v?)X%SD*Q!v@+Z))(n zA(_r3l?;-I(e0hW!ZrDzGFr`COzicznlN&foKO^J8Sa0xn4IZKr{tixr+eSF{CV>d zDl~%PgozlU+iRYY80H$+R{JH#HDSvKV4%=>{UYeZ_tKe(YddkYZv>bywAI=tC4`<6 z#Fm67U!vKHi#fqFS+0~=p_|w2x_g3N+P~2}XMDA`V~#N!%T9xzVhIRqDk36{pwL@wco70 zO6gSD$0Kgh&Thu2hx4FoOXL0(1t@@<-)tzn>MG2LN*mjf4cyTmyCmbxcviV4IzH{A{P!sAB`E>uY^wg(FmF8LAYg3aceGAu*KACB>mf4KbqU1I87IGas6> zsg@yf#N9$feF75mlWurk-or>Rp^89k@P{m!X$-5W%HPq6DtimdvY#M|Dhbb$qPf(I z7PBCjL+Z|IWUFyJF!k!|daV$^tE6%K|LVLq-#vj-18ldPXPby=0Ph=a+ejp~VLe z%oLAs_FZfA&`icr9fr?9*1+CSF`XQmr9YH(VD+XemRNE69hjF7UcQ=QG@-ht4&0>p zHcEao#HeD|7U3=Nt$817NzyuP^4KVCBE)PT{;mqo37Ut5C|ualG~)}tIl22i+sca4 z_v==3XS+by`Yyf5h)g~vZ(ReojVN@S7zKEgyEuwCit@or> zefq?^p{g{?*z-Zy^ZL=gk<(>USw$=cZ`58Y!Ei)vJN)k)nf0!j*{G8C)FsN{whT8h z8rmmp#sgx#DjTmC4bm}>!`8FZBL^1J)uLW;d)T^u!VH8b>GdVXIcgT-3cR9NUCt)v z+JQlfS7@*K%RfbZu#mq|&QbF6Bl=m&Bgwvv z+N%h9PXW`0x%T?^OYS`YeL{d%Q0&KZSX%lvVsiu*X&1;W7Un9rB(lD&8E+T + + + + \ No newline at end of file diff --git a/OpenKeychain/src/main/res/values/strings.xml b/OpenKeychain/src/main/res/values/strings.xml index 459bdf99c..6c4ba7b08 100644 --- a/OpenKeychain/src/main/res/values/strings.xml +++ b/OpenKeychain/src/main/res/values/strings.xml @@ -41,6 +41,7 @@ "Create Key" "Exchange Keys" "Advanced Key Info" + "Keys" "Identities" diff --git a/OpenKeychain/src/main/res/values/themes.xml b/OpenKeychain/src/main/res/values/themes.xml index 5b9e43c3a..3b7d66963 100644 --- a/OpenKeychain/src/main/res/values/themes.xml +++ b/OpenKeychain/src/main/res/values/themes.xml @@ -12,8 +12,16 @@ @color/accent - @integer/DRAWERTYPE_IMAGE - true + @integer/DRAWERTYPE_CUSTOM + @style/ThemeOverlay.AppCompat.Dark.ActionBar + @style/Base.V21.Theme.AppCompat.Light.Dialog + #fafafa + false + @style/MaterialSectionTheme.Light + @style/MaterialSubheaderTheme.Light + false + false + false From ae7ba2639f1ade3953c8b96fb32a17df0d3aaf8a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dominik=20Sch=C3=BCrmann?= Date: Tue, 27 Jan 2015 13:00:28 +0100 Subject: [PATCH 6/6] Support verification of detached signatures --- .../keychain/pgp/PgpDecryptVerify.java | 303 ++++++++++++------ .../keychain/remote/OpenPgpService.java | 16 +- extern/openpgp-api-lib | 2 +- 3 files changed, 223 insertions(+), 98 deletions(-) diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerify.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerify.java index 6c987f484..a69c5fe36 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerify.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerify.java @@ -28,7 +28,6 @@ import org.spongycastle.openpgp.PGPEncryptedData; import org.spongycastle.openpgp.PGPEncryptedDataList; import org.spongycastle.openpgp.PGPException; import org.spongycastle.openpgp.PGPLiteralData; -import org.spongycastle.openpgp.PGPObjectFactory; import org.spongycastle.openpgp.PGPOnePassSignature; import org.spongycastle.openpgp.PGPOnePassSignatureList; import org.spongycastle.openpgp.PGPPBEEncryptedData; @@ -36,6 +35,7 @@ import org.spongycastle.openpgp.PGPPublicKeyEncryptedData; import org.spongycastle.openpgp.PGPSignature; import org.spongycastle.openpgp.PGPSignatureList; import org.spongycastle.openpgp.PGPUtil; +import org.spongycastle.openpgp.jcajce.JcaPGPObjectFactory; import org.spongycastle.openpgp.operator.PBEDataDecryptorFactory; import org.spongycastle.openpgp.operator.PGPDigestCalculatorProvider; import org.spongycastle.openpgp.operator.PublicKeyDataDecryptorFactory; @@ -83,6 +83,7 @@ public class PgpDecryptVerify extends BaseOperation { private Set mAllowedKeyIds; private boolean mDecryptMetadataOnly; private byte[] mDecryptedSessionKey; + private byte[] mDetachedSignature; protected PgpDecryptVerify(Builder builder) { super(builder.mContext, builder.mProviderHelper, builder.mProgressable); @@ -96,6 +97,7 @@ public class PgpDecryptVerify extends BaseOperation { this.mAllowedKeyIds = builder.mAllowedKeyIds; this.mDecryptMetadataOnly = builder.mDecryptMetadataOnly; this.mDecryptedSessionKey = builder.mDecryptedSessionKey; + this.mDetachedSignature = builder.mDetachedSignature; } public static class Builder { @@ -103,15 +105,16 @@ public class PgpDecryptVerify extends BaseOperation { private Context mContext; private ProviderHelper mProviderHelper; private InputData mData; - private OutputStream mOutStream; // optional + private OutputStream mOutStream = null; private Progressable mProgressable = null; private boolean mAllowSymmetricDecryption = true; private String mPassphrase = null; private Set mAllowedKeyIds = null; private boolean mDecryptMetadataOnly = false; private byte[] mDecryptedSessionKey = null; + private byte[] mDetachedSignature = null; public Builder(Context context, ProviderHelper providerHelper, Progressable progressable, @@ -156,6 +159,17 @@ public class PgpDecryptVerify extends BaseOperation { return this; } + /** + * If detachedSignature != null, it will be used exclusively to verify the signature + * + * @param detachedSignature + * @return + */ + public Builder setDetachedSignature(byte[] detachedSignature) { + mDetachedSignature = detachedSignature; + return this; + } + public PgpDecryptVerify build() { return new PgpDecryptVerify(this); } @@ -166,22 +180,30 @@ public class PgpDecryptVerify extends BaseOperation { */ public DecryptVerifyResult execute() { try { - // automatically works with ascii armor input and binary - InputStream in = PGPUtil.getDecoderStream(mData.getInputStream()); + if (mDetachedSignature != null) { + Log.d(Constants.TAG, "Detached signature present, verifying with this signature only"); - if (in instanceof ArmoredInputStream) { - ArmoredInputStream aIn = (ArmoredInputStream) in; - // it is ascii armored - Log.d(Constants.TAG, "ASCII Armor Header Line: " + aIn.getArmorHeaderLine()); + return verifyDetachedSignature(mData.getInputStream(), 0); + } else { + // automatically works with PGP ascii armor and PGP binary + InputStream in = PGPUtil.getDecoderStream(mData.getInputStream()); - if (aIn.isClearText()) { - // a cleartext signature, verify it with the other method - return verifyCleartextSignature(aIn, 0); + if (in instanceof ArmoredInputStream) { + ArmoredInputStream aIn = (ArmoredInputStream) in; + // it is ascii armored + Log.d(Constants.TAG, "ASCII Armor Header Line: " + aIn.getArmorHeaderLine()); + + if (aIn.isClearText()) { + // a cleartext signature, verify it with the other method + return verifyCleartextSignature(aIn, 0); + } else { + // else: ascii armored encryption! go on... + return decryptVerify(in, 0); + } + } else { + return decryptVerify(in, 0); } - // else: ascii armored encryption! go on... } - - return decryptVerify(in, 0); } catch (PGPException e) { Log.d(Constants.TAG, "PGPException", e); OperationLog log = new OperationLog(); @@ -205,7 +227,7 @@ public class PgpDecryptVerify extends BaseOperation { log.add(LogType.MSG_DC, indent); indent += 1; - PGPObjectFactory pgpF = new PGPObjectFactory(in, new JcaKeyFingerprintCalculator()); + JcaPGPObjectFactory pgpF = new JcaPGPObjectFactory(in); PGPEncryptedDataList enc; Object o = pgpF.nextObject(); @@ -238,15 +260,18 @@ public class PgpDecryptVerify extends BaseOperation { // https://tools.ietf.org/html/rfc4880#page56 String charset = null; if (in instanceof ArmoredInputStream) { - for (String header : ((ArmoredInputStream) in).getArmorHeaders()) { - String[] pieces = header.split(":", 2); - if (pieces.length == 2 && "charset".equalsIgnoreCase(pieces[0])) { - charset = pieces[1].trim(); - break; + ArmoredInputStream aIn = (ArmoredInputStream) in; + if (aIn.getArmorHeaders() != null) { + for (String header : aIn.getArmorHeaders()) { + String[] pieces = header.split(":", 2); + if (pieces.length == 2 && "charset".equalsIgnoreCase(pieces[0])) { + charset = pieces[1].trim(); + break; + } + } + if (charset != null) { + log.add(LogType.MSG_DC_CHARSET, indent, charset); } - } - if (charset != null) { - log.add(LogType.MSG_DC_CHARSET, indent, charset); } } @@ -273,19 +298,19 @@ public class PgpDecryptVerify extends BaseOperation { ); } catch (ProviderHelper.NotFoundException e) { // continue with the next packet in the while loop - log.add(LogType.MSG_DC_ASKIP_NO_KEY, indent +1); + log.add(LogType.MSG_DC_ASKIP_NO_KEY, indent + 1); continue; } if (secretKeyRing == null) { // continue with the next packet in the while loop - log.add(LogType.MSG_DC_ASKIP_NO_KEY, indent +1); + log.add(LogType.MSG_DC_ASKIP_NO_KEY, indent + 1); continue; } // get subkey which has been used for this encryption packet secretEncryptionKey = secretKeyRing.getSecretKey(subKeyId); if (secretEncryptionKey == null) { // should actually never happen, so no need to be more specific. - log.add(LogType.MSG_DC_ASKIP_NO_KEY, indent +1); + log.add(LogType.MSG_DC_ASKIP_NO_KEY, indent + 1); continue; } @@ -299,7 +324,7 @@ public class PgpDecryptVerify extends BaseOperation { if (!mAllowedKeyIds.contains(masterKeyId)) { // this key is in our db, but NOT allowed! // continue with the next packet in the while loop - log.add(LogType.MSG_DC_ASKIP_NOT_ALLOWED, indent +1); + log.add(LogType.MSG_DC_ASKIP_NOT_ALLOWED, indent + 1); continue; } } @@ -314,15 +339,15 @@ public class PgpDecryptVerify extends BaseOperation { try { // returns "" if key has no passphrase mPassphrase = getCachedPassphrase(subKeyId); - log.add(LogType.MSG_DC_PASS_CACHED, indent +1); + log.add(LogType.MSG_DC_PASS_CACHED, indent + 1); } catch (PassphraseCacheInterface.NoSecretKeyException e) { - log.add(LogType.MSG_DC_ERROR_NO_KEY, indent +1); + log.add(LogType.MSG_DC_ERROR_NO_KEY, indent + 1); return new DecryptVerifyResult(DecryptVerifyResult.RESULT_ERROR, log); } // if passphrase was not cached, return here indicating that a passphrase is missing! if (mPassphrase == null) { - log.add(LogType.MSG_DC_PENDING_PASSPHRASE, indent +1); + log.add(LogType.MSG_DC_PENDING_PASSPHRASE, indent + 1); DecryptVerifyResult result = new DecryptVerifyResult(DecryptVerifyResult.RESULT_PENDING_ASYM_PASSPHRASE, log); result.setKeyIdPassphraseNeeded(subKeyId); @@ -338,8 +363,8 @@ public class PgpDecryptVerify extends BaseOperation { log.add(LogType.MSG_DC_SYM, indent); - if (! mAllowSymmetricDecryption) { - log.add(LogType.MSG_DC_SYM_SKIP, indent +1); + if (!mAllowSymmetricDecryption) { + log.add(LogType.MSG_DC_SYM_SKIP, indent + 1); continue; } @@ -354,7 +379,7 @@ public class PgpDecryptVerify extends BaseOperation { // if no passphrase is given, return here // indicating that a passphrase is missing! if (mPassphrase == null) { - log.add(LogType.MSG_DC_PENDING_PASSPHRASE, indent +1); + log.add(LogType.MSG_DC_PENDING_PASSPHRASE, indent + 1); return new DecryptVerifyResult(DecryptVerifyResult.RESULT_PENDING_SYM_PASSPHRASE, log); } @@ -399,13 +424,13 @@ public class PgpDecryptVerify extends BaseOperation { updateProgress(R.string.progress_extracting_key, currentProgress, 100); try { - log.add(LogType.MSG_DC_UNLOCKING, indent +1); + log.add(LogType.MSG_DC_UNLOCKING, indent + 1); if (!secretEncryptionKey.unlock(mPassphrase)) { - log.add(LogType.MSG_DC_ERROR_BAD_PASSPHRASE, indent +1); + log.add(LogType.MSG_DC_ERROR_BAD_PASSPHRASE, indent + 1); return new DecryptVerifyResult(DecryptVerifyResult.RESULT_ERROR, log); } } catch (PgpGeneralException e) { - log.add(LogType.MSG_DC_ERROR_EXTRACT_KEY, indent +1); + log.add(LogType.MSG_DC_ERROR_EXTRACT_KEY, indent + 1); return new DecryptVerifyResult(DecryptVerifyResult.RESULT_ERROR, log); } @@ -417,7 +442,7 @@ public class PgpDecryptVerify extends BaseOperation { = secretEncryptionKey.getDecryptorFactory(mDecryptedSessionKey); clear = encryptedDataAsymmetric.getDataStream(decryptorFactory); } catch (NfcSyncPublicKeyDataDecryptorFactoryBuilder.NfcInteractionNeeded e) { - log.add(LogType.MSG_DC_PENDING_NFC, indent +1); + log.add(LogType.MSG_DC_PENDING_NFC, indent + 1); DecryptVerifyResult result = new DecryptVerifyResult(DecryptVerifyResult.RESULT_PENDING_NFC, log); result.setNfcState(secretEncryptionKey.getKeyId(), e.encryptedSessionKey, mPassphrase); @@ -428,11 +453,11 @@ public class PgpDecryptVerify extends BaseOperation { // If we didn't find any useful data, error out // no packet has been found where we have the corresponding secret key in our db log.add( - anyPacketFound ? LogType.MSG_DC_ERROR_NO_KEY : LogType.MSG_DC_ERROR_NO_DATA, indent +1); + anyPacketFound ? LogType.MSG_DC_ERROR_NO_KEY : LogType.MSG_DC_ERROR_NO_DATA, indent + 1); return new DecryptVerifyResult(DecryptVerifyResult.RESULT_ERROR, log); } - PGPObjectFactory plainFact = new PGPObjectFactory(clear, new JcaKeyFingerprintCalculator()); + JcaPGPObjectFactory plainFact = new JcaPGPObjectFactory(clear); Object dataChunk = plainFact.nextObject(); OpenPgpSignatureResultBuilder signatureResultBuilder = new OpenPgpSignatureResultBuilder(); int signatureIndex = -1; @@ -443,25 +468,27 @@ public class PgpDecryptVerify extends BaseOperation { indent += 1; if (dataChunk instanceof PGPCompressedData) { - log.add(LogType.MSG_DC_CLEAR_DECOMPRESS, indent +1); + log.add(LogType.MSG_DC_CLEAR_DECOMPRESS, indent + 1); currentProgress += 2; updateProgress(R.string.progress_decompressing_data, currentProgress, 100); PGPCompressedData compressedData = (PGPCompressedData) dataChunk; - PGPObjectFactory fact = new PGPObjectFactory(compressedData.getDataStream(), new JcaKeyFingerprintCalculator()); + JcaPGPObjectFactory fact = new JcaPGPObjectFactory(compressedData.getDataStream()); dataChunk = fact.nextObject(); plainFact = fact; } PGPOnePassSignature signature = null; if (dataChunk instanceof PGPOnePassSignatureList) { - log.add(LogType.MSG_DC_CLEAR_SIGNATURE, indent +1); + log.add(LogType.MSG_DC_CLEAR_SIGNATURE, indent + 1); currentProgress += 2; updateProgress(R.string.progress_processing_signature, currentProgress, 100); PGPOnePassSignatureList sigList = (PGPOnePassSignatureList) dataChunk; + // NOTE: following code is similar to processSignature, but for PGPOnePassSignature + // go through all signatures // and find out for which signature we have a key in our database for (int i = 0; i < sigList.size(); ++i) { @@ -507,7 +534,7 @@ public class PgpDecryptVerify extends BaseOperation { OpenPgpMetadata metadata; if (dataChunk instanceof PGPLiteralData) { - log.add(LogType.MSG_DC_CLEAR_DATA, indent +1); + log.add(LogType.MSG_DC_CLEAR_DATA, indent + 1); indent += 2; currentProgress += 4; updateProgress(R.string.progress_decrypting, currentProgress, 100); @@ -549,12 +576,12 @@ public class PgpDecryptVerify extends BaseOperation { literalData.getModificationTime().getTime(), originalSize); - if ( ! originalFilename.equals("")) { + if (!originalFilename.equals("")) { log.add(LogType.MSG_DC_CLEAR_META_FILE, indent + 1, originalFilename); } - log.add(LogType.MSG_DC_CLEAR_META_MIME, indent +1, + log.add(LogType.MSG_DC_CLEAR_META_MIME, indent + 1, mimeType); - log.add(LogType.MSG_DC_CLEAR_META_TIME, indent +1, + log.add(LogType.MSG_DC_CLEAR_META_TIME, indent + 1, new Date(literalData.getModificationTime().getTime()).toString()); if (originalSize != 0) { log.add(LogType.MSG_DC_CLEAR_META_SIZE, indent + 1, @@ -589,7 +616,9 @@ public class PgpDecryptVerify extends BaseOperation { int length; byte[] buffer = new byte[1 << 16]; while ((length = dataIn.read(buffer)) > 0) { - mOutStream.write(buffer, 0, length); + if (mOutStream != null) { + mOutStream.write(buffer, 0, length); + } // update signature buffer if signature is also present if (signature != null) { @@ -623,9 +652,9 @@ public class PgpDecryptVerify extends BaseOperation { // Verify signature and check binding signatures boolean validSignature = signature.verify(messageSignature); if (validSignature) { - log.add(LogType.MSG_DC_CLEAR_SIGNATURE_OK, indent +1); + log.add(LogType.MSG_DC_CLEAR_SIGNATURE_OK, indent + 1); } else { - log.add(LogType.MSG_DC_CLEAR_SIGNATURE_BAD, indent +1); + log.add(LogType.MSG_DC_CLEAR_SIGNATURE_BAD, indent + 1); } signatureResultBuilder.setValidSignature(validSignature); } @@ -687,7 +716,7 @@ public class PgpDecryptVerify extends BaseOperation { ByteArrayOutputStream out = new ByteArrayOutputStream(); - updateProgress(R.string.progress_done, 0, 100); + updateProgress(R.string.progress_reading_data, 0, 100); ByteArrayOutputStream lineOut = new ByteArrayOutputStream(); int lookAhead = readInputLine(lineOut, aIn); @@ -707,10 +736,12 @@ public class PgpDecryptVerify extends BaseOperation { out.close(); byte[] clearText = out.toByteArray(); - mOutStream.write(clearText); + if (mOutStream != null) { + mOutStream.write(clearText); + } updateProgress(R.string.progress_processing_signature, 60, 100); - PGPObjectFactory pgpFact = new PGPObjectFactory(aIn, new JcaKeyFingerprintCalculator()); + JcaPGPObjectFactory pgpFact = new JcaPGPObjectFactory(aIn); PGPSignatureList sigList = (PGPSignatureList) pgpFact.nextObject(); if (sigList == null) { @@ -718,45 +749,7 @@ public class PgpDecryptVerify extends BaseOperation { return new DecryptVerifyResult(DecryptVerifyResult.RESULT_ERROR, log); } - CanonicalizedPublicKeyRing signingRing = null; - CanonicalizedPublicKey signingKey = null; - int signatureIndex = -1; - - // go through all signatures - // and find out for which signature we have a key in our database - for (int i = 0; i < sigList.size(); ++i) { - try { - long sigKeyId = sigList.get(i).getKeyID(); - signingRing = mProviderHelper.getCanonicalizedPublicKeyRing( - KeyRings.buildUnifiedKeyRingsFindBySubkeyUri(sigKeyId) - ); - signingKey = signingRing.getPublicKey(sigKeyId); - signatureIndex = i; - } catch (ProviderHelper.NotFoundException e) { - Log.d(Constants.TAG, "key not found, trying next signature..."); - } - } - - PGPSignature signature = null; - - if (signingKey != null) { - // key found in our database! - signature = sigList.get(signatureIndex); - - signatureResultBuilder.initValid(signingRing, signingKey); - - JcaPGPContentVerifierBuilderProvider contentVerifierBuilderProvider = - new JcaPGPContentVerifierBuilderProvider() - .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME); - signature.init(contentVerifierBuilderProvider, signingKey.getPublicKey()); - } else { - // no key in our database -> return "unknown pub key" status including the first key id - if (!sigList.isEmpty()) { - signatureResultBuilder.setSignatureAvailable(true); - signatureResultBuilder.setKnownKey(false); - signatureResultBuilder.setKeyId(sigList.get(0).getKeyID()); - } - } + PGPSignature signature = processPGPSignatureList(sigList, signatureResultBuilder); if (signature != null) { try { @@ -804,6 +797,134 @@ public class PgpDecryptVerify extends BaseOperation { return result; } + private DecryptVerifyResult verifyDetachedSignature(InputStream in, int indent) + throws IOException, PGPException { + + OperationLog log = new OperationLog(); + + OpenPgpSignatureResultBuilder signatureResultBuilder = new OpenPgpSignatureResultBuilder(); + // detached signatures are never encrypted + signatureResultBuilder.setSignatureOnly(true); + + updateProgress(R.string.progress_processing_signature, 0, 100); + InputStream detachedSigIn = new ByteArrayInputStream(mDetachedSignature); + detachedSigIn = PGPUtil.getDecoderStream(detachedSigIn); + + JcaPGPObjectFactory pgpFact = new JcaPGPObjectFactory(detachedSigIn); + + PGPSignatureList sigList; + Object o = pgpFact.nextObject(); + if (o instanceof PGPCompressedData) { + PGPCompressedData c1 = (PGPCompressedData) o; + pgpFact = new JcaPGPObjectFactory(c1.getDataStream()); + sigList = (PGPSignatureList) pgpFact.nextObject(); + } else if (o instanceof PGPSignatureList) { + sigList = (PGPSignatureList) o; + } else { + log.add(LogType.MSG_DC_ERROR_INVALID_SIGLIST, 0); + return new DecryptVerifyResult(DecryptVerifyResult.RESULT_ERROR, log); + } + + PGPSignature signature = processPGPSignatureList(sigList, signatureResultBuilder); + + if (signature != null) { + updateProgress(R.string.progress_reading_data, 60, 100); + + ProgressScaler progressScaler = new ProgressScaler(mProgressable, 60, 90, 100); + long alreadyWritten = 0; + long wholeSize = mData.getSize() - mData.getStreamPosition(); + int length; + byte[] buffer = new byte[1 << 16]; + while ((length = in.read(buffer)) > 0) { + if (mOutStream != null) { + mOutStream.write(buffer, 0, length); + } + + // update signature buffer if signature is also present + signature.update(buffer, 0, length); + + alreadyWritten += length; + if (wholeSize > 0) { + long progress = 100 * alreadyWritten / wholeSize; + // stop at 100% for wrong file sizes... + if (progress > 100) { + progress = 100; + } + progressScaler.setProgress((int) progress, 100); + } else { + // TODO: slow annealing to fake a progress? + } + } + + updateProgress(R.string.progress_verifying_signature, 90, 100); + log.add(LogType.MSG_DC_CLEAR_SIGNATURE_CHECK, indent); + + // these are not cleartext signatures! + signatureResultBuilder.setSignatureOnly(false); + + // Verify signature and check binding signatures + boolean validSignature = signature.verify(); + if (validSignature) { + log.add(LogType.MSG_DC_CLEAR_SIGNATURE_OK, indent + 1); + } else { + log.add(LogType.MSG_DC_CLEAR_SIGNATURE_BAD, indent + 1); + } + signatureResultBuilder.setValidSignature(validSignature); + } + + updateProgress(R.string.progress_done, 100, 100); + + log.add(LogType.MSG_DC_OK, indent); + + DecryptVerifyResult result = new DecryptVerifyResult(DecryptVerifyResult.RESULT_OK, log); + result.setSignatureResult(signatureResultBuilder.build()); + return result; + } + + private PGPSignature processPGPSignatureList(PGPSignatureList sigList, OpenPgpSignatureResultBuilder signatureResultBuilder) throws PGPException { + CanonicalizedPublicKeyRing signingRing = null; + CanonicalizedPublicKey signingKey = null; + int signatureIndex = -1; + + // go through all signatures + // and find out for which signature we have a key in our database + for (int i = 0; i < sigList.size(); ++i) { + try { + long sigKeyId = sigList.get(i).getKeyID(); + signingRing = mProviderHelper.getCanonicalizedPublicKeyRing( + KeyRings.buildUnifiedKeyRingsFindBySubkeyUri(sigKeyId) + ); + signingKey = signingRing.getPublicKey(sigKeyId); + signatureIndex = i; + } catch (ProviderHelper.NotFoundException e) { + Log.d(Constants.TAG, "key not found, trying next signature..."); + } + } + + PGPSignature signature = null; + + if (signingKey != null) { + // key found in our database! + signature = sigList.get(signatureIndex); + + signatureResultBuilder.initValid(signingRing, signingKey); + + JcaPGPContentVerifierBuilderProvider contentVerifierBuilderProvider = + new JcaPGPContentVerifierBuilderProvider() + .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME); + signature.init(contentVerifierBuilderProvider, signingKey.getPublicKey()); + } else { + // no key in our database -> return "unknown pub key" status including the first key id + if (!sigList.isEmpty()) { + signatureResultBuilder.setSignatureAvailable(true); + signatureResultBuilder.setKnownKey(false); + signatureResultBuilder.setKeyId(sigList.get(0).getKeyID()); + } + } + + return signature; + } + /** * Mostly taken from ClearSignedFileProcessor in Bouncy Castle */ diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/OpenPgpService.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/OpenPgpService.java index e1d15e2d3..d967931ce 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/OpenPgpService.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/OpenPgpService.java @@ -481,7 +481,8 @@ public class OpenPgpService extends RemoteService { InputStream is = new ParcelFileDescriptor.AutoCloseInputStream(input); OutputStream os; - if (decryptMetadataOnly) { + // output is optional, e.g., for verifying detached signatures + if (decryptMetadataOnly || output == null) { os = null; } else { os = new ParcelFileDescriptor.AutoCloseOutputStream(output); @@ -498,15 +499,17 @@ public class OpenPgpService extends RemoteService { byte[] nfcDecryptedSessionKey = data.getByteArrayExtra(OpenPgpApi.EXTRA_NFC_DECRYPTED_SESSION_KEY); + byte[] detachedSignature = data.getByteArrayExtra(OpenPgpApi.EXTRA_DETACHED_SIGNATURE); + // allow only private keys associated with accounts of this app // no support for symmetric encryption builder.setPassphrase(passphrase) .setAllowSymmetricDecryption(false) .setAllowedKeyIds(allowedKeyIds) .setDecryptMetadataOnly(decryptMetadataOnly) - .setNfcState(nfcDecryptedSessionKey); + .setNfcState(nfcDecryptedSessionKey) + .setDetachedSignature(detachedSignature); - // TODO: currently does not support binary signed-only content DecryptVerifyResult pgpResult = builder.build().execute(); if (pgpResult.isPending()) { @@ -678,15 +681,16 @@ public class OpenPgpService extends RemoteService { // version code is required and needs to correspond to version code of service! // History of versions in org.openintents.openpgp.util.OpenPgpApi - // we support 3, 4, 5 + // we support 3, 4, 5, 6 if (data.getIntExtra(OpenPgpApi.EXTRA_API_VERSION, -1) != 3 && data.getIntExtra(OpenPgpApi.EXTRA_API_VERSION, -1) != 4 - && data.getIntExtra(OpenPgpApi.EXTRA_API_VERSION, -1) != 5) { + && data.getIntExtra(OpenPgpApi.EXTRA_API_VERSION, -1) != 5 + && data.getIntExtra(OpenPgpApi.EXTRA_API_VERSION, -1) != 6) { Intent result = new Intent(); OpenPgpError error = new OpenPgpError (OpenPgpError.INCOMPATIBLE_API_VERSIONS, "Incompatible API versions!\n" + "used API version: " + data.getIntExtra(OpenPgpApi.EXTRA_API_VERSION, -1) + "\n" - + "supported API versions: 3, 4"); + + "supported API versions: 3, 4, 5, 6"); result.putExtra(OpenPgpApi.RESULT_ERROR, error); result.putExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_ERROR); return result; diff --git a/extern/openpgp-api-lib b/extern/openpgp-api-lib index edcc61970..4f5fcbf94 160000 --- a/extern/openpgp-api-lib +++ b/extern/openpgp-api-lib @@ -1 +1 @@ -Subproject commit edcc61970f24bb3601af26d89f49d384e7a3aff6 +Subproject commit 4f5fcbf94063301929d50370ab44895c6b4e1bd3