diff --git a/lib/src/main/java/im/status/hardwallet_lite_android/wallet/WalletAppletCommandSet.java b/lib/src/main/java/im/status/hardwallet_lite_android/wallet/WalletAppletCommandSet.java index b907dbf..97c2e6d 100644 --- a/lib/src/main/java/im/status/hardwallet_lite_android/wallet/WalletAppletCommandSet.java +++ b/lib/src/main/java/im/status/hardwallet_lite_android/wallet/WalletAppletCommandSet.java @@ -33,6 +33,7 @@ public class WalletAppletCommandSet { static final byte INS_GENERATE_MNEMONIC = (byte) 0xD2; static final byte INS_REMOVE_KEY = (byte) 0xD3; static final byte INS_GENERATE_KEY = (byte) 0xD4; + static final byte INS_DUPLICATE_KEY = (byte) 0xD5; static final byte INS_SIGN = (byte) 0xC0; static final byte INS_SET_PINLESS_PATH = (byte) 0xC1; static final byte INS_EXPORT_KEY = (byte) 0xC2; @@ -48,6 +49,11 @@ public class WalletAppletCommandSet { public static final byte DERIVE_P1_SOURCE_PARENT = (byte) 0x40; public static final byte DERIVE_P1_SOURCE_CURRENT = (byte) 0x80; + static final byte DUPLICATE_KEY_P1_START = 0x00; + static final byte DUPLICATE_KEY_P1_ADD_ENTROPY = 0x01; + static final byte DUPLICATE_KEY_P1_EXPORT = 0x02; + static final byte DUPLICATE_KEY_P1_IMPORT = 0x03; + public static final int GENERATE_MNEMONIC_12_WORDS = 0x04; public static final int GENERATE_MNEMONIC_15_WORDS = 0x05; public static final int GENERATE_MNEMONIC_18_WORDS = 0x06; @@ -449,6 +455,44 @@ public class WalletAppletCommandSet { return secureChannel.transmit(apduChannel, generateKey); } + /** + * Sends a DUPLICATE KEY APDU. The P1 is set to 00, P2 to the entropy count and the data is the first entropy piece. + * This starts a duplication session. Requires an open Secure Channel and authenticated PIN. + * + * @param entropyCount the number of entropy pieces to expect, including the one in this APDU + * @param firstEntropy a random 32-byte number + * @return the raw card response + * @throws IOException communication error + */ + public APDUResponse duplicateKeyStart(int entropyCount, byte[] firstEntropy) throws IOException { + APDUCommand duplicateKeyStart = secureChannel.protectedCommand(0x80, INS_DUPLICATE_KEY, DUPLICATE_KEY_P1_START, entropyCount, firstEntropy); + return secureChannel.transmit(apduChannel, duplicateKeyStart); + } + + /** + * Sends a DUPLICATE KEY APDU. The P1 is set to 01 and the data is the entropy. This adds entropy and does not require + * a Secure Channel or authenticated PIN. + * + * @param entropy a random 32-byte number + * @return the raw card response + * @throws IOException communication error + */ + public APDUResponse duplicateKeyAddEntropy(byte[] entropy) throws IOException { + APDUCommand duplicateKeyAddEntropy = new APDUCommand(0x80, INS_DUPLICATE_KEY, DUPLICATE_KEY_P1_ADD_ENTROPY, 0, secureChannel.oneShotEncrypt(entropy)); + return apduChannel.send(duplicateKeyAddEntropy); + } + + /** + * Sends a DUPLICATE KEY APDU. The P1 is set to 02. This exports the encrypted master key including chaining code. + * + * @return the raw card response + * @throws IOException communication error + */ + public APDUResponse duplicateKeyExport() throws IOException { + APDUCommand duplicateKeyExport = secureChannel.protectedCommand(0x80, INS_DUPLICATE_KEY, DUPLICATE_KEY_P1_EXPORT, 0, new byte[0]); + return secureChannel.transmit(apduChannel, duplicateKeyExport); + } + /** * Sends a SIGN APDU. This signs a precomputed hash so the input must be exactly 32-bytes long. *