diff --git a/src/main/java/im/status/wallet/Crypto.java b/src/main/java/im/status/wallet/Crypto.java index ff15820..405bcee 100644 --- a/src/main/java/im/status/wallet/Crypto.java +++ b/src/main/java/im/status/wallet/Crypto.java @@ -12,8 +12,9 @@ import javacardx.crypto.Cipher; public class Crypto { final static public short AES_BLOCK_SIZE = 16; - final static private short KEY_SECRET_SIZE = 32; - final static private short KEY_DERIVATION_INPUT_SIZE = 37; + final static short KEY_SECRET_SIZE = 32; + final static short KEY_PUB_SIZE = 65; + final static short KEY_DERIVATION_SCRATCH_SIZE = 37; final static private short HMAC_OUT_SIZE = 64; final static private byte[] MAX_S = { (byte) 0x7F, (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) 0x5D, (byte) 0x57, (byte) 0x6E, (byte) 0x73, (byte) 0x57, (byte) 0xA4, (byte) 0x50, (byte) 0x1D, (byte) 0xDF, (byte) 0xE9, (byte) 0x2F, (byte) 0x46, (byte) 0x68, (byte) 0x1B, (byte) 0x20, (byte) 0xA0 }; @@ -22,7 +23,6 @@ public class Crypto { final static private byte HMAC_IPAD = (byte) 0x36; final static private byte HMAC_OPAD = (byte) 0x5c; final static private short HMAC_BLOCK_SIZE = (short) 128; - final static private short HMAC_BLOCK_OFFSET = (short) KEY_DERIVATION_INPUT_SIZE + HMAC_OUT_SIZE; final static private byte[] KEY_BITCOIN_SEED = {'B', 'i', 't', 'c', 'o', 'i', 'n', ' ', 's', 'e', 'e', 'd'}; @@ -38,7 +38,7 @@ public class Crypto { private AESKey tmpAES256; - private byte[] tmp; + private byte[] hmacBlock; Crypto() { random = RandomData.getInstance(RandomData.ALG_SECURE_RANDOM); @@ -49,18 +49,14 @@ public class Crypto { tmpAES256 = (AESKey) KeyBuilder.buildKey(KeyBuilder.TYPE_AES_TRANSIENT_DESELECT, KeyBuilder.LENGTH_AES_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) { hmacSHA512 = null; - blockSize = HMAC_BLOCK_SIZE; + hmacBlock = JCSystem.makeTransientByteArray(HMAC_BLOCK_SIZE, JCSystem.CLEAR_ON_RESET); } - tmp = JCSystem.makeTransientByteArray((short) (HMAC_BLOCK_OFFSET + blockSize), JCSystem.CLEAR_ON_RESET); } public short oneShotAES(byte mode, byte[] src, short sOff, short sLen, byte[] dst, short dOff, byte[] key, short keyOff) { @@ -69,6 +65,10 @@ public class Crypto { return aesCbcIso9797m2.doFinal(src, (short) (sOff + AES_BLOCK_SIZE), sLen, dst, dOff); } + boolean bip32IsHardened(byte[] i, short iOff) { + return (i[iOff] & (byte) 0x80) == (byte) 0x80; + } + /** * Derives a private key according to the algorithm defined in BIP32. The BIP32 specifications define some checks * to be performed on the derived keys. In the very unlikely event that these checks fail this key is not considered @@ -76,43 +76,33 @@ public class Crypto { * * @param i the buffer containing the key path element (a 32-bit big endian integer) * @param iOff the offset in the buffer - * @param privateKey the parent private key - * @param publicKey the parent public key - * @param chain the chain code - * @param chainOff the offset in the chain code buffer * @return true if successful, false otherwise */ - boolean bip32CKDPriv(byte[] i, short iOff, ECPrivateKey privateKey, ECPublicKey publicKey, byte[] chain, short chainOff) { - short off = 0; + boolean bip32CKDPriv(byte[] i, short iOff, byte[] scratch, short scratchOff, byte[] data, short dataOff, byte[] output, short outOff) { + short off = scratchOff; - if ((i[iOff] & (byte) 0x80) == (byte) 0x80) { - tmp[off++] = 0; - off += privateKey.getS(tmp, off); + if (bip32IsHardened(i, iOff)) { + scratch[off++] = 0; + off = Util.arrayCopyNonAtomic(data, dataOff, scratch, off, KEY_SECRET_SIZE); } else { - off = (short) (publicKey.getW(tmp, (short) 0) - 1); - tmp[0] = ((tmp[off] & 1) != 0 ? (byte) 0x03 : (byte) 0x02); - off = (short) ((short) (off / 2) + 1); + scratch[off++] = ((data[(short) (dataOff + KEY_SECRET_SIZE + KEY_SECRET_SIZE + KEY_PUB_SIZE - 1)] & 1) != 0 ? (byte) 0x03 : (byte) 0x02); + off = Util.arrayCopyNonAtomic(data, (short) (dataOff + KEY_SECRET_SIZE + KEY_SECRET_SIZE + 1), scratch, off, KEY_SECRET_SIZE); } - off = Util.arrayCopyNonAtomic(i, iOff, tmp, off, (short) 4); + off = Util.arrayCopyNonAtomic(i, iOff, hmacBlock, off, (short) 4); - hmacSHA512(chain, chainOff, KEY_SECRET_SIZE, tmp, (short) 0, off, tmp, off); + hmacSHA512(data, (short)(dataOff + KEY_SECRET_SIZE), KEY_SECRET_SIZE, scratch, scratchOff, off, output, outOff); - if (ucmp256(tmp, off, SECP256k1.SECP256K1_R, (short) 0) >= 0) { + if (ucmp256(output, outOff, SECP256k1.SECP256K1_R, (short) 0) >= 0) { return false; } - privateKey.getS(tmp, (short) 0); + addm256(output, outOff, data, dataOff, SECP256k1.SECP256K1_R, (short) 0, output, outOff); - addm256(tmp, off, tmp, (short) 0, SECP256k1.SECP256K1_R, (short) 0, tmp, off); - - if (isZero256(tmp, off)) { + if (isZero256(output, outOff)) { return false; } - privateKey.setS(tmp, off, (short) KEY_SECRET_SIZE); - Util.arrayCopy(tmp, (short)(off + KEY_SECRET_SIZE), chain, chainOff, (short) KEY_SECRET_SIZE); - return true; } @@ -177,13 +167,13 @@ public class Crypto { 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)); + Util.arrayFillNonAtomic(hmacBlock, (short) 0, 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)]; + hmacBlock[j] ^= key[(short)(keyOff + j)]; } - sha512.update(tmp, HMAC_BLOCK_OFFSET, HMAC_BLOCK_SIZE); + sha512.update(hmacBlock, (short) 0, HMAC_BLOCK_SIZE); if (i == 0) { sha512.doFinal(in, inOff, inLen, out, outOff); diff --git a/src/main/java/im/status/wallet/SECP256k1.java b/src/main/java/im/status/wallet/SECP256k1.java index 8fe7ffb..ccae43c 100644 --- a/src/main/java/im/status/wallet/SECP256k1.java +++ b/src/main/java/im/status/wallet/SECP256k1.java @@ -3,6 +3,7 @@ package im.status.wallet; import javacard.security.ECKey; import javacard.security.ECPrivateKey; import javacard.security.KeyAgreement; +import javacard.security.KeyBuilder; /** * Utility methods to work with the SECP256k1 curve. This class is not meant to be instantiated, but its init method @@ -47,17 +48,22 @@ public class SECP256k1 { static final byte SECP256K1_K = (byte)0x01; + static final short SECP256K1_KEY_SIZE = 256; + private static final byte ALG_EC_SVDP_DH_PLAIN_XY = 6; // constant from JavaCard 3.0.5 + private KeyAgreement ecPointMultiplier; private Crypto crypto; + private ECPrivateKey tmpECPrivateKey; /** * Allocates objects needed by this class. Must be invoked during the applet installation exactly 1 time. */ SECP256k1(Crypto crypto) { this.crypto = crypto; - ecPointMultiplier = KeyAgreement.getInstance(ALG_EC_SVDP_DH_PLAIN_XY, false); + this.ecPointMultiplier = KeyAgreement.getInstance(ALG_EC_SVDP_DH_PLAIN_XY, false); + this.tmpECPrivateKey = (ECPrivateKey) KeyBuilder.buildKey(KeyBuilder.TYPE_EC_FP_PRIVATE, SECP256K1_KEY_SIZE, false); } /** @@ -87,6 +93,24 @@ public class SECP256k1 { return multiplyPoint(privateKey, SECP256K1_G, (short) 0, (short) SECP256K1_G.length, pubOut, pubOff); } + + /** + * Derives the public key from the given private key and outputs it in the pubOut buffer. This is done by multiplying + * the private key by the G point of the curve. + * + * @param privateKey the private key + * @param pubOut the output buffer for the public key + * @param pubOff the offset in pubOut + * @return the length of the public key + */ + short derivePublicKey(byte[] privateKey, short privOff, byte[] pubOut, short pubOff) { + tmpECPrivateKey.setS(privateKey, privOff, (short)(SECP256K1_KEY_SIZE/8)); + short res = derivePublicKey(tmpECPrivateKey, pubOut, pubOff); + // Unfortunately our current card does not support EC transient keys + tmpECPrivateKey.clearKey(); + return res; + } + /** * Multiplies a scalar in the form of a private key by the given point. Internally uses a special version of EC-DH * supported since JavaCard 3.0.5 which outputs both X and Y in their uncompressed form. diff --git a/src/main/java/im/status/wallet/WalletApplet.java b/src/main/java/im/status/wallet/WalletApplet.java index 2c18853..a81ad5e 100644 --- a/src/main/java/im/status/wallet/WalletApplet.java +++ b/src/main/java/im/status/wallet/WalletApplet.java @@ -34,7 +34,6 @@ public class WalletApplet extends Applet { static final byte PAIRING_MAX_CLIENT_COUNT = 5; static final byte UID_LENGTH = 16; - static final short EC_KEY_SIZE = 256; static final short CHAIN_CODE_SIZE = 32; static final short KEY_UID_LENGTH = 32; static final short BIP39_SEED_SIZE = CHAIN_CODE_SIZE * 2; @@ -100,7 +99,6 @@ public class WalletApplet extends Applet { private ECPublicKey parentPublicKey; private ECPrivateKey parentPrivateKey; private byte[] parentChainCode; - private boolean parentValid; private ECPublicKey publicKey; private ECPrivateKey privateKey; @@ -152,14 +150,14 @@ public class WalletApplet extends Applet { uid = new byte[UID_LENGTH]; crypto.random.generateData(uid, (short) 0, UID_LENGTH); - masterPublic = (ECPublicKey) KeyBuilder.buildKey(KeyBuilder.TYPE_EC_FP_PUBLIC, EC_KEY_SIZE, false); - masterPrivate = (ECPrivateKey) KeyBuilder.buildKey(KeyBuilder.TYPE_EC_FP_PRIVATE, EC_KEY_SIZE, false); + masterPublic = (ECPublicKey) KeyBuilder.buildKey(KeyBuilder.TYPE_EC_FP_PUBLIC, SECP256k1.SECP256K1_KEY_SIZE, false); + masterPrivate = (ECPrivateKey) KeyBuilder.buildKey(KeyBuilder.TYPE_EC_FP_PRIVATE, SECP256k1.SECP256K1_KEY_SIZE, false); - parentPublicKey = (ECPublicKey) KeyBuilder.buildKey(KeyBuilder.TYPE_EC_FP_PUBLIC, EC_KEY_SIZE, false); - parentPrivateKey = (ECPrivateKey) KeyBuilder.buildKey(KeyBuilder.TYPE_EC_FP_PRIVATE, EC_KEY_SIZE, false); + parentPublicKey = (ECPublicKey) KeyBuilder.buildKey(KeyBuilder.TYPE_EC_FP_PUBLIC, SECP256k1.SECP256K1_KEY_SIZE, false); + parentPrivateKey = (ECPrivateKey) KeyBuilder.buildKey(KeyBuilder.TYPE_EC_FP_PRIVATE, SECP256k1.SECP256K1_KEY_SIZE, false); - 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); + publicKey = (ECPublicKey) KeyBuilder.buildKey(KeyBuilder.TYPE_EC_FP_PUBLIC, SECP256k1.SECP256K1_KEY_SIZE, false); + privateKey = (ECPrivateKey) KeyBuilder.buildKey(KeyBuilder.TYPE_EC_FP_PRIVATE, SECP256k1.SECP256K1_KEY_SIZE, false); masterChainCode = new byte[CHAIN_CODE_SIZE]; parentChainCode = new byte[CHAIN_CODE_SIZE]; @@ -628,9 +626,10 @@ public class WalletApplet extends Applet { * Resets the status of the keys. This method must be called immediately before committing the transaction where key * manipulation has happened to be sure that the state is always consistent. */ - private void resetKeyStatus(boolean toParent) { - parentValid = false; - keyPathLen = toParent ? (short) (keyPathLen - 4) : 0; + private void resetKeyStatus() { + parentPrivateKey.clearKey(); + parentPublicKey.clearKey(); + keyPathLen = 0; } /** @@ -687,7 +686,7 @@ public class WalletApplet extends Applet { ISOException.throwIt(ISO7816.SW_WRONG_DATA); } - resetKeyStatus(false); + resetKeyStatus(); JCSystem.commitTransaction(); } @@ -719,7 +718,7 @@ public class WalletApplet extends Applet { masterPublic.setW(apduBuffer, (short) 0, pubLen); publicKey.setW(apduBuffer, (short) 0, pubLen); - resetKeyStatus(false); + resetKeyStatus(); JCSystem.commitTransaction(); } @@ -754,96 +753,140 @@ public class WalletApplet extends Applet { ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); } - boolean isReset = apduBuffer[ISO7816.OFFSET_P1] == DERIVE_P1_SOURCE_MASTER; - boolean fromParent = apduBuffer[ISO7816.OFFSET_P1] == DERIVE_P1_SOURCE_PARENT; - - doDerive(apduBuffer, len, isReset, fromParent); + doDerive(apduBuffer, len, apduBuffer[ISO7816.OFFSET_P1], true); } /** * Internal derivation function, called by DERIVE KEY and EXPORT KEY - * @param apduBuffer the APDU buffer + * @param apduBuffer the APDU buffer * @param len the APDU len - * @param isReset start deriving from master - * @param fromParent start deriving from parent + * @param source derivation source + * @param makeCurrent whether the results should be saved or not */ - private void doDerive(byte[] apduBuffer, short len, boolean isReset, boolean fromParent) { + private void doDerive(byte[] apduBuffer, short len, byte source, boolean makeCurrent) { if (!isExtended) { ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); } - if (fromParent && !parentValid) { - ISOException.throwIt(ISO7816.SW_WRONG_P1P2); + short newPathLen; + short pathLenOff; + ECPublicKey sourcePub; + ECPrivateKey sourcePriv; + byte[] sourceChain; + + switch (source) { + case DERIVE_P1_SOURCE_MASTER: + if (len == 0) { + resetToMaster(apduBuffer); + return; + } + + newPathLen = len; + sourcePriv = masterPrivate; + sourcePub = masterPublic; + sourceChain = masterChainCode; + pathLenOff = 0; + break; + case DERIVE_P1_SOURCE_PARENT: + if (!parentPublicKey.isInitialized()) { + ISOException.throwIt(ISO7816.SW_WRONG_P1P2); + } + + if (len == 0) { + ISOException.throwIt(ISO7816.SW_WRONG_DATA); + } + + newPathLen = (short) (keyPathLen + len - 4); + sourcePriv = parentPrivateKey; + sourcePub = parentPublicKey; + sourceChain = parentChainCode; + pathLenOff = (short) (keyPathLen - 4); + break; + case DERIVE_P1_SOURCE_CURRENT: + if (len == 0) { + ISOException.throwIt(ISO7816.SW_WRONG_DATA); + } + + newPathLen = (short) (keyPathLen + len); + sourcePriv = privateKey; + sourcePub = publicKey; + sourceChain = chainCode; + pathLenOff = keyPathLen; + break; + default: + ISOException.throwIt(ISO7816.SW_INCORRECT_P1P2); + return; } - if (((short) (len % 4) != 0) || ((short)(len + (isReset ? 0 : keyPathLen)) > keyPath.length)) { + if (((short) (len % 4) != 0) || (newPathLen > keyPath.length)) { ISOException.throwIt(ISO7816.SW_WRONG_DATA); } - short chainEnd = (short) (ISO7816.OFFSET_CDATA + len); + short scratchOff = (short) (ISO7816.OFFSET_CDATA + len); + short dataOff = (short) (scratchOff + Crypto.KEY_DERIVATION_SCRATCH_SIZE); - if (isReset || fromParent) { - resetKeys(fromParent, apduBuffer, chainEnd); + short pubKeyOff = (short) (dataOff + sourcePriv.getS(apduBuffer, dataOff)); + pubKeyOff = Util.arrayCopyNonAtomic(sourceChain, (short)0, apduBuffer, pubKeyOff, CHAIN_CODE_SIZE); + short outputOff = (short) (pubKeyOff + Crypto.KEY_PUB_SIZE); + + if (!crypto.bip32IsHardened(apduBuffer, ISO7816.OFFSET_CDATA)) { + sourcePub.getW(apduBuffer, pubKeyOff); + } else { + apduBuffer[pubKeyOff] = 0; } - for (short i = ISO7816.OFFSET_CDATA; i < chainEnd; i += 4) { - JCSystem.beginTransaction(); + for (short i = ISO7816.OFFSET_CDATA; i < scratchOff; i += 4) { + Util.arrayCopyNonAtomic(apduBuffer, outputOff, apduBuffer, dataOff, (short) (Crypto.KEY_SECRET_SIZE + CHAIN_CODE_SIZE)); - copyKeys(privateKey, publicKey, chainCode, parentPrivateKey, parentPublicKey, parentChainCode, apduBuffer, chainEnd); - - if (!crypto.bip32CKDPriv(apduBuffer, i, privateKey, publicKey, chainCode, (short) 0)) { - ISOException.throwIt(ISO7816.SW_DATA_INVALID); + if ((i > ISO7816.OFFSET_CDATA) && !crypto.bip32IsHardened(apduBuffer, i)) { + secp256k1.derivePublicKey(apduBuffer, dataOff, apduBuffer, pubKeyOff); + } else { + apduBuffer[pubKeyOff] = 0; } - Util.arrayCopy(apduBuffer, i, keyPath, keyPathLen, (short) 4); + if (!crypto.bip32CKDPriv(apduBuffer, i, apduBuffer, scratchOff, apduBuffer, dataOff, apduBuffer, outputOff)) { + ISOException.throwIt(ISO7816.SW_DATA_INVALID); + } + } - short pubLen = secp256k1.derivePublicKey(privateKey, apduBuffer, chainEnd); - publicKey.setW(apduBuffer, chainEnd, pubLen); - keyPathLen += 4; - parentValid = true; + if (makeCurrent) { + JCSystem.beginTransaction(); + parentPrivateKey.setS(apduBuffer, dataOff, Crypto.KEY_SECRET_SIZE); + Util.arrayCopy(apduBuffer, (short)(dataOff + Crypto.KEY_SECRET_SIZE), parentChainCode, (short) 0, CHAIN_CODE_SIZE); + + if (apduBuffer[pubKeyOff] == 0x04) { + parentPublicKey.setW(apduBuffer, pubKeyOff, Crypto.KEY_PUB_SIZE); + } else { + secp256k1.derivePublicKey(parentPrivateKey, apduBuffer, scratchOff); + parentPublicKey.setW(apduBuffer, scratchOff, Crypto.KEY_PUB_SIZE); + } + + parentPublicKey.setW(apduBuffer, pubKeyOff, Crypto.KEY_PUB_SIZE); + + privateKey.setS(apduBuffer, outputOff, Crypto.KEY_SECRET_SIZE); + Util.arrayCopy(apduBuffer, (short)(outputOff + Crypto.KEY_SECRET_SIZE), chainCode, (short) 0, CHAIN_CODE_SIZE); + secp256k1.derivePublicKey(privateKey, apduBuffer, scratchOff); + publicKey.setW(apduBuffer, scratchOff, Crypto.KEY_PUB_SIZE); + + Util.arrayCopy(apduBuffer, ISO7816.OFFSET_CDATA, keyPath, pathLenOff, len); + keyPathLen = newPathLen; JCSystem.commitTransaction(); } } /** - * Resets the current key and key path to the parent or master key. A transaction is used to make sure this all - * happens at once. This method is called internally by the deriveKey method. + * Resets to master key * - * @param toParent resets to the parent key - * @param buffer a buffer which can be overwritten (currently the APDU buffer) - * @param offset the offset at which the buffer is free + * @param apduBuffer the APDU buffer */ - private void resetKeys(boolean toParent, byte[] buffer, short offset) { - ECPrivateKey srcPrivKey = toParent ? parentPrivateKey : masterPrivate; - ECPublicKey srcPubKey = toParent ? parentPublicKey : masterPublic; - byte[] srcChainCode = toParent ? parentChainCode : masterChainCode; - - JCSystem.beginTransaction(); - copyKeys(srcPrivKey, srcPubKey, srcChainCode, privateKey, publicKey, chainCode, buffer, offset); - resetKeyStatus(toParent); - JCSystem.commitTransaction(); - } - - /** - * Copys a key set to another one. Requires a transient buffer which can be overwritten. - * - * @param srcPrivate source private key - * @param srcPublic source public key - * @param srcChain source chain code - * @param dstPrivate destination private key - * @param dstPublic destination public key - * @param dstChain destination chain code - * @param buffer tmp buffer - * @param offset tmp buffer offset - */ - private void copyKeys(ECPrivateKey srcPrivate, ECPublicKey srcPublic, byte[] srcChain, ECPrivateKey dstPrivate, ECPublicKey dstPublic, byte[] dstChain, byte[] buffer, short offset) { - short pubOff = (short) (offset + srcPrivate.getS(buffer, offset)); - short pubLen = srcPublic.getW(buffer, pubOff); - - Util.arrayCopy(srcChain, (short) 0, dstChain, (short) 0, CHAIN_CODE_SIZE); - dstPrivate.setS(buffer, offset, CHAIN_CODE_SIZE); - dstPublic.setW(buffer, pubOff, pubLen); + private void resetToMaster(byte[] apduBuffer) { + resetKeyStatus(); + masterPrivate.getS(apduBuffer, ISO7816.OFFSET_CDATA); + privateKey.setS(apduBuffer, ISO7816.OFFSET_CDATA, Crypto.KEY_SECRET_SIZE); + masterPublic.getW(apduBuffer, ISO7816.OFFSET_CDATA); + publicKey.setW(apduBuffer, ISO7816.OFFSET_CDATA, Crypto.KEY_PUB_SIZE); + Util.arrayCopyNonAtomic(masterChainCode, (short) 0, chainCode, (short) 0, CHAIN_CODE_SIZE); } /** @@ -941,7 +984,6 @@ public class WalletApplet extends Applet { keyPathLen = 0; pinlessPathLen = 0; - parentValid = false; isExtended = false; privateKey.clearKey(); publicKey.clearKey(); @@ -1209,8 +1251,8 @@ public class WalletApplet extends Applet { ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); } - if (makeCurrent) { - doDerive(apduBuffer, dataLen, true, false); + if (derive) { + doDerive(apduBuffer, dataLen, DERIVE_P1_SOURCE_MASTER, makeCurrent); } short off = SecureChannel.SC_OUT_OFFSET;