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:
|
||||
0x00 = Any key
|
||||
0x01 = Whisper key (m/1/1)
|
||||
0x01 = Non-wallet key
|
||||
|
||||
P2:
|
||||
0x00 = private and public key
|
||||
|
@ -341,11 +341,10 @@ Response Data format:
|
|||
- Tag 0x80 = ECC public key component
|
||||
- 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
|
||||
selected by P1. P1 is only an index, the actual key path is stored immutably in the applet itself. At the moment only
|
||||
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
|
||||
the last should remain as short as possible because of the security implications of revealing private keys to a possibly
|
||||
compromised device. The current chain code is never exported to make it impossible to further derive keys off-card.
|
||||
This command exports the current public and private key if and only if the current key path matches the conditions
|
||||
dictated by the given P1 parameter. This currently allows exporting any key whose last path component has a key index
|
||||
with the two most significant bits set (that is, matches the 0xc0000000 mask). No key in this range should be used for
|
||||
wallet accounts, even as intermediate path component. These keys are meant for client-specific use such as the Whisper key.
|
||||
|
||||
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.
|
||||
|
|
|
@ -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 EXPORT_KEY_P1_ANY = 0x00;
|
||||
static final byte EXPORT_KEY_P1_WHISPER = 0x01;
|
||||
static final byte EXPORT_KEY_P1_DATABASE = 0x02;
|
||||
static final byte EXPORT_KEY_P1_HIGH = 0x01;
|
||||
|
||||
static final byte EXPORT_KEY_P2_PRIVATE_AND_PUBLIC = 0x00;
|
||||
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;
|
||||
|
||||
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[] 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 static final byte EXPORT_KEY_HIGH_MASK = (byte) 0xc0;
|
||||
|
||||
private OwnerPIN pin;
|
||||
private OwnerPIN puk;
|
||||
|
@ -930,7 +928,6 @@ public class WalletApplet extends Applet {
|
|||
ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
|
||||
}
|
||||
|
||||
byte[] toExport;
|
||||
boolean publicOnly;
|
||||
|
||||
switch (apduBuffer[ISO7816.OFFSET_P2]) {
|
||||
|
@ -950,24 +947,17 @@ public class WalletApplet extends Applet {
|
|||
if (!publicOnly) {
|
||||
ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
|
||||
}
|
||||
|
||||
toExport = null;
|
||||
break;
|
||||
case EXPORT_KEY_P1_WHISPER:
|
||||
toExport = WHISPER_KEY_PATH;
|
||||
break;
|
||||
case EXPORT_KEY_P1_DATABASE:
|
||||
toExport = DATABASE_KEY_PATH;
|
||||
case EXPORT_KEY_P1_HIGH:
|
||||
if (keyPathLen < 4 || ((((byte)(keyPath[(byte)(keyPathLen - 4)] & EXPORT_KEY_HIGH_MASK)) != EXPORT_KEY_HIGH_MASK))){
|
||||
ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
ISOException.throwIt(ISO7816.SW_INCORRECT_P1P2);
|
||||
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;
|
||||
|
||||
apduBuffer[off++] = TLV_KEY_TEMPLATE;
|
||||
|
@ -989,7 +979,7 @@ public class WalletApplet extends Applet {
|
|||
len = (short) (off - SecureChannel.SC_OUT_OFFSET);
|
||||
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);
|
||||
|
||||
// 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());
|
||||
|
||||
cmdSet.autoOpenSecureChannel();
|
||||
|
||||
// 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());
|
||||
|
||||
response = cmdSet.verifyPIN("000000");
|
||||
|
@ -839,33 +839,33 @@ public class WalletAppletTest {
|
|||
response = cmdSet.loadKey(keyPair, false, chainCode);
|
||||
assertEquals(0x9000, response.getSW());
|
||||
|
||||
// Security condition violation: current key is not Whisper key
|
||||
response = cmdSet.exportKey(WalletApplet.EXPORT_KEY_P1_WHISPER, false);
|
||||
// Security condition violation: current key is not exportable
|
||||
response = cmdSet.exportKey(WalletApplet.EXPORT_KEY_P1_HIGH, false);
|
||||
assertEquals(0x6985, response.getSW());
|
||||
|
||||
response = cmdSet.deriveKey(new byte[] { (byte) 0x80, 0x00, 0x00, 0x2c}, WalletApplet.DERIVE_P1_SOURCE_MASTER, true, false);
|
||||
assertEquals(0x9000, response.getSW());
|
||||
response = cmdSet.deriveKey(derivePublicKey(response.getData()), WalletApplet.DERIVE_P1_SOURCE_CURRENT, true, true);
|
||||
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());
|
||||
response = cmdSet.deriveKey(new byte[] { (byte) 0x80, 0x00, 0x00, 0x3c}, WalletApplet.DERIVE_P1_SOURCE_CURRENT, true, false);
|
||||
assertEquals(0x9000, response.getSW());
|
||||
response = cmdSet.deriveKey(derivePublicKey(response.getData()), WalletApplet.DERIVE_P1_SOURCE_CURRENT, true, true);
|
||||
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());
|
||||
response = cmdSet.deriveKey(new byte[] { (byte) 0x80, 0x00, 0x00, 0x00}, WalletApplet.DERIVE_P1_SOURCE_CURRENT, true, false);
|
||||
assertEquals(0x9000, response.getSW());
|
||||
response = cmdSet.deriveKey(derivePublicKey(response.getData()), WalletApplet.DERIVE_P1_SOURCE_CURRENT, true, true);
|
||||
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());
|
||||
response = cmdSet.deriveKey(new byte[] { (byte) 0x00, 0x00, 0x00, 0x00}, WalletApplet.DERIVE_P1_SOURCE_CURRENT, true, false);
|
||||
assertEquals(0x9000, response.getSW());
|
||||
response = cmdSet.deriveKey(derivePublicKey(response.getData()), WalletApplet.DERIVE_P1_SOURCE_CURRENT, true, true);
|
||||
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());
|
||||
|
||||
|
||||
|
@ -879,7 +879,7 @@ public class WalletAppletTest {
|
|||
byte[] keyTemplate = response.getData();
|
||||
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());
|
||||
response = cmdSet.deriveKey(derivePublicKey(response.getData()), WalletApplet.DERIVE_P1_SOURCE_CURRENT, true, true);
|
||||
assertEquals(0x9000, response.getSW());
|
||||
|
@ -889,21 +889,21 @@ public class WalletAppletTest {
|
|||
assertEquals(0x6a86, response.getSW());
|
||||
|
||||
// Correct
|
||||
response = cmdSet.exportKey(WalletApplet.EXPORT_KEY_P1_WHISPER, false);
|
||||
response = cmdSet.exportKey(WalletApplet.EXPORT_KEY_P1_HIGH, false);
|
||||
assertEquals(0x9000, response.getSW());
|
||||
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
|
||||
response = cmdSet.exportKey(WalletApplet.EXPORT_KEY_P1_WHISPER, true);
|
||||
response = cmdSet.exportKey(WalletApplet.EXPORT_KEY_P1_HIGH, true);
|
||||
assertEquals(0x9000, response.getSW());
|
||||
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
|
||||
response = cmdSet.deriveKey(new byte[] {}, WalletApplet.DERIVE_P1_SOURCE_MASTER, false, false);
|
||||
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());
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue