specify assisted key derivation
This commit is contained in:
parent
dc2af3b4fb
commit
3a32731061
|
@ -26,9 +26,10 @@ The PIN can be changed by the user after authentication.
|
|||
|
||||
## Keys & Signature
|
||||
|
||||
The application allows loading a replacing of a single EC keyset, defined on the SECP256k1 curve. This keyset is used
|
||||
to sign transactions. When the applet is first installed, no keyset is available so signing will fail. It is necessary
|
||||
to first load the keyset in order for the application to be fully operational.
|
||||
The application allows loading a replacing of a single EC keyset, defined on the SECP256k1 curve. The keyset can contain
|
||||
a 32-byte chain code to further derive keys according to the [BIP32](https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki)
|
||||
specifications. This keyset is used to sign transactions. When the applet is first installed, no keyset is available so
|
||||
signing will fail. It is necessary to first load the keyset in order for the application to be fully operational.
|
||||
|
||||
Signing of transactions is done by uploading the data in blocks no larger than 255 bytes (including the overhead caused
|
||||
by the Secure Channel). Segmentation must be handled at the application protocol.
|
||||
|
@ -69,7 +70,7 @@ Response Data format:
|
|||
- Tag 0xC0 = PIN retry count (1 byte)
|
||||
- Tag 0xC1 = PUK retry count (1 byte)
|
||||
- Tag 0xC2 = 0 if key is not initialized, 1 otherwise
|
||||
- Tag 0xC3 = 1 if public key derivation is hw optimized, 0 otherwise
|
||||
- Tag 0xC3 = 1 if public key derivation is supported, 0 otherwise
|
||||
|
||||
### VERIFY PIN
|
||||
|
||||
|
@ -125,7 +126,8 @@ always returns 0x63C0, even if the PUK is inserted correctly. In this case the w
|
|||
* P1 = key type
|
||||
* P2 = 0x00
|
||||
* Data = the key data
|
||||
* Response SW = 0x9000 on success, 0x6A80 if the format is invalid, 0x6A86 if P1 is invalid
|
||||
* Response SW = 0x9000 on success, 0x6A80 if the format is invalid, 0x6A86 if P1 is invalid, 0x6A81 if public key
|
||||
derivation is not supported and the public key is omitted
|
||||
* Preconditions: Secure Channel must be opened, user PIN must be verified
|
||||
|
||||
P1:
|
||||
|
@ -137,12 +139,13 @@ Data:
|
|||
|
||||
If P1 is 0x01 or 0x02
|
||||
- Tag 0xA1 = keypair template
|
||||
- Tag 0x80 = ECC public key component (can be omitted)
|
||||
- Tag 0x80 = ECC public key component (can be omitted if public key derivation is supported)
|
||||
- Tag 0x81 = ECC private key component
|
||||
- Tag 0x82 = chain code (if P1=0x02)
|
||||
|
||||
If P1 is 0x03 a 64 byte sequence generated according to the BIP39 specifications is expected. The master key will be
|
||||
generated according to the [BIP32](https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki) specifications.
|
||||
generated according to the [BIP32](https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki) specifications. This
|
||||
is only supported if the hardware supports public key derivation.
|
||||
|
||||
This command is used to load or replace the keypair used for signing on the card. This command always aborts open
|
||||
signing sessions, if any. Unless a DERIVE KEY is sent, a subsequent SIGN command will use this keypair for signature.
|
||||
|
@ -151,14 +154,37 @@ signing sessions, if any. Unless a DERIVE KEY is sent, a subsequent SIGN command
|
|||
|
||||
* CLA = 0x80
|
||||
* INS = 0xD1
|
||||
* P1 = 0x00
|
||||
* P2 = 0x00
|
||||
* Data = a sequence of 32-bit integers (most significant byte first). Empty if the master key must be used.
|
||||
* Response SW = 0x9000 on success, 0x6A80 if the format is invalid
|
||||
* P1 = derivation options
|
||||
* P2 = assisted derivation phase
|
||||
* Data = a sequence of 32-bit integers (most significant byte first). Empty if the master key must be used. On assisted
|
||||
derivation contains a public key when P2 = 0x02.
|
||||
* Response SW = 0x9000 on success, 0x6A80 if the format is invalid, 0x6A81 if public key derivation is not supported and
|
||||
bit 0 of P1 is set, 0x6A86 if P2 = 0x01 and bit 0 of P1 is not set.
|
||||
* Response Data = On assisted derivation and P2 = 0x01 the key derivation template. Empty otherwise.
|
||||
* Preconditions: Secure Channel must be opened, user PIN must be verified, an extended keyset must be loaded
|
||||
|
||||
This command is used before a signing session to generate a private key according to the [BIP32](https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki)
|
||||
specifications. The generated key is used for all subsequent SIGN sessions.
|
||||
specifications. This command always aborts open signing sessions, if any. The generated key is used for all subsequent
|
||||
SIGN sessions. Because JavaCard does not offer native EC point multiplication before version 3.0.5, there is an
|
||||
alternative mode of operation where the public key is partially derived off-card and loaded back on card. In this mode
|
||||
of operation only 1 derivation step at the time can be completed and as such during assisted derivation the data can
|
||||
contain a single 32-bit integer, instead of a sequence.
|
||||
|
||||
P1:
|
||||
* bit 0 = if 0 derive autonomously (only works if public key derivation is supported), if 1 do assisted derivation
|
||||
* bit 1-6 = reserved
|
||||
* bit 7 = if 0 derive from master keys, if 1 derive from current keys
|
||||
|
||||
P2:
|
||||
* 0x00 = data is 32 a sequence of 32-bit integers
|
||||
* 0x01 = data is a public key
|
||||
|
||||
Response Data format:
|
||||
- Tag 0xA3 = key derivation template
|
||||
- Tag 0x83 = ECC Public Key X component
|
||||
- Tag 0x30 = ECDSA Signature
|
||||
- Tag 0x02 = R value
|
||||
- Tag 0x02 = S value
|
||||
|
||||
### GENERATE MNEMONIC
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,229 +0,0 @@
|
|||
package im.status.wallet;
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,142 +0,0 @@
|
|||
package im.status.wallet;
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
|
@ -1,195 +0,0 @@
|
|||
package im.status.wallet;
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
|
@ -1,395 +0,0 @@
|
|||
package im.status.wallet;
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
|
@ -1,133 +0,0 @@
|
|||
package im.status.wallet;
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -1,142 +0,0 @@
|
|||
package im.status.wallet;
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
|
@ -1,18 +0,0 @@
|
|||
package im.status.wallet;
|
||||
|
||||
/**
|
||||
*
|
||||
* @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;
|
||||
}
|
|
@ -1,5 +1,7 @@
|
|||
package im.status.wallet;
|
||||
|
||||
import javacard.framework.ISO7816;
|
||||
import javacard.framework.ISOException;
|
||||
import javacard.security.CryptoException;
|
||||
import javacard.security.ECKey;
|
||||
import javacard.security.ECPrivateKey;
|
||||
|
@ -47,19 +49,16 @@ public class SECP256k1 {
|
|||
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;
|
||||
private static KeyAgreement ecPointMultiplierX;
|
||||
|
||||
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);
|
||||
ecPointMultiplier = null;
|
||||
}
|
||||
|
||||
ecPointMultiplierX = KeyAgreement.getInstance(KeyAgreement.ALG_EC_SVDP_DH_PLAIN, false);
|
||||
}
|
||||
|
||||
static void setCurveParameters(ECKey key) {
|
||||
|
@ -75,23 +74,28 @@ public class SECP256k1 {
|
|||
return multiplyPoint(privateKey, SECP256K1_G, (short) 0, (short) SECP256K1_G.length, pubOut, pubOff);
|
||||
}
|
||||
|
||||
/*
|
||||
* 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) {
|
||||
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);
|
||||
}
|
||||
static short derivePublicX(ECPrivateKey privateKey, byte[] xOut, short xOff) {
|
||||
return multiplyX(privateKey, SECP256K1_G, (short) 0, (short) SECP256K1_G.length, xOut, xOff);
|
||||
}
|
||||
|
||||
static boolean hasFastECPointMultiplication() {
|
||||
static short multiplyPoint(ECPrivateKey privateKey, byte[] point, short pointOff, short pointLen, byte[] out, short outOff) {
|
||||
assetECPointMultiplicationSupport();
|
||||
ecPointMultiplier.init(privateKey);
|
||||
return ecPointMultiplier.generateSecret(point, pointOff, pointLen, out, outOff);
|
||||
}
|
||||
|
||||
static short multiplyX(ECPrivateKey privateKey, byte[] point, short pointOff, short pointLen, byte[] out, short outOff) {
|
||||
ecPointMultiplierX.init(privateKey);
|
||||
return ecPointMultiplierX.generateSecret(point, pointOff, pointLen, out, outOff);
|
||||
}
|
||||
|
||||
static boolean hasECPointMultiplication() {
|
||||
return ecPointMultiplier != null;
|
||||
}
|
||||
|
||||
static void assetECPointMultiplicationSupport() {
|
||||
if(!hasECPointMultiplication()) {
|
||||
ISOException.throwIt(ISO7816.SW_FUNC_NOT_SUPPORTED);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -47,7 +47,7 @@ public class WalletApplet extends Applet {
|
|||
static final byte TLV_PIN_RETRY_COUNT = (byte) 0xC0;
|
||||
static final byte TLV_PUK_RETRY_COUNT = (byte) 0xC1;
|
||||
static final byte TLV_KEY_INITIALIZATION_STATUS = (byte) 0xC2;
|
||||
static final byte TLV_FAST_PUBLIC_KEY_DERIVATION = (byte) 0xC3;
|
||||
static final byte TLV_PUBLIC_KEY_DERIVATION = (byte) 0xC3;
|
||||
|
||||
private OwnerPIN pin;
|
||||
private OwnerPIN puk;
|
||||
|
@ -176,9 +176,9 @@ public class WalletApplet extends Applet {
|
|||
apduBuffer[off++] = TLV_KEY_INITIALIZATION_STATUS;
|
||||
apduBuffer[off++] = 1;
|
||||
apduBuffer[off++] = privateKey.isInitialized() ? (byte) 0x01 : (byte) 0x00;
|
||||
apduBuffer[off++] = TLV_FAST_PUBLIC_KEY_DERIVATION;
|
||||
apduBuffer[off++] = TLV_PUBLIC_KEY_DERIVATION;
|
||||
apduBuffer[off++] = 1;
|
||||
apduBuffer[off++] = SECP256k1.hasFastECPointMultiplication() ? (byte) 0x01 : (byte) 0x00;
|
||||
apduBuffer[off++] = SECP256k1.hasECPointMultiplication() ? (byte) 0x01 : (byte) 0x00;
|
||||
|
||||
short len = secureChannel.encryptAPDU(apduBuffer, (short) (off - SecureChannel.SC_OUT_OFFSET));
|
||||
apdu.setOutgoingAndSend(ISO7816.OFFSET_CDATA, len);
|
||||
|
@ -276,6 +276,7 @@ public class WalletApplet extends Applet {
|
|||
short chainOffset = (short)(privOffset + apduBuffer[(short)(privOffset + 1)] + 2);
|
||||
|
||||
if (apduBuffer[pubOffset] != TLV_PUB_KEY) {
|
||||
SECP256k1.assetECPointMultiplicationSupport();
|
||||
chainOffset = privOffset;
|
||||
privOffset = pubOffset;
|
||||
pubOffset = -1;
|
||||
|
@ -322,6 +323,8 @@ public class WalletApplet extends Applet {
|
|||
}
|
||||
|
||||
private void loadSeed(byte[] apduBuffer) {
|
||||
SECP256k1.assetECPointMultiplicationSupport();
|
||||
|
||||
if (apduBuffer[ISO7816.OFFSET_LC] != SEED_SIZE) {
|
||||
ISOException.throwIt(ISO7816.SW_WRONG_DATA);
|
||||
}
|
||||
|
@ -343,6 +346,8 @@ public class WalletApplet extends Applet {
|
|||
}
|
||||
|
||||
private void deriveKey(APDU apdu) {
|
||||
SECP256k1.assetECPointMultiplicationSupport();
|
||||
|
||||
apdu.setIncomingAndReceive();
|
||||
|
||||
if (!(secureChannel.isOpen() && pin.isValidated() && isExtended)) {
|
||||
|
@ -359,6 +364,7 @@ public class WalletApplet extends Applet {
|
|||
|
||||
short chainEnd = (short) (ISO7816.OFFSET_CDATA + len);
|
||||
resetKeys(apduBuffer, chainEnd);
|
||||
signInProgress = false;
|
||||
|
||||
for (short i = ISO7816.OFFSET_CDATA; i < chainEnd; i += 4) {
|
||||
Crypto.bip32CKDPriv(apduBuffer, i, privateKey, publicKey, chainCode, (short) 0);
|
||||
|
|
|
@ -120,7 +120,7 @@ public class WalletAppletTest {
|
|||
cmdSet.openSecureChannel();
|
||||
|
||||
// Good case. Since the order of test execution is undefined, the test cannot know if the keys are initialized or not.
|
||||
// Additionally, support for fast public key derivation is hw dependent.
|
||||
// Additionally, support for public key derivation is hw dependent.
|
||||
response = cmdSet.getStatus();
|
||||
assertEquals(0x9000, response.getSW());
|
||||
byte[] data = secureChannel.decryptAPDU(response.getData());
|
||||
|
|
Loading…
Reference in New Issue