Initial code commit

This commit is contained in:
Furry12 committed 2026-10-23 00:07:07 +08:00
commit 96130738b8
40 files changed
+2753

No files matched your search

+3
View File
@@ -0,0 +1,3 @@
> "A wise man never leaves debugging symbols in production firmware"
>
> Sun Tzu, Art of War
+231
View File
@@ -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;
}
}
+52
View File
@@ -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;
}
}
+111
View File
@@ -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;
}
+424
View File
@@ -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;
}
}
+301
View File
@@ -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;
}
}
+113
View File
@@ -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);
}
}
+162
View File
@@ -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;
}
}
+78
View File
@@ -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;
}
}
+138
View File
@@ -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;
}
}
+45
View File
@@ -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;
}
}
+18
View File
@@ -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;
}
}
+43
View File
@@ -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;
}
}
+43
View File
@@ -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;
}
}
+31
View File
@@ -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;
}
}
+29
View File
@@ -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;
}
}
+95
View File
@@ -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;
}
}
+31
View File
@@ -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;
}
}
+27
View File
@@ -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;
}
}
+30
View File
@@ -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;
}
}
+25
View File
@@ -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;
}
}
+18
View File
@@ -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;
}
}
+115
View File
@@ -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;
}
}
+32
View File
@@ -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);
}
}
+25
View File
@@ -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;
}
}
+46
View File
@@ -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;
}
}
+50
View File
@@ -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;
}
}
+50
View File
@@ -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;
}
}
+41
View File
@@ -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;
}
}
+15
View File
@@ -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;
}
}
+25
View File
@@ -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;
}
}
+20
View File
@@ -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;
}
}
+58
View File
@@ -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;
}
}
+44
View File
@@ -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;
}
}
+59
View File
@@ -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;
}
}
+27
View File
@@ -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;
}
}
+26
View File
@@ -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;
}
}
+26
View File
@@ -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;
}
}
+28
View File
@@ -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;
}
}
+18
View File
@@ -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;
}
}