mirror of
https://github.com/status-im/status-keycard.git
synced 2025-02-27 04:20:27 +00:00
implement MAC
This commit is contained in:
parent
ca8c61ed5d
commit
9772f17efa
@ -86,7 +86,8 @@ command to work.
|
|||||||
* P2 = 0x00
|
* P2 = 0x00
|
||||||
* Response SW = 0x9000 on success, 0x6A86 on undefined P1
|
* Response SW = 0x9000 on success, 0x6A86 on undefined P1
|
||||||
* Response Data = Application Status Template or Key Path
|
* 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:
|
Response Data format:
|
||||||
if P1 = 0x00:
|
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
|
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.
|
generates an invalid key.
|
||||||
* Response Data = On assisted derivation and P2 = 0x01 the key derivation template. Empty otherwise.
|
* 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)
|
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
|
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
|
* Data = the data to sign
|
||||||
* Response = if P2 indicates last segment, the public key and the signature are returned
|
* Response = if P2 indicates last segment, the public key and the signature are returned
|
||||||
* Response SW = 0x9000 on success, 0x6A86 if P2 is invalid
|
* 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:
|
P1:
|
||||||
* 0x00 = transaction data
|
* 0x00 = transaction data
|
||||||
|
@ -3,10 +3,8 @@
|
|||||||
## Overview
|
## Overview
|
||||||
A Secure Channel must be established to allow communication between the applet and the client. This secure channel has
|
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 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
|
The SecureChannel guarantees protection from snooping, MITM, replay attacks and provides message integrity and
|
||||||
a session is established makes it impossible to generate the same session keys twice. Since a session is automatically
|
authentication for each APDU.
|
||||||
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.
|
|
||||||
|
|
||||||
A short description of establishing a session is as follows
|
A short description of establishing a session is as follows
|
||||||
|
|
||||||
@ -31,19 +29,22 @@ opened.
|
|||||||
* P1 = the pairing index
|
* P1 = the pairing index
|
||||||
* P2 = 0x00
|
* P2 = 0x00
|
||||||
* Data = An EC-256 public key on the SECP256k1 curve encoded as an uncompressed point.
|
* 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
|
* 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,
|
This APDU is the first step to establish a Secure Channel session. A session is aborted when the application is
|
||||||
either directly or because of a card reset/tear. This APDU and its response are not encrypted.
|
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
|
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
|
for key derivation
|
||||||
|
|
||||||
1. Use their private key and the counterpart public key to generate a secret using the EC-DH algorithm.
|
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.
|
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
|
### MUTUALLY AUTHENTICATE
|
||||||
|
|
||||||
@ -51,18 +52,18 @@ for key derivation
|
|||||||
* INS = 0x11
|
* INS = 0x11
|
||||||
* P1 = 0x00
|
* P1 = 0x00
|
||||||
* P2 = 0x00
|
* P2 = 0x00
|
||||||
* Data = 256-bit random number and its SHA-256 hash
|
* Data = 256-bit random number
|
||||||
* Response Data = 256-bit random number and its SHA-256 hash
|
* Response Data = 256-bit random number
|
||||||
* Response SW = 0x9000 on success, 0x6985 if the previous successfully executed APDU was not OPEN SECURE CHANNEL, 0x6982
|
* 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
|
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
|
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
|
guarantee authentication of the counterpart. The data sent by both parties is a 256-bit random number The APDU data is
|
||||||
SHA-256 hash. The APDU data is sent encrypted with the keys generated in the OPEN SECURE CHANNEL step. Each party must
|
sent encrypted with the keys generated in the OPEN SECURE CHANNEL step. Each party must verify the MAC of the received
|
||||||
verify that the hash indeed matches the value sent. If this is true, then the decryption was correct, meaning that the
|
APDU. If the MAC can be verified, it means that both parties are using the same keys. Only after this step has been
|
||||||
keys are matching. Only after this step has been executed the secure channel can be considered to be open and other
|
executed the secure channel can be considered to be open and other commands can be sent. If the authentication fails
|
||||||
commands can be sent. If the authentication fails with 0x6982, the OPEN SECURE CHANNEL command must be repeated to
|
the card must respond with 0x6982. In this case the OPEN SECURE CHANNEL command must be repeated to generate new keys.
|
||||||
generate new keys. This greatly slows down brute-force attempts.
|
This greatly slows down brute-force attempts.
|
||||||
|
|
||||||
### PAIR
|
### PAIR
|
||||||
|
|
||||||
@ -111,42 +112,50 @@ happens depend on the specific applet.
|
|||||||
* INS = 0x13
|
* INS = 0x13
|
||||||
* P1 = the index to unpair
|
* P1 = the index to unpair
|
||||||
* P2 = 0x00
|
* 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
|
* Response SW = 0x9000 on success, 0x6985 if security conditions are not met, 0x6A86 if the index is higher than the
|
||||||
highest possible pairing index.
|
highest possible pairing index.
|
||||||
|
|
||||||
This APDU is sent to unpair a client. An existing secure channel session must be open. The application implementing this
|
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
|
protocol may apply additional restrictions, such as the verification of a user PIN. On success the pairing slot at the
|
||||||
field is to verify that the client indeed performed authentication and has the correct session keys. On success the
|
given index will be freed and will be made available to pair other clients. If the index is already free nothing will
|
||||||
pairing slot at the given index will be freed and will be made available to pair other clients. If the index is already
|
happen.
|
||||||
free nothing will happen.
|
|
||||||
|
|
||||||
### Encrypted APDUs
|
### Encrypted APDUs
|
||||||
|
|
||||||
After a SecureChannel session has been established all communication between card and client is encrypted. Note
|
After a successful OPEN SECURE CHANNEL command all communication between card and client is encrypted. Note that only
|
||||||
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
|
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
|
||||||
R-APDU are plaintext. This means no sensitive data should be sent in these parameters.
|
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:
|
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.
|
1. The data is padded using the ISO/IEC 9797-1 Method 2 algorithm.
|
||||||
2. A random IV is generated.
|
2. The data is encrypted using AES in CBC mode using the session key.
|
||||||
3. 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 IV followed by the encrypted 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:
|
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.
|
2. The remaining data is decrypted using AES in CBC mode using the session key.
|
||||||
3. The padding is removed.
|
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
|
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.
|
payload size in a single APDU 223 bytes, meaning about a 13,5% overhead.
|
||||||
|
|
||||||
### Error conditions
|
### Error conditions
|
||||||
|
|
||||||
1. If a sensitive command is received without an active Secure Channel, the card shall respond with SW 0x6985 (
|
1. If a sensitive command is received without an active Secure Channel, the card shall respond with SW 0x6985 (
|
||||||
SW_CONDITIONS_NOT_SATISFIED)
|
SW_CONDITIONS_NOT_SATISFIED)
|
||||||
2. If a Secure Channel is established but a sensitive command is received plaintext, the card shall respond with
|
2. If a MAC cannot be verified the card shall respond 0x6982 and the Secure Channel must be closed
|
||||||
SW 0x6982 (SW_SECURITY_STATUS_NOT_SATISFIED). The error 0x6F00 (SW_UNKNOWN) is also acceptable in this case.
|
|
@ -18,13 +18,12 @@ public class Crypto {
|
|||||||
private static final short HMAC_BLOCK_SIZE = (short) 128;
|
private static final short HMAC_BLOCK_SIZE = (short) 128;
|
||||||
private static final short HMAC_BLOCK_OFFSET = (short) KEY_DERIVATION_INPUT_SIZE + HMAC_OUT_SIZE;
|
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 RandomData random;
|
||||||
static KeyAgreement ecdh;
|
static KeyAgreement ecdh;
|
||||||
|
|
||||||
static MessageDigest sha256;
|
static MessageDigest sha256;
|
||||||
|
static MessageDigest sha512;
|
||||||
|
|
||||||
private static MessageDigest sha512;
|
|
||||||
private static Signature hmacSHA512;
|
private static Signature hmacSHA512;
|
||||||
private static HMACKey hmacKey;
|
private static HMACKey hmacKey;
|
||||||
|
|
||||||
@ -37,6 +36,7 @@ public class Crypto {
|
|||||||
random = RandomData.getInstance(RandomData.ALG_SECURE_RANDOM);
|
random = RandomData.getInstance(RandomData.ALG_SECURE_RANDOM);
|
||||||
sha256 = MessageDigest.getInstance(MessageDigest.ALG_SHA_256, false);
|
sha256 = MessageDigest.getInstance(MessageDigest.ALG_SHA_256, false);
|
||||||
ecdh = KeyAgreement.getInstance(KeyAgreement.ALG_EC_SVDP_DH_PLAIN, false);
|
ecdh = KeyAgreement.getInstance(KeyAgreement.ALG_EC_SVDP_DH_PLAIN, false);
|
||||||
|
sha512 = MessageDigest.getInstance(MessageDigest.ALG_SHA_512, false);
|
||||||
|
|
||||||
short blockSize;
|
short blockSize;
|
||||||
|
|
||||||
@ -46,7 +46,6 @@ public class Crypto {
|
|||||||
hmacKey = (HMACKey) KeyBuilder.buildKey(KeyBuilder.TYPE_HMAC_TRANSIENT_DESELECT, KEY_SECRET_SIZE, false);
|
hmacKey = (HMACKey) KeyBuilder.buildKey(KeyBuilder.TYPE_HMAC_TRANSIENT_DESELECT, KEY_SECRET_SIZE, false);
|
||||||
} catch (CryptoException e) {
|
} catch (CryptoException e) {
|
||||||
hmacSHA512 = null;
|
hmacSHA512 = null;
|
||||||
sha512 = MessageDigest.getInstance(MessageDigest.ALG_SHA_512, false);
|
|
||||||
blockSize = HMAC_BLOCK_SIZE;
|
blockSize = HMAC_BLOCK_SIZE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -22,8 +22,10 @@ public class SecureChannel {
|
|||||||
public static final byte PAIR_P1_FIRST_STEP = 0x00;
|
public static final byte PAIR_P1_FIRST_STEP = 0x00;
|
||||||
public static final byte PAIR_P1_LAST_STEP = 0x01;
|
public static final byte PAIR_P1_LAST_STEP = 0x01;
|
||||||
|
|
||||||
private AESKey scKey;
|
private AESKey scEncKey;
|
||||||
|
private AESKey scMacKey;
|
||||||
private Cipher scCipher;
|
private Cipher scCipher;
|
||||||
|
private Signature scSignature;
|
||||||
private KeyPair scKeypair;
|
private KeyPair scKeypair;
|
||||||
private byte[] secret;
|
private byte[] secret;
|
||||||
private byte[] pairingSecret;
|
private byte[] pairingSecret;
|
||||||
@ -43,15 +45,17 @@ public class SecureChannel {
|
|||||||
*/
|
*/
|
||||||
public SecureChannel(byte pairingLimit, byte[] aPairingSecret, short off) {
|
public SecureChannel(byte pairingLimit, byte[] aPairingSecret, short off) {
|
||||||
scCipher = Cipher.getInstance(Cipher.ALG_AES_CBC_ISO9797_M2,false);
|
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);
|
scKeypair = new KeyPair(KeyPair.ALG_EC_FP, SC_KEY_LENGTH);
|
||||||
SECP256k1.setCurveParameters((ECKey) scKeypair.getPrivate());
|
SECP256k1.setCurveParameters((ECKey) scKeypair.getPrivate());
|
||||||
SECP256k1.setCurveParameters((ECKey) scKeypair.getPublic());
|
SECP256k1.setCurveParameters((ECKey) scKeypair.getPublic());
|
||||||
scKeypair.genKeyPair();
|
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];
|
pairingSecret = new byte[SC_SECRET_LENGTH];
|
||||||
pairingKeys = new byte[(short)(PAIRING_KEY_LENGTH * pairingLimit)];
|
pairingKeys = new byte[(short)(PAIRING_KEY_LENGTH * pairingLimit)];
|
||||||
|
|
||||||
@ -68,7 +72,6 @@ public class SecureChannel {
|
|||||||
preassignedPairingOffset = -1;
|
preassignedPairingOffset = -1;
|
||||||
mutuallyAuthenticated = false;
|
mutuallyAuthenticated = false;
|
||||||
|
|
||||||
apdu.setIncomingAndReceive();
|
|
||||||
byte[] apduBuffer = apdu.getBuffer();
|
byte[] apduBuffer = apdu.getBuffer();
|
||||||
|
|
||||||
short pairingKeyOff = checkPairingIndexAndGetOffset(apduBuffer[ISO7816.OFFSET_P1]);
|
short pairingKeyOff = checkPairingIndexAndGetOffset(apduBuffer[ISO7816.OFFSET_P1]);
|
||||||
@ -89,12 +92,15 @@ public class SecureChannel {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Crypto.random.generateData(apduBuffer, (short) 0, SC_SECRET_LENGTH);
|
Crypto.random.generateData(apduBuffer, (short) 0, (short) (SC_SECRET_LENGTH + SC_BLOCK_SIZE));
|
||||||
Crypto.sha256.update(secret, (short) 0, len);
|
Crypto.sha512.update(secret, (short) 0, len);
|
||||||
Crypto.sha256.update(pairingKeys, pairingKeyOff, SC_SECRET_LENGTH);
|
Crypto.sha512.update(pairingKeys, pairingKeyOff, SC_SECRET_LENGTH);
|
||||||
Crypto.sha256.doFinal(apduBuffer, (short) 0, SC_SECRET_LENGTH, secret, (short) 0);
|
Crypto.sha512.doFinal(apduBuffer, (short) 0, SC_SECRET_LENGTH, secret, (short) 0);
|
||||||
scKey.setKey(secret, (short) 0);
|
scEncKey.setKey(secret, (short) 0);
|
||||||
apdu.setOutgoingAndSend((short) 0, SC_SECRET_LENGTH);
|
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.
|
* @param apdu the JCRE-owned APDU object.
|
||||||
*/
|
*/
|
||||||
public void mutuallyAuthenticate(APDU apdu) {
|
public void mutuallyAuthenticate(APDU apdu) {
|
||||||
if (!scKey.isInitialized() || mutuallyAuthenticated) {
|
if (!scEncKey.isInitialized() || mutuallyAuthenticated) {
|
||||||
ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
|
ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
|
||||||
}
|
}
|
||||||
|
|
||||||
apdu.setIncomingAndReceive();
|
|
||||||
byte[] apduBuffer = apdu.getBuffer();
|
byte[] apduBuffer = apdu.getBuffer();
|
||||||
|
|
||||||
short len = decryptAPDU(apduBuffer);
|
short len = preprocessAPDU(apduBuffer);
|
||||||
|
|
||||||
if (len != (short) (SC_SECRET_LENGTH * 2)) {
|
if (len != (short) (SC_SECRET_LENGTH * 2)) {
|
||||||
ISOException.throwIt(ISO7816.SW_WRONG_DATA);
|
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);
|
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) {
|
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);
|
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.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));
|
Crypto.sha256.doFinal(apduBuffer, SC_OUT_OFFSET, SC_SECRET_LENGTH, apduBuffer, (short) (SC_OUT_OFFSET + SC_SECRET_LENGTH));
|
||||||
len = encryptAPDU(apduBuffer, len);
|
respond(apdu, len, ISO7816.SW_NO_ERROR);
|
||||||
apdu.setOutgoingAndSend(ISO7816.OFFSET_CDATA, len);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -137,8 +141,6 @@ public class SecureChannel {
|
|||||||
* @param apdu the JCRE-owned APDU object.
|
* @param apdu the JCRE-owned APDU object.
|
||||||
*/
|
*/
|
||||||
public void pair(APDU apdu) {
|
public void pair(APDU apdu) {
|
||||||
apdu.setIncomingAndReceive();
|
|
||||||
|
|
||||||
if (isOpen()) {
|
if (isOpen()) {
|
||||||
ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
|
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) {
|
public void unpair(byte[] apduBuffer) {
|
||||||
if (!isOpen()) {
|
short off = checkPairingIndexAndGetOffset(apduBuffer[ISO7816.OFFSET_P1]);
|
||||||
ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
|
Util.arrayFillNonAtomic(pairingKeys, off, PAIRING_KEY_LENGTH, (byte) 0);
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Decrypts the given APDU buffer. The plaintext is written in-place starting at the ISO7816.OFFSET_CDATA offset. The
|
* 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
|
* @param apduBuffer the APDU buffer
|
||||||
* @return the length of the decrypted
|
* @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);
|
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);
|
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;
|
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
|
* Sends the response to the command. This method always throws an ISOException with the given SW, so nothing can be
|
||||||
* place for the SecureChannel-specific data at the beginning of the APDU.
|
* 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
|
* @param len the length of the plaintext
|
||||||
* @return the length of the encrypted APDU
|
|
||||||
*/
|
*/
|
||||||
public short encryptAPDU(byte[] apduBuffer, short len) {
|
public void respond(APDU apdu, short len, short sw) {
|
||||||
Crypto.random.generateData(apduBuffer, ISO7816.OFFSET_CDATA, SC_BLOCK_SIZE);
|
byte[] apduBuffer = apdu.getBuffer();
|
||||||
scCipher.init(scKey, Cipher.MODE_ENCRYPT, apduBuffer, ISO7816.OFFSET_CDATA, SC_BLOCK_SIZE);
|
|
||||||
|
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));
|
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.
|
* @return whether a secure channel is currently established or not.
|
||||||
*/
|
*/
|
||||||
public boolean isOpen() {
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -179,8 +179,10 @@ public class WalletApplet extends Applet {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
apdu.setIncomingAndReceive();
|
||||||
byte[] apduBuffer = apdu.getBuffer();
|
byte[] apduBuffer = apdu.getBuffer();
|
||||||
|
|
||||||
|
try {
|
||||||
switch (apduBuffer[ISO7816.OFFSET_INS]) {
|
switch (apduBuffer[ISO7816.OFFSET_INS]) {
|
||||||
case SecureChannel.INS_OPEN_SECURE_CHANNEL:
|
case SecureChannel.INS_OPEN_SECURE_CHANNEL:
|
||||||
secureChannel.openSecureChannel(apdu);
|
secureChannel.openSecureChannel(apdu);
|
||||||
@ -228,6 +230,17 @@ public class WalletApplet extends Applet {
|
|||||||
ISOException.throwIt(ISO7816.SW_INS_NOT_SUPPORTED);
|
ISOException.throwIt(ISO7816.SW_INS_NOT_SUPPORTED);
|
||||||
break;
|
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.
|
* @param apdu the JCRE-owned APDU object.
|
||||||
*/
|
*/
|
||||||
private void unpair(APDU apdu) {
|
private void unpair(APDU apdu) {
|
||||||
|
byte[] apduBuffer = apdu.getBuffer();
|
||||||
|
secureChannel.preprocessAPDU(apduBuffer);
|
||||||
|
|
||||||
if (pin.isValidated()) {
|
if (pin.isValidated()) {
|
||||||
secureChannel.unpair(apdu);
|
secureChannel.unpair(apduBuffer);
|
||||||
} else {
|
} else {
|
||||||
ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
|
ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -255,8 +270,8 @@ public class WalletApplet extends Applet {
|
|||||||
signInProgress = false;
|
signInProgress = false;
|
||||||
pin.reset();
|
pin.reset();
|
||||||
puk.reset();
|
puk.reset();
|
||||||
|
secureChannel.reset();
|
||||||
|
|
||||||
apdu.setIncomingAndReceive();
|
|
||||||
byte[] apduBuffer = apdu.getBuffer();
|
byte[] apduBuffer = apdu.getBuffer();
|
||||||
|
|
||||||
apduBuffer[0] = TLV_APPLICATION_INFO_TEMPLATE;
|
apduBuffer[0] = TLV_APPLICATION_INFO_TEMPLATE;
|
||||||
@ -294,8 +309,7 @@ public class WalletApplet extends Applet {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
len = secureChannel.encryptAPDU(apduBuffer, len);
|
secureChannel.respond(apdu, len, ISO7816.SW_NO_ERROR);
|
||||||
apdu.setOutgoingAndSend(ISO7816.OFFSET_CDATA, len);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -353,14 +367,12 @@ public class WalletApplet extends Applet {
|
|||||||
* @param apdu the JCRE-owned APDU object.
|
* @param apdu the JCRE-owned APDU object.
|
||||||
*/
|
*/
|
||||||
private void verifyPIN(APDU apdu) {
|
private void verifyPIN(APDU apdu) {
|
||||||
apdu.setIncomingAndReceive();
|
|
||||||
|
|
||||||
if (!secureChannel.isOpen()) {
|
if (!secureChannel.isOpen()) {
|
||||||
ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
|
ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
|
||||||
}
|
}
|
||||||
|
|
||||||
byte[] apduBuffer = apdu.getBuffer();
|
byte[] apduBuffer = apdu.getBuffer();
|
||||||
byte len = (byte) secureChannel.decryptAPDU(apduBuffer);
|
byte len = (byte) secureChannel.preprocessAPDU(apduBuffer);
|
||||||
|
|
||||||
if (!pin.check(apduBuffer, ISO7816.OFFSET_CDATA, len)) {
|
if (!pin.check(apduBuffer, ISO7816.OFFSET_CDATA, len)) {
|
||||||
ISOException.throwIt((short)((short) 0x63c0 | (short) pin.getTriesRemaining()));
|
ISOException.throwIt((short)((short) 0x63c0 | (short) pin.getTriesRemaining()));
|
||||||
@ -375,14 +387,12 @@ public class WalletApplet extends Applet {
|
|||||||
* @param apdu the JCRE-owned APDU object.
|
* @param apdu the JCRE-owned APDU object.
|
||||||
*/
|
*/
|
||||||
private void changePIN(APDU apdu) {
|
private void changePIN(APDU apdu) {
|
||||||
apdu.setIncomingAndReceive();
|
|
||||||
|
|
||||||
if (!(secureChannel.isOpen() && pin.isValidated())) {
|
if (!(secureChannel.isOpen() && pin.isValidated())) {
|
||||||
ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
|
ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
|
||||||
}
|
}
|
||||||
|
|
||||||
byte[] apduBuffer = apdu.getBuffer();
|
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))) {
|
if (!(len == PIN_LENGTH && allDigits(apduBuffer, ISO7816.OFFSET_CDATA, len))) {
|
||||||
ISOException.throwIt(ISO7816.SW_WRONG_DATA);
|
ISOException.throwIt(ISO7816.SW_WRONG_DATA);
|
||||||
@ -401,14 +411,12 @@ public class WalletApplet extends Applet {
|
|||||||
* @param apdu the JCRE-owned APDU object.
|
* @param apdu the JCRE-owned APDU object.
|
||||||
*/
|
*/
|
||||||
private void unblockPIN(APDU apdu) {
|
private void unblockPIN(APDU apdu) {
|
||||||
apdu.setIncomingAndReceive();
|
|
||||||
|
|
||||||
if (!(secureChannel.isOpen() && pin.getTriesRemaining() == 0)) {
|
if (!(secureChannel.isOpen() && pin.getTriesRemaining() == 0)) {
|
||||||
ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
|
ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
|
||||||
}
|
}
|
||||||
|
|
||||||
byte[] apduBuffer = apdu.getBuffer();
|
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))) {
|
if (!(len == (PUK_LENGTH + PIN_LENGTH) && allDigits(apduBuffer, ISO7816.OFFSET_CDATA, len))) {
|
||||||
ISOException.throwIt(ISO7816.SW_WRONG_DATA);
|
ISOException.throwIt(ISO7816.SW_WRONG_DATA);
|
||||||
@ -434,15 +442,13 @@ public class WalletApplet extends Applet {
|
|||||||
* @param apdu the JCRE-owned APDU object.
|
* @param apdu the JCRE-owned APDU object.
|
||||||
*/
|
*/
|
||||||
private void loadKey(APDU apdu) {
|
private void loadKey(APDU apdu) {
|
||||||
apdu.setIncomingAndReceive();
|
|
||||||
|
|
||||||
if (!(secureChannel.isOpen() && pin.isValidated())) {
|
if (!(secureChannel.isOpen() && pin.isValidated())) {
|
||||||
ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
|
ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
|
||||||
}
|
}
|
||||||
|
|
||||||
byte[] apduBuffer = apdu.getBuffer();
|
byte[] apduBuffer = apdu.getBuffer();
|
||||||
|
|
||||||
secureChannel.decryptAPDU(apduBuffer);
|
secureChannel.preprocessAPDU(apduBuffer);
|
||||||
boolean newExtended = false;
|
boolean newExtended = false;
|
||||||
|
|
||||||
switch (apduBuffer[ISO7816.OFFSET_P1]) {
|
switch (apduBuffer[ISO7816.OFFSET_P1]) {
|
||||||
@ -586,8 +592,7 @@ public class WalletApplet extends Applet {
|
|||||||
|
|
||||||
byte[] apduBuffer = apdu.getBuffer();
|
byte[] apduBuffer = apdu.getBuffer();
|
||||||
|
|
||||||
apdu.setIncomingAndReceive();
|
short len = secureChannel.preprocessAPDU(apduBuffer);
|
||||||
short len = secureChannel.decryptAPDU(apduBuffer);
|
|
||||||
|
|
||||||
boolean assistedDerivation = (apduBuffer[ISO7816.OFFSET_P1] & DERIVE_P1_ASSISTED_MASK) == DERIVE_P1_ASSISTED_MASK;
|
boolean assistedDerivation = (apduBuffer[ISO7816.OFFSET_P1] & DERIVE_P1_ASSISTED_MASK) == DERIVE_P1_ASSISTED_MASK;
|
||||||
boolean isPublicKey = apduBuffer[ISO7816.OFFSET_P2] == DERIVE_P2_PUBLIC_KEY;
|
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 + 2)] = TLV_PUB_X;
|
||||||
apduBuffer[(short) (SecureChannel.SC_OUT_OFFSET + 3)] = (byte) xLen;
|
apduBuffer[(short) (SecureChannel.SC_OUT_OFFSET + 3)] = (byte) xLen;
|
||||||
|
|
||||||
short outLen = secureChannel.encryptAPDU(apduBuffer, (short) (xLen + sigLen + 4));
|
secureChannel.respond(apdu, (short) (xLen + sigLen + 4), ISO7816.SW_NO_ERROR);
|
||||||
apdu.setOutgoingAndSend(ISO7816.OFFSET_CDATA, outLen);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -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
|
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));
|
secureChannel.respond(apdu, (short) (outOff - SecureChannel.SC_OUT_OFFSET), ISO7816.SW_NO_ERROR);
|
||||||
apdu.setOutgoingAndSend(ISO7816.OFFSET_CDATA, outLen);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -782,8 +785,6 @@ public class WalletApplet extends Applet {
|
|||||||
* @param apdu the JCRE-owned APDU object.
|
* @param apdu the JCRE-owned APDU object.
|
||||||
*/
|
*/
|
||||||
private void sign(APDU apdu) {
|
private void sign(APDU apdu) {
|
||||||
apdu.setIncomingAndReceive();
|
|
||||||
|
|
||||||
if (!(secureChannel.isOpen() && (pin.isValidated() || isPinless()) && privateKey.isInitialized() && !expectPublicKey)) {
|
if (!(secureChannel.isOpen() && (pin.isValidated() || isPinless()) && privateKey.isInitialized() && !expectPublicKey)) {
|
||||||
ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
|
ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
|
||||||
}
|
}
|
||||||
@ -797,7 +798,7 @@ public class WalletApplet extends Applet {
|
|||||||
ISOException.throwIt(ISO7816.SW_INCORRECT_P1P2);
|
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) {
|
if ((apduBuffer[ISO7816.OFFSET_P2] & SIGN_P2_LAST_BLOCK_MASK) == SIGN_P2_LAST_BLOCK_MASK) {
|
||||||
signInProgress = false;
|
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 + 1)] = (byte) 0x81;
|
||||||
apduBuffer[(short)(SecureChannel.SC_OUT_OFFSET + 2)] = (byte) (outLen - 3);
|
apduBuffer[(short)(SecureChannel.SC_OUT_OFFSET + 2)] = (byte) (outLen - 3);
|
||||||
|
|
||||||
outLen = secureChannel.encryptAPDU(apduBuffer, outLen);
|
secureChannel.respond(apdu, outLen, ISO7816.SW_NO_ERROR);
|
||||||
apdu.setOutgoingAndSend(ISO7816.OFFSET_CDATA, outLen);
|
|
||||||
} else {
|
} else {
|
||||||
signature.update(apduBuffer, ISO7816.OFFSET_CDATA, len);
|
signature.update(apduBuffer, ISO7816.OFFSET_CDATA, len);
|
||||||
}
|
}
|
||||||
@ -838,14 +838,12 @@ public class WalletApplet extends Applet {
|
|||||||
* @param apdu the JCRE-owned APDU object.
|
* @param apdu the JCRE-owned APDU object.
|
||||||
*/
|
*/
|
||||||
private void setPinlessPath(APDU apdu) {
|
private void setPinlessPath(APDU apdu) {
|
||||||
apdu.setIncomingAndReceive();
|
|
||||||
|
|
||||||
if (!(secureChannel.isOpen() && pin.isValidated())) {
|
if (!(secureChannel.isOpen() && pin.isValidated())) {
|
||||||
ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
|
ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
|
||||||
}
|
}
|
||||||
|
|
||||||
byte[] apduBuffer = apdu.getBuffer();
|
byte[] apduBuffer = apdu.getBuffer();
|
||||||
short len = secureChannel.decryptAPDU(apduBuffer);
|
short len = secureChannel.preprocessAPDU(apduBuffer);
|
||||||
|
|
||||||
if (((short) (len % 4) != 0) || (len > pinlessPath.length)) {
|
if (((short) (len % 4) != 0) || (len > pinlessPath.length)) {
|
||||||
ISOException.throwIt(ISO7816.SW_WRONG_DATA);
|
ISOException.throwIt(ISO7816.SW_WRONG_DATA);
|
||||||
@ -866,8 +864,6 @@ public class WalletApplet extends Applet {
|
|||||||
* @param apdu the JCRE-owned APDU object.
|
* @param apdu the JCRE-owned APDU object.
|
||||||
*/
|
*/
|
||||||
private void exportKey(APDU apdu) {
|
private void exportKey(APDU apdu) {
|
||||||
apdu.setIncomingAndReceive();
|
|
||||||
|
|
||||||
if (!(secureChannel.isOpen() && pin.isValidated())) {
|
if (!(secureChannel.isOpen() && pin.isValidated())) {
|
||||||
ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
|
ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
|
||||||
}
|
}
|
||||||
@ -904,8 +900,7 @@ public class WalletApplet extends Applet {
|
|||||||
len += (off - SecureChannel.SC_OUT_OFFSET);
|
len += (off - SecureChannel.SC_OUT_OFFSET);
|
||||||
apduBuffer[(SecureChannel.SC_OUT_OFFSET + 1)] = (byte) (len - 2);
|
apduBuffer[(SecureChannel.SC_OUT_OFFSET + 1)] = (byte) (len - 2);
|
||||||
|
|
||||||
len = secureChannel.encryptAPDU(apduBuffer, (short) len);
|
secureChannel.respond(apdu, (short) len, ISO7816.SW_NO_ERROR);
|
||||||
apdu.setOutgoingAndSend(ISO7816.OFFSET_CDATA, len);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
Loading…
x
Reference in New Issue
Block a user