Merge pull request #36 from status-im/pos-enabled

Keycard v2.2
This commit is contained in:
Bitgamma 2019-04-04 10:21:40 +03:00 committed by GitHub
commit c574944153
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 184 additions and 31 deletions

View File

@ -54,7 +54,7 @@ dependencies {
testCompile(files("../jcardsim/jcardsim-3.0.5-SNAPSHOT.jar")) testCompile(files("../jcardsim/jcardsim-3.0.5-SNAPSHOT.jar"))
testCompile('org.web3j:core:2.3.1') testCompile('org.web3j:core:2.3.1')
testCompile('org.bitcoinj:bitcoinj-core:0.14.5') testCompile('org.bitcoinj:bitcoinj-core:0.14.5')
testCompile('com.github.status-im.status-keycard-java:desktop:425d085') testCompile('com.github.status-im.status-keycard-java:desktop:4ec4e07')
testCompile('org.bouncycastle:bcprov-jdk15on:1.60') testCompile('org.bouncycastle:bcprov-jdk15on:1.60')
testCompile("org.junit.jupiter:junit-jupiter-api:5.1.1") testCompile("org.junit.jupiter:junit-jupiter-api:5.1.1")
testRuntime("org.junit.jupiter:junit-jupiter-engine:5.1.1") testRuntime("org.junit.jupiter:junit-jupiter-engine:5.1.1")

View File

@ -8,7 +8,7 @@ import javacardx.crypto.Cipher;
* The applet's main class. All incoming commands a processed by this class. * The applet's main class. All incoming commands a processed by this class.
*/ */
public class KeycardApplet extends Applet { public class KeycardApplet extends Applet {
static final short APPLICATION_VERSION = (short) 0x0201; static final short APPLICATION_VERSION = (short) 0x0202;
static final byte INS_GET_STATUS = (byte) 0xF2; static final byte INS_GET_STATUS = (byte) 0xF2;
static final byte INS_SET_NDEF = (byte) 0xF3; static final byte INS_SET_NDEF = (byte) 0xF3;
@ -26,6 +26,8 @@ public class KeycardApplet extends Applet {
static final byte INS_SET_PINLESS_PATH = (byte) 0xC1; static final byte INS_SET_PINLESS_PATH = (byte) 0xC1;
static final byte INS_EXPORT_KEY = (byte) 0xC2; static final byte INS_EXPORT_KEY = (byte) 0xC2;
static final short SW_REFERENCED_DATA_NOT_FOUND = (short) 0x6A88;
static final byte PUK_LENGTH = 12; static final byte PUK_LENGTH = 12;
static final byte PUK_MAX_RETRIES = 5; static final byte PUK_MAX_RETRIES = 5;
static final byte PIN_LENGTH = 6; static final byte PIN_LENGTH = 6;
@ -63,6 +65,11 @@ public class KeycardApplet extends Applet {
static final byte DUPLICATE_KEY_P1_EXPORT = 0x02; static final byte DUPLICATE_KEY_P1_EXPORT = 0x02;
static final byte DUPLICATE_KEY_P1_IMPORT = 0x03; 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;
static final byte SIGN_P1_PINLESS = 0x03;
static final byte EXPORT_KEY_P1_CURRENT = 0x00; static final byte EXPORT_KEY_P1_CURRENT = 0x00;
static final byte EXPORT_KEY_P1_DERIVE = 0x01; static final byte EXPORT_KEY_P1_DERIVE = 0x01;
static final byte EXPORT_KEY_P1_DERIVE_AND_MAKE_CURRENT = 0x02; static final byte EXPORT_KEY_P1_DERIVE_AND_MAKE_CURRENT = 0x02;
@ -113,6 +120,9 @@ public class KeycardApplet extends Applet {
private ECPrivateKey privateKey; private ECPrivateKey privateKey;
private byte[] chainCode; private byte[] chainCode;
private ECPublicKey pinlessPublicKey;
private ECPrivateKey pinlessPrivateKey;
private byte[] keyPath; private byte[] keyPath;
private short keyPathLen; private short keyPathLen;
@ -171,6 +181,9 @@ public class KeycardApplet extends Applet {
publicKey = (ECPublicKey) KeyBuilder.buildKey(KeyBuilder.TYPE_EC_FP_PUBLIC, SECP256k1.SECP256K1_KEY_SIZE, false); publicKey = (ECPublicKey) KeyBuilder.buildKey(KeyBuilder.TYPE_EC_FP_PUBLIC, SECP256k1.SECP256K1_KEY_SIZE, false);
privateKey = (ECPrivateKey) KeyBuilder.buildKey(KeyBuilder.TYPE_EC_FP_PRIVATE, SECP256k1.SECP256K1_KEY_SIZE, false); privateKey = (ECPrivateKey) KeyBuilder.buildKey(KeyBuilder.TYPE_EC_FP_PRIVATE, SECP256k1.SECP256K1_KEY_SIZE, false);
pinlessPublicKey = (ECPublicKey) KeyBuilder.buildKey(KeyBuilder.TYPE_EC_FP_PUBLIC, SECP256k1.SECP256K1_KEY_SIZE, false);
pinlessPrivateKey = (ECPrivateKey) KeyBuilder.buildKey(KeyBuilder.TYPE_EC_FP_PRIVATE, SECP256k1.SECP256K1_KEY_SIZE, false);
masterChainCode = new byte[CHAIN_CODE_SIZE]; masterChainCode = new byte[CHAIN_CODE_SIZE];
parentChainCode = new byte[CHAIN_CODE_SIZE]; parentChainCode = new byte[CHAIN_CODE_SIZE];
chainCode = new byte[CHAIN_CODE_SIZE]; chainCode = new byte[CHAIN_CODE_SIZE];
@ -646,6 +659,7 @@ public class KeycardApplet extends Applet {
break; break;
} }
pinlessPathLen = 0;
generateKeyUIDAndRespond(apdu, apduBuffer); generateKeyUIDAndRespond(apdu, apduBuffer);
} }
@ -789,21 +803,22 @@ public class KeycardApplet extends Applet {
byte[] apduBuffer = apdu.getBuffer(); byte[] apduBuffer = apdu.getBuffer();
short len = secureChannel.preprocessAPDU(apduBuffer); short len = secureChannel.preprocessAPDU(apduBuffer);
if (!((pin.isValidated() || (pinlessPathLen > 0)))) { if (!pin.isValidated()) {
ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
} }
doDerive(apduBuffer, len, apduBuffer[ISO7816.OFFSET_P1], true); doDerive(apduBuffer, (short) 0, len, apduBuffer[ISO7816.OFFSET_P1], true);
} }
/** /**
* Internal derivation function, called by DERIVE KEY and EXPORT KEY * Internal derivation function, called by DERIVE KEY and EXPORT KEY
* @param apduBuffer the APDU buffer * @param apduBuffer the APDU buffer
* @param len the APDU len * @param off the offset in the APDU buffer relative to the data field
* @param len the len of the path
* @param source derivation source * @param source derivation source
* @param makeCurrent whether the results should be saved or not * @param makeCurrent whether the results should be saved or not
*/ */
private void doDerive(byte[] apduBuffer, short len, byte source, boolean makeCurrent) { private void doDerive(byte[] apduBuffer, short off, short len, byte source, boolean makeCurrent) {
if (!isExtended) { if (!isExtended) {
ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
} }
@ -862,7 +877,8 @@ public class KeycardApplet extends Applet {
ISOException.throwIt(ISO7816.SW_WRONG_DATA); ISOException.throwIt(ISO7816.SW_WRONG_DATA);
} }
short scratchOff = (short) (ISO7816.OFFSET_CDATA + len); short pathOff = (short) (ISO7816.OFFSET_CDATA + off);
short scratchOff = (short) (pathOff + len);
short dataOff = (short) (scratchOff + Crypto.KEY_DERIVATION_SCRATCH_SIZE); short dataOff = (short) (scratchOff + Crypto.KEY_DERIVATION_SCRATCH_SIZE);
short pubKeyOff = (short) (dataOff + sourcePriv.getS(apduBuffer, dataOff)); short pubKeyOff = (short) (dataOff + sourcePriv.getS(apduBuffer, dataOff));
@ -874,8 +890,8 @@ public class KeycardApplet extends Applet {
apduBuffer[pubKeyOff] = 0; apduBuffer[pubKeyOff] = 0;
} }
for (short i = ISO7816.OFFSET_CDATA; i < scratchOff; i += 4) { for (short i = pathOff; i < scratchOff; i += 4) {
if (i > ISO7816.OFFSET_CDATA) { if (i > pathOff) {
Util.arrayCopyNonAtomic(derivationOutput, (short) 0, apduBuffer, dataOff, (short) (Crypto.KEY_SECRET_SIZE + CHAIN_CODE_SIZE)); Util.arrayCopyNonAtomic(derivationOutput, (short) 0, apduBuffer, dataOff, (short) (Crypto.KEY_SECRET_SIZE + CHAIN_CODE_SIZE));
if (!crypto.bip32IsHardened(apduBuffer, i)) { if (!crypto.bip32IsHardened(apduBuffer, i)) {
@ -908,7 +924,7 @@ public class KeycardApplet extends Applet {
secp256k1.derivePublicKey(privateKey, apduBuffer, scratchOff); secp256k1.derivePublicKey(privateKey, apduBuffer, scratchOff);
publicKey.setW(apduBuffer, scratchOff, Crypto.KEY_PUB_SIZE); publicKey.setW(apduBuffer, scratchOff, Crypto.KEY_PUB_SIZE);
Util.arrayCopy(apduBuffer, ISO7816.OFFSET_CDATA, keyPath, pathLenOff, len); Util.arrayCopy(apduBuffer, pathOff, keyPath, pathLenOff, len);
keyPathLen = newPathLen; keyPathLen = newPathLen;
JCSystem.commitTransaction(); JCSystem.commitTransaction();
} }
@ -1030,6 +1046,8 @@ public class KeycardApplet extends Applet {
masterPublic.clearKey(); masterPublic.clearKey();
parentPrivateKey.clearKey(); parentPrivateKey.clearKey();
parentPublicKey.clearKey(); parentPublicKey.clearKey();
pinlessPrivateKey.clearKey();
pinlessPublicKey.clearKey();
resetCurveParameters(); resetCurveParameters();
Util.arrayFillNonAtomic(chainCode, (short) 0, (short) chainCode.length, (byte) 0); Util.arrayFillNonAtomic(chainCode, (short) 0, (short) chainCode.length, (byte) 0);
Util.arrayFillNonAtomic(parentChainCode, (short) 0, (short) parentChainCode.length, (byte) 0); Util.arrayFillNonAtomic(parentChainCode, (short) 0, (short) parentChainCode.length, (byte) 0);
@ -1057,6 +1075,7 @@ public class KeycardApplet extends Applet {
crypto.random.generateData(apduBuffer, ISO7816.OFFSET_CDATA, BIP39_SEED_SIZE); crypto.random.generateData(apduBuffer, ISO7816.OFFSET_CDATA, BIP39_SEED_SIZE);
loadSeed(apduBuffer); loadSeed(apduBuffer);
pinlessPathLen = 0;
generateKeyUIDAndRespond(apdu, apduBuffer); generateKeyUIDAndRespond(apdu, apduBuffer);
} }
@ -1094,6 +1113,7 @@ public class KeycardApplet extends Applet {
break; break;
case DUPLICATE_KEY_P1_IMPORT: case DUPLICATE_KEY_P1_IMPORT:
importDuplicate(apduBuffer); importDuplicate(apduBuffer);
pinlessPathLen = 0;
generateKeyUIDAndRespond(apdu, apduBuffer); generateKeyUIDAndRespond(apdu, apduBuffer);
break; break;
default: default:
@ -1180,32 +1200,96 @@ public class KeycardApplet extends Applet {
*/ */
private void sign(APDU apdu) { private void sign(APDU apdu) {
byte[] apduBuffer = apdu.getBuffer(); byte[] apduBuffer = apdu.getBuffer();
short len = secureChannel.preprocessAPDU(apduBuffer); boolean usePinless = false;
boolean derive = false;
boolean makeCurrent = false;
if (!((pin.isValidated() || isPinless()) && privateKey.isInitialized())) { ECPrivateKey signingKey;
ECPublicKey outputKey;
switch((byte) (apduBuffer[ISO7816.OFFSET_P1] & ~DERIVE_P1_SOURCE_MASK)) {
case SIGN_P1_CURRENT_KEY:
signingKey = privateKey;
outputKey = publicKey;
break;
case SIGN_P1_DERIVE:
signingKey = secp256k1.tmpECPrivateKey;
outputKey = null;
derive = true;
break;
case SIGN_P1_DERIVE_AND_MAKE_CURRENT:
signingKey = privateKey;
outputKey = publicKey;
derive = true;
makeCurrent = true;
break;
case SIGN_P1_PINLESS:
usePinless = true;
signingKey = pinlessPrivateKey;
outputKey = pinlessPublicKey;
break;
default:
ISOException.throwIt(ISO7816.SW_WRONG_P1P2);
return;
}
short len;
if (usePinless && !secureChannel.isOpen()) {
len = (short) (apduBuffer[ISO7816.OFFSET_LC] & (short) 0xff);
} else {
len = secureChannel.preprocessAPDU(apduBuffer);
}
if (usePinless && pinlessPathLen == 0) {
ISOException.throwIt(SW_REFERENCED_DATA_NOT_FOUND);
}
if (!((pin.isValidated() || usePinless || isPinless()) && privateKey.isInitialized())) {
ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
} }
if (len != MessageDigest.LENGTH_SHA_256) { if (derive) {
ISOException.throwIt(ISO7816.SW_WRONG_DATA); short pathLen = (short) (len - MessageDigest.LENGTH_SHA_256);
}
signature.init(privateKey, Signature.MODE_SIGN); if (pathLen <= 0) {
ISOException.throwIt(ISO7816.SW_WRONG_DATA);
}
byte derivationSource = (byte) (apduBuffer[ISO7816.OFFSET_P1] & DERIVE_P1_SOURCE_MASK);
doDerive(apduBuffer, MessageDigest.LENGTH_SHA_256, pathLen, derivationSource, makeCurrent);
} else {
if (len != MessageDigest.LENGTH_SHA_256) {
ISOException.throwIt(ISO7816.SW_WRONG_DATA);
}
}
apduBuffer[SecureChannel.SC_OUT_OFFSET] = TLV_SIGNATURE_TEMPLATE; apduBuffer[SecureChannel.SC_OUT_OFFSET] = TLV_SIGNATURE_TEMPLATE;
apduBuffer[(short)(SecureChannel.SC_OUT_OFFSET + 3)] = TLV_PUB_KEY; apduBuffer[(short)(SecureChannel.SC_OUT_OFFSET + 3)] = TLV_PUB_KEY;
short outLen = apduBuffer[(short)(SecureChannel.SC_OUT_OFFSET + 4)] = (byte) publicKey.getW(apduBuffer, (short) (SecureChannel.SC_OUT_OFFSET + 5)); short outLen = apduBuffer[(short)(SecureChannel.SC_OUT_OFFSET + 4)] = Crypto.KEY_PUB_SIZE;
if (outputKey != null) {
outputKey.getW(apduBuffer, (short) (SecureChannel.SC_OUT_OFFSET + 5));
} else {
secp256k1.derivePublicKey(derivationOutput, (short) 0, apduBuffer, (short) (SecureChannel.SC_OUT_OFFSET + 5));
}
outLen += 5; outLen += 5;
short sigOff = (short) (SecureChannel.SC_OUT_OFFSET + outLen); short sigOff = (short) (SecureChannel.SC_OUT_OFFSET + outLen);
outLen += signature.signPreComputedHash(apduBuffer, ISO7816.OFFSET_CDATA, len, apduBuffer, sigOff); signature.init(signingKey, Signature.MODE_SIGN);
outLen += signature.signPreComputedHash(apduBuffer, ISO7816.OFFSET_CDATA, MessageDigest.LENGTH_SHA_256, apduBuffer, sigOff);
outLen += crypto.fixS(apduBuffer, sigOff); outLen += crypto.fixS(apduBuffer, sigOff);
apduBuffer[(short)(SecureChannel.SC_OUT_OFFSET + 1)] = (byte) 0x81; apduBuffer[(short)(SecureChannel.SC_OUT_OFFSET + 1)] = (byte) 0x81;
apduBuffer[(short)(SecureChannel.SC_OUT_OFFSET + 2)] = (byte) (outLen - 3); apduBuffer[(short)(SecureChannel.SC_OUT_OFFSET + 2)] = (byte) (outLen - 3);
secureChannel.respond(apdu, outLen, ISO7816.SW_NO_ERROR); if (secureChannel.isOpen()) {
secureChannel.respond(apdu, outLen, ISO7816.SW_NO_ERROR);
} else {
apdu.setOutgoingAndSend(SecureChannel.SC_OUT_OFFSET, outLen);
}
} }
/** /**
@ -1231,6 +1315,14 @@ public class KeycardApplet extends Applet {
JCSystem.beginTransaction(); JCSystem.beginTransaction();
pinlessPathLen = len; pinlessPathLen = len;
Util.arrayCopy(apduBuffer, ISO7816.OFFSET_CDATA, pinlessPath, (short) 0, len); Util.arrayCopy(apduBuffer, ISO7816.OFFSET_CDATA, pinlessPath, (short) 0, len);
if (pinlessPathLen > 0) {
doDerive(apduBuffer, (short) 0, len, DERIVE_P1_SOURCE_MASTER, false);
pinlessPrivateKey.setS(derivationOutput, (short) 0, Crypto.KEY_SECRET_SIZE);
secp256k1.derivePublicKey(pinlessPrivateKey, apduBuffer, (short) 0);
pinlessPublicKey.setW(apduBuffer, (short) 0, Crypto.KEY_PUB_SIZE);
}
JCSystem.commitTransaction(); JCSystem.commitTransaction();
} }
@ -1292,7 +1384,7 @@ public class KeycardApplet extends Applet {
} }
if (derive) { if (derive) {
doDerive(apduBuffer, dataLen, derivationSource, makeCurrent); doDerive(apduBuffer, (short) 0, dataLen, derivationSource, makeCurrent);
} }
short off = SecureChannel.SC_OUT_OFFSET; short off = SecureChannel.SC_OUT_OFFSET;
@ -1379,5 +1471,8 @@ public class KeycardApplet extends Applet {
secp256k1.setCurveParameters(publicKey); secp256k1.setCurveParameters(publicKey);
secp256k1.setCurveParameters(privateKey); secp256k1.setCurveParameters(privateKey);
secp256k1.setCurveParameters(pinlessPublicKey);
secp256k1.setCurveParameters(pinlessPrivateKey);
} }
} }

View File

@ -55,7 +55,7 @@ public class SECP256k1 {
private KeyAgreement ecPointMultiplier; private KeyAgreement ecPointMultiplier;
private Crypto crypto; private Crypto crypto;
private ECPrivateKey tmpECPrivateKey; ECPrivateKey tmpECPrivateKey;
/** /**
* Allocates objects needed by this class. Must be invoked during the applet installation exactly 1 time. * Allocates objects needed by this class. Must be invoked during the applet installation exactly 1 time.

View File

@ -37,11 +37,11 @@ import java.math.BigDecimal;
import java.math.BigInteger; import java.math.BigInteger;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.nio.ByteOrder; import java.nio.ByteOrder;
import java.security.KeyFactory; import java.security.*;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.Signature;
import org.bouncycastle.jce.interfaces.ECPublicKey; import org.bouncycastle.jce.interfaces.ECPublicKey;
import java.security.spec.InvalidKeySpecException;
import java.util.Arrays; import java.util.Arrays;
import java.util.HashSet; import java.util.HashSet;
import java.util.Random; import java.util.Random;
@ -1002,11 +1002,8 @@ public class KeycardTest {
assertEquals(0x9000, response.getSw()); assertEquals(0x9000, response.getSw());
} }
Signature signature = Signature.getInstance("SHA256withECDSA", "BC");
if (!cmdSet.getApplicationInfo().hasMasterKey()) { if (!cmdSet.getApplicationInfo().hasMasterKey()) {
KeyPair keyPair = keypairGenerator().generateKeyPair(); response = cmdSet.generateKey();
response = cmdSet.loadKey(keyPair);
assertEquals(0x9000, response.getSw()); assertEquals(0x9000, response.getSw());
} }
@ -1016,6 +1013,52 @@ public class KeycardTest {
// Correctly sign a precomputed hash // Correctly sign a precomputed hash
response = cmdSet.sign(hash); response = cmdSet.sign(hash);
verifySignResp(data, response);
// Sign and derive
String currentPath = new KeyPath(cmdSet.getStatus(KeycardCommandSet.GET_STATUS_P1_KEY_PATH).checkOK().getData()).toString();
String updatedPath = new KeyPath(currentPath + "/2").toString();
response = cmdSet.signWithPath(hash, updatedPath, false);
verifySignResp(data, response);
assertEquals(currentPath, new KeyPath(cmdSet.getStatus(KeycardCommandSet.GET_STATUS_P1_KEY_PATH).checkOK().getData()).toString());
response = cmdSet.signWithPath(hash, updatedPath, true);
verifySignResp(data, response);
assertEquals(updatedPath, new KeyPath(cmdSet.getStatus(KeycardCommandSet.GET_STATUS_P1_KEY_PATH).checkOK().getData()).toString());
// Sign with PINless
String pinlessPath = currentPath + "/3";
response = cmdSet.setPinlessPath(pinlessPath);
assertEquals(0x9000, response.getSw());
// No secure channel or PIN auth
response = cmdSet.select();
assertEquals(0x9000, response.getSw());
response = cmdSet.signPinless(hash);
verifySignResp(data, response);
// With secure channel
if (cmdSet.getApplicationInfo().hasSecureChannelCapability()) {
cmdSet.autoOpenSecureChannel();
response = cmdSet.signPinless(hash);
verifySignResp(data, response);
}
// No pinless path
if (cmdSet.getApplicationInfo().hasCredentialsManagementCapability()) {
response = cmdSet.verifyPIN("000000");
assertEquals(0x9000, response.getSw());
}
response = cmdSet.resetPinlessPath();
assertEquals(0x9000, response.getSw());
response = cmdSet.signPinless(hash);
assertEquals(0x6A88, response.getSw());
}
private void verifySignResp(byte[] data, APDUResponse response) throws Exception {
Signature signature = Signature.getInstance("SHA256withECDSA", "BC");
assertEquals(0x9000, response.getSw()); assertEquals(0x9000, response.getSw());
byte[] sig = response.getData(); byte[] sig = response.getData();
byte[] keyData = extractPublicKeyFromSignature(sig); byte[] keyData = extractPublicKeyFromSignature(sig);
@ -1082,12 +1125,19 @@ public class KeycardTest {
resetAndSelectAndOpenSC(); resetAndSelectAndOpenSC();
response = cmdSet.sign(hash); response = cmdSet.sign(hash);
assertEquals(0x6985, response.getSw()); assertEquals(0x6985, response.getSw());
if (cmdSet.getApplicationInfo().hasCredentialsManagementCapability()) {
response = cmdSet.verifyPIN("000000");
assertEquals(0x9000, response.getSw());
}
response = cmdSet.deriveKey(new byte[] {0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01}, KeycardApplet.DERIVE_P1_SOURCE_MASTER); response = cmdSet.deriveKey(new byte[] {0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01}, KeycardApplet.DERIVE_P1_SOURCE_MASTER);
assertEquals(0x9000, response.getSw()); assertEquals(0x9000, response.getSw());
response = cmdSet.sign(hash);
assertEquals(0x6985, response.getSw());
response = cmdSet.deriveKey(new byte[] {0x00, 0x00, 0x00, 0x02}, KeycardApplet.DERIVE_P1_SOURCE_CURRENT); response = cmdSet.deriveKey(new byte[] {0x00, 0x00, 0x00, 0x02}, KeycardApplet.DERIVE_P1_SOURCE_CURRENT);
assertEquals(0x9000, response.getSw()); assertEquals(0x9000, response.getSw());
resetAndSelectAndOpenSC();
response = cmdSet.sign(hash); response = cmdSet.sign(hash);
assertEquals(0x9000, response.getSw()); assertEquals(0x9000, response.getSw());
@ -1102,8 +1152,16 @@ public class KeycardTest {
resetAndSelectAndOpenSC(); resetAndSelectAndOpenSC();
response = cmdSet.sign(hash); response = cmdSet.sign(hash);
assertEquals(0x6985, response.getSw()); assertEquals(0x6985, response.getSw());
if (cmdSet.getApplicationInfo().hasCredentialsManagementCapability()) {
response = cmdSet.verifyPIN("000000");
assertEquals(0x9000, response.getSw());
}
response = cmdSet.deriveKey(new byte[] {0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01}, KeycardApplet.DERIVE_P1_SOURCE_MASTER); response = cmdSet.deriveKey(new byte[] {0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01}, KeycardApplet.DERIVE_P1_SOURCE_MASTER);
assertEquals(0x9000, response.getSw()); assertEquals(0x9000, response.getSw());
resetAndSelectAndOpenSC();
response = cmdSet.sign(hash); response = cmdSet.sign(hash);
assertEquals(0x9000, response.getSw()); assertEquals(0x9000, response.getSw());