implemented EXPORT KEY changes
This commit is contained in:
parent
6af167612e
commit
fde5d12850
|
@ -330,7 +330,7 @@ will work even if no PIN authentication has been performed. An empty sequence me
|
||||||
|
|
||||||
P1:
|
P1:
|
||||||
0x00 = Any key
|
0x00 = Any key
|
||||||
0x01 = Whisper key (m/1/1)
|
0x01 = Non-wallet key
|
||||||
|
|
||||||
P2:
|
P2:
|
||||||
0x00 = private and public key
|
0x00 = private and public key
|
||||||
|
@ -341,11 +341,10 @@ Response Data format:
|
||||||
- Tag 0x80 = ECC public key component
|
- Tag 0x80 = ECC public key component
|
||||||
- Tag 0x81 = ECC private key component (if P2=0x00)
|
- Tag 0x81 = ECC private key component (if P2=0x00)
|
||||||
|
|
||||||
This command exports the current public and private key if and only if the current key path matches the one of the key
|
This command exports the current public and private key if and only if the current key path matches the conditions
|
||||||
selected by P1. P1 is only an index, the actual key path is stored immutably in the applet itself. At the moment only
|
dictated by the given P1 parameter. This currently allows exporting any key whose last path component has a key index
|
||||||
the Whisper key (P1=0x01) can be exported and its key path is m/1/1. Other key paths could be added in the future, but
|
with the two most significant bits set (that is, matches the 0xc0000000 mask). No key in this range should be used for
|
||||||
the last should remain as short as possible because of the security implications of revealing private keys to a possibly
|
wallet accounts, even as intermediate path component. These keys are meant for client-specific use such as the Whisper key.
|
||||||
compromised device. The current chain code is never exported to make it impossible to further derive keys off-card.
|
|
||||||
|
|
||||||
The special index 0x00 indicates any path, which means the current key will always be exported regardless of its actual
|
The special index 0x00 indicates any path, which means the current key will always be exported regardless of its actual
|
||||||
path. This works however only in combination with P2=0x01, so only the public key will be exported.
|
path. This works however only in combination with P2=0x01, so only the public key will be exported.
|
||||||
|
|
|
@ -59,8 +59,7 @@ public class WalletApplet extends Applet {
|
||||||
static final byte GENERATE_MNEMONIC_TMP_OFF = SecureChannel.SC_OUT_OFFSET + ((((GENERATE_MNEMONIC_P1_CS_MAX * 32) + GENERATE_MNEMONIC_P1_CS_MAX) / 11) * 2);
|
static final byte GENERATE_MNEMONIC_TMP_OFF = SecureChannel.SC_OUT_OFFSET + ((((GENERATE_MNEMONIC_P1_CS_MAX * 32) + GENERATE_MNEMONIC_P1_CS_MAX) / 11) * 2);
|
||||||
|
|
||||||
static final byte EXPORT_KEY_P1_ANY = 0x00;
|
static final byte EXPORT_KEY_P1_ANY = 0x00;
|
||||||
static final byte EXPORT_KEY_P1_WHISPER = 0x01;
|
static final byte EXPORT_KEY_P1_HIGH = 0x01;
|
||||||
static final byte EXPORT_KEY_P1_DATABASE = 0x02;
|
|
||||||
|
|
||||||
static final byte EXPORT_KEY_P2_PRIVATE_AND_PUBLIC = 0x00;
|
static final byte EXPORT_KEY_P2_PRIVATE_AND_PUBLIC = 0x00;
|
||||||
static final byte EXPORT_KEY_P2_PUBLIC_ONLY = 0x01;
|
static final byte EXPORT_KEY_P2_PUBLIC_ONLY = 0x01;
|
||||||
|
@ -83,8 +82,7 @@ public class WalletApplet extends Applet {
|
||||||
static final byte TLV_UID = (byte) 0x8F;
|
static final byte TLV_UID = (byte) 0x8F;
|
||||||
|
|
||||||
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};
|
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};
|
||||||
private static final byte[] WHISPER_KEY_PATH = {(byte) 0x80, 0x00, 0x00, 0x2c, (byte) 0x80, 0x00, 0x00, 0x3c, (byte) 0x80, 0x00, 0x00, 0x00, (byte) 0x00, 0x00, 0x00, 0x00, (byte) 0xC0, 0x00, 0x00, 0x00};
|
private static final byte EXPORT_KEY_HIGH_MASK = (byte) 0xc0;
|
||||||
private static final byte[] DATABASE_KEY_PATH = {(byte) 0x80, 0x00, 0x00, 0x2c, (byte) 0x80, 0x00, 0x00, 0x3c, (byte) 0x80, 0x00, 0x00, 0x00, (byte) 0x00, 0x00, 0x00, 0x00, (byte) 0xC0, 0x00, 0x00, 0x01};
|
|
||||||
|
|
||||||
private OwnerPIN pin;
|
private OwnerPIN pin;
|
||||||
private OwnerPIN puk;
|
private OwnerPIN puk;
|
||||||
|
@ -930,7 +928,6 @@ public class WalletApplet extends Applet {
|
||||||
ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
|
ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
|
||||||
}
|
}
|
||||||
|
|
||||||
byte[] toExport;
|
|
||||||
boolean publicOnly;
|
boolean publicOnly;
|
||||||
|
|
||||||
switch (apduBuffer[ISO7816.OFFSET_P2]) {
|
switch (apduBuffer[ISO7816.OFFSET_P2]) {
|
||||||
|
@ -950,24 +947,17 @@ public class WalletApplet extends Applet {
|
||||||
if (!publicOnly) {
|
if (!publicOnly) {
|
||||||
ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
|
ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
|
||||||
}
|
}
|
||||||
|
|
||||||
toExport = null;
|
|
||||||
break;
|
break;
|
||||||
case EXPORT_KEY_P1_WHISPER:
|
case EXPORT_KEY_P1_HIGH:
|
||||||
toExport = WHISPER_KEY_PATH;
|
if (keyPathLen < 4 || ((((byte)(keyPath[(byte)(keyPathLen - 4)] & EXPORT_KEY_HIGH_MASK)) != EXPORT_KEY_HIGH_MASK))){
|
||||||
break;
|
ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
|
||||||
case EXPORT_KEY_P1_DATABASE:
|
}
|
||||||
toExport = DATABASE_KEY_PATH;
|
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
ISOException.throwIt(ISO7816.SW_INCORRECT_P1P2);
|
ISOException.throwIt(ISO7816.SW_INCORRECT_P1P2);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!((toExport == null) || ((keyPathLen == toExport.length) && (Util.arrayCompare(keyPath, (short) 0, toExport, (short) 0, keyPathLen) == 0)))) {
|
|
||||||
ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
|
|
||||||
}
|
|
||||||
|
|
||||||
short off = SecureChannel.SC_OUT_OFFSET;
|
short off = SecureChannel.SC_OUT_OFFSET;
|
||||||
|
|
||||||
apduBuffer[off++] = TLV_KEY_TEMPLATE;
|
apduBuffer[off++] = TLV_KEY_TEMPLATE;
|
||||||
|
@ -989,7 +979,7 @@ public class WalletApplet extends Applet {
|
||||||
len = (short) (off - SecureChannel.SC_OUT_OFFSET);
|
len = (short) (off - SecureChannel.SC_OUT_OFFSET);
|
||||||
apduBuffer[(SecureChannel.SC_OUT_OFFSET + 1)] = (byte) (len - 2);
|
apduBuffer[(SecureChannel.SC_OUT_OFFSET + 1)] = (byte) (len - 2);
|
||||||
|
|
||||||
secureChannel.respond(apdu, (short) len, ISO7816.SW_NO_ERROR);
|
secureChannel.respond(apdu, len, ISO7816.SW_NO_ERROR);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -825,13 +825,13 @@ public class WalletAppletTest {
|
||||||
new Random().nextBytes(chainCode);
|
new Random().nextBytes(chainCode);
|
||||||
|
|
||||||
// Security condition violation: SecureChannel not open
|
// Security condition violation: SecureChannel not open
|
||||||
ResponseAPDU response = cmdSet.exportKey(WalletApplet.EXPORT_KEY_P1_WHISPER, false);
|
ResponseAPDU response = cmdSet.exportKey(WalletApplet.EXPORT_KEY_P1_HIGH, false);
|
||||||
assertEquals(0x6985, response.getSW());
|
assertEquals(0x6985, response.getSW());
|
||||||
|
|
||||||
cmdSet.autoOpenSecureChannel();
|
cmdSet.autoOpenSecureChannel();
|
||||||
|
|
||||||
// Security condition violation: PIN not verified
|
// Security condition violation: PIN not verified
|
||||||
response = cmdSet.exportKey(WalletApplet.EXPORT_KEY_P1_WHISPER, false);
|
response = cmdSet.exportKey(WalletApplet.EXPORT_KEY_P1_HIGH, false);
|
||||||
assertEquals(0x6985, response.getSW());
|
assertEquals(0x6985, response.getSW());
|
||||||
|
|
||||||
response = cmdSet.verifyPIN("000000");
|
response = cmdSet.verifyPIN("000000");
|
||||||
|
@ -839,33 +839,33 @@ public class WalletAppletTest {
|
||||||
response = cmdSet.loadKey(keyPair, false, chainCode);
|
response = cmdSet.loadKey(keyPair, false, chainCode);
|
||||||
assertEquals(0x9000, response.getSW());
|
assertEquals(0x9000, response.getSW());
|
||||||
|
|
||||||
// Security condition violation: current key is not Whisper key
|
// Security condition violation: current key is not exportable
|
||||||
response = cmdSet.exportKey(WalletApplet.EXPORT_KEY_P1_WHISPER, false);
|
response = cmdSet.exportKey(WalletApplet.EXPORT_KEY_P1_HIGH, false);
|
||||||
assertEquals(0x6985, response.getSW());
|
assertEquals(0x6985, response.getSW());
|
||||||
|
|
||||||
response = cmdSet.deriveKey(new byte[] { (byte) 0x80, 0x00, 0x00, 0x2c}, WalletApplet.DERIVE_P1_SOURCE_MASTER, true, false);
|
response = cmdSet.deriveKey(new byte[] { (byte) 0x80, 0x00, 0x00, 0x2c}, WalletApplet.DERIVE_P1_SOURCE_MASTER, true, false);
|
||||||
assertEquals(0x9000, response.getSW());
|
assertEquals(0x9000, response.getSW());
|
||||||
response = cmdSet.deriveKey(derivePublicKey(response.getData()), WalletApplet.DERIVE_P1_SOURCE_CURRENT, true, true);
|
response = cmdSet.deriveKey(derivePublicKey(response.getData()), WalletApplet.DERIVE_P1_SOURCE_CURRENT, true, true);
|
||||||
assertEquals(0x9000, response.getSW());
|
assertEquals(0x9000, response.getSW());
|
||||||
response = cmdSet.exportKey(WalletApplet.EXPORT_KEY_P1_WHISPER, false);
|
response = cmdSet.exportKey(WalletApplet.EXPORT_KEY_P1_HIGH, false);
|
||||||
assertEquals(0x6985, response.getSW());
|
assertEquals(0x6985, response.getSW());
|
||||||
response = cmdSet.deriveKey(new byte[] { (byte) 0x80, 0x00, 0x00, 0x3c}, WalletApplet.DERIVE_P1_SOURCE_CURRENT, true, false);
|
response = cmdSet.deriveKey(new byte[] { (byte) 0x80, 0x00, 0x00, 0x3c}, WalletApplet.DERIVE_P1_SOURCE_CURRENT, true, false);
|
||||||
assertEquals(0x9000, response.getSW());
|
assertEquals(0x9000, response.getSW());
|
||||||
response = cmdSet.deriveKey(derivePublicKey(response.getData()), WalletApplet.DERIVE_P1_SOURCE_CURRENT, true, true);
|
response = cmdSet.deriveKey(derivePublicKey(response.getData()), WalletApplet.DERIVE_P1_SOURCE_CURRENT, true, true);
|
||||||
assertEquals(0x9000, response.getSW());
|
assertEquals(0x9000, response.getSW());
|
||||||
response = cmdSet.exportKey(WalletApplet.EXPORT_KEY_P1_WHISPER, false);
|
response = cmdSet.exportKey(WalletApplet.EXPORT_KEY_P1_HIGH, false);
|
||||||
assertEquals(0x6985, response.getSW());
|
assertEquals(0x6985, response.getSW());
|
||||||
response = cmdSet.deriveKey(new byte[] { (byte) 0x80, 0x00, 0x00, 0x00}, WalletApplet.DERIVE_P1_SOURCE_CURRENT, true, false);
|
response = cmdSet.deriveKey(new byte[] { (byte) 0x80, 0x00, 0x00, 0x00}, WalletApplet.DERIVE_P1_SOURCE_CURRENT, true, false);
|
||||||
assertEquals(0x9000, response.getSW());
|
assertEquals(0x9000, response.getSW());
|
||||||
response = cmdSet.deriveKey(derivePublicKey(response.getData()), WalletApplet.DERIVE_P1_SOURCE_CURRENT, true, true);
|
response = cmdSet.deriveKey(derivePublicKey(response.getData()), WalletApplet.DERIVE_P1_SOURCE_CURRENT, true, true);
|
||||||
assertEquals(0x9000, response.getSW());
|
assertEquals(0x9000, response.getSW());
|
||||||
response = cmdSet.exportKey(WalletApplet.EXPORT_KEY_P1_WHISPER, false);
|
response = cmdSet.exportKey(WalletApplet.EXPORT_KEY_P1_HIGH, false);
|
||||||
assertEquals(0x6985, response.getSW());
|
assertEquals(0x6985, response.getSW());
|
||||||
response = cmdSet.deriveKey(new byte[] { (byte) 0x00, 0x00, 0x00, 0x00}, WalletApplet.DERIVE_P1_SOURCE_CURRENT, true, false);
|
response = cmdSet.deriveKey(new byte[] { (byte) 0x00, 0x00, 0x00, 0x00}, WalletApplet.DERIVE_P1_SOURCE_CURRENT, true, false);
|
||||||
assertEquals(0x9000, response.getSW());
|
assertEquals(0x9000, response.getSW());
|
||||||
response = cmdSet.deriveKey(derivePublicKey(response.getData()), WalletApplet.DERIVE_P1_SOURCE_CURRENT, true, true);
|
response = cmdSet.deriveKey(derivePublicKey(response.getData()), WalletApplet.DERIVE_P1_SOURCE_CURRENT, true, true);
|
||||||
assertEquals(0x9000, response.getSW());
|
assertEquals(0x9000, response.getSW());
|
||||||
response = cmdSet.exportKey(WalletApplet.EXPORT_KEY_P1_WHISPER, false);
|
response = cmdSet.exportKey(WalletApplet.EXPORT_KEY_P1_HIGH, false);
|
||||||
assertEquals(0x6985, response.getSW());
|
assertEquals(0x6985, response.getSW());
|
||||||
|
|
||||||
|
|
||||||
|
@ -879,7 +879,7 @@ public class WalletAppletTest {
|
||||||
byte[] keyTemplate = response.getData();
|
byte[] keyTemplate = response.getData();
|
||||||
verifyExportedKey(keyTemplate, keyPair, chainCode, new int[] { 0x8000002c, 0x8000003c, 0x80000000, 0x00000000 }, true);
|
verifyExportedKey(keyTemplate, keyPair, chainCode, new int[] { 0x8000002c, 0x8000003c, 0x80000000, 0x00000000 }, true);
|
||||||
|
|
||||||
response = cmdSet.deriveKey(new byte[] {(byte) 0xC0, 0x00, 0x00, 0x00}, WalletApplet.DERIVE_P1_SOURCE_CURRENT, true, false);
|
response = cmdSet.deriveKey(new byte[] {(byte) 0xC0, 0x00, 0x00, 0x01}, WalletApplet.DERIVE_P1_SOURCE_CURRENT, true, false);
|
||||||
assertEquals(0x9000, response.getSW());
|
assertEquals(0x9000, response.getSW());
|
||||||
response = cmdSet.deriveKey(derivePublicKey(response.getData()), WalletApplet.DERIVE_P1_SOURCE_CURRENT, true, true);
|
response = cmdSet.deriveKey(derivePublicKey(response.getData()), WalletApplet.DERIVE_P1_SOURCE_CURRENT, true, true);
|
||||||
assertEquals(0x9000, response.getSW());
|
assertEquals(0x9000, response.getSW());
|
||||||
|
@ -889,21 +889,21 @@ public class WalletAppletTest {
|
||||||
assertEquals(0x6a86, response.getSW());
|
assertEquals(0x6a86, response.getSW());
|
||||||
|
|
||||||
// Correct
|
// Correct
|
||||||
response = cmdSet.exportKey(WalletApplet.EXPORT_KEY_P1_WHISPER, false);
|
response = cmdSet.exportKey(WalletApplet.EXPORT_KEY_P1_HIGH, false);
|
||||||
assertEquals(0x9000, response.getSW());
|
assertEquals(0x9000, response.getSW());
|
||||||
keyTemplate = response.getData();
|
keyTemplate = response.getData();
|
||||||
verifyExportedKey(keyTemplate, keyPair, chainCode, new int[] { 0x8000002c, 0x8000003c, 0x80000000, 0x00000000, 0xC0000000 }, false);
|
verifyExportedKey(keyTemplate, keyPair, chainCode, new int[] { 0x8000002c, 0x8000003c, 0x80000000, 0x00000000, 0xC0000001 }, false);
|
||||||
|
|
||||||
// Correct public only
|
// Correct public only
|
||||||
response = cmdSet.exportKey(WalletApplet.EXPORT_KEY_P1_WHISPER, true);
|
response = cmdSet.exportKey(WalletApplet.EXPORT_KEY_P1_HIGH, true);
|
||||||
assertEquals(0x9000, response.getSW());
|
assertEquals(0x9000, response.getSW());
|
||||||
keyTemplate = response.getData();
|
keyTemplate = response.getData();
|
||||||
verifyExportedKey(keyTemplate, keyPair, chainCode, new int[] { 0x8000002c, 0x8000003c, 0x80000000, 0x00000000, 0xC0000000 }, true);
|
verifyExportedKey(keyTemplate, keyPair, chainCode, new int[] { 0x8000002c, 0x8000003c, 0x80000000, 0x00000000, 0xC0000001 }, true);
|
||||||
|
|
||||||
// Reset
|
// Reset
|
||||||
response = cmdSet.deriveKey(new byte[] {}, WalletApplet.DERIVE_P1_SOURCE_MASTER, false, false);
|
response = cmdSet.deriveKey(new byte[] {}, WalletApplet.DERIVE_P1_SOURCE_MASTER, false, false);
|
||||||
assertEquals(0x9000, response.getSW());
|
assertEquals(0x9000, response.getSW());
|
||||||
response = cmdSet.exportKey(WalletApplet.EXPORT_KEY_P1_WHISPER, false);
|
response = cmdSet.exportKey(WalletApplet.EXPORT_KEY_P1_HIGH, false);
|
||||||
assertEquals(0x6985, response.getSW());
|
assertEquals(0x6985, response.getSW());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue