573 lines
11 KiB
Dart
Executable File
573 lines
11 KiB
Dart
Executable File
import 'dart:convert';
|
||
import 'utils/utils.dart';
|
||
|
||
enum SM4CryptoMode { ECB, CBC }
|
||
|
||
class SM4 {
|
||
static const List<int> S_BOX = <int>[
|
||
0xd6,
|
||
0x90,
|
||
0xe9,
|
||
0xfe,
|
||
0xcc,
|
||
0xe1,
|
||
0x3d,
|
||
0xb7,
|
||
0x16,
|
||
0xb6,
|
||
0x14,
|
||
0xc2,
|
||
0x28,
|
||
0xfb,
|
||
0x2c,
|
||
0x05,
|
||
0x2b,
|
||
0x67,
|
||
0x9a,
|
||
0x76,
|
||
0x2a,
|
||
0xbe,
|
||
0x04,
|
||
0xc3,
|
||
0xaa,
|
||
0x44,
|
||
0x13,
|
||
0x26,
|
||
0x49,
|
||
0x86,
|
||
0x06,
|
||
0x99,
|
||
0x9c,
|
||
0x42,
|
||
0x50,
|
||
0xf4,
|
||
0x91,
|
||
0xef,
|
||
0x98,
|
||
0x7a,
|
||
0x33,
|
||
0x54,
|
||
0x0b,
|
||
0x43,
|
||
0xed,
|
||
0xcf,
|
||
0xac,
|
||
0x62,
|
||
0xe4,
|
||
0xb3,
|
||
0x1c,
|
||
0xa9,
|
||
0xc9,
|
||
0x08,
|
||
0xe8,
|
||
0x95,
|
||
0x80,
|
||
0xdf,
|
||
0x94,
|
||
0xfa,
|
||
0x75,
|
||
0x8f,
|
||
0x3f,
|
||
0xa6,
|
||
0x47,
|
||
0x07,
|
||
0xa7,
|
||
0xfc,
|
||
0xf3,
|
||
0x73,
|
||
0x17,
|
||
0xba,
|
||
0x83,
|
||
0x59,
|
||
0x3c,
|
||
0x19,
|
||
0xe6,
|
||
0x85,
|
||
0x4f,
|
||
0xa8,
|
||
0x68,
|
||
0x6b,
|
||
0x81,
|
||
0xb2,
|
||
0x71,
|
||
0x64,
|
||
0xda,
|
||
0x8b,
|
||
0xf8,
|
||
0xeb,
|
||
0x0f,
|
||
0x4b,
|
||
0x70,
|
||
0x56,
|
||
0x9d,
|
||
0x35,
|
||
0x1e,
|
||
0x24,
|
||
0x0e,
|
||
0x5e,
|
||
0x63,
|
||
0x58,
|
||
0xd1,
|
||
0xa2,
|
||
0x25,
|
||
0x22,
|
||
0x7c,
|
||
0x3b,
|
||
0x01,
|
||
0x21,
|
||
0x78,
|
||
0x87,
|
||
0xd4,
|
||
0x00,
|
||
0x46,
|
||
0x57,
|
||
0x9f,
|
||
0xd3,
|
||
0x27,
|
||
0x52,
|
||
0x4c,
|
||
0x36,
|
||
0x02,
|
||
0xe7,
|
||
0xa0,
|
||
0xc4,
|
||
0xc8,
|
||
0x9e,
|
||
0xea,
|
||
0xbf,
|
||
0x8a,
|
||
0xd2,
|
||
0x40,
|
||
0xc7,
|
||
0x38,
|
||
0xb5,
|
||
0xa3,
|
||
0xf7,
|
||
0xf2,
|
||
0xce,
|
||
0xf9,
|
||
0x61,
|
||
0x15,
|
||
0xa1,
|
||
0xe0,
|
||
0xae,
|
||
0x5d,
|
||
0xa4,
|
||
0x9b,
|
||
0x34,
|
||
0x1a,
|
||
0x55,
|
||
0xad,
|
||
0x93,
|
||
0x32,
|
||
0x30,
|
||
0xf5,
|
||
0x8c,
|
||
0xb1,
|
||
0xe3,
|
||
0x1d,
|
||
0xf6,
|
||
0xe2,
|
||
0x2e,
|
||
0x82,
|
||
0x66,
|
||
0xca,
|
||
0x60,
|
||
0xc0,
|
||
0x29,
|
||
0x23,
|
||
0xab,
|
||
0x0d,
|
||
0x53,
|
||
0x4e,
|
||
0x6f,
|
||
0xd5,
|
||
0xdb,
|
||
0x37,
|
||
0x45,
|
||
0xde,
|
||
0xfd,
|
||
0x8e,
|
||
0x2f,
|
||
0x03,
|
||
0xff,
|
||
0x6a,
|
||
0x72,
|
||
0x6d,
|
||
0x6c,
|
||
0x5b,
|
||
0x51,
|
||
0x8d,
|
||
0x1b,
|
||
0xaf,
|
||
0x92,
|
||
0xbb,
|
||
0xdd,
|
||
0xbc,
|
||
0x7f,
|
||
0x11,
|
||
0xd9,
|
||
0x5c,
|
||
0x41,
|
||
0x1f,
|
||
0x10,
|
||
0x5a,
|
||
0xd8,
|
||
0x0a,
|
||
0xc1,
|
||
0x31,
|
||
0x88,
|
||
0xa5,
|
||
0xcd,
|
||
0x7b,
|
||
0xbd,
|
||
0x2d,
|
||
0x74,
|
||
0xd0,
|
||
0x12,
|
||
0xb8,
|
||
0xe5,
|
||
0xb4,
|
||
0xb0,
|
||
0x89,
|
||
0x69,
|
||
0x97,
|
||
0x4a,
|
||
0x0c,
|
||
0x96,
|
||
0x77,
|
||
0x7e,
|
||
0x65,
|
||
0xb9,
|
||
0xf1,
|
||
0x09,
|
||
0xc5,
|
||
0x6e,
|
||
0xc6,
|
||
0x84,
|
||
0x18,
|
||
0xf0,
|
||
0x7d,
|
||
0xec,
|
||
0x3a,
|
||
0xdc,
|
||
0x4d,
|
||
0x20,
|
||
0x79,
|
||
0xee,
|
||
0x5f,
|
||
0x3e,
|
||
0xd7,
|
||
0xcb,
|
||
0x39,
|
||
0x48
|
||
];
|
||
|
||
static const List<int> FK = <int>[
|
||
0xA3B1BAC6,
|
||
0x56AA3350,
|
||
0x677D9197,
|
||
0xB27022DC
|
||
];
|
||
|
||
static const List<int> CK = <int>[
|
||
0x00070e15,
|
||
0x1c232a31,
|
||
0x383f464d,
|
||
0x545b6269,
|
||
0x70777e85,
|
||
0x8c939aa1,
|
||
0xa8afb6bd,
|
||
0xc4cbd2d9,
|
||
0xe0e7eef5,
|
||
0xfc030a11,
|
||
0x181f262d,
|
||
0x343b4249,
|
||
0x50575e65,
|
||
0x6c737a81,
|
||
0x888f969d,
|
||
0xa4abb2b9,
|
||
0xc0c7ced5,
|
||
0xdce3eaf1,
|
||
0xf8ff060d,
|
||
0x141b2229,
|
||
0x30373e45,
|
||
0x4c535a61,
|
||
0x686f767d,
|
||
0x848b9299,
|
||
0xa0a7aeb5,
|
||
0xbcc3cad1,
|
||
0xd8dfe6ed,
|
||
0xf4fb0209,
|
||
0x10171e25,
|
||
0x2c333a41,
|
||
0x484f565d,
|
||
0x646b7279
|
||
];
|
||
|
||
static const int SM4_ENCRYPT = 1;
|
||
|
||
static const int SM4_DECRYPT = 0;
|
||
|
||
static const int blockSize = 16;
|
||
|
||
static final List<int> _encryptKey = List<int>.filled(32, 0);
|
||
static final List<int> _decryptKey = List<int>.filled(32, 0);
|
||
|
||
static int _readUint32BE(List<int> b, int i) {
|
||
return ((b[i] & 0xff) << 24) |
|
||
((b[i + 1] & 0xff) << 16) |
|
||
((b[i + 2] & 0xff) << 8) |
|
||
(b[i + 3] & 0xff);
|
||
}
|
||
|
||
static void _writeUint32BE(int n, List<int> b, int i) {
|
||
b[i] = (n >> 24) & 0xff;
|
||
b[i + 1] = (n >> 16) & 0xff;
|
||
b[i + 2] = (n >> 8) & 0xff;
|
||
b[i + 3] = n & 0xff;
|
||
}
|
||
|
||
static int _Sbox(int inch) => S_BOX[inch & 0xFF];
|
||
|
||
static int _sm4F(int x0, int x1, int x2, int x3, int rk) {
|
||
final int x = x1 ^ x2 ^ x3 ^ rk;
|
||
int bb = 0;
|
||
int c = 0;
|
||
final List<int> a = List<int>.filled(4, 0);
|
||
final List<int> b = List<int>.filled(4, 0);
|
||
_writeUint32BE(x, a, 0);
|
||
b[0] = _Sbox(a[0]);
|
||
b[1] = _Sbox(a[1]);
|
||
b[2] = _Sbox(a[2]);
|
||
b[3] = _Sbox(a[3]);
|
||
bb = _readUint32BE(b, 0);
|
||
|
||
c = bb ^
|
||
SMUtils.leftShift(bb, 2) ^
|
||
SMUtils.leftShift(bb, 10) ^
|
||
SMUtils.leftShift(bb, 18) ^
|
||
SMUtils.leftShift(bb, 24);
|
||
return x0 ^ c;
|
||
}
|
||
|
||
static int _calculateRoundKey(int key) {
|
||
int roundKey = 0;
|
||
final List<int> keyBytes = List<int>.filled(4, 0);
|
||
final List<int> sboxBytes = List<int>.filled(4, 0);
|
||
_writeUint32BE(key, keyBytes, 0);
|
||
for (int i = 0; i < 4; i++) {
|
||
sboxBytes[i] = _Sbox(keyBytes[i]);
|
||
}
|
||
final int temp = _readUint32BE(sboxBytes, 0);
|
||
roundKey = temp ^ SMUtils.leftShift(temp, 13) ^ SMUtils.leftShift(temp, 23);
|
||
return roundKey;
|
||
}
|
||
|
||
static void setKey(List<int> key) {
|
||
final List<int> keyBytes = key;
|
||
final List<int> intermediateKeys = List<int>.filled(36, 0);
|
||
for (int i = 0; i < 4; i++) {
|
||
intermediateKeys[i] = _readUint32BE(keyBytes, i * 4) ^ FK[i];
|
||
}
|
||
for (int i = 0; i < 32; i++) {
|
||
intermediateKeys[i + 4] = intermediateKeys[i] ^
|
||
_calculateRoundKey(intermediateKeys[i + 1] ^
|
||
intermediateKeys[i + 2] ^
|
||
intermediateKeys[i + 3] ^
|
||
CK[i]);
|
||
_encryptKey[i] = intermediateKeys[i + 4];
|
||
}
|
||
|
||
for (int i = 0; i < 16; i++) {
|
||
final int temp = _encryptKey[i];
|
||
_decryptKey[i] = _encryptKey[31 - i];
|
||
_decryptKey[31 - i] = temp;
|
||
}
|
||
}
|
||
|
||
static void _round(List<int> sk, List<int> input, List<int> output) {
|
||
int i = 0;
|
||
final List<int> ulbuf = List<int>.filled(36, 0);
|
||
ulbuf[0] = _readUint32BE(input, 0);
|
||
ulbuf[1] = _readUint32BE(input, 4);
|
||
ulbuf[2] = _readUint32BE(input, 8);
|
||
ulbuf[3] = _readUint32BE(input, 12);
|
||
while (i < 32) {
|
||
ulbuf[i + 4] =
|
||
_sm4F(ulbuf[i], ulbuf[i + 1], ulbuf[i + 2], ulbuf[i + 3], sk[i]);
|
||
i++;
|
||
}
|
||
|
||
_writeUint32BE(ulbuf[35], output, 0);
|
||
_writeUint32BE(ulbuf[34], output, 4);
|
||
_writeUint32BE(ulbuf[33], output, 8);
|
||
_writeUint32BE(ulbuf[32], output, 12);
|
||
}
|
||
|
||
static List<int> _padding(List<int> input, int mode) {
|
||
final int padLen = blockSize - (input.length % blockSize);
|
||
|
||
if (mode == SM4_ENCRYPT) {
|
||
final List<int> paddedList = List<int>.filled(input.length + padLen, 0);
|
||
paddedList.setRange(0, input.length, input);
|
||
for (int i = input.length; i < paddedList.length; i++) {
|
||
paddedList[i] = padLen;
|
||
}
|
||
return paddedList;
|
||
} else {
|
||
// 解密时去除PKCS7填充
|
||
final int lastByte = input.last;
|
||
if (lastByte > 0 && lastByte <= blockSize) {
|
||
// 验证填充是否合法
|
||
bool isValidPadding = true;
|
||
for (int i = input.length - lastByte; i < input.length; i++) {
|
||
if (input[i] != lastByte) {
|
||
isValidPadding = false;
|
||
break;
|
||
}
|
||
}
|
||
if (isValidPadding) {
|
||
return input.sublist(0, input.length - lastByte);
|
||
}
|
||
}
|
||
return input;
|
||
// final lastByte = input.last;
|
||
// final cutLen = input.length - lastByte;
|
||
// // AppLog.log("object input.length:${input.length} lastByte:$lastByte input:$input cutLen:$cutLen");
|
||
// return input.sublist(0, cutLen);
|
||
// return input;
|
||
}
|
||
}
|
||
|
||
static List<int> _crypto(
|
||
List<int> data, int flag, SM4CryptoMode mode, String? iv) {
|
||
late List<int> lastVector;
|
||
if (mode == SM4CryptoMode.CBC) {
|
||
if (iv == null || iv.length != 32) {
|
||
throw Exception('IV must be a string of length 16');
|
||
} else {
|
||
lastVector = SMUtils.hexStringToBytes(iv);
|
||
}
|
||
}
|
||
final List<int> key = (flag == SM4_ENCRYPT) ? _encryptKey : _decryptKey;
|
||
if (flag == SM4_ENCRYPT) {
|
||
data = _padding(data, SM4_ENCRYPT);
|
||
}
|
||
final int length = data.length;
|
||
final List<int> output = <int>[];
|
||
|
||
for (int offset = 0; offset < length; offset += blockSize) {
|
||
final List<int> outData = List<int>.filled(blockSize, 0);
|
||
final int copyLen =
|
||
(offset + blockSize <= length) ? blockSize : length - offset;
|
||
final List<int> input = data.sublist(offset, offset + copyLen);
|
||
if (mode == SM4CryptoMode.CBC && flag == SM4_ENCRYPT) {
|
||
for (int i = 0; i < blockSize; i++) {
|
||
input[i] = input[i] ^ lastVector[i];
|
||
}
|
||
}
|
||
_round(key, input, outData);
|
||
|
||
if (mode == SM4CryptoMode.CBC && flag == SM4_DECRYPT) {
|
||
for (int i = 0; i < blockSize; i++) {
|
||
outData[i] ^= lastVector[i];
|
||
}
|
||
}
|
||
output.addAll(outData);
|
||
|
||
if (mode == SM4CryptoMode.CBC) {
|
||
if (flag == SM4_ENCRYPT) {
|
||
lastVector = outData;
|
||
} else {
|
||
lastVector = input;
|
||
}
|
||
}
|
||
}
|
||
if (flag == SM4_DECRYPT) {
|
||
return _padding(output, SM4_DECRYPT);
|
||
}
|
||
return output;
|
||
}
|
||
|
||
/// Utf8 to byte list
|
||
static List<int> _utf8ToArray(String str) {
|
||
return utf8.encode(str);
|
||
}
|
||
|
||
// /// auto add 0x00
|
||
static List<int> _autoAddZero(List<int> list) {
|
||
/// supplementary list
|
||
final List<int> supplementList = List.filled(16, 0x00);
|
||
|
||
/// complete list
|
||
final List<int> completeList =
|
||
<int>[...list, ...supplementList].sublist(0, 16);
|
||
return completeList;
|
||
}
|
||
|
||
/// hex byte list to hex string
|
||
static String _listToHex(List<int> arr) {
|
||
final String hexString = arr
|
||
.map((int item) {
|
||
final String itemHexString = item.toRadixString(16);
|
||
// The hexadecimal notation is 0123456789ABCDEF
|
||
//if there is a single one, add 0
|
||
if (itemHexString.length == 1) {
|
||
return '0$itemHexString';
|
||
} else {
|
||
return itemHexString;
|
||
}
|
||
})
|
||
.toList()
|
||
.join('');
|
||
|
||
return hexString;
|
||
}
|
||
|
||
static String createHexKey({
|
||
required String key,
|
||
bool autoPushZero = true,
|
||
}) {
|
||
List<int> keyList = _utf8ToArray(key);
|
||
|
||
if (autoPushZero) {
|
||
if (keyList.length < 128 / 8) {
|
||
keyList = _autoAddZero(keyList);
|
||
}
|
||
if (keyList.length > 128 / 8) {
|
||
keyList = keyList.sublist(0, 16);
|
||
}
|
||
}
|
||
return _listToHex(keyList);
|
||
}
|
||
|
||
static List<int> encrypt(List<int> data,
|
||
{List<int>? key, SM4CryptoMode mode = SM4CryptoMode.ECB, String? iv}) {
|
||
if (key != null) {
|
||
setKey(key);
|
||
}
|
||
final List<int> input = data;
|
||
final List<int> output = _crypto(input, SM4_ENCRYPT, mode, iv);
|
||
return output;
|
||
}
|
||
|
||
// static decrypt(String cipherText,
|
||
// {String? key, SM4CryptoMode mode = SM4CryptoMode.ECB, String? iv}) {
|
||
// if (key != null) setKey(key);
|
||
// List<int> input = SMUtils.hexStringToBytes(cipherText);
|
||
// List<int> output = _crypto(input, SM4_DECRYPT, mode, iv);
|
||
// return utf8.decode(output);
|
||
// }
|
||
|
||
static List<int> decrypt(List<int> data,
|
||
{List<int>? key, SM4CryptoMode mode = SM4CryptoMode.ECB, String? iv}) {
|
||
if (key != null) {
|
||
setKey(key);
|
||
}
|
||
final List<int> input = data;
|
||
final List<int> output = _crypto(input, SM4_DECRYPT, mode, iv);
|
||
return output;
|
||
}
|
||
}
|