Note: This should be considered a proof-of-concept, not a production-ready implementation.
The widely implemented symmetric authentication functions on JavaCard are, for the most-part, relatively weak. While there is no particular weakness in CBC-MAC, if properly implemented, the constraints on it make CBC-MAC difficult to use in real life.
A number of example attacks are shown on Wikipedia, including where the same symmetric key is used both for CBC-MAC and for encryption, or where a variable-length message is being authenticated.
There is therefore a benefit in using a symmetric authenticator function which does not require considerations such as whether or not a message which prefixes another message will ever be created. HMAC functions are ideal for this, since they are based on cryptographic hash functions.
Unfortunately, HMAC functions, while supported in JavaCard 2.2.2, are not widely implemented; only a handful of cards implement any HMAC.
A limited implementation of HMAC on JavaCard is available in this repository. Note that this is not a full implementation compatible with the reference RFC in every way. In particular, your attention is drawn to the lack of support for keys which are longer than the hash block length (for SHA256 this is 64 bytes), or where the key is shorter than expected.
This is therefore not for use with arbitrary keys - it should only be used where a fixed, 32-byte (256-bit) symmetric key can be securely generated by the smartcard.
Note that for ease of verification, the applet enclosed features a function to retrieve the symmetric HMAC key from the device. It should go without saying, but this must be removed for any sensible use beyond learning or playing around. Anyone who gains access to the HMAC key can generate valid signatures of any data, so this key must not be exposed by the smartcard.
In order to verify the operation of this HMAC_SHA256 implementation, a Python script is enclosed, which will calculate HMAC_SHA256 of a given message, using the same HMAC key. If the Python implementation and smartcard agree, this indicates the implementation operates correctly, at least for that scenario.
Take heed of the warning above, however, about this implementation not having support for variable-length (i.e. user-set) keys. Use this only with data of up to 255 bytes, and a key of 32 bytes (256 bits). This is a suitably long key for HMAC into the future.
To test using GlobalPlatform,
-
Install the applet
$ java -jar gp.jar --default --install <compiled_name.cap>
-
Generate a new HMAC key
$ java -jar gp.jar --applet 00112233445500 -a 8000000000 -d
-
Retrieve the HMAC key (note that no real-world applet should ever allow this!)
$ java -jar gp.jar --applet 00112233445500 -a 8002000020 -d
-
Generate an HMAC of a message (upto 255 bytes is possible) - for example, let's do the 5 bytes:
{0x01, 0x02, 0x03, 0x04, 0x05}
$ java -jar gp.jar --applet 00112233445500 -a 8001000005010203040520 -d
-
Place the returned values into the Python script and ensure signature verification succeeds. Ensure the public key, message and signature values are all updated properly.
Generate a new key per step 2 as required, to test for multiple keys