add point multiplication through JCMathLib

This commit is contained in:
Michele Balistreri 2017-10-12 13:21:19 +03:00
parent b2543239aa
commit 48f1905cee
11 changed files with 3035 additions and 18 deletions

View File

@ -1,24 +1,14 @@
package im.status.wallet;
import javacard.security.CryptoException;
import javacard.security.KeyAgreement;
import javacard.security.MessageDigest;
import javacard.security.RandomData;
public class Crypto {
private static final byte ALG_EC_SVDP_DH_PLAIN_XY = 6; // constant from JavaCard 3.0.5
static RandomData random;
static MessageDigest sha256;
static KeyAgreement ecPointMultiplier;
static MessageDigest sha256;
static void init() {
try {
ecPointMultiplier = KeyAgreement.getInstance(ALG_EC_SVDP_DH_PLAIN_XY, false);
} catch(CryptoException e) {
ecPointMultiplier = KeyAgreement.getInstance(KeyAgreement.ALG_EC_SVDP_DH_PLAIN, false);
}
random = RandomData.getInstance(RandomData.ALG_SECURE_RANDOM);
sha256 = MessageDigest.getInstance(MessageDigest.ALG_SHA_256, false);
}

View File

@ -1,7 +1,12 @@
package im.status.wallet;
import javacard.security.CryptoException;
import javacard.security.ECKey;
import javacard.security.ECPrivateKey;
import javacard.security.KeyAgreement;
import opencrypto.jcmathlib.ECConfig;
import opencrypto.jcmathlib.ECCurve;
import opencrypto.jcmathlib.ECPoint;
public class ECCurves {
static final byte SECP256K1_FP[] = {
@ -42,6 +47,24 @@ public class ECCurves {
static final byte SECP256K1_K = (byte)0x01;
private static final byte ALG_EC_SVDP_DH_PLAIN_XY = 6; // constant from JavaCard 3.0.5
private static KeyAgreement ecPointMultiplier;
private static ECConfig ecConfig;
private static ECCurve ecCurve;
private static ECPoint ecPoint;
static void init() {
try {
ecPointMultiplier = KeyAgreement.getInstance(ALG_EC_SVDP_DH_PLAIN_XY, false);
} catch(CryptoException e) {
ecConfig = new ECConfig((short) 256);
ecCurve = new ECCurve(false, SECP256K1_FP, SECP256K1_A, SECP256K1_B, SECP256K1_G, SECP256K1_R, ecConfig);
ecPoint = new ECPoint(ecCurve, ecConfig);
}
}
static void setSECP256K1CurveParameters(ECKey key) {
key.setA(SECP256K1_A, (short) 0x00, (short) SECP256K1_A.length);
key.setB(SECP256K1_B, (short) 0x00, (short) SECP256K1_B.length);
@ -56,13 +79,18 @@ public class ECCurves {
}
/*
* This works only if KeyAgreement.ALG_EC_SVDP_DH_PLAIN_XY is implemented. Otherwise we get only X. This method must be
* extended to calculate Y from X (Y^2 = X(X^2+A)+B). Since we get two possible results we will need to use signature
* verification to check which one is the actual public key. In this case performance will be very slow, around 5s
* for a single multiplication.
* This works efficiently only if KeyAgreement.ALG_EC_SVDP_DH_PLAIN_XY is implemented. Otherwise performance will
* be very slow, around 5s for a single multiplication.
*/
static short multiplyPoint(ECPrivateKey privateKey, byte[] point, short pointOff, short pointLen, byte[] out, short outOff) {
Crypto.ecPointMultiplier.init(privateKey);
return Crypto.ecPointMultiplier.generateSecret(point, pointOff, pointLen, out, outOff);
if(ecPointMultiplier != null) {
ecPointMultiplier.init(privateKey);
return ecPointMultiplier.generateSecret(point, pointOff, pointLen, out, outOff);
} else {
ecPoint.setW(point, pointOff, pointLen);
short len = privateKey.getS(out, outOff);
ecPoint.multiplication(out, outOff, len);
return ecPoint.getW(out, outOff);
}
}
}

View File

@ -68,6 +68,7 @@ public class WalletApplet extends Applet {
}
public WalletApplet(byte[] bArray, short bOffset, byte bLength) {
ECCurves.init();
Crypto.init();
short c9Off = (short)(bOffset + bArray[bOffset] + 1); // Skip AID

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,229 @@
package opencrypto.jcmathlib;
import javacard.framework.JCSystem;
import javacard.framework.Util;
import javacard.security.KeyBuilder;
import javacard.security.KeyPair;
import javacard.security.MessageDigest;
import javacard.security.RSAPublicKey;
import javacardx.crypto.Cipher;
/**
*
* @author Petr Svenda
*/
public class Bignat_Helper {
private final ECConfig ecc;
/**
* Helper flag which signalizes that code is executed inside simulator
* (during tests). Is used to address simulator specific behaviour workaround
* if required.
*/
byte[] helper_BN_array1 = null;
byte[] helper_BN_array2 = null;
/**
* Number of pre-allocated helper arrays
*/
public static final byte NUM_HELPER_ARRAYS = 2;
byte[] fnc_deep_resize_tmp = null;
public byte[] fnc_mult_resultArray1 = null;
byte[] fnc_mult_resultArray2 = null;
byte[] tmp_array_short = null;
byte[] fnc_same_value_array1 = null;
byte[] fnc_same_value_hash = null;
// These Bignats helper_BN_? are allocated
Bignat helper_BN_A;
Bignat helper_BN_B;
Bignat helper_BN_C;
Bignat helper_BN_D;
Bignat helper_BN_E;
Bignat helper_BN_F;
// These Bignats are just pointing to some helper_BN_? so reasonable naming is preserved yet no need to actually allocated whole Bignat object
Bignat fnc_mod_exp_modBN;
Bignat fnc_mod_add_tmp;
Bignat fnc_mod_sub_tmp;
Bignat fnc_mod_sub_tmpOther;
Bignat fnc_mod_sub_tmpThis;
Bignat fnc_mod_mult_tmpThis;
Bignat fnc_mult_mod_tmpThis;
Bignat fnc_mult_mod_tmp_x;
Bignat fnc_mult_mod_tmp_mod;
Bignat fnc_divide_tmpThis;
Bignat fnc_exponentiation_i;
Bignat fnc_exponentiation_tmp;
Bignat fnc_sqrt_p_1;
Bignat fnc_sqrt_Q;
Bignat fnc_sqrt_S;
Bignat fnc_sqrt_tmp;
Bignat fnc_sqrt_exp;
Bignat fnc_sqrt_z;
Bignat fnc_mod_minus_2;
Bignat fnc_negate_tmp;
Bignat fnc_int_add_tmpMag;
Bignat fnc_int_multiply_mod;
Bignat fnc_int_multiply_tmpThis;
Bignat fnc_int_divide_tmpThis;
RSAPublicKey fnc_NmodE_pubKey;
Cipher fnc_NmodE_cipher;
public Bignat ONE;
public Bignat TWO;
public Bignat THREE;
// Helper objects for fast multiplication of two large numbers (without modulo)
KeyPair fnc_mult_keypair = null;
RSAPublicKey fnc_mult_pubkey_pow2 = null;
Cipher fnc_mult_cipher = null;
MessageDigest hashEngine;
static byte[] CONST_ONE = {0x01};
static byte[] CONST_TWO = {0x02};
public Bignat_Helper(ECConfig ecCfg) {
ecc = ecCfg;
tmp_array_short = ecc.memAlloc.allocateByteArray((short) 2, JCSystem.MEMORY_TYPE_TRANSIENT_RESET); // only 2b RAM for faster add(short)
fnc_NmodE_cipher = Cipher.getInstance(Cipher.ALG_RSA_NOPAD, false);
fnc_NmodE_pubKey = (RSAPublicKey) KeyBuilder.buildKey(KeyBuilder.TYPE_RSA_PUBLIC, ecc.MODULO_RSA_ENGINE_MAX_LENGTH_BITS, false);
// Multiplication speedup engines and arrays used by Bignat.mult_RSATrick()
helper_BN_array1 = ecc.memAlloc.allocateByteArray((short) (ecc.MULT_RSA_ENGINE_MAX_LENGTH_BITS / 8), ecc.memAlloc.getAllocatorType(ObjectAllocator.BNH_helper_BN_array1));
helper_BN_array2 = ecc.memAlloc.allocateByteArray((short) (ecc.MULT_RSA_ENGINE_MAX_LENGTH_BITS / 8), ecc.memAlloc.getAllocatorType(ObjectAllocator.BNH_helper_BN_array2));
fnc_deep_resize_tmp = helper_BN_array1;
fnc_mult_resultArray1 = helper_BN_array1;
fnc_mult_resultArray2 = helper_BN_array2;
fnc_same_value_array1 = helper_BN_array1;
fnc_same_value_hash = helper_BN_array2;
helper_BN_A = new Bignat(ecc.MAX_BIGNAT_SIZE, ecc.memAlloc.getAllocatorType(ObjectAllocator.BNH_helper_BN_A), ecc);
helper_BN_B = new Bignat(ecc.MAX_BIGNAT_SIZE, ecc.memAlloc.getAllocatorType(ObjectAllocator.BNH_helper_BN_B), ecc);
helper_BN_C = new Bignat(ecc.MAX_BIGNAT_SIZE, ecc.memAlloc.getAllocatorType(ObjectAllocator.BNH_helper_BN_C), ecc);
helper_BN_D = new Bignat(ecc.MAX_BIGNAT_SIZE, ecc.memAlloc.getAllocatorType(ObjectAllocator.BNH_helper_BN_D), ecc);
helper_BN_E = new Bignat(ecc.MAX_BIGNAT_SIZE, ecc.memAlloc.getAllocatorType(ObjectAllocator.BNH_helper_BN_E), ecc);
helper_BN_F = new Bignat((short) (ecc.MAX_BIGNAT_SIZE + 2), ecc.memAlloc.getAllocatorType(ObjectAllocator.BNH_helper_BN_F), ecc); // +2 is to correct for infrequent RSA result with two or more leading zeroes
// BN below are just reassigned allocated helper_BN_? so that same helper_BN_? is not used in parallel (checked by lock() unlock())
fnc_mod_add_tmp = helper_BN_A;
fnc_mod_sub_tmpThis = helper_BN_A;
fnc_mod_sub_tmp = helper_BN_B;
fnc_mod_sub_tmpOther = helper_BN_C;
fnc_mult_mod_tmpThis = helper_BN_A;
fnc_mult_mod_tmp_mod = helper_BN_B;
fnc_mult_mod_tmp_x = helper_BN_C;
fnc_exponentiation_tmp = helper_BN_A;
fnc_exponentiation_i = helper_BN_B;
fnc_mod_minus_2 = helper_BN_B;
fnc_negate_tmp = helper_BN_B;
fnc_sqrt_S = helper_BN_A;
fnc_sqrt_exp = helper_BN_A;
fnc_sqrt_p_1 = helper_BN_B;
fnc_sqrt_Q = helper_BN_C;
fnc_sqrt_tmp = helper_BN_D;
fnc_sqrt_z = helper_BN_E;
fnc_mod_mult_tmpThis = helper_BN_E; // mod_mult is called from fnc_sqrt => requires helper_BN_E not being locked in fnc_sqrt when mod_mult is called
fnc_divide_tmpThis = helper_BN_E; // divide is called from fnc_sqrt => requires helper_BN_E not being locked in fnc_sqrt when divide is called
fnc_mod_exp_modBN = helper_BN_F; // mod_exp is called from fnc_sqrt => requires helper_BN_F not being locked in fnc_sqrt when mod_exp is called
fnc_int_add_tmpMag = helper_BN_A;
fnc_int_multiply_mod = helper_BN_A;
fnc_int_multiply_tmpThis = helper_BN_B;
fnc_int_divide_tmpThis = helper_BN_A;
// Allocate BN constants always in EEPROM (only reading)
ONE = new Bignat((short) 1, JCSystem.MEMORY_TYPE_PERSISTENT, ecc);
ONE.one();
TWO = new Bignat((short) 1, JCSystem.MEMORY_TYPE_PERSISTENT, ecc);
TWO.two();
THREE = new Bignat((short) 1, JCSystem.MEMORY_TYPE_PERSISTENT, ecc);
THREE.three();
// Speedup for fast multiplication
fnc_mult_keypair = new KeyPair(KeyPair.ALG_RSA_CRT, ecc.MULT_RSA_ENGINE_MAX_LENGTH_BITS);
fnc_mult_keypair.genKeyPair();
fnc_mult_pubkey_pow2 = (RSAPublicKey) fnc_mult_keypair.getPublic();
//mult_privkey_pow2 = (RSAPrivateCrtKey) mult_keypair.getPrivate();
fnc_mult_pubkey_pow2.setExponent(CONST_TWO, (short) 0, (short) CONST_TWO.length);
fnc_mult_cipher = Cipher.getInstance(Cipher.ALG_RSA_NOPAD, false);
hashEngine = MessageDigest.getInstance(MessageDigest.ALG_SHA_256, false);
ecc.FLAG_FAST_MULT_VIA_RSA = false; // set true only if succesfully allocated and tested below
try { // Subsequent code may fail on some real (e.g., Infineon CJTOP80K) cards - catch exception
fnc_mult_cipher.init(fnc_mult_pubkey_pow2, Cipher.MODE_ENCRYPT);
// Try operation - if doesn't work, exception SW_CANTALLOCATE_BIGNAT is emitted
Util.arrayFillNonAtomic(fnc_mult_resultArray1, (short) 0, (short) fnc_mult_resultArray1.length, (byte) 6);
fnc_mult_cipher.doFinal(fnc_mult_resultArray1, (short) 0, (short) fnc_mult_resultArray1.length, fnc_mult_resultArray1, (short) 0);
ecc.FLAG_FAST_MULT_VIA_RSA = true;
} catch (Exception ignored) {} // discard exception
}
/**
* Erase all values stored in helper objects
*/
void erase() {
helper_BN_A.erase();
helper_BN_B.erase();
helper_BN_C.erase();
helper_BN_D.erase();
helper_BN_E.erase();
helper_BN_F.erase();
Util.arrayFillNonAtomic(tmp_array_short, (short) 0, (short) tmp_array_short.length, (byte) 0);
Util.arrayFillNonAtomic(helper_BN_array1, (short) 0, (short) helper_BN_array1.length, (byte) 0);
Util.arrayFillNonAtomic(helper_BN_array2, (short) 0, (short) helper_BN_array2.length, (byte) 0);
}
/**
* Unlocks all helper objects
*/
public void unlockAll() {
if (helper_BN_A.isLocked()) {
helper_BN_A.unlock();
}
if (helper_BN_B.isLocked()) {
helper_BN_B.unlock();
}
if (helper_BN_C.isLocked()) {
helper_BN_C.unlock();
}
if (helper_BN_D.isLocked()) {
helper_BN_D.unlock();
}
if (helper_BN_E.isLocked()) {
helper_BN_E.unlock();
}
if (helper_BN_F.isLocked()) {
helper_BN_F.unlock();
}
}
}

View File

@ -0,0 +1,142 @@
package opencrypto.jcmathlib;
import javacard.framework.ISOException;
/**
* Configure itself to proper lengths and other parameters according to intended length of ECC
* @author Petr Svenda
*/
public class ECConfig {
/**
* The size of speedup engine used for fast modulo exponent computation
* (must be larger than biggest Bignat used)
*/
public short MODULO_RSA_ENGINE_MAX_LENGTH_BITS = (short) 512;
/**
* The size of speedup engine used for fast multiplication of large numbers
* Must be larger than 2x biggest Bignat used
*/
public short MULT_RSA_ENGINE_MAX_LENGTH_BITS = (short) 768;
/**
* The size of largest integer used in computations
*/
public short MAX_BIGNAT_SIZE = (short) 65; // ((short) (MODULO_ENGINE_MAX_LENGTH_BITS / 8) + 1);
/**
* The size of largest ECC point used
*/
public short MAX_POINT_SIZE = (short) 64;
/**
* The size of single coordinate of the largest ECC point used
*/
public short MAX_COORD_SIZE = (short) 32; // MAX_POINT_SIZE / 2
/**
* If true, fast multiplication of large numbers via RSA engine can be used.
* Is set automatically after successful allocation of required engines
*/
public boolean FLAG_FAST_MULT_VIA_RSA = false;
/**
* Threshold length in bits of an operand after which speedup with RSA multiplication is used.
* Schoolbook multiplication is used for shorter operands
*/
public short FAST_MULT_VIA_RSA_TRESHOLD_LENGTH = (short) 16;
/**
* I true, fast multiplication of ECPoints via KeyAgreement can be used
* Is set automatically after successful allocation of required engines
*/
public boolean FLAG_FAST_EC_MULT_VIA_KA = false;
/**
* Object responsible for easy management of target placement (RAM/EEPROM) fro allocated objects
*/
public ObjectAllocator memAlloc = null;
/**
* Helper structure containing all preallocated objects necessary for Bignat operations
*/
public Bignat_Helper bnh = null;
/**
* Helper structure containing all preallocated objects necessary for ECPoint operations
*/
public ECPoint_Helper ech = null;
/**
* Creates new control structure for requested bit length with all preallocated arrays and engines
* @param maxECLength maximum length of ECPoint objects supported. The provided value is used to
* initialize properly underlying arrays and engines.
*/
public ECConfig(short maxECLength) {
// Set proper lengths and other internal settings based on required ECC length
if (maxECLength <= (short) 256) {
setECC256Config();
}
else if (maxECLength <= (short) 384) {
setECC384Config();
}
else if (maxECLength <= (short) 512) {
setECC512Config();
}
else {
ISOException.throwIt(ReturnCodes.SW_ECPOINT_INVALIDLENGTH);
}
//locker.setLockingActive(false); // if required, locking can be disabled
memAlloc = new ObjectAllocator();
memAlloc.setAllAllocatorsRAM();
//if required, memory for helper objects and arrays can be in persistent memory to save RAM (or some tradeoff)
//ObjectAllocator.setAllAllocatorsEEPROM(); //ObjectAllocator.setAllocatorsTradeoff();
// Allocate helper objects for BN and EC
bnh = new Bignat_Helper(this);
ech = new ECPoint_Helper(this);
}
void reset() {
FLAG_FAST_MULT_VIA_RSA = false;
FLAG_FAST_EC_MULT_VIA_KA = false;
}
public void setECC256Config() {
reset();
MODULO_RSA_ENGINE_MAX_LENGTH_BITS = (short) 512;
MULT_RSA_ENGINE_MAX_LENGTH_BITS = (short) 768;
MAX_POINT_SIZE = (short) 64;
computeDerivedLengths();
}
public void setECC384Config() {
reset();
MODULO_RSA_ENGINE_MAX_LENGTH_BITS = (short) 768;
MULT_RSA_ENGINE_MAX_LENGTH_BITS = (short) 1024;
MAX_POINT_SIZE = (short) 96;
computeDerivedLengths();
}
public void setECC512Config() {
reset();
MODULO_RSA_ENGINE_MAX_LENGTH_BITS = (short) 1024;
MULT_RSA_ENGINE_MAX_LENGTH_BITS = (short) 1280;
MAX_POINT_SIZE = (short) 128;
computeDerivedLengths();
}
public void setECC521Config() {
reset();
MODULO_RSA_ENGINE_MAX_LENGTH_BITS = (short) 1280;
MULT_RSA_ENGINE_MAX_LENGTH_BITS = (short) 1280;
MAX_POINT_SIZE = (short) 129;
computeDerivedLengths();
}
private void computeDerivedLengths() {
MAX_BIGNAT_SIZE = (short) ((short) (MODULO_RSA_ENGINE_MAX_LENGTH_BITS / 8) + 1);
MAX_COORD_SIZE = (short) (MAX_POINT_SIZE / 2);
}
/**
* Unlocks all logically locked arrays and objects. Useful as recovery after premature end of some operation (e.g., due to exception)
* when some objects remains locked.
*/
void unlockAll() {
bnh.unlockAll();
ech.unlockAll();
}
}

View File

@ -0,0 +1,195 @@
package opencrypto.jcmathlib;
import javacard.framework.Util;
import javacard.security.ECPrivateKey;
import javacard.security.ECPublicKey;
import javacard.security.KeyBuilder;
import javacard.security.KeyPair;
/**
*
* @author Vasilios Mavroudis and Petr Svenda
*/
public class ECCurve {
public final short KEY_LENGTH; //Bits
public final short POINT_SIZE; //Bytes
public final short COORD_SIZE; //Bytes
//Parameters
public byte[] p = null;
public byte[] a = null;
public byte[] b = null;
public byte[] G = null;
public byte[] r = null;
public Bignat pBN;
public Bignat aBN;
public Bignat bBN;
public KeyPair disposable_pair;
public ECPrivateKey disposable_priv;
/**
* Creates new curve object from provided parameters. Either copy of provided
* arrays is performed (bCopyArgs == true, input arrays can be reused later for other
* purposes) or arguments are directly stored (bCopyArgs == false, usable for fixed static arrays) .
* @param bCopyArgs if true, copy of arguments is created, otherwise reference is directly stored
* @param p_arr array with p
* @param a_arr array with a
* @param b_arr array with b
* @param G_arr array with base point G
* @param r_arr array with r
*/
public ECCurve(boolean bCopyArgs, byte[] p_arr, byte[] a_arr, byte[] b_arr, byte[] G_arr, byte[] r_arr, ECConfig ecc) {
//ECCurve_initialize(p_arr, a_arr, b_arr, G_arr, r_arr);
this.KEY_LENGTH = (short) (p_arr.length * 8);
this.POINT_SIZE = (short) G_arr.length;
this.COORD_SIZE = (short) ((short) (G_arr.length - 1) / 2);
if (bCopyArgs) {
// Copy curve parameters into newly allocated arrays in EEPROM (will be only read, not written later => good performance even when in EEPROM)
this.p = new byte[(short) p_arr.length];
this.a = new byte[(short) a_arr.length];
this.b = new byte[(short) b_arr.length];
this.G = new byte[(short) G_arr.length];
this.r = new byte[(short) r_arr.length];
Util.arrayCopyNonAtomic(p_arr, (short) 0, p, (short) 0, (short) p.length);
Util.arrayCopyNonAtomic(a_arr, (short) 0, a, (short) 0, (short) a.length);
Util.arrayCopyNonAtomic(b_arr, (short) 0, b, (short) 0, (short) b.length);
Util.arrayCopyNonAtomic(G_arr, (short) 0, G, (short) 0, (short) G.length);
Util.arrayCopyNonAtomic(r_arr, (short) 0, r, (short) 0, (short) r.length);
}
else {
// No allocation, store directly provided arrays
this.p = p_arr;
this.a = a_arr;
this.b = b_arr;
this.G = G_arr;
this.r = r_arr;
}
// We will not modify values of p/a/b during the lifetime of curve => allocate helper bignats directly from the array
this.pBN = new Bignat(this.p, ecc);
this.aBN = new Bignat(this.a, ecc);
this.bBN = new Bignat(this.b, ecc);
this.disposable_pair = this.newKeyPair(null);
this.disposable_priv = (ECPrivateKey) this.disposable_pair.getPrivate();
}
/**
* Refresh critical information stored in RAM for performance reasons after a card reset (RAM was cleared).
*/
public void updateAfterReset() {
this.pBN.from_byte_array(this.p);
this.aBN.from_byte_array(this.a);
this.bBN.from_byte_array(this.b);
}
/**
* Creates a new keyPair based on this curve parameters. KeyPair object is reused if provided. Fresh keyPair value is generated.
* @param existingKeyPair existing KeyPair object which is reused if required. If null, new KeyPair is allocated
* @return new or existing object with fresh key pair value
*/
KeyPair newKeyPair(KeyPair existingKeyPair) {
ECPrivateKey privKey;
ECPublicKey pubKey;
if (existingKeyPair == null) { // Allocate if not supplied
existingKeyPair = new KeyPair(KeyPair.ALG_EC_FP, KEY_LENGTH);
}
// Some implementation wil not return valid pub key until ecKeyPair.genKeyPair() is called
// Other implementation will fail with exception if same is called => try catch and drop any exception
try {
pubKey = (ECPublicKey) existingKeyPair.getPublic();
if (pubKey == null) {
existingKeyPair.genKeyPair();
}
} catch (Exception e) {
} // intentionally do nothing
privKey = (ECPrivateKey) existingKeyPair.getPrivate();
pubKey = (ECPublicKey) existingKeyPair.getPublic();
// Set required values
privKey.setFieldFP(p, (short) 0, (short) p.length);
privKey.setA(a, (short) 0, (short) a.length);
privKey.setB(b, (short) 0, (short) b.length);
privKey.setG(G, (short) 0, (short) G.length);
privKey.setR(r, (short) 0, (short) r.length);
privKey.setK((short) 1);
pubKey.setFieldFP(p, (short) 0, (short) p.length);
pubKey.setA(a, (short) 0, (short) a.length);
pubKey.setB(b, (short) 0, (short) b.length);
pubKey.setG(G, (short) 0, (short) G.length);
pubKey.setR(r, (short) 0, (short) r.length);
pubKey.setK((short) 1);
existingKeyPair.genKeyPair();
return existingKeyPair;
}
public KeyPair newKeyPair_legacy(KeyPair existingKeyPair) {
ECPrivateKey privKey;
ECPublicKey pubKey;
if (existingKeyPair == null) {
// We need to create required objects
privKey = (ECPrivateKey)KeyBuilder.buildKey(KeyBuilder.TYPE_EC_FP_PRIVATE, KEY_LENGTH, false);
pubKey = (ECPublicKey)KeyBuilder.buildKey(KeyBuilder.TYPE_EC_FP_PUBLIC, KEY_LENGTH, false);
}
else {
// Obtain from object
privKey = (ECPrivateKey) existingKeyPair.getPrivate();
pubKey = (ECPublicKey) existingKeyPair.getPublic();
}
// Set required values
privKey.setFieldFP(p, (short) 0, (short) p.length);
privKey.setA(a, (short) 0, (short) a.length);
privKey.setB(b, (short) 0, (short) b.length);
privKey.setG(G, (short) 0, (short) G.length);
privKey.setR(r, (short) 0, (short) r.length);
pubKey.setFieldFP(p, (short) 0, (short) p.length);
pubKey.setA(a, (short) 0, (short) a.length);
pubKey.setB(b, (short) 0, (short) b.length);
pubKey.setG(G, (short) 0, (short) G.length);
pubKey.setR(r, (short) 0, (short) r.length);
if (existingKeyPair == null) { // Allocate if not supplied
existingKeyPair = new KeyPair(pubKey, privKey);
}
existingKeyPair.genKeyPair();
return existingKeyPair;
}
/**
* Converts provided Bignat into temporary EC private key object. No new
* allocation is performed, returned ECPrivateKey is overwritten by next call.
* @param bn Bignat with new value
* @return ECPrivateKey initialized with provided Bignat
*/
public ECPrivateKey bignatAsPrivateKey(Bignat bn) {
disposable_priv.setS(bn.as_byte_array(), (short) 0, bn.length());
return disposable_priv;
}
/**
* Set new G for this curve. Also updates all dependent key values.
* @param newG buffer with new G
* @param newGOffset start offset within newG
* @param newGLen length of new G
*/
public void setG(byte[] newG, short newGOffset, short newGLen) {
Util.arrayCopyNonAtomic(newG, newGOffset, G, (short) 0, newGLen);
this.disposable_pair = this.newKeyPair(this.disposable_pair);
this.disposable_priv = (ECPrivateKey) this.disposable_pair.getPrivate();
this.disposable_priv.setG(newG, newGOffset, newGLen);
}
}

View File

@ -0,0 +1,395 @@
package opencrypto.jcmathlib;
import javacard.framework.ISOException;
import javacard.framework.Util;
import javacard.security.ECPrivateKey;
import javacard.security.ECPublicKey;
import javacard.security.KeyPair;
import javacard.security.Signature;
/**
*
* @author Vasilios Mavroudis and Petr Svenda
*/
public class ECPoint {
private final ECConfig ecc;
private ECPublicKey thePoint;
private KeyPair thePointKeyPair;
private final ECCurve theCurve;
/**
* Creates new ECPoint object for provided {@code curve}. Random initial point value is generated.
* The point will use helper structures from provided ECPoint_Helper object.
* @param curve point's elliptic curve
* @param ecc object with preallocated helper objects and memory arrays
*/
public ECPoint(ECCurve curve, ECConfig ecc) {
this.theCurve = curve;
this.ecc = ecc;
updatePointObjects();
}
/**
* Returns length of this point in bytes.
*
* @return
*/
public short length() {
return (short) (thePoint.getSize() / 8);
}
/**
* Properly updates all point values in case of a change of an underlying curve.
* New random point value is generated.
*/
public final void updatePointObjects() {
this.thePointKeyPair = this.theCurve.newKeyPair(this.thePointKeyPair);
this.thePoint = (ECPublicKey) thePointKeyPair.getPublic();
}
/**
* Generates new random point value.
*/
public void randomize(){
if (this.thePointKeyPair == null) {
this.thePointKeyPair = this.theCurve.newKeyPair(this.thePointKeyPair);
this.thePoint = (ECPublicKey) thePointKeyPair.getPublic();
}
else {
this.thePointKeyPair.genKeyPair();
}
}
/**
* Copy value of provided point into this. This and other point must have
* curve with same parameters, only length is checked.
* @param other point to be copied
*/
public void copy(ECPoint other) {
if (this.length() != other.length()) {
ISOException.throwIt(ReturnCodes.SW_ECPOINT_INVALIDLENGTH);
}
short len = other.getW(ecc.ech.uncompressed_point_arr1, (short) 0);
this.setW(ecc.ech.uncompressed_point_arr1, (short) 0, len);
}
/**
* Set this point value (parameter W) from array with value encoded as per ANSI X9.62.
* The uncompressed form is always supported. If underlying native JavaCard implementation
* of {@code ECPublickKey} supports compressed points, then this method accepts also compressed points.
* @param buffer array with serialized point
* @param offset start offset within input array
* @param length length of point
*/
public void setW(byte[] buffer, short offset, short length) {
this.thePoint.setW(buffer, offset, length);
}
/**
* Returns current value of this point.
* @param buffer memory array where to store serailized point value
* @param offset start offset for output serialized point
* @return length of serialized point (number of bytes)
*/
public short getW(byte[] buffer, short offset) {
return thePoint.getW(buffer, offset);
}
/**
* Returns this point value as ECPublicKey object. No copy of point is made
* before return, so change of returned object will also change this point value.
* @return point as ECPublicKey object
*/
public ECPublicKey asPublicKey() {
return this.thePoint;
}
/**
* Returns curve associated with this point. No copy of curve is made
* before return, so change of returned object will also change curve for
* this point.
*
* @return curve as ECCurve object
*/
public ECCurve getCurve() {
return theCurve;
}
/**
* Returns the X coordinate of this point in uncompressed form.
* @param buffer output array for X coordinate
* @param offset start offset within output array
* @return length of X coordinate (in bytes)
*/
public short getX(byte[] buffer, short offset) {
thePoint.getW(ecc.ech.uncompressed_point_arr1, (short) 0);
Util.arrayCopyNonAtomic(ecc.ech.uncompressed_point_arr1, (short) 1, buffer, offset, this.theCurve.COORD_SIZE);
return this.theCurve.COORD_SIZE;
}
/**
* Returns the Y coordinate of this point in uncompressed form.
*
* @param buffer output array for Y coordinate
* @param offset start offset within output array
* @return length of Y coordinate (in bytes)
*/
public short getY(byte[] buffer, short offset) {
thePoint.getW(ecc.ech.uncompressed_point_arr1, (short) 0);
Util.arrayCopyNonAtomic(ecc.ech.uncompressed_point_arr1, (short)(1 + this.theCurve.COORD_SIZE), buffer, offset, this.theCurve.COORD_SIZE);
return this.theCurve.COORD_SIZE;
}
/**
* Returns the Y coordinate of this point in form of Bignat object.
*
* @param yCopy Bignat object which will be set with value of this point
*/
public void getY(Bignat yCopy) {
yCopy.set_size(this.getY(yCopy.as_byte_array(), (short) 0));
}
/**
* Doubles the current value of this point.
*/
public void makeDouble() {
// doubling via add sometimes causes exception inside KeyAgreement engine
// this.add(this);
// Use bit slower, but more robust version via multiplication by 2
this.multiplication(ecc.bnh.TWO);
}
/**
* Adds this (P) and provided (Q) point. Stores a resulting value into this point.
* @param other point to be added to this.
*/
public void add(ECPoint other) {
this.thePoint.getW(ecc.ech.uncompressed_point_arr1, (short) 0);
ecc.ech.fnc_add_x_p.lock();
ecc.ech.fnc_add_x_p.set_size(this.theCurve.COORD_SIZE);
ecc.ech.fnc_add_x_p.from_byte_array(this.theCurve.COORD_SIZE, (short) 0, ecc.ech.uncompressed_point_arr1, (short) 1);
ecc.ech.fnc_add_y_p.lock();
ecc.ech.fnc_add_y_p.set_size(this.theCurve.COORD_SIZE);
ecc.ech.fnc_add_y_p.from_byte_array(this.theCurve.COORD_SIZE, (short) 0, ecc.ech.uncompressed_point_arr1, (short) (1 + this.theCurve.COORD_SIZE));
// l = (y_q-y_p)/(x_q-x_p))
// x_r = l^2 - x_p -x_q
// y_r = l(x_p-x_r)-y_p
// P+Q=R
ecc.ech.fnc_add_nominator.lock();
ecc.ech.fnc_add_denominator.lock();
if (this == other) {
//lambda = (3(x_p^2)+a)/(2y_p)
//(3(x_p^2)+a)
ecc.ech.fnc_add_nominator.clone(ecc.ech.fnc_add_x_p);
ecc.ech.fnc_add_nominator.mod_exp(ecc.bnh.TWO, this.theCurve.pBN);
ecc.ech.fnc_add_nominator.mod_mult(ecc.ech.fnc_add_nominator, ecc.bnh.THREE, this.theCurve.pBN);
ecc.ech.fnc_add_nominator.mod_add(this.theCurve.aBN, this.theCurve.pBN);
// (2y_p)
ecc.ech.fnc_add_denominator.clone(ecc.ech.fnc_add_y_p);
ecc.ech.fnc_add_denominator.mod_mult(ecc.ech.fnc_add_y_p, ecc.bnh.TWO, this.theCurve.pBN);
ecc.ech.fnc_add_denominator.mod_inv(this.theCurve.pBN);
} else {
// lambda=(y_q-y_p)/(x_q-x_p) mod p
other.thePoint.getW(ecc.ech.uncompressed_point_arr1, (short) 0);
ecc.ech.fnc_add_x_q.lock();
ecc.ech.fnc_add_x_q.set_size(this.theCurve.COORD_SIZE);
ecc.ech.fnc_add_x_q.from_byte_array(other.theCurve.COORD_SIZE, (short) 0, ecc.ech.uncompressed_point_arr1, (short) 1);
ecc.ech.fnc_add_nominator.set_size(this.theCurve.COORD_SIZE);
ecc.ech.fnc_add_nominator.from_byte_array(this.theCurve.COORD_SIZE, (short) 0, ecc.ech.uncompressed_point_arr1, (short) (1 + this.theCurve.COORD_SIZE));
ecc.ech.fnc_add_nominator.mod(this.theCurve.pBN);
ecc.ech.fnc_add_nominator.mod_sub(ecc.ech.fnc_add_y_p, this.theCurve.pBN);
// (x_q-x_p)
ecc.ech.fnc_add_denominator.clone(ecc.ech.fnc_add_x_q);
ecc.ech.fnc_add_denominator.mod(this.theCurve.pBN);
ecc.ech.fnc_add_denominator.mod_sub(ecc.ech.fnc_add_x_p, this.theCurve.pBN);
ecc.ech.fnc_add_denominator.mod_inv(this.theCurve.pBN);
}
ecc.ech.fnc_add_lambda.lock();
ecc.ech.fnc_add_lambda.resize_to_max(false);
ecc.ech.fnc_add_lambda.zero();
ecc.ech.fnc_add_lambda.mod_mult(ecc.ech.fnc_add_nominator, ecc.ech.fnc_add_denominator, this.theCurve.pBN);
ecc.ech.fnc_add_nominator.unlock();
ecc.ech.fnc_add_denominator.unlock();
// (x_p,y_p)+(x_q,y_q)=(x_r,y_r)
// lambda=(y_q-y_p)/(x_q-x_p)
//x_r=lambda^2-x_p-x_q
ecc.ech.fnc_add_x_r.lock();
if (this == other) {
short len = this.multiplication_x(ecc.bnh.TWO, ecc.ech.fnc_add_x_r.as_byte_array(), (short) 0);
ecc.ech.fnc_add_x_r.set_size(len);
} else {
ecc.ech.fnc_add_x_r.clone(ecc.ech.fnc_add_lambda);
//m_occ.ecHelper.fnc_add_x_r.mod_exp(occ.bnHelper.TWO, this.TheCurve.pBN);
ecc.ech.fnc_add_x_r.mod_exp2(this.theCurve.pBN);
ecc.ech.fnc_add_x_r.mod_sub(ecc.ech.fnc_add_x_p, this.theCurve.pBN);
ecc.ech.fnc_add_x_r.mod_sub(ecc.ech.fnc_add_x_q, this.theCurve.pBN);
ecc.ech.fnc_add_x_q.unlock();
}
//y_r=lambda(x_p-x_r)-y_p
ecc.ech.fnc_add_y_r.lock();
ecc.ech.fnc_add_y_r.clone(ecc.ech.fnc_add_x_p);
ecc.ech.fnc_add_x_p.unlock();
ecc.ech.fnc_add_y_r.mod_sub(ecc.ech.fnc_add_x_r, this.theCurve.pBN);
ecc.ech.fnc_add_y_r.mod_mult(ecc.ech.fnc_add_y_r, ecc.ech.fnc_add_lambda, this.theCurve.pBN);
ecc.ech.fnc_add_lambda.unlock();
ecc.ech.fnc_add_y_r.mod_sub(ecc.ech.fnc_add_y_p, this.theCurve.pBN);
ecc.ech.fnc_add_y_p.unlock();
ecc.ech.uncompressed_point_arr1[0] = (byte)0x04;
// If x_r.length() and y_r.length() is smaller than this.TheCurve.COORD_SIZE due to leading zeroes which were shrinked before, then we must add these back
ecc.ech.fnc_add_x_r.prepend_zeros(this.theCurve.COORD_SIZE, ecc.ech.uncompressed_point_arr1, (short) 1);
ecc.ech.fnc_add_x_r.unlock();
ecc.ech.fnc_add_y_r.prepend_zeros(this.theCurve.COORD_SIZE, ecc.ech.uncompressed_point_arr1, (short) (1 + this.theCurve.COORD_SIZE));
ecc.ech.fnc_add_y_r.unlock();
this.setW(ecc.ech.uncompressed_point_arr1, (short) 0, this.theCurve.POINT_SIZE);
}
/**
* Multiply value of this point by provided scalar. Stores the result into
* this point.
*
* @param scalar value of scalar for multiplication
*/
public void multiplication(byte[] scalar, short scalarOffset, short scalarLen) {
ecc.ech.fnc_multiplication_scalar.lock();
ecc.ech.fnc_multiplication_scalar.set_size(scalarLen);
ecc.ech.fnc_multiplication_scalar.from_byte_array(scalarLen, (short) 0, scalar, scalarOffset);
multiplication(ecc.ech.fnc_multiplication_scalar);
ecc.ech.fnc_multiplication_scalar.unlock();
}
/**
* Multiply value of this point by provided scalar. Stores the result into this point.
* @param scalar value of scalar for multiplication
*/
public void multiplication(Bignat scalar) {
ecc.ech.fnc_multiplication_x.lock();
short len = this.multiplication_x(scalar, ecc.ech.fnc_multiplication_x.as_byte_array(), (short) 0);
ecc.ech.fnc_multiplication_x.set_size(len);
//Y^2 = X^3 + XA + B = x(x^2+A)+B
ecc.ech.fnc_multiplication_y_sq.lock();
ecc.ech.fnc_multiplication_y_sq.clone(ecc.ech.fnc_multiplication_x);
ecc.ech.fnc_multiplication_y_sq.mod_exp(ecc.bnh.TWO, this.theCurve.pBN);
ecc.ech.fnc_multiplication_y_sq.mod_add(this.theCurve.aBN, this.theCurve.pBN);
ecc.ech.fnc_multiplication_y_sq.mod_mult(ecc.ech.fnc_multiplication_y_sq, ecc.ech.fnc_multiplication_x, this.theCurve.pBN);
ecc.ech.fnc_multiplication_y_sq.mod_add(this.theCurve.bBN, this.theCurve.pBN);
ecc.ech.fnc_multiplication_y1.lock();
ecc.ech.fnc_multiplication_y1.clone(ecc.ech.fnc_multiplication_y_sq);
ecc.ech.fnc_multiplication_y_sq.unlock();
ecc.ech.fnc_multiplication_y1.sqrt_FP(this.theCurve.pBN);
// Construct public key with <x, y_1>
ecc.ech.uncompressed_point_arr1[0] = 0x04;
ecc.ech.fnc_multiplication_x.prepend_zeros(this.theCurve.COORD_SIZE, ecc.ech.uncompressed_point_arr1, (short) 1);
ecc.ech.fnc_multiplication_x.unlock();
ecc.ech.fnc_multiplication_y1.prepend_zeros(this.theCurve.COORD_SIZE, ecc.ech.uncompressed_point_arr1, (short) (1 + theCurve.COORD_SIZE));
this.setW(ecc.ech.uncompressed_point_arr1, (short) 0, theCurve.POINT_SIZE); //So that we can convert to pub key
// Check if public point <x, y_1> corresponds to the "secret" (i.e., our scalar)
if (!SignVerifyECDSA(this.theCurve.bignatAsPrivateKey(scalar), this.asPublicKey(), this.ecc.ech.fnc_SignVerifyECDSA_signEngine, ecc.bnh.fnc_mult_resultArray1)) { //If verification fails, then pick the <x, y_2>
ecc.ech.fnc_multiplication_y2.lock();
ecc.ech.fnc_multiplication_y2.clone(this.theCurve.pBN); //y_2 = p - y_1
ecc.ech.fnc_multiplication_y2.mod_sub(ecc.ech.fnc_multiplication_y1, this.theCurve.pBN);
ecc.ech.fnc_multiplication_y2.copy_to_buffer(ecc.ech.uncompressed_point_arr1, (short) (1 + theCurve.COORD_SIZE));
ecc.ech.fnc_multiplication_y2.unlock();
}
ecc.ech.fnc_multiplication_y1.unlock();
this.setW(ecc.ech.uncompressed_point_arr1, (short)0, theCurve.POINT_SIZE);
}
/**
* Multiplies this point value with provided scalar and stores result into provided array.
* No modification of this point is performed.
* @param scalar value of scalar for multiplication
* @param outBuffer output array for resulting value
* @param outBufferOffset offset within output array
* @return length of resulting value (in bytes)
*/
public short multiplication_x(Bignat scalar, byte[] outBuffer, short outBufferOffset) {
return multiplication_x_KA(scalar, outBuffer, outBufferOffset);
}
/**
* Multiplies this point value with provided scalar and stores result into
* provided array. No modification of this point is performed.
* Native KeyAgreement engine is used.
*
* @param scalar value of scalar for multiplication
* @param outBuffer output array for resulting value
* @param outBufferOffset offset within output array
* @return length of resulting value (in bytes)
*/
private short multiplication_x_KA(Bignat scalar, byte[] outBuffer, short outBufferOffset) {
// NOTE: potential problem on real cards (j2e) - when small scalar is used (e.g., Bignat.TWO), operation sometimes freezes
theCurve.disposable_priv.setS(scalar.as_byte_array(), (short) 0, scalar.length());
ecc.ech.fnc_multiplication_x_keyAgreement.init(theCurve.disposable_priv);
short len = this.getW(ecc.ech.uncompressed_point_arr1, (short) 0);
len = ecc.ech.fnc_multiplication_x_keyAgreement.generateSecret(ecc.ech.uncompressed_point_arr1, (short) 0, len, outBuffer, outBufferOffset);
// Return always length of whole coordinate X instead of len - some real cards returns shorter value equal to SHA-1 output size although PLAIN results is filled into buffer (GD60)
return this.theCurve.COORD_SIZE;
}
/**
* Computes negation of this point.
*/
public void negate() {
// Operation will dump point into uncompressed_point_arr, negate Y and restore back
ecc.ech.fnc_negate_yBN.lock();
thePoint.getW(ecc.ech.uncompressed_point_arr1, (short) 0);
ecc.ech.fnc_negate_yBN.set_size(this.theCurve.COORD_SIZE);
ecc.ech.fnc_negate_yBN.from_byte_array(this.theCurve.COORD_SIZE, (short) 0, ecc.ech.uncompressed_point_arr1, (short) (1 + this.theCurve.COORD_SIZE));
ecc.ech.fnc_negate_yBN.mod_negate(this.theCurve.pBN);
// Restore whole point back
ecc.ech.fnc_negate_yBN.prepend_zeros(this.theCurve.COORD_SIZE, ecc.ech.uncompressed_point_arr1, (short) (1 + this.theCurve.COORD_SIZE));
ecc.ech.fnc_negate_yBN.unlock();
this.setW(ecc.ech.uncompressed_point_arr1, (short) 0, this.theCurve.POINT_SIZE);
}
/**
* Compares this and provided point for equality. The comparison is made using hash of both values to prevent leak of position of mismatching byte.
* @param other second point for comparison
* @return true if both point are exactly equal (same length, same value), false otherwise
*/
public boolean isEqual(ECPoint other) {
boolean bResult = false;
if (this.length() != other.length()) {
return false;
}
else {
// The comparison is made with hash of point values instead of directly values.
// This way, offset of first mismatching byte is not leaked via timing side-channel.
// Additionally, only single array is required for storage of plain point values thus saving some RAM.
short len = this.getW(ecc.ech.uncompressed_point_arr1, (short) 0);
ecc.bnh.hashEngine.doFinal(ecc.ech.uncompressed_point_arr1, (short) 0, len, ecc.ech.hashArray, (short) 0);
len = other.getW(ecc.ech.uncompressed_point_arr1, (short) 0);
len = ecc.bnh.hashEngine.doFinal(ecc.ech.uncompressed_point_arr1, (short) 0, len, ecc.ech.uncompressed_point_arr1, (short) 0);
bResult = Util.arrayCompare(ecc.ech.hashArray, (short) 0, ecc.ech.uncompressed_point_arr1, (short) 0, len) == 0;
}
return bResult;
}
static byte[] msg = {(byte) 0x01, (byte) 0x01, (byte) 0x02, (byte) 0x03};
public static boolean SignVerifyECDSA(ECPrivateKey privateKey, ECPublicKey publicKey, Signature signEngine, byte[] tmpSignArray) {
signEngine.init(privateKey, Signature.MODE_SIGN);
short signLen = signEngine.sign(msg, (short) 0, (short) msg.length, tmpSignArray, (short) 0);
signEngine.init(publicKey, Signature.MODE_VERIFY);
return signEngine.verify(msg, (short) 0, (short) msg.length, tmpSignArray, (short) 0, signLen);
}
}

View File

@ -0,0 +1,133 @@
package opencrypto.jcmathlib;
import javacard.framework.Util;
import javacard.security.KeyAgreement;
import javacard.security.Signature;
/**
*
* @author Petr Svenda
*/
public class ECPoint_Helper {
// Selected constants missing from older JC API specs
public static final byte KeyAgreement_ALG_EC_SVDP_DH_PLAIN = (byte) 3;
public static final byte Signature_ALG_ECDSA_SHA_256 = (byte) 33;
private final ECConfig ecc;
byte[] uncompressed_point_arr1 = null;
byte[] hashArray = null;
public static final byte NUM_HELPER_ARRAYS = 2;
// These Bignats helperEC_BN_? are allocated
Bignat helperEC_BN_A;
Bignat helperEC_BN_B;
Bignat helperEC_BN_C;
Bignat helperEC_BN_D;
Bignat helperEC_BN_E;
Bignat helperEC_BN_F;
// These Bignats are just pointing to some helperEC_BN_? so reasonable naming is preserved yet no need to actually allocated whole Bignat object
Bignat fnc_add_x_r; // frequent write
Bignat fnc_add_y_r; // frequent write
Bignat fnc_add_x_p; // one init, then just read
Bignat fnc_add_y_p; // one init, then just read
Bignat fnc_add_x_q; // one init, then just read
Bignat fnc_add_lambda; // write mod_mul (but only final result)
Bignat fnc_add_nominator; // frequent write
Bignat fnc_add_denominator; // frequent write
Bignat fnc_multiplication_x; // result write
Bignat fnc_multiplication_y_sq; // frequent write
Bignat fnc_multiplication_scalar; // write once, read
Bignat fnc_multiplication_y1; // mostly just read, write inside sqrt_FP
Bignat fnc_multiplication_y2; // mostly just read, result write
Bignat fnc_negate_yBN; // mostly just read, result write
KeyAgreement fnc_multiplication_x_keyAgreement;
Signature fnc_SignVerifyECDSA_signEngine;
public ECPoint_Helper(ECConfig ecCfg) {
this.ecc = ecCfg;
helperEC_BN_A = new Bignat(ecc.MAX_POINT_SIZE, ecc.memAlloc.getAllocatorType(ObjectAllocator.ECPH_helperEC_BN_A), ecc);
helperEC_BN_B = new Bignat(ecc.MAX_COORD_SIZE, ecc.memAlloc.getAllocatorType(ObjectAllocator.ECPH_helperEC_BN_B), ecc);
helperEC_BN_C = new Bignat(ecc.MAX_COORD_SIZE, ecc.memAlloc.getAllocatorType(ObjectAllocator.ECPH_helperEC_BN_C), ecc);
helperEC_BN_D = new Bignat(ecc.MAX_COORD_SIZE, ecc.memAlloc.getAllocatorType(ObjectAllocator.ECPH_helperEC_BN_D), ecc);
helperEC_BN_E = new Bignat(ecc.MAX_COORD_SIZE, ecc.memAlloc.getAllocatorType(ObjectAllocator.ECPH_helperEC_BN_E), ecc);
helperEC_BN_F = new Bignat(ecc.MAX_COORD_SIZE, ecc.memAlloc.getAllocatorType(ObjectAllocator.ECPH_helperEC_BN_F), ecc);
// Important: assignment of helper BNs is made according to two criterions:
// 1. Correctness: same BN must not be assigned to overlapping operations (guarded by lock/unlock)
// 2. Memory tradeoff: we like to put as few BNs into RAM as possible. So most frequently used BNs for write should be in RAM
// and at the same time we like to have as few BNs in RAM as possible.
// So think twice before changing the assignments!
fnc_add_x_r = helperEC_BN_B;
fnc_add_y_r = helperEC_BN_C;
fnc_add_x_p = helperEC_BN_D;
fnc_add_y_p = helperEC_BN_E;
fnc_add_x_q = helperEC_BN_F;
fnc_add_nominator = helperEC_BN_B;
fnc_add_denominator = helperEC_BN_C;
fnc_add_lambda = helperEC_BN_A;
fnc_multiplication_scalar = helperEC_BN_F;
fnc_multiplication_x = helperEC_BN_B;
fnc_multiplication_y_sq = helperEC_BN_C;
fnc_multiplication_y1 = helperEC_BN_D;
fnc_multiplication_y2 = helperEC_BN_B;
fnc_negate_yBN = helperEC_BN_C;
uncompressed_point_arr1 = ecc.memAlloc.allocateByteArray((short) (ecc.MAX_POINT_SIZE + 1), ecc.memAlloc.getAllocatorType(ObjectAllocator.ECPH_uncompressed_point_arr1));
hashArray = ecc.memAlloc.allocateByteArray(ecc.bnh.hashEngine.getLength(), ecc.memAlloc.getAllocatorType(ObjectAllocator.ECPH_hashArray));
ecc.FLAG_FAST_EC_MULT_VIA_KA = false; // set true only if succesfully allocated and tested below
try {
fnc_multiplication_x_keyAgreement = KeyAgreement.getInstance(KeyAgreement_ALG_EC_SVDP_DH_PLAIN, false);
fnc_SignVerifyECDSA_signEngine = Signature.getInstance(Signature_ALG_ECDSA_SHA_256, false);
ecc.FLAG_FAST_EC_MULT_VIA_KA = true;
}
catch (Exception ignored) {} // Discard any exception
}
/**
* Erase all values stored in helper objects
*/
void erase() {
helperEC_BN_A.erase();
helperEC_BN_B.erase();
helperEC_BN_C.erase();
helperEC_BN_D.erase();
helperEC_BN_E.erase();
helperEC_BN_F.erase();
Util.arrayFillNonAtomic(uncompressed_point_arr1, (short) 0, (short) uncompressed_point_arr1.length, (byte) 0);
}
/**
* Unlocks all helper objects
*/
public void unlockAll() {
if (helperEC_BN_A.isLocked()) {
helperEC_BN_A.unlock();
}
if (helperEC_BN_B.isLocked()) {
helperEC_BN_B.unlock();
}
if (helperEC_BN_C.isLocked()) {
helperEC_BN_C.unlock();
}
if (helperEC_BN_D.isLocked()) {
helperEC_BN_D.unlock();
}
if (helperEC_BN_E.isLocked()) {
helperEC_BN_E.unlock();
}
if (helperEC_BN_F.isLocked()) {
helperEC_BN_F.unlock();
}
}
}

View File

@ -0,0 +1,142 @@
package opencrypto.jcmathlib;
import javacard.framework.ISOException;
import javacard.framework.JCSystem;
import javacard.framework.Util;
/**
* The control point for unified allocation of arrays and objects with customable
* specification of allocator type (RAM/EEPROM) for particular array. Allows for
* quick personalization and optimization of memory use when compiling for cards
* with more/less available memory.
*
* @author Petr Svenda
*/
public class ObjectAllocator {
short allocatedInRAM = 0;
short allocatedInEEPROM = 0;
byte[] ALLOCATOR_TYPE_ARRAY = null;
public static final byte BNH_helper_BN_array1 = 0;
public static final byte BNH_helper_BN_array2 = 1;
public static final byte BNH_helper_BN_A = 2;
public static final byte BNH_helper_BN_B = 3;
public static final byte BNH_helper_BN_C = 4;
public static final byte BNH_helper_BN_D = 5;
public static final byte BNH_helper_BN_E = 6;
public static final byte BNH_helper_BN_F = 7;
public static final byte ECPH_helperEC_BN_A = 8;
public static final byte ECPH_helperEC_BN_B = 9;
public static final byte ECPH_helperEC_BN_C = 10;
public static final byte ECPH_helperEC_BN_D = 11;
public static final byte ECPH_helperEC_BN_E = 12;
public static final byte ECPH_helperEC_BN_F = 13;
public static final byte ECPH_uncompressed_point_arr1 = 14;
public static final byte ECPH_hashArray = 15;
public static final short ALLOCATOR_TYPE_ARRAY_LENGTH = (short) (ECPH_hashArray + 1);
/**
* Creates new allocator control object, resets performance counters
*/
public ObjectAllocator() {
ALLOCATOR_TYPE_ARRAY = new byte[ALLOCATOR_TYPE_ARRAY_LENGTH];
setAllAllocatorsRAM();
resetAllocatorCounters();
}
/**
* All type of allocator for all object as EEPROM
*/
public final void setAllAllocatorsEEPROM() {
Util.arrayFillNonAtomic(ALLOCATOR_TYPE_ARRAY, (short) 0, (short) ALLOCATOR_TYPE_ARRAY.length, JCSystem.MEMORY_TYPE_PERSISTENT);
}
/**
* All type of allocator for all object as RAM
*/
public void setAllAllocatorsRAM() {
Util.arrayFillNonAtomic(ALLOCATOR_TYPE_ARRAY, (short) 0, (short) ALLOCATOR_TYPE_ARRAY.length, JCSystem.MEMORY_TYPE_TRANSIENT_RESET);
}
/**
* All type of allocator for selected object as RAM (faster), rest EEPROM (saving RAM)
* The current settings is heuristically obtained from measurements of performance of Bignat and ECPoint operations
*/
public void setAllocatorsTradeoff() {
// Set initial allocators into EEPROM
setAllAllocatorsEEPROM();
// Put only the most perfromance relevant ones into RAM
ALLOCATOR_TYPE_ARRAY[BNH_helper_BN_array1] = JCSystem.MEMORY_TYPE_TRANSIENT_RESET;
ALLOCATOR_TYPE_ARRAY[BNH_helper_BN_array2] = JCSystem.MEMORY_TYPE_TRANSIENT_RESET;
ALLOCATOR_TYPE_ARRAY[BNH_helper_BN_A] = JCSystem.MEMORY_TYPE_TRANSIENT_RESET;
ALLOCATOR_TYPE_ARRAY[BNH_helper_BN_B] = JCSystem.MEMORY_TYPE_TRANSIENT_RESET;
ALLOCATOR_TYPE_ARRAY[BNH_helper_BN_C] = JCSystem.MEMORY_TYPE_TRANSIENT_RESET;
ALLOCATOR_TYPE_ARRAY[BNH_helper_BN_D] = JCSystem.MEMORY_TYPE_TRANSIENT_RESET;
ALLOCATOR_TYPE_ARRAY[BNH_helper_BN_E] = JCSystem.MEMORY_TYPE_TRANSIENT_RESET;
ALLOCATOR_TYPE_ARRAY[BNH_helper_BN_F] = JCSystem.MEMORY_TYPE_TRANSIENT_RESET;
ALLOCATOR_TYPE_ARRAY[ECPH_helperEC_BN_B] = JCSystem.MEMORY_TYPE_TRANSIENT_RESET;
ALLOCATOR_TYPE_ARRAY[ECPH_helperEC_BN_C] = JCSystem.MEMORY_TYPE_TRANSIENT_RESET;
ALLOCATOR_TYPE_ARRAY[ECPH_uncompressed_point_arr1] = JCSystem.MEMORY_TYPE_TRANSIENT_RESET;
}
/**
* Allocates new byte[] array with provided length either in RAM or EEPROM based on an allocator type.
* Method updates internal counters of bytes allocated with specific allocator. Use {@code getAllocatedInRAM()}
* or {@code getAllocatedInEEPROM} for counters readout.
* @param length length of array
* @param allocatorType type of allocator
* @return allocated array
*/
public byte[] allocateByteArray(short length, byte allocatorType) {
switch (allocatorType) {
case JCSystem.MEMORY_TYPE_PERSISTENT:
allocatedInEEPROM += length;
return new byte[length];
case JCSystem.MEMORY_TYPE_TRANSIENT_RESET:
allocatedInRAM += length;
return JCSystem.makeTransientByteArray(length, JCSystem.CLEAR_ON_RESET);
case JCSystem.MEMORY_TYPE_TRANSIENT_DESELECT:
allocatedInRAM += length;
return JCSystem.makeTransientByteArray(length, JCSystem.CLEAR_ON_DESELECT);
}
return null;
}
/**
* Returns pre-set allocator type for provided object identified by unique objectAllocatorID
* @param objectAllocatorID unique id of target object
* @return allocator type
*/
public byte getAllocatorType(short objectAllocatorID) {
if (objectAllocatorID >= 0 && objectAllocatorID <= (short) ALLOCATOR_TYPE_ARRAY.length) {
return ALLOCATOR_TYPE_ARRAY[objectAllocatorID];
} else {
ISOException.throwIt(ReturnCodes.SW_ALLOCATOR_INVALIDOBJID);
return -1;
}
}
/**
* Returns number of bytes allocated in RAM via {@code allocateByteArray()} since last reset of counters.
* @return number of bytes allocated in RAM via this control object
*/
public short getAllocatedInRAM() {
return allocatedInRAM;
}
/**
* Returns number of bytes allocated in EEPROM via {@code allocateByteArray()}
* since last reset of counters.
*
* @return number of bytes allocated in EEPROM via this control object
*/
public short getAllocatedInEEPROM() {
return allocatedInEEPROM;
}
/**
* Resets counters of allocated bytes in RAM and EEPROM
*/
public final void resetAllocatorCounters() {
allocatedInRAM = 0;
allocatedInEEPROM = 0;
}
}

View File

@ -0,0 +1,18 @@
package opencrypto.jcmathlib;
/**
*
* @author Vasilios Mavroudis and Petr Svenda
*/
public class ReturnCodes {
// Custom error response codes
public static final short SW_BIGNAT_RESIZETOLONGER = (short) 0x7000;
public static final short SW_BIGNAT_REALLOCATIONNOTALLOWED = (short) 0x7001;
public static final short SW_BIGNAT_MODULOTOOLARGE = (short) 0x7002;
public static final short SW_BIGNAT_INVALIDCOPYOTHER = (short) 0x7003;
public static final short SW_BIGNAT_INVALIDRESIZE = (short) 0x7004;
public static final short SW_LOCK_ALREADYLOCKED = (short) 0x7005;
public static final short SW_LOCK_NOTLOCKED = (short) 0x7006;
public static final short SW_ECPOINT_INVALIDLENGTH = (short) 0x700a;
public static final short SW_ALLOCATOR_INVALIDOBJID = (short) 0x700c;
}