Merge pull request #796 from open-keychain/develop

ECC support
This commit is contained in:
Dominik Schürmann 2014-08-28 10:46:58 +02:00
commit b193965e58
29 changed files with 532 additions and 217 deletions

View file

@ -36,11 +36,11 @@ import org.spongycastle.bcpg.UserIDPacket;
import org.spongycastle.bcpg.sig.KeyFlags; import org.spongycastle.bcpg.sig.KeyFlags;
import org.spongycastle.jce.provider.BouncyCastleProvider; import org.spongycastle.jce.provider.BouncyCastleProvider;
import org.spongycastle.openpgp.PGPSignature; import org.spongycastle.openpgp.PGPSignature;
import org.spongycastle.bcpg.PublicKeyAlgorithmTags;
import org.sufficientlysecure.keychain.service.OperationResultParcel.LogType; import org.sufficientlysecure.keychain.service.OperationResultParcel.LogType;
import org.sufficientlysecure.keychain.service.OperationResultParcel.OperationLog; import org.sufficientlysecure.keychain.service.OperationResultParcel.OperationLog;
import org.sufficientlysecure.keychain.service.OperationResults.EditKeyResult; import org.sufficientlysecure.keychain.service.OperationResults.EditKeyResult;
import org.sufficientlysecure.keychain.service.SaveKeyringParcel; import org.sufficientlysecure.keychain.service.SaveKeyringParcel;
import org.sufficientlysecure.keychain.service.SaveKeyringParcel.Algorithm;
import org.sufficientlysecure.keychain.service.SaveKeyringParcel.SubkeyAdd; import org.sufficientlysecure.keychain.service.SaveKeyringParcel.SubkeyAdd;
import org.sufficientlysecure.keychain.service.SaveKeyringParcel.SubkeyChange; import org.sufficientlysecure.keychain.service.SaveKeyringParcel.SubkeyChange;
import org.sufficientlysecure.keychain.support.KeyringBuilder; import org.sufficientlysecure.keychain.support.KeyringBuilder;
@ -78,11 +78,11 @@ public class PgpKeyOperationTest {
SaveKeyringParcel parcel = new SaveKeyringParcel(); SaveKeyringParcel parcel = new SaveKeyringParcel();
parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd( parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd(
PublicKeyAlgorithmTags.RSA_GENERAL, 1024, KeyFlags.CERTIFY_OTHER, 0L)); Algorithm.RSA, 1024, null, KeyFlags.CERTIFY_OTHER, 0L));
parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd( parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd(
PublicKeyAlgorithmTags.DSA, 1024, KeyFlags.SIGN_DATA, 0L)); Algorithm.DSA, 1024, null, KeyFlags.SIGN_DATA, 0L));
parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd( parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd(
PublicKeyAlgorithmTags.ELGAMAL_ENCRYPT, 1024, KeyFlags.ENCRYPT_COMMS, 0L)); Algorithm.ELGAMAL, 1024, null, KeyFlags.ENCRYPT_COMMS, 0L));
parcel.mAddUserIds.add("twi"); parcel.mAddUserIds.add("twi");
parcel.mAddUserIds.add("pink"); parcel.mAddUserIds.add("pink");
@ -120,7 +120,7 @@ public class PgpKeyOperationTest {
{ {
parcel.reset(); parcel.reset();
parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd( parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd(
PublicKeyAlgorithmTags.RSA_GENERAL, new Random().nextInt(256)+255, KeyFlags.CERTIFY_OTHER, 0L)); Algorithm.RSA, new Random().nextInt(256)+255, null, KeyFlags.CERTIFY_OTHER, 0L));
parcel.mAddUserIds.add("shy"); parcel.mAddUserIds.add("shy");
parcel.mNewPassphrase = passphrase; parcel.mNewPassphrase = passphrase;
@ -131,18 +131,18 @@ public class PgpKeyOperationTest {
{ {
parcel.reset(); parcel.reset();
parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd( parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd(
PublicKeyAlgorithmTags.ELGAMAL_ENCRYPT, 1024, KeyFlags.CERTIFY_OTHER, 0L)); Algorithm.ELGAMAL, 1024, null, KeyFlags.CERTIFY_OTHER, 0L));
parcel.mAddUserIds.add("shy"); parcel.mAddUserIds.add("shy");
parcel.mNewPassphrase = passphrase; parcel.mNewPassphrase = passphrase;
assertFailure("creating ring with ElGamal master key should fail", parcel, assertFailure("creating ring with ElGamal master key should fail", parcel,
LogType.MSG_CR_ERROR_MASTER_ELGAMAL); LogType.MSG_CR_ERROR_FLAGS_ELGAMAL);
} }
{ {
parcel.reset(); parcel.reset();
parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd( parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd(
PublicKeyAlgorithmTags.RSA_GENERAL, 1024, KeyFlags.CERTIFY_OTHER, null)); Algorithm.RSA, 1024, null, KeyFlags.CERTIFY_OTHER, null));
parcel.mAddUserIds.add("lotus"); parcel.mAddUserIds.add("lotus");
parcel.mNewPassphrase = passphrase; parcel.mNewPassphrase = passphrase;
@ -153,18 +153,7 @@ public class PgpKeyOperationTest {
{ {
parcel.reset(); parcel.reset();
parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd( parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd(
12345, 1024, KeyFlags.CERTIFY_OTHER, 0L)); Algorithm.RSA, 1024, null, KeyFlags.SIGN_DATA, 0L));
parcel.mAddUserIds.add("shy");
parcel.mNewPassphrase = passphrase;
assertFailure("creating ring with bad algorithm choice should fail", parcel,
LogType.MSG_CR_ERROR_UNKNOWN_ALGO);
}
{
parcel.reset();
parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd(
PublicKeyAlgorithmTags.RSA_GENERAL, 1024, KeyFlags.SIGN_DATA, 0L));
parcel.mAddUserIds.add("shy"); parcel.mAddUserIds.add("shy");
parcel.mNewPassphrase = passphrase; parcel.mNewPassphrase = passphrase;
@ -175,7 +164,7 @@ public class PgpKeyOperationTest {
{ {
parcel.reset(); parcel.reset();
parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd( parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd(
PublicKeyAlgorithmTags.RSA_GENERAL, 1024, KeyFlags.CERTIFY_OTHER, 0L)); Algorithm.RSA, 1024, null, KeyFlags.CERTIFY_OTHER, 0L));
parcel.mNewPassphrase = passphrase; parcel.mNewPassphrase = passphrase;
assertFailure("creating ring without user ids should fail", parcel, assertFailure("creating ring without user ids should fail", parcel,
@ -199,7 +188,7 @@ public class PgpKeyOperationTest {
public void testMasterFlags() throws Exception { public void testMasterFlags() throws Exception {
SaveKeyringParcel parcel = new SaveKeyringParcel(); SaveKeyringParcel parcel = new SaveKeyringParcel();
parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd( parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd(
PublicKeyAlgorithmTags.RSA_GENERAL, 1024, KeyFlags.CERTIFY_OTHER | KeyFlags.SIGN_DATA, 0L)); Algorithm.RSA, 1024, null, KeyFlags.CERTIFY_OTHER | KeyFlags.SIGN_DATA, 0L));
parcel.mAddUserIds.add("luna"); parcel.mAddUserIds.add("luna");
ring = assertCreateSuccess("creating ring with master key flags must succeed", parcel); ring = assertCreateSuccess("creating ring with master key flags must succeed", parcel);
@ -313,7 +302,7 @@ public class PgpKeyOperationTest {
long expiry = new Date().getTime() / 1000 + 159; long expiry = new Date().getTime() / 1000 + 159;
int flags = KeyFlags.SIGN_DATA; int flags = KeyFlags.SIGN_DATA;
int bits = 1024 + new Random().nextInt(8); int bits = 1024 + new Random().nextInt(8);
parcel.mAddSubKeys.add(new SubkeyAdd(PublicKeyAlgorithmTags.RSA_GENERAL, bits, flags, expiry)); parcel.mAddSubKeys.add(new SubkeyAdd(Algorithm.RSA, bits, null, flags, expiry));
UncachedKeyRing modified = applyModificationWithChecks(parcel, ring, onlyA, onlyB); UncachedKeyRing modified = applyModificationWithChecks(parcel, ring, onlyA, onlyB);
@ -349,12 +338,12 @@ public class PgpKeyOperationTest {
Assert.assertEquals("added key must have expected flags", Assert.assertEquals("added key must have expected flags",
flags, newKey.getKeyUsage()); flags, newKey.getKeyUsage());
Assert.assertEquals("added key must have expected bitsize", Assert.assertEquals("added key must have expected bitsize",
bits, newKey.getBitStrength()); bits, (int) newKey.getBitStrength());
{ // bad keysize should fail { // bad keysize should fail
parcel.reset(); parcel.reset();
parcel.mAddSubKeys.add(new SubkeyAdd( parcel.mAddSubKeys.add(new SubkeyAdd(
PublicKeyAlgorithmTags.RSA_GENERAL, new Random().nextInt(512), KeyFlags.SIGN_DATA, 0L)); Algorithm.RSA, new Random().nextInt(512), null, KeyFlags.SIGN_DATA, 0L));
assertModifyFailure("creating a subkey with keysize < 512 should fail", ring, parcel, assertModifyFailure("creating a subkey with keysize < 512 should fail", ring, parcel,
LogType.MSG_CR_ERROR_KEYSIZE_512); LogType.MSG_CR_ERROR_KEYSIZE_512);
@ -363,7 +352,7 @@ public class PgpKeyOperationTest {
{ {
parcel.reset(); parcel.reset();
parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd( parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd(
PublicKeyAlgorithmTags.RSA_GENERAL, 1024, KeyFlags.SIGN_DATA, null)); Algorithm.RSA, 1024, null, KeyFlags.SIGN_DATA, null));
assertModifyFailure("creating master key with null expiry should fail", ring, parcel, assertModifyFailure("creating master key with null expiry should fail", ring, parcel,
LogType.MSG_MF_ERROR_NULL_EXPIRY); LogType.MSG_MF_ERROR_NULL_EXPIRY);
@ -371,7 +360,7 @@ public class PgpKeyOperationTest {
{ // a past expiry should fail { // a past expiry should fail
parcel.reset(); parcel.reset();
parcel.mAddSubKeys.add(new SubkeyAdd(PublicKeyAlgorithmTags.RSA_GENERAL, 1024, KeyFlags.SIGN_DATA, parcel.mAddSubKeys.add(new SubkeyAdd(Algorithm.RSA, 1024, null, KeyFlags.SIGN_DATA,
new Date().getTime()/1000-10)); new Date().getTime()/1000-10));
assertModifyFailure("creating subkey with past expiry date should fail", ring, parcel, assertModifyFailure("creating subkey with past expiry date should fail", ring, parcel,
LogType.MSG_MF_ERROR_PAST_EXPIRY); LogType.MSG_MF_ERROR_PAST_EXPIRY);

View file

@ -49,6 +49,7 @@ import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.service.OperationResultParcel; import org.sufficientlysecure.keychain.service.OperationResultParcel;
import org.sufficientlysecure.keychain.service.OperationResults.EditKeyResult; import org.sufficientlysecure.keychain.service.OperationResults.EditKeyResult;
import org.sufficientlysecure.keychain.service.SaveKeyringParcel; import org.sufficientlysecure.keychain.service.SaveKeyringParcel;
import org.sufficientlysecure.keychain.service.SaveKeyringParcel.Algorithm;
import org.sufficientlysecure.keychain.support.KeyringTestingHelper; import org.sufficientlysecure.keychain.support.KeyringTestingHelper;
import org.sufficientlysecure.keychain.support.KeyringTestingHelper.RawPacket; import org.sufficientlysecure.keychain.support.KeyringTestingHelper.RawPacket;
@ -85,11 +86,11 @@ public class UncachedKeyringCanonicalizeTest {
SaveKeyringParcel parcel = new SaveKeyringParcel(); SaveKeyringParcel parcel = new SaveKeyringParcel();
parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd( parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd(
PublicKeyAlgorithmTags.RSA_GENERAL, 1024, KeyFlags.CERTIFY_OTHER, 0L)); Algorithm.RSA, 1024, null, KeyFlags.CERTIFY_OTHER, 0L));
parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd( parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd(
PublicKeyAlgorithmTags.RSA_GENERAL, 1024, KeyFlags.SIGN_DATA, 0L)); Algorithm.RSA, 1024, null, KeyFlags.SIGN_DATA, 0L));
parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd( parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd(
PublicKeyAlgorithmTags.RSA_GENERAL, 1024, KeyFlags.ENCRYPT_COMMS, 0L)); Algorithm.RSA, 1024, null, KeyFlags.ENCRYPT_COMMS, 0L));
parcel.mAddUserIds.add("twi"); parcel.mAddUserIds.add("twi");
parcel.mAddUserIds.add("pink"); parcel.mAddUserIds.add("pink");
@ -298,7 +299,7 @@ public class UncachedKeyringCanonicalizeTest {
SaveKeyringParcel parcel = new SaveKeyringParcel(); SaveKeyringParcel parcel = new SaveKeyringParcel();
parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd( parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd(
PublicKeyAlgorithmTags.RSA_GENERAL, 1024, KeyFlags.CERTIFY_OTHER, 0L)); Algorithm.RSA, 1024, null, KeyFlags.CERTIFY_OTHER, 0L));
parcel.mAddUserIds.add("trix"); parcel.mAddUserIds.add("trix");
PgpKeyOperation op = new PgpKeyOperation(null); PgpKeyOperation op = new PgpKeyOperation(null);

View file

@ -33,6 +33,7 @@ import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.service.OperationResultParcel; import org.sufficientlysecure.keychain.service.OperationResultParcel;
import org.sufficientlysecure.keychain.service.OperationResults.EditKeyResult; import org.sufficientlysecure.keychain.service.OperationResults.EditKeyResult;
import org.sufficientlysecure.keychain.service.SaveKeyringParcel; import org.sufficientlysecure.keychain.service.SaveKeyringParcel;
import org.sufficientlysecure.keychain.service.SaveKeyringParcel.Algorithm;
import org.sufficientlysecure.keychain.support.KeyringTestingHelper; import org.sufficientlysecure.keychain.support.KeyringTestingHelper;
import org.sufficientlysecure.keychain.support.KeyringTestingHelper.RawPacket; import org.sufficientlysecure.keychain.support.KeyringTestingHelper.RawPacket;
import org.sufficientlysecure.keychain.util.ProgressScaler; import org.sufficientlysecure.keychain.util.ProgressScaler;
@ -85,9 +86,9 @@ public class UncachedKeyringMergeTest {
{ {
SaveKeyringParcel parcel = new SaveKeyringParcel(); SaveKeyringParcel parcel = new SaveKeyringParcel();
parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd( parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd(
PublicKeyAlgorithmTags.RSA_GENERAL, 1024, KeyFlags.CERTIFY_OTHER, 0L)); Algorithm.RSA, 1024, null, KeyFlags.CERTIFY_OTHER, 0L));
parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd( parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd(
PublicKeyAlgorithmTags.RSA_GENERAL, 1024, KeyFlags.SIGN_DATA, 0L)); Algorithm.RSA, 1024, null, KeyFlags.SIGN_DATA, 0L));
parcel.mAddUserIds.add("twi"); parcel.mAddUserIds.add("twi");
parcel.mAddUserIds.add("pink"); parcel.mAddUserIds.add("pink");
@ -104,7 +105,7 @@ public class UncachedKeyringMergeTest {
{ {
SaveKeyringParcel parcel = new SaveKeyringParcel(); SaveKeyringParcel parcel = new SaveKeyringParcel();
parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd( parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd(
PublicKeyAlgorithmTags.RSA_GENERAL, 1024, KeyFlags.CERTIFY_OTHER, 0L)); Algorithm.RSA, 1024, null, KeyFlags.CERTIFY_OTHER, 0L));
parcel.mAddUserIds.add("shy"); parcel.mAddUserIds.add("shy");
// passphrase is tested in PgpKeyOperationTest, just use empty here // passphrase is tested in PgpKeyOperationTest, just use empty here
@ -210,7 +211,7 @@ public class UncachedKeyringMergeTest {
parcel.reset(); parcel.reset();
parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd( parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd(
PublicKeyAlgorithmTags.RSA_GENERAL, 1024, KeyFlags.SIGN_DATA, 0L)); Algorithm.RSA, 1024, null, KeyFlags.SIGN_DATA, 0L));
modifiedA = op.modifySecretKeyRing(secretRing, parcel, "").getRing(); modifiedA = op.modifySecretKeyRing(secretRing, parcel, "").getRing();
modifiedB = op.modifySecretKeyRing(secretRing, parcel, "").getRing(); modifiedB = op.modifySecretKeyRing(secretRing, parcel, "").getRing();

View file

@ -31,6 +31,7 @@ import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException; import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
import org.sufficientlysecure.keychain.service.OperationResults.EditKeyResult; import org.sufficientlysecure.keychain.service.OperationResults.EditKeyResult;
import org.sufficientlysecure.keychain.service.SaveKeyringParcel; import org.sufficientlysecure.keychain.service.SaveKeyringParcel;
import org.sufficientlysecure.keychain.service.SaveKeyringParcel.Algorithm;
import org.sufficientlysecure.keychain.support.KeyringTestingHelper.RawPacket; import org.sufficientlysecure.keychain.support.KeyringTestingHelper.RawPacket;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
@ -55,11 +56,11 @@ public class UncachedKeyringTest {
SaveKeyringParcel parcel = new SaveKeyringParcel(); SaveKeyringParcel parcel = new SaveKeyringParcel();
parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd( parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd(
PublicKeyAlgorithmTags.RSA_GENERAL, 1024, KeyFlags.CERTIFY_OTHER, 0L)); Algorithm.RSA, 1024, null, KeyFlags.CERTIFY_OTHER, 0L));
parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd( parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd(
PublicKeyAlgorithmTags.RSA_GENERAL, 1024, KeyFlags.SIGN_DATA, 0L)); Algorithm.RSA, 1024, null, KeyFlags.SIGN_DATA, 0L));
parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd( parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd(
PublicKeyAlgorithmTags.RSA_GENERAL, 1024, KeyFlags.ENCRYPT_COMMS, 0L)); Algorithm.RSA, 1024, null, KeyFlags.ENCRYPT_COMMS, 0L));
parcel.mAddUserIds.add("twi"); parcel.mAddUserIds.add("twi");
parcel.mAddUserIds.add("pink"); parcel.mAddUserIds.add("pink");

View file

@ -283,7 +283,7 @@ public class HkpKeyserver extends Keyserver {
entry.setBitStrength(Integer.parseInt(matcher.group(3))); entry.setBitStrength(Integer.parseInt(matcher.group(3)));
final int algorithmId = Integer.decode(matcher.group(2)); 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 // group 1 contains the full fingerprint (v4) or the long key id if available
// see http://bit.ly/1d4bxbk and http://bit.ly/1gD1wwr // see http://bit.ly/1d4bxbk and http://bit.ly/1gD1wwr

View file

@ -39,7 +39,8 @@ public class ImportKeysListEntry implements Serializable, Parcelable {
private boolean mExpired; private boolean mExpired;
private Date mDate; // TODO: not displayed private Date mDate; // TODO: not displayed
private String mFingerprintHex; private String mFingerprintHex;
private int mBitStrength; private Integer mBitStrength;
private String mCurveOid;
private String mAlgorithm; private String mAlgorithm;
private boolean mSecretKey; private boolean mSecretKey;
private String mPrimaryUserId; private String mPrimaryUserId;
@ -162,10 +163,14 @@ public class ImportKeysListEntry implements Serializable, Parcelable {
this.mFingerprintHex = fingerprintHex; this.mFingerprintHex = fingerprintHex;
} }
public int getBitStrength() { public Integer getBitStrength() {
return mBitStrength; return mBitStrength;
} }
public String getCurveOid() {
return mCurveOid;
}
public void setBitStrength(int bitStrength) { public void setBitStrength(int bitStrength) {
this.mBitStrength = bitStrength; this.mBitStrength = bitStrength;
} }
@ -258,13 +263,15 @@ public class ImportKeysListEntry implements Serializable, Parcelable {
mPrimaryUserId = mUserIds.get(0); mPrimaryUserId = mUserIds.get(0);
} }
this.mKeyId = key.getKeyId(); mKeyId = key.getKeyId();
this.mKeyIdHex = PgpKeyHelper.convertKeyIdToHex(mKeyId); mKeyIdHex = PgpKeyHelper.convertKeyIdToHex(mKeyId);
this.mRevoked = key.isRevoked(); mRevoked = key.isRevoked();
this.mFingerprintHex = PgpKeyHelper.convertFingerprintToHex(key.getFingerprint()); mFingerprintHex = PgpKeyHelper.convertFingerprintToHex(key.getFingerprint());
this.mBitStrength = key.getBitStrength(); mBitStrength = key.getBitStrength();
mCurveOid = key.getCurveOid();
final int algorithm = key.getAlgorithm(); final int algorithm = key.getAlgorithm();
this.mAlgorithm = PgpKeyHelper.getAlgorithmInfo(context, algorithm); mAlgorithm = PgpKeyHelper.getAlgorithmInfo(context, algorithm, mBitStrength, mCurveOid);
} }
} }

View file

@ -75,7 +75,7 @@ public class KeybaseKeyserver extends Keyserver {
entry.setExtraData(username); entry.setExtraData(username);
final int algorithmId = match.getAlgorithmId(); final int algorithmId = match.getAlgorithmId();
entry.setAlgorithm(PgpKeyHelper.getAlgorithmInfo(algorithmId)); entry.setAlgorithm(PgpKeyHelper.getAlgorithmInfo(algorithmId, null, null));
final int bitStrength = match.getBitStrength(); final int bitStrength = match.getBitStrength();
entry.setBitStrength(bitStrength); entry.setBitStrength(bitStrength);

View file

@ -523,13 +523,7 @@ public class PgpDecryptVerify {
// update signature buffer if signature is also present // update signature buffer if signature is also present
if (signature != null) { if (signature != null) {
try { signature.update(buffer, 0, length);
signature.update(buffer, 0, length);
} catch (SignatureException e) {
Log.e(Constants.TAG, "SignatureException -> Not a valid signature!", e);
signatureResultBuilder.validSignature(false);
signature = null;
}
} }
alreadyWritten += length; alreadyWritten += length;

View file

@ -24,10 +24,16 @@ import android.text.Spannable;
import android.text.SpannableStringBuilder; import android.text.SpannableStringBuilder;
import android.text.style.ForegroundColorSpan; 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.bcpg.PublicKeyAlgorithmTags;
import org.spongycastle.util.encoders.Hex; import org.spongycastle.util.encoders.Hex;
import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R; 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 org.sufficientlysecure.keychain.util.Log;
import java.security.DigestException; import java.security.DigestException;
@ -37,18 +43,14 @@ import java.util.Locale;
public class PgpKeyHelper { public class PgpKeyHelper {
public static String getAlgorithmInfo(int algorithm) { public static String getAlgorithmInfo(int algorithm, Integer keySize, String oid) {
return getAlgorithmInfo(null, algorithm, 0); return getAlgorithmInfo(null, algorithm, keySize, oid);
}
public static String getAlgorithmInfo(Context context, int algorithm) {
return getAlgorithmInfo(context, algorithm, 0);
} }
/** /**
* Based on <a href="http://tools.ietf.org/html/rfc2440#section-9.1">OpenPGP Message Format</a> * 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; String algorithmStr;
switch (algorithm) { switch (algorithm) {
@ -69,10 +71,19 @@ public class PgpKeyHelper {
break; break;
} }
case PublicKeyAlgorithmTags.ECDSA: case PublicKeyAlgorithmTags.ECDSA: {
if (oid == null) {
return "ECDSA";
}
String oidName = PgpKeyHelper.getCurveInfo(context, oid);
return "ECDSA (" + oidName + ")";
}
case PublicKeyAlgorithmTags.ECDH: { case PublicKeyAlgorithmTags.ECDH: {
algorithmStr = "ECC"; if (oid == null) {
break; return "ECDH";
}
String oidName = PgpKeyHelper.getCurveInfo(context, oid);
return "ECDH (" + oidName + ")";
} }
default: { default: {
@ -90,6 +101,106 @@ public class PgpKeyHelper {
return algorithmStr; 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) * Converts fingerprint to hex (optional: with whitespaces after 4 characters)
* <p/> * <p/>

View file

@ -20,12 +20,12 @@ package org.sufficientlysecure.keychain.pgp;
import org.spongycastle.bcpg.CompressionAlgorithmTags; import org.spongycastle.bcpg.CompressionAlgorithmTags;
import org.spongycastle.bcpg.HashAlgorithmTags; import org.spongycastle.bcpg.HashAlgorithmTags;
import org.spongycastle.bcpg.PublicKeyAlgorithmTags;
import org.spongycastle.bcpg.SymmetricKeyAlgorithmTags; import org.spongycastle.bcpg.SymmetricKeyAlgorithmTags;
import org.spongycastle.bcpg.sig.Features; import org.spongycastle.bcpg.sig.Features;
import org.spongycastle.bcpg.sig.KeyFlags; import org.spongycastle.bcpg.sig.KeyFlags;
import org.spongycastle.jce.spec.ElGamalParameterSpec; import org.spongycastle.jce.spec.ElGamalParameterSpec;
import org.spongycastle.openpgp.PGPException; import org.spongycastle.openpgp.PGPException;
import org.spongycastle.openpgp.PGPKeyFlags;
import org.spongycastle.openpgp.PGPKeyPair; import org.spongycastle.openpgp.PGPKeyPair;
import org.spongycastle.openpgp.PGPPrivateKey; import org.spongycastle.openpgp.PGPPrivateKey;
import org.spongycastle.openpgp.PGPPublicKey; 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.OperationResultParcel.OperationLog;
import org.sufficientlysecure.keychain.service.OperationResults.EditKeyResult; import org.sufficientlysecure.keychain.service.OperationResults.EditKeyResult;
import org.sufficientlysecure.keychain.service.SaveKeyringParcel; 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.service.SaveKeyringParcel.SubkeyAdd;
import org.sufficientlysecure.keychain.util.IterableIterator; import org.sufficientlysecure.keychain.util.IterableIterator;
import org.sufficientlysecure.keychain.util.Log; import org.sufficientlysecure.keychain.util.Log;
@ -66,6 +68,7 @@ import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException; import java.security.NoSuchProviderException;
import java.security.SecureRandom; import java.security.SecureRandom;
import java.security.SignatureException; import java.security.SignatureException;
import java.security.spec.ECGenParameterSpec;
import java.util.Arrays; import java.util.Arrays;
import java.util.Date; import java.util.Date;
import java.util.Iterator; import java.util.Iterator;
@ -155,31 +158,65 @@ public class PgpKeyOperation {
mProgress.peek().setProgress(message, current, 100); 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. */ /** 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 { try {
if (keySize < 512) { // Some safety checks
log.add(LogLevel.ERROR, LogType.MSG_CR_ERROR_KEYSIZE_512, indent); if (add.mAlgorithm == Algorithm.ECDH || add.mAlgorithm == Algorithm.ECDSA) {
return null; 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; int algorithm;
KeyPairGenerator keyGen; KeyPairGenerator keyGen;
switch (algorithmChoice) { switch (add.mAlgorithm) {
case PublicKeyAlgorithmTags.DSA: { 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); progress(R.string.progress_generating_dsa, 30);
keyGen = KeyPairGenerator.getInstance("DSA", Constants.BOUNCY_CASTLE_PROVIDER_NAME); keyGen = KeyPairGenerator.getInstance("DSA", Constants.BOUNCY_CASTLE_PROVIDER_NAME);
keyGen.initialize(keySize, new SecureRandom()); keyGen.initialize(add.mKeySize, new SecureRandom());
algorithm = PGPPublicKey.DSA; algorithm = PGPPublicKey.DSA;
break; 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); progress(R.string.progress_generating_elgamal, 30);
keyGen = KeyPairGenerator.getInstance("ElGamal", Constants.BOUNCY_CASTLE_PROVIDER_NAME); 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"); BigInteger g = new BigInteger("2");
ElGamalParameterSpec elParams = new ElGamalParameterSpec(p, g); ElGamalParameterSpec elParams = new ElGamalParameterSpec(p, g);
@ -189,15 +226,44 @@ public class PgpKeyOperation {
break; break;
} }
case PublicKeyAlgorithmTags.RSA_GENERAL: { case RSA: {
progress(R.string.progress_generating_rsa, 30); progress(R.string.progress_generating_rsa, 30);
keyGen = KeyPairGenerator.getInstance("RSA", Constants.BOUNCY_CASTLE_PROVIDER_NAME); keyGen = KeyPairGenerator.getInstance("RSA", Constants.BOUNCY_CASTLE_PROVIDER_NAME);
keyGen.initialize(keySize, new SecureRandom()); keyGen.initialize(add.mKeySize, new SecureRandom());
algorithm = PGPPublicKey.RSA_GENERAL; algorithm = PGPPublicKey.RSA_GENERAL;
break; 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: { default: {
log.add(LogLevel.ERROR, LogType.MSG_CR_ERROR_UNKNOWN_ALGO, indent); log.add(LogLevel.ERROR, LogType.MSG_CR_ERROR_UNKNOWN_ALGO, indent);
return null; return null;
@ -210,7 +276,8 @@ public class PgpKeyOperation {
} catch(NoSuchProviderException e) { } catch(NoSuchProviderException e) {
throw new RuntimeException(e); throw new RuntimeException(e);
} catch(NoSuchAlgorithmException e) { } catch(NoSuchAlgorithmException e) {
throw new RuntimeException(e); log.add(LogLevel.ERROR, LogType.MSG_CR_ERROR_UNKNOWN_ALGO, indent);
return null;
} catch(InvalidAlgorithmParameterException e) { } catch(InvalidAlgorithmParameterException e) {
throw new RuntimeException(e); throw new RuntimeException(e);
} catch(PGPException e) { } catch(PGPException e) {
@ -252,13 +319,8 @@ public class PgpKeyOperation {
return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null); 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); subProgressPush(10, 30);
PGPKeyPair keyPair = createKey(add.mAlgorithm, add.mKeysize, log, indent); PGPKeyPair keyPair = createKey(add, log, indent);
subProgressPop(); subProgressPop();
// return null if this failed (an error will already have been logged by createKey) // 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())); progress(R.string.progress_modify_subkeyadd, (i-1) * (100 / saveParcel.mAddSubKeys.size()));
SaveKeyringParcel.SubkeyAdd add = saveParcel.mAddSubKeys.get(i); SaveKeyringParcel.SubkeyAdd add = saveParcel.mAddSubKeys.get(i);
log.add(LogLevel.INFO, LogType.MSG_MF_SUBKEY_NEW, indent, Integer.toString(add.mKeysize), log.add(LogLevel.INFO, LogType.MSG_MF_SUBKEY_NEW, indent,
PgpKeyHelper.getAlgorithmInfo(add.mAlgorithm) ); PgpKeyHelper.getAlgorithmInfo(add.mAlgorithm, add.mKeySize, add.mCurve) );
if (add.mExpiry == null) { if (add.mExpiry == null) {
log.add(LogLevel.ERROR, LogType.MSG_MF_ERROR_NULL_EXPIRY, indent +1); 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-1) * (100 / saveParcel.mAddSubKeys.size()),
i * (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(); subProgressPop();
if (keyPair == null) { if (keyPair == null) {
log.add(LogLevel.ERROR, LogType.MSG_MF_ERROR_PGP, indent +1); log.add(LogLevel.ERROR, LogType.MSG_MF_ERROR_PGP, indent +1);

View file

@ -58,7 +58,8 @@ import java.util.TreeSet;
* This class and its relatives UncachedPublicKey and UncachedSecretKey are * This class and its relatives UncachedPublicKey and UncachedSecretKey are
* used to move around pgp key rings in non crypto related (UI, mostly) code. * 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, * 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 * This class is also special in that it can hold either the PGPPublicKeyRing
* or PGPSecretKeyRing derivate of the PGPKeyRing class, since these are * or PGPSecretKeyRing derivate of the PGPKeyRing class, since these are
@ -605,7 +606,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())) { if (selfCert != null && cert.getCreationTime().before(selfCert.getCreationTime())) {
log.add(LogLevel.DEBUG, LogType.MSG_KC_SUB_DUP, indent); log.add(LogLevel.DEBUG, LogType.MSG_KC_SUB_DUP, indent);
redundantCerts += 1; redundantCerts += 1;

View file

@ -18,6 +18,10 @@
package org.sufficientlysecure.keychain.pgp; 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.bcpg.sig.KeyFlags;
import org.spongycastle.openpgp.PGPPublicKey; import org.spongycastle.openpgp.PGPPublicKey;
import org.spongycastle.openpgp.PGPSignature; import org.spongycastle.openpgp.PGPSignature;
@ -94,10 +98,23 @@ public class UncachedPublicKey {
return mPublicKey.getAlgorithm(); return mPublicKey.getAlgorithm();
} }
public int getBitStrength() { public Integer getBitStrength() {
if (isEC()) {
return null;
}
return mPublicKey.getBitStrength(); 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. /** 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) * This is an expensive operation, since potentially a lot of certificates (and revocations)
@ -186,6 +203,10 @@ public class UncachedPublicKey {
return getAlgorithm() == PGPPublicKey.DSA; return getAlgorithm() == PGPPublicKey.DSA;
} }
public boolean isEC() {
return getAlgorithm() == PGPPublicKey.ECDH || getAlgorithm() == PGPPublicKey.ECDSA;
}
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
// TODO make this safe // TODO make this safe
public int getKeyUsage() { public int getKeyUsage() {

View file

@ -96,9 +96,6 @@ public class WrappedSignature {
} catch (PGPException e) { } catch (PGPException e) {
// no matter // no matter
Log.e(Constants.TAG, "exception reading embedded signatures", e); Log.e(Constants.TAG, "exception reading embedded signatures", e);
} catch (IOException e) {
// no matter
Log.e(Constants.TAG, "exception reading embedded signatures", e);
} }
return sigs; return sigs;
} }
@ -150,27 +147,17 @@ public class WrappedSignature {
} }
} }
public void update(byte[] data, int offset, int length) throws PgpGeneralException { public void update(byte[] data, int offset, int length) {
try { mSig.update(data, offset, length);
mSig.update(data, offset, length);
} catch(SignatureException e) {
throw new PgpGeneralException(e);
}
} }
public void update(byte data) throws PgpGeneralException { public void update(byte data) {
try { mSig.update(data);
mSig.update(data);
} catch(SignatureException e) {
throw new PgpGeneralException(e);
}
} }
public boolean verify() throws PgpGeneralException { public boolean verify() throws PgpGeneralException {
try { try {
return mSig.verify(); return mSig.verify();
} catch(SignatureException e) {
throw new PgpGeneralException(e);
} catch(PGPException e) { } catch(PGPException e) {
throw new PgpGeneralException(e); throw new PgpGeneralException(e);
} }
@ -179,8 +166,6 @@ public class WrappedSignature {
boolean verifySignature(PGPPublicKey key) throws PgpGeneralException { boolean verifySignature(PGPPublicKey key) throws PgpGeneralException {
try { try {
return mSig.verifyCertification(key); return mSig.verifyCertification(key);
} catch (SignatureException e) {
throw new PgpGeneralException("Sign!", e);
} catch (PGPException e) { } catch (PGPException e) {
throw new PgpGeneralException("Error!", e); throw new PgpGeneralException("Error!", e);
} }
@ -189,8 +174,6 @@ public class WrappedSignature {
boolean verifySignature(PGPPublicKey masterKey, PGPPublicKey subKey) throws PgpGeneralException { boolean verifySignature(PGPPublicKey masterKey, PGPPublicKey subKey) throws PgpGeneralException {
try { try {
return mSig.verifyCertification(masterKey, subKey); return mSig.verifyCertification(masterKey, subKey);
} catch (SignatureException e) {
throw new PgpGeneralException("Sign!", e);
} catch (PGPException e) { } catch (PGPException e) {
throw new PgpGeneralException("Error!", e); throw new PgpGeneralException("Error!", e);
} }
@ -199,8 +182,6 @@ public class WrappedSignature {
boolean verifySignature(PGPPublicKey key, String uid) throws PgpGeneralException { boolean verifySignature(PGPPublicKey key, String uid) throws PgpGeneralException {
try { try {
return mSig.verifyCertification(uid, key); return mSig.verifyCertification(uid, key);
} catch (SignatureException e) {
throw new PgpGeneralException("Error!", e);
} catch (PGPException e) { } catch (PGPException e) {
throw new PgpGeneralException("Error!", e); throw new PgpGeneralException("Error!", e);
} }

View file

@ -39,6 +39,7 @@ public class KeychainContract {
String FINGERPRINT = "fingerprint"; String FINGERPRINT = "fingerprint";
String KEY_SIZE = "key_size"; String KEY_SIZE = "key_size";
String KEY_CURVE_OID = "key_curve_oid";
String CAN_SIGN = "can_sign"; String CAN_SIGN = "can_sign";
String CAN_ENCRYPT = "can_encrypt"; String CAN_ENCRYPT = "can_encrypt";
String CAN_CERTIFY = "can_certify"; String CAN_CERTIFY = "can_certify";

View file

@ -52,7 +52,7 @@ import java.io.IOException;
*/ */
public class KeychainDatabase extends SQLiteOpenHelper { public class KeychainDatabase extends SQLiteOpenHelper {
private static final String DATABASE_NAME = "openkeychain.db"; 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; static Boolean apgHack = false;
public interface Tables { public interface Tables {
@ -86,6 +86,7 @@ public class KeychainDatabase extends SQLiteOpenHelper {
+ KeysColumns.KEY_ID + " INTEGER, " + KeysColumns.KEY_ID + " INTEGER, "
+ KeysColumns.KEY_SIZE + " INTEGER, " + KeysColumns.KEY_SIZE + " INTEGER, "
+ KeysColumns.KEY_CURVE_OID + " TEXT, "
+ KeysColumns.ALGORITHM + " INTEGER, " + KeysColumns.ALGORITHM + " INTEGER, "
+ KeysColumns.FINGERPRINT + " BLOB, " + KeysColumns.FINGERPRINT + " BLOB, "
@ -202,13 +203,20 @@ public class KeychainDatabase extends SQLiteOpenHelper {
@Override @Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
if (oldVersion == 1) { // add has_secret for all who are upgrading from a beta version
// add has_secret for all who are upgrading from a beta version switch (oldVersion) {
try { case 1:
db.execSQL("ALTER TABLE keys ADD COLUMN has_secret BOOLEAN"); try {
} catch (Exception e) { db.execSQL("ALTER TABLE keys ADD COLUMN has_secret BOOLEAN");
// never mind, the column probably already existed } 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
}
} }
} }

View file

@ -246,6 +246,7 @@ public class KeychainProvider extends ContentProvider {
projectionMap.put(KeyRings.MASTER_KEY_ID, Tables.KEYS + "." + Keys.MASTER_KEY_ID); 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_ID, Tables.KEYS + "." + Keys.KEY_ID);
projectionMap.put(KeyRings.KEY_SIZE, Tables.KEYS + "." + Keys.KEY_SIZE); 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.IS_REVOKED, Tables.KEYS + "." + Keys.IS_REVOKED);
projectionMap.put(KeyRings.CAN_CERTIFY, Tables.KEYS + "." + Keys.CAN_CERTIFY); projectionMap.put(KeyRings.CAN_CERTIFY, Tables.KEYS + "." + Keys.CAN_CERTIFY);
projectionMap.put(KeyRings.CAN_ENCRYPT, Tables.KEYS + "." + Keys.CAN_ENCRYPT); 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.RANK, Tables.KEYS + "." + Keys.RANK);
projectionMap.put(Keys.KEY_ID, Keys.KEY_ID); projectionMap.put(Keys.KEY_ID, Keys.KEY_ID);
projectionMap.put(Keys.KEY_SIZE, Keys.KEY_SIZE); 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.IS_REVOKED, Keys.IS_REVOKED);
projectionMap.put(Keys.CAN_CERTIFY, Keys.CAN_CERTIFY); projectionMap.put(Keys.CAN_CERTIFY, Keys.CAN_CERTIFY);
projectionMap.put(Keys.CAN_ENCRYPT, Keys.CAN_ENCRYPT); projectionMap.put(Keys.CAN_ENCRYPT, Keys.CAN_ENCRYPT);

View file

@ -62,7 +62,6 @@ import org.sufficientlysecure.keychain.util.FileImportCache;
import org.sufficientlysecure.keychain.util.IterableIterator; import org.sufficientlysecure.keychain.util.IterableIterator;
import org.sufficientlysecure.keychain.util.Log; import org.sufficientlysecure.keychain.util.Log;
import org.sufficientlysecure.keychain.util.ProgressFixedScaler; import org.sufficientlysecure.keychain.util.ProgressFixedScaler;
import org.sufficientlysecure.keychain.util.ProgressScaler;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.io.IOException; import java.io.IOException;
@ -328,6 +327,7 @@ public class ProviderHelper {
values.put(Keys.KEY_ID, key.getKeyId()); values.put(Keys.KEY_ID, key.getKeyId());
values.put(Keys.KEY_SIZE, key.getBitStrength()); values.put(Keys.KEY_SIZE, key.getBitStrength());
values.put(Keys.KEY_CURVE_OID, key.getCurveOid());
values.put(Keys.ALGORITHM, key.getAlgorithm()); values.put(Keys.ALGORITHM, key.getAlgorithm());
values.put(Keys.FINGERPRINT, key.getFingerprint()); values.put(Keys.FINGERPRINT, key.getFingerprint());

View file

@ -349,9 +349,14 @@ public class OperationResultParcel implements Parcelable {
MSG_CR_ERROR_NO_CERTIFY (R.string.msg_cr_error_no_certify), 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_NULL_EXPIRY(R.string.msg_cr_error_null_expiry),
MSG_CR_ERROR_KEYSIZE_512 (R.string.msg_cr_error_keysize_512), 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_UNKNOWN_ALGO (R.string.msg_cr_error_unknown_algo),
MSG_CR_ERROR_INTERNAL_PGP (R.string.msg_cr_error_internal_pgp), 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 // secret key modify
MSG_MF (R.string.msg_mr), MSG_MF (R.string.msg_mr),

View file

@ -80,14 +80,16 @@ public class SaveKeyringParcel implements Parcelable {
// performance gain for using Parcelable here would probably be negligible, // performance gain for using Parcelable here would probably be negligible,
// use Serializable instead. // use Serializable instead.
public static class SubkeyAdd implements Serializable { public static class SubkeyAdd implements Serializable {
public int mAlgorithm; public Algorithm mAlgorithm;
public int mKeysize; public Integer mKeySize;
public Curve mCurve;
public int mFlags; public int mFlags;
public Long mExpiry; 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; mAlgorithm = algorithm;
mKeysize = keysize; mKeySize = keySize;
mCurve = curve;
mFlags = flags; mFlags = flags;
mExpiry = expiry; mExpiry = expiry;
} }
@ -95,7 +97,8 @@ public class SaveKeyringParcel implements Parcelable {
@Override @Override
public String toString() { public String toString() {
String out = "mAlgorithm: " + mAlgorithm + ", "; String out = "mAlgorithm: " + mAlgorithm + ", ";
out += "mKeysize: " + mKeysize + ", "; out += "mKeySize: " + mKeySize + ", ";
out += "mCurve: " + mCurve + ", ";
out += "mFlags: " + mFlags; out += "mFlags: " + mFlags;
out += "mExpiry: " + mExpiry; out += "mExpiry: " + mExpiry;
@ -214,4 +217,20 @@ public class SaveKeyringParcel implements Parcelable {
return out; 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
}
} }

View file

@ -43,6 +43,7 @@ import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler;
import org.sufficientlysecure.keychain.service.OperationResultParcel; import org.sufficientlysecure.keychain.service.OperationResultParcel;
import org.sufficientlysecure.keychain.service.OperationResults; import org.sufficientlysecure.keychain.service.OperationResults;
import org.sufficientlysecure.keychain.service.SaveKeyringParcel; import org.sufficientlysecure.keychain.service.SaveKeyringParcel;
import org.sufficientlysecure.keychain.service.SaveKeyringParcel.Algorithm;
import org.sufficientlysecure.keychain.util.Log; import org.sufficientlysecure.keychain.util.Log;
import org.sufficientlysecure.keychain.util.Notify; import org.sufficientlysecure.keychain.util.Notify;
@ -165,9 +166,12 @@ public class CreateKeyFinalFragment extends Fragment {
Bundle data = new Bundle(); Bundle data = new Bundle();
SaveKeyringParcel parcel = new SaveKeyringParcel(); SaveKeyringParcel parcel = new SaveKeyringParcel();
parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd(PublicKeyAlgorithmTags.RSA_GENERAL, 4096, KeyFlags.CERTIFY_OTHER, 0L)); parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd(
parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd(PublicKeyAlgorithmTags.RSA_GENERAL, 4096, KeyFlags.SIGN_DATA, 0L)); Algorithm.RSA, 4096, null, KeyFlags.CERTIFY_OTHER, 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.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); String userId = KeyRing.createUserId(mName, mEmail, null);
parcel.mAddUserIds.add(userId); parcel.mAddUserIds.add(userId);
parcel.mChangePrimaryUserId = userId; parcel.mChangePrimaryUserId = userId;

View file

@ -167,7 +167,7 @@ public class ViewCertActivity extends ActionBarActivity
mStatus.setTextColor(getResources().getColor(R.color.black)); 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); mAlgorithm.setText(algorithmStr);
mRowReason.setVisibility(View.GONE); mRowReason.setVisibility(View.GONE);

View file

@ -155,8 +155,8 @@ public class ImportKeysAdapter extends ArrayAdapter<ImportKeysListEntry> {
// don't show full fingerprint on key import // don't show full fingerprint on key import
holder.fingerprint.setVisibility(View.GONE); holder.fingerprint.setVisibility(View.GONE);
if (entry.getBitStrength() != 0 && entry.getAlgorithm() != null) { if (entry.getAlgorithm() != null) {
holder.algorithm.setText("" + entry.getBitStrength() + "/" + entry.getAlgorithm()); holder.algorithm.setText(entry.getAlgorithm());
holder.algorithm.setVisibility(View.VISIBLE); holder.algorithm.setVisibility(View.VISIBLE);
} else { } else {
holder.algorithm.setVisibility(View.INVISIBLE); holder.algorithm.setVisibility(View.INVISIBLE);

View file

@ -52,6 +52,7 @@ public class SubkeysAdapter extends CursorAdapter {
Keys.RANK, Keys.RANK,
Keys.ALGORITHM, Keys.ALGORITHM,
Keys.KEY_SIZE, Keys.KEY_SIZE,
Keys.KEY_CURVE_OID,
Keys.HAS_SECRET, Keys.HAS_SECRET,
Keys.CAN_CERTIFY, Keys.CAN_CERTIFY,
Keys.CAN_ENCRYPT, Keys.CAN_ENCRYPT,
@ -66,14 +67,15 @@ public class SubkeysAdapter extends CursorAdapter {
private static final int INDEX_RANK = 2; private static final int INDEX_RANK = 2;
private static final int INDEX_ALGORITHM = 3; private static final int INDEX_ALGORITHM = 3;
private static final int INDEX_KEY_SIZE = 4; private static final int INDEX_KEY_SIZE = 4;
private static final int INDEX_HAS_SECRET = 5; private static final int INDEX_KEY_CURVE_OID = 5;
private static final int INDEX_CAN_CERTIFY = 6; private static final int INDEX_HAS_SECRET = 6;
private static final int INDEX_CAN_ENCRYPT = 7; private static final int INDEX_CAN_CERTIFY = 7;
private static final int INDEX_CAN_SIGN = 8; private static final int INDEX_CAN_ENCRYPT = 8;
private static final int INDEX_IS_REVOKED = 9; private static final int INDEX_CAN_SIGN = 9;
private static final int INDEX_CREATION = 10; private static final int INDEX_IS_REVOKED = 10;
private static final int INDEX_EXPIRY = 11; private static final int INDEX_CREATION = 11;
private static final int INDEX_FINGERPRINT = 12; private static final int INDEX_EXPIRY = 12;
private static final int INDEX_FINGERPRINT = 13;
public SubkeysAdapter(Context context, Cursor c, int flags, public SubkeysAdapter(Context context, Cursor c, int flags,
SaveKeyringParcel saveKeyringParcel) { SaveKeyringParcel saveKeyringParcel) {
@ -141,7 +143,8 @@ public class SubkeysAdapter extends CursorAdapter {
String algorithmStr = PgpKeyHelper.getAlgorithmInfo( String algorithmStr = PgpKeyHelper.getAlgorithmInfo(
context, context,
cursor.getInt(INDEX_ALGORITHM), cursor.getInt(INDEX_ALGORITHM),
cursor.getInt(INDEX_KEY_SIZE) cursor.getInt(INDEX_KEY_SIZE),
cursor.getString(INDEX_KEY_CURVE_OID)
); );
vKeyId.setText(keyIdStr); vKeyId.setText(keyIdStr);

View file

@ -42,14 +42,10 @@ public class SubkeysAddedAdapter extends ArrayAdapter<SaveKeyringParcel.SubkeyAd
private LayoutInflater mInflater; private LayoutInflater mInflater;
private Activity mActivity; 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) { public SubkeysAddedAdapter(Activity activity, List<SaveKeyringParcel.SubkeyAdd> data) {
super(activity, -1, data); super(activity, -1, data);
mActivity = activity; mActivity = activity;
mInflater = (LayoutInflater) activity.getSystemService(Context.LAYOUT_INFLATER_SERVICE); mInflater = (LayoutInflater) activity.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
mData = data;
} }
static class ViewHolder { static class ViewHolder {
@ -103,7 +99,8 @@ public class SubkeysAddedAdapter extends ArrayAdapter<SaveKeyringParcel.SubkeyAd
String algorithmStr = PgpKeyHelper.getAlgorithmInfo( String algorithmStr = PgpKeyHelper.getAlgorithmInfo(
mActivity, mActivity,
holder.mModel.mAlgorithm, holder.mModel.mAlgorithm,
holder.mModel.mKeysize holder.mModel.mKeySize,
holder.mModel.mCurve
); );
holder.vKeyId.setText(R.string.edit_key_new_subkey); holder.vKeyId.setText(R.string.edit_key_new_subkey);
holder.vKeyDetails.setText(algorithmStr); holder.vKeyDetails.setText(algorithmStr);

View file

@ -20,14 +20,12 @@ package org.sufficientlysecure.keychain.ui.dialog;
import android.annotation.TargetApi; import android.annotation.TargetApi;
import android.app.AlertDialog; import android.app.AlertDialog;
import android.app.Dialog; import android.app.Dialog;
import android.content.DialogInterface;
import android.os.Build; import android.os.Build;
import android.os.Bundle; import android.os.Bundle;
import android.support.v4.app.DialogFragment; import android.support.v4.app.DialogFragment;
import android.support.v4.app.FragmentActivity; import android.support.v4.app.FragmentActivity;
import android.text.Editable; import android.text.Editable;
import android.text.TextWatcher; import android.text.TextWatcher;
import android.text.format.DateUtils;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.view.inputmethod.InputMethodManager; import android.view.inputmethod.InputMethodManager;
@ -43,16 +41,16 @@ import android.widget.TableRow;
import android.widget.TextView; import android.widget.TextView;
import android.widget.Toast; import android.widget.Toast;
import org.spongycastle.bcpg.PublicKeyAlgorithmTags;
import org.spongycastle.bcpg.sig.KeyFlags; import org.spongycastle.bcpg.sig.KeyFlags;
import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.service.SaveKeyringParcel; 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 org.sufficientlysecure.keychain.util.Choice;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Calendar; import java.util.Calendar;
import java.util.Date;
import java.util.TimeZone; import java.util.TimeZone;
public class AddSubkeyDialogFragment extends DialogFragment { public class AddSubkeyDialogFragment extends DialogFragment {
@ -69,7 +67,10 @@ public class AddSubkeyDialogFragment extends DialogFragment {
private TableRow mExpiryRow; private TableRow mExpiryRow;
private DatePicker mExpiryDatePicker; private DatePicker mExpiryDatePicker;
private Spinner mAlgorithmSpinner; private Spinner mAlgorithmSpinner;
private View mKeySizeRow;
private Spinner mKeySizeSpinner; private Spinner mKeySizeSpinner;
private View mCurveRow;
private Spinner mCurveSpinner;
private TextView mCustomKeyTextView; private TextView mCustomKeyTextView;
private EditText mCustomKeyEditText; private EditText mCustomKeyEditText;
private TextView mCustomKeyInfoTextView; private TextView mCustomKeyInfoTextView;
@ -114,6 +115,9 @@ public class AddSubkeyDialogFragment extends DialogFragment {
mExpiryDatePicker = (DatePicker) view.findViewById(R.id.add_subkey_expiry_date_picker); mExpiryDatePicker = (DatePicker) view.findViewById(R.id.add_subkey_expiry_date_picker);
mAlgorithmSpinner = (Spinner) view.findViewById(R.id.add_subkey_algorithm); mAlgorithmSpinner = (Spinner) view.findViewById(R.id.add_subkey_algorithm);
mKeySizeSpinner = (Spinner) view.findViewById(R.id.add_subkey_size); 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); mCustomKeyTextView = (TextView) view.findViewById(R.id.add_subkey_custom_key_size_label);
mCustomKeyEditText = (EditText) view.findViewById(R.id.add_subkey_custom_key_size_input); mCustomKeyEditText = (EditText) view.findViewById(R.id.add_subkey_custom_key_size_input);
mCustomKeyInfoTextView = (TextView) view.findViewById(R.id.add_subkey_custom_key_size_info); mCustomKeyInfoTextView = (TextView) view.findViewById(R.id.add_subkey_custom_key_size_info);
@ -140,24 +144,30 @@ public class AddSubkeyDialogFragment extends DialogFragment {
mExpiryDatePicker.setMinDate(minDateCal.getTime().getTime()); mExpiryDatePicker.setMinDate(minDateCal.getTime().getTime());
} }
ArrayList<Choice> choices = new ArrayList<Choice>(); {
choices.add(new Choice(PublicKeyAlgorithmTags.DSA, getResources().getString( ArrayList<Choice<Algorithm>> choices = new ArrayList<Choice<Algorithm>>();
R.string.dsa))); choices.add(new Choice<Algorithm>(Algorithm.DSA, getResources().getString(
if (!mWillBeMasterKey) { R.string.dsa)));
choices.add(new Choice(PublicKeyAlgorithmTags.ELGAMAL_ENCRYPT, getResources().getString( if (!mWillBeMasterKey) {
R.string.elgamal))); choices.add(new Choice<Algorithm>(Algorithm.ELGAMAL, getResources().getString(
} R.string.elgamal)));
choices.add(new Choice(PublicKeyAlgorithmTags.RSA_GENERAL, getResources().getString( }
R.string.rsa))); choices.add(new Choice<Algorithm>(Algorithm.RSA, getResources().getString(
ArrayAdapter<Choice> adapter = new ArrayAdapter<Choice>(context, R.string.rsa)));
android.R.layout.simple_spinner_item, choices); choices.add(new Choice<Algorithm>(Algorithm.ECDSA, getResources().getString(
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); R.string.ecdsa)));
mAlgorithmSpinner.setAdapter(adapter); choices.add(new Choice<Algorithm>(Algorithm.ECDH, getResources().getString(
// make RSA the default R.string.ecdh)));
for (int i = 0; i < choices.size(); ++i) { ArrayAdapter<Choice<Algorithm>> adapter = new ArrayAdapter<Choice<Algorithm>>(context,
if (choices.get(i).getId() == PublicKeyAlgorithmTags.RSA_GENERAL) { android.R.layout.simple_spinner_item, choices);
mAlgorithmSpinner.setSelection(i); adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
break; 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;
}
} }
} }
@ -168,6 +178,36 @@ public class AddSubkeyDialogFragment extends DialogFragment {
mKeySizeSpinner.setAdapter(keySizeAdapter); mKeySizeSpinner.setAdapter(keySizeAdapter);
mKeySizeSpinner.setSelection(1); // Default to 4096 for the key length 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); dialog.setCancelable(true);
@ -207,7 +247,7 @@ public class AddSubkeyDialogFragment extends DialogFragment {
mAlgorithmSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { mAlgorithmSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
@Override @Override
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) { public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
updateUiForAlgorithm(((Choice) parent.getSelectedItem()).getId()); updateUiForAlgorithm(((Choice<Algorithm>) parent.getSelectedItem()).getId());
setCustomKeyVisibility(); setCustomKeyVisibility();
setOkButtonAvailability(alertDialog); setOkButtonAvailability(alertDialog);
@ -237,11 +277,16 @@ public class AddSubkeyDialogFragment extends DialogFragment {
return; return;
} }
// dismiss only if at least one flag is selected Algorithm algorithm = ((Choice<Algorithm>) mAlgorithmSpinner.getSelectedItem()).getId();
dismiss(); Curve curve = null;
Integer keySize = null;
Choice newKeyAlgorithmChoice = (Choice) mAlgorithmSpinner.getSelectedItem(); // For EC keys, add a curve
int newKeySize = getProperKeyLength(newKeyAlgorithmChoice.getId(), getSelectedKeyLength()); 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; int flags = 0;
if (mFlagCertify.isChecked()) { if (mFlagCertify.isChecked()) {
@ -272,12 +317,12 @@ public class AddSubkeyDialogFragment extends DialogFragment {
} }
SaveKeyringParcel.SubkeyAdd newSubkey = new SaveKeyringParcel.SubkeyAdd( SaveKeyringParcel.SubkeyAdd newSubkey = new SaveKeyringParcel.SubkeyAdd(
newKeyAlgorithmChoice.getId(), algorithm, keySize, curve, flags, expiry
newKeySize,
flags,
expiry
); );
mAlgorithmSelectedListener.onAlgorithmSelected(newSubkey); mAlgorithmSelectedListener.onAlgorithmSelected(newSubkey);
// finally, dismiss the dialogue
dismiss();
} }
}); });
negativeButton.setOnClickListener(new View.OnClickListener() { negativeButton.setOnClickListener(new View.OnClickListener() {
@ -319,16 +364,16 @@ public class AddSubkeyDialogFragment extends DialogFragment {
* @return correct key length, according to SpongyCastle specification. Returns <code>-1</code>, if key length is * @return correct key length, according to SpongyCastle specification. Returns <code>-1</code>, if key length is
* inappropriate. * inappropriate.
*/ */
private int getProperKeyLength(int algorithmId, int currentKeyLength) { private int getProperKeyLength(Algorithm algorithm, int currentKeyLength) {
final int[] elGamalSupportedLengths = {1536, 2048, 3072, 4096, 8192}; final int[] elGamalSupportedLengths = {1536, 2048, 3072, 4096, 8192};
int properKeyLength = -1; int properKeyLength = -1;
switch (algorithmId) { switch (algorithm) {
case PublicKeyAlgorithmTags.RSA_GENERAL: case RSA:
if (currentKeyLength > 1024 && currentKeyLength <= 16384) { if (currentKeyLength > 1024 && currentKeyLength <= 16384) {
properKeyLength = currentKeyLength + ((8 - (currentKeyLength % 8)) % 8); properKeyLength = currentKeyLength + ((8 - (currentKeyLength % 8)) % 8);
} }
break; break;
case PublicKeyAlgorithmTags.ELGAMAL_ENCRYPT: case ELGAMAL:
int[] elGammalKeyDiff = new int[elGamalSupportedLengths.length]; int[] elGammalKeyDiff = new int[elGamalSupportedLengths.length];
for (int i = 0; i < elGamalSupportedLengths.length; i++) { for (int i = 0; i < elGamalSupportedLengths.length; i++) {
elGammalKeyDiff[i] = Math.abs(elGamalSupportedLengths[i] - currentKeyLength); elGammalKeyDiff[i] = Math.abs(elGamalSupportedLengths[i] - currentKeyLength);
@ -343,7 +388,7 @@ public class AddSubkeyDialogFragment extends DialogFragment {
} }
properKeyLength = elGamalSupportedLengths[minimalIndex]; properKeyLength = elGamalSupportedLengths[minimalIndex];
break; break;
case PublicKeyAlgorithmTags.DSA: case DSA:
if (currentKeyLength >= 512 && currentKeyLength <= 1024) { if (currentKeyLength >= 512 && currentKeyLength <= 1024) {
properKeyLength = currentKeyLength + ((64 - (currentKeyLength % 64)) % 64); properKeyLength = currentKeyLength + ((64 - (currentKeyLength % 64)) % 64);
} }
@ -353,10 +398,10 @@ public class AddSubkeyDialogFragment extends DialogFragment {
} }
private void setOkButtonAvailability(AlertDialog alertDialog) { private void setOkButtonAvailability(AlertDialog alertDialog) {
final Choice selectedAlgorithm = (Choice) mAlgorithmSpinner.getSelectedItem(); Algorithm algorithm = ((Choice<Algorithm>) mAlgorithmSpinner.getSelectedItem()).getId();
final int selectedKeySize = getSelectedKeyLength(); //Integer.parseInt((String) mKeySizeSpinner.getSelectedItem()); boolean enabled = algorithm == Algorithm.ECDSA || algorithm == Algorithm.ECDH
final int properKeyLength = getProperKeyLength(selectedAlgorithm.getId(), selectedKeySize); || getProperKeyLength(algorithm, getSelectedKeyLength()) > 0;
alertDialog.getButton(AlertDialog.BUTTON_POSITIVE).setEnabled(properKeyLength > 0); alertDialog.getButton(AlertDialog.BUTTON_POSITIVE).setEnabled(enabled);
} }
private void setCustomKeyVisibility() { private void setCustomKeyVisibility() {
@ -372,18 +417,20 @@ public class AddSubkeyDialogFragment extends DialogFragment {
// hide keyboard after setting visibility to gone // hide keyboard after setting visibility to gone
if (visibility == View.GONE) { if (visibility == View.GONE) {
InputMethodManager imm = (InputMethodManager) InputMethodManager imm = (InputMethodManager)
getActivity().getSystemService(getActivity().INPUT_METHOD_SERVICE); getActivity().getSystemService(FragmentActivity.INPUT_METHOD_SERVICE);
imm.hideSoftInputFromWindow(mCustomKeyEditText.getWindowToken(), 0); imm.hideSoftInputFromWindow(mCustomKeyEditText.getWindowToken(), 0);
} }
} }
private void updateUiForAlgorithm(int algorithmId) { private void updateUiForAlgorithm(Algorithm algorithm) {
final ArrayAdapter<CharSequence> keySizeAdapter = (ArrayAdapter<CharSequence>) mKeySizeSpinner.getAdapter(); final ArrayAdapter<CharSequence> keySizeAdapter = (ArrayAdapter<CharSequence>) mKeySizeSpinner.getAdapter();
final Object selectedItem = mKeySizeSpinner.getSelectedItem();
keySizeAdapter.clear(); keySizeAdapter.clear();
switch (algorithmId) { switch (algorithm) {
case PublicKeyAlgorithmTags.RSA_GENERAL: case RSA:
replaceArrayAdapterContent(keySizeAdapter, R.array.rsa_key_size_spinner_values); 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)); mCustomKeyInfoTextView.setText(getResources().getString(R.string.key_size_custom_info_rsa));
// allowed flags: // allowed flags:
mFlagSign.setEnabled(true); mFlagSign.setEnabled(true);
@ -405,8 +452,11 @@ public class AddSubkeyDialogFragment extends DialogFragment {
} }
mFlagAuthenticate.setChecked(false); mFlagAuthenticate.setChecked(false);
break; break;
case PublicKeyAlgorithmTags.ELGAMAL_ENCRYPT: case ELGAMAL:
replaceArrayAdapterContent(keySizeAdapter, R.array.elgamal_key_size_spinner_values); 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 mCustomKeyInfoTextView.setText(""); // ElGamal does not support custom key length
// allowed flags: // allowed flags:
mFlagCertify.setChecked(false); mFlagCertify.setChecked(false);
@ -418,8 +468,11 @@ public class AddSubkeyDialogFragment extends DialogFragment {
mFlagAuthenticate.setChecked(false); mFlagAuthenticate.setChecked(false);
mFlagAuthenticate.setEnabled(false); mFlagAuthenticate.setEnabled(false);
break; break;
case PublicKeyAlgorithmTags.DSA: case DSA:
replaceArrayAdapterContent(keySizeAdapter, R.array.dsa_key_size_spinner_values); 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)); mCustomKeyInfoTextView.setText(getResources().getString(R.string.key_size_custom_info_dsa));
// allowed flags: // allowed flags:
mFlagCertify.setChecked(false); mFlagCertify.setChecked(false);
@ -431,16 +484,37 @@ public class AddSubkeyDialogFragment extends DialogFragment {
mFlagAuthenticate.setChecked(false); mFlagAuthenticate.setChecked(false);
mFlagAuthenticate.setEnabled(false); mFlagAuthenticate.setEnabled(false);
break; 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(); 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) @TargetApi(Build.VERSION_CODES.HONEYCOMB)

View file

@ -17,21 +17,16 @@
package org.sufficientlysecure.keychain.util; package org.sufficientlysecure.keychain.util;
public class Choice { public class Choice <E> {
private String mName; private String mName;
private int mId; private E mId;
public Choice() { public Choice(E id, String name) {
mId = -1;
mName = "";
}
public Choice(int id, String name) {
mId = id; mId = id;
mName = name; mName = name;
} }
public int getId() { public E getId() {
return mId; return mId;
} }

View file

@ -34,7 +34,7 @@
android:padding="4dp" /> android:padding="4dp" />
</TableRow> </TableRow>
<TableRow> <TableRow android:id="@+id/add_subkey_row_size">
<TextView <TextView
android:layout_width="wrap_content" android:layout_width="wrap_content"
@ -50,6 +50,24 @@
android:padding="4dp" /> android:padding="4dp" />
</TableRow> </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 <TextView
android:id="@+id/add_subkey_custom_key_size_label" android:id="@+id/add_subkey_custom_key_size_label"
android:layout_width="wrap_content" android:layout_width="wrap_content"

View file

@ -109,6 +109,7 @@
<string name="label_expiry">Expiry</string> <string name="label_expiry">Expiry</string>
<string name="label_usage">Usage</string> <string name="label_usage">Usage</string>
<string name="label_key_size">Key Size</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_main_user_id">Primary identity</string>
<string name="label_name">Name</string> <string name="label_name">Name</string>
<string name="label_comment">Comment</string> <string name="label_comment">Comment</string>
@ -156,6 +157,8 @@
<string name="dsa">DSA</string> <string name="dsa">DSA</string>
<string name="elgamal">ElGamal</string> <string name="elgamal">ElGamal</string>
<string name="rsa">RSA</string> <string name="rsa">RSA</string>
<string name="ecdh">ECDH</string>
<string name="ecdsa">ECDSA</string>
<string name="filemanager_title_open">Open…</string> <string name="filemanager_title_open">Open…</string>
<string name="warning">Warning</string> <string name="warning">Warning</string>
<string name="error">Error</string> <string name="error">Error</string>
@ -268,6 +271,8 @@
<string name="progress_generating_rsa">generating new RSA key…</string> <string name="progress_generating_rsa">generating new RSA key…</string>
<string name="progress_generating_dsa">generating new DSA 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_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> <string name="progress_modify">modifying keyring…</string>
@ -324,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_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> <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 --> <!-- compression -->
<string name="compression_fast">fast</string> <string name="compression_fast">fast</string>
<string name="compression_very_slow">very slow</string> <string name="compression_very_slow">very slow</string>
@ -638,9 +653,14 @@
<string name="msg_cr_error_no_certify">Master key must have certify flag!</string> <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_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_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_internal_pgp">Internal PGP error!</string>
<string name="msg_cr_error_unknown_algo">Bad algorithm choice!</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_master_elgamal">Master key must not be of type ElGamal!</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 --> <!-- modifySecretKeyRing -->
<string name="msg_mr">Modifying keyring %s</string> <string name="msg_mr">Modifying keyring %s</string>
@ -665,7 +685,7 @@
<string name="msg_mf_primary_new">Generating new certificate for new primary user id</string> <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_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_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_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_error_past_expiry">Expiry date cannot be in the past!</string>
<string name="msg_mf_subkey_revoke">Revoking subkey %s</string> <string name="msg_mf_subkey_revoke">Revoking subkey %s</string>

2
extern/spongycastle vendored

@ -1 +1 @@
Subproject commit 918340cbc6d5396764bfe94fdba07aca11cd8168 Subproject commit 526d23382e53ce46a17a5efd9c23d884125e32ce