From 783dae8804fee1f1594cc910d3570fcf6178015c Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Sun, 24 Aug 2014 05:12:09 +0200 Subject: add ecc support --- .../keychain/keyimport/HkpKeyserver.java | 2 +- .../keychain/keyimport/ImportKeysListEntry.java | 23 ++- .../keychain/keyimport/KeybaseKeyserver.java | 2 +- .../keychain/pgp/PgpKeyHelper.java | 131 ++++++++++++-- .../keychain/pgp/PgpKeyOperation.java | 106 +++++++++--- .../keychain/pgp/UncachedKeyRing.java | 6 +- .../keychain/pgp/UncachedPublicKey.java | 23 ++- .../keychain/provider/KeychainContract.java | 1 + .../keychain/provider/KeychainDatabase.java | 24 ++- .../keychain/provider/KeychainProvider.java | 2 + .../keychain/provider/ProviderHelper.java | 2 +- .../keychain/service/OperationResultParcel.java | 7 +- .../keychain/service/SaveKeyringParcel.java | 29 +++- .../keychain/ui/CreateKeyFinalFragment.java | 10 +- .../keychain/ui/ViewCertActivity.java | 2 +- .../keychain/ui/adapter/ImportKeysAdapter.java | 4 +- .../keychain/ui/adapter/SubkeysAdapter.java | 21 ++- .../keychain/ui/adapter/SubkeysAddedAdapter.java | 7 +- .../ui/dialog/AddSubkeyDialogFragment.java | 188 ++++++++++++++------- .../sufficientlysecure/keychain/util/Choice.java | 13 +- .../src/main/res/layout/add_subkey_dialog.xml | 20 ++- OpenKeychain/src/main/res/values/strings.xml | 24 ++- 22 files changed, 494 insertions(+), 153 deletions(-) (limited to 'OpenKeychain/src') 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 OpenPGP Message Format */ - 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 OpenPGP Message Format + */ + 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) *

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 { // 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 mData; - public SubkeysAddedAdapter(Activity activity, List 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 choices = new ArrayList(); - 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 adapter = new ArrayAdapter(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> choices = new ArrayList>(); + choices.add(new Choice(Algorithm.DSA, getResources().getString( + R.string.dsa))); + if (!mWillBeMasterKey) { + choices.add(new Choice(Algorithm.ELGAMAL, getResources().getString( + R.string.elgamal))); + } + choices.add(new Choice(Algorithm.RSA, getResources().getString( + R.string.rsa))); + choices.add(new Choice(Algorithm.ECDSA, getResources().getString( + R.string.ecdsa))); + choices.add(new Choice(Algorithm.ECDH, getResources().getString( + R.string.ecdh))); + ArrayAdapter> adapter = new ArrayAdapter>(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> choices = new ArrayList>(); + + choices.add(new Choice(Curve.NIST_P256, getResources().getString( + R.string.key_curve_nist_p256))); + choices.add(new Choice(Curve.NIST_P384, getResources().getString( + R.string.key_curve_nist_p384))); + choices.add(new Choice(Curve.NIST_P521, getResources().getString( + R.string.key_curve_nist_p521))); + + /* @see SaveKeyringParcel + choices.add(new Choice(Curve.BRAINPOOL_P256, getResources().getString( + R.string.key_curve_bp_p256))); + choices.add(new Choice(Curve.BRAINPOOL_P384, getResources().getString( + R.string.key_curve_bp_p384))); + choices.add(new Choice(Curve.BRAINPOOL_P512, getResources().getString( + R.string.key_curve_bp_p512))); + */ + + ArrayAdapter> adapter = new ArrayAdapter>(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) 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) mAlgorithmSpinner.getSelectedItem()).getId(); + Curve curve = null; + Integer keySize = null; + // For EC keys, add a curve + if (algorithm == Algorithm.ECDH || algorithm == Algorithm.ECDSA) { + curve = ((Choice) 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 -1, 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) 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 keySizeAdapter = (ArrayAdapter) 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 { 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; } diff --git a/OpenKeychain/src/main/res/layout/add_subkey_dialog.xml b/OpenKeychain/src/main/res/layout/add_subkey_dialog.xml index e1ccfee1f..7af73be09 100644 --- a/OpenKeychain/src/main/res/layout/add_subkey_dialog.xml +++ b/OpenKeychain/src/main/res/layout/add_subkey_dialog.xml @@ -34,7 +34,7 @@ android:padding="4dp" /> - + + + + + + + + Expiry Usage Key Size + Elliptic Curve Primary identity Name Comment @@ -270,6 +271,8 @@ generating new RSA key… generating new DSA key… generating new ElGamal key… + generating new ECDSA key… + generating new ECDH key… modifying keyring… @@ -326,6 +329,16 @@ RSA key length must be greater than 1024 and at most 16384. Also it must be multiplicity of 8. DSA key length must be at least 512 and at most 1024. Also it must be multiplicity of 64. + + NIST P-256 + NIST P-384 + NIST P-521 + + fast very slow @@ -636,9 +649,14 @@ Master key must have certify flag! Expiry time cannot be "same as before" on key creation. This is a programming error, please file a bug report! Key size must be greater or equal 512! + No key size specified! This is a programming error, please file a bug report! + No elliptic curve specified! This is a programming error, please file a bug report! Internal PGP error! - Bad algorithm choice! - Master key must not be of type ElGamal! + Unknown algorithm selected! This is a programming error, please file a bug report! + Bad key flags selected, DSA cannot be used for encryption! + Bad key flags selected, ElGamal cannot be used for signing! + Bad key flags selected, ECDSA cannot be used for encryption! + Bad key flags selected, ECDH cannot be used for signing! Modifying keyring %s @@ -663,7 +681,7 @@ Generating new certificate for new primary user id Modifying subkey %s Tried to operate on missing subkey %s! - Adding new subkey of type %2$s (%1$s bit) + Adding new subkey of type %s New subkey ID: %s Expiry date cannot be in the past! Revoking subkey %s -- cgit v1.2.3