add PUT KEY command

This commit is contained in:
Michele Balistreri
2019-04-05 12:22:00 +03:00
parent a4ff736d6e
commit 205f150705
4 changed files with 75 additions and 4 deletions
@@ -121,6 +121,21 @@ public class Crypto {
}
}
public static byte[] ecb3des(byte[] key, byte[] data) {
try {
Cipher cipher = Cipher.getInstance("DES/ECB/NoPadding", "BC");
SecretKeySpec keyDes = new SecretKeySpec(resizeKey24(key), "DES");
cipher.init(Cipher.ENCRYPT_MODE, keyDes);
return cipher.doFinal(data);
} catch (GeneralSecurityException e) {
throw new RuntimeException("Could not encrypt data", e);
}
}
public static byte[] kcv3des(byte[] key) {
return Arrays.copyOf(ecb3des(key, NullBytes8), 3);
}
/**
* Generates a 3DES MAC for SCP02 communication
*
@@ -24,6 +24,7 @@ public class GlobalPlatformCommandSet {
static final byte INS_DELETE = (byte) 0xE4;
static final byte INS_INSTALL = (byte) 0xE6;
static final byte INS_LOAD = (byte) 0xE8;
static final byte INS_PUT_KEY = (byte) 0xD8;
static final byte SELECT_P1_BY_NAME = (byte) 0x04;
static final byte EXTERNAL_AUTHENTICATE_P1 = (byte) 0x01;
@@ -46,7 +47,7 @@ public class GlobalPlatformCommandSet {
*/
public GlobalPlatformCommandSet(CardChannel apduChannel) {
this.apduChannel = apduChannel;
this.cardKeys = new SCP02Keys(testKey, testKey);
this.cardKeys = new SCP02Keys(testKey, testKey, testKey);
}
/**
@@ -117,6 +118,49 @@ public class GlobalPlatformCommandSet {
externalAuthenticate(hostChallenge).checkOK();
}
/**
* Sends a PUT KEY APDU to load or replace SCP02 keys. The keys are assumed to be 3DES keys
*
* @param encKey the ENC key to load
* @param macKey the MAC key to load
* @param dekKey the DEK key to load
* @param oldKvn the KVN to replace, 0 to put a new key without replacing
* @param newKvn the KVN of the new keyset
* @return
* @throws IOException
*/
public APDUResponse putSCP02Keys(byte[] encKey, byte[] macKey, byte[] dekKey, int oldKvn, int newKvn) throws IOException {
if (encKey.length != 16 || macKey.length != 16 || dekKey.length != 16){
throw new IllegalArgumentException("All keys must be 16-byte 3DES keys");
}
ByteArrayOutputStream bos = new ByteArrayOutputStream();
bos.write(newKvn);
writeSCP02Key(bos, encKey);
writeSCP02Key(bos, macKey);
writeSCP02Key(bos, dekKey);
APDUCommand cmd = new APDUCommand(0x84, INS_PUT_KEY, oldKvn, 0, bos.toByteArray());
return this.secureChannel.send(cmd);
}
/**
* writes an encrypted key for the PUT KEY command
* @param bos the output stream to write to
* @param key the key to encrypt and write
* @throws IOException if the ByteArrayOutputStream throws it (never)
*/
private void writeSCP02Key(ByteArrayOutputStream bos, byte[] key) throws IOException {
byte[] encrypted = Crypto.ecb3des(session.getKeys().getDekKeyData(), key);
byte[] kcv = Crypto.kcv3des(key);
bos.write(0x80);
bos.write(encrypted.length);
bos.write(encrypted);
bos.write(kcv.length);
bos.write(kcv);
}
/**
* Deletes the Keycard applet instance.
*
@@ -6,14 +6,16 @@ package im.status.keycard.globalplatform;
public class SCP02Keys {
public byte[] encKeyData;
public byte[] macKeyData;
public byte[] dekKeyData;
/**
* Constructor. Takes the ENC and MAC keys.
*
* @param encKeyData encryption key
* @param macKeyData mac key
* @param dekKeyData data encryption key
*/
public SCP02Keys(byte[] encKeyData, byte[] macKeyData) {
public SCP02Keys(byte[] encKeyData, byte[] macKeyData, byte[] dekKeyData) {
this.encKeyData = encKeyData;
this.macKeyData = macKeyData;
}
@@ -34,4 +36,13 @@ public class SCP02Keys {
public byte[] getMacKeyData() {
return macKeyData;
}
/**
* The DEK key
*
* @return the DEK key
*/
public byte[] getDekKeyData() {
return dekKeyData;
}
}
@@ -8,7 +8,7 @@ import im.status.keycard.io.APDUResponse;
import im.status.keycard.io.CardChannel;
/**
* An SCP02 Secure Channel. Wraps a CardChannel to allow transparent handling of the scure channel.
* An SCP02 Secure Channel. Wraps a CardChannel to allow transparent handling of the secure channel.
*/
public class SecureChannel {
private CardChannel channel;
@@ -77,8 +77,9 @@ public class SecureChannel {
byte[] sessionEncKey = Crypto.deriveSCP02SessionKey(cardKeys.getEncKeyData(), seq, DERIVATION_PURPOSE_ENC);
byte[] sessionMacKey = Crypto.deriveSCP02SessionKey(cardKeys.getMacKeyData(), seq, DERIVATION_PURPOSE_MAC);
byte[] sessionDekKey = Crypto.deriveSCP02SessionKey(cardKeys.getDekKeyData(), seq, DERIVATION_PURPOSE_DEK);
SCP02Keys sessionKeys = new SCP02Keys(sessionEncKey, sessionMacKey);
SCP02Keys sessionKeys = new SCP02Keys(sessionEncKey, sessionMacKey, sessionDekKey);
boolean verified = Crypto.verifyCryptogram(sessionKeys.getEncKeyData(), hostChallenge, cardChallenge, cardCryptogram);
if (!verified) {