diff --git a/README.adoc b/README.adoc index 0c19aef..7a22f2b 100644 --- a/README.adoc +++ b/README.adoc @@ -17,6 +17,8 @@ What works: * OpenSC, MacOS (`piv.tokend` for login), Windows PIV * RSA-1024 and -2048 key generation on card, signing + * EC P-256 key generation (but no ECDSA) + * EC Diffie-Hellman on P-256 (not working on all cards) * PINs and change of PIN, PUK reset * Some https://developers.yubico.com/PIV/Introduction/Yubico_extensions.html[ YubiKeyPIV-compatible extensions] are implemented and working: @@ -26,6 +28,8 @@ What works: What doesn't work: * ECDSA (probably not fixable without JavaCard 3.0.5 or proprietary APIs) + * There is partial support for it in the code, but it will double-hash + input data unless client software explicitly works around it. * Yubikey extensions (TODO): - Set management key - Import asymmetric key diff --git a/src/net/cooperi/pivapplet/ECParams.java b/src/net/cooperi/pivapplet/ECParams.java index 0757e32..e83d141 100644 --- a/src/net/cooperi/pivapplet/ECParams.java +++ b/src/net/cooperi/pivapplet/ECParams.java @@ -14,20 +14,20 @@ public class ECParams { public final static byte ALG_ECDSA_SHA_256 = (byte)33; public static final byte[] nistp256_p = { - (byte)0x0, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, - (byte)0x0, (byte)0x0, (byte)0x0, (byte)0x1, (byte)0x0, (byte)0x0, + (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0x0, + (byte)0x0, (byte)0x0, (byte)0x1, (byte)0x0, (byte)0x0, (byte)0x0, (byte)0x0, (byte)0x0, (byte)0x0, (byte)0x0, (byte)0x0, (byte)0x0, - (byte)0x0, (byte)0x0, (byte)0x0, (byte)0x0, (byte)0xff, (byte)0xff, + (byte)0x0, (byte)0x0, (byte)0x0, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, - (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff + (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff }; public static final byte[] nistp256_a = { - (byte)0x0, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, - (byte)0x0, (byte)0x0, (byte)0x0, (byte)0x1, (byte)0x0, (byte)0x0, + (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0x0, + (byte)0x0, (byte)0x0, (byte)0x1, (byte)0x0, (byte)0x0, (byte)0x0, (byte)0x0, (byte)0x0, (byte)0x0, (byte)0x0, (byte)0x0, (byte)0x0, - (byte)0x0, (byte)0x0, (byte)0x0, (byte)0x0, (byte)0xff, (byte)0xff, + (byte)0x0, (byte)0x0, (byte)0x0, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, - (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xfc + (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xfc }; public static final byte[] nistp256_b = { (byte)0x5a, (byte)0xc6, (byte)0x35, (byte)0xd8, (byte)0xaa, @@ -39,11 +39,13 @@ public class ECParams { (byte)0x60, (byte)0x4b }; public static final byte[] nistp256_R = { - (byte)0x0, (byte)0xc4, (byte)0x9d, (byte)0x36, (byte)0x8, - (byte)0x86, (byte)0xe7, (byte)0x4, (byte)0x93, (byte)0x6a, - (byte)0x66, (byte)0x78, (byte)0xe1, (byte)0x13, (byte)0x9d, - (byte)0x26, (byte)0xb7, (byte)0x81, (byte)0x9f, (byte)0x7e, - (byte)0x90 + (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0x00, + (byte)0x00, (byte)0x00, (byte)0x00, (byte)0xFF, (byte)0xFF, + (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, + (byte)0xFF, (byte)0xBC, (byte)0xE6, (byte)0xFA, (byte)0xAD, + (byte)0xA7, (byte)0x17, (byte)0x9E, (byte)0x84, (byte)0xF3, + (byte)0xB9, (byte)0xCA, (byte)0xC2, (byte)0xFC, (byte)0x63, + (byte)0x25, (byte)0x51 }; /*public static final byte[] nistp256_n = { (byte)0x0, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, @@ -74,8 +76,8 @@ public static void setCurveParameters(ECKey eckey) { nistp256_p, (short)0, (short)(nistp256_p.length)); eckey.setA(nistp256_a, (short)0, (short)(nistp256_a.length)); eckey.setB(nistp256_b, (short)0, (short)(nistp256_b.length)); - eckey.setR(nistp256_R, (short)0, (short)(nistp256_R.length)); eckey.setG(nistp256_G, (short)0, (short)(nistp256_G.length)); - eckey.setK((short)1); + eckey.setR(nistp256_R, (short)0, (short)(nistp256_R.length)); + //eckey.setK((short)1); } } diff --git a/src/net/cooperi/pivapplet/PivApplet.java b/src/net/cooperi/pivapplet/PivApplet.java index 70303cc..5277e79 100644 --- a/src/net/cooperi/pivapplet/PivApplet.java +++ b/src/net/cooperi/pivapplet/PivApplet.java @@ -23,6 +23,7 @@ import javacard.security.DESKey; import javacard.security.Key; import javacard.security.KeyBuilder; +import javacard.security.KeyAgreement; import javacard.security.KeyPair; import javacard.security.PrivateKey; import javacard.security.PublicKey; @@ -85,8 +86,8 @@ public class PivApplet extends Applet implements ExtendedLength private static final byte INS_IMPORT_ASYM = (byte)0xfe; private static final byte INS_GET_VER = (byte)0xfd; - private static final short RAM_BUF_SIZE = (short)1200; - private static final short MAX_CERT_SIZE = (short)1100; + private static final short RAM_BUF_SIZE = (short)1000; + private static final short MAX_CERT_SIZE = (short)900; private static final boolean USE_EXT_LEN = false; private static final byte RBSTAT_SEND_REM = (byte)0; @@ -112,6 +113,7 @@ public class PivApplet extends Applet implements ExtendedLength private Cipher rsaPkcs1 = null; private Signature ecdsaP256Sha = null; private Signature ecdsaP256Sha256 = null; + private KeyAgreement ecdh = null; private PivSlot slot9a = null, slot9b = null, slot9c = null, slot9d = null, slot9e = null; @@ -144,6 +146,8 @@ public class PivApplet extends Applet implements ExtendedLength private static final byte TAG_CERT_9D = (byte)0x0B; private static final byte TAG_CERT_9E = (byte)0x01; + private static final byte ALG_EC_SVDP_DH_PLAIN = (byte)3; + public static void install(byte[] info, short off, byte len) { @@ -173,6 +177,25 @@ public class PivApplet extends Applet implements ExtendedLength throw (ex); } + try { + ecdh = KeyAgreement.getInstance(ALG_EC_SVDP_DH_PLAIN, + false); + } catch (CryptoException ex) { + if (ex.getReason() != CryptoException.NO_SUCH_ALGORITHM) + throw (ex); + } + + if (ecdh == null) { + try { + ecdh = KeyAgreement.getInstance( + KeyAgreement.ALG_EC_SVDP_DH, false); + } catch (CryptoException ex) { + if (ex.getReason() != + CryptoException.NO_SUCH_ALGORITHM) + throw (ex); + } + } + ramBuf = JCSystem.makeTransientByteArray(RAM_BUF_SIZE, JCSystem.CLEAR_ON_RESET); rbStat = JCSystem.makeTransientShortArray((short)3, @@ -588,14 +611,14 @@ public class PivApplet extends Applet implements ExtendedLength tlv.setTarget(ramBuf); - tlv.push64k((short)0x7F49); - switch (alg) { case PIV_ALG_RSA1024: case PIV_ALG_RSA2048: RSAPublicKey rpubk = (RSAPublicKey)slot.asym.getPublic(); + tlv.push64k((short)0x7F49); + tlv.push64k((byte)0x81); cLen = rpubk.getModulus(ramBuf, tlv.offset()); tlv.write(cLen); @@ -610,7 +633,9 @@ public class PivApplet extends Applet implements ExtendedLength ECPublicKey epubk = (ECPublicKey)slot.asym.getPublic(); - tlv.push256((byte)0x86); + tlv.push((short)0x7F49); + + tlv.push((byte)0x86); cLen = epubk.getW(ramBuf, tlv.offset()); tlv.write(cLen); tlv.pop(); @@ -857,6 +882,42 @@ public class PivApplet extends Applet implements ExtendedLength break; case GA_TAG_RESPONSE: + if (tag == GA_TAG_EXP) { + if (alg != PIV_ALG_ECCP256) { + ISOException.throwIt( + ISO7816.SW_WRONG_DATA); + return; + } + if ((key == (byte)0x9b && + !slot9b.flags[PivSlot.F_UNLOCKED]) || + !slot.checkPin(pivPin)) { + ISOException.throwIt( + ISO7816. + SW_SECURITY_STATUS_NOT_SATISFIED); + return; + } + cLen = 256; + if ((short)(RAM_BUF_SIZE - cLen) < lc) { + ISOException.throwIt(ISO7816.SW_FILE_FULL); + return; + } + lc = (short)(RAM_BUF_SIZE - cLen); + + ecdh.init(slot.asym.getPrivate()); + cLen = ecdh.generateSecret(ramBuf, tlv.offset(), + tlv.tagLength(), ramBuf, lc); + + tlv.setTarget(ramBuf); + + tlv.push((byte)0x7C, (short)(cLen + 4)); + tlv.push(GA_TAG_RESPONSE, cLen); + tlv.write(ramBuf, lc, cLen); + tlv.pop(); + + len = tlv.pop(); + sendRamBuf(apdu, len); + break; + } if (tag != GA_TAG_CHALLENGE) { ISOException.throwIt(ISO7816.SW_WRONG_DATA); return;