Consider I have two numbers a
and b
. Is that possible to calculate a^b
inside a javacard?
Few month ago I saw a master thesis (that I could not find it again!) who had used crypto resources like RSA engine in javacard to calculate BigNumber multiplication. Does anyone know a similar or other solution to handle this problem?
Thanks in Advance,
Thanks to @vlp answer, I could find an example applet inside JCMathLib github that I could use for big number calculations. This is the sample applet I used:
package JCMathLibTest ;
import javacard.framework.APDU;
import javacard.framework.Applet;
import javacard.framework.CardRuntimeException;
import javacard.framework.ISO7816;
import javacard.framework.ISOException;
import javacard.framework.JCSystem;
import javacard.framework.PINException;
import javacard.framework.SystemException;
import javacard.framework.TransactionException;
import javacard.framework.Util;
import javacard.security.CryptoException;
public class JCMathLibTest extends Applet {
// Main instruction CLAss
public final static byte CLA_OC_UT = (byte) 0xB0; // OpenCrypto Unit Tests
// INStructions
public final static byte INS_BN_ADD = (byte) 0x21;
public final static byte INS_BN_SUB = (byte) 0x22;
public final static byte INS_BN_MUL = (byte) 0x23;
public final static byte INS_BN_EXP = (byte) 0x24;
public final static byte INS_BN_MOD = (byte) 0x25;
public final static byte INS_BN_ADD_MOD = (byte) 0x30;
public final static byte INS_BN_SUB_MOD = (byte) 0x31;
public final static byte INS_BN_MUL_MOD = (byte) 0x32;
public final static byte INS_BN_EXP_MOD = (byte) 0x33;
public final static byte INS_BN_POW2_MOD = (byte) 0x35;
public final static byte INS_PERF_SETSTOP = (byte) 0xf5;
static boolean bIsSimulator = false;
static boolean bTEST_256b_CURVE = true;
static boolean bTEST_512b_CURVE = false;
short[] m_memoryInfo = null;
short m_memoryInfoOffset = 0;
ECConfig m_ecc = null;
ECCurve m_testCurve = null;
ECPoint m_testPoint1 = null;
ECPoint m_testPoint2 = null;
byte[] m_customG = null;
ECCurve m_testCurveCustom = null;
ECPoint m_testPointCustom = null;
Bignat m_testBN1;
Bignat m_testBN2;
Bignat m_testBN3;
public JCMathLibTest() {
OperationSupport.getInstance().setCard(OperationSupport.SIMULATOR); // TODO set your card
m_memoryInfo = new short[(short) (7 * 3)]; // Contains RAM and EEPROM memory required for basic library objects
if (bTEST_256b_CURVE) {
m_ecc = new ECConfig((short) 256);
}
if (bTEST_512b_CURVE) {
m_ecc = new ECConfig((short) 512);
}
// Pre-allocate test objects (no new allocation for every tested operation)
if (bTEST_256b_CURVE) {
m_testCurve = new ECCurve(false, SecP256r1.p, SecP256r1.a, SecP256r1.b, SecP256r1.G, SecP256r1.r);
//m_memoryInfoOffset = snapshotAvailableMemory((short) 3, m_memoryInfo, m_memoryInfoOffset);
// m_testCurveCustom and m_testPointCustom will have G occasionally changed so we need separate ECCurve
m_customG = new byte[(short) SecP256r1.G.length];
Util.arrayCopyNonAtomic(SecP256r1.G, (short) 0, m_customG, (short) 0, (short) SecP256r1.G.length);
m_testCurveCustom = new ECCurve(false, SecP256r1.p, SecP256r1.a, SecP256r1.b, m_customG, SecP256r1.r);
}
if (bTEST_512b_CURVE) {
m_testCurve = new ECCurve(false, P512r1.p, P512r1.a, P512r1.b, P512r1.G, P512r1.r);
// m_testCurveCustom and m_testPointCustom will have G occasionally changed so we need separate ECCurve
m_customG = new byte[(short) P512r1.G.length];
Util.arrayCopyNonAtomic(P512r1.G, (short) 0, m_customG, (short) 0, (short) P512r1.G.length);
m_testCurveCustom = new ECCurve(false, P512r1.p, P512r1.a, P512r1.b, m_customG, P512r1.r);
}
// Testing Bignat objects used in tests
byte memoryType = JCSystem.MEMORY_TYPE_TRANSIENT_RESET;
m_testBN1 = new Bignat(m_ecc.MAX_BIGNAT_SIZE, memoryType, m_ecc.bnh);
m_testBN2 = new Bignat(m_ecc.MAX_BIGNAT_SIZE, memoryType, m_ecc.bnh);
m_testBN3 = new Bignat(m_ecc.MAX_BIGNAT_SIZE, memoryType, m_ecc.bnh);
short intLen = 4;
m_testINT1 = new Integer(intLen, m_ecc.bnh);
m_testINT2 = new Integer(intLen, m_ecc.bnh);
}
public static void install(byte[] bArray, short bOffset, byte bLength) {
// GP-compliant JavaCard applet registration
if (bLength == 0) {
bIsSimulator = true;
}
new JCMathLibTest().register();
}
public boolean select() {
//updateAfterReset();
return true;
}
public void process(APDU apdu) {
byte[] apdubuf = apdu.getBuffer();
// Good practice: Return 9000 on SELECT
if (selectingApplet()) {
return;
}
// Check CLA byte
if (apdubuf[ISO7816.OFFSET_CLA] != CLA_OC_UT) {
ISOException.throwIt(ISO7816.SW_CLA_NOT_SUPPORTED);
}
// Process Input
short dataLen = apdu.setIncomingAndReceive(); // returns length of data field
try {
switch (apdubuf[ISO7816.OFFSET_INS]) {
case INS_BN_STR:
test_BN_STR(apdu, dataLen);
break;
case INS_BN_ADD:
test_BN_ADD(apdu, dataLen);
break;
case INS_BN_SUB:
test_BN_SUB(apdu, dataLen);
break;
case INS_BN_MUL:
test_BN_MUL(apdu, dataLen, true);
break;
case INS_BN_EXP:
test_BN_EXP(apdu, dataLen);
break;
case INS_BN_SQRT:
test_BN_SQRT(apdu, dataLen);
break;
case INS_BN_MOD:
test_BN_MOD(apdu, dataLen);
break;
case INS_BN_ADD_MOD:
test_BN_ADD_MOD(apdu, dataLen);
break;
case INS_BN_SUB_MOD:
test_BN_SUB_MOD(apdu, dataLen);
break;
case INS_BN_MUL_MOD:
test_BN_MUL_MOD(apdu, dataLen);
break;
case INS_BN_EXP_MOD:
test_BN_EXP_MOD(apdu, dataLen);
break;
case INS_BN_POW2_MOD:
test_BN_POW2_MOD(apdu, dataLen);
break;
default:
// good practice: If you don't know the INStruction, say so:
ISOException.throwIt(ISO7816.SW_INS_NOT_SUPPORTED);
}
// Capture all reasonable exceptions and change into readable ones (instead of 0x6f00)
} catch (ISOException e) {
throw e; // Our exception from code, just re-emit
} catch (ArrayIndexOutOfBoundsException e) {
ISOException.throwIt(ReturnCodes.SW_ArrayIndexOutOfBoundsException);
} catch (ArithmeticException e) {
ISOException.throwIt(ReturnCodes.SW_ArithmeticException);
} catch (ArrayStoreException e) {
ISOException.throwIt(ReturnCodes.SW_ArrayStoreException);
} catch (NullPointerException e) {
ISOException.throwIt(ReturnCodes.SW_NullPointerException);
} catch (NegativeArraySizeException e) {
ISOException.throwIt(ReturnCodes.SW_NegativeArraySizeException);
} catch (CryptoException e) {
ISOException.throwIt((short) (ReturnCodes.SW_CryptoException_prefix | e.getReason()));
} catch (SystemException e) {
ISOException.throwIt((short) (ReturnCodes.SW_SystemException_prefix | e.getReason()));
} catch (PINException e) {
ISOException.throwIt((short) (ReturnCodes.SW_PINException_prefix | e.getReason()));
} catch (TransactionException e) {
ISOException.throwIt((short) (ReturnCodes.SW_TransactionException_prefix | e.getReason()));
} catch (CardRuntimeException e) {
ISOException.throwIt((short) (ReturnCodes.SW_CardRuntimeException_prefix | e.getReason()));
} catch (Exception e) {
ISOException.throwIt(ReturnCodes.SW_Exception);
}
}
void test_BN_ADD(APDU apdu, short dataLen) {
byte[] apdubuf = apdu.getBuffer();
short p1 = (short) (apdubuf[ISO7816.OFFSET_P1] & 0x00FF);
PM.check(PM.TRAP_BN_ADD_1);
Bignat num1 = m_testBN1;
num1.set_size(p1);
PM.check(PM.TRAP_BN_ADD_2);
Bignat num2 = m_testBN2;
num2.set_size((short) (dataLen - p1));
PM.check(PM.TRAP_BN_ADD_3);
Bignat sum = m_testBN3;
sum.set_size((short) (p1 + 1));
PM.check(PM.TRAP_BN_ADD_4);
num1.from_byte_array(p1, (short)0, apdubuf, ISO7816.OFFSET_CDATA);
num2.from_byte_array((short) (dataLen - p1), (short)0, apdubuf, (short)(ISO7816.OFFSET_CDATA+p1));
PM.check(PM.TRAP_BN_ADD_5);
sum.copy(num1);
PM.check(PM.TRAP_BN_ADD_6);
sum.add(num2);
PM.check(PM.TRAP_BN_ADD_7);
short len = sum.copy_to_buffer(apdubuf, (short) 0);
apdu.setOutgoingAndSend((short) 0, len);
}
void test_BN_SUB(APDU apdu, short dataLen) {
byte[] apdubuf = apdu.getBuffer();
short p1 = (short) (apdubuf[ISO7816.OFFSET_P1] & 0x00FF);
PM.check(PM.TRAP_BN_SUB_1);
Bignat sub1 = m_testBN1;
sub1.set_size(p1);
PM.check(PM.TRAP_BN_SUB_2);
Bignat sub2 = m_testBN2;
sub2.set_size((short) (dataLen - p1));
PM.check(PM.TRAP_BN_SUB_3);
Bignat result = m_testBN3;
result.set_size((short) (p1 + 1));
PM.check(PM.TRAP_BN_SUB_4);
sub1.from_byte_array(dataLen, (short)0, apdubuf, ISO7816.OFFSET_CDATA);
sub2.from_byte_array(dataLen, (short)0, apdubuf, (short)(ISO7816.OFFSET_CDATA+p1));
PM.check(PM.TRAP_BN_SUB_5);
result.copy(sub1);
PM.check(PM.TRAP_BN_SUB_6);
result.subtract(sub2);
PM.check(PM.TRAP_BN_SUB_7);
short len = result.copy_to_buffer(apdubuf, (short) 0);
apdu.setOutgoingAndSend((short) 0, len);
}
void test_BN_MUL(APDU apdu, short dataLen, boolean bFastEngine) {
byte[] apdubuf = apdu.getBuffer();
short p1 = (short) (apdubuf[ISO7816.OFFSET_P1] & 0x00FF);
PM.check(PM.TRAP_BN_MUL_1);
Bignat mul1 = m_testBN1;
mul1.set_size(p1);
PM.check(PM.TRAP_BN_MUL_2);
Bignat mul2 = m_testBN2;
mul2.set_size((short) (dataLen - p1));
PM.check(PM.TRAP_BN_MUL_3);
Bignat product = m_testBN3;
product.set_size(dataLen);
PM.check(PM.TRAP_BN_MUL_4);
mul1.from_byte_array(p1, (short)0, apdubuf, ISO7816.OFFSET_CDATA);
mul2.from_byte_array((short)(dataLen-p1), (short)0, apdubuf, (short)(ISO7816.OFFSET_CDATA+p1));
PM.check(PM.TRAP_BN_MUL_5);
if (bFastEngine && !bIsSimulator) {
product.mult_rsa_trick(mul1, mul2, null, null);
}
else {
product.mult_schoolbook(mul1, mul2);
}
PM.check(PM.TRAP_BN_MUL_6);
short len = product.copy_to_buffer(apdubuf, (short) 0);
apdu.setOutgoingAndSend((short) 0, len);
}
void test_BN_EXP(APDU apdu, short dataLen) {
byte[] apdubuf = apdu.getBuffer();
short p1 = (short) (apdubuf[ISO7816.OFFSET_P1] & 0x00FF);
short p2 = (short) (apdubuf[ISO7816.OFFSET_P2] & 0x00FF);
PM.check(PM.TRAP_BN_EXP_1);
Bignat base = m_testBN1;
base.set_size(p1);
PM.check(PM.TRAP_BN_EXP_2);
Bignat exp = m_testBN2;
exp.set_size((short) (dataLen - p1));
PM.check(PM.TRAP_BN_EXP_3);
Bignat res = m_testBN3;
res.set_size((short) (m_ecc.MAX_BIGNAT_SIZE / 2));
PM.check(PM.TRAP_BN_EXP_4);
base.from_byte_array(p1, (short) 0, apdubuf, ISO7816.OFFSET_CDATA);
exp.from_byte_array((short) (dataLen - p1), (short) 0, apdubuf, (short)(ISO7816.OFFSET_CDATA+p1));
PM.check(PM.TRAP_BN_EXP_5);
res.exponentiation(base, exp);
PM.check(PM.TRAP_BN_EXP_6);
short len = res.copy_to_buffer(apdubuf, (short) 0);
apdu.setOutgoingAndSend((short) 0, len);
}
void test_BN_MOD(APDU apdu, short dataLen) {
byte[] apdubuf = apdu.getBuffer();
short p1 = (short) (apdubuf[ISO7816.OFFSET_P1] & 0x00FF);
PM.check(PM.TRAP_BN_MOD_1);
Bignat num = m_testBN1;
num.set_size(p1);
PM.check(PM.TRAP_BN_MOD_2);
Bignat mod = m_testBN2;
mod.set_size((short) (dataLen - p1));
PM.check(PM.TRAP_BN_MOD_3);
num.from_byte_array(p1, (short)0, apdubuf, ISO7816.OFFSET_CDATA);
mod.from_byte_array((short)(dataLen-p1), (short)0, apdubuf, (short)(ISO7816.OFFSET_CDATA+p1));
PM.check(PM.TRAP_BN_MOD_4);
num.mod(mod);
PM.check(PM.TRAP_BN_MOD_5);
short len = num.copy_to_buffer(apdubuf, (short) 0);
apdu.setOutgoingAndSend((short) 0, len);
}
void test_BN_ADD_MOD(APDU apdu, short dataLen) {
byte[] apdubuf = apdu.getBuffer();
short p1 = (short) (apdubuf[ISO7816.OFFSET_P1] & 0x00FF);
short p2 = (short) (apdubuf[ISO7816.OFFSET_P2] & 0x00FF);
PM.check(PM.TRAP_BN_ADD_MOD_1);
Bignat num1 = m_testBN1;
num1.set_size(p1);
PM.check(PM.TRAP_BN_ADD_MOD_2);
Bignat num2 = m_testBN2;
num2.set_size(p2);
PM.check(PM.TRAP_BN_ADD_MOD_3);
Bignat mod = m_testBN3;
mod.set_size((short) (dataLen - p1 - p2));
PM.check(PM.TRAP_BN_ADD_MOD_4);
num1.from_byte_array(p1, (short)0, apdubuf, ISO7816.OFFSET_CDATA);
num2.from_byte_array(p2, (short)0, apdubuf, (short)(ISO7816.OFFSET_CDATA+p1));
PM.check(PM.TRAP_BN_ADD_MOD_5);
mod.from_byte_array((short)(dataLen-p1-p2), (short)0, apdubuf, (short)(ISO7816.OFFSET_CDATA+p1+p2));
PM.check(PM.TRAP_BN_ADD_MOD_6);
num1.mod_add(num2, mod);
PM.check(PM.TRAP_BN_ADD_MOD_7);
short len = num1.copy_to_buffer(apdubuf, (short) 0);
apdu.setOutgoingAndSend((short) 0, len);
}
void test_BN_SUB_MOD(APDU apdu, short dataLen) {
byte[] apdubuf = apdu.getBuffer();
short p1 = (short) (apdubuf[ISO7816.OFFSET_P1] & 0x00FF);
short p2 = (short) (apdubuf[ISO7816.OFFSET_P2] & 0x00FF);
PM.check(PM.TRAP_BN_SUB_MOD_1);
Bignat num1 = m_testBN1;
num1.set_size(p1);
PM.check(PM.TRAP_BN_SUB_MOD_2);
Bignat num2 = m_testBN2;
num2.set_size(p2);
PM.check(PM.TRAP_BN_SUB_MOD_3);
Bignat mod = m_testBN3;
mod.set_size((short) (dataLen - p1 - p2));
PM.check(PM.TRAP_BN_SUB_MOD_4);
num1.from_byte_array(p1, (short)0, apdubuf, ISO7816.OFFSET_CDATA);
num2.from_byte_array(p2, (short)0, apdubuf, (short)(ISO7816.OFFSET_CDATA+p1));
mod.from_byte_array((short)(dataLen-p1-p2), (short)0, apdubuf, (short)(ISO7816.OFFSET_CDATA+p1+p2));
PM.check(PM.TRAP_BN_SUB_MOD_5);
num1.mod_sub(num2, mod);
PM.check(PM.TRAP_BN_SUB_MOD_6);
short len = num1.copy_to_buffer(apdubuf, (short) 0);
apdu.setOutgoingAndSend((short) 0, len);
}
void test_BN_MUL_MOD(APDU apdu, short dataLen) {
byte[] apdubuf = apdu.getBuffer();
short p1 = (short) (apdubuf[ISO7816.OFFSET_P1] & 0x00FF);
short p2 = (short) (apdubuf[ISO7816.OFFSET_P2] & 0x00FF);
PM.check(PM.TRAP_BN_MUL_MOD_1);
Bignat num1 = m_testBN1;
num1.set_size(p1);
PM.check(PM.TRAP_BN_MUL_MOD_2);
Bignat num2 = m_testBN2;
num2.set_size(p2);
PM.check(PM.TRAP_BN_MUL_MOD_3);
Bignat mod = m_testBN3;
mod.set_size((short) (dataLen - p1 - p2));
PM.check(PM.TRAP_BN_MUL_MOD_4);
num1.from_byte_array(p1, (short)0, apdubuf, ISO7816.OFFSET_CDATA);
num2.from_byte_array(p2, (short)0, apdubuf, (short)(ISO7816.OFFSET_CDATA+p1));
mod.from_byte_array((short)(dataLen-p1-p2), (short)0, apdubuf, (short)(ISO7816.OFFSET_CDATA+p1+p2));
PM.check(PM.TRAP_BN_MUL_MOD_5);
num1.mod_mult(num1, num2, mod);
PM.check(PM.TRAP_BN_MUL_MOD_6);
short len = num1.copy_to_buffer(apdubuf, (short) 0);
apdu.setOutgoingAndSend((short) 0, len);
}
void test_BN_EXP_MOD(APDU apdu, short dataLen) {
byte[] apdubuf = apdu.getBuffer();
short p1 = (short) (apdubuf[ISO7816.OFFSET_P1] & 0x00FF);
short p2 = (short) (apdubuf[ISO7816.OFFSET_P2] & 0x00FF);
PM.check(PM.TRAP_BN_EXP_MOD_1);
Bignat num1 = m_testBN1;
num1.set_size(p1);
PM.check(PM.TRAP_BN_EXP_MOD_2);
Bignat num2 = m_testBN2;
num2.set_size(p2);
PM.check(PM.TRAP_BN_EXP_MOD_3);
Bignat mod = m_testBN3;
mod.set_size((short) (dataLen - p1 - p2));
PM.check(PM.TRAP_BN_EXP_MOD_4);
num1.from_byte_array(p1, (short)0, apdubuf, ISO7816.OFFSET_CDATA);
num2.from_byte_array(p2, (short)0, apdubuf, (short)(ISO7816.OFFSET_CDATA+p1));
mod.from_byte_array((short)(dataLen-p1-p2), (short)0, apdubuf, (short)(ISO7816.OFFSET_CDATA+p1+p2));
PM.check(PM.TRAP_BN_EXP_MOD_5);
num1.mod_exp(num2, mod);
PM.check(PM.TRAP_BN_EXP_MOD_6);
short len = num1.copy_to_buffer(apdubuf, (short) 0);
apdu.setOutgoingAndSend((short) 0, len);
}
void test_BN_POW2_MOD(APDU apdu, short dataLen) {
byte[] apdubuf = apdu.getBuffer();
short p1 = (short) (apdubuf[ISO7816.OFFSET_P1] & 0x00FF);
short p2 = (short) (apdubuf[ISO7816.OFFSET_P2] & 0x00FF);
PM.check(PM.TRAP_BN_POW2_MOD_1);
Bignat num1 = m_testBN1;
num1.set_size(p1);
Bignat mod = m_testBN3;
mod.set_size((short) (dataLen - p1));
num1.from_byte_array(p1, (short) 0, apdubuf, ISO7816.OFFSET_CDATA);
mod.from_byte_array((short) (dataLen - p1), (short) 0, apdubuf, (short) (ISO7816.OFFSET_CDATA + p1));
PM.check(PM.TRAP_BN_POW2_MOD_2);
//num1.pow2Mod_RSATrick(mod);
num1.mod_exp2(mod);
PM.check(PM.TRAP_BN_POW2_MOD_3);
short len = num1.copy_to_buffer(apdubuf, (short) 0);
apdu.setOutgoingAndSend((short) 0, len);
}
}
and these are sample APDUs I used for my test:
INS_BN_ADD:
>> B0 21 02 00 04 01 11 02 11
>> 00 03 22 90 00
INS_BN_SUB:
<< B0 22 02 00 04 43 21 11 11
>> 00 32 10 90 00
INS_BN_MUL:
<< B0 23 02 00 04 00 20 00 02
>> 00 00 00 40 90 00
INS_BN_EXP:
<< B0 24 01 00 02 02 05
>> 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 20 90 00
INS_BN_MOD:
<< B0 25 01 00 02 05 03
>> 02 90 00
INS_BN_ADD_MOD:
<< B0 30 01 01 03 05 03 03
>> 02 90 00
<< B0 30 01 01 03 05 03 04
>> 90 00
INS_BN_SUB_MOD:
<< B0 31 01 01 03 08 03 04
>> 01 90 00
INS_BN_MUL_MOD:
<< B0 32 01 01 03 04 03 05
>> 02 90 00
<< B0 32 20 20 60 E3 9D E1 72 91 BC E3 C2 93 54 42 4B 95 9F 33 0C BC E9 CA B4 0C 3F 7C 76 FD 4F DD 8F C3 1F F5 53 9E C0 3B 35 BC 2F 44 9E CD 4E 16 72 43 20 D9 DF 17 F1 8C D5 F0 4D BE 44 1D 16 C2 34 3E F0 02 F3 16 69 C4 B9 E5 A0 3F 13 82 F6 61 BE AF 89 B1 FF 45 1D D6 5A 2A 0A AF 19 27 65 D7 80 11 D2 B3 45
>> 0B 00 0B 05 B7 B0 C6 91 86 6B 14 CD 7F C1 02 4C 39 BA 3E D2 79 99 9C F2 0C D3 B6 AE 1A 7C C6 1D 90 00
INS_BN_EXP_MOD:
<< B0 33 01 01 03 02 03 06
>> 02 90 00
<< B0 33 08 03 0E 11 22 33 44 55 66 77 88 00 11 22 04 23 00
>> 02 79 00 90 00
INS_BN_POW2_MOD
<< B0 35 01 00 02 03 04
>> 01 90 00
I did'nt add dependencies to my answer to be brief, they can be found in the github link above.
Thank you again vlp.