aboutsummaryrefslogtreecommitdiffstats
path: root/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp
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/src/main/java/org/sufficientlysecure/keychain/pgp
parent08bdb0c5b983b6d0096187873275598a1ae4c9c3 (diff)
downloadopen-keychain-783dae8804fee1f1594cc910d3570fcf6178015c.tar.gz
open-keychain-783dae8804fee1f1594cc910d3570fcf6178015c.tar.bz2
open-keychain-783dae8804fee1f1594cc910d3570fcf6178015c.zip
add ecc support
Diffstat (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp')
-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
4 files changed, 230 insertions, 36 deletions
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() {