From 205f1507052644ec8ee7241b4f08c68e10b7d5ce Mon Sep 17 00:00:00 2001 From: Michele Balistreri Date: Fri, 5 Apr 2019 12:22:00 +0300 Subject: [PATCH] add PUT KEY command --- .../status/keycard/globalplatform/Crypto.java | 15 ++++++ .../GlobalPlatformCommandSet.java | 46 ++++++++++++++++++- .../keycard/globalplatform/SCP02Keys.java | 13 +++++- .../keycard/globalplatform/SecureChannel.java | 5 +- 4 files changed, 75 insertions(+), 4 deletions(-) diff --git a/lib/src/main/java/im/status/keycard/globalplatform/Crypto.java b/lib/src/main/java/im/status/keycard/globalplatform/Crypto.java index 71aeec8..fe15379 100644 --- a/lib/src/main/java/im/status/keycard/globalplatform/Crypto.java +++ b/lib/src/main/java/im/status/keycard/globalplatform/Crypto.java @@ -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 * diff --git a/lib/src/main/java/im/status/keycard/globalplatform/GlobalPlatformCommandSet.java b/lib/src/main/java/im/status/keycard/globalplatform/GlobalPlatformCommandSet.java index c392f08..b01c24d 100644 --- a/lib/src/main/java/im/status/keycard/globalplatform/GlobalPlatformCommandSet.java +++ b/lib/src/main/java/im/status/keycard/globalplatform/GlobalPlatformCommandSet.java @@ -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. * diff --git a/lib/src/main/java/im/status/keycard/globalplatform/SCP02Keys.java b/lib/src/main/java/im/status/keycard/globalplatform/SCP02Keys.java index de8aa76..5d872e8 100644 --- a/lib/src/main/java/im/status/keycard/globalplatform/SCP02Keys.java +++ b/lib/src/main/java/im/status/keycard/globalplatform/SCP02Keys.java @@ -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; + } } diff --git a/lib/src/main/java/im/status/keycard/globalplatform/SecureChannel.java b/lib/src/main/java/im/status/keycard/globalplatform/SecureChannel.java index e30dba7..0f95820 100644 --- a/lib/src/main/java/im/status/keycard/globalplatform/SecureChannel.java +++ b/lib/src/main/java/im/status/keycard/globalplatform/SecureChannel.java @@ -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) {