Files
se-algo/Project/Src/com/cscn/Method.java
zcy 8e25aab97a new对象(short、byte数组)改为调用JCSystem.makeTransientXxxArray放到ram里面;
makeTransientXxxArray类型改为MEMORY_TYPE_TRANSIENT_RESETMEMORY_TYPE_TRANSIENT_RESET
2025-09-09 00:04:54 +08:00

206 lines
7.4 KiB
Java
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package com.cscn;
import javacard.framework.APDU;
import javacard.framework.ISO7816;
import javacard.framework.ISOException;
import javacard.framework.JCSystem;
import javacard.framework.Util;
/**
* 仅做一次“自检调用”:
* 1) ctx.init(KEY, IV); Enc(Input) == EncExpected ?
* 2) ctx.init(KEY, IV); Enc(Enc(Input)) == Input ?
* Response: 2字节 [encMatch, dblEncRestored]1=真0=假
*/
public final class Method {
// ======= 已按你提供的数据填充 =======
// Key: 32字节
private static final byte[] KEY32 = {
(byte)0x30,(byte)0x31,(byte)0x32,(byte)0x33,(byte)0x34,(byte)0x35,(byte)0x36,(byte)0x37,
(byte)0x38,(byte)0x39,(byte)0x61,(byte)0x62,(byte)0x63,(byte)0x64,(byte)0x65,(byte)0x66,
(byte)0x30,(byte)0x31,(byte)0x32,(byte)0x33,(byte)0x34,(byte)0x35,(byte)0x36,(byte)0x37,
(byte)0x38,(byte)0x39,(byte)0x61,(byte)0x62,(byte)0x63,(byte)0x64,(byte)0x65,(byte)0x66
};
// IV
private static final byte[] IV25 = {//todo 23 -> 25
(byte)0x30,(byte)0x31,(byte)0x32,(byte)0x33,(byte)0x34,(byte)0x35,(byte)0x36,(byte)0x37,
(byte)0x38,(byte)0x39,(byte)0x61,(byte)0x62,(byte)0x63,(byte)0x64,(byte)0x65,(byte)0x66,
(byte)0x67,(byte)0xC3,(byte)0x1C,(byte)0xB3,(byte)0xD3,(byte)0x5D,(byte)0xB7
};
// Input: 明文38字节
private static final byte[] INPUT = {
(byte)0x5A,(byte)0x55,(byte)0x43,(byte)0x32,(byte)0x35,(byte)0x36,(byte)0xE5,(byte)0xAF,
(byte)0xB9,(byte)0xE7,(byte)0xA7,(byte)0xB0,(byte)0xE5,(byte)0x8A,(byte)0xA0,(byte)0xE8,
(byte)0xA7,(byte)0xA3,(byte)0xE5,(byte)0xAF,(byte)0x86,(byte)0xE6,(byte)0xB5,(byte)0x8B,
(byte)0xE8,(byte)0xAF,(byte)0x95,(byte)0x3A,(byte)0x31,(byte)0x32,(byte)0x33,(byte)0x34,
(byte)0x35,(byte)0x36,(byte)0x37,(byte)0x38,(byte)0x39,(byte)0x30
};
// EncResult: 期望密文38字节
private static final byte[] ENC_EXPECTED = {
(byte)0x6C,(byte)0xEE,(byte)0x3C,(byte)0xFA,(byte)0xDE,(byte)0xBB,(byte)0xCB,(byte)0xE5,
(byte)0x33,(byte)0x51,(byte)0x07,(byte)0x07,(byte)0x90,(byte)0x25,(byte)0x93,(byte)0x27,
(byte)0x94,(byte)0xF5,(byte)0x18,(byte)0x70,(byte)0xEF,(byte)0x71,(byte)0x72,(byte)0x7D,
(byte)0xBA,(byte)0x8D,(byte)0xBF,(byte)0x4F,(byte)0x61,(byte)0xC9,(byte)0xA8,(byte)0xE9,
(byte)0xFF,(byte)0x19,(byte)0xF9,(byte)0xF9,(byte)0xE2,(byte)0xD2
};
// ======================================
// 运行时缓冲放RAM避免写EEPROM
private final Zuc256EncryptCtx ctx;
public Method() {
ctx = new Zuc256EncryptCtx(); // 仅创建一次
}
public void processData(APDU apdu) {
short L = (short) INPUT.length;
byte[] buf1; // Enc(Input)
byte[] buf2; // Enc(Enc(Input)) -> 应为 Input
buf1 = JCSystem.makeTransientByteArray(L, JCSystem.MEMORY_TYPE_TRANSIENT_RESET);
buf2 = JCSystem.makeTransientByteArray(L, JCSystem.MEMORY_TYPE_TRANSIENT_RESET);
byte[] apduBuf = apdu.getBuffer();
// 第一次Enc(Input)
ctx.init(KEY32, IV25);
Zuc256State tmpState = ctx.state;
ctx.update(INPUT, (short) INPUT.length, buf1);
ctx.finish(buf1); // 若 Input 长度为 4 的倍数则通常无副作用,留着更稳妥
Zuc256State tmpState2 = ctx.state;
boolean encMatch = (Util.arrayCompare(buf1, (short)0, ENC_EXPECTED, (short)0, (short)INPUT.length) == 0);
// 第二次Enc(Enc(Input)) 应还原 Input
ctx.init(KEY32, IV25);
ctx.update(buf1, (short) INPUT.length, buf2);
ctx.finish(buf2);
boolean dblOk = (Util.arrayCompare(buf2, (short)0, INPUT, (short)0, (short)INPUT.length) == 0);
// 返回 2 字节结果:[encMatch, dblOk]1=真, 0=假
apduBuf[0] = (byte)(encMatch ? 1 : 0);
apduBuf[1] = (byte)(dblOk ? 1 : 0);
short outLen = 2;
apdu.setOutgoing();
apdu.setOutgoingLength(outLen);
apdu.sendBytes((short)0, outLen);
}
/**
* fake数据生成函数
* 输入: stmsi[6], data[5]
* 输出: fakedata[5]
*/
private void makeFakeData(byte[] stmsi, byte[] data, byte[] fakedata) {
// 示例:逐字节异或 stmsi 前5字节
for (short i = 0; i < (short)5; i++) {
fakedata[i] = (byte)(data[i]);
}
}
/**
* APDU处理函数
*/
public short processDataFake(byte[] buffer, short off, short len, byte[] key_store) {
// 至少要有 12 个字节1+1+1+6+5
if (len < (short)12) {
ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
}
// === 解析入参 ===
byte inType = buffer[(short)(off + 0)]; // 类型 A1
byte inLen = buffer[(short)(off + 1)]; // 长度 0x0C
byte secParam = buffer[(short)(off + 2)]; // 安全参数
// STMSI
byte[] stmsi = JCSystem.makeTransientByteArray((short)6, JCSystem.MEMORY_TYPE_TRANSIENT_RESET);
Util.arrayCopyNonAtomic(buffer, (short)(off + 3), stmsi, (short)0, (short)6);
// data
byte[] data = JCSystem.makeTransientByteArray((short)5, JCSystem.MEMORY_TYPE_TRANSIENT_RESET);
Util.arrayCopyNonAtomic(buffer, (short)(off + 9), data, (short)0, (short)5);
// === 生成 fakedata ===
byte[] fakedata = JCSystem.makeTransientByteArray((short)5, JCSystem.MEMORY_TYPE_TRANSIENT_RESET);
makeFakeData(stmsi, data, fakedata);
// === 出参组装 ===
buffer[(short)(off + 0)] = (byte)0xA1; // 类型
buffer[(short)(off + 1)] = (byte)0x06; // 长度
buffer[(short)(off + 2)] = (byte)0x01; // 密钥选取
Util.arrayCopyNonAtomic(fakedata, (short)0, buffer, (short)(off + 3), (short)5);
return (short)8; // 出参总长度
}
public short updateKey(byte[] buffer, short off, short len, byte[] key_store) {
final short SLOT_SIZE = 40; // 每个槽固定40字节
final short HDR_LEN = 4; // 报文头长度: [0]=keyLenField, [1]=alg, [2]=keyId, [3]=ver
// === 解析头 ===
byte keyLenField = buffer[(short)(off + 0)]; // 包含头部+密钥的总长度
byte alg = buffer[(short)(off + 1)];
byte keyId = buffer[(short)(off + 2)];
byte ver = buffer[(short)(off + 3)];
// 计算实际密钥长度
short realKeyLen = (short)(keyLenField - (byte)3); // 长度字段 = key长度 + 3字节头
if (realKeyLen <= 0) {
ISOException.throwIt(ISO7816.SW_DATA_INVALID); // key长度不合法
}
// 检查APDU长度和key长度是否匹配
if (len != (short)(HDR_LEN + realKeyLen)) {
ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
}
if (len > SLOT_SIZE) {
ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
}
// === 将原有 key_store 拷贝到 RAM避免直接覆盖 EEPROM (可选) ===
byte[] key_buf = JCSystem.makeTransientByteArray((short)80, JCSystem.MEMORY_TYPE_TRANSIENT_RESET);
Util.arrayCopyNonAtomic(key_store, (short)0, key_buf, (short)0, (short)key_store.length);
// === 遍历槽,找到匹配的 (alg, keyId, ver),否则找空槽 ===
short slots = (short)(key_store.length / SLOT_SIZE);
short target = -1;
for (short i = 0; i < slots; i++) {
short base = (short)(i * SLOT_SIZE);
byte algOld = key_store[(short)(base + 1)];
byte keyIdOld = key_store[(short)(base + 2)];
byte verOld = key_store[(short)(base + 3)];
if (algOld == alg && keyId == keyIdOld && ver == verOld) {
target = i; // 找到已存在的,覆盖它
break;
}
if (key_store[base] == (byte)0x00 && target == -1) {
target = i; // 找到第一个空槽
}
}
if (target == -1) {
ISOException.throwIt(ISO7816.SW_FILE_FULL); // 没有空位
}
// === 覆盖写入本次APDU的头+密钥数据 ===
short base = (short)(target * SLOT_SIZE);
// 先清空槽
Util.arrayFillNonAtomic(key_store, base, SLOT_SIZE, (byte)0x00);
// 再写入APDU传入的头+数据 (len 个字节)
Util.arrayCopyNonAtomic(buffer, off, key_store, base, len);
return 0; // 无返回数据
}
}