diff --git a/src/main/java/im/status/wallet/Crypto.java b/src/main/java/im/status/wallet/Crypto.java index 4f01552..059648a 100644 --- a/src/main/java/im/status/wallet/Crypto.java +++ b/src/main/java/im/status/wallet/Crypto.java @@ -1,15 +1,159 @@ package im.status.wallet; -import javacard.security.MessageDigest; -import javacard.security.RandomData; +import javacard.framework.JCSystem; +import javacard.framework.Util; +import javacard.security.*; public class Crypto { + final static private short KEY_SECRET_SIZE = 32; + final static private short KEY_DERIVATION_INPUT_SIZE = 37; + final static private short HMAC_OUT_SIZE = 64; + + private static final byte HMAC_IPAD = (byte) 0x36; + private static final byte HMAC_OPAD = (byte) 0x5c; + private static final short HMAC_BLOCK_SIZE = (short) 128; + private static final short HMAC_BLOCK_OFFSET = (short) KEY_DERIVATION_INPUT_SIZE + HMAC_OUT_SIZE; + static RandomData random; static MessageDigest sha256; + + private static MessageDigest sha512; + private static Signature hmacSHA512; + private static HMACKey hmacKey; + private static byte[] tmp; static void init() { random = RandomData.getInstance(RandomData.ALG_SECURE_RANDOM); sha256 = MessageDigest.getInstance(MessageDigest.ALG_SHA_256, false); + + short blockSize; + + try { + blockSize = 0; + hmacSHA512 = Signature.getInstance(Signature.ALG_HMAC_SHA_512, false); + hmacKey = (HMACKey) KeyBuilder.buildKey(KeyBuilder.TYPE_HMAC_TRANSIENT_DESELECT, KEY_SECRET_SIZE, false); + } catch (CryptoException e) { + sha512 = MessageDigest.getInstance(MessageDigest.ALG_SHA_512, false); + blockSize = HMAC_BLOCK_SIZE; + } + + tmp = JCSystem.makeTransientByteArray((short) (HMAC_BLOCK_OFFSET + blockSize), JCSystem.CLEAR_ON_RESET); + } + + static boolean bip32CKDPriv(byte[] i, short iOff, ECPrivateKey privateKey, ECPublicKey publicKey, byte[] chain, short chainOff) { + short off = 0; + + if ((i[iOff] & (byte) 0x80) == (byte) 0x80) { + tmp[off++] = 0; + off += privateKey.getS(tmp, off); + } else { + off = (short) (publicKey.getW(tmp, (short) 0) - 1); + tmp[0] = ((tmp[(short) off] & 1) != 0 ? (byte) 0x03 : (byte) 0x02); + off = (short) ((short) (off / 2) + 1); + } + + off = Util.arrayCopyNonAtomic(i, iOff, tmp, off, (short) 4); + + hmacSHA512(chain, chainOff, KEY_SECRET_SIZE, tmp, (short) 0, off, tmp, off); + + if (ucmp256(tmp, off, SECP256k1.SECP256K1_R, (short) 0) >= 0) { + return false; + } + + privateKey.getS(tmp, (short) 0); + + addm256(tmp, off, tmp, (short) 0, SECP256k1.SECP256K1_R, (short)0, tmp, off); + + if (isZero256(tmp, off)) { + return false; + } + + JCSystem.beginTransaction(); + privateKey.setS(tmp, off, (short) KEY_SECRET_SIZE); + Util.arrayCopy(tmp, (short)(off + KEY_SECRET_SIZE), chain, chainOff, (short) KEY_SECRET_SIZE); + JCSystem.commitTransaction(); + + return true; + } + + private static void hmacSHA512(byte[] key, short keyOff, short keyLen, byte[] in, short inOff, short inLen, byte[] out, short outOff) { + if (hmacSHA512 != null) { + hmacKey.setKey(key, keyOff, keyLen); + hmacSHA512.init(hmacKey, Signature.MODE_SIGN); + hmacSHA512.sign(in, inOff, inLen, out, outOff); + } else { + for (byte i = 0; i < 2; i++) { + Util.arrayFillNonAtomic(tmp, HMAC_BLOCK_OFFSET, HMAC_BLOCK_SIZE, (i == 0 ? HMAC_IPAD : HMAC_OPAD)); + + for (short j = 0; j < keyLen; j++) { + tmp[(short)(HMAC_BLOCK_OFFSET + j)] ^= key[(short)(keyOff + j)]; + } + + sha512.update(tmp, HMAC_BLOCK_OFFSET, HMAC_BLOCK_SIZE); + + if (i == 0) { + Crypto.sha512.doFinal(in, inOff, inLen, out, outOff); + } else { + Crypto.sha512.doFinal(out, outOff, HMAC_OUT_SIZE, out, outOff); + } + } + } + } + + private static void addm256(byte[] a, short aOff, byte[] b, short bOff, byte[] n, short nOff, byte[] out, short outOff) { + if ((add256(a, aOff, b, bOff, out, outOff) != 0) || (ucmp256(out, outOff, n, nOff) > 0)) { + sub256(out, outOff, n, nOff, out, outOff); + } + } + + private static short ucmp256(byte[] a, short aOff, byte[] b, short bOff) { + short ai, bi; + for (short i = 0 ; i < 32; i++) { + ai = (short)(a[(short)(aOff + i)] & 0x00ff); + bi = (short)(b[(short)(bOff + i)] & 0x00ff); + + if (ai != bi) { + return (short)(ai - bi); + } + } + + return 0; + } + + private static boolean isZero256(byte[] a, short aOff) { + boolean isZero = true; + + for (short i = 0; i < (byte) 32; i++) { + if (a[(short)(aOff + i)] != 0) { + isZero = false; + break; + } + } + + return isZero; + } + + private static short add256(byte[] a, short aOff, byte[] b, short bOff, byte[] out, short outOff) { + + short outI = 0; + for (short i = 31 ; i >= 0 ; i--) { + outI = (short) ((short)(a[(short)(aOff + i)] & 0x00FF) + (short)(b[(short)(bOff + i)] & 0xFF) + outI); + out[(short)(outOff + i)] = (byte)outI ; + outI = (short)(outI >> 8); + } + return outI; + } + + private static short sub256(byte[] a, short aOff, byte[] b, short bOff, byte[] out, short outOff) { + short outI = 0; + + for (short i = 31 ; i >= 0 ; i--) { + outI = (short) ((short)(a[(short)(aOff + i)] & 0xFF) - (short)(b[(short)(bOff + i)] & 0xFF) - outI); + out[(short)(outOff + i)] = (byte)outI ; + outI = (short)(((outI >> 8) != 0) ? 1 : 0); + } + + return outI; } } diff --git a/src/main/java/im/status/wallet/SEC256k1.java b/src/main/java/im/status/wallet/SECP256k1.java similarity index 99% rename from src/main/java/im/status/wallet/SEC256k1.java rename to src/main/java/im/status/wallet/SECP256k1.java index 89a1196..b279276 100644 --- a/src/main/java/im/status/wallet/SEC256k1.java +++ b/src/main/java/im/status/wallet/SECP256k1.java @@ -5,7 +5,7 @@ import javacard.security.ECKey; import javacard.security.ECPrivateKey; import javacard.security.KeyAgreement; -public class SEC256k1 { +public class SECP256k1 { static final byte SECP256K1_FP[] = { (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, diff --git a/src/main/java/im/status/wallet/SecureChannel.java b/src/main/java/im/status/wallet/SecureChannel.java index 8e43547..589cc51 100644 --- a/src/main/java/im/status/wallet/SecureChannel.java +++ b/src/main/java/im/status/wallet/SecureChannel.java @@ -26,8 +26,8 @@ public class SecureChannel { scKey = (AESKey) KeyBuilder.buildKey(KeyBuilder.TYPE_AES_TRANSIENT_DESELECT, KeyBuilder.LENGTH_AES_256, false); scKeypair = new KeyPair(KeyPair.ALG_EC_FP, SC_KEY_LENGTH); - SEC256k1.setCurveParameters((ECKey) scKeypair.getPrivate()); - SEC256k1.setCurveParameters((ECKey) scKeypair.getPublic()); + SECP256k1.setCurveParameters((ECKey) scKeypair.getPrivate()); + SECP256k1.setCurveParameters((ECKey) scKeypair.getPublic()); scKeypair.genKeyPair(); scAgreement = KeyAgreement.getInstance(KeyAgreement.ALG_EC_SVDP_DH, false); diff --git a/src/main/java/im/status/wallet/WalletApplet.java b/src/main/java/im/status/wallet/WalletApplet.java index 3f45d4d..d3f2a5f 100644 --- a/src/main/java/im/status/wallet/WalletApplet.java +++ b/src/main/java/im/status/wallet/WalletApplet.java @@ -70,7 +70,7 @@ public class WalletApplet extends Applet { } public WalletApplet(byte[] bArray, short bOffset, byte bLength) { - SEC256k1.init(); + SECP256k1.init(); Crypto.init(); short c9Off = (short)(bOffset + bArray[bOffset] + 1); // Skip AID @@ -94,11 +94,11 @@ public class WalletApplet extends Applet { publicKey = (ECPublicKey) KeyBuilder.buildKey(KeyBuilder.TYPE_EC_FP_PUBLIC, EC_KEY_SIZE, false); privateKey = (ECPrivateKey) KeyBuilder.buildKey(KeyBuilder.TYPE_EC_FP_PRIVATE, EC_KEY_SIZE, false); - SEC256k1.setCurveParameters(masterPublic); - SEC256k1.setCurveParameters(masterPrivate); + SECP256k1.setCurveParameters(masterPublic); + SECP256k1.setCurveParameters(masterPrivate); - SEC256k1.setCurveParameters(publicKey); - SEC256k1.setCurveParameters(privateKey); + SECP256k1.setCurveParameters(publicKey); + SECP256k1.setCurveParameters(privateKey); signature = Signature.getInstance(Signature.ALG_ECDSA_SHA_256, false); @@ -178,7 +178,7 @@ public class WalletApplet extends Applet { apduBuffer[off++] = privateKey.isInitialized() ? (byte) 0x01 : (byte) 0x00; apduBuffer[off++] = TLV_FAST_PUBLIC_KEY_DERIVATION; apduBuffer[off++] = 1; - apduBuffer[off++] = SEC256k1.hasFastECPointMultiplication() ? (byte) 0x01 : (byte) 0x00; + apduBuffer[off++] = SECP256k1.hasFastECPointMultiplication() ? (byte) 0x01 : (byte) 0x00; short len = secureChannel.encryptAPDU(apduBuffer, (short) (off - SecureChannel.SC_OUT_OFFSET)); apdu.setOutgoingAndSend(ISO7816.OFFSET_CDATA, len); @@ -309,7 +309,7 @@ public class WalletApplet extends Applet { pubOffset = (short) (pubOffset + 2); } else { pubOffset = 0; - pubLen = SEC256k1.derivePublicKey(masterPrivate, apduBuffer, pubOffset); + pubLen = SECP256k1.derivePublicKey(masterPrivate, apduBuffer, pubOffset); } masterPublic.setW(apduBuffer, pubOffset, pubLen); @@ -334,7 +334,7 @@ public class WalletApplet extends Applet { Util.arrayCopy(apduBuffer, (short) (ISO7816.OFFSET_CDATA + CHAIN_CODE_SIZE), masterChainCode, (short) 0, CHAIN_CODE_SIZE); Util.arrayCopy(apduBuffer, (short) (ISO7816.OFFSET_CDATA + CHAIN_CODE_SIZE), chainCode, (short) 0, CHAIN_CODE_SIZE); - short pubLen = SEC256k1.derivePublicKey(masterPrivate, apduBuffer, (short) 0); + short pubLen = SECP256k1.derivePublicKey(masterPrivate, apduBuffer, (short) 0); masterPublic.setW(apduBuffer, (short) 0, pubLen); publicKey.setW(apduBuffer, (short) 0, pubLen);