/* ******************************************************************************* * BTChip Bitcoin Hardware Wallet Java Card implementation * (c) 2013 BTChip - 1BTChip7VfTnrPra5jqci7ejnMguuHogTn * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero 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 Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. ******************************************************************************* */ package com.btchip.applet.poc; import javacard.framework.Util; import javacard.security.CryptoException; import javacard.security.ECKey; import javacard.security.ECPrivateKey; import javacard.security.KeyBuilder; import javacard.security.KeyPair; import javacard.security.MessageDigest; import javacard.security.RandomData; import javacard.security.Signature; import javacardx.crypto.Cipher; /** * Bitcoin signature implementation * @author BTChip * */ public class Crypto { public static void init() { random = RandomData.getInstance(RandomData.ALG_SECURE_RANDOM); // it's not even possible to request a transient pair, yay. generatingPair = new KeyPair(KeyPair.ALG_EC_FP, KeyBuilder.LENGTH_EC_FP_256); try { // ok, let's save RAM transientPrivate = (ECPrivateKey)KeyBuilder.buildKey(KeyBuilder.TYPE_EC_FP_PRIVATE_TRANSIENT_DESELECT, KeyBuilder.LENGTH_EC_FP_256, false); transientPrivateTransient = true; } catch(CryptoException e) { try { // ok, let's save a bit less RAM transientPrivate = (ECPrivateKey)KeyBuilder.buildKey(KeyBuilder.TYPE_EC_FP_PRIVATE_TRANSIENT_RESET, KeyBuilder.LENGTH_EC_FP_256, false); transientPrivateTransient = true; } catch(CryptoException e1) { // ok, let's test the flash wear leveling \o/ transientPrivate = (ECPrivateKey)KeyBuilder.buildKey(KeyBuilder.TYPE_EC_FP_PRIVATE, KeyBuilder.LENGTH_EC_FP_256, false); initKeyCurve(transientPrivate); } } initKeyCurve((ECKey)generatingPair.getPublic()); initKeyCurve((ECKey)generatingPair.getPrivate()); digestFull = MessageDigest.getInstance(MessageDigest.ALG_SHA_256, false); digestAuthorization = MessageDigest.getInstance(MessageDigest.ALG_SHA_256, false); digestScratch = MessageDigest.getInstance(MessageDigest.ALG_SHA_256, false); blobEncryptDecrypt = Cipher.getInstance(Cipher.ALG_DES_CBC_NOPAD, false); signature = Signature.getInstance(Signature.ALG_ECDSA_SHA_256, false); try { digestRipemd = MessageDigest.getInstance(MessageDigest.ALG_RIPEMD160, false); } catch(CryptoException e) { // A typical Java Card implementation will not support RIPEMD160 - we deal with it Ripemd160.init(); } } public static KeyPair generatePair() { generatingPair.genKeyPair(); return generatingPair; } private static void initKeyCurve(ECKey key) { key.setA(SECP256K1_A, (short)0, (short)SECP256K1_A.length); key.setB(SECP256K1_B, (short)0, (short)SECP256K1_B.length); key.setFieldFP(SECP256K1_FP, (short)0, (short)SECP256K1_FP.length); key.setG(SECP256K1_G, (short)0, (short)SECP256K1_G.length); key.setR(SECP256K1_R, (short)0, (short)SECP256K1_R.length); key.setK(SECP256K1_K); } public static void hashRipemd32(byte[] buffer, short offset, byte[] target, short targetOffset) { if (digestRipemd != null) { digestRipemd.doFinal(buffer, offset, (short)32, target, targetOffset); } else { Ripemd160.hash32(buffer, offset, target, targetOffset); } } public static void signTransientPrivate(byte[] keyBuffer, short keyOffset, byte[] dataBuffer, short dataOffset, byte[] targetBuffer, short targetOffset) { if (transientPrivateTransient) { initKeyCurve(transientPrivate); } transientPrivate.setS(keyBuffer, keyOffset, (short)32); Util.arrayFillNonAtomic(keyBuffer, keyOffset, (short)32, (byte)0x00); // recheck with the target platform, initializing once instead might be possible and save a few flash write // (this part is unspecified in the Java Card API) signature.init(transientPrivate, Signature.MODE_SIGN); signature.sign(dataBuffer, dataOffset, (short)32, targetBuffer, targetOffset); if (transientPrivateTransient) { transientPrivate.clearKey(); } } private static KeyPair generatingPair; private static ECPrivateKey transientPrivate; private static boolean transientPrivateTransient; private static Signature signature; public static MessageDigest digestFull; public static MessageDigest digestAuthorization; public static MessageDigest digestScratch; private static MessageDigest digestRipemd; public static RandomData random; public static Cipher blobEncryptDecrypt; // Nice SECp256k1 constants, only available during NIST opening hours private 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, (byte)0xFF,(byte)0xFF,(byte)0xFF,(byte)0xFF,(byte)0xFF,(byte)0xFF,(byte)0xFF,(byte)0xFF, (byte)0xFF,(byte)0xFF,(byte)0xFF,(byte)0xFE,(byte)0xFF,(byte)0xFF,(byte)0xFC,(byte)0x2F }; private static final byte SECP256K1_A[] = { (byte)0x00,(byte)0x00,(byte)0x00,(byte)0x00,(byte)0x00,(byte)0x00,(byte)0x00,(byte)0x00, (byte)0x00,(byte)0x00,(byte)0x00,(byte)0x00,(byte)0x00,(byte)0x00,(byte)0x00,(byte)0x00, (byte)0x00,(byte)0x00,(byte)0x00,(byte)0x00,(byte)0x00,(byte)0x00,(byte)0x00,(byte)0x00, (byte)0x00,(byte)0x00,(byte)0x00,(byte)0x00,(byte)0x00,(byte)0x00,(byte)0x00,(byte)0x00 }; private static final byte SECP256K1_B[] = { (byte)0x00,(byte)0x00,(byte)0x00,(byte)0x00,(byte)0x00,(byte)0x00,(byte)0x00,(byte)0x00, (byte)0x00,(byte)0x00,(byte)0x00,(byte)0x00,(byte)0x00,(byte)0x00,(byte)0x00,(byte)0x00, (byte)0x00,(byte)0x00,(byte)0x00,(byte)0x00,(byte)0x00,(byte)0x00,(byte)0x00,(byte)0x00, (byte)0x00,(byte)0x00,(byte)0x00,(byte)0x00,(byte)0x00,(byte)0x00,(byte)0x00,(byte)0x07 }; private static final byte SECP256K1_G[] = { (byte)0x04, (byte)0x79,(byte)0xBE,(byte)0x66,(byte)0x7E,(byte)0xF9,(byte)0xDC,(byte)0xBB,(byte)0xAC, (byte)0x55,(byte)0xA0,(byte)0x62,(byte)0x95,(byte)0xCE,(byte)0x87,(byte)0x0B,(byte)0x07, (byte)0x02,(byte)0x9B,(byte)0xFC,(byte)0xDB,(byte)0x2D,(byte)0xCE,(byte)0x28,(byte)0xD9, (byte)0x59,(byte)0xF2,(byte)0x81,(byte)0x5B,(byte)0x16,(byte)0xF8,(byte)0x17,(byte)0x98, (byte)0x48,(byte)0x3A,(byte)0xDA,(byte)0x77,(byte)0x26,(byte)0xA3,(byte)0xC4,(byte)0x65, (byte)0x5D,(byte)0xA4,(byte)0xFB,(byte)0xFC,(byte)0x0E,(byte)0x11,(byte)0x08,(byte)0xA8, (byte)0xFD,(byte)0x17,(byte)0xB4,(byte)0x48,(byte)0xA6,(byte)0x85,(byte)0x54,(byte)0x19, (byte)0x9C,(byte)0x47,(byte)0xD0,(byte)0x8F,(byte)0xFB,(byte)0x10,(byte)0xD4,(byte)0xB8 }; private static final byte SECP256K1_R[] = { (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)0xFE, (byte)0xBA,(byte)0xAE,(byte)0xDC,(byte)0xE6,(byte)0xAF,(byte)0x48,(byte)0xA0,(byte)0x3B, (byte)0xBF,(byte)0xD2,(byte)0x5E,(byte)0x8C,(byte)0xD0,(byte)0x36,(byte)0x41,(byte)0x41 }; private static final byte SECP256K1_K = (byte)0x01; }