Skip to content

Commit

Permalink
fix mutual authentication algorithm based on the current MS VSC imple…
Browse files Browse the repository at this point in the history
…mentation

GIDS specification wasn't explicit about step 4 (R1|R2|Z2)
Fix also the test cases
  • Loading branch information
vletoux committed Sep 1, 2017
1 parent f467573 commit cf3658d
Show file tree
Hide file tree
Showing 3 changed files with 75 additions and 15 deletions.
39 changes: 32 additions & 7 deletions src/com/mysmartlogon/gidsApplet/GidsPINManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -463,6 +463,7 @@ private boolean CheckForExternalChallenge(APDU apdu, byte[] buf, short innerPos,
ISOException.throwIt(ISO7816.SW_DATA_INVALID);
}
RandomData randomData = RandomData.getInstance(RandomData.ALG_SECURE_RANDOM);
randomData.setSeed(buf, pos, len);
randomData.generateData(CardChallenge, (short) 0, len);

pos = 0;
Expand Down Expand Up @@ -494,6 +495,10 @@ private boolean CheckForChallengeResponse(APDU apdu, byte[] buf, short innerPos,
ISOException.throwIt(ISO7816.SW_DATA_INVALID);
}
if (status[0] == MUTUAL_CHALLENGE) {
if (len != (short) 40) {
ClearChallengeData();
ISOException.throwIt(ISO7816.SW_SECURITY_STATUS_NOT_SATISFIED);
}
Cipher cipherDES = Cipher.getInstance(Cipher.ALG_DES_CBC_NOPAD, false);
DESKey key = (DESKey) KeyBuilder.buildKey(KeyBuilder.TYPE_DES, KeyBuilder.LENGTH_DES3_3KEY, false);
key.setKey(((CRTKeyFile)(KeyReference[0])).GetSymmectricKey(), (short) 0);
Expand All @@ -510,23 +515,43 @@ private boolean CheckForChallengeResponse(APDU apdu, byte[] buf, short innerPos,
ClearChallengeData();
ISOException.throwIt(ISO7816.SW_SECURITY_STATUS_NOT_SATISFIED);
}
Util.arrayCopy(buffer, (short) 32, sharedKey, (short) 0, (short) (len - 32));
// check the padding of Z1 (7 bytes)
if (buffer[(short)39] != (byte) 0x80) {
ClearChallengeData();
ISOException.throwIt(ISO7816.SW_SECURITY_STATUS_NOT_SATISFIED);
}
// copy Z1 for later use
Util.arrayCopy(buffer, (short) 32, sharedKey, (short) 0, (short) 7);

// generate Z2
RandomData randomData = RandomData.getInstance(RandomData.ALG_SECURE_RANDOM);
randomData.generateData(sharedKey, (short) (len - 32), (short) (len - 32));
randomData.generateData(sharedKey, (short) 7, (short) 7);

Util.arrayCopy(buffer, (short) 32, sharedKey, (short) (len - 32), (short) (len - 32));
// copy R1
Util.arrayCopy(ExternalChallenge, (short) 0, buffer, (short) 0, (short) 16);
// copy R2
Util.arrayCopy(CardChallenge, (short) 0, buffer, (short) 16, (short) 16);
// copy Z2
Util.arrayCopy(sharedKey, (short) 7, buffer, (short) 32, (short) 7);
// set padding for Z2 (7 bytes)
buffer[(short) 39] = (byte) 0x80;

cipherDES.init(key, Cipher.MODE_ENCRYPT);
cipherDES.doFinal(buffer, (short) 0, len, buf, (short) 0);

cipherDES.doFinal(buffer, (short) 0, (short)40, buf, (short) 4);

// header
buf[0] = (byte) 0x7C;
buf[1] = (byte) 0x2A;
buf[2] = (byte) 0x82;
buf[3] = (byte) 0x28;

// avoid replay attack
ClearChallengeData();
status[0] = MUTUAL_AUTHENTICATED;

apdu.setOutgoing();
apdu.setOutgoingLength(len);
apdu.sendBytes((short) 0, len);
apdu.setOutgoingLength((short)44);
apdu.sendBytes((short) 0, (short)44);
} else if (status[0] == EXTERNAL_CHALLENGE) {
Cipher cipherDES = Cipher.getInstance(Cipher.ALG_DES_CBC_NOPAD, false);
DESKey key = (DESKey) KeyBuilder.buildKey(KeyBuilder.TYPE_DES, KeyBuilder.LENGTH_DES3_3KEY, false);
Expand Down
49 changes: 42 additions & 7 deletions src/com/mysmartlogon/gidsAppletTests/GidsBaseTestClass.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
import static org.junit.Assert.*;

import java.util.Arrays;
import java.util.Date;
import java.util.Random;

import javax.smartcardio.CommandAPDU;
import javax.smartcardio.ResponseAPDU;
Expand Down Expand Up @@ -97,12 +99,11 @@ protected void deauthenticate() {

protected void authenticateMutual(byte[] key, boolean successexpected) {
byte[] myChallenge= new byte [16], globalchallenge = new byte[40], challengeresponse = new byte[40];
byte[] challenge;
byte[] cardChallenge;
Cipher cipherDES = Cipher.getInstance(Cipher.ALG_DES_CBC_NOPAD, false);
DESKey deskey = (DESKey) KeyBuilder.buildKey(KeyBuilder.TYPE_DES, KeyBuilder.LENGTH_DES3_3KEY, false);
deskey.setKey(key, (short) 0);
RandomData randomData = RandomData.getInstance(RandomData.ALG_SECURE_RANDOM);
randomData.generateData(myChallenge, (short) 0, (short) myChallenge.length);
new Random().nextBytes(myChallenge);
// select admin key
execute("00 22 81 A4 03 83 01 80");
// get a challenge
Expand All @@ -111,18 +112,52 @@ protected void authenticateMutual(byte[] key, boolean successexpected) {
fail("not a challenge:" + DatatypeConverter.printHexBinary(response.getBytes()));
}
// compute the response
challenge = Arrays.copyOfRange(response.getBytes(), 4, 20);
cardChallenge = Arrays.copyOfRange(response.getBytes(), 4, 20);
//solve challenge
//R2
System.arraycopy(challenge, 0, globalchallenge, 0, 16);
System.arraycopy(cardChallenge, 0, globalchallenge, 0, 16);
//R1
System.arraycopy(myChallenge, 0, globalchallenge, 16, 16);
// keep Z1 random

globalchallenge[(short)39] = (byte) 0x80;
cipherDES.init(deskey, Cipher.MODE_ENCRYPT);
cipherDES.doFinal(globalchallenge, (short) 0, (short)40, challengeresponse, (short) 0);
// send the response
execute("00 87 00 00 2C 7C 2A 82 28" + DatatypeConverter.printHexBinary(challengeresponse), (successexpected?0x9000: 0x6982));
String command = "00 87 00 00 2C 7C 2A 82 28" + DatatypeConverter.printHexBinary(challengeresponse);

ResponseAPDU responseAPDU = execute(command, true);

if (!successexpected)
{
if(responseAPDU.getSW() != 0x6982) {
fail("expected: " + Integer.toHexString(0x6982) + " but was: " + Integer.toHexString(response.getSW()));
}
return;
}
if(responseAPDU.getSW() != 0x9000) {
fail("expected: " + Integer.toHexString(0x9000) + " but was: " + Integer.toHexString(response.getSW()));
}
byte[] cardresponse = responseAPDU.getBytes();
if (!Arrays.equals(Arrays.copyOfRange(cardresponse, 0, 4), new byte[] {0x7C,0x2A,(byte)0x82,0x28}))
{
fail("header verification failed");
}
byte[] decryptedCardResponse = new byte[40];
cipherDES.init(deskey, Cipher.MODE_DECRYPT);
cipherDES.doFinal(cardresponse, (short) 4, (short)40, decryptedCardResponse, (short) 0);


if (!Arrays.equals(Arrays.copyOfRange(decryptedCardResponse, 0, 16), myChallenge)) {
fail("R1 verification failed");
}

if (!Arrays.equals(Arrays.copyOfRange(decryptedCardResponse, 16, 32), cardChallenge)) {
fail("R2 verification failed");
}
if (decryptedCardResponse[(short)39] != (byte) 0x80) {
fail("padding failed");
}

}

protected void authenticateGeneral(byte[] key, boolean successexpected) {
Expand Down
2 changes: 1 addition & 1 deletion src/com/mysmartlogon/gidsAppletTests/PinTests.java
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,7 @@ public void authenticateMutualReplayAttack() {
//R1
System.arraycopy(myChallenge, 0, globalchallenge, 16, 16);
// keep Z1 random

globalchallenge[(short)39] = (byte) 0x80;
cipherDES.init(deskey, Cipher.MODE_ENCRYPT);
cipherDES.doFinal(globalchallenge, (short) 0, (short)40, challengeresponse, (short) 0);
// send the response
Expand Down

0 comments on commit cf3658d

Please sign in to comment.