remove DUPLICATE KEY

This commit is contained in:
Michele Balistreri 2019-10-15 14:54:49 +03:00
parent b9fa61282e
commit 1b716790f0
No known key found for this signature in database
GPG Key ID: E9567DA33A4F791A
3 changed files with 0 additions and 235 deletions

View File

@ -36,8 +36,6 @@ public class Crypto {
private Signature hmacSHA512;
private HMACKey hmacKey;
private AESKey tmpAES256;
private byte[] hmacBlock;
Crypto() {
@ -47,8 +45,6 @@ public class Crypto {
sha512 = MessageDigest.getInstance(MessageDigest.ALG_SHA_512, false);
aesCbcIso9797m2 = Cipher.getInstance(Cipher.ALG_AES_CBC_ISO9797_M2,false);
tmpAES256 = (AESKey) KeyBuilder.buildKey(KeyBuilder.TYPE_AES_TRANSIENT_DESELECT, KeyBuilder.LENGTH_AES_256, false);
try {
hmacSHA512 = Signature.getInstance(Signature.ALG_HMAC_SHA_512, false);
hmacKey = (HMACKey) KeyBuilder.buildKey(KeyBuilder.TYPE_HMAC_TRANSIENT_DESELECT, KeyBuilder.LENGTH_AES_256, false);
@ -59,12 +55,6 @@ public class Crypto {
}
public short oneShotAES(byte mode, byte[] src, short sOff, short sLen, byte[] dst, short dOff, byte[] key, short keyOff) {
tmpAES256.setKey(key, keyOff);
aesCbcIso9797m2.init(tmpAES256, mode, src, sOff, AES_BLOCK_SIZE);
return aesCbcIso9797m2.doFinal(src, (short) (sOff + AES_BLOCK_SIZE), (short) (sLen - AES_BLOCK_SIZE), dst, dOff);
}
boolean bip32IsHardened(byte[] i, short iOff) {
return (i[iOff] & (byte) 0x80) == (byte) 0x80;
}

View File

@ -21,7 +21,6 @@ public class KeycardApplet extends Applet {
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;
@ -63,11 +62,6 @@ public class KeycardApplet extends Applet {
static final byte GENERATE_MNEMONIC_P1_CS_MAX = 8;
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 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;
static final byte SIGN_P1_CURRENT_KEY = 0x00;
static final byte SIGN_P1_DERIVE = 0x01;
static final byte SIGN_P1_DERIVE_AND_MAKE_CURRENT = 0x02;
@ -139,9 +133,6 @@ public class KeycardApplet extends Applet {
private Crypto crypto;
private SECP256k1 secp256k1;
private byte[] duplicationEncKey;
private short expectedEntropy;
private byte[] derivationOutput;
private byte[] data;
@ -201,9 +192,6 @@ public class KeycardApplet extends Applet {
signature = Signature.getInstance(Signature.ALG_ECDSA_SHA_256, false);
duplicationEncKey = new byte[(short)(KeyBuilder.LENGTH_AES_256/8)];
expectedEntropy = -1;
derivationOutput = JCSystem.makeTransientByteArray((short) (Crypto.KEY_SECRET_SIZE + CHAIN_CODE_SIZE), JCSystem.CLEAR_ON_RESET);
data = new byte[MAX_DATA_LENGTH + 1];
@ -278,9 +266,6 @@ public class KeycardApplet extends Applet {
case INS_GENERATE_KEY:
generateKey(apdu);
break;
case INS_DUPLICATE_KEY:
duplicateKey(apdu);
break;
case INS_SIGN:
sign(apdu);
break;
@ -1101,116 +1086,6 @@ public class KeycardApplet extends Applet {
generateKeyUIDAndRespond(apdu, apduBuffer);
}
/**
* Processes the DUPLICATE KEY command. The actual processing depends on the subcommand.
*
* @param apdu the JCRE-owned APDU object.
*/
private void duplicateKey(APDU apdu) {
byte[] apduBuffer = apdu.getBuffer();
if (apduBuffer[ISO7816.OFFSET_P1] == DUPLICATE_KEY_P1_ADD_ENTROPY) {
if (expectedEntropy <= 0) {
ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
}
secureChannel.oneShotDecrypt(apduBuffer);
addEntropy(apduBuffer);
return;
} else {
secureChannel.preprocessAPDU(apduBuffer);
if (!pin.isValidated()) {
ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
}
}
switch(apduBuffer[ISO7816.OFFSET_P1]) {
case DUPLICATE_KEY_P1_START:
startDuplication(apduBuffer);
break;
case DUPLICATE_KEY_P1_EXPORT:
short len = exportDuplicate(apduBuffer);
secureChannel.respond(apdu, len, ISO7816.SW_NO_ERROR);
break;
case DUPLICATE_KEY_P1_IMPORT:
importDuplicate(apduBuffer);
pinlessPathLen = 0;
generateKeyUIDAndRespond(apdu, apduBuffer);
break;
default:
ISOException.throwIt(ISO7816.SW_INCORRECT_P1P2);
break;
}
}
private void startDuplication(byte[] apduBuffer) {
if (apduBuffer[ISO7816.OFFSET_LC] != (short) duplicationEncKey.length) {
ISOException.throwIt(ISO7816.SW_WRONG_DATA);
}
JCSystem.beginTransaction();
Util.arrayCopy(apduBuffer, ISO7816.OFFSET_CDATA, duplicationEncKey, (short) 0, (short) duplicationEncKey.length);
expectedEntropy = (short) (apduBuffer[ISO7816.OFFSET_P2] - 1);
JCSystem.commitTransaction();
}
private void addEntropy(byte[] apduBuffer) {
if (apduBuffer[ISO7816.OFFSET_LC] != (short) duplicationEncKey.length) {
ISOException.throwIt(ISO7816.SW_WRONG_DATA);
}
JCSystem.beginTransaction();
for (short i = 0; i < (short) duplicationEncKey.length; i++) {
duplicationEncKey[i] ^= apduBuffer[(short) (ISO7816.OFFSET_CDATA + i)];
}
expectedEntropy--;
JCSystem.commitTransaction();
}
private void finalizeDuplicationKey() {
if (expectedEntropy != 0) {
ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
}
expectedEntropy = -1;
}
private short exportDuplicate(byte[] apduBuffer) {
finalizeDuplicationKey();
crypto.random.generateData(apduBuffer, SecureChannel.SC_OUT_OFFSET, Crypto.AES_BLOCK_SIZE);
short sOff = (short) (SecureChannel.SC_OUT_OFFSET + Crypto.AES_BLOCK_SIZE);
short off = sOff;
Util.arrayCopyNonAtomic(apduBuffer, SecureChannel.SC_OUT_OFFSET, apduBuffer, off, Crypto.AES_BLOCK_SIZE);
off += Crypto.AES_BLOCK_SIZE;
apduBuffer[off++] = TLV_KEY_TEMPLATE;
short keyTemplateLenOff = off++;
apduBuffer[off++] = TLV_PRIV_KEY;
apduBuffer[off] = (byte) masterPrivate.getS(apduBuffer, (short) (off + 1));
apduBuffer[keyTemplateLenOff] = (byte) (apduBuffer[off] + 2);
off += (short) (apduBuffer[off] + 1);
if (isExtended) {
apduBuffer[off++] = TLV_CHAIN_CODE;
apduBuffer[off++] = CHAIN_CODE_SIZE;
Util.arrayCopyNonAtomic(masterChainCode, (short) 0, apduBuffer, off, CHAIN_CODE_SIZE);
apduBuffer[keyTemplateLenOff] += (byte) (CHAIN_CODE_SIZE + 2);
off += CHAIN_CODE_SIZE;
}
return (short) (Crypto.AES_BLOCK_SIZE + crypto.oneShotAES(Cipher.MODE_ENCRYPT, apduBuffer, sOff, (short)(off - sOff), apduBuffer, sOff, duplicationEncKey, (short) 0));
}
private void importDuplicate(byte[] apduBuffer) {
finalizeDuplicationKey();
short len = crypto.oneShotAES(Cipher.MODE_DECRYPT, apduBuffer, ISO7816.OFFSET_CDATA, (short) (apduBuffer[ISO7816.OFFSET_LC] & 0xff), apduBuffer, ISO7816.OFFSET_CDATA, duplicationEncKey, (short) 0);
apduBuffer[ISO7816.OFFSET_LC] = (byte) len;
loadKeyPair(apduBuffer);
}
/**
* Processes the SIGN command. Requires a secure channel to open and either the PIN to be verified or the PIN-less key
* path to be the current key path. This command supports signing a precomputed 32-bytes hash. The signature is

View File

@ -1360,106 +1360,6 @@ public class KeycardTest {
assertArrayEquals(data, response.getData());
}
@Test
@DisplayName("DUPLICATE KEY command")
@Capabilities("keyManagement")
void duplicateTest() throws Exception {
int secretCount = 5;
Random random = new Random();
byte[][] secrets = new byte[secretCount][32];
for (int i = 0; i < secretCount; i++) {
random.nextBytes(secrets[i]);
}
// Security condition violation: SecureChannel not open
APDUResponse response = cmdSet.duplicateKeyStart(secretCount, secrets[0]);
assertEquals(0x6985, response.getSw());
cmdSet.autoOpenSecureChannel();
// Security condition violation: PIN not verified
response = cmdSet.duplicateKeyStart(secretCount, secrets[0]);
assertEquals(0x6985, response.getSw());
response = cmdSet.verifyPIN("000000");
assertEquals(0x9000, response.getSw());
response = cmdSet.generateKey();
assertEquals(0x9000, response.getSw());
byte[] keyUID = response.getData();
// Init duplication
response = cmdSet.duplicateKeyStart(secretCount, secrets[0]);
assertEquals(0x9000, response.getSw());
// Adding key entropy must work without secure channel and PIN authentication
reset();
response = cmdSet.select();
assertEquals(0x9000, response.getSw());
// Put all except the last piece of entropy
for (int i = 1; i < (secretCount - 1); i++) {
response = cmdSet.duplicateKeyAddEntropy(secrets[i]);
assertEquals(0x9000, response.getSw());
}
cmdSet.autoOpenSecureChannel();
response = cmdSet.verifyPIN("000000");
assertEquals(0x9000, response.getSw());
// Try to backup before enough entropy has been set
response = cmdSet.duplicateKeyExport();
assertEquals(0x6985, response.getSw());
reset();
response = cmdSet.select();
assertEquals(0x9000, response.getSw());
// Put last piece of entropy
response = cmdSet.duplicateKeyAddEntropy(secrets[(secretCount - 1)]);
assertEquals(0x9000, response.getSw());
// Try putting more entropy (failure expected)
response = cmdSet.duplicateKeyAddEntropy(secrets[(secretCount - 1)]);
assertEquals(0x6985, response.getSw());
cmdSet.autoOpenSecureChannel();
response = cmdSet.verifyPIN("000000");
assertEquals(0x9000, response.getSw());
// Backup
response = cmdSet.duplicateKeyExport();
assertEquals(0x9000, response.getSw());
byte[] backup = response.getData();
// Try to restore the backup (failure expected, session is over)
response = cmdSet.duplicateKeyImport(backup);
assertEquals(0x6985, response.getSw());
// Now try to restore the backup and check that the key UID matches, but first change the keys to random ones
response = cmdSet.generateKey();
assertEquals(0x9000, response.getSw());
response = cmdSet.duplicateKeyStart(secretCount, secrets[0]);
assertEquals(0x9000, response.getSw());
reset();
response = cmdSet.select();
assertEquals(0x9000, response.getSw());
for (int i = 1; i < secretCount; i++) {
response = cmdSet.duplicateKeyAddEntropy(secrets[i]);
assertEquals(0x9000, response.getSw());
}
cmdSet.autoOpenSecureChannel();
response = cmdSet.verifyPIN("000000");
assertEquals(0x9000, response.getSw());
response = cmdSet.duplicateKeyImport(backup);
assertEquals(0x9000, response.getSw());
assertArrayEquals(keyUID, response.getData());
}
@Test
@DisplayName("Test the Cash applet")
@Tag("manual")