diff options
| author | hsm <hsm@lamia.panaceas.james.local> | 2016-05-29 03:12:09 +0100 | 
|---|---|---|
| committer | hsm <hsm@lamia.panaceas.james.local> | 2016-05-29 04:47:15 +0100 | 
| commit | 5aa36b089f09346787e44f9850924faae1fda363 (patch) | |
| tree | 0f14297dedecb42af771743241a0ed7ee78f308c /OpenKeychain/src | |
| parent | 24b92172be818c7ee67a235ad8403f01a4495987 (diff) | |
| download | open-keychain-master.tar.gz open-keychain-master.tar.bz2 open-keychain-master.zip | |
Diffstat (limited to 'OpenKeychain/src')
8 files changed, 153 insertions, 24 deletions
| diff --git a/OpenKeychain/src/main/AndroidManifest.xml b/OpenKeychain/src/main/AndroidManifest.xml index 0ab1e6aff..ac74a66f6 100644 --- a/OpenKeychain/src/main/AndroidManifest.xml +++ b/OpenKeychain/src/main/AndroidManifest.xml @@ -88,6 +88,7 @@      <!-- android:allowBackup="false": Don't allow backup over adb backup or other apps! -->      <application +	android:debuggable="true"          android:name=".KeychainApplication"          android:allowBackup="false"          android:fullBackupContent="false" diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpSignEncryptData.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpSignEncryptData.java index c4e569d24..5d904331e 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpSignEncryptData.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpSignEncryptData.java @@ -30,6 +30,7 @@ public class PgpSignEncryptData implements Parcelable {      protected String mVersionHeader = null;      protected boolean mEnableAsciiArmorOutput = false; +    protected boolean mSshAuth = false;      protected int mCompressionAlgorithm = CompressionAlgorithmTags.UNCOMPRESSED;      protected long[] mEncryptionMasterKeyIds = null;      protected Passphrase mSymmetricPassphrase = null; @@ -53,6 +54,7 @@ public class PgpSignEncryptData implements Parcelable {          mVersionHeader = source.readString();          mEnableAsciiArmorOutput = source.readInt() == 1; +        mSshAuth = source.readInt() == 1;          mCompressionAlgorithm = source.readInt();          mEncryptionMasterKeyIds = source.createLongArray();          mSymmetricPassphrase = source.readParcelable(loader); @@ -78,6 +80,7 @@ public class PgpSignEncryptData implements Parcelable {      public void writeToParcel(Parcel dest, int flags) {          dest.writeString(mVersionHeader);          dest.writeInt(mEnableAsciiArmorOutput ? 1 : 0); +        dest.writeInt(mSshAuth ? 1 : 0);          dest.writeInt(mCompressionAlgorithm);          dest.writeLongArray(mEncryptionMasterKeyIds);          dest.writeParcelable(mSymmetricPassphrase, 0); @@ -183,6 +186,10 @@ public class PgpSignEncryptData implements Parcelable {          return mEnableAsciiArmorOutput;      } +    public boolean isSshAuth() { +        return mSshAuth; +    } +      public String getVersionHeader() {          return mVersionHeader;      } @@ -197,6 +204,11 @@ public class PgpSignEncryptData implements Parcelable {          return this;      } +    public PgpSignEncryptData setSshAuth(boolean sshAuth) { +        mSshAuth = sshAuth; +        return this; +    } +      public PgpSignEncryptData setCleartextSignature(boolean cleartextSignature) {          this.mCleartextSignature = cleartextSignature;          return this; 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 7a1d99927..a855dcac8 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpSignEncryptOperation.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpSignEncryptOperation.java @@ -24,6 +24,7 @@ import android.net.Uri;  import android.os.Parcelable;  import android.support.annotation.NonNull; +import org.bouncycastle.bcpg.HashAlgorithmTags;  import org.bouncycastle.bcpg.ArmoredOutputStream;  import org.bouncycastle.bcpg.BCPGOutputStream;  import org.bouncycastle.bcpg.CompressionAlgorithmTags; @@ -58,6 +59,10 @@ import org.sufficientlysecure.keychain.util.Log;  import org.sufficientlysecure.keychain.util.Passphrase;  import org.sufficientlysecure.keychain.util.ProgressScaler; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; + +import java.nio.ByteBuffer;  import java.io.BufferedInputStream;  import java.io.BufferedOutputStream;  import java.io.BufferedReader; @@ -165,6 +170,7 @@ public class PgpSignEncryptOperation extends BaseOperation<PgpSignEncryptInputPa          return executeInternal(input, cryptoInput, inputData, outputStream);      } +      /**       * Signs and/or encrypts data based on parameters of class       */ @@ -238,7 +244,10 @@ public class PgpSignEncryptOperation extends BaseOperation<PgpSignEncryptInputPa                  }                  // Make sure we are allowed to sign here! -                if (!signingKey.canSign()) { +                if ((!signingKey.canSign() && !data.isSshAuth()) || +                        (!signingKey.canAuthenticate() && data.isSshAuth())) { +                    Log.w(Constants.TAG, "canSign " + signingKey.canSign() + " canAuthenticate " +                        + signingKey.canAuthenticate() + " isSshAuth  "+ data.isSshAuth());                      log.add(LogType.MSG_PSE_ERROR_KEY_SIGN, indent);                      return new PgpSignEncryptResult(PgpSignEncryptResult.RESULT_ERROR, log);                  } @@ -298,7 +307,11 @@ public class PgpSignEncryptOperation extends BaseOperation<PgpSignEncryptInputPa              // Use requested hash algo              int requestedAlgorithm = data.getSignatureHashAlgorithm();              if (requestedAlgorithm == PgpSecurityConstants.OpenKeychainHashAlgorithmTags.USE_DEFAULT) { -                data.setSignatureHashAlgorithm(PgpSecurityConstants.DEFAULT_HASH_ALGORITHM); +                if (data.isSshAuth()) { +                    data.setSignatureHashAlgorithm(HashAlgorithmTags.SHA1); +                } else { +                    data.setSignatureHashAlgorithm(PgpSecurityConstants.DEFAULT_HASH_ALGORITHM); +                }              }          }          updateProgress(R.string.progress_preparing_streams, 2, 100); @@ -386,6 +399,7 @@ public class PgpSignEncryptOperation extends BaseOperation<PgpSignEncryptInputPa          ByteArrayOutputStream detachedByteOut = null;          ArmoredOutputStream detachedArmorOut = null;          BCPGOutputStream detachedBcpgOut = null; +        MessageDigest raw_digest = null;          long opTime, startTime = System.currentTimeMillis(); @@ -491,6 +505,28 @@ public class PgpSignEncryptOperation extends BaseOperation<PgpSignEncryptInputPa              } else if (enableSignature && data.isDetachedSignature()) {                  /* detached signature */ +                // JMM - GROSS HACK +                // +                // The PGP stack doesn't do signatures of arbitary data: it wants +                // to use its own structure for the message, so we have +                // to be creative. We let the pgp stack do its stuff, but when +                // it calls out to the NFC stack, we catch it and swap the +                // message digest for own own. +                // +                // this means we have to check cyrptoInput for the correct +                // hash ourselves on the 2nd trip through when the +                // client resents the request. +                // + +                if (data.isSshAuth()) { +                    try { +                        raw_digest = MessageDigest.getInstance("SHA-1"); +                    } catch (NoSuchAlgorithmException e) { +                        log.add(LogType.MSG_PSE_ERROR_IO, indent); +                        return new PgpSignEncryptResult(PgpSignEncryptResult.RESULT_ERROR, log); +                    } +                } +                  updateProgress(R.string.progress_signing, 8, 100);                  log.add(LogType.MSG_PSE_SIGNING_DETACHED, indent); @@ -515,6 +551,9 @@ public class PgpSignEncryptOperation extends BaseOperation<PgpSignEncryptInputPa                  while ((length = in.read(buffer)) > 0) {                      // no output stream is written, no changed to original data! +                    if (raw_digest != null) +                        raw_digest.update(buffer, 0, length); +                      signatureGenerator.update(buffer, 0, length);                      alreadyWritten += length; @@ -582,11 +621,37 @@ public class PgpSignEncryptOperation extends BaseOperation<PgpSignEncryptInputPa                          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_PSE_PENDING_NFC, indent); -                    return new PgpSignEncryptResult(log, RequiredInputParcel.createSecurityTokenSignOperation( -                            signingKey.getRing().getMasterKeyId(), signingKey.getKeyId(), -                            e.hashToSign, e.hashAlgo, cryptoInput.getSignatureTime()), cryptoInput); + +                    // JMM - 2nd part of gross hack +                    // first swap the message digest for the raw one +                    // before this hits the NFC stack + +                    if (raw_digest != null) +                        e.hashToSign = raw_digest.digest(); + +                    // Test to see if we already have the answer for the hash +                    // as the test in encode() above was checking the wrong +                    // hash. + +                    byte [] answer = cryptoInput.getCryptoData().get(ByteBuffer.wrap(e.hashToSign)); + +                    if (raw_digest == null || answer == null)  { +                        // We haven't done the hash yet, so kick it off to the NFC stack + +                        // this secret key diverts to a OpenPGP card, throw exception with hash that will be signed + +                        log.add(LogType.MSG_PSE_PENDING_NFC, indent); +                        return new PgpSignEncryptResult(log, RequiredInputParcel.createSecurityTokenSignOperation( +                                signingKey.getRing().getMasterKeyId(), signingKey.getKeyId(), +                                e.hashToSign, e.hashAlgo, cryptoInput.getSignatureTime()), cryptoInput); +                    } else if (raw_digest != null) { +                        // We've aready done the work, replace the output stream +                        // from the PGP signature with just the signature as we want just +                        // a raw signature + +                        detachedByteOut = new ByteArrayOutputStream(); +                        detachedByteOut.write(answer, 0, answer.length); +                    }                  }              } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/CachedPublicKeyRing.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/CachedPublicKeyRing.java index 604a5a027..58c3a154e 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/CachedPublicKeyRing.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/CachedPublicKeyRing.java @@ -167,6 +167,23 @@ public class CachedPublicKeyRing extends KeyRing {          }      } +    /** Returns the key id which should be used for auth. +     * +     * This method returns keys which are actually available (ie. secret available, and not stripped, +     * revoked, or expired), hence only works on keyrings where a secret key is available! +     * +     */ +    public long getSecretAuthId() throws PgpKeyNotFoundException { +        try { +            Object data = mProviderHelper.getGenericData(mUri, +                    KeyRings.HAS_AUTHENTICATE, +                    ProviderHelper.FIELD_TYPE_INTEGER); +            return (Long) data; +        } catch(ProviderHelper.NotFoundException e) { +            throw new PgpKeyNotFoundException(e); +        } +    } +      @Override      public int getVerified() throws PgpKeyNotFoundException {          try { diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java index 8a5d09d7b..8f3e4c247 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java @@ -336,6 +336,8 @@ public class KeychainProvider extends ContentProvider {                          "kE." + Keys.KEY_ID + " AS " + KeyRings.HAS_ENCRYPT);                  projectionMap.put(KeyRings.HAS_SIGN,                          "kS." + Keys.KEY_ID + " AS " + KeyRings.HAS_SIGN); +                projectionMap.put(KeyRings.HAS_AUTHENTICATE, +                        "kA." + Keys.KEY_ID + " AS " + KeyRings.HAS_AUTHENTICATE);                  projectionMap.put(KeyRings.HAS_CERTIFY,                          "kC." + Keys.KEY_ID + " AS " + KeyRings.HAS_CERTIFY);                  projectionMap.put(KeyRings.IS_EXPIRED, 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 c85774ead..c5cbcbb71 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/OpenPgpService.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/OpenPgpService.java @@ -199,7 +199,8 @@ public class OpenPgpService extends Service {      }      private Intent signImpl(Intent data, InputStream inputStream, -                            OutputStream outputStream, boolean cleartextSign) { +                            OutputStream outputStream, boolean cleartextSign, +                            boolean sshAuth) {          try {              boolean asciiArmor = cleartextSign || data.getBooleanExtra(OpenPgpApi.EXTRA_REQUEST_ASCII_ARMOR, true); @@ -209,6 +210,7 @@ public class OpenPgpService extends Service {                      .setCleartextSignature(cleartextSign)                      .setDetachedSignature(!cleartextSign)                      .setVersionHeader(null) +                    .setSshAuth(sshAuth)                      .setSignatureHashAlgorithm(PgpSecurityConstants.OpenKeychainHashAlgorithmTags.USE_DEFAULT); @@ -227,9 +229,15 @@ public class OpenPgpService extends Service {                  // get first usable subkey capable of signing                  try { -                    long signSubKeyId = mProviderHelper.getCachedPublicKeyRing( +                    long subKeyId; +                    if (sshAuth) { +                         subKeyId = mProviderHelper.getCachedPublicKeyRing( +                            pgpData.getSignatureMasterKeyId()).getSecretAuthId(); +                    } else { +                         subKeyId = mProviderHelper.getCachedPublicKeyRing(                              pgpData.getSignatureMasterKeyId()).getSecretSignId(); -                    pgpData.setSignatureSubKeyId(signSubKeyId); +                    } +                    pgpData.setSignatureSubKeyId(subKeyId);                  } catch (PgpKeyNotFoundException e) {                      throw new Exception("signing subkey not found!", e);                  } @@ -941,15 +949,18 @@ public class OpenPgpService extends Service {                  return checkPermissionImpl(data);              }              case OpenPgpApi.ACTION_CLEARTEXT_SIGN: { -                return signImpl(data, inputStream, outputStream, true); +                return signImpl(data, inputStream, outputStream, true, false);              }              case OpenPgpApi.ACTION_SIGN: {                  // 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, inputStream, outputStream, true); +                return signImpl(data, inputStream, outputStream, true, false);              }              case OpenPgpApi.ACTION_DETACHED_SIGN: { -                return signImpl(data, inputStream, outputStream, false); +                return signImpl(data, inputStream, outputStream, false, false); +            } +            case OpenPgpApi.ACTION_SSH_AUTH: { +                return signImpl(data, inputStream, outputStream, false, true);              }              case OpenPgpApi.ACTION_ENCRYPT: {                  return encryptAndSignImpl(data, inputStream, outputStream, false); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/SecurityTokenHelper.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/SecurityTokenHelper.java index bc1c42f7f..deee6366f 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/SecurityTokenHelper.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/SecurityTokenHelper.java @@ -513,9 +513,11 @@ public class SecurityTokenHelper {       * @param hash the hash for signing       * @return a big integer representing the MPI for the given hash       */ -    public byte[] calculateSignature(byte[] hash, int hashAlgo) throws IOException { +    public byte[] calculateSignature(byte[] hash, int hashAlgo, boolean useAuthKey) throws IOException {          if (!mPw1ValidatedForSignature) { -            verifyPin(0x81); // (Verify PW1 with mode 81 for signing) +            // (Verify PW1 with mode 81 for signing) +            // (Verify PW1 with mode 82 for auth) +            verifyPin(useAuthKey ? 0x82 : 0x81);           }          // dsi, including Lc @@ -525,7 +527,7 @@ public class SecurityTokenHelper {          switch (hashAlgo) {              case HashAlgorithmTags.SHA1:                  if (hash.length != 20) { -                    throw new IOException("Bad hash length (" + hash.length + ", expected 10!"); +                    throw new IOException("Bad hash length (" + hash.length + ", expected 20!");                  }                  dsi = "23" // Lc                          + "3021" // Tag/Length of Sequence, the 0x21 includes all following 33 bytes @@ -568,11 +570,24 @@ public class SecurityTokenHelper {                  throw new IOException("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 apdu; + +        if (!useAuthKey) { +            // Command APDU for PERFORM SECURITY OPERATION: COMPUTE DIGITAL SIGNATURE (page 37) +            apdu = +                    "002A9E9A" // CLA, INS, P1, P2 +                            + dsi // digital signature input +                            + "00"; // Le + +        } else { +            // Command APDU for INTERNAL AUTHENTICATE (page 55) +            // This command doesn't take / +            apdu = +                    "00880000" // CLA, INS, P1, P2 +                            + dsi  // digital signature input, card does PKCS#1 +                            + "00"; // Le + +        }          String response = communicate(apdu); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SecurityTokenOperationActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SecurityTokenOperationActivity.java index 4d07025e6..4e7c06b69 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SecurityTokenOperationActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SecurityTokenOperationActivity.java @@ -200,10 +200,16 @@ public class SecurityTokenOperationActivity extends BaseSecurityTokenActivity {                  break;              }              case SECURITY_TOKEN_SIGN: { -                long tokenKeyId = KeyFormattingUtils.getKeyIdFromFingerprint( +                long tokenSignKeyId = KeyFormattingUtils.getKeyIdFromFingerprint(                          mSecurityTokenHelper.getKeyFingerprint(KeyType.SIGN)); -                if (tokenKeyId != mRequiredInput.getSubKeyId()) { +                long tokenAuthKeyId = KeyFormattingUtils.getKeyIdFromFingerprint( +                        mSecurityTokenHelper.getKeyFingerprint(KeyType.AUTH)); + +                long requiredKey = mRequiredInput.getSubKeyId(); + + +                if ((tokenSignKeyId != requiredKey) && (tokenAuthKeyId != requiredKey)) {                      throw new IOException(getString(R.string.error_wrong_security_token));                  } @@ -212,7 +218,7 @@ public class SecurityTokenOperationActivity extends BaseSecurityTokenActivity {                  for (int i = 0; i < mRequiredInput.mInputData.length; i++) {                      byte[] hash = mRequiredInput.mInputData[i];                      int algo = mRequiredInput.mSignAlgos[i]; -                    byte[] signedHash = mSecurityTokenHelper.calculateSignature(hash, algo); +                    byte[] signedHash = mSecurityTokenHelper.calculateSignature(hash, algo, requiredKey == tokenAuthKeyId);                      mInputParcel.addCryptoData(hash, signedHash);                  }                  break; | 
