diff options
| author | Vincent Breitmoser <valodim@mugenguild.com> | 2014-08-24 05:12:09 +0200 | 
|---|---|---|
| committer | Vincent Breitmoser <valodim@mugenguild.com> | 2014-08-24 16:08:37 +0200 | 
| commit | 783dae8804fee1f1594cc910d3570fcf6178015c (patch) | |
| tree | aa35d500f29badb8cc616d38c1b7df36f00922b3 /OpenKeychain/src/main/java | |
| parent | 08bdb0c5b983b6d0096187873275598a1ae4c9c3 (diff) | |
| download | open-keychain-783dae8804fee1f1594cc910d3570fcf6178015c.tar.gz open-keychain-783dae8804fee1f1594cc910d3570fcf6178015c.tar.bz2 open-keychain-783dae8804fee1f1594cc910d3570fcf6178015c.zip | |
add ecc support
Diffstat (limited to 'OpenKeychain/src/main/java')
20 files changed, 454 insertions, 149 deletions
| diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/HkpKeyserver.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/HkpKeyserver.java index 69ac2551f..e30401180 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/HkpKeyserver.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/HkpKeyserver.java @@ -283,7 +283,7 @@ public class HkpKeyserver extends Keyserver {              entry.setBitStrength(Integer.parseInt(matcher.group(3)));              final int algorithmId = Integer.decode(matcher.group(2)); -            entry.setAlgorithm(PgpKeyHelper.getAlgorithmInfo(algorithmId)); +            entry.setAlgorithm(PgpKeyHelper.getAlgorithmInfo(algorithmId, null, null));              // group 1 contains the full fingerprint (v4) or the long key id if available              // see http://bit.ly/1d4bxbk and http://bit.ly/1gD1wwr diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/ImportKeysListEntry.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/ImportKeysListEntry.java index 30e93f957..da70f1505 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/ImportKeysListEntry.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/ImportKeysListEntry.java @@ -39,7 +39,8 @@ public class ImportKeysListEntry implements Serializable, Parcelable {      private boolean mExpired;      private Date mDate; // TODO: not displayed      private String mFingerprintHex; -    private int mBitStrength; +    private Integer mBitStrength; +    private String mCurveOid;      private String mAlgorithm;      private boolean mSecretKey;      private String mPrimaryUserId; @@ -162,10 +163,14 @@ public class ImportKeysListEntry implements Serializable, Parcelable {          this.mFingerprintHex = fingerprintHex;      } -    public int getBitStrength() { +    public Integer getBitStrength() {          return mBitStrength;      } +    public String getCurveOid() { +        return mCurveOid; +    } +      public void setBitStrength(int bitStrength) {          this.mBitStrength = bitStrength;      } @@ -258,13 +263,15 @@ public class ImportKeysListEntry implements Serializable, Parcelable {              mPrimaryUserId = mUserIds.get(0);          } -        this.mKeyId = key.getKeyId(); -        this.mKeyIdHex = PgpKeyHelper.convertKeyIdToHex(mKeyId); +        mKeyId = key.getKeyId(); +        mKeyIdHex = PgpKeyHelper.convertKeyIdToHex(mKeyId); -        this.mRevoked = key.isRevoked(); -        this.mFingerprintHex = PgpKeyHelper.convertFingerprintToHex(key.getFingerprint()); -        this.mBitStrength = key.getBitStrength(); +        mRevoked = key.isRevoked(); +        mFingerprintHex = PgpKeyHelper.convertFingerprintToHex(key.getFingerprint()); +        mBitStrength = key.getBitStrength(); +        mCurveOid = key.getCurveOid();          final int algorithm = key.getAlgorithm(); -        this.mAlgorithm = PgpKeyHelper.getAlgorithmInfo(context, algorithm); +        mAlgorithm = PgpKeyHelper.getAlgorithmInfo(context, algorithm, mBitStrength, mCurveOid);      } +  } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/KeybaseKeyserver.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/KeybaseKeyserver.java index 0ca6f07fd..cbd06da90 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/KeybaseKeyserver.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/KeybaseKeyserver.java @@ -75,7 +75,7 @@ public class KeybaseKeyserver extends Keyserver {          entry.setExtraData(username);          final int algorithmId = match.getAlgorithmId(); -        entry.setAlgorithm(PgpKeyHelper.getAlgorithmInfo(algorithmId)); +        entry.setAlgorithm(PgpKeyHelper.getAlgorithmInfo(algorithmId, null, null));          final int bitStrength = match.getBitStrength();          entry.setBitStrength(bitStrength); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyHelper.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyHelper.java index 0c640538f..22956c8af 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyHelper.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyHelper.java @@ -24,10 +24,16 @@ import android.text.Spannable;  import android.text.SpannableStringBuilder;  import android.text.style.ForegroundColorSpan; +import org.spongycastle.asn1.ASN1ObjectIdentifier; +import org.spongycastle.asn1.nist.NISTNamedCurves; +import org.spongycastle.asn1.teletrust.TeleTrusTNamedCurves; +import org.spongycastle.bcpg.ECPublicBCPGKey;  import org.spongycastle.bcpg.PublicKeyAlgorithmTags;  import org.spongycastle.util.encoders.Hex;  import org.sufficientlysecure.keychain.Constants;  import org.sufficientlysecure.keychain.R; +import org.sufficientlysecure.keychain.service.SaveKeyringParcel.Algorithm; +import org.sufficientlysecure.keychain.service.SaveKeyringParcel.Curve;  import org.sufficientlysecure.keychain.util.Log;  import java.security.DigestException; @@ -37,18 +43,14 @@ import java.util.Locale;  public class PgpKeyHelper { -    public static String getAlgorithmInfo(int algorithm) { -        return getAlgorithmInfo(null, algorithm, 0); -    } - -    public static String getAlgorithmInfo(Context context, int algorithm) { -        return getAlgorithmInfo(context, algorithm, 0); +    public static String getAlgorithmInfo(int algorithm, Integer keySize, String oid) { +        return getAlgorithmInfo(null, algorithm, keySize, oid);      }      /**       * Based on <a href="http://tools.ietf.org/html/rfc2440#section-9.1">OpenPGP Message Format</a>       */ -    public static String getAlgorithmInfo(Context context, int algorithm, int keySize) { +    public static String getAlgorithmInfo(Context context, int algorithm, Integer keySize, String oid) {          String algorithmStr;          switch (algorithm) { @@ -69,10 +71,19 @@ public class PgpKeyHelper {                  break;              } -            case PublicKeyAlgorithmTags.ECDSA: +            case PublicKeyAlgorithmTags.ECDSA: { +                if (oid == null) { +                    return "ECDSA"; +                } +                String oidName = PgpKeyHelper.getCurveInfo(context, oid); +                return "ECDSA (" + oidName + ")"; +            }              case PublicKeyAlgorithmTags.ECDH: { -                algorithmStr = "ECC"; -                break; +                if (oid == null) { +                    return "ECDH"; +                } +                String oidName = PgpKeyHelper.getCurveInfo(context, oid); +                return "ECDH (" + oidName + ")";              }              default: { @@ -90,6 +101,106 @@ public class PgpKeyHelper {              return algorithmStr;      } +    public static String getAlgorithmInfo(Algorithm algorithm, Integer keySize, Curve curve) { +        return getAlgorithmInfo(null, algorithm, keySize, curve); +    } + +    /** +     * Based on <a href="http://tools.ietf.org/html/rfc2440#section-9.1">OpenPGP Message Format</a> +     */ +    public static String getAlgorithmInfo(Context context, Algorithm algorithm, Integer keySize, Curve curve) { +        String algorithmStr; + +        switch (algorithm) { +            case RSA: { +                algorithmStr = "RSA"; +                break; +            } +            case DSA: { +                algorithmStr = "DSA"; +                break; +            } + +            case ELGAMAL: { +                algorithmStr = "ElGamal"; +                break; +            } + +            case ECDSA: { +                algorithmStr = "ECDSA"; +                if (curve != null) { +                    algorithmStr += " (" + getCurveInfo(context, curve) + ")"; +                } +                return algorithmStr; +            } +            case ECDH: { +                algorithmStr = "ECDH"; +                if (curve != null) { +                    algorithmStr += " (" + getCurveInfo(context, curve) + ")"; +                } +                return algorithmStr; +            } + +            default: { +                if (context != null) { +                    algorithmStr = context.getResources().getString(R.string.unknown_algorithm); +                } else { +                    algorithmStr = "unknown"; +                } +                break; +            } +        } +        if (keySize != null && keySize > 0) +            return algorithmStr + ", " + keySize + " bit"; +        else +            return algorithmStr; +    } + +    // Return name of a curve. These are names, no need for translation +    public static String getCurveInfo(Context context, Curve curve) { +        switch(curve) { +            case NIST_P256: +                return "NIST P-256"; +            case NIST_P384: +                return "NIST P-384"; +            case NIST_P521: +                return "NIST P-521"; + +            /* see SaveKeyringParcel +            case BRAINPOOL_P256: +                return "Brainpool P-256"; +            case BRAINPOOL_P384: +                return "Brainpool P-384"; +            case BRAINPOOL_P512: +                return "Brainpool P-512"; +            */ +        } +        if (context != null) { +            return context.getResources().getString(R.string.unknown_algorithm); +        } else { +            return "unknown"; +        } +    } + +    public static String getCurveInfo(Context context, String oidStr) { +        ASN1ObjectIdentifier oid = new ASN1ObjectIdentifier(oidStr); + +        String name; +        name = NISTNamedCurves.getName(oid); +        if (name != null) { +            return name; +        } +        name = TeleTrusTNamedCurves.getName(oid); +        if (name != null) { +            return name; +        } +        if (context != null) { +            return context.getResources().getString(R.string.unknown_algorithm); +        } else { +            return "unknown"; +        } +    } +      /**       * Converts fingerprint to hex (optional: with whitespaces after 4 characters)       * <p/> diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java index 175045470..967a7caa9 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java @@ -20,12 +20,12 @@ package org.sufficientlysecure.keychain.pgp;  import org.spongycastle.bcpg.CompressionAlgorithmTags;  import org.spongycastle.bcpg.HashAlgorithmTags; -import org.spongycastle.bcpg.PublicKeyAlgorithmTags;  import org.spongycastle.bcpg.SymmetricKeyAlgorithmTags;  import org.spongycastle.bcpg.sig.Features;  import org.spongycastle.bcpg.sig.KeyFlags;  import org.spongycastle.jce.spec.ElGamalParameterSpec;  import org.spongycastle.openpgp.PGPException; +import org.spongycastle.openpgp.PGPKeyFlags;  import org.spongycastle.openpgp.PGPKeyPair;  import org.spongycastle.openpgp.PGPPrivateKey;  import org.spongycastle.openpgp.PGPPublicKey; @@ -52,6 +52,8 @@ import org.sufficientlysecure.keychain.service.OperationResultParcel.LogType;  import org.sufficientlysecure.keychain.service.OperationResultParcel.OperationLog;  import org.sufficientlysecure.keychain.service.OperationResults.EditKeyResult;  import org.sufficientlysecure.keychain.service.SaveKeyringParcel; +import org.sufficientlysecure.keychain.service.SaveKeyringParcel.Algorithm; +import org.sufficientlysecure.keychain.service.SaveKeyringParcel.Curve;  import org.sufficientlysecure.keychain.service.SaveKeyringParcel.SubkeyAdd;  import org.sufficientlysecure.keychain.util.IterableIterator;  import org.sufficientlysecure.keychain.util.Log; @@ -66,6 +68,7 @@ import java.security.NoSuchAlgorithmException;  import java.security.NoSuchProviderException;  import java.security.SecureRandom;  import java.security.SignatureException; +import java.security.spec.ECGenParameterSpec;  import java.util.Arrays;  import java.util.Date;  import java.util.Iterator; @@ -155,31 +158,65 @@ public class PgpKeyOperation {          mProgress.peek().setProgress(message, current, 100);      } +    private ECGenParameterSpec getEccParameterSpec(Curve curve) { +        switch (curve) { +            case NIST_P256: return new ECGenParameterSpec("P-256"); +            case NIST_P384: return new ECGenParameterSpec("P-384"); +            case NIST_P521: return new ECGenParameterSpec("P-521"); + +            // @see SaveKeyringParcel +            // case BRAINPOOL_P256: return new ECGenParameterSpec("brainpoolp256r1"); +            // case BRAINPOOL_P384: return new ECGenParameterSpec("brainpoolp384r1"); +            // case BRAINPOOL_P512: return new ECGenParameterSpec("brainpoolp512r1"); +        } +        throw new RuntimeException("Invalid choice! (can't happen)"); +    } +      /** Creates new secret key. */ -    private PGPKeyPair createKey(int algorithmChoice, int keySize, OperationLog log, int indent) { +    private PGPKeyPair createKey(SubkeyAdd add, OperationLog log, int indent) {          try { -            if (keySize < 512) { -                log.add(LogLevel.ERROR, LogType.MSG_CR_ERROR_KEYSIZE_512, indent); -                return null; +            // Some safety checks +            if (add.mAlgorithm == Algorithm.ECDH || add.mAlgorithm == Algorithm.ECDSA) { +                if (add.mCurve == null) { +                    log.add(LogLevel.ERROR, LogType.MSG_CR_ERROR_NO_CURVE, indent); +                    return null; +                } +            } else { +                if (add.mKeySize == null) { +                    log.add(LogLevel.ERROR, LogType.MSG_CR_ERROR_NO_KEYSIZE, indent); +                    return null; +                } +                if (add.mKeySize < 512) { +                    log.add(LogLevel.ERROR, LogType.MSG_CR_ERROR_KEYSIZE_512, indent); +                    return null; +                }              }              int algorithm;              KeyPairGenerator keyGen; -            switch (algorithmChoice) { -                case PublicKeyAlgorithmTags.DSA: { +            switch (add.mAlgorithm) { +                case DSA: { +                    if ((add.mFlags & (PGPKeyFlags.CAN_ENCRYPT_COMMS | PGPKeyFlags.CAN_ENCRYPT_STORAGE)) > 0) { +                        log.add(LogLevel.ERROR, LogType.MSG_CR_ERROR_FLAGS_DSA, indent); +                        return null; +                    }                      progress(R.string.progress_generating_dsa, 30);                      keyGen = KeyPairGenerator.getInstance("DSA", Constants.BOUNCY_CASTLE_PROVIDER_NAME); -                    keyGen.initialize(keySize, new SecureRandom()); +                    keyGen.initialize(add.mKeySize, new SecureRandom());                      algorithm = PGPPublicKey.DSA;                      break;                  } -                case PublicKeyAlgorithmTags.ELGAMAL_ENCRYPT: { +                case ELGAMAL: { +                    if ((add.mFlags & (PGPKeyFlags.CAN_SIGN | PGPKeyFlags.CAN_CERTIFY)) > 0) { +                        log.add(LogLevel.ERROR, LogType.MSG_CR_ERROR_FLAGS_ELGAMAL, indent); +                        return null; +                    }                      progress(R.string.progress_generating_elgamal, 30);                      keyGen = KeyPairGenerator.getInstance("ElGamal", Constants.BOUNCY_CASTLE_PROVIDER_NAME); -                    BigInteger p = Primes.getBestPrime(keySize); +                    BigInteger p = Primes.getBestPrime(add.mKeySize);                      BigInteger g = new BigInteger("2");                      ElGamalParameterSpec elParams = new ElGamalParameterSpec(p, g); @@ -189,15 +226,44 @@ public class PgpKeyOperation {                      break;                  } -                case PublicKeyAlgorithmTags.RSA_GENERAL: { +                case RSA: {                      progress(R.string.progress_generating_rsa, 30);                      keyGen = KeyPairGenerator.getInstance("RSA", Constants.BOUNCY_CASTLE_PROVIDER_NAME); -                    keyGen.initialize(keySize, new SecureRandom()); +                    keyGen.initialize(add.mKeySize, new SecureRandom());                      algorithm = PGPPublicKey.RSA_GENERAL;                      break;                  } +                case ECDSA: { +                    if ((add.mFlags & (PGPKeyFlags.CAN_ENCRYPT_COMMS | PGPKeyFlags.CAN_ENCRYPT_STORAGE)) > 0) { +                        log.add(LogLevel.ERROR, LogType.MSG_CR_ERROR_FLAGS_ECDSA, indent); +                        return null; +                    } +                    progress(R.string.progress_generating_ecdsa, 30); +                    ECGenParameterSpec ecParamSpec = getEccParameterSpec(add.mCurve); +                    keyGen = KeyPairGenerator.getInstance("ECDSA", Constants.BOUNCY_CASTLE_PROVIDER_NAME); +                    keyGen.initialize(ecParamSpec, new SecureRandom()); + +                    algorithm = PGPPublicKey.ECDSA; +                    break; +                } + +                case ECDH: { +                    // make sure there are no sign or certify flags set +                    if ((add.mFlags & (PGPKeyFlags.CAN_SIGN | PGPKeyFlags.CAN_CERTIFY)) > 0) { +                        log.add(LogLevel.ERROR, LogType.MSG_CR_ERROR_FLAGS_ECDH, indent); +                        return null; +                    } +                    progress(R.string.progress_generating_ecdh, 30); +                    ECGenParameterSpec ecParamSpec = getEccParameterSpec(add.mCurve); +                    keyGen = KeyPairGenerator.getInstance("ECDH", Constants.BOUNCY_CASTLE_PROVIDER_NAME); +                    keyGen.initialize(ecParamSpec, new SecureRandom()); + +                    algorithm = PGPPublicKey.ECDH; +                    break; +                } +                  default: {                      log.add(LogLevel.ERROR, LogType.MSG_CR_ERROR_UNKNOWN_ALGO, indent);                      return null; @@ -210,7 +276,8 @@ public class PgpKeyOperation {          } catch(NoSuchProviderException e) {              throw new RuntimeException(e);          } catch(NoSuchAlgorithmException e) { -            throw new RuntimeException(e); +            log.add(LogLevel.ERROR, LogType.MSG_CR_ERROR_UNKNOWN_ALGO, indent); +            return null;          } catch(InvalidAlgorithmParameterException e) {              throw new RuntimeException(e);          } catch(PGPException e) { @@ -252,13 +319,8 @@ public class PgpKeyOperation {                  return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null);              } -            if (add.mAlgorithm == PublicKeyAlgorithmTags.ELGAMAL_ENCRYPT) { -                log.add(LogLevel.ERROR, LogType.MSG_CR_ERROR_MASTER_ELGAMAL, indent); -                return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null); -            } -              subProgressPush(10, 30); -            PGPKeyPair keyPair = createKey(add.mAlgorithm, add.mKeysize, log, indent); +            PGPKeyPair keyPair = createKey(add, log, indent);              subProgressPop();              // return null if this failed (an error will already have been logged by createKey) @@ -690,8 +752,8 @@ public class PgpKeyOperation {                  progress(R.string.progress_modify_subkeyadd, (i-1) * (100 / saveParcel.mAddSubKeys.size()));                  SaveKeyringParcel.SubkeyAdd add = saveParcel.mAddSubKeys.get(i); -                log.add(LogLevel.INFO, LogType.MSG_MF_SUBKEY_NEW, indent, Integer.toString(add.mKeysize), -                        PgpKeyHelper.getAlgorithmInfo(add.mAlgorithm) ); +                log.add(LogLevel.INFO, LogType.MSG_MF_SUBKEY_NEW, indent, +                        PgpKeyHelper.getAlgorithmInfo(add.mAlgorithm, add.mKeySize, add.mCurve) );                  if (add.mExpiry == null) {                      log.add(LogLevel.ERROR, LogType.MSG_MF_ERROR_NULL_EXPIRY, indent +1); @@ -708,7 +770,7 @@ public class PgpKeyOperation {                      (i-1) * (100 / saveParcel.mAddSubKeys.size()),                      i * (100 / saveParcel.mAddSubKeys.size())                  ); -                PGPKeyPair keyPair = createKey(add.mAlgorithm, add.mKeysize, log, indent); +                PGPKeyPair keyPair = createKey(add, log, indent);                  subProgressPop();                  if (keyPair == null) {                      log.add(LogLevel.ERROR, LogType.MSG_MF_ERROR_PGP, indent +1); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java index caacb948e..9effe4e67 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java @@ -37,7 +37,6 @@ import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;  import org.sufficientlysecure.keychain.service.OperationResultParcel.LogLevel;  import org.sufficientlysecure.keychain.service.OperationResultParcel.LogType;  import org.sufficientlysecure.keychain.service.OperationResultParcel.OperationLog; -import org.sufficientlysecure.keychain.service.OperationResults;  import org.sufficientlysecure.keychain.util.IterableIterator;  import org.sufficientlysecure.keychain.util.Log; @@ -58,7 +57,8 @@ import java.util.TreeSet;   * This class and its relatives UncachedPublicKey and UncachedSecretKey are   * used to move around pgp key rings in non crypto related (UI, mostly) code.   * It should be used for simple inspection only until it saved in the database, - * all actual crypto operations should work with WrappedKeyRings exclusively. + * all actual crypto operations should work with CanonicalizedKeyRings + * exclusively.   *   * This class is also special in that it can hold either the PGPPublicKeyRing   * or PGPSecretKeyRing derivate of the PGPKeyRing class, since these are @@ -591,7 +591,7 @@ public class UncachedKeyRing {                      } -                    // if we already have a cert, and this one is not newer: skip it +                    // if we already have a cert, and this one is older: skip it                      if (selfCert != null && cert.getCreationTime().before(selfCert.getCreationTime())) {                          log.add(LogLevel.DEBUG, LogType.MSG_KC_SUB_DUP, indent);                          redundantCerts += 1; diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedPublicKey.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedPublicKey.java index dda52d786..c7a8bb1d0 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedPublicKey.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedPublicKey.java @@ -18,6 +18,10 @@  package org.sufficientlysecure.keychain.pgp; +import org.spongycastle.asn1.ASN1ObjectIdentifier; +import org.spongycastle.asn1.nist.NISTNamedCurves; +import org.spongycastle.asn1.teletrust.TeleTrusTNamedCurves; +import org.spongycastle.bcpg.ECPublicBCPGKey;  import org.spongycastle.bcpg.sig.KeyFlags;  import org.spongycastle.openpgp.PGPPublicKey;  import org.spongycastle.openpgp.PGPSignature; @@ -94,10 +98,23 @@ public class UncachedPublicKey {          return mPublicKey.getAlgorithm();      } -    public int getBitStrength() { +    public Integer getBitStrength() { +        if (isEC()) { +            return null; +        }          return mPublicKey.getBitStrength();      } +    public String getCurveOid() { +        if ( ! isEC()) { +            return null; +        } +        if ( ! (mPublicKey.getPublicKeyPacket().getKey() instanceof ECPublicBCPGKey)) { +            return null; +        } +        return ((ECPublicBCPGKey) mPublicKey.getPublicKeyPacket().getKey()).getCurveOID().getId(); +    } +      /** Returns the primary user id, as indicated by the public key's self certificates.       *       * This is an expensive operation, since potentially a lot of certificates (and revocations) @@ -186,6 +203,10 @@ public class UncachedPublicKey {          return getAlgorithm() == PGPPublicKey.DSA;      } +    public boolean isEC() { +        return getAlgorithm() == PGPPublicKey.ECDH || getAlgorithm() == PGPPublicKey.ECDSA; +    } +      @SuppressWarnings("unchecked")      // TODO make this safe      public int getKeyUsage() { diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainContract.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainContract.java index 24d86dc4a..4f6e878b4 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainContract.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainContract.java @@ -39,6 +39,7 @@ public class KeychainContract {          String FINGERPRINT = "fingerprint";          String KEY_SIZE = "key_size"; +        String KEY_CURVE_OID = "key_curve_oid";          String CAN_SIGN = "can_sign";          String CAN_ENCRYPT = "can_encrypt";          String CAN_CERTIFY = "can_certify"; diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainDatabase.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainDatabase.java index 36e94208d..0bb43d47f 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainDatabase.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainDatabase.java @@ -52,7 +52,7 @@ import java.io.IOException;   */  public class KeychainDatabase extends SQLiteOpenHelper {      private static final String DATABASE_NAME = "openkeychain.db"; -    private static final int DATABASE_VERSION = 2; +    private static final int DATABASE_VERSION = 3;      static Boolean apgHack = false;      public interface Tables { @@ -86,6 +86,7 @@ public class KeychainDatabase extends SQLiteOpenHelper {                  + KeysColumns.KEY_ID + " INTEGER, "                  + KeysColumns.KEY_SIZE + " INTEGER, " +                + KeysColumns.KEY_CURVE_OID + " TEXT, "                  + KeysColumns.ALGORITHM + " INTEGER, "                  + KeysColumns.FINGERPRINT + " BLOB, " @@ -202,13 +203,20 @@ public class KeychainDatabase extends SQLiteOpenHelper {      @Override      public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { -        if (oldVersion == 1) { -            // add has_secret for all who are upgrading from a beta version -            try { -                db.execSQL("ALTER TABLE keys ADD COLUMN has_secret BOOLEAN"); -            } catch (Exception e) { -                // never mind, the column probably already existed -            } +        // add has_secret for all who are upgrading from a beta version +        switch (oldVersion) { +            case 1: +                try { +                    db.execSQL("ALTER TABLE keys ADD COLUMN has_secret BOOLEAN"); +                } catch(Exception e){ +                    // never mind, the column probably already existed +                } +            case 2: +                try { +                    db.execSQL("ALTER TABLE keys ADD COLUMN " + KeysColumns.KEY_CURVE_OID + " TEXT"); +                } catch(Exception e){ +                    // never mind, the column probably already existed +                }          }      } 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 b2a47cccd..f6df4a3eb 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java @@ -246,6 +246,7 @@ public class KeychainProvider extends ContentProvider {                  projectionMap.put(KeyRings.MASTER_KEY_ID, Tables.KEYS + "." + Keys.MASTER_KEY_ID);                  projectionMap.put(KeyRings.KEY_ID, Tables.KEYS + "." + Keys.KEY_ID);                  projectionMap.put(KeyRings.KEY_SIZE, Tables.KEYS + "." + Keys.KEY_SIZE); +                projectionMap.put(KeyRings.KEY_CURVE_OID, Tables.KEYS + "." + Keys.KEY_CURVE_OID);                  projectionMap.put(KeyRings.IS_REVOKED, Tables.KEYS + "." + Keys.IS_REVOKED);                  projectionMap.put(KeyRings.CAN_CERTIFY, Tables.KEYS + "." + Keys.CAN_CERTIFY);                  projectionMap.put(KeyRings.CAN_ENCRYPT, Tables.KEYS + "." + Keys.CAN_ENCRYPT); @@ -412,6 +413,7 @@ public class KeychainProvider extends ContentProvider {                  projectionMap.put(Keys.RANK, Tables.KEYS + "." + Keys.RANK);                  projectionMap.put(Keys.KEY_ID, Keys.KEY_ID);                  projectionMap.put(Keys.KEY_SIZE, Keys.KEY_SIZE); +                projectionMap.put(Keys.KEY_CURVE_OID, Keys.KEY_CURVE_OID);                  projectionMap.put(Keys.IS_REVOKED, Keys.IS_REVOKED);                  projectionMap.put(Keys.CAN_CERTIFY, Keys.CAN_CERTIFY);                  projectionMap.put(Keys.CAN_ENCRYPT, Keys.CAN_ENCRYPT); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java index 16ff2286b..bb095c340 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java @@ -62,7 +62,6 @@ import org.sufficientlysecure.keychain.util.FileImportCache;  import org.sufficientlysecure.keychain.util.IterableIterator;  import org.sufficientlysecure.keychain.util.Log;  import org.sufficientlysecure.keychain.util.ProgressFixedScaler; -import org.sufficientlysecure.keychain.util.ProgressScaler;  import java.io.ByteArrayOutputStream;  import java.io.IOException; @@ -328,6 +327,7 @@ public class ProviderHelper {                      values.put(Keys.KEY_ID, key.getKeyId());                      values.put(Keys.KEY_SIZE, key.getBitStrength()); +                    values.put(Keys.KEY_CURVE_OID, key.getCurveOid());                      values.put(Keys.ALGORITHM, key.getAlgorithm());                      values.put(Keys.FINGERPRINT, key.getFingerprint()); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java index 886f77068..b69ecef34 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java @@ -347,9 +347,14 @@ public class OperationResultParcel implements Parcelable {          MSG_CR_ERROR_NO_CERTIFY (R.string.msg_cr_error_no_certify),          MSG_CR_ERROR_NULL_EXPIRY(R.string.msg_cr_error_null_expiry),          MSG_CR_ERROR_KEYSIZE_512 (R.string.msg_cr_error_keysize_512), +        MSG_CR_ERROR_NO_KEYSIZE (R.string.msg_cr_error_no_keysize), +        MSG_CR_ERROR_NO_CURVE (R.string.msg_cr_error_no_curve),          MSG_CR_ERROR_UNKNOWN_ALGO (R.string.msg_cr_error_unknown_algo),          MSG_CR_ERROR_INTERNAL_PGP (R.string.msg_cr_error_internal_pgp), -        MSG_CR_ERROR_MASTER_ELGAMAL (R.string.msg_cr_error_master_elgamal), +        MSG_CR_ERROR_FLAGS_DSA (R.string.msg_cr_error_flags_dsa), +        MSG_CR_ERROR_FLAGS_ELGAMAL (R.string.msg_cr_error_flags_elgamal), +        MSG_CR_ERROR_FLAGS_ECDSA (R.string.msg_cr_error_flags_ecdsa), +        MSG_CR_ERROR_FLAGS_ECDH (R.string.msg_cr_error_flags_ecdh),          // secret key modify          MSG_MF (R.string.msg_mr), diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/SaveKeyringParcel.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/SaveKeyringParcel.java index 6e06c4fa9..996ce6a5a 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/SaveKeyringParcel.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/SaveKeyringParcel.java @@ -80,14 +80,16 @@ public class SaveKeyringParcel implements Parcelable {      // performance gain for using Parcelable here would probably be negligible,      // use Serializable instead.      public static class SubkeyAdd implements Serializable { -        public int mAlgorithm; -        public int mKeysize; +        public Algorithm mAlgorithm; +        public Integer mKeySize; +        public Curve mCurve;          public int mFlags;          public Long mExpiry; -        public SubkeyAdd(int algorithm, int keysize, int flags, Long expiry) { +        public SubkeyAdd(Algorithm algorithm, Integer keySize, Curve curve, int flags, Long expiry) {              mAlgorithm = algorithm; -            mKeysize = keysize; +            mKeySize = keySize; +            mCurve = curve;              mFlags = flags;              mExpiry = expiry;          } @@ -95,7 +97,8 @@ public class SaveKeyringParcel implements Parcelable {          @Override          public String toString() {              String out = "mAlgorithm: " + mAlgorithm + ", "; -            out += "mKeysize: " + mKeysize + ", "; +            out += "mKeySize: " + mKeySize + ", "; +            out += "mCurve: " + mCurve + ", ";              out += "mFlags: " + mFlags;              out += "mExpiry: " + mExpiry; @@ -214,4 +217,20 @@ public class SaveKeyringParcel implements Parcelable {          return out;      } + +    // All supported algorithms +    public enum Algorithm { +        RSA, DSA, ELGAMAL, ECDSA, ECDH +    } + +    // All curves defined in the standard +    // http://www.bouncycastle.org/wiki/pages/viewpage.action?pageId=362269 +    public enum Curve { +        NIST_P256, NIST_P384, NIST_P521, + +        // these are supported by gpg, but they are not in rfc6637 and not supported by BouncyCastle yet +        // (adding support would be trivial though -> JcaPGPKeyConverter.java:190) +        // BRAINPOOL_P256, BRAINPOOL_P384, BRAINPOOL_P512 +    } +  } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyFinalFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyFinalFragment.java index d804b9b70..69f4af04b 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyFinalFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyFinalFragment.java @@ -43,6 +43,7 @@ import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler;  import org.sufficientlysecure.keychain.service.OperationResultParcel;  import org.sufficientlysecure.keychain.service.OperationResults;  import org.sufficientlysecure.keychain.service.SaveKeyringParcel; +import org.sufficientlysecure.keychain.service.SaveKeyringParcel.Algorithm;  import org.sufficientlysecure.keychain.util.Log;  import org.sufficientlysecure.keychain.util.Notify; @@ -165,9 +166,12 @@ public class CreateKeyFinalFragment extends Fragment {          Bundle data = new Bundle();          SaveKeyringParcel parcel = new SaveKeyringParcel(); -        parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd(PublicKeyAlgorithmTags.RSA_GENERAL, 4096, KeyFlags.CERTIFY_OTHER, 0L)); -        parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd(PublicKeyAlgorithmTags.RSA_GENERAL, 4096, KeyFlags.SIGN_DATA, 0L)); -        parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd(PublicKeyAlgorithmTags.RSA_GENERAL, 4096, KeyFlags.ENCRYPT_COMMS | KeyFlags.ENCRYPT_STORAGE, 0L)); +        parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd( +                Algorithm.RSA, 4096, null, KeyFlags.CERTIFY_OTHER, 0L)); +        parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd( +                Algorithm.RSA, 4096, null, KeyFlags.SIGN_DATA, 0L)); +        parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd( +                Algorithm.RSA, 4096, null, KeyFlags.ENCRYPT_COMMS | KeyFlags.ENCRYPT_STORAGE, 0L));          String userId = KeyRing.createUserId(mName, mEmail, null);          parcel.mAddUserIds.add(userId);          parcel.mChangePrimaryUserId = userId; diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewCertActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewCertActivity.java index 48e9da87f..92f38a44c 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewCertActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewCertActivity.java @@ -167,7 +167,7 @@ public class ViewCertActivity extends ActionBarActivity                  mStatus.setTextColor(getResources().getColor(R.color.black));              } -            String algorithmStr = PgpKeyHelper.getAlgorithmInfo(this, sig.getKeyAlgorithm(), 0); +            String algorithmStr = PgpKeyHelper.getAlgorithmInfo(this, sig.getKeyAlgorithm(), null, null);              mAlgorithm.setText(algorithmStr);              mRowReason.setVisibility(View.GONE); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysAdapter.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysAdapter.java index 1f809cc51..1fed58721 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysAdapter.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysAdapter.java @@ -155,8 +155,8 @@ public class ImportKeysAdapter extends ArrayAdapter<ImportKeysListEntry> {          // don't show full fingerprint on key import          holder.fingerprint.setVisibility(View.GONE); -        if (entry.getBitStrength() != 0 && entry.getAlgorithm() != null) { -            holder.algorithm.setText("" + entry.getBitStrength() + "/" + entry.getAlgorithm()); +        if (entry.getAlgorithm() != null) { +            holder.algorithm.setText(entry.getAlgorithm());              holder.algorithm.setVisibility(View.VISIBLE);          } else {              holder.algorithm.setVisibility(View.INVISIBLE); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/SubkeysAdapter.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/SubkeysAdapter.java index 9958b33a1..489cbcefb 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/SubkeysAdapter.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/SubkeysAdapter.java @@ -52,6 +52,7 @@ public class SubkeysAdapter extends CursorAdapter {              Keys.RANK,              Keys.ALGORITHM,              Keys.KEY_SIZE, +            Keys.KEY_CURVE_OID,              Keys.HAS_SECRET,              Keys.CAN_CERTIFY,              Keys.CAN_ENCRYPT, @@ -66,14 +67,15 @@ public class SubkeysAdapter extends CursorAdapter {      private static final int INDEX_RANK = 2;      private static final int INDEX_ALGORITHM = 3;      private static final int INDEX_KEY_SIZE = 4; -    private static final int INDEX_HAS_SECRET = 5; -    private static final int INDEX_CAN_CERTIFY = 6; -    private static final int INDEX_CAN_ENCRYPT = 7; -    private static final int INDEX_CAN_SIGN = 8; -    private static final int INDEX_IS_REVOKED = 9; -    private static final int INDEX_CREATION = 10; -    private static final int INDEX_EXPIRY = 11; -    private static final int INDEX_FINGERPRINT = 12; +    private static final int INDEX_KEY_CURVE_OID = 5; +    private static final int INDEX_HAS_SECRET = 6; +    private static final int INDEX_CAN_CERTIFY = 7; +    private static final int INDEX_CAN_ENCRYPT = 8; +    private static final int INDEX_CAN_SIGN = 9; +    private static final int INDEX_IS_REVOKED = 10; +    private static final int INDEX_CREATION = 11; +    private static final int INDEX_EXPIRY = 12; +    private static final int INDEX_FINGERPRINT = 13;      public SubkeysAdapter(Context context, Cursor c, int flags,                            SaveKeyringParcel saveKeyringParcel) { @@ -141,7 +143,8 @@ public class SubkeysAdapter extends CursorAdapter {          String algorithmStr = PgpKeyHelper.getAlgorithmInfo(                  context,                  cursor.getInt(INDEX_ALGORITHM), -                cursor.getInt(INDEX_KEY_SIZE) +                cursor.getInt(INDEX_KEY_SIZE), +                cursor.getString(INDEX_KEY_CURVE_OID)          );          vKeyId.setText(keyIdStr); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/SubkeysAddedAdapter.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/SubkeysAddedAdapter.java index a1dfeb10c..d50318fb4 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/SubkeysAddedAdapter.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/SubkeysAddedAdapter.java @@ -42,14 +42,10 @@ public class SubkeysAddedAdapter extends ArrayAdapter<SaveKeyringParcel.SubkeyAd      private LayoutInflater mInflater;      private Activity mActivity; -    // hold a private reference to the underlying data List -    private List<SaveKeyringParcel.SubkeyAdd> mData; -      public SubkeysAddedAdapter(Activity activity, List<SaveKeyringParcel.SubkeyAdd> data) {          super(activity, -1, data);          mActivity = activity;          mInflater = (LayoutInflater) activity.getSystemService(Context.LAYOUT_INFLATER_SERVICE); -        mData = data;      }      static class ViewHolder { @@ -103,7 +99,8 @@ public class SubkeysAddedAdapter extends ArrayAdapter<SaveKeyringParcel.SubkeyAd          String algorithmStr = PgpKeyHelper.getAlgorithmInfo(                  mActivity,                  holder.mModel.mAlgorithm, -                holder.mModel.mKeysize +                holder.mModel.mKeySize, +                holder.mModel.mCurve          );          holder.vKeyId.setText(R.string.edit_key_new_subkey);          holder.vKeyDetails.setText(algorithmStr); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/AddSubkeyDialogFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/AddSubkeyDialogFragment.java index e07941267..b4119a5eb 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/AddSubkeyDialogFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/AddSubkeyDialogFragment.java @@ -20,14 +20,12 @@ package org.sufficientlysecure.keychain.ui.dialog;  import android.annotation.TargetApi;  import android.app.AlertDialog;  import android.app.Dialog; -import android.content.DialogInterface;  import android.os.Build;  import android.os.Bundle;  import android.support.v4.app.DialogFragment;  import android.support.v4.app.FragmentActivity;  import android.text.Editable;  import android.text.TextWatcher; -import android.text.format.DateUtils;  import android.view.LayoutInflater;  import android.view.View;  import android.view.inputmethod.InputMethodManager; @@ -43,16 +41,16 @@ import android.widget.TableRow;  import android.widget.TextView;  import android.widget.Toast; -import org.spongycastle.bcpg.PublicKeyAlgorithmTags;  import org.spongycastle.bcpg.sig.KeyFlags;  import org.sufficientlysecure.keychain.R;  import org.sufficientlysecure.keychain.service.SaveKeyringParcel; +import org.sufficientlysecure.keychain.service.SaveKeyringParcel.Algorithm; +import org.sufficientlysecure.keychain.service.SaveKeyringParcel.Curve;  import org.sufficientlysecure.keychain.util.Choice;  import java.util.ArrayList;  import java.util.Arrays;  import java.util.Calendar; -import java.util.Date;  import java.util.TimeZone;  public class AddSubkeyDialogFragment extends DialogFragment { @@ -69,7 +67,10 @@ public class AddSubkeyDialogFragment extends DialogFragment {      private TableRow mExpiryRow;      private DatePicker mExpiryDatePicker;      private Spinner mAlgorithmSpinner; +    private View mKeySizeRow;      private Spinner mKeySizeSpinner; +    private View mCurveRow; +    private Spinner mCurveSpinner;      private TextView mCustomKeyTextView;      private EditText mCustomKeyEditText;      private TextView mCustomKeyInfoTextView; @@ -114,6 +115,9 @@ public class AddSubkeyDialogFragment extends DialogFragment {          mExpiryDatePicker = (DatePicker) view.findViewById(R.id.add_subkey_expiry_date_picker);          mAlgorithmSpinner = (Spinner) view.findViewById(R.id.add_subkey_algorithm);          mKeySizeSpinner = (Spinner) view.findViewById(R.id.add_subkey_size); +        mCurveSpinner = (Spinner) view.findViewById(R.id.add_subkey_curve); +        mKeySizeRow = view.findViewById(R.id.add_subkey_row_size); +        mCurveRow = view.findViewById(R.id.add_subkey_row_curve);          mCustomKeyTextView = (TextView) view.findViewById(R.id.add_subkey_custom_key_size_label);          mCustomKeyEditText = (EditText) view.findViewById(R.id.add_subkey_custom_key_size_input);          mCustomKeyInfoTextView = (TextView) view.findViewById(R.id.add_subkey_custom_key_size_info); @@ -140,28 +144,30 @@ public class AddSubkeyDialogFragment extends DialogFragment {              mExpiryDatePicker.setMinDate(minDateCal.getTime().getTime());          } -        ArrayList<Choice> choices = new ArrayList<Choice>(); -        choices.add(new Choice(PublicKeyAlgorithmTags.DSA, getResources().getString( -                R.string.dsa))); -        if (!mWillBeMasterKey) { -            choices.add(new Choice(PublicKeyAlgorithmTags.ELGAMAL_ENCRYPT, getResources().getString( -                    R.string.elgamal))); -        } -        choices.add(new Choice(PublicKeyAlgorithmTags.RSA_GENERAL, getResources().getString( -                R.string.rsa))); -        choices.add(new Choice(PublicKeyAlgorithmTags.ECDH, getResources().getString( -                R.string.ecdh))); -        choices.add(new Choice(PublicKeyAlgorithmTags.ECDSA, getResources().getString( -                R.string.ecdsa))); -        ArrayAdapter<Choice> adapter = new ArrayAdapter<Choice>(context, -                android.R.layout.simple_spinner_item, choices); -        adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); -        mAlgorithmSpinner.setAdapter(adapter); -        // make RSA the default -        for (int i = 0; i < choices.size(); ++i) { -            if (choices.get(i).getId() == PublicKeyAlgorithmTags.RSA_GENERAL) { -                mAlgorithmSpinner.setSelection(i); -                break; +        { +            ArrayList<Choice<Algorithm>> choices = new ArrayList<Choice<Algorithm>>(); +            choices.add(new Choice<Algorithm>(Algorithm.DSA, getResources().getString( +                    R.string.dsa))); +            if (!mWillBeMasterKey) { +                choices.add(new Choice<Algorithm>(Algorithm.ELGAMAL, getResources().getString( +                        R.string.elgamal))); +            } +            choices.add(new Choice<Algorithm>(Algorithm.RSA, getResources().getString( +                    R.string.rsa))); +            choices.add(new Choice<Algorithm>(Algorithm.ECDSA, getResources().getString( +                    R.string.ecdsa))); +            choices.add(new Choice<Algorithm>(Algorithm.ECDH, getResources().getString( +                    R.string.ecdh))); +            ArrayAdapter<Choice<Algorithm>> adapter = new ArrayAdapter<Choice<Algorithm>>(context, +                    android.R.layout.simple_spinner_item, choices); +            adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); +            mAlgorithmSpinner.setAdapter(adapter); +            // make RSA the default +            for (int i = 0; i < choices.size(); ++i) { +                if (choices.get(i).getId() == Algorithm.RSA) { +                    mAlgorithmSpinner.setSelection(i); +                    break; +                }              }          } @@ -172,6 +178,36 @@ public class AddSubkeyDialogFragment extends DialogFragment {          mKeySizeSpinner.setAdapter(keySizeAdapter);          mKeySizeSpinner.setSelection(1); // Default to 4096 for the key length +        { +            ArrayList<Choice<Curve>> choices = new ArrayList<Choice<Curve>>(); + +            choices.add(new Choice<Curve>(Curve.NIST_P256, getResources().getString( +                    R.string.key_curve_nist_p256))); +            choices.add(new Choice<Curve>(Curve.NIST_P384, getResources().getString( +                    R.string.key_curve_nist_p384))); +            choices.add(new Choice<Curve>(Curve.NIST_P521, getResources().getString( +                    R.string.key_curve_nist_p521))); + +            /* @see SaveKeyringParcel +            choices.add(new Choice<Curve>(Curve.BRAINPOOL_P256, getResources().getString( +                    R.string.key_curve_bp_p256))); +            choices.add(new Choice<Curve>(Curve.BRAINPOOL_P384, getResources().getString( +                    R.string.key_curve_bp_p384))); +            choices.add(new Choice<Curve>(Curve.BRAINPOOL_P512, getResources().getString( +                    R.string.key_curve_bp_p512))); +            */ + +            ArrayAdapter<Choice<Curve>> adapter = new ArrayAdapter<Choice<Curve>>(context, +                    android.R.layout.simple_spinner_item, choices); +            mCurveSpinner.setAdapter(adapter); +            // make NIST P-256 the default +            for (int i = 0; i < choices.size(); ++i) { +                if (choices.get(i).getId() == Curve.NIST_P256) { +                    mCurveSpinner.setSelection(i); +                    break; +                } +            } +        }          dialog.setCancelable(true); @@ -211,7 +247,7 @@ public class AddSubkeyDialogFragment extends DialogFragment {          mAlgorithmSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {              @Override              public void onItemSelected(AdapterView<?> parent, View view, int position, long id) { -                updateUiForAlgorithm(((Choice) parent.getSelectedItem()).getId()); +                updateUiForAlgorithm(((Choice<Algorithm>) parent.getSelectedItem()).getId());                  setCustomKeyVisibility();                  setOkButtonAvailability(alertDialog); @@ -241,11 +277,16 @@ public class AddSubkeyDialogFragment extends DialogFragment {                          return;                      } -                    // dismiss only if at least one flag is selected -                    dismiss(); - -                    Choice newKeyAlgorithmChoice = (Choice) mAlgorithmSpinner.getSelectedItem(); -                    int newKeySize = getProperKeyLength(newKeyAlgorithmChoice.getId(), getSelectedKeyLength()); +                    Algorithm algorithm = ((Choice<Algorithm>) mAlgorithmSpinner.getSelectedItem()).getId(); +                    Curve curve = null; +                    Integer keySize = null; +                    // For EC keys, add a curve +                    if (algorithm == Algorithm.ECDH || algorithm == Algorithm.ECDSA) { +                        curve = ((Choice<Curve>) mCurveSpinner.getSelectedItem()).getId(); +                    // Otherwise, get a keysize +                    } else { +                        keySize = getProperKeyLength(algorithm, getSelectedKeyLength()); +                    }                      int flags = 0;                      if (mFlagCertify.isChecked()) { @@ -276,12 +317,12 @@ public class AddSubkeyDialogFragment extends DialogFragment {                      }                      SaveKeyringParcel.SubkeyAdd newSubkey = new SaveKeyringParcel.SubkeyAdd( -                            newKeyAlgorithmChoice.getId(), -                            newKeySize, -                            flags, -                            expiry +                            algorithm, keySize, curve, flags, expiry                      );                      mAlgorithmSelectedListener.onAlgorithmSelected(newSubkey); + +                    // finally, dismiss the dialogue +                    dismiss();                  }              });              negativeButton.setOnClickListener(new View.OnClickListener() { @@ -323,16 +364,16 @@ public class AddSubkeyDialogFragment extends DialogFragment {       * @return correct key length, according to SpongyCastle specification. Returns <code>-1</code>, if key length is       * inappropriate.       */ -    private int getProperKeyLength(int algorithmId, int currentKeyLength) { +    private int getProperKeyLength(Algorithm algorithm, int currentKeyLength) {          final int[] elGamalSupportedLengths = {1536, 2048, 3072, 4096, 8192};          int properKeyLength = -1; -        switch (algorithmId) { -            case PublicKeyAlgorithmTags.RSA_GENERAL: +        switch (algorithm) { +            case RSA:                  if (currentKeyLength > 1024 && currentKeyLength <= 16384) {                      properKeyLength = currentKeyLength + ((8 - (currentKeyLength % 8)) % 8);                  }                  break; -            case PublicKeyAlgorithmTags.ELGAMAL_ENCRYPT: +            case ELGAMAL:                  int[] elGammalKeyDiff = new int[elGamalSupportedLengths.length];                  for (int i = 0; i < elGamalSupportedLengths.length; i++) {                      elGammalKeyDiff[i] = Math.abs(elGamalSupportedLengths[i] - currentKeyLength); @@ -347,7 +388,7 @@ public class AddSubkeyDialogFragment extends DialogFragment {                  }                  properKeyLength = elGamalSupportedLengths[minimalIndex];                  break; -            case PublicKeyAlgorithmTags.DSA: +            case DSA:                  if (currentKeyLength >= 512 && currentKeyLength <= 1024) {                      properKeyLength = currentKeyLength + ((64 - (currentKeyLength % 64)) % 64);                  } @@ -357,10 +398,10 @@ public class AddSubkeyDialogFragment extends DialogFragment {      }      private void setOkButtonAvailability(AlertDialog alertDialog) { -        final Choice selectedAlgorithm = (Choice) mAlgorithmSpinner.getSelectedItem(); -        final int selectedKeySize = getSelectedKeyLength(); //Integer.parseInt((String) mKeySizeSpinner.getSelectedItem()); -        final int properKeyLength = getProperKeyLength(selectedAlgorithm.getId(), selectedKeySize); -        alertDialog.getButton(AlertDialog.BUTTON_POSITIVE).setEnabled(properKeyLength > 0); +        Algorithm algorithm = ((Choice<Algorithm>) mAlgorithmSpinner.getSelectedItem()).getId(); +        boolean enabled = algorithm == Algorithm.ECDSA || algorithm == Algorithm.ECDH +                || getProperKeyLength(algorithm, getSelectedKeyLength()) > 0; +        alertDialog.getButton(AlertDialog.BUTTON_POSITIVE).setEnabled(enabled);      }      private void setCustomKeyVisibility() { @@ -376,18 +417,20 @@ public class AddSubkeyDialogFragment extends DialogFragment {          // hide keyboard after setting visibility to gone          if (visibility == View.GONE) {              InputMethodManager imm = (InputMethodManager) -                    getActivity().getSystemService(getActivity().INPUT_METHOD_SERVICE); +                    getActivity().getSystemService(FragmentActivity.INPUT_METHOD_SERVICE);              imm.hideSoftInputFromWindow(mCustomKeyEditText.getWindowToken(), 0);          }      } -    private void updateUiForAlgorithm(int algorithmId) { +    private void updateUiForAlgorithm(Algorithm algorithm) {          final ArrayAdapter<CharSequence> keySizeAdapter = (ArrayAdapter<CharSequence>) mKeySizeSpinner.getAdapter(); -        final Object selectedItem = mKeySizeSpinner.getSelectedItem();          keySizeAdapter.clear(); -        switch (algorithmId) { -            case PublicKeyAlgorithmTags.RSA_GENERAL: +        switch (algorithm) { +            case RSA:                  replaceArrayAdapterContent(keySizeAdapter, R.array.rsa_key_size_spinner_values); +                mKeySizeSpinner.setSelection(1); +                mKeySizeRow.setVisibility(View.VISIBLE); +                mCurveRow.setVisibility(View.GONE);                  mCustomKeyInfoTextView.setText(getResources().getString(R.string.key_size_custom_info_rsa));                  // allowed flags:                  mFlagSign.setEnabled(true); @@ -409,8 +452,11 @@ public class AddSubkeyDialogFragment extends DialogFragment {                  }                  mFlagAuthenticate.setChecked(false);                  break; -            case PublicKeyAlgorithmTags.ELGAMAL_ENCRYPT: +            case ELGAMAL:                  replaceArrayAdapterContent(keySizeAdapter, R.array.elgamal_key_size_spinner_values); +                mKeySizeSpinner.setSelection(3); +                mKeySizeRow.setVisibility(View.VISIBLE); +                mCurveRow.setVisibility(View.GONE);                  mCustomKeyInfoTextView.setText(""); // ElGamal does not support custom key length                  // allowed flags:                  mFlagCertify.setChecked(false); @@ -422,8 +468,11 @@ public class AddSubkeyDialogFragment extends DialogFragment {                  mFlagAuthenticate.setChecked(false);                  mFlagAuthenticate.setEnabled(false);                  break; -            case PublicKeyAlgorithmTags.DSA: +            case DSA:                  replaceArrayAdapterContent(keySizeAdapter, R.array.dsa_key_size_spinner_values); +                mKeySizeSpinner.setSelection(2); +                mKeySizeRow.setVisibility(View.VISIBLE); +                mCurveRow.setVisibility(View.GONE);                  mCustomKeyInfoTextView.setText(getResources().getString(R.string.key_size_custom_info_dsa));                  // allowed flags:                  mFlagCertify.setChecked(false); @@ -435,16 +484,37 @@ public class AddSubkeyDialogFragment extends DialogFragment {                  mFlagAuthenticate.setChecked(false);                  mFlagAuthenticate.setEnabled(false);                  break; +            case ECDSA: +                mKeySizeRow.setVisibility(View.GONE); +                mCurveRow.setVisibility(View.VISIBLE); +                mCustomKeyInfoTextView.setText(""); +                // allowed flags: +                mFlagCertify.setEnabled(mWillBeMasterKey); +                mFlagCertify.setChecked(mWillBeMasterKey); +                mFlagSign.setEnabled(true); +                mFlagSign.setChecked(!mWillBeMasterKey); +                mFlagEncrypt.setEnabled(false); +                mFlagEncrypt.setChecked(false); +                mFlagAuthenticate.setEnabled(true); +                mFlagAuthenticate.setChecked(false); +                break; +            case ECDH: +                mKeySizeRow.setVisibility(View.GONE); +                mCurveRow.setVisibility(View.VISIBLE); +                mCustomKeyInfoTextView.setText(""); +                // allowed flags: +                mFlagCertify.setChecked(false); +                mFlagCertify.setEnabled(false); +                mFlagSign.setChecked(false); +                mFlagSign.setEnabled(false); +                mFlagEncrypt.setChecked(true); +                mFlagEncrypt.setEnabled(true); +                mFlagAuthenticate.setChecked(false); +                mFlagAuthenticate.setEnabled(false); +                break;          }          keySizeAdapter.notifyDataSetChanged(); -        // when switching algorithm, try to select same key length as before -        for (int i = 0; i < keySizeAdapter.getCount(); i++) { -            if (selectedItem.equals(keySizeAdapter.getItem(i))) { -                mKeySizeSpinner.setSelection(i); -                break; -            } -        }      }      @TargetApi(Build.VERSION_CODES.HONEYCOMB) diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/Choice.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/Choice.java index 70c7d80fe..48f10d4b9 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/Choice.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/Choice.java @@ -17,21 +17,16 @@  package org.sufficientlysecure.keychain.util; -public class Choice { +public class Choice <E> {      private String mName; -    private int mId; +    private E mId; -    public Choice() { -        mId = -1; -        mName = ""; -    } - -    public Choice(int id, String name) { +    public Choice(E id, String name) {          mId = id;          mName = name;      } -    public int getId() { +    public E getId() {          return mId;      } | 
