implement MAC

This commit is contained in:
Michele Balistreri 2017-11-20 17:42:12 +03:00
parent ca8c61ed5d
commit 9772f17efa
5 changed files with 202 additions and 168 deletions

View File

@ -86,7 +86,8 @@ command to work.
* P2 = 0x00
* Response SW = 0x9000 on success, 0x6A86 on undefined P1
* Response Data = Application Status Template or Key Path
* Preconditions: Secure Channel must be opened, if Key Path is required then the card must not be in the middle of a derivation session
* Preconditions: Secure Channel must be opened, if Key Path is required then the card must not be in the middle of a
derivation session
Response Data format:
if P1 = 0x00:
@ -189,7 +190,8 @@ signing sessions, if any. Unless a DERIVE KEY is sent, a subsequent SIGN command
bit 0 of P1 is set, 0x6A86 if P2 = 0x01 and bit 0 of P1 is not set, 0x6984 if one of the components in the path
generates an invalid key.
* Response Data = On assisted derivation and P2 = 0x01 the key derivation template. Empty otherwise.
* Preconditions: Secure Channel must be opened, user PIN must be verified (if no PIN-less key is defined), an extended keyset must be loaded
* Preconditions: Secure Channel must be opened, user PIN must be verified (if no PIN-less key is defined), an extended
keyset must be loaded
This command is used before a signing session to generate a private key according to the [BIP32](https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki)
specifications. This command always aborts open signing sessions, if any. The generated key is used for all subsequent
@ -242,7 +244,8 @@ human-readable mnemonic. Each integer can have a value from 0 to 2047.
* Data = the data to sign
* Response = if P2 indicates last segment, the public key and the signature are returned
* Response SW = 0x9000 on success, 0x6A86 if P2 is invalid
* Preconditions: Secure Channel must be opened, user PIN must be verified (or a PIN-less key must be active), a valid keypair must be loaded
* Preconditions: Secure Channel must be opened, user PIN must be verified (or a PIN-less key must be active), a valid
keypair must be loaded
P1:
* 0x00 = transaction data

View File

@ -3,10 +3,8 @@
## Overview
A Secure Channel must be established to allow communication between the applet and the client. This secure channel has
the concept of pairing with multiple devices (how many clients can be paired at the same time is defined by the applet).
The SecureChannel guarantees protection from snooping, MITM and replay attacks across different sessions, since the way
a session is established makes it impossible to generate the same session keys twice. Since a session is automatically
aborted as soon as power is lost or the application is reselected, it not possible to resume the last session and
perform replay attacks there.
The SecureChannel guarantees protection from snooping, MITM, replay attacks and provides message integrity and
authentication for each APDU.
A short description of establishing a session is as follows
@ -31,19 +29,22 @@ opened.
* P1 = the pairing index
* P2 = 0x00
* Data = An EC-256 public key on the SECP256k1 curve encoded as an uncompressed point.
* Response Data = A 256-bit salt
* Response Data = A 256-bit salt and a 128-bit seed IV
* Response SW = 0x9000 on success, 0x6A86 if P1 is invalid, 0x6A80 if the data is not a public key
This APDU is the first step to establish a Secure Channel session. A session is aborted when the application is deselected,
either directly or because of a card reset/tear. This APDU and its response are not encrypted.
This APDU is the first step to establish a Secure Channel session. A session is aborted when the application is
deselected, either directly or because of a card reset/tear.
The card generates a random 256-bit salt which is sent to the client. Both the client and the card do the following
for key derivation
1. Use their private key and the counterpart public key to generate a secret using the EC-DH algorithm.
2. The generated secret, he pairing key and the salt are concatenated and the SHA-256 of the concatenated value is
2. The generated secret, the pairing key and the salt are concatenated and the SHA-512 of the concatenated value is
calculated.
3. The output of the SHA-256 algorithm is used as the AES key for further communication.
3. The output of the SHA-512 algorithm is split in two parts of 256-bit. The first part is used as the encryption key
and the second part is used as the MAC key for further communication.
The seed IV is used by the client as the IV for the next encrypted APDU.
### MUTUALLY AUTHENTICATE
@ -51,18 +52,18 @@ for key derivation
* INS = 0x11
* P1 = 0x00
* P2 = 0x00
* Data = 256-bit random number and its SHA-256 hash
* Response Data = 256-bit random number and its SHA-256 hash
* Data = 256-bit random number
* Response Data = 256-bit random number
* Response SW = 0x9000 on success, 0x6985 if the previous successfully executed APDU was not OPEN SECURE CHANNEL, 0x6982
if authentication failed, 0x6A80 if the data is not exactly 64 bytes long
This APDU allows both parties to verify that the keys generated in the OPEN SECURE CHANNEL step are matching and thus
guarantee authentication of the counterpart. The data sent by both parties is a 32-bit random number followed by its own
SHA-256 hash. The APDU data is sent encrypted with the keys generated in the OPEN SECURE CHANNEL step. Each party must
verify that the hash indeed matches the value sent. If this is true, then the decryption was correct, meaning that the
keys are matching. Only after this step has been executed the secure channel can be considered to be open and other
commands can be sent. If the authentication fails with 0x6982, the OPEN SECURE CHANNEL command must be repeated to
generate new keys. This greatly slows down brute-force attempts.
guarantee authentication of the counterpart. The data sent by both parties is a 256-bit random number The APDU data is
sent encrypted with the keys generated in the OPEN SECURE CHANNEL step. Each party must verify the MAC of the received
APDU. If the MAC can be verified, it means that both parties are using the same keys. Only after this step has been
executed the secure channel can be considered to be open and other commands can be sent. If the authentication fails
the card must respond with 0x6982. In this case the OPEN SECURE CHANNEL command must be repeated to generate new keys.
This greatly slows down brute-force attempts.
### PAIR
@ -111,42 +112,50 @@ happens depend on the specific applet.
* INS = 0x13
* P1 = the index to unpair
* P2 = 0x00
* Data = the same index as in P1
* Response SW = 0x9000 on success, 0x6985 if security conditions are not met, 0x6A86 if the index is higher than the
highest possible pairing index.
This APDU is sent to unpair a client. An existing secure channel session must be open. The application implementing this
protocol may apply additional restrictions, such as the verification of a user PIN. The reason to repeat P1 in the data
field is to verify that the client indeed performed authentication and has the correct session keys. On success the
pairing slot at the given index will be freed and will be made available to pair other clients. If the index is already
free nothing will happen.
protocol may apply additional restrictions, such as the verification of a user PIN. On success the pairing slot at the
given index will be freed and will be made available to pair other clients. If the index is already free nothing will
happen.
### Encrypted APDUs
After a SecureChannel session has been established all communication between card and client is encrypted. Note
that only the data fields of C-APDU and R-APDU are encrypted, which means that CLA, INS, P1, P2 for C-APDU and SW1SW2 for
R-APDU are plaintext. This means no sensitive data should be sent in these parameters.
After a successful OPEN SECURE CHANNEL command all communication between card and client is encrypted. Note that only
the data fields of C-APDU and R-APDU are encrypted, which means that CLA, INS, P1, P2 for C-APDU and SW1SW2 for R-APDU
are plaintext. This means no sensitive data should be sent in these parameters. Additionally a MAC is calculated for the
entire APDU, including the unencrypted fields.
To encrypt the data both the card and the client do the following:
1. The data is padded using the ISO/IEC 9797-1 Method 2 algorithm.
2. A random IV is generated.
3. The data is encrypted using AES in CBC mode using the session key.
4. The data field of the APDU is set to the IV followed by the encrypted data.
2. The data is encrypted using AES in CBC mode using the session key.
3. An AES CBC-MAC is calculated over the entire APDU data
4. The data field of the APDU is set to the MAC followed by the encrypted data.
To decrypt the data both the card and the client do the following:
1. The first 16 bytes of the APDU are treated as IV.
1. The first 16 bytes of the APDU data are the MAC to be verified
2. The remaining data is decrypted using AES in CBC mode using the session key.
3. The padding is removed.
The IV used for the encryption is the last seen MAC from the counterpart. This optimizes the number
of transmitted bytes and guarantees protection from replay attacks. For the MAC generation, a zero IV is always used.
MAC generation for C-APDUs is calculated on the concatenation of CLA INS P1 P2 LC 00 00 00 00 00 00 00 00 00 00 00 and
the encrypted data field. The 11-byte long padding does not become part of the data field and does not affect LC
MAC generation fo R-APDUs is calculated on the concatenation of Lr SW1 SW2 00 00 00 00 00 00 00 00 00 00 00 00 00 and
the encrypted data field. The 13-byte long padding does not become part of the response field. Lr is the length of the
encrypted response data field and is not transmitted.
Because AES in CBC mode requires the data field length in bytes to be a multiple of 16, the maximum effective APDU
size becomes 240 bytes. Of these 16 bytes are used for the IV and minimum of 1 byte for padding, making the maximum
size becomes 240 bytes. Of these 16 bytes are used for the MAC and minimum of 1 byte for padding, making the maximum
payload size in a single APDU 223 bytes, meaning about a 13,5% overhead.
### Error conditions
1. If a sensitive command is received without an active Secure Channel, the card shall respond with SW 0x6985 (
SW_CONDITIONS_NOT_SATISFIED)
2. If a Secure Channel is established but a sensitive command is received plaintext, the card shall respond with
SW 0x6982 (SW_SECURITY_STATUS_NOT_SATISFIED). The error 0x6F00 (SW_UNKNOWN) is also acceptable in this case.
2. If a MAC cannot be verified the card shall respond 0x6982 and the Secure Channel must be closed

View File

@ -18,13 +18,12 @@ public class Crypto {
private static final short HMAC_BLOCK_SIZE = (short) 128;
private static final short HMAC_BLOCK_OFFSET = (short) KEY_DERIVATION_INPUT_SIZE + HMAC_OUT_SIZE;
// The below two objects are meant to be access from the entire applet
// The below 4 objects can be accessed anywhere from the entire applet
static RandomData random;
static KeyAgreement ecdh;
static MessageDigest sha256;
static MessageDigest sha512;
private static MessageDigest sha512;
private static Signature hmacSHA512;
private static HMACKey hmacKey;
@ -37,6 +36,7 @@ public class Crypto {
random = RandomData.getInstance(RandomData.ALG_SECURE_RANDOM);
sha256 = MessageDigest.getInstance(MessageDigest.ALG_SHA_256, false);
ecdh = KeyAgreement.getInstance(KeyAgreement.ALG_EC_SVDP_DH_PLAIN, false);
sha512 = MessageDigest.getInstance(MessageDigest.ALG_SHA_512, false);
short blockSize;
@ -46,7 +46,6 @@ public class Crypto {
hmacKey = (HMACKey) KeyBuilder.buildKey(KeyBuilder.TYPE_HMAC_TRANSIENT_DESELECT, KEY_SECRET_SIZE, false);
} catch (CryptoException e) {
hmacSHA512 = null;
sha512 = MessageDigest.getInstance(MessageDigest.ALG_SHA_512, false);
blockSize = HMAC_BLOCK_SIZE;
}

View File

@ -22,8 +22,10 @@ public class SecureChannel {
public static final byte PAIR_P1_FIRST_STEP = 0x00;
public static final byte PAIR_P1_LAST_STEP = 0x01;
private AESKey scKey;
private AESKey scEncKey;
private AESKey scMacKey;
private Cipher scCipher;
private Signature scSignature;
private KeyPair scKeypair;
private byte[] secret;
private byte[] pairingSecret;
@ -43,15 +45,17 @@ public class SecureChannel {
*/
public SecureChannel(byte pairingLimit, byte[] aPairingSecret, short off) {
scCipher = Cipher.getInstance(Cipher.ALG_AES_CBC_ISO9797_M2,false);
scSignature = Signature.getInstance(Signature.ALG_AES_MAC_128_NOPAD, false);
scKey = (AESKey) KeyBuilder.buildKey(KeyBuilder.TYPE_AES_TRANSIENT_DESELECT, KeyBuilder.LENGTH_AES_256, false);
scEncKey = (AESKey) KeyBuilder.buildKey(KeyBuilder.TYPE_AES_TRANSIENT_DESELECT, KeyBuilder.LENGTH_AES_256, false);
scMacKey = (AESKey) KeyBuilder.buildKey(KeyBuilder.TYPE_AES_TRANSIENT_DESELECT, KeyBuilder.LENGTH_AES_256, false);
scKeypair = new KeyPair(KeyPair.ALG_EC_FP, SC_KEY_LENGTH);
SECP256k1.setCurveParameters((ECKey) scKeypair.getPrivate());
SECP256k1.setCurveParameters((ECKey) scKeypair.getPublic());
scKeypair.genKeyPair();
secret = JCSystem.makeTransientByteArray(SC_SECRET_LENGTH, JCSystem.CLEAR_ON_DESELECT);
secret = JCSystem.makeTransientByteArray((byte)(SC_SECRET_LENGTH * 2), JCSystem.CLEAR_ON_DESELECT);
pairingSecret = new byte[SC_SECRET_LENGTH];
pairingKeys = new byte[(short)(PAIRING_KEY_LENGTH * pairingLimit)];
@ -68,7 +72,6 @@ public class SecureChannel {
preassignedPairingOffset = -1;
mutuallyAuthenticated = false;
apdu.setIncomingAndReceive();
byte[] apduBuffer = apdu.getBuffer();
short pairingKeyOff = checkPairingIndexAndGetOffset(apduBuffer[ISO7816.OFFSET_P1]);
@ -89,12 +92,15 @@ public class SecureChannel {
return;
}
Crypto.random.generateData(apduBuffer, (short) 0, SC_SECRET_LENGTH);
Crypto.sha256.update(secret, (short) 0, len);
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);
Crypto.random.generateData(apduBuffer, (short) 0, (short) (SC_SECRET_LENGTH + SC_BLOCK_SIZE));
Crypto.sha512.update(secret, (short) 0, len);
Crypto.sha512.update(pairingKeys, pairingKeyOff, SC_SECRET_LENGTH);
Crypto.sha512.doFinal(apduBuffer, (short) 0, SC_SECRET_LENGTH, secret, (short) 0);
scEncKey.setKey(secret, (short) 0);
scMacKey.setKey(secret, SC_SECRET_LENGTH);
Util.arrayCopyNonAtomic(apduBuffer, SC_SECRET_LENGTH, secret, (short) 0, SC_BLOCK_SIZE);
Util.arrayFillNonAtomic(secret, SC_BLOCK_SIZE, SC_SECRET_LENGTH, (byte) 0);
apdu.setOutgoingAndSend((short) 0, (short) (SC_SECRET_LENGTH + SC_BLOCK_SIZE));
}
/**
@ -103,14 +109,13 @@ public class SecureChannel {
* @param apdu the JCRE-owned APDU object.
*/
public void mutuallyAuthenticate(APDU apdu) {
if (!scKey.isInitialized() || mutuallyAuthenticated) {
if (!scEncKey.isInitialized() || mutuallyAuthenticated) {
ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
}
apdu.setIncomingAndReceive();
byte[] apduBuffer = apdu.getBuffer();
short len = decryptAPDU(apduBuffer);
short len = preprocessAPDU(apduBuffer);
if (len != (short) (SC_SECRET_LENGTH * 2)) {
ISOException.throwIt(ISO7816.SW_WRONG_DATA);
@ -119,7 +124,7 @@ public class SecureChannel {
Crypto.sha256.doFinal(apduBuffer, ISO7816.OFFSET_CDATA, SC_SECRET_LENGTH, apduBuffer, ISO7816.OFFSET_CDATA);
if (Util.arrayCompare(apduBuffer, ISO7816.OFFSET_CDATA, apduBuffer, (short) (ISO7816.OFFSET_CDATA + SC_SECRET_LENGTH), SC_SECRET_LENGTH) != 0) {
scKey.clearKey();
reset();
ISOException.throwIt(ISO7816.SW_SECURITY_STATUS_NOT_SATISFIED);
}
@ -127,8 +132,7 @@ public class SecureChannel {
Crypto.random.generateData(apduBuffer, SC_OUT_OFFSET, SC_SECRET_LENGTH);
Crypto.sha256.doFinal(apduBuffer, SC_OUT_OFFSET, SC_SECRET_LENGTH, apduBuffer, (short) (SC_OUT_OFFSET + SC_SECRET_LENGTH));
len = encryptAPDU(apduBuffer, len);
apdu.setOutgoingAndSend(ISO7816.OFFSET_CDATA, len);
respond(apdu, len, ISO7816.SW_NO_ERROR);
}
/**
@ -137,8 +141,6 @@ public class SecureChannel {
* @param apdu the JCRE-owned APDU object.
*/
public void pair(APDU apdu) {
apdu.setIncomingAndReceive();
if (isOpen()) {
ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
}
@ -222,38 +224,42 @@ public class SecureChannel {
}
/**
* Processes the UNPAIR command. For security reasons the key is not only marked as free but also zero-ed out.
* Processes the UNPAIR command. For security reasons the key is not only marked as free but also zero-ed out. This
* method assumes that all security checks have been performed by the calling method.
*
* @param apdu the JCRE-owned APDU object.
* @param apduBuffer the APDU buffer
*/
public void unpair(APDU apdu) {
if (!isOpen()) {
ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
}
apdu.setIncomingAndReceive();
byte[] apduBuffer = apdu.getBuffer();
short v = decryptAPDU(apduBuffer);
if ((v != 1) || (apduBuffer[ISO7816.OFFSET_CDATA] != apduBuffer[ISO7816.OFFSET_P1])) {
ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
}
v = checkPairingIndexAndGetOffset(apduBuffer[ISO7816.OFFSET_P1]);
Util.arrayFillNonAtomic(pairingKeys, v, PAIRING_KEY_LENGTH, (byte) 0);
public void unpair(byte[] apduBuffer) {
short off = checkPairingIndexAndGetOffset(apduBuffer[ISO7816.OFFSET_P1]);
Util.arrayFillNonAtomic(pairingKeys, off, PAIRING_KEY_LENGTH, (byte) 0);
}
/**
* Decrypts the given APDU buffer. The plaintext is written in-place starting at the ISO7816.OFFSET_CDATA offset. The
* IV and padding are stripped. The LC byte is overwritten with the plaintext length.
* MAC and padding are stripped. The LC byte is overwritten with the plaintext length. If the MAC cannot be verified
* the secure channel is reset and the SW 0x6982 is thrown.
*
* @param apduBuffer the APDU buffer
* @return the length of the decrypted
*/
public short decryptAPDU(byte[] apduBuffer) {
public short preprocessAPDU(byte[] apduBuffer) {
if (!isOpen()) {
ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
}
short apduLen = (short)((short) apduBuffer[ISO7816.OFFSET_LC] & 0xff);
scCipher.init(scKey, Cipher.MODE_DECRYPT, apduBuffer, ISO7816.OFFSET_CDATA, SC_BLOCK_SIZE);
scSignature.init(scMacKey, Signature.MODE_VERIFY);
scSignature.update(apduBuffer, (short) 0, ISO7816.OFFSET_CDATA);
scSignature.update(secret, SC_BLOCK_SIZE, (short) (SC_BLOCK_SIZE - ISO7816.OFFSET_CDATA));
if (!scSignature.verify(apduBuffer, (short) (ISO7816.OFFSET_CDATA + SC_BLOCK_SIZE), (short) (apduLen - SC_BLOCK_SIZE), apduBuffer, ISO7816.OFFSET_CDATA, SC_BLOCK_SIZE)) {
reset();
ISOException.throwIt(ISO7816.SW_SECURITY_STATUS_NOT_SATISFIED);
}
scCipher.init(scEncKey, Cipher.MODE_DECRYPT, secret, (short) 0, SC_BLOCK_SIZE);
Util.arrayCopyNonAtomic(apduBuffer, ISO7816.OFFSET_CDATA, secret, (short) 0, SC_BLOCK_SIZE);
short len = scCipher.doFinal(apduBuffer, (short)(ISO7816.OFFSET_CDATA + SC_BLOCK_SIZE), (short) (apduLen - SC_BLOCK_SIZE), apduBuffer, ISO7816.OFFSET_CDATA);
apduBuffer[ISO7816.OFFSET_LC] = (byte) len;
@ -262,18 +268,31 @@ public class SecureChannel {
}
/**
* Encrypts the APDU buffer. The plaintext must be placed starting at the SecureChannel.SC_OUT_OFFSET offset, to leave
* place for the SecureChannel-specific data at the beginning of the APDU.
* Sends the response to the command. This method always throws an ISOException with the given SW, so nothing can be
* called after its execution. The response data must be placed starting at the SecureChannel.SC_OUT_OFFSET offset, to
* leave place for the SecureChannel-specific data at the beginning of the APDU.
*
* @param apduBuffer the APDU buffer
* @param apdu the APDU object
* @param len the length of the plaintext
* @return the length of the encrypted APDU
*/
public short encryptAPDU(byte[] apduBuffer, short len) {
Crypto.random.generateData(apduBuffer, ISO7816.OFFSET_CDATA, SC_BLOCK_SIZE);
scCipher.init(scKey, Cipher.MODE_ENCRYPT, apduBuffer, ISO7816.OFFSET_CDATA, SC_BLOCK_SIZE);
public void respond(APDU apdu, short len, short sw) {
byte[] apduBuffer = apdu.getBuffer();
Util.setShort(apduBuffer, (short) 0, sw);
scCipher.init(scEncKey, Cipher.MODE_ENCRYPT, secret, (short) 0, SC_BLOCK_SIZE);
len = scCipher.doFinal(apduBuffer, SC_OUT_OFFSET, len, apduBuffer, (short)(ISO7816.OFFSET_CDATA + SC_BLOCK_SIZE));
return (short)(len + SC_BLOCK_SIZE);
scSignature.init(scMacKey, Signature.MODE_SIGN);
scSignature.update(apduBuffer, (short) 0, (short) 2);
scSignature.update(secret, SC_BLOCK_SIZE, (short)(SC_BLOCK_SIZE - 2));
scSignature.sign(apduBuffer, SC_OUT_OFFSET, len, apduBuffer, ISO7816.OFFSET_CDATA);
Util.arrayCopyNonAtomic(apduBuffer, ISO7816.OFFSET_CDATA, secret, (short) 0, SC_BLOCK_SIZE);
len += SC_BLOCK_SIZE;
apdu.setOutgoingAndSend(ISO7816.OFFSET_CDATA, len);
ISOException.throwIt(sw);
}
/**
@ -293,7 +312,16 @@ public class SecureChannel {
* @return whether a secure channel is currently established or not.
*/
public boolean isOpen() {
return scKey.isInitialized() && mutuallyAuthenticated;
return scEncKey.isInitialized() && scMacKey.isInitialized() && mutuallyAuthenticated;
}
/**
* Resets the Secure Channel, invalidating the current session. If no session is opened, this does nothing.
*/
public void reset() {
scEncKey.clearKey();
scMacKey.clearKey();
mutuallyAuthenticated = false;
}
/**

View File

@ -179,8 +179,10 @@ public class WalletApplet extends Applet {
return;
}
apdu.setIncomingAndReceive();
byte[] apduBuffer = apdu.getBuffer();
try {
switch (apduBuffer[ISO7816.OFFSET_INS]) {
case SecureChannel.INS_OPEN_SECURE_CHANNEL:
secureChannel.openSecureChannel(apdu);
@ -228,6 +230,17 @@ public class WalletApplet extends Applet {
ISOException.throwIt(ISO7816.SW_INS_NOT_SUPPORTED);
break;
}
} catch(ISOException sw) {
if (secureChannel.isOpen() && apdu.getCurrentState() != APDU.STATE_FULL_OUTGOING) {
secureChannel.respond(apdu, (short) 0, sw.getReason());
} else {
throw sw;
}
}
if (secureChannel.isOpen()) {
secureChannel.respond(apdu, (short) 0, ISO7816.SW_NO_ERROR);
}
}
/**
@ -237,11 +250,13 @@ public class WalletApplet extends Applet {
* @param apdu the JCRE-owned APDU object.
*/
private void unpair(APDU apdu) {
byte[] apduBuffer = apdu.getBuffer();
secureChannel.preprocessAPDU(apduBuffer);
if (pin.isValidated()) {
secureChannel.unpair(apdu);
secureChannel.unpair(apduBuffer);
} else {
ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
}
}
@ -255,8 +270,8 @@ public class WalletApplet extends Applet {
signInProgress = false;
pin.reset();
puk.reset();
secureChannel.reset();
apdu.setIncomingAndReceive();
byte[] apduBuffer = apdu.getBuffer();
apduBuffer[0] = TLV_APPLICATION_INFO_TEMPLATE;
@ -294,8 +309,7 @@ public class WalletApplet extends Applet {
return;
}
len = secureChannel.encryptAPDU(apduBuffer, len);
apdu.setOutgoingAndSend(ISO7816.OFFSET_CDATA, len);
secureChannel.respond(apdu, len, ISO7816.SW_NO_ERROR);
}
/**
@ -353,14 +367,12 @@ public class WalletApplet extends Applet {
* @param apdu the JCRE-owned APDU object.
*/
private void verifyPIN(APDU apdu) {
apdu.setIncomingAndReceive();
if (!secureChannel.isOpen()) {
ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
}
byte[] apduBuffer = apdu.getBuffer();
byte len = (byte) secureChannel.decryptAPDU(apduBuffer);
byte len = (byte) secureChannel.preprocessAPDU(apduBuffer);
if (!pin.check(apduBuffer, ISO7816.OFFSET_CDATA, len)) {
ISOException.throwIt((short)((short) 0x63c0 | (short) pin.getTriesRemaining()));
@ -375,14 +387,12 @@ public class WalletApplet extends Applet {
* @param apdu the JCRE-owned APDU object.
*/
private void changePIN(APDU apdu) {
apdu.setIncomingAndReceive();
if (!(secureChannel.isOpen() && pin.isValidated())) {
ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
}
byte[] apduBuffer = apdu.getBuffer();
byte len = (byte) secureChannel.decryptAPDU(apduBuffer);
byte len = (byte) secureChannel.preprocessAPDU(apduBuffer);
if (!(len == PIN_LENGTH && allDigits(apduBuffer, ISO7816.OFFSET_CDATA, len))) {
ISOException.throwIt(ISO7816.SW_WRONG_DATA);
@ -401,14 +411,12 @@ public class WalletApplet extends Applet {
* @param apdu the JCRE-owned APDU object.
*/
private void unblockPIN(APDU apdu) {
apdu.setIncomingAndReceive();
if (!(secureChannel.isOpen() && pin.getTriesRemaining() == 0)) {
ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
}
byte[] apduBuffer = apdu.getBuffer();
byte len = (byte) secureChannel.decryptAPDU(apduBuffer);
byte len = (byte) secureChannel.preprocessAPDU(apduBuffer);
if (!(len == (PUK_LENGTH + PIN_LENGTH) && allDigits(apduBuffer, ISO7816.OFFSET_CDATA, len))) {
ISOException.throwIt(ISO7816.SW_WRONG_DATA);
@ -434,15 +442,13 @@ public class WalletApplet extends Applet {
* @param apdu the JCRE-owned APDU object.
*/
private void loadKey(APDU apdu) {
apdu.setIncomingAndReceive();
if (!(secureChannel.isOpen() && pin.isValidated())) {
ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
}
byte[] apduBuffer = apdu.getBuffer();
secureChannel.decryptAPDU(apduBuffer);
secureChannel.preprocessAPDU(apduBuffer);
boolean newExtended = false;
switch (apduBuffer[ISO7816.OFFSET_P1]) {
@ -586,8 +592,7 @@ public class WalletApplet extends Applet {
byte[] apduBuffer = apdu.getBuffer();
apdu.setIncomingAndReceive();
short len = secureChannel.decryptAPDU(apduBuffer);
short len = secureChannel.preprocessAPDU(apduBuffer);
boolean assistedDerivation = (apduBuffer[ISO7816.OFFSET_P1] & DERIVE_P1_ASSISTED_MASK) == DERIVE_P1_ASSISTED_MASK;
boolean isPublicKey = apduBuffer[ISO7816.OFFSET_P2] == DERIVE_P2_PUBLIC_KEY;
@ -662,8 +667,7 @@ public class WalletApplet extends Applet {
apduBuffer[(short) (SecureChannel.SC_OUT_OFFSET + 2)] = TLV_PUB_X;
apduBuffer[(short) (SecureChannel.SC_OUT_OFFSET + 3)] = (byte) xLen;
short outLen = secureChannel.encryptAPDU(apduBuffer, (short) (xLen + sigLen + 4));
apdu.setOutgoingAndSend(ISO7816.OFFSET_CDATA, outLen);
secureChannel.respond(apdu, (short) (xLen + sigLen + 4), ISO7816.SW_NO_ERROR);
}
/**
@ -739,8 +743,7 @@ public class WalletApplet extends Applet {
outOff -= 2; // a last spurious 11 bit number will be generated when cs length is less than 6 because 16 - cs >= 11
}
short outLen = secureChannel.encryptAPDU(apduBuffer, (short) (outOff - SecureChannel.SC_OUT_OFFSET));
apdu.setOutgoingAndSend(ISO7816.OFFSET_CDATA, outLen);
secureChannel.respond(apdu, (short) (outOff - SecureChannel.SC_OUT_OFFSET), ISO7816.SW_NO_ERROR);
}
/**
@ -782,8 +785,6 @@ public class WalletApplet extends Applet {
* @param apdu the JCRE-owned APDU object.
*/
private void sign(APDU apdu) {
apdu.setIncomingAndReceive();
if (!(secureChannel.isOpen() && (pin.isValidated() || isPinless()) && privateKey.isInitialized() && !expectPublicKey)) {
ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
}
@ -797,7 +798,7 @@ public class WalletApplet extends Applet {
ISOException.throwIt(ISO7816.SW_INCORRECT_P1P2);
}
short len = secureChannel.decryptAPDU(apduBuffer);
short len = secureChannel.preprocessAPDU(apduBuffer);
if ((apduBuffer[ISO7816.OFFSET_P2] & SIGN_P2_LAST_BLOCK_MASK) == SIGN_P2_LAST_BLOCK_MASK) {
signInProgress = false;
@ -819,8 +820,7 @@ public class WalletApplet extends Applet {
apduBuffer[(short)(SecureChannel.SC_OUT_OFFSET + 1)] = (byte) 0x81;
apduBuffer[(short)(SecureChannel.SC_OUT_OFFSET + 2)] = (byte) (outLen - 3);
outLen = secureChannel.encryptAPDU(apduBuffer, outLen);
apdu.setOutgoingAndSend(ISO7816.OFFSET_CDATA, outLen);
secureChannel.respond(apdu, outLen, ISO7816.SW_NO_ERROR);
} else {
signature.update(apduBuffer, ISO7816.OFFSET_CDATA, len);
}
@ -838,14 +838,12 @@ public class WalletApplet extends Applet {
* @param apdu the JCRE-owned APDU object.
*/
private void setPinlessPath(APDU apdu) {
apdu.setIncomingAndReceive();
if (!(secureChannel.isOpen() && pin.isValidated())) {
ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
}
byte[] apduBuffer = apdu.getBuffer();
short len = secureChannel.decryptAPDU(apduBuffer);
short len = secureChannel.preprocessAPDU(apduBuffer);
if (((short) (len % 4) != 0) || (len > pinlessPath.length)) {
ISOException.throwIt(ISO7816.SW_WRONG_DATA);
@ -866,8 +864,6 @@ public class WalletApplet extends Applet {
* @param apdu the JCRE-owned APDU object.
*/
private void exportKey(APDU apdu) {
apdu.setIncomingAndReceive();
if (!(secureChannel.isOpen() && pin.isValidated())) {
ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
}
@ -904,8 +900,7 @@ public class WalletApplet extends Applet {
len += (off - SecureChannel.SC_OUT_OFFSET);
apduBuffer[(SecureChannel.SC_OUT_OFFSET + 1)] = (byte) (len - 2);
len = secureChannel.encryptAPDU(apduBuffer, (short) len);
apdu.setOutgoingAndSend(ISO7816.OFFSET_CDATA, len);
secureChannel.respond(apdu, (short) len, ISO7816.SW_NO_ERROR);
}
/**