546 lines
10 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 = [
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 = [0xA3B1BAC6, 0x56AA3350, 0x677D9197, 0xB27022DC];
static const List<int> CK = [
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 _encryptKey = List<int>.filled(32, 0);
static final _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 x = x1 ^ x2 ^ x3 ^ rk;
int bb = 0;
int c = 0;
List<int> a = List<int>.filled(4, 0);
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;
List<int> keyBytes = List<int>.filled(4, 0);
List<int> sboxBytes = List<int>.filled(4, 0);
_writeUint32BE(key, keyBytes, 0);
for (int i = 0; i < 4; i++) {
sboxBytes[i] = _Sbox(keyBytes[i]);
}
int temp = _readUint32BE(sboxBytes, 0);
roundKey = temp ^ SMUtils.leftShift(temp, 13) ^ SMUtils.leftShift(temp, 23);
return roundKey;
}
static void setKey(List<int> key) {
List<int> keyBytes = key;
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++) {
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;
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 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 {
// final lastByte = input.last;
// final cutLen = input.length - lastByte;
// Log.p("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 key = (flag == SM4_ENCRYPT) ? _encryptKey : _decryptKey;
if (flag == SM4_ENCRYPT) {
data = _padding(data, SM4_ENCRYPT);
}
final length = data.length;
final List<int> output = [];
for (int offset = 0; offset < length; offset += blockSize) {
final outData = List<int>.filled(blockSize, 0);
final copyLen =
(offset + blockSize <= length) ? blockSize : length - offset;
final 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
List<int> supplementList = List.filled(16, 0x00);
/// complete list
List<int> completeList = [...list, ...supplementList].sublist(0, 16);
return completeList;
}
/// hex byte list to hex string
static String _listToHex(List<int> arr) {
String hexString = arr
.map((item) {
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,
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);
List<int> input = data;
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 decrypt(List<int> data,
{List<int>? key, SM4CryptoMode mode = SM4CryptoMode.ECB, String? iv}) {
if (key != null) setKey(key);
List<int> input = data;
List<int> output = _crypto(input, SM4_DECRYPT, mode, iv);
return output;
}
}