mirror of
https://github.com/status-im/status-keycard.git
synced 2025-03-03 06:10:29 +00:00
implement assisted DERIVE KEY
This commit is contained in:
parent
49c8f06c2d
commit
760f431a16
@ -58,7 +58,7 @@ public class WalletApplet extends Applet {
|
||||
static final byte TLV_KEY_INITIALIZATION_STATUS = (byte) 0xC2;
|
||||
static final byte TLV_PUBLIC_KEY_DERIVATION = (byte) 0xC3;
|
||||
|
||||
private static final byte[] ASSISTED_DERIVATION_HASH = { (byte) 0xAA, (byte) 0x2D, (byte) 0xA9, (byte) 0x9D, (byte) 0x91, (byte) 0x8C, (byte) 0x7D, (byte) 0x95, (byte) 0xB8, (byte) 0x96, (byte) 0x89, (byte) 0x87, (byte) 0x3E, (byte) 0xAA, (byte) 0x37, (byte) 0x67, (byte) 0x25, (byte) 0x0C, (byte) 0xFF, (byte) 0x50, (byte) 0x13, (byte) 0x9A, (byte) 0x2F, (byte) 0x87, (byte) 0xBB, (byte) 0x4F, (byte) 0xCA, (byte) 0xB4, (byte) 0xAE, (byte) 0xC3, (byte) 0xE8, (byte) 0x90};
|
||||
static final byte[] ASSISTED_DERIVATION_HASH = { (byte) 0xAA, (byte) 0x2D, (byte) 0xA9, (byte) 0x9D, (byte) 0x91, (byte) 0x8C, (byte) 0x7D, (byte) 0x95, (byte) 0xB8, (byte) 0x96, (byte) 0x89, (byte) 0x87, (byte) 0x3E, (byte) 0xAA, (byte) 0x37, (byte) 0x67, (byte) 0x25, (byte) 0x0C, (byte) 0xFF, (byte) 0x50, (byte) 0x13, (byte) 0x9A, (byte) 0x2F, (byte) 0x87, (byte) 0xBB, (byte) 0x4F, (byte) 0xCA, (byte) 0xB4, (byte) 0xAE, (byte) 0xC3, (byte) 0xE8, (byte) 0x90};
|
||||
|
||||
private OwnerPIN pin;
|
||||
private OwnerPIN puk;
|
||||
@ -367,8 +367,20 @@ public class WalletApplet extends Applet {
|
||||
short len = secureChannel.decryptAPDU(apduBuffer);
|
||||
|
||||
boolean assistedDerivation = (apduBuffer[ISO7816.OFFSET_P1] & DERIVE_P1_ASSISTED_MASK) == DERIVE_P1_ASSISTED_MASK;
|
||||
boolean isPublicKey = apduBuffer[ISO7816.OFFSET_P2] == DERIVE_P2_PUBLIC_KEY;
|
||||
boolean isReset = (apduBuffer[ISO7816.OFFSET_P1] & DERIVE_P1_APPEND_MASK) != DERIVE_P1_APPEND_MASK;
|
||||
|
||||
if (((short) (len % 4) != 0) || (assistedDerivation && len > 0)) {
|
||||
if ((isPublicKey != (expectPublicKey && !isReset)) || (isPublicKey && !assistedDerivation)) {
|
||||
ISOException.throwIt(ISO7816.SW_INCORRECT_P1P2);
|
||||
}
|
||||
|
||||
if (isPublicKey) {
|
||||
publicKey.setW(apduBuffer, ISO7816.OFFSET_CDATA, len);
|
||||
expectPublicKey = false;
|
||||
return;
|
||||
}
|
||||
|
||||
if (((short) (len % 4) != 0) || (assistedDerivation && (len > 4))) {
|
||||
ISOException.throwIt(ISO7816.SW_WRONG_DATA);
|
||||
}
|
||||
|
||||
@ -378,7 +390,7 @@ public class WalletApplet extends Applet {
|
||||
|
||||
short chainEnd = (short) (ISO7816.OFFSET_CDATA + len);
|
||||
|
||||
if ((apduBuffer[ISO7816.OFFSET_P1] & DERIVE_P1_APPEND_MASK) != DERIVE_P1_APPEND_MASK) {
|
||||
if (isReset) {
|
||||
resetKeys(apduBuffer, chainEnd);
|
||||
expectPublicKey = false;
|
||||
}
|
||||
@ -387,9 +399,33 @@ public class WalletApplet extends Applet {
|
||||
|
||||
for (short i = ISO7816.OFFSET_CDATA; i < chainEnd; i += 4) {
|
||||
Crypto.bip32CKDPriv(apduBuffer, i, privateKey, publicKey, chainCode, (short) 0);
|
||||
short pubLen = SECP256k1.derivePublicKey(privateKey, apduBuffer, chainEnd);
|
||||
publicKey.setW(apduBuffer, chainEnd, pubLen);
|
||||
|
||||
if (assistedDerivation) {
|
||||
expectPublicKey = true;
|
||||
outputPublicX(apdu, apduBuffer);
|
||||
return;
|
||||
} else {
|
||||
short pubLen = SECP256k1.derivePublicKey(privateKey, apduBuffer, chainEnd);
|
||||
publicKey.setW(apduBuffer, chainEnd, pubLen);
|
||||
}
|
||||
}
|
||||
|
||||
expectPublicKey = false;
|
||||
}
|
||||
|
||||
private void outputPublicX(APDU apdu, byte[] apduBuffer) {
|
||||
short xLen = SECP256k1.derivePublicX(privateKey, apduBuffer, (short) (SecureChannel.SC_OUT_OFFSET + 4));
|
||||
|
||||
signature.init(privateKey, Signature.MODE_SIGN);
|
||||
short sigLen = signature.signPreComputedHash(ASSISTED_DERIVATION_HASH, (short) 0, (short) ASSISTED_DERIVATION_HASH.length, apduBuffer, (short) (SecureChannel.SC_OUT_OFFSET + xLen + 4));
|
||||
|
||||
apduBuffer[SecureChannel.SC_OUT_OFFSET] = TLV_KEY_DERIVATION_TEMPLATE;
|
||||
apduBuffer[(short) (SecureChannel.SC_OUT_OFFSET + 1)] = (byte) (xLen + sigLen + 2);
|
||||
apduBuffer[(short) (SecureChannel.SC_OUT_OFFSET + 2)] = TLV_PUB_X;
|
||||
apduBuffer[(short) (SecureChannel.SC_OUT_OFFSET + 3)] = (byte) xLen;
|
||||
|
||||
short outLen = secureChannel.encryptAPDU(apduBuffer, (short) (xLen + sigLen + 4));
|
||||
apdu.setOutgoingAndSend(ISO7816.OFFSET_CDATA, outLen);
|
||||
}
|
||||
|
||||
private void resetKeys(byte[] buffer, short offset) {
|
||||
|
@ -4,6 +4,7 @@ import com.licel.jcardsim.smartcardio.CardSimulator;
|
||||
import com.licel.jcardsim.smartcardio.CardTerminalSimulator;
|
||||
import com.licel.jcardsim.utils.AIDUtil;
|
||||
import javacard.framework.AID;
|
||||
import org.bitcoinj.core.ECKey;
|
||||
import org.bitcoinj.crypto.ChildNumber;
|
||||
import org.bitcoinj.crypto.DeterministicKey;
|
||||
import org.bitcoinj.crypto.HDKeyDerivation;
|
||||
@ -395,9 +396,19 @@ public class WalletAppletTest {
|
||||
response = cmdSet.loadKey(keyPair, false, chainCode);
|
||||
assertEquals(0x9000, response.getSW());
|
||||
|
||||
// Wrong data format (data length not a multiple of 4)
|
||||
// Wrong P1/P2
|
||||
response = cmdSet.deriveKey(new byte[] {0x00, 0x00, 0x00, 0x01}, false, true, true);
|
||||
assertEquals(0x6A86, response.getSW());
|
||||
response = cmdSet.deriveKey(new byte[] {0x00, 0x00, 0x00, 0x01}, false, false, true);
|
||||
assertEquals(0x6A86, response.getSW());
|
||||
|
||||
// Wrong data format
|
||||
response = cmdSet.deriveKey(new byte[] {0x00, 0x00, 0x00});
|
||||
assertEquals(0x6A80, response.getSW());
|
||||
response = cmdSet.deriveKey(new byte[] {0x00, 0x00, 0x00, 0x00, 0x00});
|
||||
assertEquals(0x6A80, response.getSW());
|
||||
response = cmdSet.deriveKey(new byte[] {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, true, true, false);
|
||||
assertEquals(0x6A80, response.getSW());
|
||||
|
||||
// Correct
|
||||
response = cmdSet.deriveKey(new byte[] {0x00, 0x00, 0x00, 0x01});
|
||||
@ -422,6 +433,28 @@ public class WalletAppletTest {
|
||||
response = cmdSet.deriveKey(new byte[] {0x00, 0x00, 0x00, 0x02}, false, false, false);
|
||||
assertEquals(0x9000, response.getSW());
|
||||
verifyKeyDerivation(keyPair, chainCode, new int[] { 1, 0x80000000, 2});
|
||||
|
||||
// Assisted derivation
|
||||
response = cmdSet.deriveKey(new byte[] {0x00, 0x00, 0x00, 0x01}, true, true, false);
|
||||
assertEquals(0x9000, response.getSW());
|
||||
response = cmdSet.deriveKey(derivePublicKey(secureChannel.decryptAPDU(response.getData())), false, true, true);
|
||||
assertEquals(0x9000, response.getSW());
|
||||
verifyKeyDerivation(keyPair, chainCode, new int[] { 1 });
|
||||
|
||||
response = cmdSet.deriveKey(new byte[] {0x00, 0x00, 0x00, 0x02}, false, true, false);
|
||||
assertEquals(0x9000, response.getSW());
|
||||
response = cmdSet.deriveKey(derivePublicKey(secureChannel.decryptAPDU(response.getData())), false, true, true);
|
||||
assertEquals(0x9000, response.getSW());
|
||||
verifyKeyDerivation(keyPair, chainCode, new int[] { 1, 2 });
|
||||
|
||||
// Try to derive two keys at once
|
||||
response = cmdSet.deriveKey(new byte[] {0x00, 0x00, 0x00, 0x02}, false, true, false);
|
||||
assertEquals(0x9000, response.getSW());
|
||||
response = cmdSet.deriveKey(new byte[] {0x00, 0x00, 0x00, 0x02}, false, true, false);
|
||||
assertEquals(0x6a86, response.getSW());
|
||||
response = cmdSet.deriveKey(new byte[0]);
|
||||
assertEquals(0x9000, response.getSW());
|
||||
verifyKeyDerivation(keyPair, chainCode, new int[0]);
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -670,6 +703,21 @@ public class WalletAppletTest {
|
||||
assertArrayEquals(key.getPubKeyPoint().getEncoded(false), publicKey);
|
||||
}
|
||||
|
||||
private byte[] derivePublicKey(byte[] data) {
|
||||
byte[] pubKey = Arrays.copyOfRange(data, 3, 4 + data[3]);
|
||||
byte[] signature = Arrays.copyOfRange(data, 4 + data[3], data.length);
|
||||
|
||||
pubKey[0] = 0x02;
|
||||
ECKey candidate = ECKey.fromPublicOnly(pubKey);
|
||||
if (!candidate.verify(WalletApplet.ASSISTED_DERIVATION_HASH, signature)) {
|
||||
pubKey[0] = 0x03;
|
||||
candidate = ECKey.fromPublicOnly(pubKey);
|
||||
assertTrue(candidate.verify(WalletApplet.ASSISTED_DERIVATION_HASH, signature));
|
||||
}
|
||||
|
||||
return candidate.decompress().getPubKey();
|
||||
}
|
||||
|
||||
private Sign.SignatureData signMessage(byte[] message) throws Exception {
|
||||
byte[] messageHash = Hash.sha3(message);
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user