implement low-level BIP32 CKDPriv function

This commit is contained in:
Michele Balistreri 2017-10-18 11:40:20 +03:00
parent 4c0c58ac90
commit de91e33f74
4 changed files with 157 additions and 13 deletions

View File

@ -1,15 +1,159 @@
package im.status.wallet;
import javacard.security.MessageDigest;
import javacard.security.RandomData;
import javacard.framework.JCSystem;
import javacard.framework.Util;
import javacard.security.*;
public class Crypto {
final static private short KEY_SECRET_SIZE = 32;
final static private short KEY_DERIVATION_INPUT_SIZE = 37;
final static private short HMAC_OUT_SIZE = 64;
private static final byte HMAC_IPAD = (byte) 0x36;
private static final byte HMAC_OPAD = (byte) 0x5c;
private static final short HMAC_BLOCK_SIZE = (short) 128;
private static final short HMAC_BLOCK_OFFSET = (short) KEY_DERIVATION_INPUT_SIZE + HMAC_OUT_SIZE;
static RandomData random;
static MessageDigest sha256;
private static MessageDigest sha512;
private static Signature hmacSHA512;
private static HMACKey hmacKey;
private static byte[] tmp;
static void init() {
random = RandomData.getInstance(RandomData.ALG_SECURE_RANDOM);
sha256 = MessageDigest.getInstance(MessageDigest.ALG_SHA_256, false);
short blockSize;
try {
blockSize = 0;
hmacSHA512 = Signature.getInstance(Signature.ALG_HMAC_SHA_512, false);
hmacKey = (HMACKey) KeyBuilder.buildKey(KeyBuilder.TYPE_HMAC_TRANSIENT_DESELECT, KEY_SECRET_SIZE, false);
} catch (CryptoException e) {
sha512 = MessageDigest.getInstance(MessageDigest.ALG_SHA_512, false);
blockSize = HMAC_BLOCK_SIZE;
}
tmp = JCSystem.makeTransientByteArray((short) (HMAC_BLOCK_OFFSET + blockSize), JCSystem.CLEAR_ON_RESET);
}
static boolean bip32CKDPriv(byte[] i, short iOff, ECPrivateKey privateKey, ECPublicKey publicKey, byte[] chain, short chainOff) {
short off = 0;
if ((i[iOff] & (byte) 0x80) == (byte) 0x80) {
tmp[off++] = 0;
off += privateKey.getS(tmp, off);
} else {
off = (short) (publicKey.getW(tmp, (short) 0) - 1);
tmp[0] = ((tmp[(short) off] & 1) != 0 ? (byte) 0x03 : (byte) 0x02);
off = (short) ((short) (off / 2) + 1);
}
off = Util.arrayCopyNonAtomic(i, iOff, tmp, off, (short) 4);
hmacSHA512(chain, chainOff, KEY_SECRET_SIZE, tmp, (short) 0, off, tmp, off);
if (ucmp256(tmp, off, SECP256k1.SECP256K1_R, (short) 0) >= 0) {
return false;
}
privateKey.getS(tmp, (short) 0);
addm256(tmp, off, tmp, (short) 0, SECP256k1.SECP256K1_R, (short)0, tmp, off);
if (isZero256(tmp, off)) {
return false;
}
JCSystem.beginTransaction();
privateKey.setS(tmp, off, (short) KEY_SECRET_SIZE);
Util.arrayCopy(tmp, (short)(off + KEY_SECRET_SIZE), chain, chainOff, (short) KEY_SECRET_SIZE);
JCSystem.commitTransaction();
return true;
}
private static void hmacSHA512(byte[] key, short keyOff, short keyLen, byte[] in, short inOff, short inLen, byte[] out, short outOff) {
if (hmacSHA512 != null) {
hmacKey.setKey(key, keyOff, keyLen);
hmacSHA512.init(hmacKey, Signature.MODE_SIGN);
hmacSHA512.sign(in, inOff, inLen, out, outOff);
} else {
for (byte i = 0; i < 2; i++) {
Util.arrayFillNonAtomic(tmp, HMAC_BLOCK_OFFSET, HMAC_BLOCK_SIZE, (i == 0 ? HMAC_IPAD : HMAC_OPAD));
for (short j = 0; j < keyLen; j++) {
tmp[(short)(HMAC_BLOCK_OFFSET + j)] ^= key[(short)(keyOff + j)];
}
sha512.update(tmp, HMAC_BLOCK_OFFSET, HMAC_BLOCK_SIZE);
if (i == 0) {
Crypto.sha512.doFinal(in, inOff, inLen, out, outOff);
} else {
Crypto.sha512.doFinal(out, outOff, HMAC_OUT_SIZE, out, outOff);
}
}
}
}
private static void addm256(byte[] a, short aOff, byte[] b, short bOff, byte[] n, short nOff, byte[] out, short outOff) {
if ((add256(a, aOff, b, bOff, out, outOff) != 0) || (ucmp256(out, outOff, n, nOff) > 0)) {
sub256(out, outOff, n, nOff, out, outOff);
}
}
private static short ucmp256(byte[] a, short aOff, byte[] b, short bOff) {
short ai, bi;
for (short i = 0 ; i < 32; i++) {
ai = (short)(a[(short)(aOff + i)] & 0x00ff);
bi = (short)(b[(short)(bOff + i)] & 0x00ff);
if (ai != bi) {
return (short)(ai - bi);
}
}
return 0;
}
private static boolean isZero256(byte[] a, short aOff) {
boolean isZero = true;
for (short i = 0; i < (byte) 32; i++) {
if (a[(short)(aOff + i)] != 0) {
isZero = false;
break;
}
}
return isZero;
}
private static short add256(byte[] a, short aOff, byte[] b, short bOff, byte[] out, short outOff) {
short outI = 0;
for (short i = 31 ; i >= 0 ; i--) {
outI = (short) ((short)(a[(short)(aOff + i)] & 0x00FF) + (short)(b[(short)(bOff + i)] & 0xFF) + outI);
out[(short)(outOff + i)] = (byte)outI ;
outI = (short)(outI >> 8);
}
return outI;
}
private static short sub256(byte[] a, short aOff, byte[] b, short bOff, byte[] out, short outOff) {
short outI = 0;
for (short i = 31 ; i >= 0 ; i--) {
outI = (short) ((short)(a[(short)(aOff + i)] & 0xFF) - (short)(b[(short)(bOff + i)] & 0xFF) - outI);
out[(short)(outOff + i)] = (byte)outI ;
outI = (short)(((outI >> 8) != 0) ? 1 : 0);
}
return outI;
}
}

View File

@ -5,7 +5,7 @@ import javacard.security.ECKey;
import javacard.security.ECPrivateKey;
import javacard.security.KeyAgreement;
public class SEC256k1 {
public class SECP256k1 {
static final byte SECP256K1_FP[] = {
(byte)0xFF,(byte)0xFF,(byte)0xFF,(byte)0xFF,(byte)0xFF,(byte)0xFF,(byte)0xFF,(byte)0xFF,
(byte)0xFF,(byte)0xFF,(byte)0xFF,(byte)0xFF,(byte)0xFF,(byte)0xFF,(byte)0xFF,(byte)0xFF,

View File

@ -26,8 +26,8 @@ public class SecureChannel {
scKey = (AESKey) KeyBuilder.buildKey(KeyBuilder.TYPE_AES_TRANSIENT_DESELECT, KeyBuilder.LENGTH_AES_256, false);
scKeypair = new KeyPair(KeyPair.ALG_EC_FP, SC_KEY_LENGTH);
SEC256k1.setCurveParameters((ECKey) scKeypair.getPrivate());
SEC256k1.setCurveParameters((ECKey) scKeypair.getPublic());
SECP256k1.setCurveParameters((ECKey) scKeypair.getPrivate());
SECP256k1.setCurveParameters((ECKey) scKeypair.getPublic());
scKeypair.genKeyPair();
scAgreement = KeyAgreement.getInstance(KeyAgreement.ALG_EC_SVDP_DH, false);

View File

@ -70,7 +70,7 @@ public class WalletApplet extends Applet {
}
public WalletApplet(byte[] bArray, short bOffset, byte bLength) {
SEC256k1.init();
SECP256k1.init();
Crypto.init();
short c9Off = (short)(bOffset + bArray[bOffset] + 1); // Skip AID
@ -94,11 +94,11 @@ public class WalletApplet extends Applet {
publicKey = (ECPublicKey) KeyBuilder.buildKey(KeyBuilder.TYPE_EC_FP_PUBLIC, EC_KEY_SIZE, false);
privateKey = (ECPrivateKey) KeyBuilder.buildKey(KeyBuilder.TYPE_EC_FP_PRIVATE, EC_KEY_SIZE, false);
SEC256k1.setCurveParameters(masterPublic);
SEC256k1.setCurveParameters(masterPrivate);
SECP256k1.setCurveParameters(masterPublic);
SECP256k1.setCurveParameters(masterPrivate);
SEC256k1.setCurveParameters(publicKey);
SEC256k1.setCurveParameters(privateKey);
SECP256k1.setCurveParameters(publicKey);
SECP256k1.setCurveParameters(privateKey);
signature = Signature.getInstance(Signature.ALG_ECDSA_SHA_256, false);
@ -178,7 +178,7 @@ public class WalletApplet extends Applet {
apduBuffer[off++] = privateKey.isInitialized() ? (byte) 0x01 : (byte) 0x00;
apduBuffer[off++] = TLV_FAST_PUBLIC_KEY_DERIVATION;
apduBuffer[off++] = 1;
apduBuffer[off++] = SEC256k1.hasFastECPointMultiplication() ? (byte) 0x01 : (byte) 0x00;
apduBuffer[off++] = SECP256k1.hasFastECPointMultiplication() ? (byte) 0x01 : (byte) 0x00;
short len = secureChannel.encryptAPDU(apduBuffer, (short) (off - SecureChannel.SC_OUT_OFFSET));
apdu.setOutgoingAndSend(ISO7816.OFFSET_CDATA, len);
@ -309,7 +309,7 @@ public class WalletApplet extends Applet {
pubOffset = (short) (pubOffset + 2);
} else {
pubOffset = 0;
pubLen = SEC256k1.derivePublicKey(masterPrivate, apduBuffer, pubOffset);
pubLen = SECP256k1.derivePublicKey(masterPrivate, apduBuffer, pubOffset);
}
masterPublic.setW(apduBuffer, pubOffset, pubLen);
@ -334,7 +334,7 @@ public class WalletApplet extends Applet {
Util.arrayCopy(apduBuffer, (short) (ISO7816.OFFSET_CDATA + CHAIN_CODE_SIZE), masterChainCode, (short) 0, CHAIN_CODE_SIZE);
Util.arrayCopy(apduBuffer, (short) (ISO7816.OFFSET_CDATA + CHAIN_CODE_SIZE), chainCode, (short) 0, CHAIN_CODE_SIZE);
short pubLen = SEC256k1.derivePublicKey(masterPrivate, apduBuffer, (short) 0);
short pubLen = SECP256k1.derivePublicKey(masterPrivate, apduBuffer, (short) 0);
masterPublic.setW(apduBuffer, (short) 0, pubLen);
publicKey.setW(apduBuffer, (short) 0, pubLen);