Improve memory handling with SecureMessaging

This commit is contained in:
Arnaud Fontaine 2017-07-07 19:34:46 +02:00
parent f693a7290e
commit ec84edb737
4 changed files with 124 additions and 109 deletions

View File

@ -31,7 +31,7 @@ public final class CmacKey {
protected final byte[] k1; protected final byte[] k1;
protected final byte[] k2; protected final byte[] k2;
protected CmacKey(final byte aesKeyLength) { protected CmacKey(final short aesKeyLength) {
key = (AESKey)KeyBuilder.buildKey(KeyBuilder.TYPE_AES_TRANSIENT_DESELECT, key = (AESKey)KeyBuilder.buildKey(KeyBuilder.TYPE_AES_TRANSIENT_DESELECT,
(short)(aesKeyLength * 8), (short)(aesKeyLength * 8),
false); false);
@ -51,6 +51,10 @@ public final class CmacKey {
Util.arrayFillNonAtomic(k2, (short)0, (short)k2.length, (byte)0); Util.arrayFillNonAtomic(k2, (short)0, (short)k2.length, (byte)0);
} }
protected final short getSize() {
return key.getSize();
}
protected final void setKey(final byte[] buf, final short bufOff) { protected final void setKey(final byte[] buf, final short bufOff) {
key.setKey(buf, bufOff); key.setKey(buf, bufOff);

View File

@ -51,8 +51,13 @@ public final class CmacSignature {
} }
protected final void clear() { protected final void clear() {
if(key != null) {
if(key.isInitialized()) {
key.clearKey();
}
key = null; key = null;
} }
}
private final byte blockLen() { private final byte blockLen() {
return bytes[BYTE_OFFSET_BLOCK_LEN]; return bytes[BYTE_OFFSET_BLOCK_LEN];

View File

@ -27,6 +27,14 @@ import javacardx.crypto.*;
public final class Common { public final class Common {
protected static final short aesKeyLength(final ECParams params) {
if(params.nb_bits < (short)512) {
return (short)16;
} else {
return (short)32;
}
}
protected static final short writeLength(final byte[] buf, short off, final short len) { protected static final short writeLength(final byte[] buf, short off, final short len) {
if(len > 0xff) { if(len > 0xff) {
buf[off] = (byte)0x82; buf[off] = (byte)0x82;

View File

@ -29,7 +29,6 @@ public final class SecureMessaging {
public static final short MAC_LENGTH = (short)(Constants.AES_BLOCK_SIZE / (short)2); public static final short MAC_LENGTH = (short)(Constants.AES_BLOCK_SIZE / (short)2);
protected static final byte[] PADDING_BLOCK = { protected static final byte[] PADDING_BLOCK = {
(byte)0x80, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x80, (byte)0x00, (byte)0x00, (byte)0x00,
(byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00,
@ -37,62 +36,101 @@ public final class SecureMessaging {
(byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00 (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00
}; };
private final byte[] iv; private static final byte[] CRT_PREFIX = {
private final byte[] mac_chaining; (byte)0xA6, (byte)0x0D,
(byte)0x90, (byte)0x02, (byte)0x11, (byte)0x00,
(byte)0x95, (byte)0x01, (byte)0x3C,
(byte)0x80, (byte)0x01, (byte)0x88,
(byte)0x81, (byte)0x01
};
private final Cipher cipher; private final MessageDigest digest;
private final KeyAgreement key_agreement;
private final CmacSignature macer;
private AESKey senc;
private CmacKey smac;
private CmacKey srmac;
protected final PGPKey static_key; protected final PGPKey static_key;
private final Cipher cipher;
private AESKey senc;
private final byte[] iv;
private final CmacSignature macer;
private final byte[] mac_chaining;
private CmacKey sreceiptmac;
private CmacKey smac;
private CmacKey srmac;
protected SecureMessaging(final Transients transients) { protected SecureMessaging(final Transients transients) {
digest = MessageDigest.getInstance(MessageDigest.ALG_SHA_256, false);
key_agreement = KeyAgreement.getInstance(KeyAgreement.ALG_EC_SVDP_DH_PLAIN, false);
static_key = new PGPKey(true);
cipher = Cipher.getInstance(Cipher.ALG_AES_BLOCK_128_CBC_NOPAD, false); cipher = Cipher.getInstance(Cipher.ALG_AES_BLOCK_128_CBC_NOPAD, false);
senc = null;
macer = new CmacSignature();
iv = JCSystem.makeTransientByteArray(Constants.AES_BLOCK_SIZE, iv = JCSystem.makeTransientByteArray(Constants.AES_BLOCK_SIZE,
JCSystem.CLEAR_ON_DESELECT); JCSystem.CLEAR_ON_DESELECT);
macer = new CmacSignature();
mac_chaining = JCSystem.makeTransientByteArray(Constants.AES_BLOCK_SIZE, mac_chaining = JCSystem.makeTransientByteArray(Constants.AES_BLOCK_SIZE,
JCSystem.CLEAR_ON_DESELECT); JCSystem.CLEAR_ON_DESELECT);
sreceiptmac = null;
senc = null;
smac = null; smac = null;
srmac = null; srmac = null;
static_key = new PGPKey(true);
reset(transients); reset(transients);
} }
protected final void clearSession(final Transients transients) { protected final void clearSession(final Transients transients) {
if(senc != null) { if((senc != null) && senc.isInitialized()) {
senc.clearKey(); senc.clearKey();
senc = null;
} }
if(smac != null) {
smac.clearKey();
smac = null;
}
if(srmac != null) {
srmac.clearKey();
srmac = null;
}
macer.clear();
transients.setSecureMessagingEncryptionCounter((short)0);
Util.arrayFillNonAtomic(iv, (short)0, (short)iv.length, (byte)0); Util.arrayFillNonAtomic(iv, (short)0, (short)iv.length, (byte)0);
macer.clear();
Util.arrayFillNonAtomic(mac_chaining, (short)0, (short)mac_chaining.length, (byte)0); Util.arrayFillNonAtomic(mac_chaining, (short)0, (short)mac_chaining.length, (byte)0);
if((sreceiptmac != null) && senc.isInitialized()) {
sreceiptmac.clearKey();
}
if((smac != null) && smac.isInitialized()) {
smac.clearKey();
}
if((srmac != null) && srmac.isInitialized()) {
srmac.clearKey();
}
transients.setSecureMessagingEncryptionCounter((short)0);
} }
protected final void reset(final Transients transients) { protected final void reset(final Transients transients) {
clearSession(transients); clearSession(transients);
sreceiptmac = null;
senc = null;
smac = null;
srmac = null;
static_key.reset(); static_key.reset();
} }
private final void initSession(final short keyLength,
final byte[] buf, final short off) {
if((sreceiptmac == null) ||
(sreceiptmac.getSize() != (short)(keyLength * 8))) {
senc = (AESKey)KeyBuilder.buildKey(KeyBuilder.TYPE_AES_TRANSIENT_DESELECT,
(short)(keyLength * 8),
false);
sreceiptmac = new CmacKey(keyLength);
smac = new CmacKey(keyLength);
srmac = new CmacKey(keyLength);
}
sreceiptmac.setKey(buf, off);
senc.setKey(buf, (short)(off + keyLength));
smac.setKey(buf, (short)(off + (short)(2 * keyLength)));
srmac.setKey(buf, (short)(off + (short)(3 * keyLength)));
}
protected final boolean isInitialized() { protected final boolean isInitialized() {
return static_key.isInitialized(); return static_key.isInitialized();
} }
@ -104,38 +142,27 @@ public final class SecureMessaging {
&& (srmac != null) && srmac.isInitialized(); && (srmac != null) && srmac.isInitialized();
} }
private static final byte aesKeyLength(final ECParams params) {
if(params.nb_bits < (short)512) {
return (byte)16;
} else {
return (byte)32;
}
}
private final short scp11b(final ECParams params, private final short scp11b(final ECCurves curves,
final byte[] buf, final short len) { final byte[] buf, final short len) {
final byte[] crt = new byte[]{ (byte)0xA6, (byte)0x0D, final ECParams params = static_key.ecParams(curves);
(byte)0x90, (byte)0x02, (byte)0x11, (byte)0x00,
(byte)0x95, (byte)0x01, (byte)0x3C,
(byte)0x80, (byte)0x01, (byte)0x88,
(byte)0x81, (byte)0x01 };
if(len <= (short)((short)crt.length + 4)) { if(len <= (short)((short)CRT_PREFIX.length + 4)) {
ISOException.throwIt(ISO7816.SW_WRONG_DATA); ISOException.throwIt(ISO7816.SW_WRONG_DATA);
return 0; return 0;
} }
if(Util.arrayCompare(crt, (short)0, if(Util.arrayCompare(CRT_PREFIX, (short)0,
buf, (short)0, buf, (short)0,
(short)crt.length) != (byte)0) { (short)CRT_PREFIX.length) != (byte)0) {
ISOException.throwIt(ISO7816.SW_WRONG_DATA); ISOException.throwIt(ISO7816.SW_WRONG_DATA);
return 0; return 0;
} }
short off = (short)crt.length; short off = (short)CRT_PREFIX.length;
if(buf[off] != aesKeyLength(params)) { if(buf[off] != Common.aesKeyLength(params)) {
ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
return 0; return 0;
} }
@ -147,7 +174,7 @@ public final class SecureMessaging {
} }
off += 2; off += 2;
final short keylen = Common.readLength(buf, off, len); short keylen = Common.readLength(buf, off, len);
off = Common.skipLength(buf, off, len); off = Common.skipLength(buf, off, len);
@ -161,17 +188,17 @@ public final class SecureMessaging {
return 0; return 0;
} }
final ECPrivateKey eskcard = (ECPrivateKey)KeyBuilder.buildKey(KeyBuilder.TYPE_EC_FP_PRIVATE, ECPrivateKey eskcard = (ECPrivateKey)KeyBuilder.buildKey(KeyBuilder.TYPE_EC_FP_PRIVATE,
params.nb_bits, params.nb_bits,
false); false);
final ECPublicKey epkcard = (ECPublicKey)KeyBuilder.buildKey(KeyBuilder.TYPE_EC_FP_PUBLIC, ECPublicKey epkcard = (ECPublicKey)KeyBuilder.buildKey(KeyBuilder.TYPE_EC_FP_PUBLIC,
params.nb_bits, params.nb_bits,
false); false);
params.setParams(eskcard); params.setParams(eskcard);
params.setParams(epkcard); params.setParams(epkcard);
final KeyPair ekcard = new KeyPair(epkcard, eskcard); KeyPair ekcard = new KeyPair(epkcard, eskcard);
ekcard.genKeyPair(); ekcard.genKeyPair();
@ -181,17 +208,16 @@ public final class SecureMessaging {
return 0; return 0;
} }
final KeyAgreement ka = KeyAgreement.getInstance(KeyAgreement.ALG_EC_SVDP_DH_PLAIN, false); key_agreement.init(eskcard);
ka.init(eskcard);
short msglen = 0; short msglen = 0;
msglen += ka.generateSecret(buf, off, keylen, buf, len); msglen += key_agreement.generateSecret(buf, off, keylen, buf, len);
eskcard.clearKey(); eskcard.clearKey();
eskcard = null;
static_key.initKeyAgreement(ka); static_key.initKeyAgreement(key_agreement);
msglen += ka.generateSecret(buf, off, keylen, buf, (short)(len + msglen)); msglen += key_agreement.generateSecret(buf, off, keylen, buf, (short)(len + msglen));
Util.setShort(buf, (short)(len + msglen), (short)0); Util.setShort(buf, (short)(len + msglen), (short)0);
msglen += 2; msglen += 2;
@ -200,52 +226,26 @@ public final class SecureMessaging {
off = (short)(len + msglen); off = (short)(len + msglen);
msglen += 2; msglen += 2;
buf[(short)(len + msglen)] = crt[(short)8]; buf[(short)(len + msglen)] = CRT_PREFIX[(short)8];
++msglen; ++msglen;
buf[(short)(len + msglen)] = crt[(short)11]; buf[(short)(len + msglen)] = CRT_PREFIX[(short)11];
++msglen; ++msglen;
buf[(short)(len + msglen)] = buf[crt.length]; buf[(short)(len + msglen)] = buf[CRT_PREFIX.length];
++msglen; ++msglen;
short keydata_len = 0; keylen = 0;
final MessageDigest digest = MessageDigest.getInstance(MessageDigest.ALG_SHA_256, false); while(keylen < (short)(4 * buf[CRT_PREFIX.length])) {
while(keydata_len < (short)(4 * buf[crt.length])) {
Util.setShort(buf, off, counter); Util.setShort(buf, off, counter);
++counter; ++counter;
keydata_len += digest.doFinal(buf, len, msglen, keylen += digest.doFinal(buf, len, msglen,
buf, (short)(len + msglen + keydata_len)); buf, (short)(len + msglen + keylen));
} }
final CmacKey sreceiptmac = new CmacKey(aesKeyLength(params)); initSession(Common.aesKeyLength(params), buf, (short)(len + msglen));
sreceiptmac.setKey(buf, (short)(len + msglen));
if(senc != null) { Util.arrayFillNonAtomic(buf, len, (short)(msglen + keylen), (byte)0);
senc.clearKey();
senc = null;
}
senc = (AESKey)KeyBuilder.buildKey(KeyBuilder.TYPE_AES_TRANSIENT_DESELECT,
(short)(aesKeyLength(params) * 8),
false);
senc.setKey(buf, (short)(len + msglen + aesKeyLength(params)));
if(smac != null) {
smac.clearKey();
smac = null;
}
smac = new CmacKey(aesKeyLength(params));
smac.setKey(buf, (short)(len + msglen + 2 * aesKeyLength(params)));
if(srmac != null) {
srmac.clearKey();
srmac = null;
}
srmac = new CmacKey(aesKeyLength(params));
srmac.setKey(buf, (short)(len + msglen + 3 * aesKeyLength(params)));
Util.arrayFillNonAtomic(buf, len, (short)(msglen + keydata_len), (byte)0);
off = len; off = len;
Util.setShort(buf, off, (short)0x5F49); Util.setShort(buf, off, (short)0x5F49);
@ -255,6 +255,9 @@ public final class SecureMessaging {
msglen = off; msglen = off;
epkcard.clearKey(); epkcard.clearKey();
epkcard = null;
ekcard = null;
buf[off++] = (byte)0x86; buf[off++] = (byte)0x86;
buf[off++] = (byte)Constants.AES_BLOCK_SIZE; buf[off++] = (byte)Constants.AES_BLOCK_SIZE;
@ -262,7 +265,6 @@ public final class SecureMessaging {
macer.init(sreceiptmac); macer.init(sreceiptmac);
macer.sign(buf, (short)0, msglen, macer.sign(buf, (short)0, msglen,
buf, off, Constants.AES_BLOCK_SIZE); buf, off, Constants.AES_BLOCK_SIZE);
sreceiptmac.clearKey();
macer.clear(); macer.clear();
Util.arrayCopy(buf, off, mac_chaining, (short)0, Constants.AES_BLOCK_SIZE); Util.arrayCopy(buf, off, mac_chaining, (short)0, Constants.AES_BLOCK_SIZE);
@ -284,11 +286,7 @@ public final class SecureMessaging {
clearSession(transients); clearSession(transients);
if(isInitialized() && static_key.isEc()) { if(isInitialized() && static_key.isEc()) {
final ECParams params = static_key.ecParams(ec); return scp11b(ec, buf, len);
if(params != null) {
return scp11b(params, buf, len);
}
} }
ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);