Initial code commit
This commit is contained in:
commit
96130738b8
40 files changed
+2753
No files matched your search
@@ -0,0 +1,3 @@
|
|||||||
|
> "A wise man never leaves debugging symbols in production firmware"
|
||||||
|
>
|
||||||
|
> Sun Tzu, Art of War
|
||||||
@@ -0,0 +1,231 @@
|
|||||||
|
package sega.a7;
|
||||||
|
|
||||||
|
import javacard.framework.APDU;
|
||||||
|
import javacard.framework.Applet;
|
||||||
|
import javacard.framework.Util;
|
||||||
|
import javacard.security.CryptoException;
|
||||||
|
import javacard.security.MessageDigest;
|
||||||
|
import javacard.security.RandomData;
|
||||||
|
import javacardx.apdu.ExtendedLength;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SEGA A7 keychip applet.
|
||||||
|
*
|
||||||
|
* <p>The ISO 7816 command header is ignored. Every APDU carries a chunk of an
|
||||||
|
* internal A7 frame; the applet accumulates chunks until the total size in the
|
||||||
|
* frame header is available, then dispatches on the 16-bit ordinal at offset 4.
|
||||||
|
* Responses are placed at APDU buffer offset 0.</p>
|
||||||
|
*/
|
||||||
|
public class A7Firm extends Applet implements ExtendedLength {
|
||||||
|
|
||||||
|
private final CommandServer m_ee_server;
|
||||||
|
private final AppData m_ee_data;
|
||||||
|
private final MessageDigest m_ee_sha1;
|
||||||
|
private final HmacSha1 m_ee_hmacSha1;
|
||||||
|
private final RandomData m_ee_random;
|
||||||
|
private final MakePacket m_ee_makePacket;
|
||||||
|
private final Crypt m_ee_crypt;
|
||||||
|
private final AppBuffer m_ee_buffer;
|
||||||
|
|
||||||
|
public A7Firm() {
|
||||||
|
m_ee_random = RandomData.getInstance(RandomData.ALG_SECURE_RANDOM);
|
||||||
|
m_ee_data = new AppData(m_ee_random);
|
||||||
|
m_ee_server = new CommandServer();
|
||||||
|
m_ee_sha1 = MessageDigest.getInstance(MessageDigest.ALG_SHA, false);
|
||||||
|
m_ee_hmacSha1 = new HmacSha1();
|
||||||
|
m_ee_makePacket = new MakePacket();
|
||||||
|
m_ee_crypt = new Crypt();
|
||||||
|
m_ee_buffer = new AppBuffer();
|
||||||
|
|
||||||
|
byte[] modulus = m_ee_data.getRsaDefaultPriModulus_ee();
|
||||||
|
byte[] exponent = m_ee_data.getRsaDefaultPriExponent_ee();
|
||||||
|
|
||||||
|
byte[] tmp = m_ee_data.getCryptBuffer_ramD();
|
||||||
|
Util.arrayFillNonAtomic(tmp, (short) 0, (short) tmp.length, (byte) 0xFF);
|
||||||
|
check(m_ee_crypt.init(tmp, (short) 0, (short) tmp.length,
|
||||||
|
tmp, (short) 0, (short) tmp.length, AppConfig.CRYPT_CONN_DECRYPT));
|
||||||
|
check(m_ee_crypt.init(tmp, (short) 0, (short) tmp.length,
|
||||||
|
tmp, (short) 0, (short) tmp.length, AppConfig.CRYPT_CONN_ENCRYPT));
|
||||||
|
check(m_ee_crypt.init(tmp, (short) 0, (short) tmp.length,
|
||||||
|
tmp, (short) 0, (short) tmp.length, AppConfig.CRYPT_GKEY_DECRYPT));
|
||||||
|
check(m_ee_crypt.init(tmp, (short) 0, (short) tmp.length,
|
||||||
|
tmp, (short) 0, (short) tmp.length, AppConfig.CRYPT_GKEY_ENCRYPT));
|
||||||
|
check(m_ee_crypt.init(modulus, (short) 0, (short) modulus.length,
|
||||||
|
exponent, (short) 0, (short) exponent.length,
|
||||||
|
AppConfig.CRYPT_RSA_DECRYPT));
|
||||||
|
|
||||||
|
m_ee_server.setNoCommand(new NoCommand((short) -1, m_ee_data,
|
||||||
|
m_ee_makePacket, m_ee_sha1));
|
||||||
|
|
||||||
|
m_ee_server.attachCommand(new SessionOpen(AppConfig.ORD_SESSION_OPEN,
|
||||||
|
m_ee_data, m_ee_makePacket, m_ee_sha1));
|
||||||
|
m_ee_server.attachCommand(new SessionClose(AppConfig.ORD_SESSION_CLOSE,
|
||||||
|
m_ee_data, m_ee_makePacket, m_ee_sha1));
|
||||||
|
m_ee_server.attachCommand(new AuthStart(AppConfig.ORD_AUTH_START,
|
||||||
|
m_ee_data, m_ee_makePacket, m_ee_sha1, m_ee_hmacSha1, m_ee_crypt));
|
||||||
|
m_ee_server.attachCommand(new HmacChange(AppConfig.ORD_HMAC_CHANGE,
|
||||||
|
m_ee_data, m_ee_makePacket, m_ee_sha1, m_ee_hmacSha1, m_ee_crypt));
|
||||||
|
m_ee_server.attachCommand(new KeyChange(AppConfig.ORD_KEY_CHANGE,
|
||||||
|
m_ee_data, m_ee_makePacket, m_ee_sha1, m_ee_hmacSha1, m_ee_crypt));
|
||||||
|
m_ee_server.attachCommand(new LvGet(AppConfig.ORD_LV_GET,
|
||||||
|
m_ee_data, m_ee_makePacket, m_ee_sha1));
|
||||||
|
m_ee_server.attachCommand(new ErrorGet(AppConfig.ORD_ERROR_GET,
|
||||||
|
m_ee_data, m_ee_makePacket, m_ee_sha1));
|
||||||
|
m_ee_server.attachCommand(new VerGet(AppConfig.ORD_VER_GET,
|
||||||
|
m_ee_data, m_ee_makePacket, m_ee_sha1));
|
||||||
|
m_ee_server.attachCommand(new KeychipInfoWrite(AppConfig.ORD_KEYCHIP_INFO_WRITE,
|
||||||
|
m_ee_data, m_ee_makePacket, m_ee_sha1, m_ee_hmacSha1, m_ee_crypt));
|
||||||
|
m_ee_server.attachCommand(new KeychipInfoRead(AppConfig.ORD_KEYCHIP_INFO_READ,
|
||||||
|
m_ee_data, m_ee_makePacket, m_ee_sha1, m_ee_hmacSha1, m_ee_crypt));
|
||||||
|
m_ee_server.attachCommand(new GkeyWrite(AppConfig.ORD_GKEY_WRITE,
|
||||||
|
m_ee_data, m_ee_makePacket, m_ee_sha1, m_ee_hmacSha1, m_ee_crypt));
|
||||||
|
m_ee_server.attachCommand(new GkeyEnc(AppConfig.ORD_GKEY_ENC,
|
||||||
|
m_ee_data, m_ee_makePacket, m_ee_sha1, m_ee_hmacSha1, m_ee_crypt));
|
||||||
|
m_ee_server.attachCommand(new GkeyDec(AppConfig.ORD_GKEY_DEC,
|
||||||
|
m_ee_data, m_ee_makePacket, m_ee_sha1, m_ee_hmacSha1, m_ee_crypt));
|
||||||
|
m_ee_server.attachCommand(new SflagWrite(AppConfig.ORD_SFLAG_WRITE,
|
||||||
|
m_ee_data, m_ee_makePacket, m_ee_sha1, m_ee_hmacSha1, m_ee_crypt));
|
||||||
|
m_ee_server.attachCommand(new EepInit(AppConfig.ORD_EEP_INIT,
|
||||||
|
m_ee_data, m_ee_makePacket, m_ee_sha1, m_ee_hmacSha1, m_ee_crypt));
|
||||||
|
m_ee_server.attachCommand(new LvEnable(AppConfig.ORD_LV_ENABLE,
|
||||||
|
m_ee_data, m_ee_makePacket, m_ee_sha1));
|
||||||
|
m_ee_server.attachCommand(new PrikeyModulusWrite(AppConfig.ORD_PRIKEY_MODULUS_WRITE,
|
||||||
|
m_ee_data, m_ee_makePacket, m_ee_sha1, m_ee_hmacSha1, m_ee_crypt));
|
||||||
|
m_ee_server.attachCommand(new PrikeyExponentWrite(AppConfig.ORD_PRIKEY_EXPONENT_WRITE,
|
||||||
|
m_ee_data, m_ee_makePacket, m_ee_sha1, m_ee_hmacSha1, m_ee_crypt));
|
||||||
|
m_ee_server.attachCommand(new UdataWrite(AppConfig.ORD_UDATA_WRITE,
|
||||||
|
m_ee_data, m_ee_makePacket, m_ee_sha1, m_ee_hmacSha1, m_ee_crypt));
|
||||||
|
m_ee_server.attachCommand(new UdataRead(AppConfig.ORD_UDATA_READ,
|
||||||
|
m_ee_data, m_ee_makePacket, m_ee_sha1, m_ee_hmacSha1, m_ee_crypt));
|
||||||
|
m_ee_server.attachCommand(new StorageWrite(AppConfig.ORD_STORAGE_WRITE,
|
||||||
|
m_ee_data, m_ee_makePacket, m_ee_sha1, m_ee_hmacSha1, m_ee_crypt));
|
||||||
|
m_ee_server.attachCommand(new StorageRead(AppConfig.ORD_STORAGE_READ,
|
||||||
|
m_ee_data, m_ee_makePacket, m_ee_sha1, m_ee_hmacSha1, m_ee_crypt));
|
||||||
|
m_ee_server.attachCommand(new RanGet(AppConfig.ORD_RAN_GET,
|
||||||
|
m_ee_data, m_ee_makePacket, m_ee_sha1, m_ee_hmacSha1, m_ee_crypt,
|
||||||
|
m_ee_random));
|
||||||
|
m_ee_server.attachCommand(new PlayCountIncrement(AppConfig.ORD_PLAY_COUNT_INCREMENT,
|
||||||
|
m_ee_data, m_ee_makePacket, m_ee_sha1, m_ee_hmacSha1, m_ee_crypt));
|
||||||
|
m_ee_server.attachCommand(new PlayCountRead(AppConfig.ORD_PLAY_COUNT_READ,
|
||||||
|
m_ee_data, m_ee_makePacket, m_ee_sha1, m_ee_hmacSha1, m_ee_crypt,
|
||||||
|
m_ee_random));
|
||||||
|
m_ee_server.attachCommand(new TraceDataInfoWrite(AppConfig.ORD_TRACE_DATA_INFO_WRITE,
|
||||||
|
m_ee_data, m_ee_makePacket, m_ee_sha1, m_ee_hmacSha1, m_ee_crypt));
|
||||||
|
m_ee_server.attachCommand(new TraceDataInfoRead(AppConfig.ORD_TRACE_DATA_INFO_READ,
|
||||||
|
m_ee_data, m_ee_makePacket, m_ee_sha1, m_ee_hmacSha1, m_ee_crypt));
|
||||||
|
m_ee_server.attachCommand(new StorageSizeGet(AppConfig.ORD_STORAGE_SIZE_GET,
|
||||||
|
m_ee_data, m_ee_makePacket, m_ee_sha1, m_ee_hmacSha1, m_ee_crypt,
|
||||||
|
m_ee_random));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void check(boolean ok) {
|
||||||
|
if (!ok) {
|
||||||
|
CryptoException.throwIt(CryptoException.ILLEGAL_USE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean select() {
|
||||||
|
boolean ret = super.select();
|
||||||
|
if (ret) {
|
||||||
|
m_ee_buffer.clear_ramD();
|
||||||
|
m_ee_data.init();
|
||||||
|
m_ee_server.select();
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void deselect() {
|
||||||
|
super.deselect();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void install(byte[] bArray, short bOffset, byte bLength) {
|
||||||
|
A7Firm applet = new A7Firm();
|
||||||
|
applet.register(bArray, (short) (bOffset + 1), bArray[bOffset]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void process(APDU apdu) {
|
||||||
|
if (selectingApplet()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_ee_data.getSflag_ee()
|
||||||
|
&& m_ee_data.getLevel_ramD() == AppConfig.LEVEL_BLOCKED) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (m_ee_data.getLevel_ramD() == AppConfig.LEVEL_INITIAL) {
|
||||||
|
m_ee_data.setLevel_ramD(AppConfig.LEVEL_SESSION);
|
||||||
|
}
|
||||||
|
|
||||||
|
byte[] apduBuffer = apdu.getBuffer();
|
||||||
|
short incomingLength = apdu.getIncomingLength();
|
||||||
|
short dataOffset = apdu.getOffsetCdata();
|
||||||
|
short received = apdu.setIncomingAndReceive();
|
||||||
|
|
||||||
|
while (received > 0) {
|
||||||
|
if (!m_ee_buffer.setData_ramD(apduBuffer, dataOffset, received)) {
|
||||||
|
m_ee_buffer.clear_ramD();
|
||||||
|
short sendSize = makeErrorPacket(AppConfig.FAIL, (short) -1,
|
||||||
|
apduBuffer, (short) 0, (short) apduBuffer.length);
|
||||||
|
if (sendSize > 0) {
|
||||||
|
apdu.setOutgoingAndSend((short) 0, sendSize);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
short accumulated = m_ee_buffer.getBufferSize_ramD();
|
||||||
|
if (accumulated >= incomingLength) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
received = apdu.receiveBytes(dataOffset);
|
||||||
|
}
|
||||||
|
|
||||||
|
short frameLength = m_ee_buffer.getBufferSize_ramD();
|
||||||
|
if (frameLength == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
byte[] frame = m_ee_buffer.getBuffer_ramD();
|
||||||
|
short cmd = -1;
|
||||||
|
short sendSize;
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (frameLength >= AppConfig.HEADER_SIZE) {
|
||||||
|
cmd = Util.getShort(frame, AppConfig.CMD_OFFSET);
|
||||||
|
}
|
||||||
|
if (frameLength < 4) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
short declaredSize = Util.getShort(frame, AppConfig.SIZE_OFFSET);
|
||||||
|
if (declaredSize < AppConfig.MIN_FRAME_SIZE
|
||||||
|
|| declaredSize > AppConfig.MAX_FRAME_SIZE) {
|
||||||
|
sendSize = makeErrorPacket(AppConfig.BAD_DATASIZE, cmd,
|
||||||
|
apduBuffer, (short) 0, (short) apduBuffer.length);
|
||||||
|
} else if (frameLength < declaredSize) {
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
sendSize = m_ee_server.execute(frame, (short) 0, declaredSize,
|
||||||
|
apduBuffer, (short) 0, (short) apduBuffer.length);
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
sendSize = makeErrorPacket(AppConfig.FAIL, cmd,
|
||||||
|
apduBuffer, (short) 0, (short) apduBuffer.length);
|
||||||
|
}
|
||||||
|
|
||||||
|
m_ee_buffer.clear_ramD();
|
||||||
|
if (sendSize > 0) {
|
||||||
|
apdu.setOutgoingAndSend((short) 0, sendSize);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private short makeErrorPacket(short error, short cmd, byte[] sendPacket,
|
||||||
|
short packetOffset, short packetLength) {
|
||||||
|
m_ee_data.setLastError_ramD(error);
|
||||||
|
boolean ok = m_ee_makePacket.setData(null, (short) 0, (short) 0,
|
||||||
|
AppConfig.TAG_RSP_COMMAND, AppConfig.ERROR_PACKET_SIZE, error, cmd,
|
||||||
|
null, (short) 0, (short) 0,
|
||||||
|
null, (short) 0, (short) 0,
|
||||||
|
null, (short) 0, (short) 0,
|
||||||
|
sendPacket, packetOffset, packetLength, m_ee_sha1, null);
|
||||||
|
return ok ? AppConfig.ERROR_PACKET_SIZE : (short) 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
package sega.a7;
|
||||||
|
|
||||||
|
import javacard.framework.JCSystem;
|
||||||
|
import javacard.framework.Util;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Accumulates the bytes of one internal A7 frame across APDUs.
|
||||||
|
*/
|
||||||
|
public class AppBuffer {
|
||||||
|
|
||||||
|
private final short[] m_ramD_size;
|
||||||
|
private final byte[] m_ramD_buffer;
|
||||||
|
|
||||||
|
public AppBuffer() {
|
||||||
|
m_ramD_buffer = JCSystem.makeTransientByteArray(AppConfig.BUFFER_SIZE, JCSystem.CLEAR_ON_RESET);
|
||||||
|
m_ramD_size = JCSystem.makeTransientShortArray((short) 1, JCSystem.CLEAR_ON_RESET);
|
||||||
|
m_ramD_size[0] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] getBuffer_ramD() {
|
||||||
|
return m_ramD_buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public short getBufferSize_ramD() {
|
||||||
|
return m_ramD_size[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean setData_ramD(byte[] data, short offset, short length) {
|
||||||
|
if (data == null || offset < 0 || length < 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (offset > data.length || length > (short) (data.length - offset)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
short used = m_ramD_size[0];
|
||||||
|
if (used < 0 || used > AppConfig.BUFFER_SIZE) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (length > (short) (AppConfig.BUFFER_SIZE - used)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (length != 0) {
|
||||||
|
Util.arrayCopyNonAtomic(data, offset, m_ramD_buffer, used, length);
|
||||||
|
}
|
||||||
|
m_ramD_size[0] = (short) (used + length);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void clear_ramD() {
|
||||||
|
m_ramD_size[0] = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,111 @@
|
|||||||
|
package sega.a7;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wire-format and state constants for the A7 keychip applet.
|
||||||
|
*
|
||||||
|
* <p>All multi-byte values on the wire are big-endian. A frame is:
|
||||||
|
* {@code tag(2) | totalSize(2) | command-or-result(2) | payload | footer}.
|
||||||
|
* "totalSize" covers the whole frame including the footer.</p>
|
||||||
|
*/
|
||||||
|
public interface AppConfig {
|
||||||
|
|
||||||
|
short VERSION = 0x0205;
|
||||||
|
|
||||||
|
short HEADER_SIZE = 6;
|
||||||
|
short TAG_OFFSET = 0;
|
||||||
|
short SIZE_OFFSET = 2;
|
||||||
|
short CMD_OFFSET = 4;
|
||||||
|
|
||||||
|
short SHA1_SIZE = 20;
|
||||||
|
short NONCE_SIZE = 20;
|
||||||
|
short HMAC_SIZE = 20;
|
||||||
|
short AUTH_FOOTER_SIZE = 40;
|
||||||
|
|
||||||
|
short AES_KEY_SIZE = 16;
|
||||||
|
short AES_IV_SIZE = 16;
|
||||||
|
short AES_BLOCK_SIZE = 16;
|
||||||
|
|
||||||
|
short RSA_KEY_SIZE = 256;
|
||||||
|
short RSA_UPDATE_EXPONENT = 8;
|
||||||
|
short RSA_UPDATE_MODULUS = 64;
|
||||||
|
|
||||||
|
short UDATA_SIZE = 16;
|
||||||
|
short PLAY_COUNT_SIZE = 4;
|
||||||
|
short STORAGE_SIZE = 10240;
|
||||||
|
short STORAGE_ARRAY_SIZE = 10256;
|
||||||
|
short STORAGE_MAX_READ = 192;
|
||||||
|
short STORAGE_MAX_WRITE = 256;
|
||||||
|
short KEYCHIP_INFO_SIZE = 96;
|
||||||
|
short TRACE_DATA_INFO_SIZE = 16;
|
||||||
|
|
||||||
|
short BUFFER_SIZE = 416;
|
||||||
|
short MIN_FRAME_SIZE = 26;
|
||||||
|
short MAX_FRAME_SIZE = 416;
|
||||||
|
short ERROR_PACKET_SIZE = 26;
|
||||||
|
|
||||||
|
short TAG_RQU_COMMAND = 0x00C1;
|
||||||
|
short TAG_RQU_AUTH_COMMAND = 0x00C2;
|
||||||
|
short TAG_RSP_COMMAND = 0x00C3;
|
||||||
|
short TAG_RSP_AUTH_COMMAND = 0x00C4;
|
||||||
|
short TAG_RQU_RSA_COMMAND = 0x00C5;
|
||||||
|
short TAG_RSP_RSA_COMMAND = 0x00C6;
|
||||||
|
|
||||||
|
short ORD_SESSION_OPEN = 1;
|
||||||
|
short ORD_SESSION_CLOSE = 2;
|
||||||
|
short ORD_AUTH_START = 3;
|
||||||
|
short ORD_HMAC_CHANGE = 4;
|
||||||
|
short ORD_KEY_CHANGE = 5;
|
||||||
|
short ORD_LV_GET = 6;
|
||||||
|
short ORD_ERROR_GET = 7;
|
||||||
|
short ORD_VER_GET = 8;
|
||||||
|
short ORD_KEYCHIP_INFO_WRITE = 9;
|
||||||
|
short ORD_KEYCHIP_INFO_READ = 10;
|
||||||
|
short ORD_GKEY_WRITE = 11;
|
||||||
|
short ORD_GKEY_ENC = 12;
|
||||||
|
short ORD_GKEY_DEC = 13;
|
||||||
|
short ORD_SFLAG_WRITE = 14;
|
||||||
|
short ORD_EEP_INIT = 15;
|
||||||
|
short ORD_LV_ENABLE = 16;
|
||||||
|
short ORD_PRIKEY_MODULUS_WRITE = 17;
|
||||||
|
short ORD_PRIKEY_EXPONENT_WRITE = 18;
|
||||||
|
short ORD_UDATA_WRITE = 19;
|
||||||
|
short ORD_UDATA_READ = 20;
|
||||||
|
short ORD_STORAGE_WRITE = 21;
|
||||||
|
short ORD_STORAGE_READ = 22;
|
||||||
|
short ORD_RAN_GET = 23;
|
||||||
|
short ORD_PLAY_COUNT_INCREMENT = 24;
|
||||||
|
short ORD_PLAY_COUNT_READ = 25;
|
||||||
|
short ORD_TRACE_DATA_INFO_WRITE = 26;
|
||||||
|
short ORD_TRACE_DATA_INFO_READ = 27;
|
||||||
|
short ORD_STORAGE_SIZE_GET = 28;
|
||||||
|
|
||||||
|
short SUCCESS = 0;
|
||||||
|
short INVALID_AUTH = 1;
|
||||||
|
short AUTHFAIL = 2;
|
||||||
|
short LV_ERROR = 3;
|
||||||
|
short BAD_PARAMETER = 4;
|
||||||
|
short EEP_WRITEFAIL = 5;
|
||||||
|
short BAD_TAG = 6;
|
||||||
|
short BAD_ORDINAL = 7;
|
||||||
|
short SUMFAIL = 8;
|
||||||
|
short EEPWRITE_DISABLE = 9;
|
||||||
|
short BAD_DATASIZE = 10;
|
||||||
|
short FAIL = 11;
|
||||||
|
|
||||||
|
byte LEVEL_BLOCKED = -1;
|
||||||
|
byte LEVEL_INITIAL = 0;
|
||||||
|
byte LEVEL_SESSION = 1;
|
||||||
|
byte LEVEL_AUTH = 2;
|
||||||
|
|
||||||
|
byte SESSION_DISABLE = 0;
|
||||||
|
byte SESSION_ENABLE = 1;
|
||||||
|
|
||||||
|
byte SFLAG_SHIPMENT = 4;
|
||||||
|
byte SFLAG_BLOCK = 32;
|
||||||
|
|
||||||
|
short CRYPT_GKEY_ENCRYPT = 0;
|
||||||
|
short CRYPT_GKEY_DECRYPT = 1;
|
||||||
|
short CRYPT_CONN_ENCRYPT = 2;
|
||||||
|
short CRYPT_CONN_DECRYPT = 3;
|
||||||
|
short CRYPT_RSA_DECRYPT = 4;
|
||||||
|
}
|
||||||
@@ -0,0 +1,424 @@
|
|||||||
|
package sega.a7;
|
||||||
|
|
||||||
|
import javacard.framework.JCSystem;
|
||||||
|
import javacard.framework.Util;
|
||||||
|
import javacard.security.RandomData;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Persistent (EEPROM) and transient (RAM) state of the keychip.
|
||||||
|
*
|
||||||
|
* <p>Transient layout of {@code m_ramD_value}:
|
||||||
|
* [0]=auth level, [1]=session, [2..3]=last error (big-endian),
|
||||||
|
* [4]=consecutive HMAC failure count. The transient state is cleared on card
|
||||||
|
* reset; {@link #init()} rebuilds the level from the persistent block flag
|
||||||
|
* every time the applet is selected.</p>
|
||||||
|
*/
|
||||||
|
public class AppData {
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Factory-default 2048-bit RSA key material from the original CAP.
|
||||||
|
* The modulus matches the public modulus published for hdd.sys; the second
|
||||||
|
* array is the matching private exponent. Keeping these defaults preserves
|
||||||
|
* wire compatibility, but they are public and MUST be replaced during
|
||||||
|
* personalisation on any real deployment.
|
||||||
|
*/
|
||||||
|
private static final byte[] m_ee_defaultPriModulus = new byte[]{-97, -48, -92, 58, 105, 115, 41, -59, 75, -82, -61, 60, -16, 115, 3, -69, 37, -12, -21, 15, 54, 107, 33, 70, 51, -44, -118, -65, 99, -100, 16, 87, 85, -67, -121, -12, 7, 26, 32, -27, -95, 57, -36, -14, -125, -56, 109, -27, -28, -47, 61, -93, 74, 2, 62, 33, -84, 27, 39, -92, 82, -60, -112, 68, -105, -26, 37, -61, 47, 111, -105, 98, -69, -128, 35, 93, 17, -43, -110, -106, 94, -90, -11, -125, -107, -122, -61, 6, 111, 32, 121, -25, 12, -106, 5, 90, -4, 90, 127, -62, 87, -119, -75, 77, -80, -125, 61, 54, 37, -72, -38, -31, 115, -95, 39, -90, -127, -52, -30, 16, 16, 59, -73, 47, -48, 32, 92, -45, -16, 36, -63, -69, 38, -30, 97, 102, 77, 99, -125, -112, -8, -17, 125, 81, 45, -49, 43, -57, 42, -126, 110, -20, 65, 63, 35, 55, -120, -70, 19, 9, 38, 64, 24, -17, -37, -40, -53, 116, -114, 8, -111, 124, -103, 42, 103, -73, -78, -64, 53, 126, 69, -85, -100, -98, 81, -49, 48, -114, -4, -64, 37, 111, 40, -103, -101, 7, -101, -69, 58, -33, 103, -41, 34, 26, 29, -24, -13, -11, -121, -95, -92, -106, 98, -32, -33, -26, -11, -6, 85, 115, 81, -16, 71, 121, 63, -50, 82, 100, 29, 44, 125, -58, -80, -22, -70, -118, -27, 41, -47, -33, -12, -36, 121, 59, -72, 41, -108, -53, 73, -74, -28, -17, -40, 15, 20, 43};
|
||||||
|
private static final byte[] m_ee_defaultPriExponent = new byte[]{49, 10, 6, -70, -35, -52, -74, -50, -111, -80, -88, -68, 52, 84, 54, -102, -32, -60, -52, -76, 56, 80, -101, 63, 61, 124, 68, -81, 1, -45, -53, -73, -32, -39, 109, -76, 85, -47, -112, -78, -91, -52, 105, -98, 9, 14, 33, -102, -33, -95, -51, 54, 35, -23, -104, 67, 32, -38, -100, 73, 114, 20, 90, -24, -111, 107, -49, -66, -102, -7, -75, 12, 88, -40, -77, 1, 70, 13, -79, 19, 111, 43, -16, 62, 86, 77, -87, -34, 121, -113, 15, -104, -12, 101, -68, 114, -12, -24, -104, 55, 124, 65, 7, 95, 53, -128, -81, -15, -61, -107, 123, -92, -98, -92, -98, -44, 27, 20, 81, 113, -8, 105, 58, -122, -13, 104, 90, -45, 57, -66, -3, -93, 24, -127, 65, -103, 7, -121, -105, 127, -40, -66, 125, 88, -120, -122, -90, 126, 78, -21, -21, 56, -5, -80, -97, -81, 68, 70, -113, -7, -17, 116, -69, 121, 97, -19, 16, 99, 113, 76, -54, 110, -54, -20, -125, -119, -50, 74, 111, 49, 92, 48, 105, -22, 75, -14, -10, -54, -17, 32, 40, -94, 9, -80, 48, 114, 0, 71, 95, 119, 92, -54, -46, 72, -30, -23, 60, 66, -61, 18, -7, 84, 24, 61, 35, 62, -79, -78, -108, 9, 56, -116, 3, -26, 41, 97, -88, -125, 63, 90, 80, -125, 5, -73, -40, 1, 58, -85, -26, -51, 26, 1, 3, 74, 24, -28, 67, -115, -51, 70, -91, 40, 37, -11, 57, -15};
|
||||||
|
|
||||||
|
private final byte[] m_ee_sflag;
|
||||||
|
private final byte[] m_ee_keychipInfo;
|
||||||
|
private final byte[] m_ee_traceDataInfo;
|
||||||
|
private final byte[] m_ee_priModulus;
|
||||||
|
private final byte[] m_ee_priExponent;
|
||||||
|
private final byte[] m_ee_udata;
|
||||||
|
private final byte[] m_ee_playCount;
|
||||||
|
private final byte[] m_ee_storage;
|
||||||
|
private final byte[] m_ee_gkeyIv;
|
||||||
|
private final RandomData m_ee_rand;
|
||||||
|
|
||||||
|
private final byte[] m_ramD_value;
|
||||||
|
private final byte[] m_ramD_rsa_update_status;
|
||||||
|
private final byte[] m_ramD_kh;
|
||||||
|
private final byte[] m_ramD_newNonceEvent;
|
||||||
|
private final byte[] m_ramD_lastNonceEvent;
|
||||||
|
private final byte[] m_ramD_cKeyIv;
|
||||||
|
private final byte[] m_ramD_generalBuffer;
|
||||||
|
private final byte[] m_ramD_footerBuffer;
|
||||||
|
private final byte[] m_ramD_nonceOddBuffer;
|
||||||
|
private final byte[] m_ramD_sendOutBuffer;
|
||||||
|
private final short[] m_ramD_sendOutInfo;
|
||||||
|
private final byte[] m_ramD_cryptBuffer;
|
||||||
|
private final byte[] m_ramD_zeroBuffer;
|
||||||
|
|
||||||
|
public AppData(RandomData rand) {
|
||||||
|
m_ee_rand = rand;
|
||||||
|
m_ee_sflag = new byte[1];
|
||||||
|
m_ee_keychipInfo = new byte[AppConfig.KEYCHIP_INFO_SIZE];
|
||||||
|
m_ee_traceDataInfo = new byte[AppConfig.TRACE_DATA_INFO_SIZE];
|
||||||
|
m_ee_priModulus = new byte[AppConfig.RSA_KEY_SIZE];
|
||||||
|
m_ee_priExponent = new byte[AppConfig.RSA_KEY_SIZE];
|
||||||
|
m_ee_udata = new byte[AppConfig.UDATA_SIZE];
|
||||||
|
m_ee_playCount = new byte[AppConfig.PLAY_COUNT_SIZE];
|
||||||
|
m_ee_storage = new byte[AppConfig.STORAGE_ARRAY_SIZE];
|
||||||
|
m_ee_gkeyIv = new byte[AppConfig.AES_IV_SIZE];
|
||||||
|
|
||||||
|
Util.arrayCopy(m_ee_defaultPriModulus, (short) 0, m_ee_priModulus,
|
||||||
|
(short) 0, (short) m_ee_priModulus.length);
|
||||||
|
Util.arrayCopy(m_ee_defaultPriExponent, (short) 0, m_ee_priExponent,
|
||||||
|
(short) 0, (short) m_ee_priExponent.length);
|
||||||
|
|
||||||
|
m_ee_sflag[0] = 0;
|
||||||
|
Util.arrayFillNonAtomic(m_ee_keychipInfo, (short) 0,
|
||||||
|
(short) m_ee_keychipInfo.length, (byte) 0xFF);
|
||||||
|
Util.arrayFillNonAtomic(m_ee_udata, (short) 0,
|
||||||
|
(short) m_ee_udata.length, (byte) 0xFF);
|
||||||
|
Util.arrayFillNonAtomic(m_ee_storage, (short) 0,
|
||||||
|
(short) m_ee_storage.length, (byte) 0xFF);
|
||||||
|
Util.arrayFillNonAtomic(m_ee_traceDataInfo, (short) 0,
|
||||||
|
(short) m_ee_traceDataInfo.length, (byte) 0xFF);
|
||||||
|
Util.arrayFillNonAtomic(m_ee_playCount, (short) 0,
|
||||||
|
(short) m_ee_playCount.length, (byte) 0);
|
||||||
|
Util.arrayFillNonAtomic(m_ee_gkeyIv, (short) 0,
|
||||||
|
(short) m_ee_gkeyIv.length, (byte) 0xFF);
|
||||||
|
|
||||||
|
m_ramD_value = JCSystem.makeTransientByteArray((short) 6, JCSystem.CLEAR_ON_RESET);
|
||||||
|
m_ramD_rsa_update_status = JCSystem.makeTransientByteArray((short) 1,
|
||||||
|
JCSystem.CLEAR_ON_RESET);
|
||||||
|
m_ramD_kh = JCSystem.makeTransientByteArray(AppConfig.NONCE_SIZE,
|
||||||
|
JCSystem.CLEAR_ON_RESET);
|
||||||
|
m_ramD_newNonceEvent = JCSystem.makeTransientByteArray(AppConfig.NONCE_SIZE,
|
||||||
|
JCSystem.CLEAR_ON_RESET);
|
||||||
|
m_ramD_lastNonceEvent = JCSystem.makeTransientByteArray(AppConfig.NONCE_SIZE,
|
||||||
|
JCSystem.CLEAR_ON_RESET);
|
||||||
|
m_ramD_cKeyIv = JCSystem.makeTransientByteArray(AppConfig.AES_IV_SIZE,
|
||||||
|
JCSystem.CLEAR_ON_RESET);
|
||||||
|
m_ramD_generalBuffer = JCSystem.makeTransientByteArray((short) 256,
|
||||||
|
JCSystem.CLEAR_ON_RESET);
|
||||||
|
m_ramD_footerBuffer = JCSystem.makeTransientByteArray(AppConfig.SHA1_SIZE,
|
||||||
|
JCSystem.CLEAR_ON_RESET);
|
||||||
|
m_ramD_nonceOddBuffer = JCSystem.makeTransientByteArray(AppConfig.NONCE_SIZE,
|
||||||
|
JCSystem.CLEAR_ON_RESET);
|
||||||
|
m_ramD_sendOutBuffer = JCSystem.makeTransientByteArray((short) 256,
|
||||||
|
JCSystem.CLEAR_ON_RESET);
|
||||||
|
m_ramD_sendOutInfo = JCSystem.makeTransientShortArray((short) 2,
|
||||||
|
JCSystem.CLEAR_ON_RESET);
|
||||||
|
m_ramD_cryptBuffer = JCSystem.makeTransientByteArray(AppConfig.AES_BLOCK_SIZE,
|
||||||
|
JCSystem.CLEAR_ON_RESET);
|
||||||
|
m_ramD_zeroBuffer = JCSystem.makeTransientByteArray(AppConfig.AES_IV_SIZE,
|
||||||
|
JCSystem.CLEAR_ON_RESET);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean fits(short arrayLength, short offset, short length) {
|
||||||
|
return offset >= 0 && length >= 0 && offset <= arrayLength
|
||||||
|
&& length <= (short) (arrayLength - offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void init() {
|
||||||
|
m_ramD_value[0] = getBlockFlag_ee() ? AppConfig.LEVEL_BLOCKED
|
||||||
|
: AppConfig.LEVEL_INITIAL;
|
||||||
|
m_ramD_value[1] = AppConfig.SESSION_DISABLE;
|
||||||
|
m_ramD_value[4] = 0;
|
||||||
|
|
||||||
|
Util.arrayFillNonAtomic(m_ramD_kh, (short) 0,
|
||||||
|
(short) m_ramD_kh.length, (byte) 0);
|
||||||
|
Util.arrayFillNonAtomic(m_ramD_newNonceEvent, (short) 0,
|
||||||
|
(short) m_ramD_newNonceEvent.length, (byte) 0);
|
||||||
|
Util.arrayFillNonAtomic(m_ramD_lastNonceEvent, (short) 0,
|
||||||
|
(short) m_ramD_lastNonceEvent.length, (byte) 0);
|
||||||
|
Util.arrayFillNonAtomic(m_ramD_cKeyIv, (short) 0,
|
||||||
|
(short) m_ramD_cKeyIv.length, (byte) 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void reset() {
|
||||||
|
m_ee_sflag[0] = 0;
|
||||||
|
Util.arrayFillNonAtomic(m_ee_keychipInfo, (short) 0,
|
||||||
|
(short) m_ee_keychipInfo.length, (byte) 0xFF);
|
||||||
|
Util.arrayFillNonAtomic(m_ee_udata, (short) 0,
|
||||||
|
(short) m_ee_udata.length, (byte) 0xFF);
|
||||||
|
Util.arrayFillNonAtomic(m_ee_storage, (short) 0,
|
||||||
|
(short) m_ee_storage.length, (byte) 0xFF);
|
||||||
|
Util.arrayFillNonAtomic(m_ee_traceDataInfo, (short) 0,
|
||||||
|
(short) m_ee_traceDataInfo.length, (byte) 0xFF);
|
||||||
|
Util.arrayFillNonAtomic(m_ee_playCount, (short) 0,
|
||||||
|
(short) m_ee_playCount.length, (byte) 0);
|
||||||
|
Util.arrayFillNonAtomic(m_ee_gkeyIv, (short) 0,
|
||||||
|
(short) m_ee_gkeyIv.length, (byte) 0xFF);
|
||||||
|
m_ramD_rsa_update_status[0] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLevel_ramD(byte setLevel) {
|
||||||
|
m_ramD_value[0] = setLevel;
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte getLevel_ramD() {
|
||||||
|
return m_ramD_value[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSession_ramD(byte setSession) {
|
||||||
|
m_ramD_value[1] = setSession;
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte getSession_ramD() {
|
||||||
|
return m_ramD_value[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addValuePlayCounter_ee(byte value) {
|
||||||
|
short carry = (short) (value & 0xFF);
|
||||||
|
for (short i = (short) (m_ee_playCount.length - 1); i >= 0; i--) {
|
||||||
|
short sum = (short) (carry + (short) (m_ee_playCount[i] & 0xFF));
|
||||||
|
m_ee_playCount[i] = (byte) (sum & 0xFF);
|
||||||
|
if (sum <= 0xFF) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
carry = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean getPlayCounter_ee(byte[] playCount, short offset, short length) {
|
||||||
|
if (playCount == null || !fits((short) playCount.length, offset, length)
|
||||||
|
|| length < m_ee_playCount.length) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
Util.arrayCopyNonAtomic(m_ee_playCount, (short) 0, playCount, offset,
|
||||||
|
(short) m_ee_playCount.length);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public final byte[] getNewNonceEvent_ramD() {
|
||||||
|
m_ee_rand.generateData(m_ramD_newNonceEvent, (short) 0,
|
||||||
|
(short) m_ramD_newNonceEvent.length);
|
||||||
|
return m_ramD_newNonceEvent;
|
||||||
|
}
|
||||||
|
|
||||||
|
public final byte[] getLastNonceEvent_ramD() {
|
||||||
|
return m_ramD_lastNonceEvent;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean setLastNonceEvent_ramD(byte[] lastNonceEvent, short offset, short length) {
|
||||||
|
if (lastNonceEvent == null
|
||||||
|
|| !fits((short) lastNonceEvent.length, offset, length)
|
||||||
|
|| !fits((short) m_ramD_lastNonceEvent.length, (short) 0, length)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
Util.arrayCopyNonAtomic(lastNonceEvent, offset, m_ramD_lastNonceEvent,
|
||||||
|
(short) 0, length);
|
||||||
|
if (length < AppConfig.NONCE_SIZE) {
|
||||||
|
Util.arrayFillNonAtomic(m_ramD_lastNonceEvent, length,
|
||||||
|
(short) (AppConfig.NONCE_SIZE - length), (byte) 0);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public final byte[] getKh_ramD() {
|
||||||
|
return m_ramD_kh;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean setKh_ramD(byte[] kh, short offset, short length) {
|
||||||
|
if (kh == null || !fits((short) kh.length, offset, length)
|
||||||
|
|| !fits((short) m_ramD_kh.length, (short) 0, length)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
Util.arrayCopyNonAtomic(kh, offset, m_ramD_kh, (short) 0, length);
|
||||||
|
if (length < AppConfig.NONCE_SIZE) {
|
||||||
|
Util.arrayFillNonAtomic(m_ramD_kh, length,
|
||||||
|
(short) (AppConfig.NONCE_SIZE - length), (byte) 0);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public final byte[] getGkeyIv_ee() {
|
||||||
|
return m_ee_gkeyIv;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean setGkeyIv_ee(byte[] newGkeyIv, short offset, short length) {
|
||||||
|
if (newGkeyIv == null || !fits((short) newGkeyIv.length, offset, length)
|
||||||
|
|| !fits((short) m_ee_gkeyIv.length, (short) 0, length)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
Util.arrayCopy(newGkeyIv, offset, m_ee_gkeyIv, (short) 0, length);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public final byte[] getCkeyIv_ramD() {
|
||||||
|
return m_ramD_cKeyIv;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean setCkeyIv_ramD(byte[] newCkeyIv, short offset, short length) {
|
||||||
|
if (newCkeyIv == null || !fits((short) newCkeyIv.length, offset, length)
|
||||||
|
|| !fits((short) m_ramD_cKeyIv.length, (short) 0, length)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
Util.arrayCopyNonAtomic(newCkeyIv, offset, m_ramD_cKeyIv, (short) 0, length);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public final byte[] getRsaDefaultPriModulus_ee() {
|
||||||
|
return m_ee_defaultPriModulus;
|
||||||
|
}
|
||||||
|
|
||||||
|
public final byte[] getRsaDefaultPriExponent_ee() {
|
||||||
|
return m_ee_defaultPriExponent;
|
||||||
|
}
|
||||||
|
|
||||||
|
public final byte[] getRsaPriModulus_ee() {
|
||||||
|
return m_ee_priModulus;
|
||||||
|
}
|
||||||
|
|
||||||
|
public final byte[] getRsaPriExponent_ee() {
|
||||||
|
return m_ee_priExponent;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean setRsaPriKeyModulus_ee(byte[] modulus, short mOffset, short mLength) {
|
||||||
|
if (modulus == null || !fits((short) modulus.length, mOffset, mLength)
|
||||||
|
|| !fits((short) m_ee_priModulus.length, (short) 0, mLength)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
Util.arrayCopy(modulus, mOffset, m_ee_priModulus, (short) 0, mLength);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean setRsaPriKeyExponent_ee(byte[] exponent, short eOffset, short eLength) {
|
||||||
|
if (exponent == null || !fits((short) exponent.length, eOffset, eLength)
|
||||||
|
|| !fits((short) m_ee_priExponent.length, (short) 0, eLength)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
Util.arrayCopy(exponent, eOffset, m_ee_priExponent, (short) 0, eLength);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean getSflag_ee() {
|
||||||
|
return (m_ee_sflag[0] & AppConfig.SFLAG_SHIPMENT) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void activeSflag() {
|
||||||
|
m_ee_sflag[0] = (byte) (m_ee_sflag[0] | AppConfig.SFLAG_SHIPMENT);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean getRsaExponentUpdate_ramD() {
|
||||||
|
return (m_ramD_rsa_update_status[0] & AppConfig.RSA_UPDATE_EXPONENT) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void activeRsaExponentUpdate_ramD() {
|
||||||
|
m_ramD_rsa_update_status[0] =
|
||||||
|
(byte) (m_ramD_rsa_update_status[0] | AppConfig.RSA_UPDATE_EXPONENT);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean getRsaModulusUpdate_ramD() {
|
||||||
|
return (m_ramD_rsa_update_status[0] & AppConfig.RSA_UPDATE_MODULUS) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void activeRsaModulusUpdate_ramD() {
|
||||||
|
m_ramD_rsa_update_status[0] =
|
||||||
|
(byte) (m_ramD_rsa_update_status[0] | AppConfig.RSA_UPDATE_MODULUS);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean getBlockFlag_ee() {
|
||||||
|
return (m_ee_sflag[0] & AppConfig.SFLAG_BLOCK) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void activeBlockFlag() {
|
||||||
|
m_ee_sflag[0] = (byte) (m_ee_sflag[0] | AppConfig.SFLAG_BLOCK);
|
||||||
|
}
|
||||||
|
|
||||||
|
public final byte[] getKeychipInfo_ee() {
|
||||||
|
return m_ee_keychipInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean setKeychipInfo_ee(byte[] newKeychipInfo, short offset, short length) {
|
||||||
|
if (newKeychipInfo == null || !fits((short) newKeychipInfo.length, offset, length)
|
||||||
|
|| !fits((short) m_ee_keychipInfo.length, (short) 0, length)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
Util.arrayCopy(newKeychipInfo, offset, m_ee_keychipInfo, (short) 0, length);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public final byte[] getTraceDataInfo_ee() {
|
||||||
|
return m_ee_traceDataInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean setTraceDataInfo_ee(byte[] newTraceDataInfo, short offset, short length) {
|
||||||
|
if (newTraceDataInfo == null || !fits((short) newTraceDataInfo.length, offset, length)
|
||||||
|
|| !fits((short) m_ee_traceDataInfo.length, (short) 0, length)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
Util.arrayCopy(newTraceDataInfo, offset, m_ee_traceDataInfo, (short) 0, length);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public final byte[] getUdata_ee() {
|
||||||
|
return m_ee_udata;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean setUdata_ee(byte[] newUdata, short offset, short length) {
|
||||||
|
if (newUdata == null || !fits((short) newUdata.length, offset, length)
|
||||||
|
|| !fits((short) m_ee_udata.length, (short) 0, length)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
Util.arrayCopy(newUdata, offset, m_ee_udata, (short) 0, length);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public final byte[] getStorageData_ee() {
|
||||||
|
return m_ee_storage;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean setStorageData_ee(byte[] newStorage, short offset, short length,
|
||||||
|
short dstOffset) {
|
||||||
|
if (newStorage == null || !fits((short) newStorage.length, offset, length)
|
||||||
|
|| dstOffset < 0
|
||||||
|
|| !fits((short) m_ee_storage.length, dstOffset, length)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
Util.arrayCopy(newStorage, offset, m_ee_storage, dstOffset, length);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] getGeneralBuffer_ramD() {
|
||||||
|
return m_ramD_generalBuffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] getSendOutBuffer_ramD() {
|
||||||
|
return m_ramD_sendOutBuffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public short[] getSendOutInfo_ramD() {
|
||||||
|
return m_ramD_sendOutInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] getCryptBuffer_ramD() {
|
||||||
|
return m_ramD_cryptBuffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] getZeroBuffer_ramD() {
|
||||||
|
return m_ramD_zeroBuffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] getFooterBuffer_ramD() {
|
||||||
|
return m_ramD_footerBuffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] getNonceOddBuffer_ramD() {
|
||||||
|
return m_ramD_nonceOddBuffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLastError_ramD(short error) {
|
||||||
|
Util.setShort(m_ramD_value, (short) 2, error);
|
||||||
|
}
|
||||||
|
|
||||||
|
public short getLastError_ramD() {
|
||||||
|
return Util.getShort(m_ramD_value, (short) 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void incrementErrorCount() {
|
||||||
|
m_ramD_value[4] = (byte) (m_ramD_value[4] + 1);
|
||||||
|
if (m_ramD_value[4] == (byte) 3) {
|
||||||
|
activeBlockFlag();
|
||||||
|
m_ramD_value[0] = AppConfig.LEVEL_BLOCKED;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void clearErrorCount() {
|
||||||
|
m_ramD_value[4] = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,301 @@
|
|||||||
|
package sega.a7;
|
||||||
|
|
||||||
|
import javacard.framework.Util;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Base class for authenticated commands (tag C2 / C4).
|
||||||
|
*
|
||||||
|
* <p>The session must be open and the current level must be exactly the level
|
||||||
|
* required by the command. The request footer is
|
||||||
|
* {@code nonceOdd(20) | HMAC-SHA1(Kh, SHA1(header||payload) || lastNonceEven || nonceOdd)}.</p>
|
||||||
|
*/
|
||||||
|
public class AuthCommand extends Command {
|
||||||
|
|
||||||
|
private final Crypt m_ee_crypt;
|
||||||
|
private final HmacSha1 m_ee_hmacSha1;
|
||||||
|
private final byte m_ee_authLevel;
|
||||||
|
|
||||||
|
public AuthCommand(short cmd, short recvPacketSize, short sendPacketSize, byte authLevel,
|
||||||
|
AppData data, MakePacket packet, javacard.security.MessageDigest sha1,
|
||||||
|
HmacSha1 hmacSha1, Crypt crypt) {
|
||||||
|
super(cmd, recvPacketSize, sendPacketSize, data, packet, sha1);
|
||||||
|
m_ee_crypt = crypt;
|
||||||
|
m_ee_hmacSha1 = hmacSha1;
|
||||||
|
m_ee_authLevel = authLevel;
|
||||||
|
m_ee_tag = AppConfig.TAG_RQU_AUTH_COMMAND;
|
||||||
|
}
|
||||||
|
|
||||||
|
public short execute(byte[] data, short dataOffset, short dataLength,
|
||||||
|
byte[] dst, short dstOffset, short dstLength) {
|
||||||
|
if (m_ee_data.getSession_ramD() != AppConfig.SESSION_ENABLE) {
|
||||||
|
return makeErrorPacket(AppConfig.INVALID_AUTH, dst, dstOffset, dstLength);
|
||||||
|
}
|
||||||
|
if (m_ee_data.getLevel_ramD() != m_ee_authLevel) {
|
||||||
|
return makeErrorPacket(AppConfig.LV_ERROR, dst, dstOffset, dstLength);
|
||||||
|
}
|
||||||
|
|
||||||
|
short error = checkHeader(data, dataOffset, dataLength);
|
||||||
|
if (error != AppConfig.SUCCESS) {
|
||||||
|
return makeErrorPacket(error, dst, dstOffset, dstLength);
|
||||||
|
}
|
||||||
|
if (dataLength < AppConfig.AUTH_FOOTER_SIZE) {
|
||||||
|
return makeErrorPacket(AppConfig.BAD_DATASIZE, dst, dstOffset, dstLength);
|
||||||
|
}
|
||||||
|
|
||||||
|
error = checkFooter(data, dataOffset, dataLength);
|
||||||
|
if (error != AppConfig.SUCCESS) {
|
||||||
|
return makeErrorPacket(error, dst, dstOffset, dstLength);
|
||||||
|
}
|
||||||
|
|
||||||
|
m_ramD_outDataInfo[0] = 0;
|
||||||
|
m_ramD_outDataInfo[1] = 0;
|
||||||
|
|
||||||
|
error = core(data, dataOffset, dataLength);
|
||||||
|
if (error != AppConfig.SUCCESS) {
|
||||||
|
return makeErrorPacket(error, dst, dstOffset, dstLength);
|
||||||
|
}
|
||||||
|
|
||||||
|
short dataRef = m_ramD_outDataInfo[0];
|
||||||
|
short dataSize = m_ramD_outDataInfo[1];
|
||||||
|
if (dataRef < 0 || dataSize < 0 || dataRef > m_ramD_outData.length
|
||||||
|
|| dataSize > (short) (m_ramD_outData.length - dataRef)) {
|
||||||
|
return makeErrorPacket(AppConfig.FAIL, dst, dstOffset, dstLength);
|
||||||
|
}
|
||||||
|
|
||||||
|
short paramSize = (short) (AppConfig.HEADER_SIZE + dataSize + AppConfig.AUTH_FOOTER_SIZE);
|
||||||
|
m_ramD_setSendPacketSize[0] = paramSize;
|
||||||
|
short oddOffset = (short) (dataOffset + dataLength - AppConfig.AUTH_FOOTER_SIZE);
|
||||||
|
|
||||||
|
return makeSendAuthPacket(AppConfig.TAG_RSP_AUTH_COMMAND, paramSize,
|
||||||
|
AppConfig.SUCCESS, m_ramD_outData, dataRef, dataSize,
|
||||||
|
data, oddOffset, AppConfig.NONCE_SIZE, dst, dstOffset, dstLength);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected boolean changeConnectionKey_ee(byte[] aesKey, short aesKeyOffset, short aesKeyLength,
|
||||||
|
byte[] aesIv, short aesIvOffset, short aesIvLength) {
|
||||||
|
if (aesKeyLength != AppConfig.AES_KEY_SIZE || aesIvLength != AppConfig.AES_IV_SIZE) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
byte[] zeroIv = m_ee_data.getZeroBuffer_ramD();
|
||||||
|
Util.arrayFillNonAtomic(zeroIv, (short) 0, (short) zeroIv.length, (byte) 0);
|
||||||
|
|
||||||
|
if (!m_ee_crypt.init(aesKey, aesKeyOffset, aesKeyLength, zeroIv, (short) 0,
|
||||||
|
(short) zeroIv.length, AppConfig.CRYPT_CONN_ENCRYPT)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!m_ee_crypt.init(aesKey, aesKeyOffset, aesKeyLength, zeroIv, (short) 0,
|
||||||
|
(short) zeroIv.length, AppConfig.CRYPT_CONN_DECRYPT)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return m_ee_data.setCkeyIv_ramD(aesIv, aesIvOffset, aesIvLength);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected boolean changeGKey_ee(byte[] aesKey, short aesKeyOffset, short aesKeyLength,
|
||||||
|
byte[] aesIv, short aesIvOffset, short aesIvLength) {
|
||||||
|
if (aesKeyLength != AppConfig.AES_KEY_SIZE || aesIvLength != AppConfig.AES_IV_SIZE) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
byte[] zeroIv = m_ee_data.getZeroBuffer_ramD();
|
||||||
|
Util.arrayFillNonAtomic(zeroIv, (short) 0, (short) zeroIv.length, (byte) 0);
|
||||||
|
|
||||||
|
if (!m_ee_crypt.init(aesKey, aesKeyOffset, aesKeyLength, zeroIv, (short) 0,
|
||||||
|
(short) zeroIv.length, AppConfig.CRYPT_GKEY_ENCRYPT)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!m_ee_crypt.init(aesKey, aesKeyOffset, aesKeyLength, zeroIv, (short) 0,
|
||||||
|
(short) zeroIv.length, AppConfig.CRYPT_GKEY_DECRYPT)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return m_ee_data.setGkeyIv_ee(aesIv, aesIvOffset, aesIvLength);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* One-shot AES-CBC decrypt with the connection key. The first plaintext
|
||||||
|
* block is XORed with the connection IV, matching the original CAP.
|
||||||
|
*/
|
||||||
|
protected short decData(byte[] src, short srcOffset, short srcLength,
|
||||||
|
byte[] dst, short dstOffset) {
|
||||||
|
short size = m_ee_crypt.doFinal(src, srcOffset, srcLength, dst, dstOffset,
|
||||||
|
AppConfig.CRYPT_CONN_DECRYPT);
|
||||||
|
if (size <= 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
byte[] iv = m_ee_data.getCkeyIv_ramD();
|
||||||
|
short xorLength = (size < AppConfig.AES_BLOCK_SIZE) ? size : AppConfig.AES_BLOCK_SIZE;
|
||||||
|
for (short i = 0; i < xorLength; i++) {
|
||||||
|
dst[(short) (dstOffset + i)] = (byte) (dst[(short) (dstOffset + i)] ^ iv[i]);
|
||||||
|
}
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stateful GKEY CBC decrypt. {@code bInit} resets the chain and applies
|
||||||
|
* the persistent GKEY IV to the first block.
|
||||||
|
*/
|
||||||
|
protected short decUpdate_Gkey(byte[] src, short srcOffset, short srcLength,
|
||||||
|
byte[] dst, short dstOffset, boolean bInit) {
|
||||||
|
byte[] tmp = m_ee_data.getCryptBuffer_ramD();
|
||||||
|
if (bInit) {
|
||||||
|
m_ee_crypt.doFinal(tmp, (short) 0, AppConfig.AES_BLOCK_SIZE, tmp, (short) 0,
|
||||||
|
AppConfig.CRYPT_GKEY_DECRYPT);
|
||||||
|
}
|
||||||
|
short size = m_ee_crypt.update(src, srcOffset, srcLength, dst, dstOffset,
|
||||||
|
AppConfig.CRYPT_GKEY_DECRYPT);
|
||||||
|
if (bInit && size > 0) {
|
||||||
|
byte[] iv = m_ee_data.getGkeyIv_ee();
|
||||||
|
short xorLength = (size < AppConfig.AES_BLOCK_SIZE) ? size : AppConfig.AES_BLOCK_SIZE;
|
||||||
|
for (short i = 0; i < xorLength; i++) {
|
||||||
|
dst[(short) (dstOffset + i)] = (byte) (dst[(short) (dstOffset + i)] ^ iv[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* One-shot AES-CBC encrypt with the connection key. The first block is
|
||||||
|
* XORed with the connection IV and zero padded to a full block.
|
||||||
|
*/
|
||||||
|
protected short encData(byte[] src, short srcOffset, short srcLength,
|
||||||
|
byte[] dst, short dstOffset) {
|
||||||
|
if (srcLength <= 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (srcLength > AppConfig.AES_BLOCK_SIZE
|
||||||
|
&& (short) ((srcLength - AppConfig.AES_BLOCK_SIZE) % AppConfig.AES_BLOCK_SIZE) != 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
byte[] tmp = m_ee_data.getCryptBuffer_ramD();
|
||||||
|
byte[] iv = m_ee_data.getCkeyIv_ramD();
|
||||||
|
|
||||||
|
for (short i = 0; i < AppConfig.AES_BLOCK_SIZE; i++) {
|
||||||
|
byte value = (i < srcLength) ? src[(short) (srcOffset + i)] : (byte) 0;
|
||||||
|
tmp[i] = (byte) (value ^ iv[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (srcLength <= AppConfig.AES_BLOCK_SIZE) {
|
||||||
|
return m_ee_crypt.doFinal(tmp, (short) 0, AppConfig.AES_BLOCK_SIZE,
|
||||||
|
dst, dstOffset, AppConfig.CRYPT_CONN_ENCRYPT);
|
||||||
|
}
|
||||||
|
|
||||||
|
short size = m_ee_crypt.update(tmp, (short) 0, AppConfig.AES_BLOCK_SIZE,
|
||||||
|
dst, dstOffset, AppConfig.CRYPT_CONN_ENCRYPT);
|
||||||
|
size = (short) (size + m_ee_crypt.doFinal(src,
|
||||||
|
(short) (srcOffset + AppConfig.AES_BLOCK_SIZE),
|
||||||
|
(short) (srcLength - AppConfig.AES_BLOCK_SIZE),
|
||||||
|
dst, (short) (dstOffset + AppConfig.AES_BLOCK_SIZE),
|
||||||
|
AppConfig.CRYPT_CONN_ENCRYPT));
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stateful GKEY CBC encrypt. {@code bInit} resets the chain and applies
|
||||||
|
* the persistent GKEY IV to the first block.
|
||||||
|
*/
|
||||||
|
protected short encUpdate_GKey(byte[] src, short srcOffset, short srcLength,
|
||||||
|
byte[] dst, short dstOffset, boolean bInit) {
|
||||||
|
if (!bInit) {
|
||||||
|
return m_ee_crypt.update(src, srcOffset, srcLength, dst, dstOffset,
|
||||||
|
AppConfig.CRYPT_GKEY_ENCRYPT);
|
||||||
|
}
|
||||||
|
if (srcLength <= 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
byte[] tmp = m_ee_data.getCryptBuffer_ramD();
|
||||||
|
byte[] iv = m_ee_data.getGkeyIv_ee();
|
||||||
|
|
||||||
|
m_ee_crypt.doFinal(tmp, (short) 0, AppConfig.AES_BLOCK_SIZE, tmp, (short) 0,
|
||||||
|
AppConfig.CRYPT_GKEY_ENCRYPT);
|
||||||
|
|
||||||
|
for (short i = 0; i < AppConfig.AES_BLOCK_SIZE; i++) {
|
||||||
|
byte value = (i < srcLength) ? src[(short) (srcOffset + i)] : (byte) 0;
|
||||||
|
tmp[i] = (byte) (value ^ iv[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (srcLength <= AppConfig.AES_BLOCK_SIZE) {
|
||||||
|
return m_ee_crypt.update(tmp, (short) 0, AppConfig.AES_BLOCK_SIZE,
|
||||||
|
dst, dstOffset, AppConfig.CRYPT_GKEY_ENCRYPT);
|
||||||
|
}
|
||||||
|
|
||||||
|
short size = m_ee_crypt.update(tmp, (short) 0, AppConfig.AES_BLOCK_SIZE,
|
||||||
|
dst, dstOffset, AppConfig.CRYPT_GKEY_ENCRYPT);
|
||||||
|
size = (short) (size + m_ee_crypt.update(src,
|
||||||
|
(short) (srcOffset + AppConfig.AES_BLOCK_SIZE),
|
||||||
|
(short) (srcLength - AppConfig.AES_BLOCK_SIZE),
|
||||||
|
dst, (short) (dstOffset + AppConfig.AES_BLOCK_SIZE),
|
||||||
|
AppConfig.CRYPT_GKEY_ENCRYPT));
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected boolean changeRsaKey_ee(byte[] data1, short data1Offset, short data1Length,
|
||||||
|
byte[] data2, short data2Offset, short data2Length) {
|
||||||
|
return m_ee_crypt.init(data1, data1Offset, data1Length,
|
||||||
|
data2, data2Offset, data2Length, AppConfig.CRYPT_RSA_DECRYPT);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected short rsaDecData(byte[] src, short srcOffset, short srcLength,
|
||||||
|
byte[] dst, short dstOffset) {
|
||||||
|
return m_ee_crypt.doFinal(src, srcOffset, srcLength, dst, dstOffset,
|
||||||
|
AppConfig.CRYPT_RSA_DECRYPT);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected short checkFooter(byte[] data, short dataOffset, short dataLength) {
|
||||||
|
byte[] kh = m_ee_data.getKh_ramD();
|
||||||
|
return checkFooterWithKey(kh, (short) 0, (short) kh.length,
|
||||||
|
data, dataOffset, dataLength);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Verifies an authenticated request against an explicit HMAC key. Used by
|
||||||
|
* AuthStart so a failed handshake does not overwrite the active Kh.
|
||||||
|
*/
|
||||||
|
protected short checkFooterWithKey(byte[] kh, short khOffset, short khLength,
|
||||||
|
byte[] data, short dataOffset, short dataLength) {
|
||||||
|
if (data == null || dataLength < AppConfig.AUTH_FOOTER_SIZE) {
|
||||||
|
return AppConfig.BAD_DATASIZE;
|
||||||
|
}
|
||||||
|
short oddOffset = (short) (dataOffset + dataLength - AppConfig.AUTH_FOOTER_SIZE);
|
||||||
|
short authOffset = (short) (dataOffset + dataLength - AppConfig.NONCE_SIZE);
|
||||||
|
byte[] buffer = m_ee_data.getFooterBuffer_ramD();
|
||||||
|
byte[] lastNonce = m_ee_data.getLastNonceEvent_ramD();
|
||||||
|
|
||||||
|
m_ee_sha1.reset();
|
||||||
|
m_ee_sha1.doFinal(data, dataOffset,
|
||||||
|
(short) (dataLength - AppConfig.AUTH_FOOTER_SIZE), buffer, (short) 0);
|
||||||
|
|
||||||
|
boolean ok = m_ee_hmacSha1.init(kh, khOffset, khLength);
|
||||||
|
ok = ok && m_ee_hmacSha1.update(buffer, (short) 0, AppConfig.SHA1_SIZE);
|
||||||
|
ok = ok && m_ee_hmacSha1.update(lastNonce, (short) 0,
|
||||||
|
(short) lastNonce.length);
|
||||||
|
ok = ok && m_ee_hmacSha1.sign(data, oddOffset, AppConfig.NONCE_SIZE,
|
||||||
|
buffer, (short) 0);
|
||||||
|
|
||||||
|
if (!ok || Util.arrayCompare(data, authOffset, buffer, (short) 0,
|
||||||
|
AppConfig.NONCE_SIZE) != 0) {
|
||||||
|
m_ee_data.incrementErrorCount();
|
||||||
|
return AppConfig.AUTHFAIL;
|
||||||
|
}
|
||||||
|
m_ee_data.clearErrorCount();
|
||||||
|
return AppConfig.SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected short makeSendAuthPacket(short tag, short paramSize, short result,
|
||||||
|
byte[] data, short dataOffset, short dataSize,
|
||||||
|
byte[] nonceOdd, short oddOffset, short oddLength,
|
||||||
|
byte[] sendPacket, short packetOffset, short packetLength) {
|
||||||
|
byte[] kh = m_ee_data.getKh_ramD();
|
||||||
|
byte[] nonceEvent = m_ee_data.getNewNonceEvent_ramD();
|
||||||
|
|
||||||
|
boolean ok = m_ee_packet.setData(kh, (short) 0, (short) kh.length,
|
||||||
|
tag, paramSize, result, m_ee_cmd,
|
||||||
|
data, dataOffset, dataSize,
|
||||||
|
nonceEvent, (short) 0, (short) nonceEvent.length,
|
||||||
|
nonceOdd, oddOffset, oddLength,
|
||||||
|
sendPacket, packetOffset, packetLength, m_ee_sha1, m_ee_hmacSha1);
|
||||||
|
if (!ok) {
|
||||||
|
return makeErrorPacket(AppConfig.FAIL, sendPacket, packetOffset, packetLength);
|
||||||
|
}
|
||||||
|
m_ee_data.setLastNonceEvent_ramD(nonceEvent, (short) 0,
|
||||||
|
(short) nonceEvent.length);
|
||||||
|
return paramSize;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,113 @@
|
|||||||
|
package sega.a7;
|
||||||
|
|
||||||
|
import javacard.framework.Util;
|
||||||
|
import javacard.security.CryptoException;
|
||||||
|
import javacard.security.MessageDigest;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* RSA-authenticated session handshake (tag C5 / C6).
|
||||||
|
*
|
||||||
|
* <p>Request: header(6) | RSA-2048 ciphertext(256) | nonceOdd(20) | HMAC(20).
|
||||||
|
* The decrypted plaintext must contain AES key(16) | IV(16) | new Kh(20).
|
||||||
|
* The request HMAC is verified with the new Kh before it replaces the active
|
||||||
|
* key, so a failed handshake cannot corrupt an established session.</p>
|
||||||
|
*/
|
||||||
|
public class AuthStart extends AuthCommand {
|
||||||
|
|
||||||
|
private static final short RSA_CIPHERTEXT_OFFSET = AppConfig.HEADER_SIZE;
|
||||||
|
private static final short NONCE_ODD_OFFSET =
|
||||||
|
(short) (RSA_CIPHERTEXT_OFFSET + AppConfig.RSA_KEY_SIZE);
|
||||||
|
private static final short NEW_KH_OFFSET =
|
||||||
|
(short) (AppConfig.AES_KEY_SIZE + AppConfig.AES_IV_SIZE);
|
||||||
|
private static final short SESSION_MATERIAL_SIZE =
|
||||||
|
(short) (NEW_KH_OFFSET + AppConfig.NONCE_SIZE);
|
||||||
|
|
||||||
|
public AuthStart(short cmd, AppData data, MakePacket packet, MessageDigest sha1,
|
||||||
|
HmacSha1 hmacSha1, Crypt crypt) {
|
||||||
|
super(cmd, (short) 302, (short) 46, AppConfig.LEVEL_SESSION,
|
||||||
|
data, packet, sha1, hmacSha1, crypt);
|
||||||
|
}
|
||||||
|
|
||||||
|
public short execute(byte[] data, short dataOffset, short dataLength,
|
||||||
|
byte[] dst, short dstOffset, short dstLength) {
|
||||||
|
if (m_ee_data.getSession_ramD() != AppConfig.SESSION_ENABLE) {
|
||||||
|
return makeErrorPacket(AppConfig.INVALID_AUTH, dst, dstOffset, dstLength);
|
||||||
|
}
|
||||||
|
if (m_ee_data.getLevel_ramD() != AppConfig.LEVEL_SESSION) {
|
||||||
|
return makeErrorPacket(AppConfig.LV_ERROR, dst, dstOffset, dstLength);
|
||||||
|
}
|
||||||
|
|
||||||
|
short error = checkHeader(data, dataOffset, dataLength);
|
||||||
|
if (error != AppConfig.SUCCESS) {
|
||||||
|
return makeErrorPacket(error, dst, dstOffset, dstLength);
|
||||||
|
}
|
||||||
|
|
||||||
|
byte[] buffer = m_ee_data.getGeneralBuffer_ramD();
|
||||||
|
if (buffer.length < AppConfig.RSA_KEY_SIZE) {
|
||||||
|
return makeErrorPacket(AppConfig.FAIL, dst, dstOffset, dstLength);
|
||||||
|
}
|
||||||
|
|
||||||
|
short decSize;
|
||||||
|
try {
|
||||||
|
decSize = rsaDecData(data,
|
||||||
|
(short) (dataOffset + RSA_CIPHERTEXT_OFFSET),
|
||||||
|
AppConfig.RSA_KEY_SIZE, buffer, (short) 0);
|
||||||
|
} catch (CryptoException e) {
|
||||||
|
return makeErrorPacket(AppConfig.AUTHFAIL, dst, dstOffset, dstLength);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (decSize < SESSION_MATERIAL_SIZE) {
|
||||||
|
zeroize(buffer);
|
||||||
|
return makeErrorPacket(AppConfig.AUTHFAIL, dst, dstOffset, dstLength);
|
||||||
|
}
|
||||||
|
|
||||||
|
error = checkFooterWithKey(buffer, NEW_KH_OFFSET, AppConfig.NONCE_SIZE,
|
||||||
|
data, dataOffset, dataLength);
|
||||||
|
if (error != AppConfig.SUCCESS) {
|
||||||
|
zeroize(buffer);
|
||||||
|
return makeErrorPacket(error, dst, dstOffset, dstLength);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!m_ee_data.setKh_ramD(buffer, NEW_KH_OFFSET, AppConfig.NONCE_SIZE)) {
|
||||||
|
zeroize(buffer);
|
||||||
|
return makeErrorPacket(AppConfig.FAIL, dst, dstOffset, dstLength);
|
||||||
|
}
|
||||||
|
if (!changeConnectionKey_ee(buffer, (short) 0, AppConfig.AES_KEY_SIZE,
|
||||||
|
buffer, AppConfig.AES_KEY_SIZE, AppConfig.AES_IV_SIZE)) {
|
||||||
|
zeroize(buffer);
|
||||||
|
return makeErrorPacket(AppConfig.FAIL, dst, dstOffset, dstLength);
|
||||||
|
}
|
||||||
|
|
||||||
|
m_ee_data.setLevel_ramD(AppConfig.LEVEL_AUTH);
|
||||||
|
short sendSize = makeSendAuthPacket(AppConfig.TAG_RSP_RSA_COMMAND,
|
||||||
|
(short) 46, AppConfig.SUCCESS, null, (short) 0, (short) 0,
|
||||||
|
data, (short) (dataOffset + NONCE_ODD_OFFSET), AppConfig.NONCE_SIZE,
|
||||||
|
dst, dstOffset, dstLength);
|
||||||
|
zeroize(buffer);
|
||||||
|
return sendSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected short checkHeader(byte[] data, short dataOffset, short dataLength) {
|
||||||
|
if (data == null || dataOffset < 0 || dataLength < 0
|
||||||
|
|| dataOffset > data.length
|
||||||
|
|| dataLength > (short) (data.length - dataOffset)) {
|
||||||
|
return AppConfig.BAD_DATASIZE;
|
||||||
|
}
|
||||||
|
if (dataLength != (short) 302) {
|
||||||
|
return AppConfig.BAD_DATASIZE;
|
||||||
|
}
|
||||||
|
if (Util.getShort(data, dataOffset) != AppConfig.TAG_RQU_RSA_COMMAND) {
|
||||||
|
return AppConfig.BAD_TAG;
|
||||||
|
}
|
||||||
|
if (Util.getShort(data, (short) (dataOffset + AppConfig.SIZE_OFFSET))
|
||||||
|
!= (short) 302) {
|
||||||
|
return AppConfig.BAD_DATASIZE;
|
||||||
|
}
|
||||||
|
return AppConfig.SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void zeroize(byte[] buffer) {
|
||||||
|
Util.arrayFillNonAtomic(buffer, (short) 0, (short) buffer.length,
|
||||||
|
(byte) 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,162 @@
|
|||||||
|
package sega.a7;
|
||||||
|
|
||||||
|
import javacard.framework.JCSystem;
|
||||||
|
import javacard.framework.Util;
|
||||||
|
import javacard.security.MessageDigest;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Base class for unauthenticated commands (tag C1 / C3).
|
||||||
|
*/
|
||||||
|
public class Command {
|
||||||
|
|
||||||
|
protected short m_ee_cmd;
|
||||||
|
protected short m_ee_recvPacketSize;
|
||||||
|
protected short m_ee_sendPacketSize;
|
||||||
|
protected short[] m_ramD_setSendPacketSize;
|
||||||
|
protected short m_ee_tag;
|
||||||
|
protected MakePacket m_ee_packet;
|
||||||
|
protected AppData m_ee_data;
|
||||||
|
protected MessageDigest m_ee_sha1;
|
||||||
|
|
||||||
|
protected byte[] m_ramD_outData;
|
||||||
|
protected short[] m_ramD_outDataInfo;
|
||||||
|
|
||||||
|
public Command(short cmd, short recvPacketSize, short sendPacketSize,
|
||||||
|
AppData data, MakePacket packet, MessageDigest sha1) {
|
||||||
|
m_ee_cmd = cmd;
|
||||||
|
m_ee_data = data;
|
||||||
|
m_ee_packet = packet;
|
||||||
|
m_ee_recvPacketSize = recvPacketSize;
|
||||||
|
m_ee_sendPacketSize = sendPacketSize;
|
||||||
|
m_ee_tag = AppConfig.TAG_RQU_COMMAND;
|
||||||
|
m_ee_sha1 = sha1;
|
||||||
|
m_ramD_setSendPacketSize = JCSystem.makeTransientShortArray((short) 1,
|
||||||
|
JCSystem.CLEAR_ON_RESET);
|
||||||
|
m_ramD_setSendPacketSize[0] = sendPacketSize;
|
||||||
|
m_ramD_outData = data.getSendOutBuffer_ramD();
|
||||||
|
m_ramD_outDataInfo = data.getSendOutInfo_ramD();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean select() {
|
||||||
|
m_ramD_setSendPacketSize[0] = m_ee_sendPacketSize;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public short core(byte[] data, short dataOffset, short dataLength) {
|
||||||
|
return AppConfig.SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
public short execute(byte[] data, short dataOffset, short dataLength,
|
||||||
|
byte[] dst, short dstOffset, short dstLength) {
|
||||||
|
short error = checkHeader(data, dataOffset, dataLength);
|
||||||
|
if (error != AppConfig.SUCCESS) {
|
||||||
|
return makeErrorPacket(error, dst, dstOffset, dstLength);
|
||||||
|
}
|
||||||
|
|
||||||
|
error = checkFooter(data, dataOffset, dataLength);
|
||||||
|
if (error != AppConfig.SUCCESS) {
|
||||||
|
return makeErrorPacket(error, dst, dstOffset, dstLength);
|
||||||
|
}
|
||||||
|
|
||||||
|
m_ramD_outDataInfo[0] = 0;
|
||||||
|
m_ramD_outDataInfo[1] = 0;
|
||||||
|
|
||||||
|
error = core(data, dataOffset, dataLength);
|
||||||
|
if (error != AppConfig.SUCCESS) {
|
||||||
|
return makeErrorPacket(error, dst, dstOffset, dstLength);
|
||||||
|
}
|
||||||
|
|
||||||
|
short dataRef = m_ramD_outDataInfo[0];
|
||||||
|
short dataSize = m_ramD_outDataInfo[1];
|
||||||
|
if (dataRef < 0 || dataSize < 0 || dataRef > m_ramD_outData.length
|
||||||
|
|| dataSize > (short) (m_ramD_outData.length - dataRef)) {
|
||||||
|
return makeErrorPacket(AppConfig.FAIL, dst, dstOffset, dstLength);
|
||||||
|
}
|
||||||
|
|
||||||
|
short paramSize = (short) (AppConfig.HEADER_SIZE + dataSize + AppConfig.SHA1_SIZE);
|
||||||
|
m_ramD_setSendPacketSize[0] = paramSize;
|
||||||
|
return makeSendPacket(AppConfig.TAG_RSP_COMMAND, paramSize, AppConfig.SUCCESS,
|
||||||
|
m_ramD_outData, dataRef, dataSize, dst, dstOffset, dstLength);
|
||||||
|
}
|
||||||
|
|
||||||
|
public short getCmd_ee() {
|
||||||
|
return m_ee_cmd;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected short setSendPacketSize_ramD(short sendSize) {
|
||||||
|
m_ramD_setSendPacketSize[0] = sendSize;
|
||||||
|
return m_ramD_setSendPacketSize[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
protected short checkHeader(byte[] data, short dataOffset, short dataLength) {
|
||||||
|
if (data == null || dataOffset < 0 || dataLength < 0
|
||||||
|
|| dataOffset > data.length
|
||||||
|
|| dataLength > (short) (data.length - dataOffset)) {
|
||||||
|
return AppConfig.BAD_DATASIZE;
|
||||||
|
}
|
||||||
|
if (dataLength < (short) (AppConfig.HEADER_SIZE + AppConfig.SHA1_SIZE)) {
|
||||||
|
return AppConfig.BAD_DATASIZE;
|
||||||
|
}
|
||||||
|
|
||||||
|
short paramSize = Util.getShort(data, (short) (dataOffset + AppConfig.SIZE_OFFSET));
|
||||||
|
if (paramSize != dataLength) {
|
||||||
|
return AppConfig.BAD_DATASIZE;
|
||||||
|
}
|
||||||
|
|
||||||
|
short tag = Util.getShort(data, (short) (dataOffset + AppConfig.TAG_OFFSET));
|
||||||
|
if (tag != m_ee_tag) {
|
||||||
|
return AppConfig.BAD_TAG;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_ee_recvPacketSize != 0 && dataLength != m_ee_recvPacketSize) {
|
||||||
|
return AppConfig.BAD_DATASIZE;
|
||||||
|
}
|
||||||
|
return AppConfig.SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected short checkFooter(byte[] data, short dataOffset, short dataLength) {
|
||||||
|
if (data == null || dataLength < AppConfig.SHA1_SIZE) {
|
||||||
|
return AppConfig.SUMFAIL;
|
||||||
|
}
|
||||||
|
short contentLength = (short) (dataLength - AppConfig.SHA1_SIZE);
|
||||||
|
short digestOffset = (short) (dataOffset + contentLength);
|
||||||
|
byte[] buffer = m_ee_data.getFooterBuffer_ramD();
|
||||||
|
|
||||||
|
m_ee_sha1.reset();
|
||||||
|
m_ee_sha1.doFinal(data, dataOffset, contentLength, buffer, (short) 0);
|
||||||
|
if (Util.arrayCompare(data, digestOffset, buffer, (short) 0,
|
||||||
|
AppConfig.SHA1_SIZE) != 0) {
|
||||||
|
return AppConfig.SUMFAIL;
|
||||||
|
}
|
||||||
|
return AppConfig.SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected short makeSendPacket(short tag, short paramSize, short result,
|
||||||
|
byte[] data, short dataOffset, short dataSize,
|
||||||
|
byte[] sendPacket, short packetOffset, short packetLength) {
|
||||||
|
boolean ok = m_ee_packet.setData(null, (short) 0, (short) 0, tag, paramSize,
|
||||||
|
result, m_ee_cmd, data, dataOffset, dataSize,
|
||||||
|
null, (short) 0, (short) 0, null, (short) 0, (short) 0,
|
||||||
|
sendPacket, packetOffset, packetLength, m_ee_sha1, null);
|
||||||
|
if (!ok) {
|
||||||
|
return makeErrorPacket(AppConfig.FAIL, sendPacket, packetOffset, packetLength);
|
||||||
|
}
|
||||||
|
return paramSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected short makeErrorPacket(short error, byte[] sendPacket,
|
||||||
|
short packetOffset, short packetLength) {
|
||||||
|
return makeErrorPacket(error, m_ee_cmd, sendPacket, packetOffset, packetLength);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected short makeErrorPacket(short error, short cmd, byte[] sendPacket,
|
||||||
|
short packetOffset, short packetLength) {
|
||||||
|
m_ee_data.setLastError_ramD(error);
|
||||||
|
m_ee_packet.setData(null, (short) 0, (short) 0, AppConfig.TAG_RSP_COMMAND,
|
||||||
|
AppConfig.ERROR_PACKET_SIZE, error, cmd,
|
||||||
|
null, (short) 0, (short) 0, null, (short) 0, (short) 0,
|
||||||
|
null, (short) 0, (short) 0,
|
||||||
|
sendPacket, packetOffset, packetLength, m_ee_sha1, null);
|
||||||
|
return AppConfig.ERROR_PACKET_SIZE;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,78 @@
|
|||||||
|
package sega.a7;
|
||||||
|
|
||||||
|
import javacard.framework.Util;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ordinal-to-command dispatcher.
|
||||||
|
*/
|
||||||
|
public class CommandServer {
|
||||||
|
|
||||||
|
private Command m_ee_noCommand;
|
||||||
|
private Command[] m_ee_list;
|
||||||
|
private short m_ee_listCount;
|
||||||
|
|
||||||
|
public CommandServer() {
|
||||||
|
m_ee_noCommand = null;
|
||||||
|
m_ee_list = new Command[4];
|
||||||
|
m_ee_listCount = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setNoCommand(Command cmdClass) {
|
||||||
|
m_ee_noCommand = cmdClass;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void attachCommand(Command cmdClass) {
|
||||||
|
if (cmdClass == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (m_ee_listCount >= m_ee_list.length) {
|
||||||
|
Command[] tmp = new Command[(short) (m_ee_list.length * 2)];
|
||||||
|
for (short i = 0; i < m_ee_list.length; i++) {
|
||||||
|
tmp[i] = m_ee_list[i];
|
||||||
|
}
|
||||||
|
m_ee_list = tmp;
|
||||||
|
}
|
||||||
|
m_ee_list[m_ee_listCount] = cmdClass;
|
||||||
|
m_ee_listCount = (short) (m_ee_listCount + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
public short findCommand(short cmd) {
|
||||||
|
for (short i = 0; i < m_ee_listCount; i++) {
|
||||||
|
if (cmd == m_ee_list[i].getCmd_ee()) {
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean select() {
|
||||||
|
boolean ret = true;
|
||||||
|
for (short i = 0; i < m_ee_listCount; i++) {
|
||||||
|
if (!m_ee_list[i].select()) {
|
||||||
|
ret = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
public short execute(byte[] data, short dataOffset, short dataLength,
|
||||||
|
byte[] dst, short dstOffset, short dstLength) {
|
||||||
|
short cmd = -1;
|
||||||
|
if (data != null && dataOffset >= 0 && dataLength >= AppConfig.HEADER_SIZE
|
||||||
|
&& dataOffset <= (short) (data.length - AppConfig.HEADER_SIZE)) {
|
||||||
|
cmd = Util.getShort(data,
|
||||||
|
(short) (dataOffset + AppConfig.CMD_OFFSET));
|
||||||
|
}
|
||||||
|
|
||||||
|
short index = findCommand(cmd);
|
||||||
|
if (index >= 0) {
|
||||||
|
return m_ee_list[index].execute(data, dataOffset, dataLength,
|
||||||
|
dst, dstOffset, dstLength);
|
||||||
|
}
|
||||||
|
if (m_ee_noCommand != null) {
|
||||||
|
return m_ee_noCommand.execute(data, dataOffset, dataLength,
|
||||||
|
dst, dstOffset, dstLength);
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,138 @@
|
|||||||
|
package sega.a7;
|
||||||
|
|
||||||
|
import javacard.security.AESKey;
|
||||||
|
import javacard.security.CryptoException;
|
||||||
|
import javacard.security.KeyBuilder;
|
||||||
|
import javacard.security.RSAPrivateKey;
|
||||||
|
import javacardx.crypto.Cipher;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Owns the four AES contexts (GKEY encrypt/decrypt, connection encrypt/decrypt)
|
||||||
|
* and the RSA private-key context.
|
||||||
|
*
|
||||||
|
* <p>GKEY contexts are persistent and intentionally keep their CBC chaining
|
||||||
|
* state between commands. Connection contexts are re-initialised for every
|
||||||
|
* command and therefore behave as independent CBC operations.</p>
|
||||||
|
*/
|
||||||
|
public class Crypt {
|
||||||
|
|
||||||
|
private final AESKey m_ee_aesKeyEnc;
|
||||||
|
private final AESKey m_ee_aesKeyDec;
|
||||||
|
private final RSAPrivateKey m_ee_rsaKey;
|
||||||
|
|
||||||
|
private final Cipher m_ee_cipherAesEnc;
|
||||||
|
private final Cipher m_ee_cipherAesDec;
|
||||||
|
private final Cipher m_ee_cipherRsa;
|
||||||
|
|
||||||
|
private final AESKey m_ee_aesConnectionKeyEnc;
|
||||||
|
private final AESKey m_ee_aesConnectionKeyDec;
|
||||||
|
private final Cipher m_ee_cipherConnectionAesEnc;
|
||||||
|
private final Cipher m_ee_cipherConnectionAesDec;
|
||||||
|
|
||||||
|
public Crypt() {
|
||||||
|
m_ee_aesKeyEnc = (AESKey) KeyBuilder.buildKey(KeyBuilder.TYPE_AES,
|
||||||
|
KeyBuilder.LENGTH_AES_128, false);
|
||||||
|
m_ee_aesKeyDec = (AESKey) KeyBuilder.buildKey(KeyBuilder.TYPE_AES,
|
||||||
|
KeyBuilder.LENGTH_AES_128, false);
|
||||||
|
m_ee_rsaKey = (RSAPrivateKey) KeyBuilder.buildKey(KeyBuilder.TYPE_RSA_PRIVATE,
|
||||||
|
KeyBuilder.LENGTH_RSA_2048, false);
|
||||||
|
|
||||||
|
m_ee_cipherAesEnc = Cipher.getInstance(Cipher.ALG_AES_BLOCK_128_CBC_NOPAD, false);
|
||||||
|
m_ee_cipherAesDec = Cipher.getInstance(Cipher.ALG_AES_BLOCK_128_CBC_NOPAD, false);
|
||||||
|
m_ee_cipherRsa = Cipher.getInstance(Cipher.ALG_RSA_PKCS1, false);
|
||||||
|
|
||||||
|
m_ee_aesConnectionKeyEnc = (AESKey) KeyBuilder.buildKey(
|
||||||
|
KeyBuilder.TYPE_AES_TRANSIENT_RESET, KeyBuilder.LENGTH_AES_128, false);
|
||||||
|
m_ee_aesConnectionKeyDec = (AESKey) KeyBuilder.buildKey(
|
||||||
|
KeyBuilder.TYPE_AES_TRANSIENT_RESET, KeyBuilder.LENGTH_AES_128, false);
|
||||||
|
m_ee_cipherConnectionAesEnc = Cipher.getInstance(
|
||||||
|
Cipher.ALG_AES_BLOCK_128_CBC_NOPAD, false);
|
||||||
|
m_ee_cipherConnectionAesDec = Cipher.getInstance(
|
||||||
|
Cipher.ALG_AES_BLOCK_128_CBC_NOPAD, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean init(byte[] data1, short data1Offset, short data1Length,
|
||||||
|
byte[] data2, short data2Offset, short data2Length,
|
||||||
|
short mode) {
|
||||||
|
if (data1 == null || data2 == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (data1Offset < 0 || data1Length < 0 || data1Offset > data1.length
|
||||||
|
|| data1Length > (short) (data1.length - data1Offset)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (data2Offset < 0 || data2Length < 0 || data2Offset > data2.length
|
||||||
|
|| data2Length > (short) (data2.length - data2Offset)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (mode == AppConfig.CRYPT_GKEY_ENCRYPT) {
|
||||||
|
m_ee_aesKeyEnc.setKey(data1, data1Offset);
|
||||||
|
m_ee_cipherAesEnc.init(m_ee_aesKeyEnc, Cipher.MODE_ENCRYPT,
|
||||||
|
data2, data2Offset, data2Length);
|
||||||
|
} else if (mode == AppConfig.CRYPT_GKEY_DECRYPT) {
|
||||||
|
m_ee_aesKeyDec.setKey(data1, data1Offset);
|
||||||
|
m_ee_cipherAesDec.init(m_ee_aesKeyDec, Cipher.MODE_DECRYPT,
|
||||||
|
data2, data2Offset, data2Length);
|
||||||
|
} else if (mode == AppConfig.CRYPT_CONN_ENCRYPT) {
|
||||||
|
m_ee_aesConnectionKeyEnc.setKey(data1, data1Offset);
|
||||||
|
m_ee_cipherConnectionAesEnc.init(m_ee_aesConnectionKeyEnc,
|
||||||
|
Cipher.MODE_ENCRYPT, data2, data2Offset, data2Length);
|
||||||
|
} else if (mode == AppConfig.CRYPT_CONN_DECRYPT) {
|
||||||
|
m_ee_aesConnectionKeyDec.setKey(data1, data1Offset);
|
||||||
|
m_ee_cipherConnectionAesDec.init(m_ee_aesConnectionKeyDec,
|
||||||
|
Cipher.MODE_DECRYPT, data2, data2Offset, data2Length);
|
||||||
|
} else if (mode == AppConfig.CRYPT_RSA_DECRYPT) {
|
||||||
|
m_ee_rsaKey.setModulus(data1, data1Offset, data1Length);
|
||||||
|
m_ee_rsaKey.setExponent(data2, data2Offset, data2Length);
|
||||||
|
m_ee_cipherRsa.init(m_ee_rsaKey, Cipher.MODE_DECRYPT);
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} catch (CryptoException e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public short update(byte[] src, short srcOffset, short srcLength,
|
||||||
|
byte[] dst, short dstOffset, short mode) {
|
||||||
|
if (src == null || srcOffset < 0 || srcLength < 0 || srcOffset > src.length
|
||||||
|
|| srcLength > (short) (src.length - srcOffset)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (mode == AppConfig.CRYPT_GKEY_ENCRYPT) {
|
||||||
|
return m_ee_cipherAesEnc.update(src, srcOffset, srcLength, dst, dstOffset);
|
||||||
|
} else if (mode == AppConfig.CRYPT_GKEY_DECRYPT) {
|
||||||
|
return m_ee_cipherAesDec.update(src, srcOffset, srcLength, dst, dstOffset);
|
||||||
|
} else if (mode == AppConfig.CRYPT_CONN_ENCRYPT) {
|
||||||
|
return m_ee_cipherConnectionAesEnc.update(src, srcOffset, srcLength, dst, dstOffset);
|
||||||
|
} else if (mode == AppConfig.CRYPT_CONN_DECRYPT) {
|
||||||
|
return m_ee_cipherConnectionAesDec.update(src, srcOffset, srcLength, dst, dstOffset);
|
||||||
|
} else if (mode == AppConfig.CRYPT_RSA_DECRYPT) {
|
||||||
|
return m_ee_cipherRsa.update(src, srcOffset, srcLength, dst, dstOffset);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public short doFinal(byte[] src, short srcOffset, short srcLength,
|
||||||
|
byte[] dst, short dstOffset, short mode) {
|
||||||
|
if (src == null || srcOffset < 0 || srcLength < 0 || srcOffset > src.length
|
||||||
|
|| srcLength > (short) (src.length - srcOffset)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (mode == AppConfig.CRYPT_GKEY_ENCRYPT) {
|
||||||
|
return m_ee_cipherAesEnc.doFinal(src, srcOffset, srcLength, dst, dstOffset);
|
||||||
|
} else if (mode == AppConfig.CRYPT_GKEY_DECRYPT) {
|
||||||
|
return m_ee_cipherAesDec.doFinal(src, srcOffset, srcLength, dst, dstOffset);
|
||||||
|
} else if (mode == AppConfig.CRYPT_CONN_ENCRYPT) {
|
||||||
|
return m_ee_cipherConnectionAesEnc.doFinal(src, srcOffset, srcLength, dst, dstOffset);
|
||||||
|
} else if (mode == AppConfig.CRYPT_CONN_DECRYPT) {
|
||||||
|
return m_ee_cipherConnectionAesDec.doFinal(src, srcOffset, srcLength, dst, dstOffset);
|
||||||
|
} else if (mode == AppConfig.CRYPT_RSA_DECRYPT) {
|
||||||
|
return m_ee_cipherRsa.doFinal(src, srcOffset, srcLength, dst, dstOffset);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
package sega.a7;
|
||||||
|
|
||||||
|
import javacard.framework.Util;
|
||||||
|
import javacard.security.MessageDigest;
|
||||||
|
|
||||||
|
public class EepInit extends AuthCommand {
|
||||||
|
|
||||||
|
public EepInit(short cmd, AppData data, MakePacket packet, MessageDigest sha1,
|
||||||
|
HmacSha1 hmacSha1, Crypt crypt) {
|
||||||
|
super(cmd, (short) 46, (short) 46, AppConfig.LEVEL_AUTH,
|
||||||
|
data, packet, sha1, hmacSha1, crypt);
|
||||||
|
}
|
||||||
|
|
||||||
|
public short core(byte[] data, short dataOffset, short dataLength) {
|
||||||
|
if (m_ee_data.getSflag_ee()) {
|
||||||
|
return AppConfig.EEPWRITE_DISABLE;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_ee_data.reset();
|
||||||
|
|
||||||
|
byte[] modulus = m_ee_data.getRsaDefaultPriModulus_ee();
|
||||||
|
byte[] exponent = m_ee_data.getRsaDefaultPriExponent_ee();
|
||||||
|
|
||||||
|
if (!m_ee_data.setRsaPriKeyExponent_ee(exponent, (short) 0,
|
||||||
|
(short) exponent.length)) {
|
||||||
|
return AppConfig.EEP_WRITEFAIL;
|
||||||
|
}
|
||||||
|
if (!m_ee_data.setRsaPriKeyModulus_ee(modulus, (short) 0,
|
||||||
|
(short) modulus.length)) {
|
||||||
|
return AppConfig.EEP_WRITEFAIL;
|
||||||
|
}
|
||||||
|
if (!changeRsaKey_ee(modulus, (short) 0, (short) modulus.length,
|
||||||
|
exponent, (short) 0, (short) exponent.length)) {
|
||||||
|
return AppConfig.FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
byte[] tmp = m_ee_data.getCryptBuffer_ramD();
|
||||||
|
Util.arrayFillNonAtomic(tmp, (short) 0, (short) tmp.length, (byte) 0xFF);
|
||||||
|
if (!changeGKey_ee(tmp, (short) 0, (short) tmp.length,
|
||||||
|
tmp, (short) 0, (short) tmp.length)) {
|
||||||
|
return AppConfig.FAIL;
|
||||||
|
}
|
||||||
|
return AppConfig.SUCCESS;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
package sega.a7;
|
||||||
|
|
||||||
|
import javacard.framework.Util;
|
||||||
|
import javacard.security.MessageDigest;
|
||||||
|
|
||||||
|
public class ErrorGet extends Command {
|
||||||
|
|
||||||
|
public ErrorGet(short cmd, AppData data, MakePacket packet, MessageDigest sha1) {
|
||||||
|
super(cmd, (short) 26, (short) 28, data, packet, sha1);
|
||||||
|
}
|
||||||
|
|
||||||
|
public short core(byte[] data, short dataOffset, short dataLength) {
|
||||||
|
Util.setShort(m_ramD_outData, (short) 0, m_ee_data.getLastError_ramD());
|
||||||
|
m_ramD_outDataInfo[0] = 0;
|
||||||
|
m_ramD_outDataInfo[1] = 2;
|
||||||
|
return AppConfig.SUCCESS;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
package sega.a7;
|
||||||
|
|
||||||
|
import javacard.framework.Util;
|
||||||
|
import javacard.security.MessageDigest;
|
||||||
|
|
||||||
|
public class GkeyDec extends AuthCommand {
|
||||||
|
|
||||||
|
public GkeyDec(short cmd, AppData data, MakePacket packet, MessageDigest sha1,
|
||||||
|
HmacSha1 hmacSha1, Crypt crypt) {
|
||||||
|
super(cmd, (short) 63, (short) 62, AppConfig.LEVEL_AUTH,
|
||||||
|
data, packet, sha1, hmacSha1, crypt);
|
||||||
|
}
|
||||||
|
|
||||||
|
public short core(byte[] data, short dataOffset, short dataLength) {
|
||||||
|
byte resetFlag = data[(short) (dataOffset + AppConfig.HEADER_SIZE)];
|
||||||
|
byte[] buffer = m_ee_data.getGeneralBuffer_ramD();
|
||||||
|
|
||||||
|
short decSize = decData(data, (short) (dataOffset + AppConfig.HEADER_SIZE + 1),
|
||||||
|
AppConfig.AES_BLOCK_SIZE, buffer, (short) 0);
|
||||||
|
if (decSize != AppConfig.AES_BLOCK_SIZE) {
|
||||||
|
return AppConfig.FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean bInit = (resetFlag == AppConfig.SESSION_ENABLE);
|
||||||
|
short gkeySize = decUpdate_Gkey(buffer, (short) 0, AppConfig.AES_BLOCK_SIZE,
|
||||||
|
buffer, AppConfig.AES_BLOCK_SIZE, bInit);
|
||||||
|
if (gkeySize != AppConfig.AES_BLOCK_SIZE) {
|
||||||
|
return AppConfig.FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
short encSize = encData(buffer, AppConfig.AES_BLOCK_SIZE, AppConfig.AES_BLOCK_SIZE,
|
||||||
|
buffer, (short) 32);
|
||||||
|
if (encSize != AppConfig.AES_BLOCK_SIZE) {
|
||||||
|
return AppConfig.FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_ramD_outDataInfo[0] = 32;
|
||||||
|
m_ramD_outDataInfo[1] = AppConfig.AES_BLOCK_SIZE;
|
||||||
|
Util.arrayCopyNonAtomic(buffer, (short) 32, m_ramD_outData, (short) 32,
|
||||||
|
AppConfig.AES_BLOCK_SIZE);
|
||||||
|
return AppConfig.SUCCESS;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
package sega.a7;
|
||||||
|
|
||||||
|
import javacard.framework.Util;
|
||||||
|
import javacard.security.MessageDigest;
|
||||||
|
|
||||||
|
public class GkeyEnc extends AuthCommand {
|
||||||
|
|
||||||
|
public GkeyEnc(short cmd, AppData data, MakePacket packet, MessageDigest sha1,
|
||||||
|
HmacSha1 hmacSha1, Crypt crypt) {
|
||||||
|
super(cmd, (short) 63, (short) 62, AppConfig.LEVEL_AUTH,
|
||||||
|
data, packet, sha1, hmacSha1, crypt);
|
||||||
|
}
|
||||||
|
|
||||||
|
public short core(byte[] data, short dataOffset, short dataLength) {
|
||||||
|
byte resetFlag = data[(short) (dataOffset + AppConfig.HEADER_SIZE)];
|
||||||
|
byte[] buffer = m_ee_data.getGeneralBuffer_ramD();
|
||||||
|
|
||||||
|
short decSize = decData(data, (short) (dataOffset + AppConfig.HEADER_SIZE + 1),
|
||||||
|
AppConfig.AES_BLOCK_SIZE, buffer, (short) 0);
|
||||||
|
if (decSize != AppConfig.AES_BLOCK_SIZE) {
|
||||||
|
return AppConfig.FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean bInit = (resetFlag == AppConfig.SESSION_ENABLE);
|
||||||
|
short gkeySize = encUpdate_GKey(buffer, (short) 0, AppConfig.AES_BLOCK_SIZE,
|
||||||
|
buffer, AppConfig.AES_BLOCK_SIZE, bInit);
|
||||||
|
if (gkeySize != AppConfig.AES_BLOCK_SIZE) {
|
||||||
|
return AppConfig.FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
short encSize = encData(buffer, AppConfig.AES_BLOCK_SIZE, AppConfig.AES_BLOCK_SIZE,
|
||||||
|
buffer, (short) 32);
|
||||||
|
if (encSize != AppConfig.AES_BLOCK_SIZE) {
|
||||||
|
return AppConfig.FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_ramD_outDataInfo[0] = 32;
|
||||||
|
m_ramD_outDataInfo[1] = AppConfig.AES_BLOCK_SIZE;
|
||||||
|
Util.arrayCopyNonAtomic(buffer, (short) 32, m_ramD_outData, (short) 32,
|
||||||
|
AppConfig.AES_BLOCK_SIZE);
|
||||||
|
return AppConfig.SUCCESS;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
package sega.a7;
|
||||||
|
|
||||||
|
import javacard.framework.Util;
|
||||||
|
import javacard.security.MessageDigest;
|
||||||
|
|
||||||
|
public class GkeyWrite extends AuthCommand {
|
||||||
|
|
||||||
|
public GkeyWrite(short cmd, AppData data, MakePacket packet, MessageDigest sha1,
|
||||||
|
HmacSha1 hmacSha1, Crypt crypt) {
|
||||||
|
super(cmd, (short) 78, (short) 46, AppConfig.LEVEL_AUTH,
|
||||||
|
data, packet, sha1, hmacSha1, crypt);
|
||||||
|
}
|
||||||
|
|
||||||
|
public short core(byte[] data, short dataOffset, short dataLength) {
|
||||||
|
if (m_ee_data.getSflag_ee()) {
|
||||||
|
return AppConfig.EEPWRITE_DISABLE;
|
||||||
|
}
|
||||||
|
byte[] buffer = m_ee_data.getGeneralBuffer_ramD();
|
||||||
|
short decSize = decData(data, (short) (dataOffset + AppConfig.HEADER_SIZE),
|
||||||
|
(short) (AppConfig.AES_KEY_SIZE + AppConfig.AES_IV_SIZE),
|
||||||
|
buffer, (short) 0);
|
||||||
|
if (decSize != (short) (AppConfig.AES_KEY_SIZE + AppConfig.AES_IV_SIZE)) {
|
||||||
|
return AppConfig.FAIL;
|
||||||
|
}
|
||||||
|
if (!changeGKey_ee(buffer, (short) 0, AppConfig.AES_KEY_SIZE,
|
||||||
|
buffer, AppConfig.AES_KEY_SIZE, AppConfig.AES_IV_SIZE)) {
|
||||||
|
return AppConfig.EEP_WRITEFAIL;
|
||||||
|
}
|
||||||
|
return AppConfig.SUCCESS;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
package sega.a7;
|
||||||
|
|
||||||
|
import javacard.security.MessageDigest;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Replaces the HMAC key. The success response is already protected with the
|
||||||
|
* new key, so the host must switch keys before verifying it.
|
||||||
|
*/
|
||||||
|
public class HmacChange extends AuthCommand {
|
||||||
|
|
||||||
|
public HmacChange(short cmd, AppData data, MakePacket packet, MessageDigest sha1,
|
||||||
|
HmacSha1 hmacSha1, Crypt crypt) {
|
||||||
|
super(cmd, (short) 78, (short) 46, AppConfig.LEVEL_AUTH,
|
||||||
|
data, packet, sha1, hmacSha1, crypt);
|
||||||
|
}
|
||||||
|
|
||||||
|
public short core(byte[] data, short dataOffset, short dataLength) {
|
||||||
|
byte[] buffer = m_ee_data.getGeneralBuffer_ramD();
|
||||||
|
short decSize = decData(data, (short) (dataOffset + AppConfig.HEADER_SIZE),
|
||||||
|
(short) (AppConfig.HMAC_SIZE + 12), buffer, (short) 0);
|
||||||
|
if (decSize < AppConfig.HMAC_SIZE) {
|
||||||
|
return AppConfig.FAIL;
|
||||||
|
}
|
||||||
|
if (!m_ee_data.setKh_ramD(buffer, (short) 0, AppConfig.HMAC_SIZE)) {
|
||||||
|
return AppConfig.FAIL;
|
||||||
|
}
|
||||||
|
return AppConfig.SUCCESS;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,95 @@
|
|||||||
|
package sega.a7;
|
||||||
|
|
||||||
|
import javacard.framework.JCSystem;
|
||||||
|
import javacard.framework.Util;
|
||||||
|
import javacard.security.MessageDigest;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* HMAC-SHA1 (RFC 2104). Keys longer than the 64-byte block are hashed first.
|
||||||
|
*/
|
||||||
|
public class HmacSha1 {
|
||||||
|
|
||||||
|
private static final short BLOCK_SIZE = 64;
|
||||||
|
private static final byte IPAD = 0x36;
|
||||||
|
private static final byte OPAD = 0x5C;
|
||||||
|
|
||||||
|
private final byte[] m_ramD_key;
|
||||||
|
private final byte[] m_ramD_padBuffer;
|
||||||
|
private final MessageDigest m_ee_sha1;
|
||||||
|
|
||||||
|
public HmacSha1() {
|
||||||
|
m_ee_sha1 = MessageDigest.getInstance(MessageDigest.ALG_SHA, false);
|
||||||
|
m_ramD_key = JCSystem.makeTransientByteArray(BLOCK_SIZE, JCSystem.CLEAR_ON_RESET);
|
||||||
|
m_ramD_padBuffer = JCSystem.makeTransientByteArray(BLOCK_SIZE, JCSystem.CLEAR_ON_RESET);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean init(byte[] key, short offset, short length) {
|
||||||
|
if (key == null || length <= 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (offset < 0 || offset > key.length || length > (short) (key.length - offset)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
short effectiveLength;
|
||||||
|
if (length > BLOCK_SIZE) {
|
||||||
|
m_ee_sha1.reset();
|
||||||
|
m_ee_sha1.doFinal(key, offset, length, m_ramD_key, (short) 0);
|
||||||
|
effectiveLength = AppConfig.SHA1_SIZE;
|
||||||
|
} else {
|
||||||
|
Util.arrayCopyNonAtomic(key, offset, m_ramD_key, (short) 0, length);
|
||||||
|
effectiveLength = length;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (effectiveLength < BLOCK_SIZE) {
|
||||||
|
Util.arrayFillNonAtomic(m_ramD_key, effectiveLength,
|
||||||
|
(short) (BLOCK_SIZE - effectiveLength), (byte) 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (short i = 0; i < BLOCK_SIZE; i++) {
|
||||||
|
m_ramD_padBuffer[i] = (byte) (m_ramD_key[i] ^ IPAD);
|
||||||
|
}
|
||||||
|
|
||||||
|
m_ee_sha1.reset();
|
||||||
|
m_ee_sha1.update(m_ramD_padBuffer, (short) 0, BLOCK_SIZE);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean update(byte[] inBuff, short inOffset, short inLength) {
|
||||||
|
if (inBuff == null || inLength < 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (inOffset < 0 || inOffset > inBuff.length
|
||||||
|
|| inLength > (short) (inBuff.length - inOffset)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (inLength != 0) {
|
||||||
|
m_ee_sha1.update(inBuff, inOffset, inLength);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean sign(byte[] inBuff, short inOffset, short inLength,
|
||||||
|
byte[] sigBuff, short sigOffset) {
|
||||||
|
if (inBuff == null || inLength < 0 || sigBuff == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (inOffset < 0 || inOffset > inBuff.length
|
||||||
|
|| inLength > (short) (inBuff.length - inOffset)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (sigOffset < 0 || sigOffset > sigBuff.length
|
||||||
|
|| AppConfig.SHA1_SIZE > (short) (sigBuff.length - sigOffset)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_ee_sha1.doFinal(inBuff, inOffset, inLength, sigBuff, sigOffset);
|
||||||
|
|
||||||
|
for (short i = 0; i < BLOCK_SIZE; i++) {
|
||||||
|
m_ramD_padBuffer[i] = (byte) (m_ramD_key[i] ^ OPAD);
|
||||||
|
}
|
||||||
|
m_ee_sha1.update(m_ramD_padBuffer, (short) 0, BLOCK_SIZE);
|
||||||
|
m_ee_sha1.doFinal(sigBuff, sigOffset, AppConfig.SHA1_SIZE, sigBuff, sigOffset);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
package sega.a7;
|
||||||
|
|
||||||
|
import javacard.security.MessageDigest;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Replaces the connection AES key and IV. The response is not encrypted, but
|
||||||
|
* all following commands use the new connection key.
|
||||||
|
*/
|
||||||
|
public class KeyChange extends AuthCommand {
|
||||||
|
|
||||||
|
public KeyChange(short cmd, AppData data, MakePacket packet, MessageDigest sha1,
|
||||||
|
HmacSha1 hmacSha1, Crypt crypt) {
|
||||||
|
super(cmd, (short) 78, (short) 46, AppConfig.LEVEL_AUTH,
|
||||||
|
data, packet, sha1, hmacSha1, crypt);
|
||||||
|
}
|
||||||
|
|
||||||
|
public short core(byte[] data, short dataOffset, short dataLength) {
|
||||||
|
byte[] buffer = m_ee_data.getGeneralBuffer_ramD();
|
||||||
|
short decSize = decData(data, (short) (dataOffset + AppConfig.HEADER_SIZE),
|
||||||
|
(short) (AppConfig.AES_KEY_SIZE + AppConfig.AES_IV_SIZE),
|
||||||
|
buffer, (short) 0);
|
||||||
|
if (decSize != (short) (AppConfig.AES_KEY_SIZE + AppConfig.AES_IV_SIZE)) {
|
||||||
|
return AppConfig.FAIL;
|
||||||
|
}
|
||||||
|
if (!changeConnectionKey_ee(buffer, (short) 0, AppConfig.AES_KEY_SIZE,
|
||||||
|
buffer, AppConfig.AES_KEY_SIZE, AppConfig.AES_IV_SIZE)) {
|
||||||
|
return AppConfig.FAIL;
|
||||||
|
}
|
||||||
|
return AppConfig.SUCCESS;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
package sega.a7;
|
||||||
|
|
||||||
|
import javacard.framework.Util;
|
||||||
|
import javacard.security.MessageDigest;
|
||||||
|
|
||||||
|
public class KeychipInfoRead extends AuthCommand {
|
||||||
|
|
||||||
|
public KeychipInfoRead(short cmd, AppData data, MakePacket packet, MessageDigest sha1,
|
||||||
|
HmacSha1 hmacSha1, Crypt crypt) {
|
||||||
|
super(cmd, (short) 46, (short) 142, AppConfig.LEVEL_AUTH,
|
||||||
|
data, packet, sha1, hmacSha1, crypt);
|
||||||
|
}
|
||||||
|
|
||||||
|
public short core(byte[] data, short dataOffset, short dataLength) {
|
||||||
|
byte[] info = m_ee_data.getKeychipInfo_ee();
|
||||||
|
byte[] buffer = m_ee_data.getGeneralBuffer_ramD();
|
||||||
|
short encSize = encData(info, (short) 0, (short) info.length,
|
||||||
|
buffer, (short) 0);
|
||||||
|
if (encSize != (short) info.length) {
|
||||||
|
return AppConfig.FAIL;
|
||||||
|
}
|
||||||
|
m_ramD_outDataInfo[0] = 0;
|
||||||
|
m_ramD_outDataInfo[1] = encSize;
|
||||||
|
Util.arrayCopyNonAtomic(buffer, (short) 0, m_ramD_outData, (short) 0, encSize);
|
||||||
|
return AppConfig.SUCCESS;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
package sega.a7;
|
||||||
|
|
||||||
|
import javacard.framework.Util;
|
||||||
|
import javacard.security.MessageDigest;
|
||||||
|
|
||||||
|
public class KeychipInfoWrite extends AuthCommand {
|
||||||
|
|
||||||
|
public KeychipInfoWrite(short cmd, AppData data, MakePacket packet, MessageDigest sha1,
|
||||||
|
HmacSha1 hmacSha1, Crypt crypt) {
|
||||||
|
super(cmd, (short) 142, (short) 46, AppConfig.LEVEL_AUTH,
|
||||||
|
data, packet, sha1, hmacSha1, crypt);
|
||||||
|
}
|
||||||
|
|
||||||
|
public short core(byte[] data, short dataOffset, short dataLength) {
|
||||||
|
if (m_ee_data.getSflag_ee()) {
|
||||||
|
return AppConfig.EEPWRITE_DISABLE;
|
||||||
|
}
|
||||||
|
byte[] buffer = m_ee_data.getGeneralBuffer_ramD();
|
||||||
|
short decSize = decData(data, (short) (dataOffset + AppConfig.HEADER_SIZE),
|
||||||
|
AppConfig.KEYCHIP_INFO_SIZE, buffer, (short) 0);
|
||||||
|
if (decSize != AppConfig.KEYCHIP_INFO_SIZE) {
|
||||||
|
return AppConfig.FAIL;
|
||||||
|
}
|
||||||
|
if (!m_ee_data.setKeychipInfo_ee(buffer, (short) 0,
|
||||||
|
AppConfig.KEYCHIP_INFO_SIZE)) {
|
||||||
|
return AppConfig.EEP_WRITEFAIL;
|
||||||
|
}
|
||||||
|
return AppConfig.SUCCESS;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
package sega.a7;
|
||||||
|
|
||||||
|
import javacard.security.MessageDigest;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Recovers from the persistent block flag until the next card reset.
|
||||||
|
* The block bit itself is intentionally preserved.
|
||||||
|
*/
|
||||||
|
public class LvEnable extends Command {
|
||||||
|
|
||||||
|
public LvEnable(short cmd, AppData data, MakePacket packet, MessageDigest sha1) {
|
||||||
|
super(cmd, (short) 26, (short) 26, data, packet, sha1);
|
||||||
|
}
|
||||||
|
|
||||||
|
public short core(byte[] data, short dataOffset, short dataLength) {
|
||||||
|
if (m_ee_data.getLevel_ramD() != AppConfig.LEVEL_BLOCKED) {
|
||||||
|
return AppConfig.LV_ERROR;
|
||||||
|
}
|
||||||
|
if (m_ee_data.getSflag_ee()) {
|
||||||
|
return AppConfig.EEPWRITE_DISABLE;
|
||||||
|
}
|
||||||
|
m_ee_data.setLevel_ramD(AppConfig.LEVEL_INITIAL);
|
||||||
|
return AppConfig.SUCCESS;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
package sega.a7;
|
||||||
|
|
||||||
|
import javacard.framework.Util;
|
||||||
|
import javacard.security.MessageDigest;
|
||||||
|
|
||||||
|
public class LvGet extends Command {
|
||||||
|
|
||||||
|
public LvGet(short cmd, AppData data, MakePacket packet, MessageDigest sha1) {
|
||||||
|
super(cmd, (short) 26, (short) 27, data, packet, sha1);
|
||||||
|
}
|
||||||
|
|
||||||
|
public short core(byte[] data, short dataOffset, short dataLength) {
|
||||||
|
m_ramD_outData[0] = m_ee_data.getLevel_ramD();
|
||||||
|
m_ramD_outDataInfo[0] = 0;
|
||||||
|
m_ramD_outDataInfo[1] = 1;
|
||||||
|
return AppConfig.SUCCESS;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,115 @@
|
|||||||
|
package sega.a7;
|
||||||
|
|
||||||
|
import javacard.framework.JCSystem;
|
||||||
|
import javacard.framework.Util;
|
||||||
|
import javacard.security.MessageDigest;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Serialises responses. The SHA-1/HMAC input includes the logical command
|
||||||
|
* number even though the wire header carries the result code at offset 4.
|
||||||
|
*/
|
||||||
|
public class MakePacket {
|
||||||
|
|
||||||
|
private final byte[] m_ramD_buffer;
|
||||||
|
|
||||||
|
public MakePacket() {
|
||||||
|
m_ramD_buffer = JCSystem.makeTransientByteArray(AppConfig.SHA1_SIZE,
|
||||||
|
JCSystem.CLEAR_ON_RESET);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean setData(byte[] kh, short khOffset, short khLength,
|
||||||
|
short tag, short paramSize, short result, short cmd,
|
||||||
|
byte[] data, short dataOffset, short dataLength,
|
||||||
|
byte[] nonceEvent, short eventOffset, short eventLength,
|
||||||
|
byte[] nonceOdd, short oddOffset, short oddLength,
|
||||||
|
byte[] packet, short packetOffset, short packetLength,
|
||||||
|
MessageDigest sha1, HmacSha1 hmacSha1) {
|
||||||
|
|
||||||
|
if (packet == null || sha1 == null || packetOffset < 0 || packetLength < 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (packetOffset > packet.length || packetLength > (short) (packet.length - packetOffset)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean normal = (tag == AppConfig.TAG_RSP_COMMAND);
|
||||||
|
boolean auth = (tag == AppConfig.TAG_RSP_AUTH_COMMAND);
|
||||||
|
boolean rsaAuth = (tag == AppConfig.TAG_RSP_RSA_COMMAND);
|
||||||
|
if (!normal && !auth && !rsaAuth) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!normal) {
|
||||||
|
if (kh == null || nonceEvent == null || nonceOdd == null || hmacSha1 == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (khOffset < 0 || khLength <= 0 || khOffset > kh.length
|
||||||
|
|| khLength > (short) (kh.length - khOffset)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (eventOffset < 0 || eventLength <= 0 || eventOffset > nonceEvent.length
|
||||||
|
|| eventLength > (short) (nonceEvent.length - eventOffset)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (oddOffset < 0 || oddLength <= 0 || oddOffset > nonceOdd.length
|
||||||
|
|| oddLength > (short) (nonceOdd.length - oddOffset)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
short payloadLength = 0;
|
||||||
|
if (data != null) {
|
||||||
|
if (dataOffset < 0 || dataLength < 0 || dataOffset > data.length
|
||||||
|
|| dataLength > (short) (data.length - dataOffset)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
payloadLength = dataLength;
|
||||||
|
}
|
||||||
|
|
||||||
|
short footerLength = normal ? AppConfig.SHA1_SIZE : AppConfig.AUTH_FOOTER_SIZE;
|
||||||
|
short expectedSize = (short) (AppConfig.HEADER_SIZE + payloadLength + footerLength);
|
||||||
|
if (paramSize != expectedSize || paramSize > packetLength) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
short offset = packetOffset;
|
||||||
|
|
||||||
|
sha1.reset();
|
||||||
|
Util.setShort(packet, offset, tag);
|
||||||
|
offset += 2;
|
||||||
|
Util.setShort(packet, offset, paramSize);
|
||||||
|
offset += 2;
|
||||||
|
Util.setShort(packet, offset, result);
|
||||||
|
offset += 2;
|
||||||
|
|
||||||
|
Util.setShort(m_ramD_buffer, (short) 0, cmd);
|
||||||
|
sha1.update(packet, packetOffset, AppConfig.HEADER_SIZE);
|
||||||
|
|
||||||
|
if (data == null) {
|
||||||
|
sha1.doFinal(m_ramD_buffer, (short) 0, (short) 2, packet, offset);
|
||||||
|
} else {
|
||||||
|
sha1.update(m_ramD_buffer, (short) 0, (short) 2);
|
||||||
|
if (dataLength != 0) {
|
||||||
|
Util.arrayCopyNonAtomic(data, dataOffset, packet, offset, dataLength);
|
||||||
|
}
|
||||||
|
offset = (short) (offset + dataLength);
|
||||||
|
sha1.doFinal(data, dataOffset, dataLength, packet, offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (normal) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
Util.arrayCopyNonAtomic(packet, offset, m_ramD_buffer, (short) 0,
|
||||||
|
AppConfig.SHA1_SIZE);
|
||||||
|
Util.arrayCopyNonAtomic(nonceEvent, eventOffset, packet, offset, eventLength);
|
||||||
|
offset = (short) (offset + eventLength);
|
||||||
|
|
||||||
|
if (!hmacSha1.init(kh, khOffset, khLength)
|
||||||
|
|| !hmacSha1.update(m_ramD_buffer, (short) 0, AppConfig.SHA1_SIZE)
|
||||||
|
|| !hmacSha1.update(nonceEvent, eventOffset, eventLength)
|
||||||
|
|| !hmacSha1.sign(nonceOdd, oddOffset, oddLength, packet, offset)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
package sega.a7;
|
||||||
|
|
||||||
|
import javacard.framework.Util;
|
||||||
|
import javacard.security.MessageDigest;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fallback for unknown ordinals. Mirrors the original behaviour: frames shorter
|
||||||
|
* than the six-byte header return BAD_PARAMETER, everything else BAD_ORDINAL.
|
||||||
|
*/
|
||||||
|
public class NoCommand extends Command {
|
||||||
|
|
||||||
|
public NoCommand(short cmd, AppData data, MakePacket packet, MessageDigest sha1) {
|
||||||
|
super(cmd, (short) 0, AppConfig.ERROR_PACKET_SIZE, data, packet, sha1);
|
||||||
|
}
|
||||||
|
|
||||||
|
public short execute(byte[] data, short dataOffset, short dataLength,
|
||||||
|
byte[] dst, short dstOffset, short dstLength) {
|
||||||
|
short cmd = -1;
|
||||||
|
if (data != null && dataOffset >= 0 && dataLength >= AppConfig.HEADER_SIZE
|
||||||
|
&& dataOffset <= (short) (data.length - AppConfig.HEADER_SIZE)) {
|
||||||
|
cmd = Util.getShort(data,
|
||||||
|
(short) (dataOffset + AppConfig.CMD_OFFSET));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dataLength < AppConfig.HEADER_SIZE) {
|
||||||
|
return makeErrorPacket(AppConfig.BAD_PARAMETER, cmd,
|
||||||
|
dst, dstOffset, dstLength);
|
||||||
|
}
|
||||||
|
return makeErrorPacket(AppConfig.BAD_ORDINAL, cmd,
|
||||||
|
dst, dstOffset, dstLength);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
package sega.a7;
|
||||||
|
|
||||||
|
import javacard.security.MessageDigest;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds one byte to the persistent 32-bit play counter.
|
||||||
|
* Payload: increment value (1 byte).
|
||||||
|
*/
|
||||||
|
public class PlayCountIncrement extends AuthCommand {
|
||||||
|
|
||||||
|
public PlayCountIncrement(short cmd, AppData data, MakePacket packet,
|
||||||
|
MessageDigest sha1, HmacSha1 hmacSha1, Crypt crypt) {
|
||||||
|
super(cmd, (short) 47, (short) 46, AppConfig.LEVEL_AUTH,
|
||||||
|
data, packet, sha1, hmacSha1, crypt);
|
||||||
|
}
|
||||||
|
|
||||||
|
public short core(byte[] data, short dataOffset, short dataLength) {
|
||||||
|
if (dataLength < (short) (AppConfig.HEADER_SIZE + 1)) {
|
||||||
|
return AppConfig.BAD_DATASIZE;
|
||||||
|
}
|
||||||
|
m_ee_data.addValuePlayCounter_ee(
|
||||||
|
data[(short) (dataOffset + AppConfig.HEADER_SIZE)]);
|
||||||
|
return AppConfig.SUCCESS;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
package sega.a7;
|
||||||
|
|
||||||
|
import javacard.framework.Util;
|
||||||
|
import javacard.security.MessageDigest;
|
||||||
|
import javacard.security.RandomData;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the persistent play counter inside an encrypted 16-byte block:
|
||||||
|
* random(16) with the first four bytes replaced by the counter.
|
||||||
|
*/
|
||||||
|
public class PlayCountRead extends AuthCommand {
|
||||||
|
|
||||||
|
private final RandomData m_ee_rand;
|
||||||
|
|
||||||
|
public PlayCountRead(short cmd, AppData data, MakePacket packet, MessageDigest sha1,
|
||||||
|
HmacSha1 hmacSha1, Crypt crypt, RandomData rand) {
|
||||||
|
super(cmd, (short) 46, (short) 62, AppConfig.LEVEL_AUTH,
|
||||||
|
data, packet, sha1, hmacSha1, crypt);
|
||||||
|
m_ee_rand = rand;
|
||||||
|
}
|
||||||
|
|
||||||
|
public short core(byte[] data, short dataOffset, short dataLength) {
|
||||||
|
byte[] buffer = m_ee_data.getGeneralBuffer_ramD();
|
||||||
|
if (buffer.length < 32) {
|
||||||
|
return AppConfig.FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_ee_rand.generateData(buffer, (short) 0, AppConfig.AES_BLOCK_SIZE);
|
||||||
|
if (!m_ee_data.getPlayCounter_ee(buffer, (short) 0,
|
||||||
|
AppConfig.PLAY_COUNT_SIZE)) {
|
||||||
|
return AppConfig.FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
short encSize = encData(buffer, (short) 0, AppConfig.AES_BLOCK_SIZE,
|
||||||
|
buffer, AppConfig.AES_BLOCK_SIZE);
|
||||||
|
if (encSize != AppConfig.AES_BLOCK_SIZE) {
|
||||||
|
return AppConfig.FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_ramD_outDataInfo[0] = AppConfig.AES_BLOCK_SIZE;
|
||||||
|
m_ramD_outDataInfo[1] = encSize;
|
||||||
|
Util.arrayCopyNonAtomic(buffer, AppConfig.AES_BLOCK_SIZE,
|
||||||
|
m_ramD_outData, AppConfig.AES_BLOCK_SIZE, encSize);
|
||||||
|
return AppConfig.SUCCESS;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
package sega.a7;
|
||||||
|
|
||||||
|
import javacard.security.MessageDigest;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Replaces the RSA private exponent. Both the modulus and the exponent must be
|
||||||
|
* written before the new key becomes active; the update is staged in RAM.
|
||||||
|
*/
|
||||||
|
public class PrikeyExponentWrite extends AuthCommand {
|
||||||
|
|
||||||
|
public PrikeyExponentWrite(short cmd, AppData data, MakePacket packet,
|
||||||
|
MessageDigest sha1, HmacSha1 hmacSha1, Crypt crypt) {
|
||||||
|
super(cmd, (short) 302, (short) 46, AppConfig.LEVEL_AUTH,
|
||||||
|
data, packet, sha1, hmacSha1, crypt);
|
||||||
|
}
|
||||||
|
|
||||||
|
public short core(byte[] data, short dataOffset, short dataLength) {
|
||||||
|
if (m_ee_data.getSflag_ee()) {
|
||||||
|
return AppConfig.EEPWRITE_DISABLE;
|
||||||
|
}
|
||||||
|
|
||||||
|
byte[] buffer = m_ee_data.getGeneralBuffer_ramD();
|
||||||
|
if (buffer.length < AppConfig.RSA_KEY_SIZE) {
|
||||||
|
return AppConfig.FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
short decSize = decData(data,
|
||||||
|
(short) (dataOffset + AppConfig.HEADER_SIZE),
|
||||||
|
AppConfig.RSA_KEY_SIZE, buffer, (short) 0);
|
||||||
|
if (decSize < AppConfig.RSA_KEY_SIZE) {
|
||||||
|
return AppConfig.FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!m_ee_data.setRsaPriKeyExponent_ee(buffer, (short) 0,
|
||||||
|
AppConfig.RSA_KEY_SIZE)) {
|
||||||
|
return AppConfig.EEP_WRITEFAIL;
|
||||||
|
}
|
||||||
|
m_ee_data.activeRsaExponentUpdate_ramD();
|
||||||
|
|
||||||
|
if (m_ee_data.getRsaModulusUpdate_ramD()) {
|
||||||
|
byte[] modulus = m_ee_data.getRsaPriModulus_ee();
|
||||||
|
byte[] exponent = m_ee_data.getRsaPriExponent_ee();
|
||||||
|
if (!changeRsaKey_ee(modulus, (short) 0, (short) modulus.length,
|
||||||
|
exponent, (short) 0, (short) exponent.length)) {
|
||||||
|
return AppConfig.FAIL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return AppConfig.SUCCESS;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
package sega.a7;
|
||||||
|
|
||||||
|
import javacard.security.MessageDigest;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Replaces the RSA private modulus. Both the modulus and the exponent must be
|
||||||
|
* written before the new key becomes active; the update is staged in RAM.
|
||||||
|
*/
|
||||||
|
public class PrikeyModulusWrite extends AuthCommand {
|
||||||
|
|
||||||
|
public PrikeyModulusWrite(short cmd, AppData data, MakePacket packet,
|
||||||
|
MessageDigest sha1, HmacSha1 hmacSha1, Crypt crypt) {
|
||||||
|
super(cmd, (short) 302, (short) 46, AppConfig.LEVEL_AUTH,
|
||||||
|
data, packet, sha1, hmacSha1, crypt);
|
||||||
|
}
|
||||||
|
|
||||||
|
public short core(byte[] data, short dataOffset, short dataLength) {
|
||||||
|
if (m_ee_data.getSflag_ee()) {
|
||||||
|
return AppConfig.EEPWRITE_DISABLE;
|
||||||
|
}
|
||||||
|
|
||||||
|
byte[] buffer = m_ee_data.getGeneralBuffer_ramD();
|
||||||
|
if (buffer.length < AppConfig.RSA_KEY_SIZE) {
|
||||||
|
return AppConfig.FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
short decSize = decData(data,
|
||||||
|
(short) (dataOffset + AppConfig.HEADER_SIZE),
|
||||||
|
AppConfig.RSA_KEY_SIZE, buffer, (short) 0);
|
||||||
|
if (decSize < AppConfig.RSA_KEY_SIZE) {
|
||||||
|
return AppConfig.FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!m_ee_data.setRsaPriKeyModulus_ee(buffer, (short) 0,
|
||||||
|
AppConfig.RSA_KEY_SIZE)) {
|
||||||
|
return AppConfig.EEP_WRITEFAIL;
|
||||||
|
}
|
||||||
|
m_ee_data.activeRsaModulusUpdate_ramD();
|
||||||
|
|
||||||
|
if (m_ee_data.getRsaExponentUpdate_ramD()) {
|
||||||
|
byte[] modulus = m_ee_data.getRsaPriModulus_ee();
|
||||||
|
byte[] exponent = m_ee_data.getRsaPriExponent_ee();
|
||||||
|
if (!changeRsaKey_ee(modulus, (short) 0, (short) modulus.length,
|
||||||
|
exponent, (short) 0, (short) exponent.length)) {
|
||||||
|
return AppConfig.FAIL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return AppConfig.SUCCESS;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
package sega.a7;
|
||||||
|
|
||||||
|
import javacard.framework.Util;
|
||||||
|
import javacard.security.MessageDigest;
|
||||||
|
import javacard.security.RandomData;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns 16 fresh random bytes, encrypted with the connection key.
|
||||||
|
* Payload: encrypted random block (16 bytes).
|
||||||
|
*/
|
||||||
|
public class RanGet extends AuthCommand {
|
||||||
|
|
||||||
|
private final RandomData m_ee_rand;
|
||||||
|
|
||||||
|
public RanGet(short cmd, AppData data, MakePacket packet, MessageDigest sha1,
|
||||||
|
HmacSha1 hmacSha1, Crypt crypt, RandomData rand) {
|
||||||
|
super(cmd, (short) 46, (short) 62, AppConfig.LEVEL_AUTH,
|
||||||
|
data, packet, sha1, hmacSha1, crypt);
|
||||||
|
m_ee_rand = rand;
|
||||||
|
}
|
||||||
|
|
||||||
|
public short core(byte[] data, short dataOffset, short dataLength) {
|
||||||
|
byte[] buffer = m_ee_data.getGeneralBuffer_ramD();
|
||||||
|
if (buffer.length < 32) {
|
||||||
|
return AppConfig.FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_ee_rand.generateData(buffer, (short) 0, AppConfig.AES_BLOCK_SIZE);
|
||||||
|
short encSize = encData(buffer, (short) 0, AppConfig.AES_BLOCK_SIZE,
|
||||||
|
buffer, AppConfig.AES_BLOCK_SIZE);
|
||||||
|
if (encSize != AppConfig.AES_BLOCK_SIZE) {
|
||||||
|
return AppConfig.FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_ramD_outDataInfo[0] = AppConfig.AES_BLOCK_SIZE;
|
||||||
|
m_ramD_outDataInfo[1] = encSize;
|
||||||
|
Util.arrayCopyNonAtomic(buffer, AppConfig.AES_BLOCK_SIZE,
|
||||||
|
m_ramD_outData, AppConfig.AES_BLOCK_SIZE, encSize);
|
||||||
|
return AppConfig.SUCCESS;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
package sega.a7;
|
||||||
|
|
||||||
|
import javacard.security.MessageDigest;
|
||||||
|
|
||||||
|
public class SessionClose extends Command {
|
||||||
|
|
||||||
|
public SessionClose(short cmd, AppData data, MakePacket packet, MessageDigest sha1) {
|
||||||
|
super(cmd, (short) 26, (short) 26, data, packet, sha1);
|
||||||
|
}
|
||||||
|
|
||||||
|
public short core(byte[] data, short dataOffset, short dataLength) {
|
||||||
|
m_ee_data.setSession_ramD(AppConfig.SESSION_DISABLE);
|
||||||
|
return AppConfig.SUCCESS;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
package sega.a7;
|
||||||
|
|
||||||
|
import javacard.framework.Util;
|
||||||
|
import javacard.security.MessageDigest;
|
||||||
|
|
||||||
|
public class SessionOpen extends Command {
|
||||||
|
|
||||||
|
public SessionOpen(short cmd, AppData data, MakePacket packet, MessageDigest sha1) {
|
||||||
|
super(cmd, (short) 26, (short) 46, data, packet, sha1);
|
||||||
|
}
|
||||||
|
|
||||||
|
public short core(byte[] data, short dataOffset, short dataLength) {
|
||||||
|
byte[] nonce = m_ee_data.getNewNonceEvent_ramD();
|
||||||
|
if (!m_ee_data.setLastNonceEvent_ramD(nonce, (short) 0,
|
||||||
|
(short) nonce.length)) {
|
||||||
|
return AppConfig.FAIL;
|
||||||
|
}
|
||||||
|
m_ee_data.setSession_ramD(AppConfig.SESSION_ENABLE);
|
||||||
|
m_ramD_outDataInfo[0] = 0;
|
||||||
|
m_ramD_outDataInfo[1] = AppConfig.NONCE_SIZE;
|
||||||
|
Util.arrayCopyNonAtomic(nonce, (short) 0, m_ramD_outData, (short) 0,
|
||||||
|
AppConfig.NONCE_SIZE);
|
||||||
|
return AppConfig.SUCCESS;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
package sega.a7;
|
||||||
|
|
||||||
|
import javacard.security.MessageDigest;
|
||||||
|
|
||||||
|
public class SflagWrite extends AuthCommand {
|
||||||
|
|
||||||
|
public SflagWrite(short cmd, AppData data, MakePacket packet, MessageDigest sha1,
|
||||||
|
HmacSha1 hmacSha1, Crypt crypt) {
|
||||||
|
super(cmd, (short) 46, (short) 46, AppConfig.LEVEL_AUTH,
|
||||||
|
data, packet, sha1, hmacSha1, crypt);
|
||||||
|
}
|
||||||
|
|
||||||
|
public short core(byte[] data, short dataOffset, short dataLength) {
|
||||||
|
if (m_ee_data.getSflag_ee()) {
|
||||||
|
return AppConfig.EEPWRITE_DISABLE;
|
||||||
|
}
|
||||||
|
m_ee_data.activeSflag();
|
||||||
|
return AppConfig.SUCCESS;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,58 @@
|
|||||||
|
package sega.a7;
|
||||||
|
|
||||||
|
import javacard.framework.Util;
|
||||||
|
import javacard.security.MessageDigest;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reads up to 192 bytes from the persistent storage.
|
||||||
|
*
|
||||||
|
* <p>Payload: storageOffset(2) | readSize(2). The read size is rounded up to
|
||||||
|
* an AES block and the resulting ciphertext is returned as the payload.</p>
|
||||||
|
*/
|
||||||
|
public class StorageRead extends AuthCommand {
|
||||||
|
|
||||||
|
public StorageRead(short cmd, AppData data, MakePacket packet, MessageDigest sha1,
|
||||||
|
HmacSha1 hmacSha1, Crypt crypt) {
|
||||||
|
super(cmd, (short) 50, (short) 46, AppConfig.LEVEL_AUTH,
|
||||||
|
data, packet, sha1, hmacSha1, crypt);
|
||||||
|
}
|
||||||
|
|
||||||
|
public short core(byte[] data, short dataOffset, short dataLength) {
|
||||||
|
short storageOffset = Util.getShort(data,
|
||||||
|
(short) (dataOffset + AppConfig.HEADER_SIZE));
|
||||||
|
short readSize = Util.getShort(data,
|
||||||
|
(short) (dataOffset + AppConfig.HEADER_SIZE + 2));
|
||||||
|
|
||||||
|
if (readSize <= 0 || readSize > AppConfig.STORAGE_MAX_READ
|
||||||
|
|| storageOffset < 0) {
|
||||||
|
return AppConfig.BAD_PARAMETER;
|
||||||
|
}
|
||||||
|
|
||||||
|
short remainder = (short) (readSize % AppConfig.AES_BLOCK_SIZE);
|
||||||
|
if (remainder != 0) {
|
||||||
|
readSize = (short) (readSize
|
||||||
|
+ (AppConfig.AES_BLOCK_SIZE - remainder));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (storageOffset > (short) (AppConfig.STORAGE_SIZE - readSize)) {
|
||||||
|
return AppConfig.BAD_PARAMETER;
|
||||||
|
}
|
||||||
|
|
||||||
|
byte[] buffer = m_ee_data.getGeneralBuffer_ramD();
|
||||||
|
if (buffer.length < readSize) {
|
||||||
|
return AppConfig.BAD_PARAMETER;
|
||||||
|
}
|
||||||
|
|
||||||
|
short encSize = encData(m_ee_data.getStorageData_ee(), storageOffset,
|
||||||
|
readSize, buffer, (short) 0);
|
||||||
|
if (encSize < readSize) {
|
||||||
|
return AppConfig.FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_ramD_outDataInfo[0] = 0;
|
||||||
|
m_ramD_outDataInfo[1] = encSize;
|
||||||
|
Util.arrayCopyNonAtomic(buffer, (short) 0, m_ramD_outData,
|
||||||
|
(short) 0, encSize);
|
||||||
|
return AppConfig.SUCCESS;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
package sega.a7;
|
||||||
|
|
||||||
|
import javacard.framework.Util;
|
||||||
|
import javacard.security.MessageDigest;
|
||||||
|
import javacard.security.RandomData;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the storage capacity (10240) inside an encrypted 16-byte block:
|
||||||
|
* random(16) with the first two bytes replaced by the big-endian size.
|
||||||
|
*/
|
||||||
|
public class StorageSizeGet extends AuthCommand {
|
||||||
|
|
||||||
|
private final RandomData m_ee_rand;
|
||||||
|
|
||||||
|
public StorageSizeGet(short cmd, AppData data, MakePacket packet,
|
||||||
|
MessageDigest sha1, HmacSha1 hmacSha1, Crypt crypt,
|
||||||
|
RandomData rand) {
|
||||||
|
super(cmd, (short) 46, (short) 62, AppConfig.LEVEL_AUTH,
|
||||||
|
data, packet, sha1, hmacSha1, crypt);
|
||||||
|
m_ee_rand = rand;
|
||||||
|
}
|
||||||
|
|
||||||
|
public short core(byte[] data, short dataOffset, short dataLength) {
|
||||||
|
byte[] buffer = m_ee_data.getGeneralBuffer_ramD();
|
||||||
|
if (buffer.length < 32) {
|
||||||
|
return AppConfig.FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_ee_rand.generateData(buffer, (short) 0, AppConfig.AES_BLOCK_SIZE);
|
||||||
|
Util.setShort(buffer, (short) 0, AppConfig.STORAGE_SIZE);
|
||||||
|
|
||||||
|
short encSize = encData(buffer, (short) 0, AppConfig.AES_BLOCK_SIZE,
|
||||||
|
buffer, AppConfig.AES_BLOCK_SIZE);
|
||||||
|
if (encSize != AppConfig.AES_BLOCK_SIZE) {
|
||||||
|
return AppConfig.FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_ramD_outDataInfo[0] = AppConfig.AES_BLOCK_SIZE;
|
||||||
|
m_ramD_outDataInfo[1] = encSize;
|
||||||
|
Util.arrayCopyNonAtomic(buffer, AppConfig.AES_BLOCK_SIZE,
|
||||||
|
m_ramD_outData, AppConfig.AES_BLOCK_SIZE, encSize);
|
||||||
|
return AppConfig.SUCCESS;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,59 @@
|
|||||||
|
package sega.a7;
|
||||||
|
|
||||||
|
import javacard.framework.Util;
|
||||||
|
import javacard.security.MessageDigest;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Writes up to 256 bytes into the persistent 10 KiB storage.
|
||||||
|
*
|
||||||
|
* <p>Payload: writeSize(2) | storageOffset(2) | AES-CBC ciphertext.
|
||||||
|
* The ciphertext length is {@code frameSize - 50}; it must be a positive
|
||||||
|
* multiple of 16 and the plaintext must cover writeSize bytes.</p>
|
||||||
|
*/
|
||||||
|
public class StorageWrite extends AuthCommand {
|
||||||
|
|
||||||
|
private static final short PAYLOAD_SIZE = 4;
|
||||||
|
|
||||||
|
public StorageWrite(short cmd, AppData data, MakePacket packet, MessageDigest sha1,
|
||||||
|
HmacSha1 hmacSha1, Crypt crypt) {
|
||||||
|
super(cmd, (short) 0, (short) 46, AppConfig.LEVEL_AUTH,
|
||||||
|
data, packet, sha1, hmacSha1, crypt);
|
||||||
|
}
|
||||||
|
|
||||||
|
public short core(byte[] data, short dataOffset, short dataLength) {
|
||||||
|
short encDataSize = (short) (dataLength
|
||||||
|
- AppConfig.HEADER_SIZE - AppConfig.AUTH_FOOTER_SIZE - PAYLOAD_SIZE);
|
||||||
|
if (encDataSize <= 0 || encDataSize > AppConfig.STORAGE_MAX_WRITE
|
||||||
|
|| (short) (encDataSize % AppConfig.AES_BLOCK_SIZE) != 0) {
|
||||||
|
return AppConfig.BAD_PARAMETER;
|
||||||
|
}
|
||||||
|
|
||||||
|
byte[] buffer = m_ee_data.getGeneralBuffer_ramD();
|
||||||
|
if (buffer.length < encDataSize) {
|
||||||
|
return AppConfig.BAD_PARAMETER;
|
||||||
|
}
|
||||||
|
|
||||||
|
short writeSize = Util.getShort(data,
|
||||||
|
(short) (dataOffset + AppConfig.HEADER_SIZE));
|
||||||
|
short storageOffset = Util.getShort(data,
|
||||||
|
(short) (dataOffset + AppConfig.HEADER_SIZE + 2));
|
||||||
|
|
||||||
|
if (writeSize <= 0 || storageOffset < 0
|
||||||
|
|| storageOffset > (short) (AppConfig.STORAGE_SIZE - writeSize)) {
|
||||||
|
return AppConfig.BAD_PARAMETER;
|
||||||
|
}
|
||||||
|
|
||||||
|
short decSize = decData(data,
|
||||||
|
(short) (dataOffset + AppConfig.HEADER_SIZE + PAYLOAD_SIZE),
|
||||||
|
encDataSize, buffer, (short) 0);
|
||||||
|
if (decSize < writeSize) {
|
||||||
|
return AppConfig.FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!m_ee_data.setStorageData_ee(buffer, (short) 0, writeSize,
|
||||||
|
storageOffset)) {
|
||||||
|
return AppConfig.EEP_WRITEFAIL;
|
||||||
|
}
|
||||||
|
return AppConfig.SUCCESS;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
package sega.a7;
|
||||||
|
|
||||||
|
import javacard.framework.Util;
|
||||||
|
import javacard.security.MessageDigest;
|
||||||
|
|
||||||
|
public class TraceDataInfoRead extends AuthCommand {
|
||||||
|
|
||||||
|
public TraceDataInfoRead(short cmd, AppData data, MakePacket packet, MessageDigest sha1,
|
||||||
|
HmacSha1 hmacSha1, Crypt crypt) {
|
||||||
|
super(cmd, (short) 46, (short) 62, AppConfig.LEVEL_AUTH,
|
||||||
|
data, packet, sha1, hmacSha1, crypt);
|
||||||
|
}
|
||||||
|
|
||||||
|
public short core(byte[] data, short dataOffset, short dataLength) {
|
||||||
|
byte[] info = m_ee_data.getTraceDataInfo_ee();
|
||||||
|
byte[] buffer = m_ee_data.getGeneralBuffer_ramD();
|
||||||
|
short encSize = encData(info, (short) 0, AppConfig.TRACE_DATA_INFO_SIZE,
|
||||||
|
buffer, (short) 0);
|
||||||
|
if (encSize != AppConfig.TRACE_DATA_INFO_SIZE) {
|
||||||
|
return AppConfig.FAIL;
|
||||||
|
}
|
||||||
|
m_ramD_outDataInfo[0] = 0;
|
||||||
|
m_ramD_outDataInfo[1] = encSize;
|
||||||
|
Util.arrayCopyNonAtomic(buffer, (short) 0, m_ramD_outData, (short) 0, encSize);
|
||||||
|
return AppConfig.SUCCESS;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
package sega.a7;
|
||||||
|
|
||||||
|
import javacard.security.MessageDigest;
|
||||||
|
|
||||||
|
public class TraceDataInfoWrite extends AuthCommand {
|
||||||
|
|
||||||
|
public TraceDataInfoWrite(short cmd, AppData data, MakePacket packet, MessageDigest sha1,
|
||||||
|
HmacSha1 hmacSha1, Crypt crypt) {
|
||||||
|
super(cmd, (short) 62, (short) 46, AppConfig.LEVEL_AUTH,
|
||||||
|
data, packet, sha1, hmacSha1, crypt);
|
||||||
|
}
|
||||||
|
|
||||||
|
public short core(byte[] data, short dataOffset, short dataLength) {
|
||||||
|
byte[] buffer = m_ee_data.getGeneralBuffer_ramD();
|
||||||
|
short decSize = decData(data, (short) (dataOffset + AppConfig.HEADER_SIZE),
|
||||||
|
AppConfig.TRACE_DATA_INFO_SIZE, buffer, (short) 0);
|
||||||
|
if (decSize != AppConfig.TRACE_DATA_INFO_SIZE) {
|
||||||
|
return AppConfig.FAIL;
|
||||||
|
}
|
||||||
|
if (!m_ee_data.setTraceDataInfo_ee(buffer, (short) 0,
|
||||||
|
AppConfig.TRACE_DATA_INFO_SIZE)) {
|
||||||
|
return AppConfig.EEP_WRITEFAIL;
|
||||||
|
}
|
||||||
|
return AppConfig.SUCCESS;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
package sega.a7;
|
||||||
|
|
||||||
|
import javacard.framework.Util;
|
||||||
|
import javacard.security.MessageDigest;
|
||||||
|
|
||||||
|
public class UdataRead extends AuthCommand {
|
||||||
|
|
||||||
|
public UdataRead(short cmd, AppData data, MakePacket packet, MessageDigest sha1,
|
||||||
|
HmacSha1 hmacSha1, Crypt crypt) {
|
||||||
|
super(cmd, (short) 46, (short) 62, AppConfig.LEVEL_AUTH,
|
||||||
|
data, packet, sha1, hmacSha1, crypt);
|
||||||
|
}
|
||||||
|
|
||||||
|
public short core(byte[] data, short dataOffset, short dataLength) {
|
||||||
|
byte[] udata = m_ee_data.getUdata_ee();
|
||||||
|
byte[] buffer = m_ee_data.getGeneralBuffer_ramD();
|
||||||
|
short encSize = encData(udata, (short) 0, AppConfig.UDATA_SIZE, buffer, (short) 0);
|
||||||
|
if (encSize != AppConfig.UDATA_SIZE) {
|
||||||
|
return AppConfig.FAIL;
|
||||||
|
}
|
||||||
|
m_ramD_outDataInfo[0] = 0;
|
||||||
|
m_ramD_outDataInfo[1] = encSize;
|
||||||
|
Util.arrayCopyNonAtomic(buffer, (short) 0, m_ramD_outData, (short) 0, encSize);
|
||||||
|
return AppConfig.SUCCESS;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
package sega.a7;
|
||||||
|
|
||||||
|
import javacard.security.MessageDigest;
|
||||||
|
|
||||||
|
public class UdataWrite extends AuthCommand {
|
||||||
|
|
||||||
|
public UdataWrite(short cmd, AppData data, MakePacket packet, MessageDigest sha1,
|
||||||
|
HmacSha1 hmacSha1, Crypt crypt) {
|
||||||
|
super(cmd, (short) 62, (short) 46, AppConfig.LEVEL_AUTH,
|
||||||
|
data, packet, sha1, hmacSha1, crypt);
|
||||||
|
}
|
||||||
|
|
||||||
|
public short core(byte[] data, short dataOffset, short dataLength) {
|
||||||
|
if (m_ee_data.getSflag_ee()) {
|
||||||
|
return AppConfig.EEPWRITE_DISABLE;
|
||||||
|
}
|
||||||
|
byte[] buffer = m_ee_data.getGeneralBuffer_ramD();
|
||||||
|
short decSize = decData(data, (short) (dataOffset + AppConfig.HEADER_SIZE),
|
||||||
|
AppConfig.UDATA_SIZE, buffer, (short) 0);
|
||||||
|
if (decSize != AppConfig.UDATA_SIZE) {
|
||||||
|
return AppConfig.FAIL;
|
||||||
|
}
|
||||||
|
if (!m_ee_data.setUdata_ee(buffer, (short) 0, AppConfig.UDATA_SIZE)) {
|
||||||
|
return AppConfig.EEP_WRITEFAIL;
|
||||||
|
}
|
||||||
|
return AppConfig.SUCCESS;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
package sega.a7;
|
||||||
|
|
||||||
|
import javacard.framework.Util;
|
||||||
|
import javacard.security.MessageDigest;
|
||||||
|
|
||||||
|
public class VerGet extends Command {
|
||||||
|
|
||||||
|
public VerGet(short cmd, AppData data, MakePacket packet, MessageDigest sha1) {
|
||||||
|
super(cmd, (short) 26, (short) 28, data, packet, sha1);
|
||||||
|
}
|
||||||
|
|
||||||
|
public short core(byte[] data, short dataOffset, short dataLength) {
|
||||||
|
Util.setShort(m_ramD_outData, (short) 0, AppConfig.VERSION);
|
||||||
|
m_ramD_outDataInfo[0] = 0;
|
||||||
|
m_ramD_outDataInfo[1] = 2;
|
||||||
|
return AppConfig.SUCCESS;
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in new issue
Block a user