SecureApplet

September 23, 2020 ยท View on GitHub

A base class used by all applets in this repo. Provides PIN code and secure communication functionalities. Most commands of the applets are only accessible over secure channel.

APDUs

Unsecure Get Random

Plaintext command. Provides 32 random bytes to the host.

FieldValue
CLA0xB0
INS0xB1
P0, P1ignored, use for example 0x00 for both
DATAignored
RETURNSW: 0x9000, DATA: 32 random bytes

Example: B0B10000 -> returns 32 random bytes

Get card's static public key

This key is generated when the applet is installed. It will remain the same whenever you insert the card, but when applet is updated it will be re-generated.

Once you know the card's static public key you can use it to establish secure communication channel. You can also verify that the card is the same next time you start talking to it.

Corresponding private key is used to sign return message from the card when openning a secure channel.

This APDU returns 65-byte sequence with serialized uncompressed public key (0x04<x><y>)

FieldValue
CLA0xB0
INS0xB2
P0, P1ignored, use for example 0x00 for both
DATAignored
RETURNSW: 0x9000, DATA: 65 bytes with serialized static pubkey

Example: B0B20000 -> returns 65 bytes with static public key of the card, for example 045879312CB80C51B6FF53EF946603E64CCA37C9E06D96E7FB7BB798F822117D89FB537A4F53EA59802946AB4532BCD403EFA20518360411C262C010B1A496B39C.

Establish secure channel

For secure communication we need to establish shared secrets. For this we use ECDH key agreement. We use AES_CBC for encryption with M2 padding (add 0x8000..00 to round to 16-byte blocks). Truncated to 14 bytes HMAC-SHA256 is used for authentication and applied to the ciphertext (encrypt-then-hmac).

There are 3 different modes you can use - ss, es and ee.

  • In ss mode both card and host use static public keys and 32-byte random nonces. Shared secret is derived as sha256(ecdh(s,s) | host_nonce | card_nonce).
  • In es mode the host uses a random key, card uses a static key and a random nonce. Shared secret is derived as sha256(ecdh(e,s) | card_nonce).
  • In ee mode both use random keys, secret is calculated as sha256(ecdh(e,e)).

This shared secret is used to derive 4 keys for encryption and authentication for each side:

  • host_aes_key=SHA256('host_aes'|secret) - symmetric key for data from the host
  • card_aes_key=SHA256('card_eas'|secret) - symmetric key for data coming from the card
  • host_mac_key=SHA256('host_mac'|secret) - authentication key for data from the host
  • card_mac_key=SHA256('card_mac'|secret) - authentication key for data coming from the card

When secure channel is established iv for the AES cypher is set to 0 and incremented on every message. We can use the same iv both for incoming and outgoing data because we use different keys on each side. iv is not transmitted but is used in hmac authentication.

If you are out of sync for some reason just re-establish secure channel. If iv is hitting the limit of 16 bytes - also re-establish secure channel.

Establish secure channel in SS mode

Returns < 32-byte card nonce > | <14 byte HMAC-SHA256(card_key, data)> | ECDSA_SIGNATURE.

FieldValue
CLA0xB0
INS0xB3
P0, P1ignored, use for example 0x00 for both
DATA<host_pubkey><host_nonce>: 65-byte public key of the host serialized in uncompressed form followed by the 32-byte host nonce
RETURNSW: 0x9000, DATA: `< 32-byte card nonce >

Establish secure channel in ES mode

Returns < 32-byte card nonce > | < 14 byte HMAC-SHA256(card_key, data)> | ECDSA_SIGNATURE.

FieldValue
CLA0xB0
INS0xB4
P0, P1ignored, use for example 0x00 for both
DATA65-byte public key of the host serialized in uncompressed form
RETURNSW: 0x9000, DATA: `< 32-byte card nonce >

Establish secure channel in EE mode

Returns < card ephemeral pubkey > | < 14 byte HMAC-SHA256(card_key, data)> | ECDSA_SIG(card_pubkey, data incl HMAC).

FieldValue
CLA0xB0
INS0xB5
P0, P1ignored, use for example 0x00 for both
DATA65-byte public key of the host serialized in uncompressed form
RETURNSW: 0x9000, DATA: 65-byte cards fresh pubkey followed by HMAC-SHA256(card_key, data) (first 14 bytes), then ECDSA signature signing all previous data

Secure message

All commands via secure channel are sent with this APDU. If decryption or authentication check failed the card will throw an error and close the channel. Otherwise it will always return 0x9000, but inside the payload it will send the actual data or error code.

FieldValue
CLA0xB0
INS0xB6
P0, P1ignored, use for example 0x00 for both
DATAencrypted payload
RETURNSW: 0x9000, DATA: encrypted responce

Maximum size of the encrypted payload is 255 bytes. Even though we could use extended APDU, but we don't really need this. We have very strict RAM limits anyways, so we can always work with 255 bytes or less.

Message is formed as follows:

  • All messages coming from the host should be encrypted using host_aes_key and authenticated with host_mac_key
  • All responces from card are encrypted with card_aes_key and authenticated with card_mac_key
  • AES-CBC with M2 padding (0x8000...00) is used to round data to 16-byte AES blocks.
  • For authentication we use first 14 bytes of HMAC-SHA256(key, iv | ciphertext)
  • You need to increase iv after every request to the card.

Encrypted packet format: < ciphertext > < hmac_sha256(key, iv|ciphertext)[:15] >

Close channel

Closes secure communication channel. Internally overwrites all session keys with random junk, so nobody will be able to communicate with the card. I have no idea what's the reason to do that...

FieldValue
CLA0xB0
INS0xB7
P0, P1ignored, use for example 0x00 for both
DATAignored
RETURNSW: 0x9000, DATA: empty

Secure channel commands

All secure channel commands are transmitted encrypted and authenticated using a Secure Message APDU.

Each command starts with two command bytes (CMD and SUBCMD) and followed by the payload.

Error codes are transmitted over secure channel as well. Here are the common errorcodes we use:

CodeValueComment
SUCCESS9000Everything is fine
INVALID_LEN0403Payload has wrong length
INVALID_CMD0404Command is unknown
INVALID_SUBCMD0405Subcommand is unknown
NOT_IMPLEMENTED0406Functionality is not available
CARD_LOCKED0501Card is locked, but request requires unlocked card
INVALID_PIN0502Provided PIN code is incorrect
NO_ATTEMPTS_LEFT0503Card is blocked - number of PIN attempts reached 0
ALREADY_UNLOCKED0504Card is already unlocked
NOT_INITIALIZED0505Card is not set up yet
PIN_ALREADY_SET0506PIN code already exists, unset PIN first

Echo

Simple command that sends back the same responce. Useful to test secure communication as the payload is decrypted and re-encrypted on the card.

FieldValue
CMD0x00
SUBCMD0x00
DATAdata to echo
RETURNResponce code: 0x9000, DATA: same as incoming data

Get random

Returns 32 bytes of random data over secure channel

FieldValue
CMD0x01
SUBCMD0x00
DATAignored
RETURNResponce code: 0x9000, DATA: 32 bytes of random data

Pin commands

PIN code can be any sequence of bytes up to 32 bytes long. Number of PIN attempts is limited to 10. By default PIN code is not set. Use Set PIN command to initialize it.

Pin status

Returns current status of the card, 3 bytes long:

  • Number of attempts left
  • Max number of attempts
  • Current card status:
    • 0x00 if PIN is disabled
    • 0x01 if PIN is set but the card is locked
    • 0x02 if PIN is set and the card is unlocked
    • 0x03 if the card is bricked (number of attempts reached zero)
FieldValue
CMD0x03
SUBCMD0x00
DATAignored
RETURNResponce code: 0x9000, DATA: 3-byte status

Unlock with PIN

Unlock the card with the PIN code.

FieldValue
CMD0x03
SUBCMD0x01
DATAPIN code
RETURNResponce code: 0x9000 on success, error code otherwise

Lock the card

Lock the card.

FieldValue
CMD0x03
SUBCMD0x02
DATAIgnored
RETURNResponce code: 0x9000 on success, error code otherwise

Change PIN

Changes the PIN code. Requires both old and new PIN codes in the payload. In this case PIN codes should be encoded as len-value pairs

FieldValue
CMD0x03
SUBCMD0x03
DATA<len_old><pin_old><len_new><pin_new>
RETURNResponce code: 0x9000 on success, error code otherwise

Set PIN

Enables PIN code and sets it to the value in the payload.

FieldValue
CMD0x03
SUBCMD0x04
DATAPIN code
RETURNResponce code: 0x9000 on success, error code otherwise

Unset PIN

Disables PIN code. Requires PIN code in the payload.

FieldValue
CMD0x03
SUBCMD0x05
DATAPIN code
RETURNResponce code: 0x9000 on success, error code otherwise