From c8608321d9e54a600ce8c75c32b42be15c5ce408 Mon Sep 17 00:00:00 2001 From: zcy Date: Tue, 9 Sep 2025 16:06:49 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A4=84=E7=90=86=E5=85=A5=E5=8F=82STMSI?= =?UTF-8?q?=E5=BE=97=E5=88=B0iv?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Src/com/cscn/Method.java | 201 +++++++++++++++++++----------- Src/com/cscn/RandomGenerator.java | 22 ++++ Src/com/cscn/XwSecurity.java | 14 +-- 3 files changed, 159 insertions(+), 78 deletions(-) create mode 100644 Src/com/cscn/RandomGenerator.java diff --git a/Src/com/cscn/Method.java b/Src/com/cscn/Method.java index 8c8dacf..40a6bc5 100644 --- a/Src/com/cscn/Method.java +++ b/Src/com/cscn/Method.java @@ -1,27 +1,34 @@ 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 { +public class Method { // ======= 已按你提供的数据填充 ======= - // Key: 32字节 - private static final byte[] KEY32 = { + + // 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 +37,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, // 9–18 0x00 10B + (byte)0xFF,(byte)0xFF,(byte)0xFF,(byte)0xFF,(byte)0xFF,(byte)0xFF // 19–24 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 +112,7 @@ public final class Method { byte[] extract_iv_last8; byte[] extracted_iv_23; + byte[] mix_iv_25; short[] add64_tmp; @@ -157,20 +175,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 +284,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 +325,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 +354,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; // 出参总长度 diff --git a/Src/com/cscn/RandomGenerator.java b/Src/com/cscn/RandomGenerator.java new file mode 100644 index 0000000..e3432bd --- /dev/null +++ b/Src/com/cscn/RandomGenerator.java @@ -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; +// } +//} diff --git a/Src/com/cscn/XwSecurity.java b/Src/com/cscn/XwSecurity.java index b4532c8..e2e8151 100644 --- a/Src/com/cscn/XwSecurity.java +++ b/Src/com/cscn/XwSecurity.java @@ -4,6 +4,7 @@ import javacard.framework.APDU; import javacard.framework.Applet; import javacard.framework.ISO7816; import javacard.framework.ISOException; +//import javacard.security.RandomData; /** * @author liuww @@ -12,7 +13,7 @@ import javacard.framework.ISOException; 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,11 +29,10 @@ 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]); } public static void install(byte[] bArray, short bOffset, byte bLength) @@ -71,10 +71,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: