add ecc support
This commit is contained in:
@@ -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/>
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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() {
|
||||
|
||||
Reference in New Issue
Block a user