aboutsummaryrefslogtreecommitdiffstats
path: root/OpenKeychain
diff options
context:
space:
mode:
authorVincent Breitmoser <valodim@mugenguild.com>2014-08-24 05:12:09 +0200
committerVincent Breitmoser <valodim@mugenguild.com>2014-08-24 16:08:37 +0200
commit783dae8804fee1f1594cc910d3570fcf6178015c (patch)
treeaa35d500f29badb8cc616d38c1b7df36f00922b3 /OpenKeychain
parent08bdb0c5b983b6d0096187873275598a1ae4c9c3 (diff)
downloadopen-keychain-783dae8804fee1f1594cc910d3570fcf6178015c.tar.gz
open-keychain-783dae8804fee1f1594cc910d3570fcf6178015c.tar.bz2
open-keychain-783dae8804fee1f1594cc910d3570fcf6178015c.zip
add ecc support
Diffstat (limited to 'OpenKeychain')
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/HkpKeyserver.java2
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/ImportKeysListEntry.java23
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/KeybaseKeyserver.java2
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyHelper.java131
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java106
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java6
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedPublicKey.java23
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainContract.java1
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainDatabase.java24
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java2
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java2
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java7
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/SaveKeyringParcel.java29
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyFinalFragment.java10
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewCertActivity.java2
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysAdapter.java4
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/SubkeysAdapter.java21
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/SubkeysAddedAdapter.java7
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/AddSubkeyDialogFragment.java188
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/Choice.java13
-rw-r--r--OpenKeychain/src/main/res/layout/add_subkey_dialog.xml20
-rw-r--r--OpenKeychain/src/main/res/values/strings.xml24
22 files changed, 494 insertions, 153 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;
}
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" />
</TableRow>
- <TableRow>
+ <TableRow android:id="@+id/add_subkey_row_size">
<TextView
android:layout_width="wrap_content"
@@ -50,6 +50,24 @@
android:padding="4dp" />
</TableRow>
+ <TableRow
+ android:id="@+id/add_subkey_row_curve"
+ android:visibility="gone">
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical"
+ android:text="@string/label_ecc_curve"/>
+
+ <Spinner
+ android:id="@+id/add_subkey_curve"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:gravity="right"
+ android:padding="4dp"/>
+ </TableRow>
+
<TextView
android:id="@+id/add_subkey_custom_key_size_label"
android:layout_width="wrap_content"
diff --git a/OpenKeychain/src/main/res/values/strings.xml b/OpenKeychain/src/main/res/values/strings.xml
index ba5ed867a..d9996c0d7 100644
--- a/OpenKeychain/src/main/res/values/strings.xml
+++ b/OpenKeychain/src/main/res/values/strings.xml
@@ -109,6 +109,7 @@
<string name="label_expiry">Expiry</string>
<string name="label_usage">Usage</string>
<string name="label_key_size">Key Size</string>
+ <string name="label_ecc_curve">Elliptic Curve</string>
<string name="label_main_user_id">Primary identity</string>
<string name="label_name">Name</string>
<string name="label_comment">Comment</string>
@@ -270,6 +271,8 @@
<string name="progress_generating_rsa">generating new RSA key…</string>
<string name="progress_generating_dsa">generating new DSA key…</string>
<string name="progress_generating_elgamal">generating new ElGamal key…</string>
+ <string name="progress_generating_ecdsa">generating new ECDSA key…</string>
+ <string name="progress_generating_ecdh">generating new ECDH key…</string>
<string name="progress_modify">modifying keyring…</string>
@@ -326,6 +329,16 @@
<string name="key_size_custom_info_rsa">RSA key length must be greater than 1024 and at most 16384. Also it must be multiplicity of 8.</string>
<string name="key_size_custom_info_dsa">DSA key length must be at least 512 and at most 1024. Also it must be multiplicity of 64.</string>
+ <!-- elliptic curve names -->
+ <string name="key_curve_nist_p256">NIST P-256</string>
+ <string name="key_curve_nist_p384">NIST P-384</string>
+ <string name="key_curve_nist_p521">NIST P-521</string>
+ <!-- not in for now, see SaveKeyringParcel
+ <string name="key_curve_bp_p256">Brainpool P-256</string>
+ <string name="key_curve_bp_p384">Brainpool P-384</string>
+ <string name="key_curve_bp_p512">Brainpool P-512</string>
+ -->
+
<!-- compression -->
<string name="compression_fast">fast</string>
<string name="compression_very_slow">very slow</string>
@@ -636,9 +649,14 @@
<string name="msg_cr_error_no_certify">Master key must have certify flag!</string>
<string name="msg_cr_error_null_expiry">Expiry time cannot be "same as before" on key creation. This is a programming error, please file a bug report!</string>
<string name="msg_cr_error_keysize_512">Key size must be greater or equal 512!</string>
+ <string name="msg_cr_error_no_curve">No key size specified! This is a programming error, please file a bug report!</string>
+ <string name="msg_cr_error_no_keysize">No elliptic curve specified! This is a programming error, please file a bug report!</string>
<string name="msg_cr_error_internal_pgp">Internal PGP error!</string>
- <string name="msg_cr_error_unknown_algo">Bad algorithm choice!</string>
- <string name="msg_cr_error_master_elgamal">Master key must not be of type ElGamal!</string>
+ <string name="msg_cr_error_unknown_algo">Unknown algorithm selected! This is a programming error, please file a bug report!</string>
+ <string name="msg_cr_error_flags_dsa">Bad key flags selected, DSA cannot be used for encryption!</string>
+ <string name="msg_cr_error_flags_elgamal">Bad key flags selected, ElGamal cannot be used for signing!</string>
+ <string name="msg_cr_error_flags_ecdsa">Bad key flags selected, ECDSA cannot be used for encryption!</string>
+ <string name="msg_cr_error_flags_ecdh">Bad key flags selected, ECDH cannot be used for signing!</string>
<!-- modifySecretKeyRing -->
<string name="msg_mr">Modifying keyring %s</string>
@@ -663,7 +681,7 @@
<string name="msg_mf_primary_new">Generating new certificate for new primary user id</string>
<string name="msg_mf_subkey_change">Modifying subkey %s</string>
<string name="msg_mf_error_subkey_missing">Tried to operate on missing subkey %s!</string>
- <string name="msg_mf_subkey_new">Adding new subkey of type %2$s (%1$s bit)</string>
+ <string name="msg_mf_subkey_new">Adding new subkey of type %s</string>
<string name="msg_mf_subkey_new_id">New subkey ID: %s</string>
<string name="msg_mf_error_past_expiry">Expiry date cannot be in the past!</string>
<string name="msg_mf_subkey_revoke">Revoking subkey %s</string>