diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/Constants.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/Constants.java
index 368e3fb06..f681769b9 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/Constants.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/Constants.java
@@ -26,7 +26,7 @@ import android.os.Environment;
import org.bouncycastle.bcpg.sig.KeyFlags;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.sufficientlysecure.keychain.securitytoken.KeyFormat;
-import org.sufficientlysecure.keychain.securitytoken.RSAKeyFormat;
+import org.sufficientlysecure.keychain.securitytoken.RsaKeyFormat;
import org.sufficientlysecure.keychain.service.SaveKeyringParcel;
import org.sufficientlysecure.keychain.service.SaveKeyringParcel.SubkeyAdd;
@@ -187,9 +187,9 @@ public final class Constants {
* Default key format for OpenPGP smart cards v2: 2048 bit RSA (sign+certify, decrypt, auth)
*/
private static final int ELEN = 17; //65537
- public static final KeyFormat SECURITY_TOKEN_V2_SIGN = new RSAKeyFormat(2048, ELEN, RSAKeyFormat.RSAAlgorithmFormat.CRT_WITH_MODULUS);
- public static final KeyFormat SECURITY_TOKEN_V2_DEC = new RSAKeyFormat(2048, ELEN, RSAKeyFormat.RSAAlgorithmFormat.CRT_WITH_MODULUS);
- public static final KeyFormat SECURITY_TOKEN_V2_AUTH = new RSAKeyFormat(2048, ELEN, RSAKeyFormat.RSAAlgorithmFormat.CRT_WITH_MODULUS);
+ public static final KeyFormat SECURITY_TOKEN_V2_SIGN = RsaKeyFormat.getInstance(2048, ELEN, RsaKeyFormat.RsaImportFormat.CRT_WITH_MODULUS);
+ public static final KeyFormat SECURITY_TOKEN_V2_DEC = RsaKeyFormat.getInstance(2048, ELEN, RsaKeyFormat.RsaImportFormat.CRT_WITH_MODULUS);
+ public static final KeyFormat SECURITY_TOKEN_V2_AUTH = RsaKeyFormat.getInstance(2048, ELEN, RsaKeyFormat.RsaImportFormat.CRT_WITH_MODULUS);
private static boolean isRunningUnitTest() {
try {
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/ECKeyFormat.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/ECKeyFormat.java
deleted file mode 100644
index 11afd0e5b..000000000
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/ECKeyFormat.java
+++ /dev/null
@@ -1,200 +0,0 @@
-/*
- * Copyright (C) 2017 Schürmann & Breitmoser GbR
- *
- * 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 .
- */
-
-package org.sufficientlysecure.keychain.securitytoken;
-
-import androidx.annotation.Nullable;
-
-import com.google.auto.value.AutoValue;
-
-import org.bouncycastle.asn1.ASN1ObjectIdentifier;
-import org.bouncycastle.asn1.nist.NISTNamedCurves;
-import org.bouncycastle.asn1.x9.ECNamedCurveTable;
-import org.bouncycastle.asn1.x9.X9ECParameters;
-import org.bouncycastle.bcpg.PublicKeyAlgorithmTags;
-import org.bouncycastle.bcpg.sig.KeyFlags;
-import org.bouncycastle.math.ec.ECCurve;
-import org.bouncycastle.util.Arrays;
-import org.sufficientlysecure.keychain.service.SaveKeyringParcel;
-import org.sufficientlysecure.keychain.service.SaveKeyringParcel.SubkeyAdd;
-
-import java.io.IOException;
-
-import timber.log.Timber;
-
-
-// 4.3.3.6 Algorithm Attributes
-@AutoValue
-public abstract class ECKeyFormat extends KeyFormat {
-
- public abstract byte[] oidField();
-
- @Nullable // TODO
- public abstract ECAlgorithmFormat ecAlgorithmFormat();
-
- private static final byte ATTRS_IMPORT_FORMAT_WITH_PUBKEY = (byte) 0xff;
-
- ECKeyFormat() {
- super(KeyFormatType.ECKeyFormatType);
- }
-
- public static KeyFormat getInstance(byte[] oidField, ECAlgorithmFormat from) {
- return new AutoValue_ECKeyFormat(oidField, from);
- }
-
- public static ECKeyFormat getInstance(ASN1ObjectIdentifier oidAsn1, ECAlgorithmFormat from) {
- byte[] oidField = asn1ToOidField(oidAsn1);
- return new AutoValue_ECKeyFormat(oidField, from);
- }
-
- public static KeyFormat getInstanceFromBytes(byte[] bytes) {
- if (bytes.length < 2) {
- throw new IllegalArgumentException("Bad length for EC attributes");
- }
-
- int len = bytes.length - 1;
- if (bytes[bytes.length - 1] == ATTRS_IMPORT_FORMAT_WITH_PUBKEY) {
- len -= 1;
- }
-
- final byte[] oidField = new byte[len];
- System.arraycopy(bytes, 1, oidField, 0, len);
- return getInstance(oidField, ECKeyFormat.ECAlgorithmFormat.from(bytes[0], bytes[bytes.length - 1]));
- }
-
- public byte[] toBytes(KeyType slot) {
- byte[] attrs = new byte[1 + oidField().length + 1];
-
- attrs[0] = ecAlgorithmFormat().getAlgorithmId();
- System.arraycopy(oidField(), 0, attrs, 1, oidField().length);
- attrs[attrs.length - 1] = ATTRS_IMPORT_FORMAT_WITH_PUBKEY;
-
- return attrs;
- }
-
- public ASN1ObjectIdentifier asn1ParseOid() {
- ASN1ObjectIdentifier asn1CurveOid = oidFieldToOidAsn1(oidField());
- String curveName = ECNamedCurveTable.getName(asn1CurveOid);
- if (curveName == null) {
- Timber.w("Unknown curve OID: %s. Could be YubiKey firmware bug < 5.2.8. Trying again with last byte removed.", asn1CurveOid.getId());
-
- // https://bugs.chromium.org/p/chromium/issues/detail?id=1120933#c10
- // The OpenPGP applet of a Yubikey with firmware version below 5.2.8 appends
- // a potentially arbitrary byte to the intended byte representation of an ECC
- // curve OID. This case is handled by retrying the decoding with the last
- // byte stripped if the resulting OID does not label a known curve.
- byte[] oidRemoveLastByte = Arrays.copyOf(oidField(), oidField().length - 1);
- ASN1ObjectIdentifier asn1CurveOidYubikey = oidFieldToOidAsn1(oidRemoveLastByte);
- curveName = ECNamedCurveTable.getName(asn1CurveOidYubikey);
-
- if (curveName != null) {
- Timber.w("Detected curve OID: %s", asn1CurveOidYubikey.getId());
- return asn1CurveOidYubikey;
- } else {
- Timber.e("Still Unknown curve OID: %s", asn1CurveOidYubikey.getId());
- return asn1CurveOid;
- }
- }
-
- return asn1CurveOid;
- }
-
- private static byte[] asn1ToOidField(ASN1ObjectIdentifier oidAsn1) {
- byte[] encodedAsn1Oid;
- try {
- encodedAsn1Oid = oidAsn1.getEncoded();
- } catch (IOException e) {
- throw new IllegalStateException("Failed to encode curve OID!");
- }
- byte[] oidField = new byte[encodedAsn1Oid.length - 2];
- System.arraycopy(encodedAsn1Oid, 2, oidField, 0, encodedAsn1Oid.length - 2);
-
- return oidField;
- }
-
- private static ASN1ObjectIdentifier oidFieldToOidAsn1(byte[] oidField) {
- final byte[] boid = new byte[2 + oidField.length];
- boid[0] = (byte) 0x06;
- boid[1] = (byte) oidField.length;
- System.arraycopy(oidField, 0, boid, 2, oidField.length);
- return ASN1ObjectIdentifier.getInstance(boid);
- }
-
- public enum ECAlgorithmFormat {
- ECDH((byte) PublicKeyAlgorithmTags.ECDH, true, false),
- ECDH_WITH_PUBKEY((byte) PublicKeyAlgorithmTags.ECDH, true, true),
- ECDSA((byte) PublicKeyAlgorithmTags.ECDSA, false, false),
- ECDSA_WITH_PUBKEY((byte) PublicKeyAlgorithmTags.ECDSA, false, true);
-
- private final byte mAlgorithmId;
- private final boolean mIsECDH;
- private final boolean mWithPubkey;
-
- ECAlgorithmFormat(final byte algorithmId, final boolean isECDH, final boolean withPubkey) {
- mAlgorithmId = algorithmId;
- mIsECDH = isECDH;
- mWithPubkey = withPubkey;
- }
-
- public static ECKeyFormat.ECAlgorithmFormat from(final byte bFirst, final byte bLast) {
- for (ECKeyFormat.ECAlgorithmFormat format : values()) {
- if (format.mAlgorithmId == bFirst &&
- ((bLast == ATTRS_IMPORT_FORMAT_WITH_PUBKEY) == format.isWithPubkey())) {
- return format;
- }
- }
- return null;
- }
-
- public final byte getAlgorithmId() {
- return mAlgorithmId;
- }
-
- public final boolean isECDH() {
- return mIsECDH;
- }
-
- public final boolean isWithPubkey() {
- return mWithPubkey;
- }
- }
-
- public void addToSaveKeyringParcel(SaveKeyringParcel.Builder builder, int keyFlags) {
- ASN1ObjectIdentifier oidAsn1 = asn1ParseOid();
- final X9ECParameters params = NISTNamedCurves.getByOID(oidAsn1);
- 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 (oidAsn1.equals(NISTNamedCurves.getOID("P-256"))) {
- scurve = SaveKeyringParcel.Curve.NIST_P256;
- } else if (oidAsn1.equals(NISTNamedCurves.getOID("P-384"))) {
- scurve = SaveKeyringParcel.Curve.NIST_P384;
- } else if (oidAsn1.equals(NISTNamedCurves.getOID("P-521"))) {
- scurve = SaveKeyringParcel.Curve.NIST_P521;
- } else {
- throw new IllegalArgumentException("Unsupported curve " + oidAsn1);
- }
-
- builder.addSubkeyAdd(SubkeyAdd.createSubkeyAdd(algo, curve.getFieldSize(), scurve, keyFlags, 0L));
- }
-}
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 000000000..9dc7af9ff
--- /dev/null
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/EcKeyFormat.java
@@ -0,0 +1,131 @@
+/*
+ * Copyright (C) 2017 Schürmann & Breitmoser GbR
+ *
+ * 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 .
+ */
+
+package org.sufficientlysecure.keychain.securitytoken;
+
+import com.google.auto.value.AutoValue;
+
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.nist.NISTNamedCurves;
+import org.bouncycastle.asn1.x9.X9ECParameters;
+import org.bouncycastle.bcpg.PublicKeyAlgorithmTags;
+import org.bouncycastle.bcpg.sig.KeyFlags;
+import org.bouncycastle.math.ec.ECCurve;
+import org.sufficientlysecure.keychain.service.SaveKeyringParcel;
+import org.sufficientlysecure.keychain.service.SaveKeyringParcel.SubkeyAdd;
+
+
+// OpenPGP Card Spec: Algorithm Attributes: ECC
+@AutoValue
+public abstract class EcKeyFormat extends KeyFormat {
+
+ public abstract int algorithmId();
+
+ public abstract ASN1ObjectIdentifier curveOid();
+
+ public abstract boolean withPubkey();
+
+ private static final byte ATTRS_IMPORT_FORMAT_WITH_PUBKEY = (byte) 0xff;
+
+ public static EcKeyFormat getInstance(int algorithmId, ASN1ObjectIdentifier oid, boolean withPubkey) {
+ return new AutoValue_EcKeyFormat(algorithmId, oid, withPubkey);
+ }
+
+ public static EcKeyFormat getInstanceForKeyGeneration(KeyType keyType, ASN1ObjectIdentifier oidAsn1) {
+ if (keyType == KeyType.ENCRYPT) {
+ return getInstance(PublicKeyAlgorithmTags.ECDH, oidAsn1, true);
+ } else { // SIGN, AUTH
+ if (EcObjectIdentifiers.ED25519.equals(oidAsn1)) {
+ return getInstance(PublicKeyAlgorithmTags.EDDSA, oidAsn1, true);
+ } else {
+ return getInstance(PublicKeyAlgorithmTags.ECDSA, oidAsn1, true);
+ }
+ }
+ }
+
+ public static EcKeyFormat getInstanceFromBytes(byte[] bytes) {
+ if (bytes.length < 2) {
+ throw new IllegalArgumentException("Bad length for EC attributes");
+ }
+
+ int algorithmId = bytes[0];
+ int oidLen = bytes.length - 1;
+
+ boolean withPubkey = false;
+ if (bytes[bytes.length - 1] == ATTRS_IMPORT_FORMAT_WITH_PUBKEY) {
+ withPubkey = true;
+ oidLen -= 1;
+ }
+
+ final byte[] oidField = new byte[oidLen];
+ System.arraycopy(bytes, 1, oidField, 0, oidLen);
+ ASN1ObjectIdentifier oid = EcObjectIdentifiers.parseOid(oidField);
+
+ return getInstance(algorithmId, oid, withPubkey);
+ }
+
+ public byte[] toBytes(KeyType slot) {
+ byte[] oidField = EcObjectIdentifiers.asn1ToOidField(curveOid());
+
+ int len = 1 + oidField.length;
+ if (withPubkey()) {
+ len += 1;
+ }
+ byte[] attrs = new byte[len];
+
+ attrs[0] = (byte) algorithmId();
+ System.arraycopy(oidField, 0, attrs, 1, oidField.length);
+ if (withPubkey()) {
+ attrs[len - 1] = ATTRS_IMPORT_FORMAT_WITH_PUBKEY;
+ }
+
+ return attrs;
+ }
+
+ public boolean isX25519() {
+ return EcObjectIdentifiers.X25519.equals(curveOid());
+ }
+
+ public final boolean isEdDsa() {
+ return algorithmId() == PublicKeyAlgorithmTags.EDDSA;
+ }
+
+ public void addToSaveKeyringParcel(SaveKeyringParcel.Builder builder, int keyFlags) {
+ final X9ECParameters params = NISTNamedCurves.getByOID(curveOid());
+ 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 (EcObjectIdentifiers.NIST_P_256.equals(curveOid())) {
+ scurve = SaveKeyringParcel.Curve.NIST_P256;
+ } else if (EcObjectIdentifiers.NIST_P_384.equals(curveOid())) {
+ scurve = SaveKeyringParcel.Curve.NIST_P384;
+ } else if (EcObjectIdentifiers.NIST_P_521.equals(curveOid())) {
+ scurve = SaveKeyringParcel.Curve.NIST_P521;
+ } else {
+ throw new IllegalArgumentException("Unsupported curve " + curveOid());
+ }
+
+ builder.addSubkeyAdd(SubkeyAdd.createSubkeyAdd(algo, curve.getFieldSize(), scurve, keyFlags, 0L));
+ }
+
+}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/EcObjectIdentifiers.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/EcObjectIdentifiers.java
new file mode 100644
index 000000000..a55f6d4c2
--- /dev/null
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/EcObjectIdentifiers.java
@@ -0,0 +1,72 @@
+package org.sufficientlysecure.keychain.securitytoken;
+
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.cryptlib.CryptlibObjectIdentifiers;
+import org.bouncycastle.asn1.gnu.GNUObjectIdentifiers;
+import org.bouncycastle.asn1.sec.SECObjectIdentifiers;
+import org.bouncycastle.asn1.teletrust.TeleTrusTObjectIdentifiers;
+
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.HashSet;
+
+import timber.log.Timber;
+
+public class EcObjectIdentifiers {
+
+ public static final ASN1ObjectIdentifier NIST_P_256 = SECObjectIdentifiers.secp256r1;
+ public static final ASN1ObjectIdentifier NIST_P_384 = SECObjectIdentifiers.secp384r1;
+ public static final ASN1ObjectIdentifier NIST_P_521 = SECObjectIdentifiers.secp521r1;
+ public static final ASN1ObjectIdentifier BRAINPOOL_P256_R1 = TeleTrusTObjectIdentifiers.brainpoolP256r1;
+ public static final ASN1ObjectIdentifier BRAINPOOL_P512_R1 = TeleTrusTObjectIdentifiers.brainpoolP512r1;
+ public static final ASN1ObjectIdentifier ED25519 = GNUObjectIdentifiers.Ed25519; // for use with EdDSA
+ public static final ASN1ObjectIdentifier X25519 = CryptlibObjectIdentifiers.curvey25519; // for use with ECDH
+
+ public static HashSet sOids = new HashSet<>(Arrays.asList(
+ NIST_P_256, NIST_P_384, NIST_P_521, BRAINPOOL_P256_R1, BRAINPOOL_P512_R1, ED25519, X25519
+ ));
+
+ public static ASN1ObjectIdentifier parseOid(byte[] oidField) {
+ ASN1ObjectIdentifier asn1CurveOid = oidFieldToOidAsn1(oidField);
+ if (sOids.contains(asn1CurveOid)) {
+ return asn1CurveOid;
+ }
+ Timber.w("Unknown curve OID: %s. Could be YubiKey firmware bug < 5.2.8. Trying again with last byte removed.", asn1CurveOid.getId());
+
+ // https://bugs.chromium.org/p/chromium/issues/detail?id=1120933#c10
+ // The OpenPGP applet of a Yubikey with firmware version below 5.2.8 appends
+ // a potentially arbitrary byte to the intended byte representation of an ECC
+ // curve OID. This case is handled by retrying the decoding with the last
+ // byte stripped if the resulting OID does not label a known curve.
+ byte[] oidRemoveLastByte = Arrays.copyOf(oidField, oidField.length - 1);
+ ASN1ObjectIdentifier asn1CurveOidYubikey = oidFieldToOidAsn1(oidRemoveLastByte);
+ if (sOids.contains(asn1CurveOidYubikey)) {
+ Timber.w("Detected curve OID: %s", asn1CurveOidYubikey.getId());
+ } else {
+ Timber.e("Still Unknown curve OID: %s", asn1CurveOidYubikey.getId());
+ }
+ return asn1CurveOidYubikey;
+ }
+
+ public static byte[] asn1ToOidField(ASN1ObjectIdentifier oidAsn1) {
+ byte[] encodedAsn1Oid;
+ try {
+ encodedAsn1Oid = oidAsn1.getEncoded();
+ } catch (IOException e) {
+ throw new IllegalStateException("Failed to encode curve OID!");
+ }
+ byte[] oidField = new byte[encodedAsn1Oid.length - 2];
+ System.arraycopy(encodedAsn1Oid, 2, oidField, 0, encodedAsn1Oid.length - 2);
+
+ return oidField;
+ }
+
+ public static ASN1ObjectIdentifier oidFieldToOidAsn1(byte[] oidField) {
+ final byte[] boid = new byte[2 + oidField.length];
+ boid[0] = (byte) 0x06;
+ boid[1] = (byte) oidField.length;
+ System.arraycopy(oidField, 0, boid, 2, oidField.length);
+ return ASN1ObjectIdentifier.getInstance(boid);
+ }
+
+}
\ No newline at end of file
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/EdDSAKeyFormat.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/EdDSAKeyFormat.java
deleted file mode 100644
index 1ed413651..000000000
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/EdDSAKeyFormat.java
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * Copyright (C) 2017 Schürmann & Breitmoser GbR
- *
- * 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 .
- */
-
-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;
-import org.sufficientlysecure.keychain.service.SaveKeyringParcel.SubkeyAdd;
-
-
-// 4.3.3.6 Algorithm Attributes
-public class EdDSAKeyFormat extends KeyFormat {
-
- public EdDSAKeyFormat() {
- super(KeyFormatType.EdDSAKeyFormatType);
- }
-
- @Override
- public void addToSaveKeyringParcel(SaveKeyringParcel.Builder builder, int keyFlags) {
- builder.addSubkeyAdd(SubkeyAdd.createSubkeyAdd(SaveKeyringParcel.Algorithm.EDDSA,
- null, null, 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 1f56087ed..bcbb2e731 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/KeyFormat.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/KeyFormat.java
@@ -17,62 +17,44 @@
package org.sufficientlysecure.keychain.securitytoken;
-import org.bouncycastle.asn1.nist.NISTNamedCurves;
import org.bouncycastle.bcpg.PublicKeyAlgorithmTags;
import org.sufficientlysecure.keychain.service.SaveKeyringParcel;
import org.sufficientlysecure.keychain.ui.CreateSecurityTokenAlgorithmFragment;
public abstract class KeyFormat {
- public enum KeyFormatType {
- RSAKeyFormatType,
- ECKeyFormatType,
- EdDSAKeyFormatType
- }
-
- private final KeyFormatType mKeyFormatType;
-
- KeyFormat(final KeyFormatType keyFormatType) {
- mKeyFormatType = keyFormatType;
- }
-
- public final KeyFormatType keyFormatType() {
- return mKeyFormatType;
- }
-
public static KeyFormat fromBytes(byte[] bytes) {
switch (bytes[0]) {
case PublicKeyAlgorithmTags.RSA_GENERAL:
- return RSAKeyFormat.fromBytes(bytes);
+ return RsaKeyFormat.getInstanceFromBytes(bytes);
case PublicKeyAlgorithmTags.ECDH:
case PublicKeyAlgorithmTags.ECDSA:
- return ECKeyFormat.getInstanceFromBytes(bytes);
case PublicKeyAlgorithmTags.EDDSA:
- return new EdDSAKeyFormat();
-
+ return EcKeyFormat.getInstanceFromBytes(bytes);
default:
throw new IllegalArgumentException("Unsupported Algorithm id " + bytes[0]);
}
}
+ public abstract byte[] toBytes(KeyType slot);
+
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;
+ final int algorithmId = forEncryption ? PublicKeyAlgorithmTags.ECDH : PublicKeyAlgorithmTags.ECDSA;
switch (t) {
case RSA_2048:
- return new RSAKeyFormat(2048, elen, RSAKeyFormat.RSAAlgorithmFormat.CRT_WITH_MODULUS);
+ return RsaKeyFormat.getInstance(2048, elen, RsaKeyFormat.RsaImportFormat.CRT_WITH_MODULUS);
case RSA_3072:
- return new RSAKeyFormat(3072, elen, RSAKeyFormat.RSAAlgorithmFormat.CRT_WITH_MODULUS);
+ return RsaKeyFormat.getInstance(3072, elen, RsaKeyFormat.RsaImportFormat.CRT_WITH_MODULUS);
case RSA_4096:
- return new RSAKeyFormat(4096, elen, RSAKeyFormat.RSAAlgorithmFormat.CRT_WITH_MODULUS);
+ return RsaKeyFormat.getInstance(4096, elen, RsaKeyFormat.RsaImportFormat.CRT_WITH_MODULUS);
case ECC_P256:
- return ECKeyFormat.getInstance(NISTNamedCurves.getOID("P-256"), kf);
+ return EcKeyFormat.getInstance(algorithmId, EcObjectIdentifiers.NIST_P_256, true);
case ECC_P384:
- return ECKeyFormat.getInstance(NISTNamedCurves.getOID("P-384"), kf);
+ return EcKeyFormat.getInstance(algorithmId, EcObjectIdentifiers.NIST_P_384, true);
case ECC_P521:
- return ECKeyFormat.getInstance(NISTNamedCurves.getOID("P-521"), kf);
+ return EcKeyFormat.getInstance(algorithmId, EcObjectIdentifiers.NIST_P_521, true);
}
throw new IllegalArgumentException("Unsupported Algorithm id " + t);
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/RSAKeyFormat.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/RSAKeyFormat.java
deleted file mode 100644
index ce398cf5a..000000000
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/RSAKeyFormat.java
+++ /dev/null
@@ -1,103 +0,0 @@
-/*
- * Copyright (C) 2017 Schürmann & Breitmoser GbR
- *
- * 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 .
- */
-
-package org.sufficientlysecure.keychain.securitytoken;
-
-import org.bouncycastle.bcpg.PublicKeyAlgorithmTags;
-import org.sufficientlysecure.keychain.service.SaveKeyringParcel;
-import org.sufficientlysecure.keychain.service.SaveKeyringParcel.SubkeyAdd;
-
-
-// 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 static KeyFormat fromBytes(byte[] bytes) {
- 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]));
- }
-
- public RSAAlgorithmFormat getAlgorithmFormat() {
- return mRSAAlgorithmFormat;
- }
-
- public enum RSAAlgorithmFormat {
- STANDARD((byte) 0x00, false, false),
- STANDARD_WITH_MODULUS((byte) 0x01, false, true),
- CRT((byte) 0x02, true, false),
- CRT_WITH_MODULUS((byte) 0x03, true, true);
-
- private byte mImportFormat;
- private boolean mIncludeModulus;
- private boolean mIncludeCrt;
-
- RSAAlgorithmFormat(byte importFormat, boolean includeCrt, boolean includeModulus) {
- mImportFormat = importFormat;
- mIncludeModulus = includeModulus;
- mIncludeCrt = includeCrt;
- }
-
- public static RSAAlgorithmFormat from(byte importFormatByte) {
- for (RSAAlgorithmFormat format : values()) {
- if (format.mImportFormat == importFormatByte) {
- return format;
- }
- }
- return null;
- }
-
- public byte getImportFormat() {
- return mImportFormat;
- }
-
- public boolean isIncludeModulus() {
- return mIncludeModulus;
- }
-
- public boolean isIncludeCrt() {
- return mIncludeCrt;
- }
- }
-
- public void addToSaveKeyringParcel(SaveKeyringParcel.Builder builder, int keyFlags) {
- builder.addSubkeyAdd(SubkeyAdd.createSubkeyAdd(SaveKeyringParcel.Algorithm.RSA,
- mModulusLength, null, keyFlags, 0L));
- }
-}
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 000000000..ddaae6a0e
--- /dev/null
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/RsaKeyFormat.java
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2017 Schürmann & Breitmoser GbR
+ *
+ * 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 .
+ */
+
+package org.sufficientlysecure.keychain.securitytoken;
+
+import androidx.annotation.RestrictTo;
+
+import com.google.auto.value.AutoValue;
+
+import org.bouncycastle.bcpg.PublicKeyAlgorithmTags;
+import org.sufficientlysecure.keychain.service.SaveKeyringParcel;
+import org.sufficientlysecure.keychain.service.SaveKeyringParcel.SubkeyAdd;
+
+
+// OpenPGP Card Spec: Algorithm Attributes: RSA
+@AutoValue
+public abstract class RsaKeyFormat extends KeyFormat {
+
+ public static final int ALGORITHM_ID = PublicKeyAlgorithmTags.RSA_GENERAL;
+
+ public abstract int modulusLength();
+
+ public abstract int exponentLength();
+
+ public abstract RsaImportFormat rsaImportFormat();
+
+ public static RsaKeyFormat getInstance(int modulusLength, int exponentLength, RsaImportFormat from) {
+ return new AutoValue_RsaKeyFormat(modulusLength, exponentLength, from);
+ }
+
+ public static RsaKeyFormat getInstanceDefault2048BitFormat() {
+ return getInstance(2048, 4, RsaImportFormat.CRT_WITH_MODULUS);
+ }
+
+ public RsaKeyFormat withModulus(int modulus) {
+ return RsaKeyFormat.getInstance(modulus, exponentLength(), rsaImportFormat());
+ }
+
+ public static KeyFormat getInstanceFromBytes(byte[] bytes) {
+ if (bytes.length < 6) {
+ throw new IllegalArgumentException("Bad length for RSA attributes");
+ }
+ int modulusLength = bytes[1] << 8 | bytes[2];
+ int exponentLength = bytes[3] << 8 | bytes[4];
+ RsaImportFormat importFormat = RsaImportFormat.from(bytes[5]);
+
+ return getInstance(modulusLength, exponentLength, importFormat);
+ }
+
+ @Override
+ public byte[] toBytes(KeyType slot) {
+ int i = 0;
+ byte[] attrs = new byte[6];
+ attrs[i++] = (byte) ALGORITHM_ID;
+ attrs[i++] = (byte) ((modulusLength() >> 8) & 0xff);
+ attrs[i++] = (byte) (modulusLength() & 0xff);
+ attrs[i++] = (byte) ((exponentLength() >> 8) & 0xff);
+ attrs[i++] = (byte) (exponentLength() & 0xff);
+ attrs[i] = rsaImportFormat().getImportFormat();
+
+ return attrs;
+ }
+
+ public enum RsaImportFormat {
+ STANDARD((byte) 0x00, false, false),
+ STANDARD_WITH_MODULUS((byte) 0x01, false, true),
+ CRT((byte) 0x02, true, false),
+ CRT_WITH_MODULUS((byte) 0x03, true, true);
+
+ private byte importFormat;
+ private boolean includeModulus;
+ private boolean includeCrt;
+
+ RsaImportFormat(byte importFormat, boolean includeCrt, boolean includeModulus) {
+ this.importFormat = importFormat;
+ this.includeModulus = includeModulus;
+ this.includeCrt = includeCrt;
+ }
+
+ public static RsaImportFormat from(byte importFormatByte) {
+ for (RsaImportFormat format : values()) {
+ if (format.importFormat == importFormatByte) {
+ return format;
+ }
+ }
+ return null;
+ }
+
+ public byte getImportFormat() {
+ return importFormat;
+ }
+
+ public boolean isIncludeModulus() {
+ return includeModulus;
+ }
+
+ public boolean isIncludeCrt() {
+ return includeCrt;
+ }
+ }
+
+ public void addToSaveKeyringParcel(SaveKeyringParcel.Builder builder, int keyFlags) {
+ builder.addSubkeyAdd(SubkeyAdd.createSubkeyAdd(SaveKeyringParcel.Algorithm.RSA,
+ modulusLength(), null, keyFlags, 0L));
+ }
+}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/SCP11bSecureMessaging.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/SCP11bSecureMessaging.java
index 3fab42670..3a140f61b 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/SCP11bSecureMessaging.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/SCP11bSecureMessaging.java
@@ -150,24 +150,24 @@ class SCP11bSecureMessaging implements SecureMessaging {
&& (mMacChaining != null);
}
- private static ECParameterSpec getAlgorithmParameterSpec(final ECKeyFormat kf)
+ private static ECParameterSpec getAlgorithmParameterSpec(final EcKeyFormat kf)
throws NoSuchProviderException, NoSuchAlgorithmException, InvalidParameterSpecException {
final AlgorithmParameters algoParams = AlgorithmParameters.getInstance(SCP11B_KEY_AGREEMENT_KEY_ALGO, PROVIDER);
- algoParams.init(new ECGenParameterSpec(ECNamedCurveTable.getName(kf.asn1ParseOid())));
+ algoParams.init(new ECGenParameterSpec(ECNamedCurveTable.getName(kf.curveOid())));
return algoParams.getParameterSpec(ECParameterSpec.class);
}
- private static ECPublicKey newECDHPublicKey(final ECKeyFormat kf, byte[] data)
+ private static ECPublicKey newECDHPublicKey(final EcKeyFormat kf, byte[] data)
throws InvalidKeySpecException, NoSuchAlgorithmException,
InvalidParameterSpecException, NoSuchProviderException {
if (ecdhFactory == null) {
ecdhFactory = KeyFactory.getInstance(SCP11B_KEY_AGREEMENT_KEY_TYPE, PROVIDER);
}
- final X9ECParameters params = NISTNamedCurves.getByOID(kf.asn1ParseOid());
+ final X9ECParameters params = NISTNamedCurves.getByOID(kf.curveOid());
if (params == null) {
throw new InvalidParameterSpecException("unsupported curve");
}
@@ -185,7 +185,7 @@ class SCP11bSecureMessaging implements SecureMessaging {
return (ECPublicKey)(ecdhFactory.generatePublic(pk));
}
- private static KeyPair generateECDHKeyPair(final ECKeyFormat kf)
+ private static KeyPair generateECDHKeyPair(final EcKeyFormat kf)
throws NoSuchProviderException, NoSuchAlgorithmException,
InvalidParameterSpecException, InvalidAlgorithmParameterException {
final KeyPairGenerator gen = KeyPairGenerator.getInstance(SCP11B_KEY_AGREEMENT_KEY_ALGO, PROVIDER);
@@ -200,7 +200,7 @@ class SCP11bSecureMessaging implements SecureMessaging {
}
private static ECPublicKey verifyCertificate(final Context ctx,
- final ECKeyFormat kf,
+ final EcKeyFormat kf,
final byte[] data) throws IOException {
try {
@@ -299,13 +299,13 @@ class SCP11bSecureMessaging implements SecureMessaging {
final KeyFormat kf = KeyFormat.fromBytes(tlvs[0].mV);
- if (kf.keyFormatType() != KeyFormat.KeyFormatType.ECKeyFormatType) {
+ if (!(kf instanceof EcKeyFormat)) {
throw new SecureMessagingException("invalid format of secure messaging key");
}
- final ECKeyFormat eckf = (ECKeyFormat)kf;
+ final EcKeyFormat eckf = (EcKeyFormat)kf;
- if (eckf.asn1ParseOid() == null) {
+ if (eckf.curveOid() == null) {
throw new SecureMessagingException("unsupported curve");
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/SecurityTokenUtils.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/SecurityTokenUtils.java
index 2d249779b..55b1c7a17 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/SecurityTokenUtils.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/SecurityTokenUtils.java
@@ -18,11 +18,10 @@
package org.sufficientlysecure.keychain.securitytoken;
import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.bcpg.PublicKeyAlgorithmTags;
import org.bouncycastle.util.Arrays;
import org.bouncycastle.util.encoders.Hex;
import org.sufficientlysecure.keychain.pgp.CanonicalizedSecretKey;
-import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
-import org.sufficientlysecure.keychain.securitytoken.RSAKeyFormat.RSAAlgorithmFormat;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
@@ -37,15 +36,15 @@ public class SecurityTokenUtils {
KeyFormat formatForKeyType)
throws IOException {
if (secretKey.isRSA()) {
- return attributesForRsaKey(secretKey.getBitStrength(), (RSAKeyFormat) formatForKeyType);
+ return attributesForRsaKey(secretKey.getBitStrength(), (RsaKeyFormat) formatForKeyType);
} else if (secretKey.isEC()) {
byte[] oid = new ASN1ObjectIdentifier(secretKey.getCurveOid()).getEncoded();
byte[] attrs = new byte[1 + (oid.length - 2) + 1];
if (slot.equals(KeyType.ENCRYPT))
- attrs[0] = ECKeyFormat.ECAlgorithmFormat.ECDH_WITH_PUBKEY.getAlgorithmId();
+ attrs[0] = PublicKeyAlgorithmTags.ECDH;
else { // SIGN and AUTH is ECDSA
- attrs[0] = ECKeyFormat.ECAlgorithmFormat.ECDSA_WITH_PUBKEY.getAlgorithmId();
+ attrs[0] = PublicKeyAlgorithmTags.ECDSA;
}
System.arraycopy(oid, 2, attrs, 1, (oid.length - 2));
@@ -58,13 +57,13 @@ public class SecurityTokenUtils {
}
}
- private static byte[] attributesForRsaKey(int modulusLength, RSAKeyFormat formatForKeyType) {
- RSAAlgorithmFormat algorithmFormat = formatForKeyType.getAlgorithmFormat();
- int exponentLength = formatForKeyType.getExponentLength();
+ private static byte[] attributesForRsaKey(int modulusLength, RsaKeyFormat formatForKeyType) {
+ RsaKeyFormat.RsaImportFormat algorithmFormat = formatForKeyType.rsaImportFormat();
+ int exponentLength = formatForKeyType.exponentLength();
int i = 0;
byte[] attrs = new byte[6];
- attrs[i++] = (byte) 0x01;
+ attrs[i++] = (byte) RsaKeyFormat.ALGORITHM_ID;
attrs[i++] = (byte) ((modulusLength >> 8) & 0xff);
attrs[i++] = (byte) (modulusLength & 0xff);
attrs[i++] = (byte) ((exponentLength >> 8) & 0xff);
@@ -87,18 +86,18 @@ public class SecurityTokenUtils {
}
public static byte[] createRSAPrivKeyTemplate(RSAPrivateCrtKey secretKey, KeyType slot,
- RSAKeyFormat format) throws IOException {
+ RsaKeyFormat format) throws IOException {
ByteArrayOutputStream stream = new ByteArrayOutputStream(),
template = new ByteArrayOutputStream(),
data = new ByteArrayOutputStream(),
res = new ByteArrayOutputStream();
- int expLengthBytes = (format.getExponentLength() + 7) / 8;
+ int expLengthBytes = (format.exponentLength() + 7) / 8;
// Public exponent
template.write(new byte[]{(byte) 0x91, (byte) expLengthBytes});
writeBits(data, secretKey.getPublicExponent(), expLengthBytes);
- final int modLengthBytes = format.getModulusLength() / 8;
+ final int modLengthBytes = format.modulusLength() / 8;
final byte[] lengthByteArray = generateLengthByteArray(modLengthBytes / 2);
// Prime P, length modLengthBytes / 2
@@ -112,7 +111,7 @@ public class SecurityTokenUtils {
writeBits(data, secretKey.getPrimeQ(), modLengthBytes / 2);
- if (format.getAlgorithmFormat().isIncludeCrt()) {
+ if (format.rsaImportFormat().isIncludeCrt()) {
// Coefficient (1/q mod p), length modLengthBytes / 2
template.write(Hex.decode("94"));
template.write(lengthByteArray);
@@ -129,7 +128,7 @@ public class SecurityTokenUtils {
writeBits(data, secretKey.getPrimeExponentQ(), modLengthBytes / 2);
}
- if (format.getAlgorithmFormat().isIncludeModulus()) {
+ if (format.rsaImportFormat().isIncludeModulus()) {
// Modulus, length modLengthBytes, last item in private key template
template.write(Hex.decode("97"));
template.write(generateLengthByteArray(modLengthBytes));
@@ -162,7 +161,7 @@ public class SecurityTokenUtils {
}
public static byte[] createECPrivKeyTemplate(ECPrivateKey secretKey, ECPublicKey publicKey, KeyType slot,
- ECKeyFormat format) throws IOException {
+ EcKeyFormat format) throws IOException {
ByteArrayOutputStream stream = new ByteArrayOutputStream(),
template = new ByteArrayOutputStream(),
data = new ByteArrayOutputStream(),
@@ -174,7 +173,7 @@ public class SecurityTokenUtils {
template.write(Hex.decode("92"));
template.write(encodeLength(data.size()));
- if (format.ecAlgorithmFormat().isWithPubkey()) {
+ if (format.withPubkey()) {
data.write(Hex.decode("04"));
writeBits(data, publicKey.getW().getAffineX(), csize);
writeBits(data, publicKey.getW().getAffineY(), csize);
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/operations/PsoDecryptTokenOp.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/operations/PsoDecryptTokenOp.java
index ea078e686..537c92b5a 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/operations/PsoDecryptTokenOp.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/operations/PsoDecryptTokenOp.java
@@ -21,7 +21,6 @@ package org.sufficientlysecure.keychain.securitytoken.operations;
import androidx.annotation.NonNull;
import androidx.annotation.VisibleForTesting;
-import org.bouncycastle.asn1.cryptlib.CryptlibObjectIdentifiers;
import org.bouncycastle.asn1.x9.ECNamedCurveTable;
import org.bouncycastle.asn1.x9.X9ECParameters;
import org.bouncycastle.jcajce.util.MessageDigestUtils;
@@ -34,9 +33,10 @@ import org.bouncycastle.util.encoders.Hex;
import org.sufficientlysecure.keychain.pgp.CanonicalizedPublicKey;
import org.sufficientlysecure.keychain.securitytoken.CardException;
import org.sufficientlysecure.keychain.securitytoken.CommandApdu;
-import org.sufficientlysecure.keychain.securitytoken.ECKeyFormat;
+import org.sufficientlysecure.keychain.securitytoken.EcKeyFormat;
import org.sufficientlysecure.keychain.securitytoken.KeyFormat;
import org.sufficientlysecure.keychain.securitytoken.ResponseApdu;
+import org.sufficientlysecure.keychain.securitytoken.RsaKeyFormat;
import org.sufficientlysecure.keychain.securitytoken.SecurityTokenConnection;
import java.io.IOException;
@@ -73,15 +73,12 @@ public class PsoDecryptTokenOp {
connection.verifyPinForOther();
KeyFormat kf = connection.getOpenPgpCapabilities().getEncryptKeyFormat();
- switch (kf.keyFormatType()) {
- case RSAKeyFormatType:
- return decryptSessionKeyRsa(encryptedSessionKeyMpi);
-
- case ECKeyFormatType:
- return decryptSessionKeyEcdh(encryptedSessionKeyMpi, (ECKeyFormat) kf, publicKey);
-
- default:
- throw new CardException("Unknown encryption key type!");
+ if (kf instanceof RsaKeyFormat) {
+ return decryptSessionKeyRsa(encryptedSessionKeyMpi);
+ } else if (kf instanceof EcKeyFormat) {
+ return decryptSessionKeyEcdh(encryptedSessionKeyMpi, (EcKeyFormat) kf, publicKey);
+ } else {
+ throw new CardException("Unknown encryption key type!");
}
}
@@ -112,7 +109,7 @@ public class PsoDecryptTokenOp {
return psoDecipherPayload;
}
- private byte[] decryptSessionKeyEcdh(byte[] encryptedSessionKeyMpi, ECKeyFormat eckf, CanonicalizedPublicKey publicKey)
+ private byte[] decryptSessionKeyEcdh(byte[] encryptedSessionKeyMpi, EcKeyFormat eckf, CanonicalizedPublicKey publicKey)
throws IOException {
int mpiLength = getMpiLength(encryptedSessionKeyMpi);
byte[] encryptedPoint = Arrays.copyOfRange(encryptedSessionKeyMpi, 2, mpiLength + 2);
@@ -165,14 +162,13 @@ public class PsoDecryptTokenOp {
byte[] keyEncryptionKey = response.getData();
int xLen;
- boolean isCurve25519 = CryptlibObjectIdentifiers.curvey25519.equals(eckf.asn1ParseOid());
- if (isCurve25519) {
+ if (eckf.isX25519()) {
xLen = keyEncryptionKey.length;
} else {
xLen = (keyEncryptionKey.length - 1) / 2;
}
final byte[] kekX = new byte[xLen];
- System.arraycopy(keyEncryptionKey, isCurve25519 ? 0 : 1, kekX, 0, xLen);
+ System.arraycopy(keyEncryptionKey, eckf.isX25519() ? 0 : 1, kekX, 0, xLen);
final byte[] keyEnc = new byte[encryptedSessionKeyMpi[mpiLength + 2]];
@@ -206,12 +202,11 @@ public class PsoDecryptTokenOp {
}
}
- private byte[] getEcDecipherPayload(ECKeyFormat eckf, byte[] encryptedPoint) throws CardException {
- // TODO is this the right curve?
- if (CryptlibObjectIdentifiers.curvey25519.equals(eckf.asn1ParseOid())) {
+ private byte[] getEcDecipherPayload(EcKeyFormat eckf, byte[] encryptedPoint) throws CardException {
+ if (eckf.isX25519()) {
return Arrays.copyOfRange(encryptedPoint, 1, 33);
} else {
- X9ECParameters x9Params = ECNamedCurveTable.getByOID(eckf.asn1ParseOid());
+ X9ECParameters x9Params = ECNamedCurveTable.getByOID(eckf.curveOid());
ECPoint p = x9Params.getCurve().decodePoint(encryptedPoint);
if (!p.isValid()) {
throw new CardException("Invalid EC point!");
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/operations/SecurityTokenChangeKeyTokenOp.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/operations/SecurityTokenChangeKeyTokenOp.java
index 6c784326d..ff5dcafb2 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/operations/SecurityTokenChangeKeyTokenOp.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/operations/SecurityTokenChangeKeyTokenOp.java
@@ -32,11 +32,11 @@ import org.sufficientlysecure.keychain.pgp.CanonicalizedSecretKey;
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
import org.sufficientlysecure.keychain.securitytoken.CardException;
import org.sufficientlysecure.keychain.securitytoken.CommandApdu;
-import org.sufficientlysecure.keychain.securitytoken.ECKeyFormat;
+import org.sufficientlysecure.keychain.securitytoken.EcKeyFormat;
import org.sufficientlysecure.keychain.securitytoken.KeyFormat;
import org.sufficientlysecure.keychain.securitytoken.KeyType;
import org.sufficientlysecure.keychain.securitytoken.OpenPgpCapabilities;
-import org.sufficientlysecure.keychain.securitytoken.RSAKeyFormat;
+import org.sufficientlysecure.keychain.securitytoken.RsaKeyFormat;
import org.sufficientlysecure.keychain.securitytoken.ResponseApdu;
import org.sufficientlysecure.keychain.securitytoken.SecurityTokenConnection;
import org.sufficientlysecure.keychain.securitytoken.SecurityTokenUtils;
@@ -107,37 +107,32 @@ public class SecurityTokenChangeKeyTokenOp {
OpenPgpCapabilities openPgpCapabilities = connection.getOpenPgpCapabilities();
KeyFormat formatForKeyType = openPgpCapabilities.getFormatForKeyType(slot);
- switch (formatForKeyType.keyFormatType()) {
- case RSAKeyFormatType:
- if (!secretKey.isRSA()) {
- throw new IOException("Security Token not configured for RSA key.");
- }
- crtSecretKey = secretKey.getSecurityTokenRSASecretKey();
+ if (formatForKeyType instanceof RsaKeyFormat) {
+ if (!secretKey.isRSA()) {
+ throw new IOException("Security Token not configured for RSA key.");
+ }
+ crtSecretKey = secretKey.getSecurityTokenRSASecretKey();
- // 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.");
- }
+ // 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) formatForKeyType);
- break;
+ keyBytes = SecurityTokenUtils.createRSAPrivKeyTemplate(crtSecretKey, slot,
+ (RsaKeyFormat) formatForKeyType);
+ } else if (formatForKeyType instanceof EcKeyFormat) {
+ if (!secretKey.isEC()) {
+ throw new IOException("Security Token not configured for EC key.");
+ }
- case ECKeyFormatType:
- if (!secretKey.isEC()) {
- throw new IOException("Security Token not configured for EC key.");
- }
+ secretKey.unlock(passphrase);
+ ecSecretKey = secretKey.getSecurityTokenECSecretKey();
+ ecPublicKey = secretKey.getSecurityTokenECPublicKey();
- secretKey.unlock(passphrase);
- ecSecretKey = secretKey.getSecurityTokenECSecretKey();
- ecPublicKey = secretKey.getSecurityTokenECPublicKey();
-
- keyBytes = SecurityTokenUtils.createECPrivKeyTemplate(ecSecretKey, ecPublicKey, slot,
- (ECKeyFormat) formatForKeyType);
- break;
-
- default:
- throw new IOException("Key type unsupported by security token.");
+ keyBytes = SecurityTokenUtils.createECPrivKeyTemplate(ecSecretKey, ecPublicKey, slot,
+ (EcKeyFormat) formatForKeyType);
+ } else {
+ throw new IOException("Key type unsupported by security token.");
}
} catch (PgpGeneralException e) {
throw new IOException(e.getMessage());
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/operations/SecurityTokenPsoSignTokenOp.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/operations/SecurityTokenPsoSignTokenOp.java
index 1f0907970..71bee4638 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/operations/SecurityTokenPsoSignTokenOp.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/securitytoken/operations/SecurityTokenPsoSignTokenOp.java
@@ -30,9 +30,10 @@ import org.bouncycastle.util.Arrays;
import org.bouncycastle.util.encoders.Hex;
import org.sufficientlysecure.keychain.securitytoken.CardException;
import org.sufficientlysecure.keychain.securitytoken.CommandApdu;
+import org.sufficientlysecure.keychain.securitytoken.EcKeyFormat;
import org.sufficientlysecure.keychain.securitytoken.KeyFormat;
import org.sufficientlysecure.keychain.securitytoken.OpenPgpCapabilities;
-import org.sufficientlysecure.keychain.securitytoken.RSAKeyFormat;
+import org.sufficientlysecure.keychain.securitytoken.RsaKeyFormat;
import org.sufficientlysecure.keychain.securitytoken.ResponseApdu;
import org.sufficientlysecure.keychain.securitytoken.SecurityTokenConnection;
import timber.log.Timber;
@@ -102,56 +103,56 @@ public class SecurityTokenPsoSignTokenOp {
}
private byte[] prepareData(byte[] hash, int hashAlgo, KeyFormat keyFormat) throws IOException {
- byte[] data;
- switch (keyFormat.keyFormatType()) {
- case RSAKeyFormatType:
- data = prepareDsi(hash, hashAlgo);
- break;
- case ECKeyFormatType:
- case EdDSAKeyFormatType:
- data = hash;
- break;
- default:
- throw new IOException("Not supported key type!");
+ if (keyFormat instanceof RsaKeyFormat) {
+ return prepareDsi(hash, hashAlgo);
+ } else if (keyFormat instanceof EcKeyFormat) {
+ return hash;
+ } else {
+ throw new IOException("Not supported key type!");
}
- return data;
}
private byte[] encodeSignature(byte[] signature, KeyFormat keyFormat) throws IOException {
// Make sure the signature we received is actually the expected number of bytes long!
- switch (keyFormat.keyFormatType()) {
- case RSAKeyFormatType:
- // no encoding necessary
- int modulusLength = ((RSAKeyFormat) keyFormat).getModulusLength();
- if (signature.length != (modulusLength / 8)) {
- throw new IOException("Bad signature length! Expected " + (modulusLength / 8) +
- " bytes, got " + signature.length);
- }
- break;
-
- case ECKeyFormatType: {
- // "plain" encoding, see https://github.com/open-keychain/open-keychain/issues/2108
- 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;
+ if (keyFormat instanceof RsaKeyFormat) {
+ // no encoding necessary
+ int modulusLength = ((RsaKeyFormat) keyFormat).modulusLength();
+ if (signature.length != (modulusLength / 8)) {
+ throw new IOException("Bad signature length! Expected " + (modulusLength / 8) +
+ " bytes, got " + signature.length);
}
- case EdDSAKeyFormatType:
- break;
+ return signature;
+ } else if (keyFormat instanceof EcKeyFormat) {
+ EcKeyFormat ecKeyFormat = (EcKeyFormat) keyFormat;
+ if (ecKeyFormat.isEdDsa()) {
+ return signature;
+ }
+
+ // "plain" encoding, see https://github.com/open-keychain/open-keychain/issues/2108
+ if (signature.length % 2 != 0) {
+ throw new IOException("Bad signature length!");
+ }
+ byte[] br = new byte[signature.length / 2];
+ 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];
+ }
+ if (br[0] == 0x00 && (br[1] & 0x80) == 0) {
+ br = Arrays.copyOfRange(br, 1, br.length);
+ }
+ if (bs[0] == 0x00 && (bs[1] & 0x80) == 0) {
+ bs = Arrays.copyOfRange(bs, 1, bs.length);
+ }
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ ASN1OutputStream out = ASN1OutputStream.create(baos);
+ out.writeObject(new DERSequence(new ASN1Encodable[]{new ASN1Integer(br), new ASN1Integer(bs)}));
+ out.flush();
+ return baos.toByteArray();
+ } else {
+ throw new IOException("Not supported key format!");
}
- return signature;
}
/**
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 f9ba6b136..f645e6dc1 100644
--- a/OpenKeychain/src/test/java/org/sufficientlysecure/keychain/securitytoken/SecurityTokenUtilsTest.java
+++ b/OpenKeychain/src/test/java/org/sufficientlysecure/keychain/securitytoken/SecurityTokenUtilsTest.java
@@ -157,7 +157,7 @@ public class SecurityTokenUtilsTest extends Mockito {
"1212121212121212121212121212121212121212121212121212121212121212" +
"1212121212121212121212121212121212121212121212121212121212121212"
),
- SecurityTokenUtils.createRSAPrivKeyTemplate(key2048, KeyType.AUTH, new RSAKeyFormat(2048, exp.bitLength(), RSAKeyFormat.RSAAlgorithmFormat.STANDARD)));
+ SecurityTokenUtils.createRSAPrivKeyTemplate(key2048, KeyType.AUTH, new RsaKeyFormat(2048, exp.bitLength(), RsaKeyFormat.RSAAlgorithmFormat.STANDARD)));
}
@Test