SmartPGP/patches/open-keychain/0001-Support-of-OpenPGP-card-v3.patch
2016-10-25 16:36:34 +02:00

2043 lines
97 KiB
Diff

From d9952df66529b9dda611c4433ba09dfff3509633 Mon Sep 17 00:00:00 2001
From: Arnaud Fontaine <arnaud.fontaine@ssi.gouv.fr>
Date: Tue, 25 Oct 2016 15:23:30 +0200
Subject: [PATCH 1/3] Support of OpenPGP card v3
---
.../operations/results/OperationResult.java | 1 +
.../keychain/pgp/CanonicalizedPublicKey.java | 71 ++++++
.../keychain/pgp/CanonicalizedSecretKey.java | 23 ++
.../keychain/pgp/PgpKeyOperation.java | 54 ++++-
.../keychain/securitytoken/ECKeyFormat.java | 83 +++++++
.../keychain/securitytoken/KeyFormat.java | 114 +++++----
.../securitytoken/OpenPgpCapabilities.java | 28 ++-
.../keychain/securitytoken/RSAKeyFormat.java | 89 +++++++
.../securitytoken/SecurityTokenHelper.java | 263 ++++++++++++++++++---
.../keychain/ui/CreateKeyActivity.java | 10 +-
.../keychain/ui/CreateKeyFinalFragment.java | 9 +-
.../ui/CreateSecurityTokenAlgorithmFragment.java | 193 +++++++++++++++
.../ui/CreateSecurityTokenBlankFragment.java | 6 +-
.../ui/CreateSecurityTokenImportResetFragment.java | 4 +
.../ui/CreateSecurityTokenPinFragment.java | 2 +-
.../keychain/ui/EditKeyFragment.java | 40 +++-
.../ui/SecurityTokenOperationActivity.java | 12 +-
.../keychain/ui/ViewKeyActivity.java | 13 +-
.../keychain/ui/ViewKeyAdvSubkeysFragment.java | 40 +++-
.../keychain/ui/ViewKeySecurityTokenFragment.java | 6 +-
.../keychain/ui/adapter/SubkeysAdapter.java | 5 +
.../ui/base/BaseSecurityTokenActivity.java | 2 +-
.../keychain/util/SecurityTokenUtils.java | 112 ++++++++-
.../layout/create_yubi_key_algorithm_fragment.xml | 121 ++++++++++
OpenKeychain/src/main/res/values/strings.xml | 7 +
.../keychain/pgp/PgpKeyOperationTest.java | 2 +
.../securitytoken/SecurityTokenUtilsTest.java | 6 +-
27 files changed, 1153 insertions(+), 163 deletions(-)
create mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/ECKeyFormat.java
create mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/RSAKeyFormat.java
create mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateSecurityTokenAlgorithmFragment.java
create mode 100644 OpenKeychain/src/main/res/layout/create_yubi_key_algorithm_fragment.xml
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/OperationResult.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/OperationResult.java
index be736d7..5cf6415 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/OperationResult.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/OperationResult.java
@@ -566,6 +566,7 @@ public abstract class OperationResult implements Parcelable {
MSG_MF_ERROR_INVALID_FLAGS_FOR_KEYTOCARD(LogLevel.ERROR, R.string.msg_mf_error_invalid_flags_for_keytocard),
MSG_MF_ERROR_BAD_SECURITY_TOKEN_ALGO(LogLevel.ERROR, R.string.edit_key_error_bad_security_token_algo),
MSG_MF_ERROR_BAD_SECURITY_TOKEN_SIZE(LogLevel.ERROR, R.string.edit_key_error_bad_security_token_size),
+ MSG_MF_ERROR_BAD_SECURITY_TOKEN_CURVE(LogLevel.ERROR, R.string.edit_key_error_bad_security_token_curve),
MSG_MF_ERROR_BAD_SECURITY_TOKEN_STRIPPED(LogLevel.ERROR, R.string.edit_key_error_bad_security_token_stripped),
MSG_MF_MASTER (LogLevel.DEBUG, R.string.msg_mf_master),
MSG_MF_PASSPHRASE (LogLevel.INFO, R.string.msg_mf_passphrase),
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedPublicKey.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedPublicKey.java
index 177fe15..95be5bb 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedPublicKey.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedPublicKey.java
@@ -18,14 +18,27 @@
package org.sufficientlysecure.keychain.pgp;
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.nist.NISTObjectIdentifiers;
+import org.bouncycastle.bcpg.ECDHPublicBCPGKey;
+import org.bouncycastle.bcpg.HashAlgorithmTags;
+import org.bouncycastle.bcpg.SymmetricKeyAlgorithmTags;
import org.bouncycastle.bcpg.sig.KeyFlags;
+import org.bouncycastle.openpgp.PGPException;
import org.bouncycastle.openpgp.PGPPublicKey;
import org.bouncycastle.openpgp.PGPSignature;
+import org.bouncycastle.openpgp.operator.KeyFingerPrintCalculator;
+import org.bouncycastle.openpgp.operator.RFC6637Utils;
+import org.bouncycastle.openpgp.operator.jcajce.JcaPGPKeyConverter;
import org.bouncycastle.openpgp.operator.jcajce.JcePublicKeyKeyEncryptionMethodGenerator;
import org.sufficientlysecure.keychain.Constants;
+import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
import org.sufficientlysecure.keychain.util.IterableIterator;
import org.sufficientlysecure.keychain.util.Log;
+import java.io.IOException;
+import java.security.PublicKey;
+import java.security.interfaces.ECPublicKey;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
@@ -189,4 +202,62 @@ public class CanonicalizedPublicKey extends UncachedPublicKey {
return !isRevoked() && !isExpired();
}
+ // For use only in card export; returns the public key.
+ public ECPublicKey getECPublicKey()
+ throws PgpGeneralException {
+ JcaPGPKeyConverter keyConverter = new JcaPGPKeyConverter();
+ PublicKey retVal;
+ try {
+ retVal = keyConverter.getPublicKey(mPublicKey);
+ } catch (PGPException e) {
+ throw new PgpGeneralException("Error converting public key!", e);
+ }
+
+ return (ECPublicKey) retVal;
+ }
+
+ public ASN1ObjectIdentifier getHashAlgorithm()
+ throws PGPException {
+ if (!isEC()) {
+ throw new PGPException("Key encryption OID is valid only for EC key!");
+ }
+
+ final ECDHPublicBCPGKey eck = (ECDHPublicBCPGKey)mPublicKey.getPublicKeyPacket().getKey();
+
+ switch (eck.getHashAlgorithm()) {
+ case HashAlgorithmTags.SHA256:
+ return NISTObjectIdentifiers.id_sha256;
+ case HashAlgorithmTags.SHA384:
+ return NISTObjectIdentifiers.id_sha384;
+ case HashAlgorithmTags.SHA512:
+ return NISTObjectIdentifiers.id_sha512;
+ default:
+ throw new PGPException("Invalid hash algorithm for EC key : " + eck.getHashAlgorithm());
+ }
+ }
+
+ public int getSymmetricKeySize()
+ throws PGPException {
+ if (!isEC()) {
+ throw new PGPException("Key encryption OID is valid only for EC key!");
+ }
+
+ final ECDHPublicBCPGKey eck = (ECDHPublicBCPGKey)mPublicKey.getPublicKeyPacket().getKey();
+
+ switch (eck.getSymmetricKeyAlgorithm()) {
+ case SymmetricKeyAlgorithmTags.AES_128:
+ return 128;
+ case SymmetricKeyAlgorithmTags.AES_192:
+ return 192;
+ case SymmetricKeyAlgorithmTags.AES_256:
+ return 256;
+ default:
+ throw new PGPException("Invalid symmetric encryption algorithm for EC key : " + eck.getSymmetricKeyAlgorithm());
+ }
+ }
+
+ public byte[] createUserKeyingMaterial(KeyFingerPrintCalculator fingerPrintCalculator)
+ throws IOException, PGPException {
+ return RFC6637Utils.createUserKeyingMaterial(mPublicKey.getPublicKeyPacket(), fingerPrintCalculator);
+ }
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedSecretKey.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedSecretKey.java
index a02ff66..318c837 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedSecretKey.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedSecretKey.java
@@ -21,6 +21,7 @@ package org.sufficientlysecure.keychain.pgp;
import java.nio.ByteBuffer;
import java.security.PrivateKey;
+import java.security.interfaces.ECPrivateKey;
import java.security.interfaces.RSAPrivateCrtKey;
import java.util.Date;
import java.util.HashMap;
@@ -319,6 +320,28 @@ public class CanonicalizedSecretKey extends CanonicalizedPublicKey {
return (RSAPrivateCrtKey)retVal;
}
+ // For use only in card export; returns the secret key.
+ public ECPrivateKey getECSecretKey()
+ throws PgpGeneralException {
+ if (mPrivateKeyState == PRIVATE_KEY_STATE_LOCKED) {
+ throw new PgpGeneralException("Cannot get secret key attributes while key is locked.");
+ }
+
+ if (mPrivateKeyState == PRIVATE_KEY_STATE_DIVERT_TO_CARD) {
+ throw new PgpGeneralException("Cannot get secret key attributes of divert-to-card key.");
+ }
+
+ JcaPGPKeyConverter keyConverter = new JcaPGPKeyConverter();
+ PrivateKey retVal;
+ try {
+ retVal = keyConverter.getPrivateKey(mPrivateKey);
+ } catch (PGPException e) {
+ throw new PgpGeneralException("Error converting private key! " + e.getMessage(), e);
+ }
+
+ return (ECPrivateKey) retVal;
+ }
+
public byte[] getIv() {
return mSecretKey.getIV();
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java
index 404e072..ed5d925 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java
@@ -35,6 +35,10 @@ import java.util.Iterator;
import java.util.Stack;
import java.util.concurrent.atomic.AtomicBoolean;
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.nist.NISTNamedCurves;
+import org.bouncycastle.bcpg.ECDHPublicBCPGKey;
+import org.bouncycastle.bcpg.ECDSAPublicBCPGKey;
import org.bouncycastle.bcpg.PublicKeyAlgorithmTags;
import org.bouncycastle.bcpg.S2K;
import org.bouncycastle.bcpg.sig.Features;
@@ -1645,20 +1649,44 @@ public class PgpKeyOperation {
}
private static boolean checkSecurityTokenCompatibility(PGPSecretKey key, OperationLog log, int indent) {
- PGPPublicKey publicKey = key.getPublicKey();
- int algorithm = publicKey.getAlgorithm();
- if (algorithm != PublicKeyAlgorithmTags.RSA_ENCRYPT &&
- algorithm != PublicKeyAlgorithmTags.RSA_SIGN &&
- algorithm != PublicKeyAlgorithmTags.RSA_GENERAL) {
- log.add(LogType.MSG_MF_ERROR_BAD_SECURITY_TOKEN_ALGO, indent + 1);
- return false;
- }
- // Key size must be 2048
- int keySize = publicKey.getBitStrength();
- if (keySize != 2048) {
- log.add(LogType.MSG_MF_ERROR_BAD_SECURITY_TOKEN_SIZE, indent + 1);
- return false;
+ final PGPPublicKey publicKey = key.getPublicKey();
+ ASN1ObjectIdentifier curve;
+
+ switch (publicKey.getAlgorithm()) {
+ case PublicKeyAlgorithmTags.RSA_ENCRYPT:
+ case PublicKeyAlgorithmTags.RSA_SIGN:
+ case PublicKeyAlgorithmTags.RSA_GENERAL:
+ // Key size must be at least 2048
+ if (publicKey.getBitStrength() < 2048) {
+ log.add(LogType.MSG_MF_ERROR_BAD_SECURITY_TOKEN_SIZE, indent + 1);
+ return false;
+ }
+ break;
+
+ case PublicKeyAlgorithmTags.ECDH:
+ curve = ((ECDHPublicBCPGKey)(publicKey.getPublicKeyPacket().getKey())).getCurveOID();
+ if (!curve.equals(NISTNamedCurves.getOID("P-256")) &&
+ !curve.equals(NISTNamedCurves.getOID("P-384")) &&
+ !curve.equals(NISTNamedCurves.getOID("P-521"))) {
+ log.add(LogType.MSG_MF_ERROR_BAD_SECURITY_TOKEN_CURVE, indent + 1);
+ return false;
+ }
+ break;
+
+ case PublicKeyAlgorithmTags.ECDSA:
+ curve = ((ECDSAPublicBCPGKey)(publicKey.getPublicKeyPacket().getKey())).getCurveOID();
+ if (!curve.equals(NISTNamedCurves.getOID("P-256")) &&
+ !curve.equals(NISTNamedCurves.getOID("P-384")) &&
+ !curve.equals(NISTNamedCurves.getOID("P-521"))) {
+ log.add(LogType.MSG_MF_ERROR_BAD_SECURITY_TOKEN_CURVE, indent + 1);
+ return false;
+ }
+ break;
+
+ default:
+ log.add(LogType.MSG_MF_ERROR_BAD_SECURITY_TOKEN_ALGO, indent + 1);
+ return false;
}
// Secret key parts must be available
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/ECKeyFormat.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/ECKeyFormat.java
new file mode 100644
index 0000000..d3b4d5f
--- /dev/null
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/ECKeyFormat.java
@@ -0,0 +1,83 @@
+package org.sufficientlysecure.keychain.securitytoken;
+
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.nist.NISTNamedCurves;
+import org.bouncycastle.asn1.x9.X9ECParameters;
+import org.bouncycastle.bcpg.sig.KeyFlags;
+import org.bouncycastle.math.ec.ECCurve;
+import org.sufficientlysecure.keychain.service.SaveKeyringParcel;
+
+// 4.3.3.6 Algorithm Attributes
+public class ECKeyFormat extends KeyFormat {
+
+ private final ECAlgorithmFormat mECAlgorithmFormat;
+ private final ASN1ObjectIdentifier mECCurveOID;
+
+ public ECKeyFormat(final ASN1ObjectIdentifier ecCurveOid,
+ final ECAlgorithmFormat ecAlgorithmFormat) {
+ super(KeyFormatType.ECKeyFormatType);
+ mECAlgorithmFormat = ecAlgorithmFormat;
+ mECCurveOID = ecCurveOid;
+ }
+
+ public ECKeyFormat.ECAlgorithmFormat getAlgorithmFormat() {
+ return mECAlgorithmFormat;
+ }
+
+ public ASN1ObjectIdentifier getCurveOID() { return mECCurveOID; }
+
+ public enum ECAlgorithmFormat {
+ ECDH((byte)18, true, false),
+ ECDH_WITH_PUBKEY((byte)18, true, true),
+ ECDSA((byte)19, false, false),
+ ECDSA_WITH_PUBKEY((byte)19, false, true);
+
+ private final byte mValue;
+ private final boolean mIsECDH;
+ private final boolean mWithPubkey;
+
+ ECAlgorithmFormat(final byte value, final boolean isECDH, final boolean withPubkey) {
+ mValue = value;
+ mIsECDH = isECDH;
+ mWithPubkey = withPubkey;
+ }
+
+ public static ECKeyFormat.ECAlgorithmFormat from(final byte bFirst, final byte bLast) {
+ for (ECKeyFormat.ECAlgorithmFormat format : values()) {
+ if (format.mValue == bFirst && ((bLast == (byte)0xff) == format.isWithPubkey())) {
+ return format;
+ }
+ }
+ return null;
+ }
+
+ public final byte getValue() { return mValue; }
+ public final boolean isECDH() { return mIsECDH; }
+ public final boolean isWithPubkey() { return mWithPubkey; }
+ }
+
+ public void addToKeyring(SaveKeyringParcel keyring, int keyFlags) {
+ final X9ECParameters params = NISTNamedCurves.getByOID(mECCurveOID);
+ final ECCurve curve = params.getCurve();
+
+ SaveKeyringParcel.Algorithm algo = SaveKeyringParcel.Algorithm.ECDSA;
+ if (((keyFlags & KeyFlags.ENCRYPT_COMMS) == KeyFlags.ENCRYPT_COMMS)
+ || ((keyFlags & KeyFlags.ENCRYPT_STORAGE) == KeyFlags.ENCRYPT_STORAGE)) {
+ algo = SaveKeyringParcel.Algorithm.ECDH;
+ }
+
+ SaveKeyringParcel.Curve scurve;
+ if (mECCurveOID.equals(NISTNamedCurves.getOID("P-256"))) {
+ scurve = SaveKeyringParcel.Curve.NIST_P256;
+ } else if (mECCurveOID.equals(NISTNamedCurves.getOID("P-384"))) {
+ scurve = SaveKeyringParcel.Curve.NIST_P384;
+ } else if (mECCurveOID.equals(NISTNamedCurves.getOID("P-521"))) {
+ scurve = SaveKeyringParcel.Curve.NIST_P521;
+ } else {
+ throw new IllegalArgumentException("Unsupported curve " + mECCurveOID);
+ }
+
+ keyring.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd(algo,
+ curve.getFieldSize(), scurve, keyFlags, 0L));
+ }
+}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/KeyFormat.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/KeyFormat.java
index 3b2e93f..4a81d46 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/KeyFormat.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/KeyFormat.java
@@ -17,71 +17,83 @@
package org.sufficientlysecure.keychain.securitytoken;
-// 4.3.3.6 Algorithm Attributes
-public class KeyFormat {
- private int mAlgorithmId;
- private int mModulusLength;
- private int mExponentLength;
- private AlgorithmFormat mAlgorithmFormat;
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.nist.NISTNamedCurves;
+import org.bouncycastle.bcpg.PublicKeyAlgorithmTags;
+import org.sufficientlysecure.keychain.service.SaveKeyringParcel;
+import org.sufficientlysecure.keychain.ui.CreateSecurityTokenAlgorithmFragment;
- public KeyFormat(byte[] bytes) {
- mAlgorithmId = bytes[0];
- mModulusLength = bytes[1] << 8 | bytes[2];
- mExponentLength = bytes[3] << 8 | bytes[4];
- mAlgorithmFormat = AlgorithmFormat.from(bytes[5]);
+public abstract class KeyFormat {
- if (mAlgorithmId != 1) { // RSA
- throw new IllegalArgumentException("Unsupported Algorithm id " + mAlgorithmId);
- }
- }
+ public enum KeyFormatType {
+ RSAKeyFormatType,
+ ECKeyFormatType
+ };
- public int getAlgorithmId() {
- return mAlgorithmId;
- }
+ private final KeyFormatType mKeyFormatType;
- public int getModulusLength() {
- return mModulusLength;
+ public KeyFormat(final KeyFormatType keyFormatType) {
+ mKeyFormatType = keyFormatType;
}
- public int getExponentLength() {
- return mExponentLength;
+ public final KeyFormatType keyFormatType() {
+ return mKeyFormatType;
}
- public AlgorithmFormat getAlgorithmFormat() {
- return mAlgorithmFormat;
- }
-
- public enum AlgorithmFormat {
- STANDARD(0, false, false),
- STANDARD_WITH_MODULUS(1, false, true),
- CRT(2, true, false),
- CRT_WITH_MODULUS(3, true, true);
+ public static KeyFormat fromBytes(byte[] bytes) {
+ switch (bytes[0]) {
+ case PublicKeyAlgorithmTags.RSA_GENERAL:
+ if (bytes.length < 6) {
+ throw new IllegalArgumentException("Bad length for RSA attributes");
+ }
+ return new RSAKeyFormat(bytes[1] << 8 | bytes[2],
+ bytes[3] << 8 | bytes[4],
+ RSAKeyFormat.RSAAlgorithmFormat.from(bytes[5]));
- private int mValue;
- private boolean mIncludeModulus;
- private boolean mIncludeCrt;
+ case PublicKeyAlgorithmTags.ECDH:
+ case PublicKeyAlgorithmTags.ECDSA:
+ if (bytes.length < 2) {
+ throw new IllegalArgumentException("Bad length for RSA attributes");
+ }
+ int len = bytes.length - 1;
+ if (bytes[bytes.length - 1] == (byte)0xff) {
+ len -= 1;
+ }
+ final byte[] boid = new byte[2 + len];
+ boid[0] = (byte)0x06;
+ boid[1] = (byte)len;
+ System.arraycopy(bytes, 1, boid, 2, len);
+ final ASN1ObjectIdentifier oid = ASN1ObjectIdentifier.getInstance(boid);
+ return new ECKeyFormat(oid, ECKeyFormat.ECAlgorithmFormat.from(bytes[0], bytes[bytes.length - 1]));
- AlgorithmFormat(int value, boolean includeCrt, boolean includeModulus) {
- mValue = value;
- mIncludeModulus = includeModulus;
- mIncludeCrt = includeCrt;
+ default:
+ throw new IllegalArgumentException("Unsupported Algorithm id " + bytes[0]);
}
+ }
- public static AlgorithmFormat from(byte b) {
- for (AlgorithmFormat format : values()) {
- if (format.mValue == b) {
- return format;
- }
- }
- return null;
- }
+ public static KeyFormat fromCreationKeyType(CreateSecurityTokenAlgorithmFragment.SupportedKeyType t, boolean forEncryption) {
+ final int elen = 17; //65537
+ final ECKeyFormat.ECAlgorithmFormat kf =
+ forEncryption ? ECKeyFormat.ECAlgorithmFormat.ECDH_WITH_PUBKEY : ECKeyFormat.ECAlgorithmFormat.ECDSA_WITH_PUBKEY;
- public boolean isIncludeModulus() {
- return mIncludeModulus;
+ switch (t) {
+ case RSA_2048:
+ return new RSAKeyFormat(2048, elen, RSAKeyFormat.RSAAlgorithmFormat.CRT_WITH_MODULUS);
+ case RSA_3072:
+ return new RSAKeyFormat(3072, elen, RSAKeyFormat.RSAAlgorithmFormat.CRT_WITH_MODULUS);
+ case RSA_4096:
+ return new RSAKeyFormat(4096, elen, RSAKeyFormat.RSAAlgorithmFormat.CRT_WITH_MODULUS);
+ case ECC_P256:
+ return new ECKeyFormat(NISTNamedCurves.getOID("P-256"), kf);
+ case ECC_P384:
+ return new ECKeyFormat(NISTNamedCurves.getOID("P-384"), kf);
+ case ECC_P521:
+ return new ECKeyFormat(NISTNamedCurves.getOID("P-521"), kf);
}
- public boolean isIncludeCrt() {
- return mIncludeCrt;
- }
+ throw new IllegalArgumentException("Unsupported Algorithm id " + t);
}
+
+ public abstract void addToKeyring(SaveKeyringParcel keyring, int keyFlags);
+
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/OpenPgpCapabilities.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/OpenPgpCapabilities.java
index 7a87a0a..d28a746 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/OpenPgpCapabilities.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/OpenPgpCapabilities.java
@@ -36,15 +36,19 @@ public class OpenPgpCapabilities {
private boolean mAttriburesChangable;
private boolean mHasKeyImport;
- private byte mSMAlgo;
+ private int mSMAESKeySize;
private int mMaxCmdLen;
private int mMaxRspLen;
private Map<KeyType, KeyFormat> mKeyFormats;
public OpenPgpCapabilities(byte[] data) throws IOException {
- Iso7816TLV[] tlvs = Iso7816TLV.readList(data, true);
mKeyFormats = new HashMap<>();
+ updateWithData(data);
+ }
+
+ public void updateWithData(byte[] data) throws IOException {
+ Iso7816TLV[] tlvs = Iso7816TLV.readList(data, true);
if (tlvs.length == 1 && tlvs[0].mT == 0x6E) {
tlvs = ((Iso7816TLV.Iso7816CompositeTLV) tlvs[0]).mSubs;
}
@@ -64,13 +68,13 @@ public class OpenPgpCapabilities {
parseExtendedCaps(tlv.mV);
break;
case 0xC1:
- mKeyFormats.put(KeyType.SIGN, new KeyFormat(tlv.mV));
+ mKeyFormats.put(KeyType.SIGN, KeyFormat.fromBytes(tlv.mV));
break;
case 0xC2:
- mKeyFormats.put(KeyType.ENCRYPT, new KeyFormat(tlv.mV));
+ mKeyFormats.put(KeyType.ENCRYPT, KeyFormat.fromBytes(tlv.mV));
break;
case 0xC3:
- mKeyFormats.put(KeyType.AUTH, new KeyFormat(tlv.mV));
+ mKeyFormats.put(KeyType.AUTH, KeyFormat.fromBytes(tlv.mV));
break;
case 0xC4:
mPw1ValidForMultipleSignatures = tlv.mV[0] == 1;
@@ -86,13 +90,13 @@ public class OpenPgpCapabilities {
parseExtendedCaps(tlv.mV);
break;
case 0xC1:
- mKeyFormats.put(KeyType.SIGN, new KeyFormat(tlv.mV));
+ mKeyFormats.put(KeyType.SIGN, KeyFormat.fromBytes(tlv.mV));
break;
case 0xC2:
- mKeyFormats.put(KeyType.ENCRYPT, new KeyFormat(tlv.mV));
+ mKeyFormats.put(KeyType.ENCRYPT, KeyFormat.fromBytes(tlv.mV));
break;
case 0xC3:
- mKeyFormats.put(KeyType.AUTH, new KeyFormat(tlv.mV));
+ mKeyFormats.put(KeyType.AUTH, KeyFormat.fromBytes(tlv.mV));
break;
case 0xC4:
mPw1ValidForMultipleSignatures = tlv.mV[0] == 1;
@@ -106,7 +110,7 @@ public class OpenPgpCapabilities {
mHasKeyImport = (v[0] & MASK_KEY_IMPORT) != 0;
mAttriburesChangable =(v[0] & MASK_ATTRIBUTES_CHANGABLE) != 0;
- mSMAlgo = v[1];
+ mSMAESKeySize = (v[1] == 1) ? 16 : 32;
mMaxCmdLen = (v[6] << 8) + v[7];
mMaxRspLen = (v[8] << 8) + v[9];
@@ -128,7 +132,7 @@ public class OpenPgpCapabilities {
return mHasSM;
}
- public boolean isAttriburesChangable() {
+ public boolean isAttributesChangable() {
return mAttriburesChangable;
}
@@ -136,8 +140,8 @@ public class OpenPgpCapabilities {
return mHasKeyImport;
}
- public byte getSMAlgo() {
- return mSMAlgo;
+ public int getSMAESKeySize() {
+ return mSMAESKeySize;
}
public int getMaxCmdLen() {
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/RSAKeyFormat.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/RSAKeyFormat.java
new file mode 100644
index 0000000..27b4274
--- /dev/null
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/RSAKeyFormat.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2016 Nikita Mikhailov <nikita.s.mikhailov@gmail.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package org.sufficientlysecure.keychain.securitytoken;
+
+import org.sufficientlysecure.keychain.service.SaveKeyringParcel;
+
+// 4.3.3.6 Algorithm Attributes
+public class RSAKeyFormat extends KeyFormat {
+ private int mModulusLength;
+ private int mExponentLength;
+ private RSAAlgorithmFormat mRSAAlgorithmFormat;
+
+ public RSAKeyFormat(int modulusLength,
+ int exponentLength,
+ RSAAlgorithmFormat rsaAlgorithmFormat) {
+ super(KeyFormatType.RSAKeyFormatType);
+ mModulusLength = modulusLength;
+ mExponentLength = exponentLength;
+ mRSAAlgorithmFormat = rsaAlgorithmFormat;
+ }
+
+ public int getModulusLength() {
+ return mModulusLength;
+ }
+
+ public int getExponentLength() {
+ return mExponentLength;
+ }
+
+ public RSAAlgorithmFormat getAlgorithmFormat() {
+ return mRSAAlgorithmFormat;
+ }
+
+ public enum RSAAlgorithmFormat {
+ STANDARD((byte)0, false, false),
+ STANDARD_WITH_MODULUS((byte)1, false, true),
+ CRT((byte)2, true, false),
+ CRT_WITH_MODULUS((byte)3, true, true);
+
+ private byte mValue;
+ private boolean mIncludeModulus;
+ private boolean mIncludeCrt;
+
+ RSAAlgorithmFormat(byte value, boolean includeCrt, boolean includeModulus) {
+ mValue = value;
+ mIncludeModulus = includeModulus;
+ mIncludeCrt = includeCrt;
+ }
+
+ public static RSAAlgorithmFormat from(byte b) {
+ for (RSAAlgorithmFormat format : values()) {
+ if (format.mValue == b) {
+ return format;
+ }
+ }
+ return null;
+ }
+
+ public byte getValue() { return mValue; }
+
+ public boolean isIncludeModulus() {
+ return mIncludeModulus;
+ }
+
+ public boolean isIncludeCrt() {
+ return mIncludeCrt;
+ }
+ }
+
+ public void addToKeyring(SaveKeyringParcel keyring, int keyFlags) {
+ keyring.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd(SaveKeyringParcel.Algorithm.RSA,
+ mModulusLength, null, keyFlags, 0L));
+ }
+}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/SecurityTokenHelper.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/SecurityTokenHelper.java
index 878bf9d..c3619db 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/SecurityTokenHelper.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/SecurityTokenHelper.java
@@ -23,12 +23,28 @@ package org.sufficientlysecure.keychain.securitytoken;
import android.support.annotation.NonNull;
+import org.bouncycastle.asn1.ASN1Encodable;
+import org.bouncycastle.asn1.ASN1Integer;
+import org.bouncycastle.asn1.ASN1OutputStream;
+import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.asn1.nist.NISTNamedCurves;
+import org.bouncycastle.asn1.x9.X9ECParameters;
import org.bouncycastle.bcpg.HashAlgorithmTags;
+import org.bouncycastle.jcajce.util.MessageDigestUtils;
+import org.bouncycastle.math.ec.ECPoint;
+import org.bouncycastle.openpgp.PGPException;
+import org.bouncycastle.openpgp.operator.PGPPad;
+import org.bouncycastle.openpgp.operator.jcajce.JcaKeyFingerprintCalculator;
import org.bouncycastle.util.Arrays;
import org.bouncycastle.util.encoders.Hex;
import org.sufficientlysecure.keychain.Constants;
+import org.sufficientlysecure.keychain.pgp.CanonicalizedPublicKey;
import org.sufficientlysecure.keychain.pgp.CanonicalizedSecretKey;
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
+
+import javax.crypto.Cipher;
+import javax.crypto.NoSuchPaddingException;
+import javax.crypto.spec.SecretKeySpec;
import javax.smartcardio.CommandAPDU;
import javax.smartcardio.ResponseAPDU;
import org.sufficientlysecure.keychain.securitytoken.usb.UsbTransportException;
@@ -41,6 +57,12 @@ import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.math.BigInteger;
import java.nio.ByteBuffer;
+import java.security.InvalidKeyException;
+import java.security.Key;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.security.interfaces.ECPrivateKey;
+import java.security.interfaces.ECPublicKey;
import java.security.interfaces.RSAPrivateCrtKey;
/**
@@ -64,6 +86,9 @@ public class SecurityTokenHelper {
private static final String FIDESMO_APPS_AID_PREFIX = "A000000617";
private static final byte[] BLANK_FINGERPRINT = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+
+ private final JcaKeyFingerprintCalculator fingerprintCalculator = new JcaKeyFingerprintCalculator();
+
private Transport mTransport;
private CardCapabilities mCardCapabilities;
private OpenPgpCapabilities mOpenPgpCapabilities;
@@ -77,6 +102,12 @@ public class SecurityTokenHelper {
protected SecurityTokenHelper() {
}
+ public static double parseOpenPgpVersion(final byte[] aid) {
+ float minv = aid[7];
+ while (minv > 0) minv /= 10.0;
+ return aid[6] + minv;
+ }
+
public static SecurityTokenHelper getInstance() {
return LazyHolder.SECURITY_TOKEN_HELPER;
}
@@ -218,17 +249,62 @@ public class SecurityTokenHelper {
* Call DECIPHER command
*
* @param encryptedSessionKey the encoded session key
+ * @param publicKey
* @return the decoded session key
*/
- public byte[] decryptSessionKey(@NonNull byte[] encryptedSessionKey) throws IOException {
+ public byte[] decryptSessionKey(@NonNull byte[] encryptedSessionKey,
+ CanonicalizedPublicKey publicKey)
+ throws IOException {
+ final KeyFormat kf = mOpenPgpCapabilities.getFormatForKeyType(KeyType.ENCRYPT);
+
if (!mPw1ValidatedForDecrypt) {
verifyPin(0x82); // (Verify PW1 with mode 82 for decryption)
}
- // Transmit
- byte[] data = Arrays.copyOfRange(encryptedSessionKey, 2, encryptedSessionKey.length);
- if (data[0] != 0) {
- data = Arrays.prepend(data, (byte) 0x00);
+ byte[] data;
+ int pLen = 0;
+
+ X9ECParameters x9Params = null;
+
+ switch (kf.keyFormatType()) {
+ case RSAKeyFormatType:
+ data = Arrays.copyOfRange(encryptedSessionKey, 2, encryptedSessionKey.length);
+ if (data[0] != 0) {
+ data = Arrays.prepend(data, (byte) 0x00);
+ }
+ break;
+
+ case ECKeyFormatType:
+ pLen = ((((encryptedSessionKey[0] & 0xff) << 8) + (encryptedSessionKey[1] & 0xff)) + 7) / 8;
+ data = new byte[pLen];
+
+ System.arraycopy(encryptedSessionKey, 2, data, 0, pLen);
+
+ final ECKeyFormat eckf = (ECKeyFormat)kf;
+ x9Params = NISTNamedCurves.getByOID(eckf.getCurveOID());
+
+ final ECPoint p = x9Params.getCurve().decodePoint(data);
+ if (!p.isValid()) {
+ throw new CardException("Invalid EC point!");
+ }
+
+ data = p.getEncoded(false);
+ data = Arrays.concatenate(
+ Hex.decode("86"),
+ new byte[]{ (byte)data.length },
+ data);
+ data = Arrays.concatenate(
+ Hex.decode("7F49"),
+ new byte[] { (byte)data.length },
+ data);
+ data = Arrays.concatenate(
+ Hex.decode("A6"),
+ new byte[] { (byte)data.length },
+ data);
+ break;
+
+ default:
+ throw new CardException("Unknown encryption key type!");
}
CommandAPDU command = new CommandAPDU(0x00, 0x2A, 0x80, 0x86, data, MAX_APDU_NE_EXT);
@@ -238,7 +314,47 @@ public class SecurityTokenHelper {
throw new CardException("Deciphering with Security token failed on receive", response.getSW());
}
- return response.getData();
+ switch (mOpenPgpCapabilities.getFormatForKeyType(KeyType.ENCRYPT).keyFormatType()) {
+ case RSAKeyFormatType:
+ return response.getData();
+
+ case ECKeyFormatType:
+ data = response.getData();
+
+ final byte[] keyEnc = new byte[encryptedSessionKey[pLen + 2]];
+
+ System.arraycopy(encryptedSessionKey, 2 + pLen + 1, keyEnc, 0, keyEnc.length);
+
+ try {
+ final MessageDigest kdf = MessageDigest.getInstance(MessageDigestUtils.getDigestName(publicKey.getHashAlgorithm()));
+
+ kdf.update(new byte[]{ (byte)0, (byte)0, (byte)0, (byte)1 });
+ kdf.update(data);
+ kdf.update(publicKey.createUserKeyingMaterial(fingerprintCalculator));
+
+ final byte[] kek = kdf.digest();
+ final Cipher c = Cipher.getInstance("AESWrap");
+
+ c.init(Cipher.UNWRAP_MODE, new SecretKeySpec(kek, 0, publicKey.getSymmetricKeySize() / 8, "AES"));
+
+ final Key paddedSessionKey = c.unwrap(keyEnc, "Session", Cipher.SECRET_KEY);
+
+ Arrays.fill(kek, (byte)0);
+
+ return PGPPad.unpadSessionData(paddedSessionKey.getEncoded());
+ } catch (NoSuchAlgorithmException e) {
+ throw new CardException("Unknown digest/encryption algorithm!");
+ } catch (NoSuchPaddingException e) {
+ throw new CardException("Unknown padding algorithm!");
+ } catch (PGPException e) {
+ throw new CardException(e.getMessage());
+ } catch (InvalidKeyException e) {
+ throw new CardException("Invalid KEK!");
+ }
+
+ default:
+ throw new CardException("Unknown encryption key type!");
+ }
}
/**
@@ -300,6 +416,36 @@ public class SecurityTokenHelper {
}
}
+
+ private void setKeyAttributes(final KeyType slot, final CanonicalizedSecretKey secretKey)
+ throws IOException {
+
+ if (mOpenPgpCapabilities.isAttributesChangable()) {
+ int tag;
+
+ if (slot == KeyType.SIGN) {
+ tag = 0xC1;
+ } else if (slot == KeyType.ENCRYPT) {
+ tag = 0xC2;
+ } else if (slot == KeyType.AUTH) {
+ tag = 0xC3;
+ } else {
+ throw new IOException("Unknown key for card.");
+ }
+
+ try {
+
+ putData(tag, SecurityTokenUtils.attributesFromSecretKey(slot, secretKey));
+
+ mOpenPgpCapabilities.updateWithData(getData(0x00, tag));
+
+ } catch (PgpGeneralException e) {
+ throw new IOException("Key algorithm not supported by the security token.");
+ }
+
+ }
+ }
+
/**
* Puts a key on the token in the given slot.
*
@@ -311,33 +457,58 @@ public class SecurityTokenHelper {
private void putKey(KeyType slot, CanonicalizedSecretKey secretKey, Passphrase passphrase)
throws IOException {
RSAPrivateCrtKey crtSecretKey;
+ ECPrivateKey ecSecretKey;
+ ECPublicKey ecPublicKey;
+
+ if (!mPw3Validated) {
+ verifyPin(0x83); // (Verify PW3 with mode 83)
+ }
+
+ // Now we're ready to communicate with the token.
+ byte[] keyBytes = null;
+
try {
secretKey.unlock(passphrase);
- crtSecretKey = secretKey.getCrtSecretKey();
- } catch (PgpGeneralException e) {
- throw new IOException(e.getMessage());
- }
- // Shouldn't happen; the UI should block the user from getting an incompatible key this far.
- if (crtSecretKey.getModulus().bitLength() > 2048) {
- throw new IOException("Key too large to export to Security Token.");
- }
+ setKeyAttributes(slot, secretKey);
- // Should happen only rarely; all GnuPG keys since 2006 use public exponent 65537.
- if (!crtSecretKey.getPublicExponent().equals(new BigInteger("65537"))) {
- throw new IOException("Invalid public exponent for smart Security Token.");
- }
+ switch (mOpenPgpCapabilities.getFormatForKeyType(slot).keyFormatType()) {
+ case RSAKeyFormatType:
+ if (!secretKey.isRSA()) {
+ throw new IOException("Security Token not configured for RSA key.");
+ }
+ crtSecretKey = secretKey.getCrtSecretKey();
- if (!mPw3Validated) {
- verifyPin(0x83); // (Verify PW3 with mode 83)
- }
+ // Should happen only rarely; all GnuPG keys since 2006 use public exponent 65537.
+ if (!crtSecretKey.getPublicExponent().equals(new BigInteger("65537"))) {
+ throw new IOException("Invalid public exponent for smart Security Token.");
+ }
+ keyBytes = SecurityTokenUtils.createRSAPrivKeyTemplate(crtSecretKey, slot,
+ (RSAKeyFormat) (mOpenPgpCapabilities.getFormatForKeyType(slot)));
+ break;
- // Now we're ready to communicate with the token.
- byte[] bytes = SecurityTokenUtils.createPrivKeyTemplate(crtSecretKey, slot,
- mOpenPgpCapabilities.getFormatForKeyType(slot));
+ case ECKeyFormatType:
+ if (!secretKey.isEC()) {
+ throw new IOException("Security Token not configured for EC key.");
+ }
+
+ secretKey.unlock(passphrase);
+ ecSecretKey = secretKey.getECSecretKey();
+ ecPublicKey = secretKey.getECPublicKey();
- CommandAPDU apdu = new CommandAPDU(0x00, 0xDB, 0x3F, 0xFF, bytes);
+ keyBytes = SecurityTokenUtils.createECPrivKeyTemplate(ecSecretKey, ecPublicKey, slot,
+ (ECKeyFormat) (mOpenPgpCapabilities.getFormatForKeyType(slot)));
+ break;
+
+ default:
+ throw new IOException("Key type unsupported by security token.");
+ }
+ } catch (PgpGeneralException e) {
+ throw new IOException(e.getMessage());
+ }
+
+ CommandAPDU apdu = new CommandAPDU(0x00, 0xDB, 0x3F, 0xFF, keyBytes);
ResponseAPDU response = communicate(apdu);
if (response.getSW() != APDU_SW_SUCCESS) {
@@ -462,8 +633,21 @@ public class SecurityTokenHelper {
throw new IOException("Not supported hash algo!");
}
+ byte[] data;
+
+ switch (mOpenPgpCapabilities.getFormatForKeyType(KeyType.SIGN).keyFormatType()) {
+ case RSAKeyFormatType:
+ data = dsi;
+ break;
+ case ECKeyFormatType:
+ data = hash;
+ break;
+ default:
+ throw new IOException("Not supported key type!");
+ }
+
// Command APDU for PERFORM SECURITY OPERATION: COMPUTE DIGITAL SIGNATURE (page 37)
- CommandAPDU command = new CommandAPDU(0x00, 0x2A, 0x9E, 0x9A, dsi, MAX_APDU_NE_EXT);
+ CommandAPDU command = new CommandAPDU(0x00, 0x2A, 0x9E, 0x9A, data, MAX_APDU_NE_EXT);
ResponseAPDU response = communicate(command);
if (response.getSW() != APDU_SW_SUCCESS) {
@@ -477,9 +661,30 @@ public class SecurityTokenHelper {
byte[] signature = response.getData();
// Make sure the signature we received is actually the expected number of bytes long!
- if (signature.length != 128 && signature.length != 256
- && signature.length != 384 && signature.length != 512) {
- throw new IOException("Bad signature length! Expected 128/256/384/512 bytes, got " + signature.length);
+ switch (mOpenPgpCapabilities.getFormatForKeyType(KeyType.SIGN).keyFormatType()) {
+ case RSAKeyFormatType:
+ if (signature.length != 128 && signature.length != 256
+ && signature.length != 384 && signature.length != 512) {
+ throw new IOException("Bad signature length! Expected 128/256/384/512 bytes, got " + signature.length);
+ }
+ break;
+
+ case ECKeyFormatType:
+ if (signature.length % 2 != 0) {
+ throw new IOException("Bad signature length!");
+ }
+ final byte[] br = new byte[signature.length / 2];
+ final byte[] bs = new byte[signature.length / 2];
+ for(int i = 0; i < br.length; ++i) {
+ br[i] = signature[i];
+ bs[i] = signature[br.length + i];
+ }
+ final ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ ASN1OutputStream out = new ASN1OutputStream(baos);
+ out.writeObject(new DERSequence(new ASN1Encodable[] { new ASN1Integer(br), new ASN1Integer(bs) }));
+ out.flush();
+ signature = baos.toByteArray();
+ break;
}
return signature;
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyActivity.java
index b0e922c..19cbb76 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyActivity.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyActivity.java
@@ -31,6 +31,7 @@ import org.sufficientlysecure.keychain.pgp.exception.PgpKeyNotFoundException;
import org.sufficientlysecure.keychain.provider.CachedPublicKeyRing;
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
+import org.sufficientlysecure.keychain.securitytoken.KeyFormat;
import org.sufficientlysecure.keychain.ui.base.BaseSecurityTokenActivity;
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
import org.sufficientlysecure.keychain.util.Passphrase;
@@ -64,6 +65,9 @@ public class CreateKeyActivity extends BaseSecurityTokenActivity {
boolean mCreateSecurityToken;
Passphrase mSecurityTokenPin;
Passphrase mSecurityTokenAdminPin;
+ KeyFormat mSecurityTokenSign;
+ KeyFormat mSecurityTokenDec;
+ KeyFormat mSecurityTokenAuth;
Fragment mCurrentFragment;
@@ -97,6 +101,7 @@ public class CreateKeyActivity extends BaseSecurityTokenActivity {
mPassphrase = savedInstanceState.getParcelable(EXTRA_PASSPHRASE);
mFirstTime = savedInstanceState.getBoolean(EXTRA_FIRST_TIME);
mCreateSecurityToken = savedInstanceState.getBoolean(EXTRA_CREATE_SECURITY_TOKEN);
+ mSecurityTokenAid = savedInstanceState.getByteArray(EXTRA_SECURITY_TOKEN_AID);
mSecurityTokenPin = savedInstanceState.getParcelable(EXTRA_SECURITY_TOKEN_PIN);
mSecurityTokenAdminPin = savedInstanceState.getParcelable(EXTRA_SECURITY_TOKEN_ADMIN_PIN);
@@ -122,7 +127,7 @@ public class CreateKeyActivity extends BaseSecurityTokenActivity {
setTitle(R.string.title_import_keys);
} else {
- Fragment frag = CreateSecurityTokenBlankFragment.newInstance();
+ Fragment frag = CreateSecurityTokenBlankFragment.newInstance(nfcAid);
loadFragment(frag, FragAction.START);
setTitle(R.string.title_manage_my_keys);
}
@@ -192,7 +197,7 @@ public class CreateKeyActivity extends BaseSecurityTokenActivity {
loadFragment(frag, FragAction.TO_RIGHT);
}
} else {
- Fragment frag = CreateSecurityTokenBlankFragment.newInstance();
+ Fragment frag = CreateSecurityTokenBlankFragment.newInstance(mSecurityTokenAid);
loadFragment(frag, FragAction.TO_RIGHT);
}
}
@@ -221,6 +226,7 @@ public class CreateKeyActivity extends BaseSecurityTokenActivity {
outState.putParcelable(EXTRA_PASSPHRASE, mPassphrase);
outState.putBoolean(EXTRA_FIRST_TIME, mFirstTime);
outState.putBoolean(EXTRA_CREATE_SECURITY_TOKEN, mCreateSecurityToken);
+ outState.putByteArray(EXTRA_SECURITY_TOKEN_AID, mSecurityTokenAid);
outState.putParcelable(EXTRA_SECURITY_TOKEN_PIN, mSecurityTokenPin);
outState.putParcelable(EXTRA_SECURITY_TOKEN_ADMIN_PIN, mSecurityTokenAdminPin);
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyFinalFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyFinalFragment.java
index 99a4b02..1d5473d 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyFinalFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyFinalFragment.java
@@ -282,12 +282,9 @@ public class CreateKeyFinalFragment extends Fragment {
SaveKeyringParcel saveKeyringParcel = new SaveKeyringParcel();
if (createKeyActivity.mCreateSecurityToken) {
- saveKeyringParcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd(Algorithm.RSA,
- 2048, null, KeyFlags.SIGN_DATA | KeyFlags.CERTIFY_OTHER, 0L));
- saveKeyringParcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd(Algorithm.RSA,
- 2048, null, KeyFlags.ENCRYPT_COMMS | KeyFlags.ENCRYPT_STORAGE, 0L));
- saveKeyringParcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd(Algorithm.RSA,
- 2048, null, KeyFlags.AUTHENTICATION, 0L));
+ createKeyActivity.mSecurityTokenSign.addToKeyring(saveKeyringParcel, KeyFlags.SIGN_DATA | KeyFlags.CERTIFY_OTHER);
+ createKeyActivity.mSecurityTokenDec.addToKeyring(saveKeyringParcel, KeyFlags.ENCRYPT_COMMS | KeyFlags.ENCRYPT_STORAGE);
+ createKeyActivity.mSecurityTokenAuth.addToKeyring(saveKeyringParcel, KeyFlags.AUTHENTICATION);
// use empty passphrase
saveKeyringParcel.setNewUnlock(new ChangeUnlockParcel(new Passphrase()));
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateSecurityTokenAlgorithmFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateSecurityTokenAlgorithmFragment.java
new file mode 100644
index 0000000..90a9022
--- /dev/null
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateSecurityTokenAlgorithmFragment.java
@@ -0,0 +1,193 @@
+/*
+ * Copyright (C) 2015 Dominik Schürmann <dominik@dominikschuermann.de>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package org.sufficientlysecure.keychain.ui;
+
+import android.app.Activity;
+import android.content.Context;
+import android.os.Bundle;
+import android.support.v4.app.Fragment;
+import android.support.v4.app.FragmentActivity;
+import android.text.Html;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ArrayAdapter;
+import android.widget.Spinner;
+import android.widget.TextView;
+
+import org.sufficientlysecure.keychain.R;
+import org.sufficientlysecure.keychain.securitytoken.KeyFormat;
+import org.sufficientlysecure.keychain.securitytoken.SecurityTokenHelper;
+import org.sufficientlysecure.keychain.ui.CreateKeyActivity.FragAction;
+import org.sufficientlysecure.keychain.util.Choice;
+import org.sufficientlysecure.keychain.util.Log;
+import org.sufficientlysecure.keychain.util.SecurityTokenUtils;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class CreateSecurityTokenAlgorithmFragment extends Fragment {
+
+
+ public enum SupportedKeyType {
+ RSA_2048, RSA_3072, RSA_4096, ECC_P256, ECC_P384, ECC_P521
+ }
+
+ private CreateKeyActivity mCreateKeyActivity;
+
+ private View mBackButton;
+ private View mNextButton;
+
+ private Spinner mSignKeySpinner;
+ private Spinner mDecKeySpinner;
+ private Spinner mAuthKeySpinner;
+
+
+ /**
+ * Creates new instance of this fragment
+ */
+ public static CreateSecurityTokenAlgorithmFragment newInstance() {
+ CreateSecurityTokenAlgorithmFragment frag = new CreateSecurityTokenAlgorithmFragment();
+
+ Bundle args = new Bundle();
+ frag.setArguments(args);
+
+ return frag;
+ }
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
+ final FragmentActivity context = getActivity();
+ View view = inflater.inflate(R.layout.create_yubi_key_algorithm_fragment, container, false);
+
+ mBackButton = (TextView) view.findViewById(R.id.create_key_back_button);
+ mNextButton = (TextView) view.findViewById(R.id.create_key_next_button);
+
+ mBackButton.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ back();
+ }
+ });
+ mNextButton.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ nextClicked();
+ }
+ });
+
+ mSignKeySpinner = (Spinner) view.findViewById(R.id.create_key_yubi_key_algorithm_sign);
+ mDecKeySpinner = (Spinner) view.findViewById(R.id.create_key_yubi_key_algorithm_dec);
+ mAuthKeySpinner = (Spinner) view.findViewById(R.id.create_key_yubi_key_algorithm_auth);
+
+ ArrayList<Choice<SupportedKeyType>> choices = new ArrayList<>();
+
+ choices.add(new Choice<>(SupportedKeyType.RSA_2048, getResources().getString(
+ R.string.rsa_2048), getResources().getString(R.string.rsa_2048_description_html)));
+ choices.add(new Choice<>(SupportedKeyType.RSA_3072, getResources().getString(
+ R.string.rsa_3072), getResources().getString(R.string.rsa_3072_description_html)));
+ choices.add(new Choice<>(SupportedKeyType.RSA_4096, getResources().getString(
+ R.string.rsa_4096), getResources().getString(R.string.rsa_4096_description_html)));
+
+ final double version = SecurityTokenHelper.parseOpenPgpVersion(mCreateKeyActivity.mSecurityTokenAid);
+
+ if (version >= 3.0) {
+ choices.add(new Choice<>(SupportedKeyType.ECC_P256, getResources().getString(
+ R.string.ecc_p256), getResources().getString(R.string.ecc_p256_description_html)));
+ choices.add(new Choice<>(SupportedKeyType.ECC_P384, getResources().getString(
+ R.string.ecc_p384), getResources().getString(R.string.ecc_p384_description_html)));
+ choices.add(new Choice<>(SupportedKeyType.ECC_P521, getResources().getString(
+ R.string.ecc_p521), getResources().getString(R.string.ecc_p521_description_html)));
+ }
+
+ TwoLineArrayAdapter adapter = new TwoLineArrayAdapter(context,
+ android.R.layout.simple_spinner_item, choices);
+ mSignKeySpinner.setAdapter(adapter);
+ mDecKeySpinner.setAdapter(adapter);
+ mAuthKeySpinner.setAdapter(adapter);
+
+ // make ECC nist256 the default for v3.x
+ for (int i = 0; i < choices.size(); ++i) {
+ if (version >= 3.0) {
+ if (choices.get(i).getId() == SupportedKeyType.ECC_P256) {
+ mSignKeySpinner.setSelection(i);
+ mDecKeySpinner.setSelection(i);
+ mAuthKeySpinner.setSelection(i);
+ break;
+ }
+ } else {
+ if (choices.get(i).getId() == SupportedKeyType.RSA_2048) {
+ mSignKeySpinner.setSelection(i);
+ mDecKeySpinner.setSelection(i);
+ mAuthKeySpinner.setSelection(i);
+ break;
+ }
+ }
+ }
+
+ return view;
+ }
+
+ @Override
+ public void onAttach(Activity activity) {
+ super.onAttach(activity);
+ mCreateKeyActivity = (CreateKeyActivity) getActivity();
+ }
+
+ private void back() {
+ mCreateKeyActivity.loadFragment(null, FragAction.TO_LEFT);
+ }
+
+ private void nextClicked() {
+ mCreateKeyActivity.mSecurityTokenSign = KeyFormat.fromCreationKeyType(((Choice<SupportedKeyType>)mSignKeySpinner.getSelectedItem()).getId(), false);
+ mCreateKeyActivity.mSecurityTokenDec = KeyFormat.fromCreationKeyType(((Choice<SupportedKeyType>)mDecKeySpinner.getSelectedItem()).getId(), true);
+ mCreateKeyActivity.mSecurityTokenAuth = KeyFormat.fromCreationKeyType(((Choice<SupportedKeyType>)mAuthKeySpinner.getSelectedItem()).getId(), false);
+
+ CreateKeyFinalFragment frag = CreateKeyFinalFragment.newInstance();
+ mCreateKeyActivity.loadFragment(frag, FragAction.TO_RIGHT);
+ }
+
+
+
+
+ private class TwoLineArrayAdapter extends ArrayAdapter<Choice<SupportedKeyType>> {
+ public TwoLineArrayAdapter(Context context, int resource, List<Choice<SupportedKeyType>> objects) {
+ super(context, resource, objects);
+ }
+
+
+ @Override
+ public View getDropDownView(int position, View convertView, ViewGroup parent) {
+ // inflate view if not given one
+ if (convertView == null) {
+ convertView = getActivity().getLayoutInflater()
+ .inflate(R.layout.two_line_spinner_dropdown_item, parent, false);
+ }
+
+ Choice c = this.getItem(position);
+
+ TextView text1 = (TextView) convertView.findViewById(android.R.id.text1);
+ TextView text2 = (TextView) convertView.findViewById(android.R.id.text2);
+
+ text1.setText(c.getName());
+ text2.setText(Html.fromHtml(c.getDescription()));
+
+ return convertView;
+ }
+ }
+}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateSecurityTokenBlankFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateSecurityTokenBlankFragment.java
index 08441c1..3f52e09 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateSecurityTokenBlankFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateSecurityTokenBlankFragment.java
@@ -33,14 +33,17 @@ public class CreateSecurityTokenBlankFragment extends Fragment {
View mBackButton;
View mNextButton;
+ private byte[] mAid;
+
/**
* Creates new instance of this fragment
*/
- public static CreateSecurityTokenBlankFragment newInstance() {
+ public static CreateSecurityTokenBlankFragment newInstance(byte[] aid) {
CreateSecurityTokenBlankFragment frag = new CreateSecurityTokenBlankFragment();
Bundle args = new Bundle();
+ frag.mAid = aid;
frag.setArguments(args);
return frag;
@@ -82,6 +85,7 @@ public class CreateSecurityTokenBlankFragment extends Fragment {
private void nextClicked() {
mCreateKeyActivity.mCreateSecurityToken = true;
+ mCreateKeyActivity.mSecurityTokenAid = mAid;
CreateKeyNameFragment frag = CreateKeyNameFragment.newInstance();
mCreateKeyActivity.loadFragment(frag, FragAction.TO_RIGHT);
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateSecurityTokenImportResetFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateSecurityTokenImportResetFragment.java
index 795d27b..480e0fa 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateSecurityTokenImportResetFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateSecurityTokenImportResetFragment.java
@@ -38,6 +38,7 @@ import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.keyimport.ParcelableKeyRing;
import org.sufficientlysecure.keychain.operations.results.ImportKeyResult;
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
+import org.sufficientlysecure.keychain.securitytoken.SecurityTokenHelper;
import org.sufficientlysecure.keychain.service.ImportKeyringParcel;
import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;
import org.sufficientlysecure.keychain.service.input.RequiredInputParcel;
@@ -62,6 +63,7 @@ public class CreateSecurityTokenImportResetFragment
private byte[] mTokenFingerprints;
private byte[] mTokenAid;
+ private double mTokenVersion;
private String mTokenUserId;
private String mTokenFingerprint;
private ImportKeysListFragment mListFragment;
@@ -251,6 +253,7 @@ public class CreateSecurityTokenImportResetFragment
mTokenFingerprints = mCreateKeyActivity.getSecurityTokenHelper().getFingerprints();
mTokenAid = mCreateKeyActivity.getSecurityTokenHelper().getAid();
+ mTokenVersion = SecurityTokenHelper.parseOpenPgpVersion(mTokenAid);
mTokenUserId = mCreateKeyActivity.getSecurityTokenHelper().getUserId();
byte[] fp = new byte[20];
@@ -287,6 +290,7 @@ public class CreateSecurityTokenImportResetFragment
viewKeyIntent.setData(KeyRings.buildGenericKeyRingUri(masterKeyIds[0]));
viewKeyIntent.putExtra(ViewKeyActivity.EXTRA_DISPLAY_RESULT, result);
viewKeyIntent.putExtra(ViewKeyActivity.EXTRA_SECURITY_TOKEN_AID, mTokenAid);
+ viewKeyIntent.putExtra(ViewKeyActivity.EXTRA_SECURITY_TOKEN_VERSION, mTokenVersion);
viewKeyIntent.putExtra(ViewKeyActivity.EXTRA_SECURITY_TOKEN_USER_ID, mTokenUserId);
viewKeyIntent.putExtra(ViewKeyActivity.EXTRA_SECURITY_TOKEN_FINGERPRINTS, mTokenFingerprints);
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateSecurityTokenPinFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateSecurityTokenPinFragment.java
index a6ecef4..9fe35fc 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateSecurityTokenPinFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateSecurityTokenPinFragment.java
@@ -200,7 +200,7 @@ public class CreateSecurityTokenPinFragment extends Fragment {
mCreateKeyActivity.mSecurityTokenPin = new Passphrase(mPin.getText().toString());
- CreateKeyFinalFragment frag = CreateKeyFinalFragment.newInstance();
+ CreateSecurityTokenAlgorithmFragment frag = CreateSecurityTokenAlgorithmFragment.newInstance();
hideKeyboard();
mCreateKeyActivity.loadFragment(frag, FragAction.TO_RIGHT);
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyFragment.java
index 9181c6f..6bf7cae 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyFragment.java
@@ -35,6 +35,8 @@ import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.ListView;
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.nist.NISTNamedCurves;
import org.bouncycastle.bcpg.PublicKeyAlgorithmTags;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
@@ -449,19 +451,31 @@ public class EditKeyFragment extends QueueingCryptoOperationFragment<SaveKeyring
break;
}
- int algorithm = mSubkeysAdapter.getAlgorithm(position);
- if (algorithm != PublicKeyAlgorithmTags.RSA_GENERAL
- && algorithm != PublicKeyAlgorithmTags.RSA_ENCRYPT
- && algorithm != PublicKeyAlgorithmTags.RSA_SIGN) {
- Notify.create(getActivity(), R.string.edit_key_error_bad_security_token_algo, Notify.Style.ERROR)
- .show();
- break;
- }
-
- if (mSubkeysAdapter.getKeySize(position) != 2048) {
- Notify.create(getActivity(), R.string.edit_key_error_bad_security_token_size, Notify.Style.ERROR)
- .show();
- break;
+ switch (mSubkeysAdapter.getAlgorithm(position)) {
+ case PublicKeyAlgorithmTags.RSA_GENERAL:
+ case PublicKeyAlgorithmTags.RSA_ENCRYPT:
+ case PublicKeyAlgorithmTags.RSA_SIGN:
+ if (mSubkeysAdapter.getKeySize(position) < 2048) {
+ Notify.create(getActivity(), R.string.edit_key_error_bad_security_token_size, Notify.Style.ERROR)
+ .show();
+ }
+ break;
+
+ case PublicKeyAlgorithmTags.ECDH:
+ case PublicKeyAlgorithmTags.ECDSA:
+ final ASN1ObjectIdentifier curve = NISTNamedCurves.getOID(mSubkeysAdapter.getCurveOid(position));
+ if (!curve.equals(NISTNamedCurves.getByName("P-256")) &&
+ !curve.equals(NISTNamedCurves.getByName("P-384")) &&
+ !curve.equals(NISTNamedCurves.getByName("P-521"))) {
+ Notify.create(getActivity(), R.string.edit_key_error_bad_security_token_curve, Notify.Style.ERROR)
+ .show();
+ }
+ break;
+
+ default:
+ Notify.create(getActivity(), R.string.edit_key_error_bad_security_token_algo, Notify.Style.ERROR)
+ .show();
+ break;
}
SubkeyChange change;
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SecurityTokenOperationActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SecurityTokenOperationActivity.java
index 4d07025..651780f 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SecurityTokenOperationActivity.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SecurityTokenOperationActivity.java
@@ -32,6 +32,7 @@ import android.widget.ViewAnimator;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
+import org.sufficientlysecure.keychain.pgp.CanonicalizedPublicKeyRing;
import org.sufficientlysecure.keychain.pgp.CanonicalizedSecretKey;
import org.sufficientlysecure.keychain.pgp.CanonicalizedSecretKeyRing;
import org.sufficientlysecure.keychain.provider.KeychainContract;
@@ -192,9 +193,18 @@ public class SecurityTokenOperationActivity extends BaseSecurityTokenActivity {
throw new IOException(getString(R.string.error_wrong_security_token));
}
+ ProviderHelper providerHelper = new ProviderHelper(this);
+ CanonicalizedPublicKeyRing publicKeyRing;
+ try {
+ publicKeyRing = providerHelper.getCanonicalizedPublicKeyRing(
+ KeychainContract.KeyRings.buildUnifiedKeyRingsFindBySubkeyUri(mRequiredInput.getMasterKeyId()));
+ } catch (ProviderHelper.NotFoundException e) {
+ throw new IOException("Couldn't find subkey for key to token operation.");
+ }
+
for (int i = 0; i < mRequiredInput.mInputData.length; i++) {
byte[] encryptedSessionKey = mRequiredInput.mInputData[i];
- byte[] decryptedSessionKey = mSecurityTokenHelper.decryptSessionKey(encryptedSessionKey);
+ byte[] decryptedSessionKey = mSecurityTokenHelper.decryptSessionKey(encryptedSessionKey, publicKeyRing.getPublicKey(tokenKeyId));
mInputParcel.addCryptoData(encryptedSessionKey, decryptedSessionKey);
}
break;
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java
index 4dceb94..feceb18 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java
@@ -110,6 +110,7 @@ public class ViewKeyActivity extends BaseSecurityTokenActivity implements
public static final String EXTRA_SECURITY_TOKEN_USER_ID = "security_token_user_id";
public static final String EXTRA_SECURITY_TOKEN_AID = "security_token_aid";
+ public static final String EXTRA_SECURITY_TOKEN_VERSION = "security_token_version";
public static final String EXTRA_SECURITY_TOKEN_FINGERPRINTS = "security_token_fingerprints";
@Retention(RetentionPolicy.SOURCE)
@@ -175,6 +176,7 @@ public class ViewKeyActivity extends BaseSecurityTokenActivity implements
private byte[] mSecurityTokenFingerprints;
private String mSecurityTokenUserId;
private byte[] mSecurityTokenAid;
+ private double mSecurityTokenVersion;
@SuppressLint("InflateParams")
@Override
@@ -668,7 +670,7 @@ public class ViewKeyActivity extends BaseSecurityTokenActivity implements
// if the master key of that key matches this one, just show the token dialog
if (KeyFormattingUtils.convertFingerprintToHex(candidateFp).equals(mFingerprintString)) {
- showSecurityTokenFragment(mSecurityTokenFingerprints, mSecurityTokenUserId, mSecurityTokenAid);
+ showSecurityTokenFragment(mSecurityTokenFingerprints, mSecurityTokenUserId, mSecurityTokenAid, mSecurityTokenVersion);
return;
}
@@ -682,6 +684,7 @@ public class ViewKeyActivity extends BaseSecurityTokenActivity implements
ViewKeyActivity.this, ViewKeyActivity.class);
intent.setData(KeyRings.buildGenericKeyRingUri(masterKeyId));
intent.putExtra(ViewKeyActivity.EXTRA_SECURITY_TOKEN_AID, mSecurityTokenAid);
+ intent.putExtra(ViewKeyActivity.EXTRA_SECURITY_TOKEN_VERSION, mSecurityTokenVersion);
intent.putExtra(ViewKeyActivity.EXTRA_SECURITY_TOKEN_USER_ID, mSecurityTokenUserId);
intent.putExtra(ViewKeyActivity.EXTRA_SECURITY_TOKEN_FINGERPRINTS, mSecurityTokenFingerprints);
startActivity(intent);
@@ -697,6 +700,7 @@ public class ViewKeyActivity extends BaseSecurityTokenActivity implements
Intent intent = new Intent(
ViewKeyActivity.this, CreateKeyActivity.class);
intent.putExtra(ViewKeyActivity.EXTRA_SECURITY_TOKEN_AID, mSecurityTokenAid);
+ intent.putExtra(ViewKeyActivity.EXTRA_SECURITY_TOKEN_VERSION, mSecurityTokenVersion);
intent.putExtra(ViewKeyActivity.EXTRA_SECURITY_TOKEN_USER_ID, mSecurityTokenUserId);
intent.putExtra(ViewKeyActivity.EXTRA_SECURITY_TOKEN_FINGERPRINTS, mSecurityTokenFingerprints);
startActivity(intent);
@@ -707,13 +711,13 @@ public class ViewKeyActivity extends BaseSecurityTokenActivity implements
}
public void showSecurityTokenFragment(
- final byte[] tokenFingerprints, final String tokenUserId, final byte[] tokenAid) {
+ final byte[] tokenFingerprints, final String tokenUserId, final byte[] tokenAid, final double tokenVersion) {
new Handler().post(new Runnable() {
@Override
public void run() {
ViewKeySecurityTokenFragment frag = ViewKeySecurityTokenFragment.newInstance(
- mMasterKeyId, tokenFingerprints, tokenUserId, tokenAid);
+ mMasterKeyId, tokenFingerprints, tokenUserId, tokenAid, tokenVersion);
FragmentManager manager = getSupportFragmentManager();
@@ -896,7 +900,8 @@ public class ViewKeyActivity extends BaseSecurityTokenActivity implements
byte[] tokenFingerprints = intent.getByteArrayExtra(EXTRA_SECURITY_TOKEN_FINGERPRINTS);
String tokenUserId = intent.getStringExtra(EXTRA_SECURITY_TOKEN_USER_ID);
byte[] tokenAid = intent.getByteArrayExtra(EXTRA_SECURITY_TOKEN_AID);
- showSecurityTokenFragment(tokenFingerprints, tokenUserId, tokenAid);
+ double tokenVersion = intent.getDoubleExtra(EXTRA_SECURITY_TOKEN_VERSION, 2.0);
+ showSecurityTokenFragment(tokenFingerprints, tokenUserId, tokenAid, tokenVersion);
}
mIsSecret = data.getInt(INDEX_HAS_ANY_SECRET) != 0;
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyAdvSubkeysFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyAdvSubkeysFragment.java
index 93b38af..e70ac5a 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyAdvSubkeysFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyAdvSubkeysFragment.java
@@ -39,6 +39,8 @@ import android.widget.AdapterView;
import android.widget.ListView;
import android.widget.ViewAnimator;
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.nist.NISTNamedCurves;
import org.bouncycastle.bcpg.PublicKeyAlgorithmTags;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
@@ -356,19 +358,31 @@ public class ViewKeyAdvSubkeysFragment extends LoaderFragment implements
break;
}
- int algorithm = mSubkeysAdapter.getAlgorithm(position);
- if (algorithm != PublicKeyAlgorithmTags.RSA_GENERAL
- && algorithm != PublicKeyAlgorithmTags.RSA_ENCRYPT
- && algorithm != PublicKeyAlgorithmTags.RSA_SIGN) {
- Notify.create(getActivity(), R.string.edit_key_error_bad_security_token_algo, Notify.Style.ERROR)
- .show();
- break;
- }
-
- if (mSubkeysAdapter.getKeySize(position) != 2048) {
- Notify.create(getActivity(), R.string.edit_key_error_bad_security_token_size, Notify.Style.ERROR)
- .show();
- break;
+ switch (mSubkeysAdapter.getAlgorithm(position)) {
+ case PublicKeyAlgorithmTags.RSA_GENERAL:
+ case PublicKeyAlgorithmTags.RSA_ENCRYPT:
+ case PublicKeyAlgorithmTags.RSA_SIGN:
+ if (mSubkeysAdapter.getKeySize(position) < 2048) {
+ Notify.create(getActivity(), R.string.edit_key_error_bad_security_token_size, Notify.Style.ERROR)
+ .show();
+ }
+ break;
+
+ case PublicKeyAlgorithmTags.ECDH:
+ case PublicKeyAlgorithmTags.ECDSA:
+ final ASN1ObjectIdentifier curve = NISTNamedCurves.getOID(mSubkeysAdapter.getCurveOid(position));
+ if (!curve.equals(NISTNamedCurves.getOID("P-256")) &&
+ !curve.equals(NISTNamedCurves.getOID("P-384")) &&
+ !curve.equals(NISTNamedCurves.getOID("P-521"))) {
+ Notify.create(getActivity(), R.string.edit_key_error_bad_security_token_curve, Notify.Style.ERROR)
+ .show();
+ }
+ break;
+
+ default:
+ Notify.create(getActivity(), R.string.edit_key_error_bad_security_token_algo, Notify.Style.ERROR)
+ .show();
+ break;
}
SubkeyChange change;
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeySecurityTokenFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeySecurityTokenFragment.java
index 8b77341..e142656 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeySecurityTokenFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeySecurityTokenFragment.java
@@ -52,10 +52,12 @@ public class ViewKeySecurityTokenFragment
public static final String ARG_FINGERPRINT = "fingerprint";
public static final String ARG_USER_ID = "user_id";
public static final String ARG_CARD_AID = "aid";
+ public static final String ARG_CARD_VERSION = "version";
private byte[][] mFingerprints;
private String mUserId;
private byte[] mCardAid;
+ private double mCardVersion;
private long mMasterKeyId;
private long[] mSubKeyIds;
@@ -63,7 +65,7 @@ public class ViewKeySecurityTokenFragment
private TextView vStatus;
public static ViewKeySecurityTokenFragment newInstance(long masterKeyId,
- byte[] fingerprints, String userId, byte[] aid) {
+ byte[] fingerprints, String userId, byte[] aid, double version) {
ViewKeySecurityTokenFragment frag = new ViewKeySecurityTokenFragment();
Bundle args = new Bundle();
@@ -71,6 +73,7 @@ public class ViewKeySecurityTokenFragment
args.putByteArray(ARG_FINGERPRINT, fingerprints);
args.putString(ARG_USER_ID, userId);
args.putByteArray(ARG_CARD_AID, aid);
+ args.putDouble(ARG_CARD_VERSION, version);
frag.setArguments(args);
return frag;
@@ -93,6 +96,7 @@ public class ViewKeySecurityTokenFragment
}
mUserId = args.getString(ARG_USER_ID);
mCardAid = args.getByteArray(ARG_CARD_AID);
+ mCardVersion = args.getDouble(ARG_CARD_VERSION);
mMasterKeyId = args.getLong(ARG_MASTER_KEY_ID);
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/SubkeysAdapter.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/SubkeysAdapter.java
index b5ef1d5..ba288ea 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/SubkeysAdapter.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/SubkeysAdapter.java
@@ -121,6 +121,11 @@ public class SubkeysAdapter extends CursorAdapter {
return mCursor.getInt(INDEX_KEY_SIZE);
}
+ public String getCurveOid(int position) {
+ mCursor.moveToPosition(position);
+ return mCursor.getString(INDEX_KEY_CURVE_OID);
+ }
+
public SecretKeyType getSecretKeyType(int position) {
mCursor.moveToPosition(position);
return SecretKeyType.fromNum(mCursor.getInt(INDEX_HAS_SECRET));
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/base/BaseSecurityTokenActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/base/BaseSecurityTokenActivity.java
index 72216bd..fca1666 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/base/BaseSecurityTokenActivity.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/base/BaseSecurityTokenActivity.java
@@ -91,9 +91,9 @@ public abstract class BaseSecurityTokenActivity extends BaseActivity
* Override to implement SecurityToken operations (background thread)
*/
protected void doSecurityTokenInBackground() throws IOException {
+ mSecurityTokenAid = mSecurityTokenHelper.getAid();
mSecurityTokenFingerprints = mSecurityTokenHelper.getFingerprints();
mSecurityTokenUserId = mSecurityTokenHelper.getUserId();
- mSecurityTokenAid = mSecurityTokenHelper.getAid();
}
/**
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/SecurityTokenUtils.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/SecurityTokenUtils.java
index 2f959f9..4066aed 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/SecurityTokenUtils.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/SecurityTokenUtils.java
@@ -17,19 +17,61 @@
package org.sufficientlysecure.keychain.util;
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
import org.bouncycastle.util.Arrays;
import org.bouncycastle.util.encoders.Hex;
-import org.sufficientlysecure.keychain.securitytoken.KeyFormat;
+import org.sufficientlysecure.keychain.pgp.CanonicalizedSecretKey;
+import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
+import org.sufficientlysecure.keychain.securitytoken.ECKeyFormat;
+import org.sufficientlysecure.keychain.securitytoken.RSAKeyFormat;
import org.sufficientlysecure.keychain.securitytoken.KeyType;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.math.BigInteger;
+import java.security.interfaces.ECPrivateKey;
+import java.security.interfaces.ECPublicKey;
import java.security.interfaces.RSAPrivateCrtKey;
public class SecurityTokenUtils {
- public static byte[] createPrivKeyTemplate(RSAPrivateCrtKey secretKey, KeyType slot,
- KeyFormat format) throws IOException {
+ public static byte[] attributesFromSecretKey(final KeyType slot, final CanonicalizedSecretKey secretKey) throws IOException, PgpGeneralException {
+ if (secretKey.isRSA()) {
+ final int mModulusLength = secretKey.getBitStrength();
+ final int mExponentLength = secretKey.getCrtSecretKey().getPublicExponent().bitLength();
+ final byte[] attrs = new byte[6];
+ int i = 0;
+
+ attrs[i++] = (byte)0x01;
+ attrs[i++] = (byte)((mModulusLength >> 8) & 0xff);
+ attrs[i++] = (byte)(mModulusLength & 0xff);
+ attrs[i++] = (byte)((mExponentLength >> 8) & 0xff);
+ attrs[i++] = (byte)(mExponentLength & 0xff);
+ attrs[i++] = RSAKeyFormat.RSAAlgorithmFormat.CRT_WITH_MODULUS.getValue();
+
+ return attrs;
+ } else if (secretKey.isEC()) {
+ final byte[] oid = new ASN1ObjectIdentifier(secretKey.getCurveOid()).getEncoded();
+ final byte[] attrs = new byte[1 + (oid.length - 2) + 1];
+
+ if (slot.equals(KeyType.SIGN))
+ attrs[0] = ECKeyFormat.ECAlgorithmFormat.ECDSA_WITH_PUBKEY.getValue();
+ else {
+ attrs[0] = ECKeyFormat.ECAlgorithmFormat.ECDH_WITH_PUBKEY.getValue();
+ }
+
+ System.arraycopy(oid, 2, attrs, 1, (oid.length - 2));
+
+ attrs[attrs.length - 1] = (byte)0xff;
+
+ return attrs;
+ } else {
+ throw new IOException("Unsupported key type");
+ }
+ }
+
+
+ public static byte[] createRSAPrivKeyTemplate(RSAPrivateCrtKey secretKey, KeyType slot,
+ RSAKeyFormat format) throws IOException {
ByteArrayOutputStream stream = new ByteArrayOutputStream(),
template = new ByteArrayOutputStream(),
data = new ByteArrayOutputStream(),
@@ -86,6 +128,51 @@ public class SecurityTokenUtils {
stream.write(encodeLength(data.size()));
stream.write(data.toByteArray());
+ // Result tlv
+ res.write(Hex.decode("4D"));
+ res.write(encodeLength(stream.size()));
+ res.write(stream.toByteArray());
+
+ return res.toByteArray();
+ }
+
+ public static byte[] createECPrivKeyTemplate(ECPrivateKey secretKey, ECPublicKey publicKey, KeyType slot,
+ ECKeyFormat format) throws IOException {
+ ByteArrayOutputStream stream = new ByteArrayOutputStream(),
+ template = new ByteArrayOutputStream(),
+ data = new ByteArrayOutputStream(),
+ res = new ByteArrayOutputStream();
+
+ final int csize = (int)Math.ceil(publicKey.getParams().getCurve().getField().getFieldSize() / 8.0);
+
+ writeBits(data, secretKey.getS(), csize);
+ template.write(Hex.decode("92"));
+ template.write(encodeLength(data.size()));
+
+ if (format.getAlgorithmFormat().isWithPubkey()) {
+ data.write(Hex.decode("04"));
+ writeBits(data, publicKey.getW().getAffineX(), csize);
+ writeBits(data, publicKey.getW().getAffineY(), csize);
+ template.write(Hex.decode("99"));
+ template.write(encodeLength(1 + 2 * csize));
+ }
+
+ // Bundle up
+
+ // Ext header list data
+ // Control Reference Template to indicate the private key
+ stream.write(slot.getSlot());
+ stream.write(0);
+
+ // Cardholder private key template
+ stream.write(Hex.decode("7F48"));
+ stream.write(encodeLength(template.size()));
+ stream.write(template.toByteArray());
+
+ // Concatenation of key data as defined in DO 7F48
+ stream.write(Hex.decode("5F48"));
+ stream.write(encodeLength(data.size()));
+ stream.write(data.toByteArray());
// Result tlv
res.write(Hex.decode("4D"));
@@ -132,20 +219,21 @@ public class SecurityTokenUtils {
throw new IllegalArgumentException("width <= 0");
}
- byte[] prime = value.toByteArray();
- int stripIdx = 0;
- while (prime[stripIdx] == 0 && stripIdx + 1 < prime.length) {
- stripIdx++;
- }
+ final byte[] prime = value.toByteArray();
+ int skip = 0;
- if (prime.length - stripIdx > width) {
+ while((skip < prime.length) && (prime[skip] == 0)) ++skip;
+
+ if ((prime.length - skip) > width) {
throw new IllegalArgumentException("not enough width to fit value: "
- + prime.length + "/" + width);
+ + (prime.length - skip) + "/" + width);
}
+
byte[] res = new byte[width];
- int empty = width - (prime.length - stripIdx);
- System.arraycopy(prime, stripIdx, res, Math.max(0, empty), Math.min(prime.length, width));
+ System.arraycopy(prime, skip,
+ res, width - (prime.length - skip),
+ prime.length - skip);
stream.write(res, 0, width);
Arrays.fill(res, (byte) 0);
diff --git a/OpenKeychain/src/main/res/layout/create_yubi_key_algorithm_fragment.xml b/OpenKeychain/src/main/res/layout/create_yubi_key_algorithm_fragment.xml
new file mode 100644
index 0000000..c61f4d7
--- /dev/null
+++ b/OpenKeychain/src/main/res/layout/create_yubi_key_algorithm_fragment.xml
@@ -0,0 +1,121 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <ScrollView
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_above="@+id/create_key_buttons"
+ android:fillViewport="true">
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:paddingLeft="16dp"
+ android:paddingRight="16dp">
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="8dp"
+ android:layout_marginTop="16dp"
+ android:text="@string/create_key_yubi_key_algorithm_text"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:layout_marginBottom="16dp" />
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="8dp"
+ android:layout_marginTop="16dp"
+ android:text="@string/create_key_yubi_key_algorithm_sign_text"
+ android:textAppearance="?android:attr/textAppearanceMedium" />
+
+ <org.sufficientlysecure.keychain.ui.util.spinner.FocusFirstItemSpinner
+ android:id="@+id/create_key_yubi_key_algorithm_sign"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:dropDownWidth="wrap_content"
+ android:layout_marginBottom="16dp"/>
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="8dp"
+ android:layout_marginTop="16dp"
+ android:text="@string/create_key_yubi_key_algorithm_dec_text"
+ android:textAppearance="?android:attr/textAppearanceMedium" />
+
+ <org.sufficientlysecure.keychain.ui.util.spinner.FocusFirstItemSpinner
+ android:id="@+id/create_key_yubi_key_algorithm_dec"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:dropDownWidth="wrap_content"
+ android:layout_marginBottom="16dp"/>
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="8dp"
+ android:layout_marginTop="16dp"
+ android:text="@string/create_key_yubi_key_algorithm_auth_text"
+ android:textAppearance="?android:attr/textAppearanceMedium" />
+
+ <org.sufficientlysecure.keychain.ui.util.spinner.FocusFirstItemSpinner
+ android:id="@+id/create_key_yubi_key_algorithm_auth"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:dropDownWidth="wrap_content"/>
+ </LinearLayout>
+ </ScrollView>
+
+ <LinearLayout
+ android:id="@+id/create_key_buttons"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_alignParentBottom="true"
+ android:layout_alignParentLeft="true"
+ android:layout_alignParentStart="true"
+ android:background="?attr/colorButtonRow"
+ android:orientation="horizontal">
+
+ <TextView
+ android:id="@+id/create_key_back_button"
+ style="?android:attr/borderlessButtonStyle"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical"
+ android:layout_weight="1"
+ android:clickable="true"
+ android:drawableLeft="@drawable/ic_chevron_left_grey_24dp"
+ android:drawablePadding="8dp"
+ android:gravity="left|center_vertical"
+ android:minHeight="?android:attr/listPreferredItemHeight"
+ android:paddingLeft="16dp"
+ android:paddingRight="16dp"
+ android:text="@string/btn_back"
+ android:textAllCaps="true"
+ android:textAppearance="?android:attr/textAppearanceMedium" />
+
+ <TextView
+ android:id="@+id/create_key_next_button"
+ style="?android:attr/borderlessButtonStyle"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical"
+ android:layout_weight="1"
+ android:clickable="true"
+ android:drawablePadding="8dp"
+ android:drawableRight="@drawable/ic_chevron_right_grey_24dp"
+ android:gravity="right|center_vertical"
+ android:minHeight="?android:attr/listPreferredItemHeight"
+ android:paddingLeft="16dp"
+ android:paddingRight="16dp"
+ android:text="@string/btn_next"
+ android:textAllCaps="true"
+ android:textAppearance="?android:attr/textAppearanceMedium" />
+ </LinearLayout>
+</RelativeLayout>
diff --git a/OpenKeychain/src/main/res/values/strings.xml b/OpenKeychain/src/main/res/values/strings.xml
index 21b6c09..770d97c 100644
--- a/OpenKeychain/src/main/res/values/strings.xml
+++ b/OpenKeychain/src/main/res/values/strings.xml
@@ -293,6 +293,8 @@
<string name="rsa_4096_description_html">"larger file size, considered secure until 2040+"</string>
<string name="ecc_p256">"ECC P-256"</string>
<string name="ecc_p256_description_html">"very tiny file size, considered secure until 2040 &lt;br/> &lt;u>experimental and not supported by all implementations&lt;/u>"</string>
+ <string name="ecc_p384">"ECC P-384"</string>
+ <string name="ecc_p384_description_html">"very tiny file size, considered secure until 2040 &lt;br/> &lt;u>experimental and not supported by all implementations&lt;/u>"</string>
<string name="ecc_p521">"ECC P-521"</string>
<string name="ecc_p521_description_html">"tiny file size, considered secure until 2040+ &lt;br/> &lt;u>experimental and not supported by all implementations&lt;/u>"</string>
<string name="usage_none">"None (subkey binding only)"</string>
@@ -764,6 +766,7 @@
<string name="edit_key_error_add_subkey">"Add at least one subkey!"</string>
<string name="edit_key_error_bad_security_token_algo">"Algorithm not supported by Security Token!"</string>
<string name="edit_key_error_bad_security_token_size">"Key size not supported by Security Token!"</string>
+ <string name="edit_key_error_bad_security_token_curve">"Curve not supported by Security Token!"</string>
<string name="edit_key_error_bad_security_token_stripped">"Cannot move key to Security Token (either stripped or already on Security Token)!"</string>
<!-- Create key -->
@@ -792,6 +795,10 @@
<string name="create_key_yubi_key_pin_not_correct">"PIN is not correct!"</string>
<string name="create_key_yubi_key_pin_too_short">"PIN must be at least 6 numbers long!"</string>
<string name="create_key_yubi_key_pin_insecure">"Please choose a secure PIN, not 000000, 123456 or similar combinations (the top 20 most chosen PINs are not allowed)"</string>
+ <string name="create_key_yubi_key_algorithm_text">"Please choose an algorithm for each key."</string>
+ <string name="create_key_yubi_key_algorithm_sign_text">Signature key</string>
+ <string name="create_key_yubi_key_algorithm_dec_text">Decryption key</string>
+ <string name="create_key_yubi_key_algorithm_auth_text">Authentication key</string>
<!-- View key -->
<string name="view_key_revoked">"Revoked: Key must not be used anymore!"</string>
diff --git a/OpenKeychain/src/test/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperationTest.java b/OpenKeychain/src/test/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperationTest.java
index 403b6c6..87e7803 100644
--- a/OpenKeychain/src/test/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperationTest.java
+++ b/OpenKeychain/src/test/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperationTest.java
@@ -856,6 +856,7 @@ public class PgpKeyOperationTest {
UncachedKeyRing modified;
+ /*
{ // moveKeyToSecurityToken should fail with BAD_NFC_SIZE when presented with the RSA-3072 key
long keyId = KeyringTestingHelper.getSubkeyId(ringSecurityToken, 2);
parcelSecurityToken.reset();
@@ -864,6 +865,7 @@ public class PgpKeyOperationTest {
assertModifyFailure("moveKeyToSecurityToken operation should fail on invalid key size", ringSecurityToken,
parcelSecurityToken, cryptoInput, LogType.MSG_MF_ERROR_BAD_SECURITY_TOKEN_SIZE);
}
+ */
{ // moveKeyToSecurityToken should fail with BAD_NFC_ALGO when presented with the DSA-1024 key
long keyId = KeyringTestingHelper.getSubkeyId(ringSecurityToken, 0);
diff --git a/OpenKeychain/src/test/java/org/sufficientlysecure/keychain/securitytoken/SecurityTokenUtilsTest.java b/OpenKeychain/src/test/java/org/sufficientlysecure/keychain/securitytoken/SecurityTokenUtilsTest.java
index 0f0bf7a..241a21a 100644
--- a/OpenKeychain/src/test/java/org/sufficientlysecure/keychain/securitytoken/SecurityTokenUtilsTest.java
+++ b/OpenKeychain/src/test/java/org/sufficientlysecure/keychain/securitytoken/SecurityTokenUtilsTest.java
@@ -128,7 +128,6 @@ public class SecurityTokenUtilsTest extends Mockito {
@Test
public void testPrivateKeyTemplateSimple2048() throws Exception {
- KeyFormat format = new KeyFormat(Hex.decode("010000001800"));
RSAPrivateCrtKey key2048 = mock(RSAPrivateCrtKey.class);
byte[] tmp = new byte[128];
Arrays.fill(tmp, (byte) 0x11);
@@ -137,7 +136,8 @@ public class SecurityTokenUtilsTest extends Mockito {
Arrays.fill(tmp, (byte) 0x12);
when(key2048.getPrimeQ()).thenReturn(new BigInteger(tmp));
- when(key2048.getPublicExponent()).thenReturn(new BigInteger("65537"));
+ BigInteger exp = new BigInteger("65537");
+ when(key2048.getPublicExponent()).thenReturn(exp);
Assert.assertArrayEquals(
Hex.decode("4d820115" + // Header TL
@@ -160,8 +160,7 @@ public class SecurityTokenUtilsTest extends Mockito {
"1212121212121212121212121212121212121212121212121212121212121212" +
"1212121212121212121212121212121212121212121212121212121212121212"
),
- SecurityTokenUtils.createPrivKeyTemplate(key2048, KeyType.AUTH, format));
+ SecurityTokenUtils.createRSAPrivKeyTemplate(key2048, KeyType.AUTH, new RSAKeyFormat(2048, exp.bitLength(), RSAKeyFormat.RSAAlgorithmFormat.STANDARD)));
}
@Test
--
2.9.3