diff options
Diffstat (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure')
15 files changed, 857 insertions, 215 deletions
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/PgpSignEncryptResult.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/PgpSignEncryptResult.java index de2f64404..a1204c0b5 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/PgpSignEncryptResult.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/PgpSignEncryptResult.java @@ -35,7 +35,6 @@ public class PgpSignEncryptResult extends OperationResult {      long mNfcKeyId;      byte[] mNfcHash;      int mNfcAlgo; -    Date mNfcTimestamp;      String mNfcPassphrase;      byte[] mDetachedSignature; @@ -47,11 +46,10 @@ public class PgpSignEncryptResult extends OperationResult {          mKeyIdPassphraseNeeded = keyIdPassphraseNeeded;      } -    public void setNfcData(long nfcKeyId, byte[] nfcHash, int nfcAlgo, Date nfcTimestamp, String passphrase) { +    public void setNfcData(long nfcKeyId, byte[] nfcHash, int nfcAlgo, String passphrase) {          mNfcKeyId = nfcKeyId;          mNfcHash = nfcHash;          mNfcAlgo = nfcAlgo; -        mNfcTimestamp = nfcTimestamp;          mNfcPassphrase = passphrase;      } @@ -71,10 +69,6 @@ public class PgpSignEncryptResult extends OperationResult {          return mNfcAlgo;      } -    public Date getNfcTimestamp() { -        return mNfcTimestamp; -    } -      public String getNfcPassphrase() {          return mNfcPassphrase;      } @@ -95,7 +89,6 @@ public class PgpSignEncryptResult extends OperationResult {          super(source);          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;      } @@ -112,12 +105,6 @@ public class PgpSignEncryptResult extends OperationResult {              dest.writeInt(0);          }          dest.writeInt(mNfcAlgo); -        if (mNfcTimestamp != null) { -            dest.writeInt(1); -            dest.writeLong(mNfcTimestamp.getTime()); -        } else { -            dest.writeInt(0); -        }          if (mDetachedSignature != null) {              dest.writeInt(1);              dest.writeByteArray(mDetachedSignature); 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 ed0de65b0..23e8094b9 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 @@ -21,6 +21,9 @@ import android.os.Parcel;  import java.util.ArrayList; +import org.sufficientlysecure.keychain.service.input.NfcOperationsParcel; + +  public class SignEncryptResult extends OperationResult {      ArrayList<PgpSignEncryptResult> mResults; 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 ab91d7747..df409902f 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedSecretKey.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedSecretKey.java @@ -42,10 +42,13 @@ import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;  import org.sufficientlysecure.keychain.pgp.exception.PgpKeyNotFoundException;  import org.sufficientlysecure.keychain.util.Log; +import java.nio.ByteBuffer;  import java.util.ArrayList;  import java.util.Date;  import java.util.HashMap;  import java.util.List; +import java.util.Map; +  /**   * Wrapper for a PGPSecretKey. @@ -183,13 +186,13 @@ public class CanonicalizedSecretKey extends CanonicalizedPublicKey {          return PgpConstants.sPreferredHashAlgorithms;      } -    private PGPContentSignerBuilder getContentSignerBuilder(int hashAlgo, byte[] nfcSignedHash, -                                                            Date nfcCreationTimestamp) { +    private PGPContentSignerBuilder getContentSignerBuilder(int hashAlgo, +            Map<ByteBuffer,byte[]> signedHashes) {          if (mPrivateKeyState == PRIVATE_KEY_STATE_DIVERT_TO_CARD) {              // use synchronous "NFC based" SignerBuilder              return new NfcSyncPGPContentSignerBuilder(                      mSecretKey.getPublicKey().getAlgorithm(), hashAlgo, -                    mSecretKey.getKeyID(), nfcSignedHash, nfcCreationTimestamp) +                    mSecretKey.getKeyID(), signedHashes)                      .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);          } else {              // content signer based on signing key algorithm and chosen hash algorithm @@ -200,28 +203,24 @@ public class CanonicalizedSecretKey extends CanonicalizedPublicKey {      }      public PGPSignatureGenerator getSignatureGenerator(int hashAlgo, boolean cleartext, -                                                       byte[] nfcSignedHash, Date nfcCreationTimestamp) +            Map<ByteBuffer,byte[]> signedHashes, Date creationTimestamp)              throws PgpGeneralException {          if (mPrivateKeyState == PRIVATE_KEY_STATE_LOCKED) {              throw new PrivateKeyNotUnlockedException();          } -        if (nfcSignedHash != null && nfcCreationTimestamp == null) { -            throw new PgpGeneralException("Got nfc hash without timestamp!!"); -        }          // We explicitly create a signature creation timestamp in this place.          // That way, we can inject an artificial one from outside, ie the one          // used in previous runs of this function. -        if (nfcCreationTimestamp == null) { +        if (creationTimestamp == null) {              // to sign using nfc PgpSignEncrypt is executed two times.              // the first time it stops to return the PendingIntent for nfc connection and signing the hash              // the second time the signed hash is used.              // to get the same hash we cache the timestamp for the second round! -            nfcCreationTimestamp = new Date(); +            creationTimestamp = new Date();          } -        PGPContentSignerBuilder contentSignerBuilder = getContentSignerBuilder(hashAlgo, -                nfcSignedHash, nfcCreationTimestamp); +        PGPContentSignerBuilder contentSignerBuilder = getContentSignerBuilder(hashAlgo, signedHashes);          int signatureType;          if (cleartext) { @@ -237,7 +236,7 @@ public class CanonicalizedSecretKey extends CanonicalizedPublicKey {              PGPSignatureSubpacketGenerator spGen = new PGPSignatureSubpacketGenerator();              spGen.setSignerUserID(false, mRing.getPrimaryUserIdWithFallback()); -            spGen.setSignatureCreationTime(false, nfcCreationTimestamp); +            spGen.setSignatureCreationTime(false, creationTimestamp);              signatureGenerator.setHashedSubpackets(spGen.generate());              return signatureGenerator;          } catch (PgpKeyNotFoundException | PGPException e) { @@ -267,8 +266,9 @@ public class CanonicalizedSecretKey extends CanonicalizedPublicKey {       * @param userIds       User IDs to certify       * @return A keyring with added certifications       */ -    public UncachedKeyRing certifyUserIds(CanonicalizedPublicKeyRing publicKeyRing, List<String> userIds, -                                          byte[] nfcSignedHash, Date nfcCreationTimestamp) { +    public UncachedKeyRing certifyUserIds(CanonicalizedPublicKeyRing publicKeyRing, +            List<String> userIds, +            HashMap<ByteBuffer,byte[]> signedHashes, Date creationTimestamp) {          if (mPrivateKeyState == PRIVATE_KEY_STATE_LOCKED) {              throw new PrivateKeyNotUnlockedException();          } @@ -283,7 +283,7 @@ public class CanonicalizedSecretKey extends CanonicalizedPublicKey {          PGPSignatureGenerator signatureGenerator;          {              PGPContentSignerBuilder contentSignerBuilder = getContentSignerBuilder( -                    PgpConstants.CERTIFY_HASH_ALGO, nfcSignedHash, nfcCreationTimestamp); +                    PgpConstants.CERTIFY_HASH_ALGO, signedHashes);              signatureGenerator = new PGPSignatureGenerator(contentSignerBuilder);              try { @@ -296,9 +296,9 @@ public class CanonicalizedSecretKey extends CanonicalizedPublicKey {          { // supply signatureGenerator with a SubpacketVector              PGPSignatureSubpacketGenerator spGen = new PGPSignatureSubpacketGenerator(); -            if (nfcCreationTimestamp != null) { -                spGen.setSignatureCreationTime(false, nfcCreationTimestamp); -                Log.d(Constants.TAG, "For NFC: set sig creation time to " + nfcCreationTimestamp); +            if (creationTimestamp != null) { +                spGen.setSignatureCreationTime(false, creationTimestamp); +                Log.d(Constants.TAG, "For NFC: set sig creation time to " + creationTimestamp);              }              PGPSignatureSubpacketVector packetVector = spGen.generate();              signatureGenerator.setHashedSubpackets(packetVector); @@ -331,7 +331,8 @@ public class CanonicalizedSecretKey extends CanonicalizedPublicKey {       * @return A keyring with added certifications       */      public UncachedKeyRing certifyUserAttributes(CanonicalizedPublicKeyRing publicKeyRing, -            List<WrappedUserAttribute> userAttributes, byte[] nfcSignedHash, Date nfcCreationTimestamp) { +            List<WrappedUserAttribute> userAttributes, +            HashMap<ByteBuffer,byte[]> signedHashes, Date creationTimestamp) {          if (mPrivateKeyState == PRIVATE_KEY_STATE_LOCKED) {              throw new PrivateKeyNotUnlockedException();          } @@ -346,7 +347,7 @@ public class CanonicalizedSecretKey extends CanonicalizedPublicKey {          PGPSignatureGenerator signatureGenerator;          {              PGPContentSignerBuilder contentSignerBuilder = getContentSignerBuilder( -                    PgpConstants.CERTIFY_HASH_ALGO, nfcSignedHash, nfcCreationTimestamp); +                    PgpConstants.CERTIFY_HASH_ALGO, signedHashes);              signatureGenerator = new PGPSignatureGenerator(contentSignerBuilder);              try { @@ -359,9 +360,9 @@ public class CanonicalizedSecretKey extends CanonicalizedPublicKey {          { // supply signatureGenerator with a SubpacketVector              PGPSignatureSubpacketGenerator spGen = new PGPSignatureSubpacketGenerator(); -            if (nfcCreationTimestamp != null) { -                spGen.setSignatureCreationTime(false, nfcCreationTimestamp); -                Log.d(Constants.TAG, "For NFC: set sig creation time to " + nfcCreationTimestamp); +            if (creationTimestamp != null) { +                spGen.setSignatureCreationTime(false, creationTimestamp); +                Log.d(Constants.TAG, "For NFC: set sig creation time to " + creationTimestamp);              }              PGPSignatureSubpacketVector packetVector = spGen.generate();              signatureGenerator.setHashedSubpackets(packetVector); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpSignEncryptInput.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpSignEncryptInputParcel.java index 2dec4b9c2..00ecc179e 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpSignEncryptInput.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpSignEncryptInputParcel.java @@ -20,10 +20,17 @@ package org.sufficientlysecure.keychain.pgp;  import org.spongycastle.bcpg.CompressionAlgorithmTags;  import org.sufficientlysecure.keychain.Constants; +import org.sufficientlysecure.keychain.service.input.CryptoInputParcel; +import java.nio.ByteBuffer;  import java.util.Date; +import java.util.Map; -public class PgpSignEncryptInput { +import android.os.Parcel; +import android.os.Parcelable; + + +public class PgpSignEncryptInputParcel implements Parcelable {      protected String mVersionHeader = null;      protected boolean mEnableAsciiArmorOutput = false; @@ -36,13 +43,71 @@ public class PgpSignEncryptInput {      protected int mSignatureHashAlgorithm = PgpConstants.OpenKeychainHashAlgorithmTags.USE_PREFERRED;      protected String mSignaturePassphrase = null;      protected long mAdditionalEncryptId = Constants.key.none; -    protected byte[] mNfcSignedHash = null; -    protected Date mNfcCreationTimestamp = null;      protected boolean mFailOnMissingEncryptionKeyIds = false;      protected String mCharset;      protected boolean mCleartextSignature;      protected boolean mDetachedSignature = false;      protected boolean mHiddenRecipients = false; +    protected CryptoInputParcel mCryptoInput = new CryptoInputParcel(null); + +    public PgpSignEncryptInputParcel() { + +    } + +    PgpSignEncryptInputParcel(Parcel source) { + +        // we do all of those here, so the PgpSignEncryptInput class doesn't have to be parcelable +        mVersionHeader = source.readString(); +        mEnableAsciiArmorOutput  = source.readInt() == 1; +        mCompressionId = source.readInt(); +        mEncryptionMasterKeyIds = source.createLongArray(); +        mSymmetricPassphrase = source.readString(); +        mSymmetricEncryptionAlgorithm = source.readInt(); +        mSignatureMasterKeyId = source.readLong(); +        mSignatureSubKeyId = source.readInt() == 1 ? source.readLong() : null; +        mSignatureHashAlgorithm = source.readInt(); +        mSignaturePassphrase = source.readString(); +        mAdditionalEncryptId = source.readLong(); +        mFailOnMissingEncryptionKeyIds = source.readInt() == 1; +        mCharset = source.readString(); +        mCleartextSignature = source.readInt() == 1; +        mDetachedSignature = source.readInt() == 1; +        mHiddenRecipients = source.readInt() == 1; + +        mCryptoInput = source.readParcelable(PgpSignEncryptInputParcel.class.getClassLoader()); +    } + +    @Override +    public int describeContents() { +        return 0; +    } + +    @Override +    public void writeToParcel(Parcel dest, int flags) { +        dest.writeString(mVersionHeader); +        dest.writeInt(mEnableAsciiArmorOutput ? 1 : 0); +        dest.writeInt(mCompressionId); +        dest.writeLongArray(mEncryptionMasterKeyIds); +        dest.writeString(mSymmetricPassphrase); +        dest.writeInt(mSymmetricEncryptionAlgorithm); +        dest.writeLong(mSignatureMasterKeyId); +        if (mSignatureSubKeyId != null) { +            dest.writeInt(1); +            dest.writeLong(mSignatureSubKeyId); +        } else { +            dest.writeInt(0); +        } +        dest.writeInt(mSignatureHashAlgorithm); +        dest.writeString(mSignaturePassphrase); +        dest.writeLong(mAdditionalEncryptId); +        dest.writeInt(mFailOnMissingEncryptionKeyIds ? 1 : 0); +        dest.writeString(mCharset); +        dest.writeInt(mCleartextSignature ? 1 : 0); +        dest.writeInt(mDetachedSignature ? 1 : 0); +        dest.writeInt(mHiddenRecipients ? 1 : 0); + +        dest.writeParcelable(mCryptoInput, 0); +    }      public String getCharset() {          return mCharset; @@ -56,19 +121,11 @@ public class PgpSignEncryptInput {          return mFailOnMissingEncryptionKeyIds;      } -    public Date getNfcCreationTimestamp() { -        return mNfcCreationTimestamp; -    } - -    public byte[] getNfcSignedHash() { -        return mNfcSignedHash; -    } -      public long getAdditionalEncryptId() {          return mAdditionalEncryptId;      } -    public PgpSignEncryptInput setAdditionalEncryptId(long additionalEncryptId) { +    public PgpSignEncryptInputParcel setAdditionalEncryptId(long additionalEncryptId) {          mAdditionalEncryptId = additionalEncryptId;          return this;      } @@ -77,7 +134,7 @@ public class PgpSignEncryptInput {          return mSignaturePassphrase;      } -    public PgpSignEncryptInput setSignaturePassphrase(String signaturePassphrase) { +    public PgpSignEncryptInputParcel setSignaturePassphrase(String signaturePassphrase) {          mSignaturePassphrase = signaturePassphrase;          return this;      } @@ -86,7 +143,7 @@ public class PgpSignEncryptInput {          return mSignatureHashAlgorithm;      } -    public PgpSignEncryptInput setSignatureHashAlgorithm(int signatureHashAlgorithm) { +    public PgpSignEncryptInputParcel setSignatureHashAlgorithm(int signatureHashAlgorithm) {          mSignatureHashAlgorithm = signatureHashAlgorithm;          return this;      } @@ -95,7 +152,7 @@ public class PgpSignEncryptInput {          return mSignatureSubKeyId;      } -    public PgpSignEncryptInput setSignatureSubKeyId(long signatureSubKeyId) { +    public PgpSignEncryptInputParcel setSignatureSubKeyId(long signatureSubKeyId) {          mSignatureSubKeyId = signatureSubKeyId;          return this;      } @@ -104,7 +161,7 @@ public class PgpSignEncryptInput {          return mSignatureMasterKeyId;      } -    public PgpSignEncryptInput setSignatureMasterKeyId(long signatureMasterKeyId) { +    public PgpSignEncryptInputParcel setSignatureMasterKeyId(long signatureMasterKeyId) {          mSignatureMasterKeyId = signatureMasterKeyId;          return this;      } @@ -113,7 +170,7 @@ public class PgpSignEncryptInput {          return mSymmetricEncryptionAlgorithm;      } -    public PgpSignEncryptInput setSymmetricEncryptionAlgorithm(int symmetricEncryptionAlgorithm) { +    public PgpSignEncryptInputParcel setSymmetricEncryptionAlgorithm(int symmetricEncryptionAlgorithm) {          mSymmetricEncryptionAlgorithm = symmetricEncryptionAlgorithm;          return this;      } @@ -122,7 +179,7 @@ public class PgpSignEncryptInput {          return mSymmetricPassphrase;      } -    public PgpSignEncryptInput setSymmetricPassphrase(String symmetricPassphrase) { +    public PgpSignEncryptInputParcel setSymmetricPassphrase(String symmetricPassphrase) {          mSymmetricPassphrase = symmetricPassphrase;          return this;      } @@ -131,7 +188,7 @@ public class PgpSignEncryptInput {          return mEncryptionMasterKeyIds;      } -    public PgpSignEncryptInput setEncryptionMasterKeyIds(long[] encryptionMasterKeyIds) { +    public PgpSignEncryptInputParcel setEncryptionMasterKeyIds(long[] encryptionMasterKeyIds) {          mEncryptionMasterKeyIds = encryptionMasterKeyIds;          return this;      } @@ -140,7 +197,7 @@ public class PgpSignEncryptInput {          return mCompressionId;      } -    public PgpSignEncryptInput setCompressionId(int compressionId) { +    public PgpSignEncryptInputParcel setCompressionId(int compressionId) {          mCompressionId = compressionId;          return this;      } @@ -153,28 +210,22 @@ public class PgpSignEncryptInput {          return mVersionHeader;      } -    public PgpSignEncryptInput setVersionHeader(String versionHeader) { +    public PgpSignEncryptInputParcel setVersionHeader(String versionHeader) {          mVersionHeader = versionHeader;          return this;      } -    public PgpSignEncryptInput setEnableAsciiArmorOutput(boolean enableAsciiArmorOutput) { +    public PgpSignEncryptInputParcel setEnableAsciiArmorOutput(boolean enableAsciiArmorOutput) {          mEnableAsciiArmorOutput = enableAsciiArmorOutput;          return this;      } -    public PgpSignEncryptInput setFailOnMissingEncryptionKeyIds(boolean failOnMissingEncryptionKeyIds) { +    public PgpSignEncryptInputParcel setFailOnMissingEncryptionKeyIds(boolean failOnMissingEncryptionKeyIds) {          mFailOnMissingEncryptionKeyIds = failOnMissingEncryptionKeyIds;          return this;      } -    public PgpSignEncryptInput setNfcState(byte[] signedHash, Date creationTimestamp) { -        mNfcSignedHash = signedHash; -        mNfcCreationTimestamp = creationTimestamp; -        return this; -    } - -    public PgpSignEncryptInput setCleartextSignature(boolean cleartextSignature) { +    public PgpSignEncryptInputParcel setCleartextSignature(boolean cleartextSignature) {          this.mCleartextSignature = cleartextSignature;          return this;      } @@ -183,7 +234,7 @@ public class PgpSignEncryptInput {          return mCleartextSignature;      } -    public PgpSignEncryptInput setDetachedSignature(boolean detachedSignature) { +    public PgpSignEncryptInputParcel setDetachedSignature(boolean detachedSignature) {          this.mDetachedSignature = detachedSignature;          return this;      } @@ -192,7 +243,7 @@ public class PgpSignEncryptInput {          return mDetachedSignature;      } -    public PgpSignEncryptInput setHiddenRecipients(boolean hiddenRecipients) { +    public PgpSignEncryptInputParcel setHiddenRecipients(boolean hiddenRecipients) {          this.mHiddenRecipients = hiddenRecipients;          return this;      } @@ -200,5 +251,29 @@ public class PgpSignEncryptInput {      public boolean isHiddenRecipients() {          return mHiddenRecipients;      } + +    public PgpSignEncryptInputParcel setCryptoInput(CryptoInputParcel cryptoInput) { +        mCryptoInput = cryptoInput; +        return this; +    } + +    public Map<ByteBuffer, byte[]> getCryptoData() { +        return mCryptoInput.getCryptoData(); +    } + +    public Date getSignatureTime() { +        return mCryptoInput.getSignatureTime(); +    } + +    public static final Creator<PgpSignEncryptInputParcel> CREATOR = new Creator<PgpSignEncryptInputParcel>() { +        public PgpSignEncryptInputParcel createFromParcel(final Parcel source) { +            return new PgpSignEncryptInputParcel(source); +        } + +        public PgpSignEncryptInputParcel[] newArray(final int size) { +            return new PgpSignEncryptInputParcel[size]; +        } +    }; +  } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpSignEncryptOperation.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpSignEncryptOperation.java index 94e04060d..2e515137a 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpSignEncryptOperation.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpSignEncryptOperation.java @@ -44,6 +44,8 @@ import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;  import org.sufficientlysecure.keychain.pgp.exception.PgpKeyNotFoundException;  import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;  import org.sufficientlysecure.keychain.provider.ProviderHelper; +import org.sufficientlysecure.keychain.service.input.CryptoInputParcel; +import org.sufficientlysecure.keychain.service.input.NfcOperationsParcel;  import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;  import org.sufficientlysecure.keychain.util.InputData;  import org.sufficientlysecure.keychain.util.Log; @@ -71,7 +73,7 @@ import java.util.concurrent.atomic.AtomicBoolean;   *   * For a high-level operation based on URIs, see SignEncryptOperation.   * - * @see org.sufficientlysecure.keychain.pgp.PgpSignEncryptInput + * @see PgpSignEncryptInputParcel   * @see org.sufficientlysecure.keychain.operations.results.PgpSignEncryptResult   * @see org.sufficientlysecure.keychain.operations.SignEncryptOperation   * @@ -99,7 +101,7 @@ public class PgpSignEncryptOperation extends BaseOperation {      /**       * Signs and/or encrypts data based on parameters of class       */ -    public PgpSignEncryptResult execute(PgpSignEncryptInput input, +    public PgpSignEncryptResult execute(PgpSignEncryptInputParcel input,                                       InputData inputData, OutputStream outputStream) {          int indent = 0; @@ -282,7 +284,8 @@ public class PgpSignEncryptOperation extends BaseOperation {              try {                  boolean cleartext = input.isCleartextSignature() && input.isEnableAsciiArmorOutput() && !enableEncryption;                  signatureGenerator = signingKey.getSignatureGenerator( -                        input.getSignatureHashAlgorithm(), cleartext, input.getNfcSignedHash(), input.getNfcCreationTimestamp()); +                        input.getSignatureHashAlgorithm(), cleartext, +                        input.getCryptoData(), input.getSignatureTime());              } catch (PgpGeneralException e) {                  log.add(LogType.MSG_PSE_ERROR_NFC, indent);                  return new PgpSignEncryptResult(PgpSignEncryptResult.RESULT_ERROR, log); @@ -489,7 +492,8 @@ public class PgpSignEncryptOperation extends BaseOperation {                              new PgpSignEncryptResult(PgpSignEncryptResult.RESULT_PENDING_NFC, log);                      // Note that the checked key here is the master key, not the signing key                      // (although these are always the same on Yubikeys) -                    result.setNfcData(input.getSignatureSubKeyId(), e.hashToSign, e.hashAlgo, e.creationTimestamp, input.getSignaturePassphrase()); +                    result.setNfcData(signingKey.getKeyId(), e.hashToSign, e.hashAlgo, +                            input.getSignaturePassphrase());                      Log.d(Constants.TAG, "e.hashToSign" + Hex.toHexString(e.hashToSign));                      return result;                  } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/SignEncryptParcel.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/SignEncryptParcel.java index 8e71e8815..1b14e78fb 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/SignEncryptParcel.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/SignEncryptParcel.java @@ -20,7 +20,6 @@ package org.sufficientlysecure.keychain.pgp;  import android.net.Uri;  import android.os.Parcel; -import android.os.Parcelable;  import java.util.ArrayList;  import java.util.Collection; @@ -40,7 +39,7 @@ import java.util.List;   *   left, which will be returned in a byte array as part of the result parcel.   *   */ -public class SignEncryptParcel extends PgpSignEncryptInput implements Parcelable { +public class SignEncryptParcel extends PgpSignEncryptInputParcel {      public ArrayList<Uri> mInputUris = new ArrayList<>();      public ArrayList<Uri> mOutputUris = new ArrayList<>(); @@ -51,26 +50,7 @@ public class SignEncryptParcel extends PgpSignEncryptInput implements Parcelable      }      public SignEncryptParcel(Parcel src) { - -        // we do all of those here, so the PgpSignEncryptInput class doesn't have to be parcelable -        mVersionHeader = src.readString(); -        mEnableAsciiArmorOutput  = src.readInt() == 1; -        mCompressionId = src.readInt(); -        mEncryptionMasterKeyIds = src.createLongArray(); -        mSymmetricPassphrase = src.readString(); -        mSymmetricEncryptionAlgorithm = src.readInt(); -        mSignatureMasterKeyId = src.readLong(); -        mSignatureSubKeyId = src.readInt() == 1 ? src.readLong() : null; -        mSignatureHashAlgorithm = src.readInt(); -        mSignaturePassphrase = src.readString(); -        mAdditionalEncryptId = src.readLong(); -        mNfcSignedHash = src.createByteArray(); -        mNfcCreationTimestamp = src.readInt() == 1 ? new Date(src.readLong()) : null; -        mFailOnMissingEncryptionKeyIds = src.readInt() == 1; -        mCharset = src.readString(); -        mCleartextSignature = src.readInt() == 1; -        mDetachedSignature = src.readInt() == 1; -        mHiddenRecipients = src.readInt() == 1; +        super(src);          mInputUris = src.createTypedArrayList(Uri.CREATOR);          mOutputUris = src.createTypedArrayList(Uri.CREATOR); @@ -108,34 +88,7 @@ public class SignEncryptParcel extends PgpSignEncryptInput implements Parcelable      }      public void writeToParcel(Parcel dest, int flags) { -        dest.writeString(mVersionHeader); -        dest.writeInt(mEnableAsciiArmorOutput ? 1 : 0); -        dest.writeInt(mCompressionId); -        dest.writeLongArray(mEncryptionMasterKeyIds); -        dest.writeString(mSymmetricPassphrase); -        dest.writeInt(mSymmetricEncryptionAlgorithm); -        dest.writeLong(mSignatureMasterKeyId); -        if (mSignatureSubKeyId != null) { -            dest.writeInt(1); -            dest.writeLong(mSignatureSubKeyId); -        } else { -            dest.writeInt(0); -        } -        dest.writeInt(mSignatureHashAlgorithm); -        dest.writeString(mSignaturePassphrase); -        dest.writeLong(mAdditionalEncryptId); -        dest.writeByteArray(mNfcSignedHash); -        if (mNfcCreationTimestamp != null) { -            dest.writeInt(1); -            dest.writeLong(mNfcCreationTimestamp.getTime()); -        } else { -            dest.writeInt(0); -        } -        dest.writeInt(mFailOnMissingEncryptionKeyIds ? 1 : 0); -        dest.writeString(mCharset); -        dest.writeInt(mCleartextSignature ? 1 : 0); -        dest.writeInt(mDetachedSignature ? 1 : 0); -        dest.writeInt(mHiddenRecipients ? 1 : 0); +        super.writeToParcel(dest, flags);          dest.writeTypedList(mInputUris);          dest.writeTypedList(mOutputUris); 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 a4bc95602..f15bf7925 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/OpenPgpService.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/OpenPgpService.java @@ -38,7 +38,7 @@ import org.sufficientlysecure.keychain.operations.results.OperationResult.LogEnt  import org.sufficientlysecure.keychain.operations.results.PgpSignEncryptResult;  import org.sufficientlysecure.keychain.pgp.PgpConstants;  import org.sufficientlysecure.keychain.pgp.PgpDecryptVerify; -import org.sufficientlysecure.keychain.pgp.PgpSignEncryptInput; +import org.sufficientlysecure.keychain.pgp.PgpSignEncryptInputParcel;  import org.sufficientlysecure.keychain.pgp.PgpSignEncryptOperation;  import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;  import org.sufficientlysecure.keychain.provider.KeychainContract; @@ -48,6 +48,7 @@ import org.sufficientlysecure.keychain.provider.KeychainDatabase.Tables;  import org.sufficientlysecure.keychain.provider.ProviderHelper;  import org.sufficientlysecure.keychain.remote.ui.RemoteServiceActivity;  import org.sufficientlysecure.keychain.remote.ui.SelectSignKeyIdActivity; +import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;  import org.sufficientlysecure.keychain.ui.ImportKeysActivity;  import org.sufficientlysecure.keychain.ui.NfcActivity;  import org.sufficientlysecure.keychain.ui.PassphraseDialogActivity; @@ -258,11 +259,13 @@ public class OpenPgpService extends RemoteService {              }              // carefully: only set if timestamp exists -            Date nfcCreationDate = null; +            Date nfcCreationDate;              long nfcCreationTimestamp = data.getLongExtra(OpenPgpApi.EXTRA_NFC_SIG_CREATION_TIMESTAMP, -1);              Log.d(Constants.TAG, "nfcCreationTimestamp: " + nfcCreationTimestamp);              if (nfcCreationTimestamp != -1) {                  nfcCreationDate = new Date(nfcCreationTimestamp); +            } else { +                nfcCreationDate = new Date();              }              // Get Input- and OutputStream from ParcelFileDescriptor @@ -275,15 +278,18 @@ public class OpenPgpService extends RemoteService {              long inputLength = is.available();              InputData inputData = new InputData(is, inputLength); +            CryptoInputParcel cryptoInput = new CryptoInputParcel(nfcCreationDate); +            cryptoInput.addCryptoData(null, nfcSignedHash); // TODO fix +              // sign-only -            PgpSignEncryptInput pseInput = new PgpSignEncryptInput() +            PgpSignEncryptInputParcel pseInput = new PgpSignEncryptInputParcel()                      .setEnableAsciiArmorOutput(asciiArmor)                      .setCleartextSignature(cleartextSign)                      .setDetachedSignature(!cleartextSign)                      .setVersionHeader(null)                      .setSignatureHashAlgorithm(PgpConstants.OpenKeychainHashAlgorithmTags.USE_PREFERRED)                      .setSignatureMasterKeyId(signKeyId) -                    .setNfcState(nfcSignedHash, nfcCreationDate); +                    .setCryptoInput(cryptoInput);              // execute PGP operation!              PgpSignEncryptOperation pse = new PgpSignEncryptOperation(this, new ProviderHelper(getContext()), null); @@ -298,7 +304,7 @@ public class OpenPgpService extends RemoteService {                      // return PendingIntent to execute NFC activity                      // pass through the signature creation timestamp to be used again on second execution                      // of PgpSignEncrypt when we have the signed hash! -                    data.putExtra(OpenPgpApi.EXTRA_NFC_SIG_CREATION_TIMESTAMP, pgpResult.getNfcTimestamp().getTime()); +                    data.putExtra(OpenPgpApi.EXTRA_NFC_SIG_CREATION_TIMESTAMP, nfcCreationDate.getTime());                      // return PendingIntent to be executed by client                      Intent result = new Intent(); @@ -389,7 +395,7 @@ public class OpenPgpService extends RemoteService {              long inputLength = is.available();              InputData inputData = new InputData(is, inputLength, originalFilename); -            PgpSignEncryptInput pseInput = new PgpSignEncryptInput(); +            PgpSignEncryptInputParcel pseInput = new PgpSignEncryptInputParcel();              pseInput.setEnableAsciiArmorOutput(asciiArmor)                      .setVersionHeader(null)                      .setCompressionId(compressionId) @@ -412,16 +418,21 @@ public class OpenPgpService extends RemoteService {                  byte[] nfcSignedHash = data.getByteArrayExtra(OpenPgpApi.EXTRA_NFC_SIGNED_HASH);                  // carefully: only set if timestamp exists -                Date nfcCreationDate = null; +                Date nfcCreationDate;                  long nfcCreationTimestamp = data.getLongExtra(OpenPgpApi.EXTRA_NFC_SIG_CREATION_TIMESTAMP, -1);                  if (nfcCreationTimestamp != -1) {                      nfcCreationDate = new Date(nfcCreationTimestamp); +                } else { +                    nfcCreationDate = new Date();                  } +                CryptoInputParcel cryptoInput = new CryptoInputParcel(nfcCreationDate); +                cryptoInput.addCryptoData(null, nfcSignedHash); // TODO fix! +                  // sign and encrypt                  pseInput.setSignatureHashAlgorithm(PgpConstants.OpenKeychainHashAlgorithmTags.USE_PREFERRED)                          .setSignatureMasterKeyId(signKeyId) -                        .setNfcState(nfcSignedHash, nfcCreationDate) +                        .setCryptoInput(cryptoInput)                          .setAdditionalEncryptId(signKeyId); // add sign key for encryption              } @@ -439,7 +450,7 @@ public class OpenPgpService extends RemoteService {                      // return PendingIntent to execute NFC activity                      // pass through the signature creation timestamp to be used again on second execution                      // of PgpSignEncrypt when we have the signed hash! -                    data.putExtra(OpenPgpApi.EXTRA_NFC_SIG_CREATION_TIMESTAMP, pgpResult.getNfcTimestamp().getTime()); +                    data.putExtra(OpenPgpApi.EXTRA_NFC_SIG_CREATION_TIMESTAMP, 0L); // TODO fix                      // return PendingIntent to be executed by client                      Intent result = new Intent();                      result.putExtra(OpenPgpApi.RESULT_INTENT, diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/CertifyActionsParcel.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/CertifyActionsParcel.java index d6da18e6e..485d5de72 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/CertifyActionsParcel.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/CertifyActionsParcel.java @@ -26,31 +26,31 @@ import java.util.ArrayList;  import java.util.Date;  import org.sufficientlysecure.keychain.pgp.WrappedUserAttribute; -import org.sufficientlysecure.keychain.service.input.CryptoOperationParcel; +import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;  /**   * This class is a a transferable representation for a number of keyrings to   * be certified.   */ -public class CertifyActionsParcel extends CryptoOperationParcel { +public class CertifyActionsParcel implements Parcelable {      // the master key id to certify with      final public long mMasterKeyId;      public CertifyLevel mLevel;      public ArrayList<CertifyAction> mCertifyActions = new ArrayList<>(); +    public CryptoInputParcel mCryptoInput;      public CertifyActionsParcel(Date operationTime, long masterKeyId) { -        super(operationTime);          mMasterKeyId = masterKeyId; +        mCryptoInput = new CryptoInputParcel(operationTime);          mLevel = CertifyLevel.DEFAULT;      }      public CertifyActionsParcel(Parcel source) { -        super(source); -          mMasterKeyId = source.readLong(); +        mCryptoInput = source.readParcelable(CertifyActionsParcel.class.getClassLoader());          // just like parcelables, this is meant for ad-hoc IPC only and is NOT portable!          mLevel = CertifyLevel.values()[source.readInt()]; @@ -63,9 +63,8 @@ public class CertifyActionsParcel extends CryptoOperationParcel {      @Override      public void writeToParcel(Parcel destination, int flags) { -        super.writeToParcel(destination, flags); -          destination.writeLong(mMasterKeyId); +        destination.writeParcelable(mCryptoInput, 0);          destination.writeInt(mLevel.ordinal());          destination.writeSerializable(mCertifyActions); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/input/CryptoInputParcel.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/input/CryptoInputParcel.java new file mode 100644 index 000000000..e02eda4b3 --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/input/CryptoInputParcel.java @@ -0,0 +1,81 @@ +package org.sufficientlysecure.keychain.service.input; + +import java.nio.ByteBuffer; +import java.util.Collections; +import java.util.Date; +import java.util.HashMap; +import java.util.Map; + +import android.os.Parcel; +import android.os.Parcelable; + + +/** This is a base class for the input of crypto operations. + * + */ +public class CryptoInputParcel implements Parcelable { + +    Date mSignatureTime; + +    // this map contains both decrypted session keys and signed hashes to be +    // used in the crypto operation described by this parcel. +    private HashMap<ByteBuffer,byte[]> mCryptoData = new HashMap<>(); + +    public CryptoInputParcel(Date signatureTime) { +        mSignatureTime = signatureTime == null ? new Date() : signatureTime; +    } + +    protected CryptoInputParcel(Parcel source) { +        mSignatureTime = new Date(source.readLong()); + +        { +            int count = source.readInt(); +            mCryptoData = new HashMap<>(count); +            for (int i = 0; i < count; i++) { +                byte[] key = source.createByteArray(); +                byte[] value = source.createByteArray(); +                mCryptoData.put(ByteBuffer.wrap(key), value); +            } +        } + +    } + +    @Override +    public int describeContents() { +        return 0; +    } + +    @Override +    public void writeToParcel(Parcel dest, int flags) { +        dest.writeLong(mSignatureTime.getTime()); + +        dest.writeInt(mCryptoData.size()); +        for (HashMap.Entry<ByteBuffer,byte[]> entry : mCryptoData.entrySet()) { +            dest.writeByteArray(entry.getKey().array()); +            dest.writeByteArray(entry.getValue()); +        } +    } + +    public void addCryptoData(byte[] hash, byte[] signedHash) { +        mCryptoData.put(ByteBuffer.wrap(hash), signedHash); +    } + +    public Map<ByteBuffer, byte[]> getCryptoData() { +        return Collections.unmodifiableMap(mCryptoData); +    } + +    public Date getSignatureTime() { +        return mSignatureTime; +    } + +    public static final Creator<CryptoInputParcel> CREATOR = new Creator<CryptoInputParcel>() { +        public CryptoInputParcel createFromParcel(final Parcel source) { +            return new CryptoInputParcel(source); +        } + +        public CryptoInputParcel[] newArray(final int size) { +            return new CryptoInputParcel[size]; +        } +    }; + +} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/input/CryptoOperationParcel.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/input/CryptoOperationParcel.java deleted file mode 100644 index 2101755ad..000000000 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/input/CryptoOperationParcel.java +++ /dev/null @@ -1,52 +0,0 @@ -package org.sufficientlysecure.keychain.service.input; - -import java.nio.ByteBuffer; -import java.util.Date; -import java.util.HashMap; - -import android.os.Parcel; -import android.os.Parcelable; - - -/** This is a base class for the input of crypto operations. - * - */ -public abstract class CryptoOperationParcel implements Parcelable { - -    Date mOperationTime; - -    // this map contains both decrypted session keys and signed hashes to be -    // used in the crypto operation described by this parcel. -    HashMap<ByteBuffer,byte[]> mCryptoData; - -    protected CryptoOperationParcel(Date operationTime) { -        mOperationTime = operationTime; -    } - -    protected CryptoOperationParcel(Parcel source) { -        mOperationTime = new Date(source.readLong()); - -        { -            int count = source.readInt(); -            mCryptoData = new HashMap<>(count); -            for (int i = 0; i < count; i++) { -                byte[] key = source.createByteArray(); -                byte[] value = source.createByteArray(); -                mCryptoData.put(ByteBuffer.wrap(key), value); -            } -        } - -    } - -    @Override -    public void writeToParcel(Parcel dest, int flags) { -        dest.writeLong(mOperationTime.getTime()); - -        dest.writeInt(mCryptoData.size()); -        for (HashMap.Entry<ByteBuffer,byte[]> entry : mCryptoData.entrySet()) { -            dest.writeByteArray(entry.getKey().array()); -            dest.writeByteArray(entry.getValue()); -        } -    } - -} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/input/NfcOperationsParcel.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/input/NfcOperationsParcel.java new file mode 100644 index 000000000..5d35e94e0 --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/input/NfcOperationsParcel.java @@ -0,0 +1,85 @@ +package org.sufficientlysecure.keychain.service.input; + +import java.util.Date; + +import android.os.Parcel; +import android.os.Parcelable; + + +public class NfcOperationsParcel implements Parcelable { + +    public enum NfcOperationType { +        NFC_SIGN, NFC_DECRYPT +    } + +    public Date mSignatureTime; +    public final NfcOperationType mType; +    public final byte[][] mInputHash; +    public final int[] mSignAlgo; + +    private NfcOperationsParcel(NfcOperationType type, byte[] inputHash, int signAlgo, Date signatureTime) { +        mType = type; +        mInputHash = new byte[][] { inputHash }; +        mSignAlgo = new int[] { signAlgo }; +        mSignatureTime = signatureTime; +    } + +    public NfcOperationsParcel(Parcel source) { +        mType = NfcOperationType.values()[source.readInt()]; + +        { +            int count = source.readInt(); +            mInputHash = new byte[count][]; +            mSignAlgo = new int[count]; +            for (int i = 0; i < count; i++) { +                mInputHash[i] = source.createByteArray(); +                mSignAlgo[i] = source.readInt(); +            } +        } + +        mSignatureTime = source.readInt() != 0 ? new Date(source.readLong()) : null; + +    } + +    public static NfcOperationsParcel createNfcSignOperation( +            byte[] inputHash, int signAlgo, Date signatureTime) { +        return new NfcOperationsParcel(NfcOperationType.NFC_SIGN, inputHash, signAlgo, signatureTime); +    } + +    public static NfcOperationsParcel createNfcDecryptOperation(byte[] inputHash) { +        return new NfcOperationsParcel(NfcOperationType.NFC_DECRYPT, inputHash, 0, null); +    } + +    @Override +    public int describeContents() { +        return 0; +    } + +    @Override +    public void writeToParcel(Parcel dest, int flags) { +        dest.writeInt(mType.ordinal()); +        dest.writeInt(mInputHash.length); +        for (int i = 0; i < mInputHash.length; i++) { +            dest.writeByteArray(mInputHash[i]); +            dest.writeInt(mSignAlgo[i]); +        } +        if (mSignatureTime != null) { +            dest.writeInt(1); +            dest.writeLong(mSignatureTime.getTime()); +        } else { +            dest.writeInt(0); +        } + +    } + +    public static final Creator<NfcOperationsParcel> CREATOR = new Creator<NfcOperationsParcel>() { +        public NfcOperationsParcel createFromParcel(final Parcel source) { +            return new NfcOperationsParcel(source); +        } + +        public NfcOperationsParcel[] newArray(final int size) { +            return new NfcOperationsParcel[size]; +        } +    }; + +} 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 35dfcb87c..75f22f01c 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptActivity.java @@ -25,15 +25,15 @@ import android.os.Message;  import android.os.Messenger;  import android.view.View; -import org.openintents.openpgp.util.OpenPgpApi;  import org.sufficientlysecure.keychain.R;  import org.sufficientlysecure.keychain.operations.results.PgpSignEncryptResult;  import org.sufficientlysecure.keychain.operations.results.SignEncryptResult;  import org.sufficientlysecure.keychain.pgp.SignEncryptParcel;  import org.sufficientlysecure.keychain.service.KeychainIntentService;  import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler; +import org.sufficientlysecure.keychain.service.input.CryptoInputParcel; +import org.sufficientlysecure.keychain.service.input.NfcOperationsParcel; -import java.util.Date;  public abstract class EncryptActivity extends BaseActivity { @@ -42,8 +42,6 @@ public abstract class EncryptActivity extends BaseActivity {      // For NFC data      protected String mSigningKeyPassphrase = null; -    protected Date mNfcTimestamp = null; -    protected byte[] mNfcHash = null;      @Override      public void onCreate(Bundle savedInstanceState) { @@ -64,17 +62,12 @@ public abstract class EncryptActivity extends BaseActivity {          startActivityForResult(intent, REQUEST_CODE_PASSPHRASE);      } -    protected void startNfcSign(long keyId, String pin, byte[] hashToSign, int hashAlgo) { -        // build PendingIntent for Yubikey NFC operations -        Intent intent = new Intent(this, NfcActivity.class); -        intent.setAction(NfcActivity.ACTION_SIGN_HASH); +    protected void startNfcSign(long keyId, String pin, NfcOperationsParcel nfcOps) { -        // pass params through to activity that it can be returned again later to repeat pgp operation -        intent.putExtra(NfcActivity.EXTRA_DATA, new Intent()); // not used, only relevant to OpenPgpService -        intent.putExtra(NfcActivity.EXTRA_KEY_ID, keyId); -        intent.putExtra(NfcActivity.EXTRA_PIN, pin); -        intent.putExtra(NfcActivity.EXTRA_NFC_HASH_TO_SIGN, hashToSign); -        intent.putExtra(NfcActivity.EXTRA_NFC_HASH_ALGO, hashAlgo); +        Intent intent = new Intent(this, NfcOperationActivity.class); +        intent.putExtra(NfcOperationActivity.EXTRA_PIN, pin); +        intent.putExtra(NfcOperationActivity.EXTRA_NFC_OPS, nfcOps); +        // TODO respect keyid(?)          startActivityForResult(intent, REQUEST_CODE_NFC);      } @@ -93,8 +86,9 @@ public abstract class EncryptActivity extends BaseActivity {              case REQUEST_CODE_NFC: {                  if (resultCode == RESULT_OK && data != null) { -                    mNfcHash = data.getByteArrayExtra(OpenPgpApi.EXTRA_NFC_SIGNED_HASH); -                    startEncrypt(); +                    CryptoInputParcel cryptoInput = +                            data.getParcelableExtra(NfcOperationActivity.RESULT_DATA); +                    startEncrypt(cryptoInput);                      return;                  }                  break; @@ -108,6 +102,10 @@ public abstract class EncryptActivity extends BaseActivity {      }      public void startEncrypt() { +        startEncrypt(null); +    } + +    public void startEncrypt(CryptoInputParcel cryptoInput) {          if (!inputIsValid()) {              // Notify was created by inputIsValid.              return; @@ -117,8 +115,13 @@ public abstract class EncryptActivity extends BaseActivity {          Intent intent = new Intent(this, KeychainIntentService.class);          intent.setAction(KeychainIntentService.ACTION_SIGN_ENCRYPT); +        final SignEncryptParcel input = createEncryptBundle(); +        if (cryptoInput != null) { +            input.setCryptoInput(cryptoInput); +        } +          Bundle data = new Bundle(); -        data.putParcelable(KeychainIntentService.SIGN_ENCRYPT_PARCEL, createEncryptBundle()); +        data.putParcelable(KeychainIntentService.SIGN_ENCRYPT_PARCEL, input);          intent.putExtra(KeychainIntentService.EXTRA_DATA, data);          // Message is received after encrypting is done in KeychainIntentService @@ -141,9 +144,13 @@ public abstract class EncryptActivity extends BaseActivity {                          } else if ((pgpResult.getResult() & PgpSignEncryptResult.RESULT_PENDING_NFC) ==                                  PgpSignEncryptResult.RESULT_PENDING_NFC) { -                            mNfcTimestamp = pgpResult.getNfcTimestamp(); -                            startNfcSign(pgpResult.getNfcKeyId(), pgpResult.getNfcPassphrase(), -                                    pgpResult.getNfcHash(), pgpResult.getNfcAlgo()); +                            NfcOperationsParcel parcel = NfcOperationsParcel.createNfcSignOperation( +                                    pgpResult.getNfcHash(), +                                    pgpResult.getNfcAlgo(), +                                    input.getSignatureTime()); +                            startNfcSign(pgpResult.getNfcKeyId(), +                                    pgpResult.getNfcPassphrase(), parcel); +                          } else {                              throw new RuntimeException("Unhandled pending result!");                          } @@ -158,8 +165,6 @@ public abstract class EncryptActivity extends BaseActivity {                      // no matter the result, reset parameters                      mSigningKeyPassphrase = null; -                    mNfcHash = null; -                    mNfcTimestamp = null;                  }              }          }; 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 f1784fab3..49a0f5016 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptFilesActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptFilesActivity.java @@ -252,7 +252,6 @@ public class EncryptFilesActivity extends EncryptActivity implements EncryptActi              data.setEncryptionMasterKeyIds(mEncryptionKeyIds);              data.setSignatureMasterKeyId(mSigningKeyId);              data.setSignaturePassphrase(mSigningKeyPassphrase); -            data.setNfcState(mNfcHash, mNfcTimestamp);          }          return data;      } 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 14f2c492d..894c1202d 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptTextActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptTextActivity.java @@ -232,7 +232,6 @@ public class EncryptTextActivity extends EncryptActivity implements EncryptActiv              data.setEncryptionMasterKeyIds(mEncryptionKeyIds);              data.setSignatureMasterKeyId(mSigningKeyId);              data.setSignaturePassphrase(mSigningKeyPassphrase); -            data.setNfcState(mNfcHash, mNfcTimestamp);          }          return data;      } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/NfcOperationActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/NfcOperationActivity.java new file mode 100644 index 000000000..c1403d748 --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/NfcOperationActivity.java @@ -0,0 +1,492 @@ +/** + * Copyright (c) 2013-2014 Philipp Jakubeit, Signe Rüsch, Dominik Schürmann + * + * Licensed under the Bouncy Castle License (MIT license). See LICENSE file for details. + */ + +package org.sufficientlysecure.keychain.ui; + +import android.annotation.TargetApi; +import android.app.PendingIntent; +import android.content.Intent; +import android.content.IntentFilter; +import android.nfc.NfcAdapter; +import android.nfc.Tag; +import android.nfc.tech.IsoDep; +import android.os.Build; +import android.os.Bundle; +import android.view.WindowManager; +import android.widget.Toast; + +import org.spongycastle.bcpg.HashAlgorithmTags; +import org.spongycastle.util.encoders.Hex; +import org.sufficientlysecure.keychain.Constants; +import org.sufficientlysecure.keychain.R; +import org.sufficientlysecure.keychain.service.input.CryptoInputParcel; +import org.sufficientlysecure.keychain.service.input.NfcOperationsParcel; +import org.sufficientlysecure.keychain.util.Iso7816TLV; +import org.sufficientlysecure.keychain.util.Log; + +import java.io.IOException; +import java.nio.ByteBuffer; + + +/** + * This class provides a communication interface to OpenPGP applications on ISO SmartCard compliant + * NFC devices. + * + * For the full specs, see http://g10code.com/docs/openpgp-card-2.0.pdf + */ +@TargetApi(Build.VERSION_CODES.GINGERBREAD_MR1) +public class NfcOperationActivity extends BaseActivity { + +    public static final String EXTRA_PIN = "pin"; +    public static final String EXTRA_NFC_OPS = "nfc_operations"; + +    public static final String RESULT_DATA = "result_data"; + +    private static final int TIMEOUT = 100000; + +    private NfcAdapter mNfcAdapter; +    private IsoDep mIsoDep; + +    private String mPin; + +    NfcOperationsParcel mNfcOperations; + +    @Override +    protected void onCreate(Bundle savedInstanceState) { +        super.onCreate(savedInstanceState); +        Log.d(Constants.TAG, "NfcOperationActivity.onCreate"); + +        getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); + +        Intent intent = getIntent(); +        String action = intent.getAction(); +        if (NfcAdapter.ACTION_TAG_DISCOVERED.equals(action)) { +            throw new AssertionError("should not happen: NfcOperationActivity.onCreate is called instead of onNewIntent!"); +        } + +        Bundle data = intent.getExtras(); + +        mNfcOperations = data.getParcelable(EXTRA_NFC_OPS); +        mPin = data.getString(EXTRA_PIN); + +    } + +    @Override +    protected void initLayout() { +        setContentView(R.layout.nfc_activity); +    } + +    /** +     * Called when the system is about to start resuming a previous activity, +     * disables NFC Foreground Dispatch +     */ +    public void onPause() { +        super.onPause(); +        Log.d(Constants.TAG, "NfcOperationActivity.onPause"); + +        disableNfcForegroundDispatch(); +    } + +    /** +     * Called when the activity will start interacting with the user, +     * enables NFC Foreground Dispatch +     */ +    public void onResume() { +        super.onResume(); +        Log.d(Constants.TAG, "NfcOperationActivity.onResume"); + +        enableNfcForegroundDispatch(); +    } + +    /** +     * This activity is started as a singleTop activity. +     * All new NFC Intents which are delivered to this activity are handled here +     */ +    public void onNewIntent(Intent intent) { +        if (NfcAdapter.ACTION_TAG_DISCOVERED.equals(intent.getAction())) { +            try { +                handleNdefDiscoveredIntent(intent); +            } catch (IOException e) { +                Log.e(Constants.TAG, "Connection error!", e); +                toast("Connection Error: " + e.getMessage()); +                setResult(RESULT_CANCELED); +                finish(); +            } +        } +    } + +    /** Handle NFC communication and return a result. +     * +     * This method is called by onNewIntent above upon discovery of an NFC tag. +     * It handles initialization and login to the application, subsequently +     * calls either nfcCalculateSignature() or nfcDecryptSessionKey(), then +     * finishes the activity with an appropiate result. +     * +     * On general communication, see also +     * http://www.cardwerk.com/smartcards/smartcard_standard_ISO7816-4_annex-a.aspx +     * +     * References to pages are generally related to the OpenPGP Application +     * on ISO SmartCard Systems specification. +     * +     */ +    private void handleNdefDiscoveredIntent(Intent intent) throws IOException { + +        Tag detectedTag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG); + +        // Connect to the detected tag, setting a couple of settings +        mIsoDep = IsoDep.get(detectedTag); +        mIsoDep.setTimeout(TIMEOUT); // timeout is set to 100 seconds to avoid cancellation during calculation +        mIsoDep.connect(); + +        // SW1/2 0x9000 is the generic "ok" response, which we expect most of the time. +        // See specification, page 51 +        String accepted = "9000"; + +        // Command APDU (page 51) for SELECT FILE command (page 29) +        String opening = +                "00" // CLA +                        + "A4" // INS +                        + "04" // P1 +                        + "00" // P2 +                        + "06" // Lc (number of bytes) +                        + "D27600012401" // Data (6 bytes) +                        + "00"; // Le +        if ( ! card(opening).equals(accepted)) { // activate connection +            toast("Opening Error!"); +            setResult(RESULT_CANCELED); +            finish(); +            return; +        } + +        // Command APDU for VERIFY command (page 32) +        String login = +                "00" // CLA +                        + "20" // INS +                        + "00" // P1 +                        + "82" // P2 (PW1) +                        + String.format("%02x", mPin.length()) // Lc +                        + Hex.toHexString(mPin.getBytes()); +        if ( ! card(login).equals(accepted)) { // login +            toast("Wrong PIN!"); +            setResult(RESULT_CANCELED); +            finish(); +            return; +        } + +        CryptoInputParcel resultData = new CryptoInputParcel(mNfcOperations.mSignatureTime); + +        switch (mNfcOperations.mType) { + +            case NFC_DECRYPT: + +                for (int i = 0; i < mNfcOperations.mInputHash.length; i++) { +                    byte[] hash = mNfcOperations.mInputHash[i]; +                    byte[] decryptedSessionKey = nfcDecryptSessionKey(hash); +                    resultData.addCryptoData(hash, decryptedSessionKey); +                } +                break; + +            case NFC_SIGN: +                for (int i = 0; i < mNfcOperations.mInputHash.length; i++) { +                    byte[] hash = mNfcOperations.mInputHash[i]; +                    int algo = mNfcOperations.mSignAlgo[i]; +                    byte[] signedHash = nfcCalculateSignature(hash, algo); +                    resultData.addCryptoData(hash, signedHash); +                } +                break; +        } + +        // give data through for new service call +        Intent result = new Intent(); +        result.putExtra(RESULT_DATA, resultData); +        setResult(RESULT_OK, result); +        finish(); + +    } + +    /** +     * Gets the user ID +     * +     * @return the user id as "name <email>" +     * @throws java.io.IOException +     */ +    public String getUserId() throws IOException { +        String info = "00CA006500"; +        String data = "00CA005E00"; +        return getName(card(info)) + " <" + (new String(Hex.decode(getDataField(card(data))))) + ">"; +    } + +    /** Return the key id from application specific data stored on tag, or null +     * if it doesn't exist. +     * +     * @param idx Index of the key to return the fingerprint from. +     * @return The long key id of the requested key, or null if not found. +     */ +    public static Long nfcGetKeyId(IsoDep isoDep, int idx) throws IOException { +        byte[] fp = nfcGetFingerprint(isoDep, idx); +        if (fp == null) { +            return null; +        } +        ByteBuffer buf = ByteBuffer.wrap(fp); +        // skip first 12 bytes of the fingerprint +        buf.position(12); +        // the last eight bytes are the key id (big endian, which is default order in ByteBuffer) +        return buf.getLong(); +    } + +    /** Return fingerprints of all keys from application specific data stored +     * on tag, or null if data not available. +     * +     * @return The fingerprints of all subkeys in a contiguous byte array. +     */ +    public static byte[] nfcGetFingerprints(IsoDep isoDep) throws IOException { +        String data = "00CA006E00"; +        byte[] buf = isoDep.transceive(Hex.decode(data)); + +        Iso7816TLV tlv = Iso7816TLV.readSingle(buf, true); +        Log.d(Constants.TAG, "nfc tlv data:\n" + tlv.prettyPrint()); + +        Iso7816TLV fptlv = Iso7816TLV.findRecursive(tlv, 0xc5); +        if (fptlv == null) { +            return null; +        } + +        return fptlv.mV; +    } + +    /** Return the fingerprint from application specific data stored on tag, or +     * null if it doesn't exist. +     * +     * @param idx Index of the key to return the fingerprint from. +     * @return The fingerprint of the requested key, or null if not found. +     */ +    public static byte[] nfcGetFingerprint(IsoDep isoDep, int idx) throws IOException { +        byte[] data = nfcGetFingerprints(isoDep); + +        // return the master key fingerprint +        ByteBuffer fpbuf = ByteBuffer.wrap(data); +        byte[] fp = new byte[20]; +        fpbuf.position(idx*20); +        fpbuf.get(fp, 0, 20); + +        return fp; +    } + +    /** +     * Calls to calculate the signature and returns the MPI value +     * +     * @param hash the hash for signing +     * @return a big integer representing the MPI for the given hash +     * @throws java.io.IOException +     */ +    public byte[] nfcCalculateSignature(byte[] hash, int hashAlgo) throws IOException { + +        // dsi, including Lc +        String dsi; + +        Log.i(Constants.TAG, "Hash: " + hashAlgo); +        switch (hashAlgo) { +            case HashAlgorithmTags.SHA1: +                if (hash.length != 20) { +                    throw new RuntimeException("Bad hash length (" + hash.length + ", expected 10!"); +                } +                dsi = "23" // Lc +                        + "3021" // Tag/Length of Sequence, the 0x21 includes all following 33 bytes +                        + "3009" // Tag/Length of Sequence, the 0x09 are the following header bytes +                        + "0605" + "2B0E03021A" // OID of SHA1 +                        + "0500" // TLV coding of ZERO +                        + "0414" + getHex(hash); // 0x14 are 20 hash bytes +                break; +            case HashAlgorithmTags.RIPEMD160: +                if (hash.length != 20) { +                    throw new RuntimeException("Bad hash length (" + hash.length + ", expected 20!"); +                } +                dsi = "233021300906052B2403020105000414" + getHex(hash); +                break; +            case HashAlgorithmTags.SHA224: +                if (hash.length != 28) { +                    throw new RuntimeException("Bad hash length (" + hash.length + ", expected 28!"); +                } +                dsi = "2F302D300D06096086480165030402040500041C" + getHex(hash); +                break; +            case HashAlgorithmTags.SHA256: +                if (hash.length != 32) { +                    throw new RuntimeException("Bad hash length (" + hash.length + ", expected 32!"); +                } +                dsi = "333031300D060960864801650304020105000420" + getHex(hash); +                break; +            case HashAlgorithmTags.SHA384: +                if (hash.length != 48) { +                    throw new RuntimeException("Bad hash length (" + hash.length + ", expected 48!"); +                } +                dsi = "433041300D060960864801650304020205000430" + getHex(hash); +                break; +            case HashAlgorithmTags.SHA512: +                if (hash.length != 64) { +                    throw new RuntimeException("Bad hash length (" + hash.length + ", expected 64!"); +                } +                dsi = "533051300D060960864801650304020305000440" + getHex(hash); +                break; +            default: +                throw new RuntimeException("Not supported hash algo!"); +        } + +        // Command APDU for PERFORM SECURITY OPERATION: COMPUTE DIGITAL SIGNATURE (page 37) +        String apdu  = +                "002A9E9A" // CLA, INS, P1, P2 +                        + dsi // digital signature input +                        + "00"; // Le + +        String response = card(apdu); + +        // split up response into signature and status +        String status = response.substring(response.length()-4); +        String signature = response.substring(0, response.length() - 4); + +        // while we are getting 0x61 status codes, retrieve more data +        while (status.substring(0, 2).equals("61")) { +            Log.d(Constants.TAG, "requesting more data, status " + status); +            // Send GET RESPONSE command +            response = card("00C00000" + status.substring(2)); +            status = response.substring(response.length()-4); +            signature += response.substring(0, response.length()-4); +        } + +        Log.d(Constants.TAG, "final response:" + status); + +        if ( ! status.equals("9000")) { +            toast("Bad NFC response code: " + status); +            return null; +        } + +        // Make sure the signature we received is actually the expected number of bytes long! +        if (signature.length() != 256 && signature.length() != 512) { +            toast("Bad signature length! Expected 128 or 256 bytes, got " + signature.length() / 2); +            return null; +        } + +        return Hex.decode(signature); +    } + +    /** +     * Calls to calculate the signature and returns the MPI value +     * +     * @param encryptedSessionKey the encoded session key +     * @return the decoded session key +     * @throws java.io.IOException +     */ +    public byte[] nfcDecryptSessionKey(byte[] encryptedSessionKey) throws IOException { +        String firstApdu = "102a8086fe"; +        String secondApdu = "002a808603"; +        String le = "00"; + +        byte[] one = new byte[254]; +        // leave out first byte: +        System.arraycopy(encryptedSessionKey, 1, one, 0, one.length); + +        byte[] two = new byte[encryptedSessionKey.length - 1 - one.length]; +        for (int i = 0; i < two.length; i++) { +            two[i] = encryptedSessionKey[i + one.length + 1]; +        } + +        String first = card(firstApdu + getHex(one)); +        String second = card(secondApdu + getHex(two) + le); + +        String decryptedSessionKey = getDataField(second); + +        Log.d(Constants.TAG, "decryptedSessionKey: " + decryptedSessionKey); + +        return Hex.decode(decryptedSessionKey); +    } + +    /** +     * Prints a message to the screen +     * +     * @param text the text which should be contained within the toast +     */ +    private void toast(String text) { +        Toast.makeText(this, text, Toast.LENGTH_LONG).show(); +    } + +    /** +     * Receive new NFC Intents to this activity only by enabling foreground dispatch. +     * This can only be done in onResume! +     */ +    public void enableNfcForegroundDispatch() { +        mNfcAdapter = NfcAdapter.getDefaultAdapter(this); +        Intent nfcI = new Intent(this, NfcOperationActivity.class) +                .addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP | Intent.FLAG_ACTIVITY_CLEAR_TOP); +        PendingIntent nfcPendingIntent = PendingIntent.getActivity(this, 0, nfcI, PendingIntent.FLAG_CANCEL_CURRENT); +        IntentFilter[] writeTagFilters = new IntentFilter[]{ +                new IntentFilter(NfcAdapter.ACTION_TAG_DISCOVERED) +        }; + +        // https://code.google.com/p/android/issues/detail?id=62918 +        // maybe mNfcAdapter.enableReaderMode(); ? +        try { +            mNfcAdapter.enableForegroundDispatch(this, nfcPendingIntent, writeTagFilters, null); +        } catch (IllegalStateException e) { +            Log.i(Constants.TAG, "NfcForegroundDispatch Error!", e); +        } +        Log.d(Constants.TAG, "NfcForegroundDispatch has been enabled!"); +    } + +    /** +     * Disable foreground dispatch in onPause! +     */ +    public void disableNfcForegroundDispatch() { +        mNfcAdapter.disableForegroundDispatch(this); +        Log.d(Constants.TAG, "NfcForegroundDispatch has been disabled!"); +    } + +    /** +     * Gets the name of the user out of the raw card output regarding card holder related data +     * +     * @param name the raw card holder related data from the card +     * @return the name given in this data +     */ +    public String getName(String name) { +        String slength; +        int ilength; +        name = name.substring(6); +        slength = name.substring(0, 2); +        ilength = Integer.parseInt(slength, 16) * 2; +        name = name.substring(2, ilength + 2); +        name = (new String(Hex.decode(name))).replace('<', ' '); +        return (name); +    } + +    /** +     * Reduces the raw data from the card by four characters +     * +     * @param output the raw data from the card +     * @return the data field of that data +     */ +    private String getDataField(String output) { +        return output.substring(0, output.length() - 4); +    } + +    /** +     * Communicates with the OpenPgpCard via the APDU +     * +     * @param hex the hexadecimal APDU +     * @return The answer from the card +     * @throws java.io.IOException throws an exception if something goes wrong +     */ +    public String card(String hex) throws IOException { +        return getHex(mIsoDep.transceive(Hex.decode(hex))); +    } + +    /** +     * Converts a byte array into an hex string +     * +     * @param raw the byte array representation +     * @return the  hexadecimal string representation +     */ +    public static String getHex(byte[] raw) { +        return new String(Hex.encode(raw)); +    } +}  | 
