Compare commits

..

3 Commits

Author SHA1 Message Date
zcy
4af4d3342e 注释改一改 2025-09-09 19:56:51 +08:00
zcy
506c89cc09 处理入参STMSI得到iv 2025-09-09 16:08:23 +08:00
zcy
c8608321d9 处理入参STMSI得到iv 2025-09-09 16:06:49 +08:00
4 changed files with 160 additions and 89 deletions

View File

@@ -1,27 +1,31 @@
package com.cscn;
import javacard.framework.APDU;
import javacard.framework.ISO7816;
import javacard.framework.ISOException;
import javacard.framework.JCSystem;
import javacard.framework.Util;
//import javacard.security.RandomData;
/**
* 仅做一次“自检调用”:
* 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 = {
public class Method {
// todo 写固定值在代码里了
// 加密密钥0: 32字节
private static final byte[] ZUC256_ENC_KEY_0 = {
(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
(byte)0x38,(byte)0x39,(byte)0x61,(byte)0x62,(byte)0x63,(byte)0x64,(byte)0x65,(byte)0x66,
};
// todo 写固定值在代码里了
// 加密密钥1: 32字节
private static final byte[] ZUC256_ENC_KEY_1 = {
(byte)0x61,(byte)0x62,(byte)0x63,(byte)0x64,(byte)0x65,(byte)0x66,(byte)0x67,(byte)0x68,
(byte)0x69,(byte)0x6A,(byte)0x6B,(byte)0x6C,(byte)0x6D,(byte)0x6E,(byte)0x6F,(byte)0x70,
(byte)0x71,(byte)0x72,(byte)0x73,(byte)0x74,(byte)0x75,(byte)0x76,(byte)0x77,(byte)0x78,
(byte)0x79,(byte)0x7A,(byte)0x30,(byte)0x31,(byte)0x32,(byte)0x33,(byte)0x34,(byte)0x35,
};
// IV
@@ -30,43 +34,53 @@ public final class Method {
// (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
// };
private static final byte[] IV25 = {
(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)0x30,(byte)0x31,(byte)0x32,(byte)0x33,(byte)0x34,(byte)0x35,(byte)0x36,
(byte)0x37
// private static final byte[] IV25 = {
// (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)0x30,(byte)0x31,(byte)0x32,(byte)0x33,(byte)0x34,(byte)0x35,(byte)0x36,
// (byte)0x37
// };
// STMSI:0,1,2,3,4,5; 10B 0x00; 9B 0x01
private static final byte[] IV_MASK_25 = {
(byte)0x01,(byte)0x01,(byte)0x01,(byte)0x01,(byte)0x01,(byte)0x01,(byte)0x01,(byte)0x01, (byte)0x01, // 0-8, 0x01 9B
(byte)0x00,(byte)0x00,(byte)0x00,(byte)0x00,(byte)0x00,(byte)0x00,(byte)0x00, (byte)0x00,(byte)0x00,(byte)0x00, // 918 0x00 10B
(byte)0xFF,(byte)0xFF,(byte)0xFF,(byte)0xFF,(byte)0xFF,(byte)0xFF // 1924 STMSI 6B
};
// 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
};
// // 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字节
// pri
//
// vate 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
// };
// ======================================
// 输入数据缓冲区最大值
static final short MAX_DATA_BLOCK_SIZE = 128;
// 运行时缓冲放RAM避免写EEPROM
// 运行时缓冲
byte[] ctx_buf;
short ctx_buflen;
//todo test
byte[] buf1; // Enc(Input)
//todo test
byte[] buf2; // Enc(Enc(Input)) -> 应为 Input
// //todo test
// byte[] buf1; // Enc(Input)
// //todo test
// byte[] buf2; // Enc(Enc(Input)) -> 应为 Input
// LFSR: 原 int[16] -> hi/lo 各 16
public short[] LFSR_hi;
@@ -95,6 +109,7 @@ public final class Method {
byte[] extract_iv_last8;
byte[] extracted_iv_23;
byte[] mix_iv_25;
short[] add64_tmp;
@@ -157,20 +172,31 @@ public final class Method {
short[] finishZuc256EncryptCtx_ks;
byte[] finishZuc256EncryptCtx_keystreamBytes;
byte[] randomByte;
static final byte[] seed = { (byte)0x12, (byte)0x77, (byte)0x56 };//rand seed
// rand instance
// RandomData rng;
public Method(){//RandomData rng) {
// rng = RandomData.getInstance(RandomData.ALG_PSEUDO_RANDOM);
// rng.setSeed(seed, (short)0, (short)seed.length);
randomByte = JCSystem.makeTransientByteArray((short)1, JCSystem.MEMORY_TYPE_TRANSIENT_RESET);
public Method() {
// key list tmp buffer, lenth must equal with key_store(flash)!
update_key_buf = JCSystem.makeTransientByteArray((short)120, JCSystem.MEMORY_TYPE_TRANSIENT_RESET);
// 算法内部使用23Byte IV
extracted_iv_23 = JCSystem.makeTransientByteArray((short)23, JCSystem.MEMORY_TYPE_TRANSIENT_RESET);
// 临时存储拼接25B IV buffer
mix_iv_25 = JCSystem.makeTransientByteArray((short)25, JCSystem.MEMORY_TYPE_TRANSIENT_RESET);
ctx_buf = JCSystem.makeTransientByteArray((short)4, JCSystem.MEMORY_TYPE_TRANSIENT_RESET);
LFSR_hi = JCSystem.makeTransientShortArray((short)16, JCSystem.MEMORY_TYPE_TRANSIENT_RESET);
LFSR_lo = JCSystem.makeTransientShortArray((short)16, JCSystem.MEMORY_TYPE_TRANSIENT_RESET);
// todo buf1 if not use, delete please
buf1 = JCSystem.makeTransientByteArray(MAX_DATA_BLOCK_SIZE, JCSystem.MEMORY_TYPE_TRANSIENT_RESET);
// todo buf2 if not use, delete please
buf2 = JCSystem.makeTransientByteArray(MAX_DATA_BLOCK_SIZE, JCSystem.MEMORY_TYPE_TRANSIENT_RESET);
// // todo buf1 if not use, delete please
// buf1 = JCSystem.makeTransientByteArray(MAX_DATA_BLOCK_SIZE, JCSystem.MEMORY_TYPE_TRANSIENT_RESET);
// // todo buf2 if not use, delete please
// buf2 = JCSystem.makeTransientByteArray(MAX_DATA_BLOCK_SIZE, JCSystem.MEMORY_TYPE_TRANSIENT_RESET);
stmsi = JCSystem.makeTransientByteArray((short)6, JCSystem.MEMORY_TYPE_TRANSIENT_RESET);
location_data = JCSystem.makeTransientByteArray((short)5, JCSystem.MEMORY_TYPE_TRANSIENT_RESET);
@@ -255,32 +281,40 @@ public final class Method {
}
//todo test
public void testZuc256(APDU apdu) {
// //todo test
// public void testZuc256(APDU apdu) {
//
// byte[] apduBuf = apdu.getBuffer();
//
// // 第一次Enc(Input)
// initZuc256EncryptCtx(ZUC256_ENC_KEY_1, IV25);
// updateZuc256EncryptCtx(INPUT, (short) INPUT.length, buf1);
// finishZuc256EncryptCtx(buf1, (short) INPUT.length); // 若 Input 长度为 4 的倍数则通常无副作用,留着更稳妥
//
// boolean encMatch = (Util.arrayCompare(buf1, (short)0, ENC_EXPECTED, (short)0, (short)INPUT.length) == 0);
//
// // 第二次Enc(Enc(Input)) 应还原 Input
// initZuc256EncryptCtx(ZUC256_ENC_KEY_1, IV25);
// updateZuc256EncryptCtx(buf1, (short) INPUT.length, buf2);
// finishZuc256EncryptCtx(buf2, (short) INPUT.length);
// 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);
// }
byte[] apduBuf = apdu.getBuffer();
// 第一次Enc(Input)
initZuc256EncryptCtx(KEY32, IV25);
updateZuc256EncryptCtx(INPUT, (short) INPUT.length, buf1);
finishZuc256EncryptCtx(buf1, (short) INPUT.length); // 若 Input 长度为 4 的倍数则通常无副作用,留着更稳妥
boolean encMatch = (Util.arrayCompare(buf1, (short)0, ENC_EXPECTED, (short)0, (short)INPUT.length) == 0);
// 第二次Enc(Enc(Input)) 应还原 Input
initZuc256EncryptCtx(KEY32, IV25);
updateZuc256EncryptCtx(buf1, (short) INPUT.length, buf2);
finishZuc256EncryptCtx(buf2, (short) INPUT.length);
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);
/**
* 拼接6B STMSI与IV_MASK为25B IV
*/
private void mix_stmsi_iv_25B(byte[] stmsi) {
Util.arrayCopyNonAtomic(IV_MASK_25, (short)0, mix_iv_25, (short)0, (short)25);
Util.arrayCopyNonAtomic(stmsi, (short)0, mix_iv_25, (short)19, (short)6);
}
/**
@@ -288,12 +322,15 @@ public final class Method {
* 输入: stmsi[6], data[5]
* 输出: out[5]
*/
private void encryptLocationZuc256(byte[] stmsi, byte[] data, byte[] out) {
initZuc256EncryptCtx(KEY32, IV25);
private void encryptLocationZuc256(byte[] stmsi, byte[] data, byte[] out, byte[] chosenKey) {
mix_stmsi_iv_25B(stmsi);
initZuc256EncryptCtx(chosenKey, mix_iv_25);
updateZuc256EncryptCtx(data, (short) 5, out);
finishZuc256EncryptCtx(out, (short) 5); // 若 Input 长度为 4 的倍数则通常无副作用,留着更稳妥
finishZuc256EncryptCtx(out, (short) 5);
}
/**
* APDU处理函数
*/
@@ -314,13 +351,32 @@ public final class Method {
// data
Util.arrayCopyNonAtomic(buffer, (short)(off + 9), location_data, (short)0, (short)5);
// === 生成 fakedata ===
encryptLocationZuc256(stmsi, location_data, location_res_data);
byte[] chosenKey;
// 产生随机数选取Key1 or Key2
// rng.generateData(randomByte, (short) 0, (short) 1);
// byte rnd = randomByte[0];
byte rnd = secParam; // todo 临时用输入的安全参数当作随机数值因本地调试环境不支持RandomData
// 随机数模2运算结果
byte mod2 = (byte) (rnd & 0x01);
// 取 rnd 的低5bit 左移到高5bit位置, 填入回参的 密钥选取字节(1B)
byte resKeyRandByte = (byte) ((rnd & 0x1F) << 3);
// 行动分支
if (mod2 == 0) {
chosenKey = ZUC256_ENC_KEY_0;
resKeyRandByte = (byte)(resKeyRandByte | 0x00); // 选取第一条 b2 b1 b0 填 0 0 0 todo 待确认
} else {
chosenKey = ZUC256_ENC_KEY_1;
resKeyRandByte = (byte)(resKeyRandByte | 0x01); // 选取第一条 b2 b1 b0 填 0 0 1 todo 待确认
}
// === 生成 zuc256加密 ===
encryptLocationZuc256(stmsi, location_data, location_res_data, chosenKey);
// === 出参组装 ===
buffer[(short)(off + 0)] = (byte)0xA1; // 类型
buffer[(short)(off + 1)] = (byte)0x06; // 长度
buffer[(short)(off + 2)] = (byte)0x01; // 密钥选取
buffer[(short)(off + 2)] = resKeyRandByte; // 密钥选取
Util.arrayCopyNonAtomic(location_res_data, (short)0, buffer, (short)(off + 3), (short)5);
return (short)8; // 出参总长度
@@ -1482,7 +1538,6 @@ public final class Method {
// System.arraycopy(out, 4, newOut, 0, out.length - 4);
// }
// out = newOut;
// 这里C实现就是直接指针+4的。JavaSE实现搞这个new干嘛。。
outPos += 4;
}
}
@@ -1557,8 +1612,6 @@ public final class Method {
}
}
// 清理上下文
// Arrays.fill(this.buf, (byte) 0);
for(short i=0; i<4; i++) {
@@ -1574,7 +1627,6 @@ public final class Method {
LFSR_hi[i] = 0;
}
// this.state.R1 = 0;
// this.state.R2 = 0;
// R1、R2 清零

View File

@@ -0,0 +1,22 @@
//package com.cscn;
//
//import javacard.security.RandomData;
//
//public class RandomGenerator {
// private final RandomData random;
//
// //构造函数
// public RandomGenerator()
// {
// //类当中有getInstance的都要先调用这个函数获取对象实例才能使用其他方法不然6F00
// random = RandomData.getInstance(RandomData.ALG_SECURE_RANDOM);
// }
//
// //产生length长度的随机数并返回
// public final byte[] GenrateSecureRand(short length, byte[] out)
// {
// //生成4bit的随机数
// random.generateData(out, (short)0, (short)length);
// return out;
// }
//}

View File

@@ -4,15 +4,15 @@ import javacard.framework.APDU;
import javacard.framework.Applet;
import javacard.framework.ISO7816;
import javacard.framework.ISOException;
//import javacard.security.RandomData;
/**
* @author liuww
*
*/
public class XwSecurity extends Applet {
//todo test
public static final byte INS_PROCESS_DATA_TEST = (byte)0xE3;
// public static final byte INS_PROCESS_DATA_TEST = (byte)0xE3;
public static final byte INS_LOCATION_ENCRYPT = (byte)0xCA;
@@ -28,10 +28,9 @@ public class XwSecurity extends Applet {
public XwSecurity(byte[] bArray, short bOffset, byte bLength) {
// TODO Auto-generated constructor stub
method = new Method(); //todo new?
method = new Method();//rng); //todo new?
// key store -> flash
key_store_byte = new byte[120];
register(bArray, (short)(bOffset + 1), bArray[bOffset]);
}
@@ -71,10 +70,10 @@ public class XwSecurity extends Applet {
// break;
//todo test
case INS_PROCESS_DATA_TEST:
method.testZuc256(apdu);
break;
// //todo test
// case INS_PROCESS_DATA_TEST:
// method.testZuc256(apdu);
// break;
case INS_LOCATION_ENCRYPT:

View File

@@ -2,12 +2,10 @@ package com.cscn;
/**
* 常量表S0/S1 与 ZUC256_D。
* 注意JavaCard 目标环境建议将表定义为 static final 数组,按 int/short 存放。
* 适配说明:已将 32bit int 数组改为 16bit short 数组符合JavaCard 16bit能力要求
*
*/
public final class Zuc256Tables {
// 私有构造函数:防止类被实例化
private Zuc256Tables() {}
// S盒S0, S1