document Crypto utility class

This commit is contained in:
Michele Balistreri 2017-10-28 19:43:07 +03:00
parent a216b29b97
commit 10a429bf6d
3 changed files with 94 additions and 3 deletions

View File

@ -166,7 +166,8 @@ signing sessions, if any. Unless a DERIVE KEY is sent, a subsequent SIGN command
* Data = a sequence of 32-bit integers (most significant byte first). Empty if the master key must be used. On assisted
derivation contains a public key when P2 = 0x02.
* Response SW = 0x9000 on success, 0x6A80 if the format is invalid, 0x6A81 if public key derivation is not supported and
bit 0 of P1 is set, 0x6A86 if P2 = 0x01 and bit 0 of P1 is not set.
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
@ -176,7 +177,9 @@ SIGN sessions. Because JavaCard does not offer native EC point multiplication be
alternative mode of operation where the public key is partially derived off-card and loaded back on card. In this mode
of operation only 1 derivation step at the time can be completed and as such during assisted derivation the data can
contain a single 32-bit integer, instead of a sequence. The maximum depth of derivation from the master key is 10. Any
attempt to get deeper results in 0x6A80 being returned.
attempt to get deeper results in 0x6A80 being returned. The BIP32 specifications define a few checks which must be
performed on the derived keys. If these fail, the 0x6984 is returned and the invalid key is discarded. A client should
perform a GET STATUS command to get the actual current key path and resume derivation using a different path.
P1:
* bit 0 = if 0 derive autonomously (only works if public key derivation is supported), if 1 do assisted derivation

View File

@ -4,6 +4,10 @@ import javacard.framework.JCSystem;
import javacard.framework.Util;
import javacard.security.*;
/**
* Crypto utilities, mostly BIP32 related. The init method must be called during application installation. This class
* is not meant to be instantiated.
*/
public class Crypto {
final static private short KEY_SECRET_SIZE = 32;
final static private short KEY_DERIVATION_INPUT_SIZE = 37;
@ -14,6 +18,7 @@ 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
static RandomData random;
static MessageDigest sha256;
@ -23,6 +28,9 @@ public class Crypto {
private static byte[] tmp;
/**
* Initializes the objects required by this class. Must be invoked exactly 1 time during application installation.
*/
static void init() {
random = RandomData.getInstance(RandomData.ALG_SECURE_RANDOM);
sha256 = MessageDigest.getInstance(MessageDigest.ALG_SHA_256, false);
@ -42,6 +50,19 @@ public class Crypto {
tmp = JCSystem.makeTransientByteArray((short) (HMAC_BLOCK_OFFSET + blockSize), JCSystem.CLEAR_ON_RESET);
}
/**
* Derives a private key according to the algorithm defined in BIP32. The BIP32 specifications define some checks
* to be performed on the derived keys. In the very unlikely event that these checks fail this key is not considered
* to be valid so the derived key is discarded and this method returns false.
*
* @param i the buffer containing the key path element (a 32-bit big endian integer)
* @param iOff the offset in the buffer
* @param privateKey the parent private key
* @param publicKey the parent public key
* @param chain the chain code
* @param chainOff the offset in the chain code buffer
* @return true if successful, false otherwise
*/
static boolean bip32CKDPriv(byte[] i, short iOff, ECPrivateKey privateKey, ECPublicKey publicKey, byte[] chain, short chainOff) {
short off = 0;
@ -76,6 +97,19 @@ public class Crypto {
return true;
}
/**
* Calculates the HMAC-SHA512 with the given key and data. Uses a software implementation which only requires SHA-512
* to be supported on cards which do not have native HMAC-SHA512.
*
* @param key the HMAC key
* @param keyOff the offset of the key
* @param keyLen the length of the key
* @param in the input data
* @param inOff the offset of the input data
* @param inLen the length of the input data
* @param out the output buffer
* @param outOff the offset in the output buffer
*/
private static void hmacSHA512(byte[] key, short keyOff, short keyLen, byte[] in, short inOff, short inLen, byte[] out, short outOff) {
if (hmacSHA512 != null) {
hmacKey.setKey(key, keyOff, keyLen);
@ -100,12 +134,33 @@ public class Crypto {
}
}
/**
* Modulo addition of two 256-bit numbers.
*
* @param a the a operand
* @param aOff the offset of the a operand
* @param b the b operand
* @param bOff the offset of the b operand
* @param n the modulo
* @param nOff the offset of the modulo
* @param out the output buffer
* @param outOff the offset in the output buffer
*/
private static void addm256(byte[] a, short aOff, byte[] b, short bOff, byte[] n, short nOff, byte[] out, short outOff) {
if ((add256(a, aOff, b, bOff, out, outOff) != 0) || (ucmp256(out, outOff, n, nOff) > 0)) {
sub256(out, outOff, n, nOff, out, outOff);
}
}
/**
* Compares two 256-bit numbers. Returns 1 if a > b, -1 if a < b and 0 if a = b.
*
* @param a the a operand
* @param aOff the offset of the a operand
* @param b the b operand
* @param bOff the offset of the b operand
* @return the comparison result
*/
private static short ucmp256(byte[] a, short aOff, byte[] b, short bOff) {
short ai, bi;
for (short i = 0 ; i < 32; i++) {
@ -120,6 +175,13 @@ public class Crypto {
return 0;
}
/**
* Checks if the given 256-bit number is 0.
*
* @param a the a operand
* @param aOff the offset of the a operand
* @return true if a is 0, false otherwise
*/
private static boolean isZero256(byte[] a, short aOff) {
boolean isZero = true;
@ -133,6 +195,17 @@ public class Crypto {
return isZero;
}
/**
* Addition of two 256-bit numbers.
*
* @param a the a operand
* @param aOff the offset of the a operand
* @param b the b operand
* @param bOff the offset of the b operand
* @param out the output buffer
* @param outOff the offset in the output buffer
* @return the carry of the addition
*/
private static short add256(byte[] a, short aOff, byte[] b, short bOff, byte[] out, short outOff) {
short outI = 0;
for (short i = 31 ; i >= 0 ; i--) {
@ -143,6 +216,17 @@ public class Crypto {
return outI;
}
/**
* Subtraction of two 256-bit numbers.
*
* @param a the a operand
* @param aOff the offset of the a operand
* @param b the b operand
* @param bOff the offset of the b operand
* @param out the output buffer
* @param outOff the offset in the output buffer
* @return the carry of the subtraction
*/
private static short sub256(byte[] a, short aOff, byte[] b, short bOff, byte[] out, short outOff) {
short outI = 0;

View File

@ -580,7 +580,11 @@ public class WalletApplet extends Applet {
for (short i = ISO7816.OFFSET_CDATA; i < chainEnd; i += 4) {
JCSystem.beginTransaction();
Crypto.bip32CKDPriv(apduBuffer, i, privateKey, publicKey, chainCode, (short) 0);
if (!Crypto.bip32CKDPriv(apduBuffer, i, privateKey, publicKey, chainCode, (short) 0)) {
ISOException.throwIt(ISO7816.SW_DATA_INVALID);
}
Util.arrayCopy(apduBuffer, i, keyPath, keyPathLen, (short) 4);
if (assistedDerivation) {