diff options
| author | Dominik Schürmann <dominik@dominikschuermann.de> | 2015-01-27 13:04:21 +0100 | 
|---|---|---|
| committer | Dominik Schürmann <dominik@dominikschuermann.de> | 2015-01-27 13:04:21 +0100 | 
| commit | 3b23a71209b3c5acdc9d34351d1731184eea8aca (patch) | |
| tree | 848eb0cc4484dd8e5f70f96ec4ef70221ee8883e /OpenKeychain/src | |
| parent | 9c6f195eb7c4df33dceafcfdd9f6c17246e8b9c5 (diff) | |
| parent | ae7ba2639f1ade3953c8b96fb32a17df0d3aaf8a (diff) | |
| download | open-keychain-3b23a71209b3c5acdc9d34351d1731184eea8aca.tar.gz open-keychain-3b23a71209b3c5acdc9d34351d1731184eea8aca.tar.bz2 open-keychain-3b23a71209b3c5acdc9d34351d1731184eea8aca.zip  | |
Merge branch 'detached-sigs-api' into development
Diffstat (limited to 'OpenKeychain/src')
15 files changed, 449 insertions, 200 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<SignEncryptResult> CREATOR = new Creator<SignEncryptResult>() { 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 cac5c1d97..40f2f48ad 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/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<Long> 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<Long> 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()); - -                if (aIn.isClearText()) { -                    // a cleartext signature, verify it with the other method -                    return verifyCleartextSignature(aIn, 0); +                return verifyDetachedSignature(mData.getInputStream(), 0); +            } else { +                // automatically works with PGP ascii armor and PGP binary +                InputStream in = PGPUtil.getDecoderStream(mData.getInputStream()); + +                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/pgp/PgpSignEncrypt.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpSignEncrypt.java index c2fa811bd..060db96b4 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;      private String mCharset; @@ -114,7 +116,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; @@ -142,7 +145,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; @@ -230,14 +234,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;          } @@ -420,7 +423,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) { @@ -436,6 +439,10 @@ public class PgpSignEncrypt extends BaseOperation {          OutputStream encryptionOut = null;          BCPGOutputStream bcpgOut; +        ByteArrayOutputStream detachedByteOut = null; +        ArmoredOutputStream detachedArmorOut = null; +        BCPGOutputStream detachedBcpgOut = null; +          try {              if (enableEncryption) { @@ -464,7 +471,7 @@ public class PgpSignEncrypt extends BaseOperation {                  PGPLiteralDataGenerator literalGen = new PGPLiteralDataGenerator();                  char literalDataFormatTag; -                if (mCleartextInput) { +                if (mCleartextSignature) {                      literalDataFormatTag = PGPLiteralData.UTF8;                  } else {                      literalDataFormatTag = PGPLiteralData.BINARY; @@ -494,7 +501,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); @@ -529,11 +536,48 @@ 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) { +                    detachedArmorOut = new ArmoredOutputStream(detachedOut); +                    if (mVersionHeader != null) { +                        detachedArmorOut.setHeader("Version", mVersionHeader); +                    } + +                    detachedOut = detachedArmorOut; +                } +                detachedBcpgOut = new BCPGOutputStream(detachedOut); + +                long alreadyWritten = 0; +                int length; +                byte[] buffer = new byte[1 << 16]; +                while ((length = in.read(buffer)) > 0) { +                    // no output stream is written, no changed to original data! + +                    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(); @@ -568,13 +612,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); @@ -583,27 +632,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); @@ -619,10 +679,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 68677fb9c..d967931ce 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) { @@ -243,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); @@ -284,6 +290,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 +299,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 +318,22 @@ 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(); +                if (os != null) { +                    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 +451,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 +463,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(); @@ -476,13 +481,13 @@ 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);              } -            Intent result = new Intent();              try {                  String passphrase = data.getStringExtra(OpenPgpApi.EXTRA_PASSPHRASE);                  long inputLength = is.available(); @@ -494,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()) { @@ -522,6 +529,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) { @@ -563,6 +571,8 @@ public class OpenPgpService extends RemoteService {                          result.putExtra(OpenPgpApi.RESULT_CHARSET, charset);                      } +                    result.putExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_SUCCESS); +                    return result;                  } else {                      LogEntryParcel errorMsg = pgpResult.getLog().getLast();                      throw new Exception(getString(errorMsg.mType.getMsgId())); @@ -573,9 +583,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(); @@ -674,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; @@ -724,33 +732,36 @@ public class OpenPgpService extends RemoteService {              }              String action = data.getAction(); -            switch (action) { -                case OpenPgpApi.ACTION_SIGN: -                    return signImpl(data, input, output, accSettings); -                case OpenPgpApi.ACTION_ENCRYPT: -                    return encryptAndSignImpl(data, input, output, accSettings, false); -                case OpenPgpApi.ACTION_SIGN_AND_ENCRYPT: -                    return encryptAndSignImpl(data, input, output, accSettings, true); -                case OpenPgpApi.ACTION_DECRYPT_VERIFY: { -                    String currentPkg = getCurrentCallingPackage(); -                    Set<Long> allowedKeyIds = -                            mProviderHelper.getAllKeyIdsForApp( -                                    ApiAccounts.buildBaseUri(currentPkg)); -                    return decryptAndVerifyImpl(data, input, output, allowedKeyIds, false); -                } -                case OpenPgpApi.ACTION_DECRYPT_METADATA: { -                    String currentPkg = getCurrentCallingPackage(); -                    Set<Long> allowedKeyIds = -                            mProviderHelper.getAllKeyIdsForApp( -                                    ApiAccounts.buildBaseUri(currentPkg)); -                    return decryptAndVerifyImpl(data, input, output, allowedKeyIds, true); -                } -                case OpenPgpApi.ACTION_GET_KEY: -                    return getKeyImpl(data); -                case OpenPgpApi.ACTION_GET_KEY_IDS: -                    return getKeyIdsImpl(data); -                default: -                    return null; +            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 +                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); +            } else if (OpenPgpApi.ACTION_ENCRYPT.equals(action)) { +                return encryptAndSignImpl(data, input, output, accSettings, false); +            } else if (OpenPgpApi.ACTION_SIGN_AND_ENCRYPT.equals(action)) { +                return encryptAndSignImpl(data, input, output, accSettings, true); +            } else if (OpenPgpApi.ACTION_DECRYPT_VERIFY.equals(action)) { +                String currentPkg = getCurrentCallingPackage(); +                Set<Long> allowedKeyIds = +                        mProviderHelper.getAllKeyIdsForApp( +                                ApiAccounts.buildBaseUri(currentPkg)); +                return decryptAndVerifyImpl(data, input, output, allowedKeyIds, false); +            } else if (OpenPgpApi.ACTION_DECRYPT_METADATA.equals(action)) { +                String currentPkg = getCurrentCallingPackage(); +                Set<Long> allowedKeyIds = +                        mProviderHelper.getAllKeyIdsForApp( +                                ApiAccounts.buildBaseUri(currentPkg)); +                return decryptAndVerifyImpl(data, input, output, allowedKeyIds, true); +            } else if (OpenPgpApi.ACTION_GET_KEY.equals(action)) { +                return getKeyImpl(data); +            } else if (OpenPgpApi.ACTION_GET_KEY_IDS.equals(action)) { +                return getKeyIdsImpl(data); +            } else { +                return null;              }          } 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 c88e0bd8d..515711d1f 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java @@ -486,11 +486,6 @@ public class KeychainIntentService extends IntentService implements Progressable                              // TODO Just silently drop the requested signature? Shouldn't we throw here?                          } -                        // this assumes that the bytes are cleartext (valid for current implementation!) -                        if (source == IO_BYTES) { -                            builder.setCleartextInput(true); -                        } -                          SignEncryptResult result = builder.build().execute();                          resultData.putParcelable(SignEncryptResult.EXTRA_RESULT, result); 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 Binary files differdeleted file mode 100644 index e35902ec7..000000000 --- a/OpenKeychain/src/main/res/drawable/mat2.jpg +++ /dev/null diff --git a/OpenKeychain/src/main/res/layout/custom_drawer.xml b/OpenKeychain/src/main/res/layout/custom_drawer.xml new file mode 100644 index 000000000..021226ba3 --- /dev/null +++ b/OpenKeychain/src/main/res/layout/custom_drawer.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="utf-8"?> +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" +    android:orientation="vertical" +    android:layout_width="match_parent" +    android:layout_height="match_parent" +    android:gravity="center" +    android:background="@color/colorPrimary"> + +    <TextView +        android:layout_width="wrap_content" +        android:layout_height="wrap_content" +        android:textAppearance="?android:attr/textAppearanceMedium" +        android:text="@string/app_name" +        android:textColor="@color/white" +        android:layout_gravity="center_horizontal" /> +</LinearLayout>
\ 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 a51420ba1..dfb675a37 100644 --- a/OpenKeychain/src/main/res/values/strings.xml +++ b/OpenKeychain/src/main/res/values/strings.xml @@ -41,6 +41,7 @@      <string name="title_create_key">"Create Key"</string>      <string name="title_exchange_keys">"Exchange Keys"</string>      <string name="title_advanced_key_info">"Advanced Key Info"</string> +    <string name="title_keys">"Keys"</string>      <!-- section -->      <string name="section_user_ids">"Identities"</string> 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 @@          <item name="colorAccent">@color/accent</item>          <!-- Navigation Drawer library --> -        <item name="drawerType">@integer/DRAWERTYPE_IMAGE</item> -        <item name="multipaneSupport">true</item> +        <item name="drawerType">@integer/DRAWERTYPE_CUSTOM</item> +        <item name="theme">@style/ThemeOverlay.AppCompat.Dark.ActionBar</item> +        <item name="popupTheme">@style/Base.V21.Theme.AppCompat.Light.Dialog</item> +        <item name="drawerColor">#fafafa</item> +        <item name="singleAccount">false</item> +        <item name="sectionStyle">@style/MaterialSectionTheme.Light</item> +        <item name="subheaderStyle">@style/MaterialSubheaderTheme.Light</item> +        <item name="multipaneSupport">false</item> +        <item name="rippleBackport">false</item> +        <item name="uniqueToolbarColor">false</item>      </style>      <!-- http://android-developers.blogspot.de/2014/10/appcompat-v21-material-design-for-pre.html -->  | 
