update test to use pairing/unpairing

This commit is contained in:
Michele Balistreri 2017-11-15 12:11:01 +03:00
parent e11d817c64
commit a61369d1bc
4 changed files with 136 additions and 3 deletions

View File

@ -80,7 +80,7 @@ public class SecureChannel {
short len = Crypto.ecdh.generateSecret(apduBuffer, ISO7816.OFFSET_CDATA, apduBuffer[ISO7816.OFFSET_LC], secret, (short) 0);
Crypto.random.generateData(apduBuffer, (short) 0, SC_SECRET_LENGTH);
Crypto.sha256.update(secret, (short) 0, len);
Crypto.sha256.update(pairingSecret, pairingKeyOff, SC_SECRET_LENGTH);
Crypto.sha256.update(pairingKeys, pairingKeyOff, SC_SECRET_LENGTH);
Crypto.sha256.doFinal(apduBuffer, (short) 0, SC_SECRET_LENGTH, secret, (short) 0);
scKey.setKey(secret, (short) 0);
apdu.setOutgoingAndSend((short) 0, SC_SECRET_LENGTH);

View File

@ -14,6 +14,7 @@ import javax.smartcardio.CardException;
import javax.smartcardio.CommandAPDU;
import javax.smartcardio.ResponseAPDU;
import java.security.*;
import java.util.Arrays;
/**
* Handles a SecureChannel session with the card.
@ -23,6 +24,8 @@ public class SecureChannelSession {
private byte[] secret;
private byte[] publicKey;
private byte[] pairingKey;
private byte pairingIndex;
private Cipher sessionCipher;
private SecretKeySpec sessionKey;
private SecureRandom random;
@ -69,13 +72,14 @@ public class SecureChannelSession {
* @throws CardException communication error
*/
public ResponseAPDU openSecureChannel(CardChannel apduChannel) throws CardException {
CommandAPDU openSecureChannel = new CommandAPDU(0x80, SecureChannel.INS_OPEN_SECURE_CHANNEL, 0, 0, publicKey);
CommandAPDU openSecureChannel = new CommandAPDU(0x80, SecureChannel.INS_OPEN_SECURE_CHANNEL, pairingIndex, 0, publicKey);
ResponseAPDU response = apduChannel.transmit(openSecureChannel);
byte[] salt = response.getData();
try {
MessageDigest md = MessageDigest.getInstance("SHA256", "BC");
md.update(secret);
md.update(pairingKey);
sessionKey = new SecretKeySpec(md.digest(salt), "AES");
sessionCipher = Cipher.getInstance("AES/CBC/ISO7816-4Padding", "BC");
} catch(Exception e) {
@ -85,6 +89,98 @@ public class SecureChannelSession {
return response;
}
/**
* Handles the entire pairing procedure in order to be able to use the secure channel
*
* @param apduChannel the apdu channel
* @throws CardException communication error
*/
public void autoPair(CardChannel apduChannel, byte[] sharedSecret) throws CardException {
byte[] challenge = new byte[32];
random.nextBytes(challenge);
ResponseAPDU resp = pair(apduChannel, SecureChannel.PAIR_P1_FIRST_STEP, challenge);
if (resp.getSW() != 0x9000) {
throw new CardException("Pairing failed on step 1");
}
byte[] respData = resp.getData();
byte[] cardCryptogram = Arrays.copyOf(respData, 32);
byte[] cardChallenge = Arrays.copyOfRange(respData, 32, respData.length);
byte[] checkCryptogram;
MessageDigest md;
try {
md = MessageDigest.getInstance("SHA256", "BC");
} catch(Exception e) {
throw new RuntimeException("Is BouncyCastle in the classpath?", e);
}
md.update(sharedSecret);
checkCryptogram = md.digest(challenge);
if (!Arrays.equals(checkCryptogram, cardCryptogram)) {
throw new CardException("Invalid card cryptogram");
}
md.update(sharedSecret);
checkCryptogram = md.digest(cardChallenge);
resp = pair(apduChannel, SecureChannel.PAIR_P1_LAST_STEP, checkCryptogram);
if (resp.getSW() != 0x9000) {
throw new CardException("Pairing failed on step 2");
}
respData = resp.getData();
md.update(sharedSecret);
pairingKey = md.digest(Arrays.copyOfRange(respData, 1, respData.length));
pairingIndex = respData[0];
}
/**
* Unpairs the current paired key
*
* @param apduChannel the apdu channel
* @throws CardException communication error
*/
public void autoUnpair(CardChannel apduChannel) throws CardException {
ResponseAPDU resp = unpair(apduChannel, pairingIndex, new byte[] { pairingIndex });
if (resp.getSW() != 0x9000) {
throw new CardException("Unpairing failed");
}
}
/**
* Sends a PAIR APDU.
*
* @param apduChannel the apdu channel
* @param p1 the P1 parameter
* @param data the data
* @return the raw card response
* @throws CardException communication error
*/
public ResponseAPDU pair(CardChannel apduChannel, byte p1, byte[] data) throws CardException {
CommandAPDU openSecureChannel = new CommandAPDU(0x80, SecureChannel.INS_PAIR, p1, 0, data);
return apduChannel.transmit(openSecureChannel);
}
/**
* Sends a UNPAIR APDU.
*
* @param apduChannel the apdu channel
* @param p1 the P1 parameter
* @param data the data
* @return the raw card response
* @throws CardException communication error
*/
public ResponseAPDU unpair(CardChannel apduChannel, byte p1, byte[] data) throws CardException {
CommandAPDU openSecureChannel = new CommandAPDU(0x80, SecureChannel.INS_UNPAIR, p1, 0, encryptAPDU(data));
return apduChannel.transmit(openSecureChannel);
}
/**
* Encrypts the plaintext data using the session key. The maximum plaintext size is 223 bytes. The returned ciphertext
* already includes the IV and padding and can be sent as-is in the APDU payload. If the input is an empty byte array

View File

@ -55,6 +55,38 @@ public class WalletAppletCommandSet {
return secureChannel.openSecureChannel(apduChannel);
}
/**
* Automatically pairs. Calls the corresponding method of the SecureChannel class.
*
* @throws CardException communication error
*/
public void autoPair(byte[] sharedSecret) throws CardException {
secureChannel.autoPair(apduChannel, sharedSecret);
}
/**
* Automatically unpairs. Calls the corresponding method of the SecureChannel class.
*
* @throws CardException communication error
*/
public void autoUnpair() throws CardException {
secureChannel.autoUnpair(apduChannel);
}
/**
* Sends a PAIR APDU. Calls the corresponding method of the SecureChannel class.
*/
public ResponseAPDU pair(byte p1, byte[] data) throws CardException {
return secureChannel.pair(apduChannel, p1, data);
}
/**
* Sends a UNPAIR APDU. Calls the corresponding method of the SecureChannel class.
*/
public ResponseAPDU unpair(byte p1, byte[] data) throws CardException {
return secureChannel.unpair(apduChannel, p1, data);
}
/**
* Sends a GET STATUS APDU. The info byte is the P1 parameter of the command, valid constants are defined in the applet
* class itself.

View File

@ -87,10 +87,15 @@ public class WalletAppletTest {
byte[] keyData = cmdSet.select().getData();
secureChannel = new SecureChannelSession(keyData);
cmdSet.setSecureChannel(secureChannel);
cmdSet.autoPair(sha256("123456789012".getBytes()));
}
@AfterEach
void tearDown() {
void tearDown() throws CardException {
resetAndSelectAndOpenSC();
ResponseAPDU response = cmdSet.verifyPIN("000000");
assertEquals(0x9000, response.getSW());
cmdSet.autoUnpair();
}
@AfterAll