573 lines
11 KiB
Dart
Executable File
Raw Permalink 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.

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;
}
}