/* * SatoChip Bitcoin Hardware Wallet based on javacard * (c) 2015 by Toporin - 16DMCk4WUaHofchAhpMaQS4UPm4urcy2dN * Sources available on https://github.com/Toporin * * 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 org.satochip.applet; import javacard.framework.Util; import javacard.security.KeyBuilder; import javacard.security.RSAPublicKey; import javacard.security.RandomData; import javacardx.crypto.Cipher; public class EccComputation { private static final short EXP2=0; private static final short EXP3=1; private static final short EXP4=2; private static final short EXP6=3; private static final short EXP8=4; private static final short EXP12=5; private static final short EXP16=6; private static final short EXP24=7; private static final short EXP32=8; private static final short EXP48=9; private static final short EXP96=10; private static final short DECR=11; // Q decomposition: divide by a factor of 96 (2^5*3) or decrement then repeat private final static byte[] QDECOMP={ EXP4, DECR, EXP2, DECR, EXP32, DECR, EXP2, DECR, EXP2, DECR, EXP2, DECR, EXP2, DECR, EXP2, DECR, EXP2, DECR, EXP2, DECR, EXP2, DECR, EXP2, DECR, EXP2, DECR, EXP2, DECR, EXP2, DECR, EXP2, DECR, EXP2, DECR, EXP2, DECR, EXP2, DECR, EXP2, DECR, EXP2, DECR, EXP2, DECR, EXP2, DECR, EXP2, DECR, EXP4, DECR, EXP6, EXP3, DECR, EXP6, DECR, EXP96, DECR, EXP6, EXP3, DECR, EXP2, DECR, EXP2, DECR, EXP2, DECR, EXP4, DECR, EXP48, DECR, EXP6, DECR, EXP8, DECR, EXP2, DECR, EXP2, DECR, EXP2, DECR, EXP2, DECR, EXP4, DECR, EXP6, DECR, EXP16, DECR, EXP6, DECR, EXP2, DECR, EXP2, DECR, EXP4, DECR, EXP6, EXP3, EXP3, DECR, EXP24, DECR, EXP2, DECR, EXP4, DECR, EXP6, EXP3, DECR, EXP6, DECR, EXP16, DECR, EXP48, DECR, EXP6, EXP3, EXP3, DECR, EXP2, DECR, EXP2, DECR, EXP2, DECR, EXP4, DECR, EXP6, EXP3, DECR, EXP6, EXP3, DECR, EXP12, DECR, EXP2, DECR, EXP4, DECR, EXP24, EXP3, DECR, EXP2, DECR, EXP8, DECR, EXP8, DECR, EXP8, DECR, EXP2, DECR, EXP2, DECR, EXP2, DECR, EXP32, EXP8, DECR, EXP6, DECR, EXP96, DECR, EXP12, EXP3, DECR, EXP12, DECR, EXP4, DECR, EXP6, DECR, EXP6, DECR, EXP6, EXP3, EXP3, DECR, EXP6, DECR, EXP48, DECR, EXP12, EXP3, EXP3, EXP3, DECR, EXP4, DECR, EXP6, EXP3, EXP3, DECR, EXP32, EXP8, DECR, EXP6, DECR, EXP2, DECR, EXP2, DECR, EXP2, DECR, EXP16, DECR, EXP6, DECR, EXP12, DECR, EXP2, DECR, EXP2, DECR, EXP2, DECR, EXP4, DECR, EXP12, DECR }; private final static byte[] EXP={ (byte)2, (byte)3, (byte)4, (byte)6, (byte)8, (byte)12, (byte)16, (byte)24, (byte)32, (byte)48, (byte)96}; private final static short[] EXPOFF={ (short)48, (short)64, (short)72, (short)80, (short)84, (short)88, (short)90, (short)92, (short)93, (short)94, (short)95}; private final static byte[] SECP256K1_P = {(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}; // q= (p+1)/4 // private final static byte[] Q= // {(byte)0x3f,(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)0xbf,(byte)0xff,(byte)0xff,(byte)0x0c}; private static byte[] tmpBuffer; private static final short REG0=(short)0; private static final short REG1=(short)128; private static final short REG2=(short)160; private static final short REG3=(short)192; private static final short REG4=(short)224; //private static final short REG5=(short)256; private static final short OPLENGTH=(short)32; private static RSAPublicKey rsa_PublicKey = null; private static Cipher m_encryptCipherRSA = null; public static final byte ALG_RSA_NOPAD = 12; public static void init(byte[] tmp){ tmpBuffer=tmp; // Allocate objects if not allocated yet if (rsa_PublicKey == null) { rsa_PublicKey = (RSAPublicKey) KeyBuilder.buildKey(KeyBuilder.TYPE_RSA_PUBLIC,KeyBuilder.LENGTH_RSA_1024,false); } if (m_encryptCipherRSA == null) { m_encryptCipherRSA = Cipher.getInstance(ALG_RSA_NOPAD, false); } // set SECP256K1_P*2^96 Util.arrayFillNonAtomic(tmpBuffer, REG0, (short)128, (byte)0); Util.arrayCopyNonAtomic(SECP256K1_P, (short)0, tmpBuffer, REG0, (short)SECP256K1_P.length); rsa_PublicKey.setModulus(tmpBuffer, REG0, (short)128); } // uses REG3 & REG4 public static void SqrtRootOpt(byte[] src, short srcOff, byte[] dst, short dstOff){ //, short ctrstop){ // y^2 = z = x^3+7 (mod p) // y= sqrt(z)= z^((p+1)/4) (mod p) // z= x^3+7 ModularExp32b(src, srcOff, tmpBuffer, REG4, EXP3); Util.arrayFillNonAtomic(tmpBuffer, REG2, OPLENGTH, (byte)0); tmpBuffer[(short)(REG2+31)]=(byte)7; //LSB if(Biginteger.add_carry(tmpBuffer, REG4, tmpBuffer, REG2, OPLENGTH)){ Biginteger.subtract(tmpBuffer, REG4, SECP256K1_P, (short)0, OPLENGTH); } Util.arrayFillNonAtomic(tmpBuffer, REG3, OPLENGTH, (byte)0); tmpBuffer[(short)(REG3+31)]=(byte)1; //LSB for(short i=0; i<QDECOMP.length; i++){ byte op= QDECOMP[i]; if (op==DECR){ ModularMult32bOpt(tmpBuffer, REG3, tmpBuffer, REG4, tmpBuffer, REG3); } else{ ModularExp32b(tmpBuffer, REG4, tmpBuffer, REG4, op); } //if (counter==ctrstop) //debug //break; } Util.arrayCopyNonAtomic(tmpBuffer, REG3, dst, dstOff, OPLENGTH); //Util.arrayCopyNonAtomic(tmpBuffer, REG3, dst, dstOff, (short)96);//debug } // uses REG0 public static void ModularExp32b(byte[] src, short srcOff, byte[] dst, short dstOff, short exp){ //https://proofwiki.org/wiki/Congruence_by_Product_of_Modulo Util.arrayFillNonAtomic(tmpBuffer, REG0, (short)128, (byte)0); Util.arrayCopyNonAtomic(src, srcOff, tmpBuffer, EXPOFF[exp], OPLENGTH); rsa_PublicKey.setExponent(EXP, (short)exp, (short) 1); m_encryptCipherRSA.init(rsa_PublicKey, Cipher.MODE_ENCRYPT); short offset = m_encryptCipherRSA.doFinal(tmpBuffer, (short)0, (short)128, tmpBuffer, (byte) 0); Util.arrayCopyNonAtomic(tmpBuffer, (short)0, dst, dstOff, OPLENGTH); } // uses REG1 & REG2 public static void ModularMult32bOpt(byte[]x, short xOff, byte[] y, short yOff, byte[] dst, short dstOff){ // https://www.cosic.esat.kuleuven.be/publications/article-1296.pdf // 4xy = (x+y)^2 - (x-y)^2 (mod n) // (x+y) Util.arrayCopyNonAtomic(x, xOff, tmpBuffer, REG1, OPLENGTH); if(Biginteger.add_carry(tmpBuffer, REG1, y, yOff, OPLENGTH)){ Biginteger.subtract(tmpBuffer, REG1, SECP256K1_P, (short)0, OPLENGTH); }else{ // in the unlikely case where SECP256K1_P<=x+y<2^256 if(!Biginteger.lessThan(tmpBuffer, REG1, SECP256K1_P, (short)0, OPLENGTH)){ Biginteger.subtract(tmpBuffer, REG1, SECP256K1_P, (short)0, OPLENGTH); } } // (x+y)^2 ModularExp32b(tmpBuffer, REG1, tmpBuffer, REG1, EXP2); // (x-y) Util.arrayCopyNonAtomic(x, xOff, tmpBuffer, REG2, OPLENGTH); if(Biginteger.lessThan(tmpBuffer, REG2, y, yOff, OPLENGTH)){ Biginteger.add_carry(tmpBuffer, REG2, SECP256K1_P, (short)0, OPLENGTH); } Biginteger.subtract(tmpBuffer, REG2, y, yOff, OPLENGTH); // (x-y)^2 ModularExp32b(tmpBuffer, REG2, tmpBuffer, REG2, EXP2); // (x+y)^2-(x-y)^2 if(Biginteger.lessThan(tmpBuffer, REG1, tmpBuffer, REG2, OPLENGTH)){ Biginteger.add_carry(tmpBuffer, REG1, SECP256K1_P, (short)0, OPLENGTH); } Biginteger.subtract(tmpBuffer, REG1, tmpBuffer, REG2, OPLENGTH); // divide by 2 if ((tmpBuffer[(short)(REG1+31)]&1)==0){ Biginteger.Shift1bit(tmpBuffer, REG1, OPLENGTH); } else{ // LSB is 1 boolean carry= Biginteger.add_carry(tmpBuffer, REG1, SECP256K1_P, (short)0, OPLENGTH); Biginteger.Shift1bit(tmpBuffer, REG1, OPLENGTH); if (carry){// set MSB tmpBuffer[REG1]|=(byte)0x80; } } // divide by 2 again if ((tmpBuffer[(short)(REG1+31)]&1)==0){ Biginteger.Shift1bit(tmpBuffer, REG1, OPLENGTH); } else{ // LSB is 1 boolean carry= Biginteger.add_carry(tmpBuffer, REG1, SECP256K1_P, (short)0, OPLENGTH); Biginteger.Shift1bit(tmpBuffer, REG1, OPLENGTH); if (carry){// set MSB tmpBuffer[REG1]|=(byte)0x80; } } Util.arrayCopyNonAtomic(tmpBuffer, REG1, dst, dstOff, OPLENGTH); } public static void getSecondPoint(byte[]src, short srcOff, byte[] dst, short dstOff){ Util.arrayCopyNonAtomic(SECP256K1_P, (short)0, dst, dstOff, OPLENGTH); Biginteger.subtract(dst, dstOff, src, srcOff, OPLENGTH); } }