Skip to content

SM4 — блочный шифр ​

SM4 — китайский национальный блочный шифр (GM/T 0002-2012): блок 128 бит, ключ 128 бит, 32 раунда. Раундовая функция работает в sm4.wasm; шесть режимов (ECB / CBC / CTR / CFB / OFB / GCM) собираются на стороне TS из вызовов блочного уровня.

  • Ключ всегда 16 байт;
  • Четыре блочных режима (ECB / CBC / CFB) принимают 16-байтный IV; в GCM обычно 12 байт;
  • Шифрование и расшифрование возвращают hex-строки.

Импорт ​

ts
import { sm4 } from 'sm-crypto-wasm';

await sm4.ensureLoaded();

Как выбрать режим ​

РежимIVВыравниваниеРаспространение ошибокДля чего
ECBнетнет—только тесты / совместимость; одинаковые блоки открытого текста дают одинаковые блоки шифротекста
CBC16 BPKCS#7повреждённый блок влияет на следующийвыбор по умолчанию для общего случая
CTR16 Bнетнетпотоково, произвольный доступ; никогда не повторяйте IV+counter
CFB16 Bнетвлияет на последующие сегментысамосинхронизирующийся поток
OFB16 Bнетнеткогда важен характер распространения ошибок
GCMобычно 12 Bнет—рекомендуемый вариант с аутентификацией (AEAD)

Наша рекомендация по умолчанию

Нужны и конфиденциальность, и целостность → GCM. Только конфиденциальность и не хочется управлять выравниванием → CTR. К ECB / CBC стоит прибегать лишь ради совместимости формата со старыми системами.

API ​

Все функции принимают ключ как string | Uint8Array: строки распознаются как hex / base64 («текстового ключа в UTF-8» не существует — ключи это байты). Все они возвращают hex в нижнем регистре.

Блочные режимы ​

ФункцияСигнатураПримечание
encryptECB(key, msg) => stringбез выравнивания; длина msg должна быть ненулевой кратной 16
decryptECB(key, ct) => stringто же ограничение
encryptCBC(key, iv, msg) => stringвыравнивание PKCS#7, любая длина (включая пустую)
decryptCBC(key, iv, ct) => stringснимает выравнивание
encryptCFB(key, iv, msg) => stringCFB128, обратная связь берётся из сегмента шифротекста
decryptCFB(key, iv, ct) => string

Потоковые режимы ​

ФункцияСигнатураПримечание
encryptCTR / decryptCTR(key, iv, data) => stringодна и та же реализация (псевдоним cryptCTR); XOR с потоком ключей, любая длина
encryptOFB / decryptOFB(key, iv, data) => stringто же (псевдоним cryptOFB)

CTR и OFB — инволюции: decrypt(encrypt(x)) === x, поэтому шифрование и расшифрование имеют одну точку входа. Канонические имена в исходниках — cryptCTR / cryptOFB; написания encrypt* / decrypt* — псевдонимы к тем же функциям.

GCM (AEAD) ​

ts
function encryptGCM(
  key: string | Uint8Array,
  iv: string | Uint8Array,
  msg: string | Uint8Array,
  aad?: string | Uint8Array,
): { ct: string; tag: string }

function decryptGCM(
  key: string | Uint8Array,
  iv: string | Uint8Array,
  ct: string | Uint8Array,
  tag: string | Uint8Array,
  aad?: string | Uint8Array | { aad?: string | Uint8Array; safeMode?: boolean },
): string
ПараметрТипОбязателенОписание
keystring | Uint8Arrayда16 байт
ivstring | Uint8Arrayдане может быть пустым; 12-байтный IV используется напрямую как J0 по спецификации GCM
msg / ctstring | Uint8Arrayдаможет быть пустым (пустой открытый текст допустим)
tagstring | Uint8Arrayпри расшифровании16-байтный тег аутентификации
aadstring | Uint8Arrayнетдополнительные аутентифицируемые данные: не шифруются, но аутентифицируются

Пятый аргумент decryptGCM принимает либо aad, либо { aad, safeMode: true } — при safeMode неудачная аутентификация возвращает null вместо исключения DecryptionError.

Использование ​

ts
import { sm4 } from 'sm-crypto-wasm';

await sm4.ensureLoaded();

const key = '0123456789abcdeffedcba9876543210';  // 16 байт
const iv  = '000102030405060708090a0b0c0d0e0f';  // 16 байт

// ECB: без выравнивания, длина кратна 16
sm4.encryptECB(key, '00112233445566778899aabbccddeeff');

// CBC: выравнивание PKCS#7, любая длина
const ct = sm4.encryptCBC(key, iv, 'hello');
sm4.decryptCBC(key, iv, ct);                     // → '68656c6c6f'

// CTR / OFB: инволюции
sm4.encryptCTR(key, iv, '616263');
sm4.decryptCTR(key, iv, sm4.encryptCTR(key, iv, '616263'));

// GCM: дополнительно выдаётся 16-байтный тег
const { ct: gcmCt, tag } = sm4.encryptGCM(key, iv, '616263', 'aabbccdd');
sm4.decryptGCM(key, iv, gcmCt, tag, 'aabbccdd');                          // вернёт текст только при успешной проверке
sm4.decryptGCM(key, iv, gcmCt, tag, { aad: 'aabbccdd', safeMode: true }); // → null при неудаче

Playground ​

SM4: шифрование (ECB / CBC / CTR / CFB / OFB / GCM)

WASM · live

Выберите режим и направление, задайте ключ и запустите. ECB не выравнивает, поэтому открытый текст должен быть кратен 16 байтам; CBC добавляет PKCS#7; GCM — это AEAD и дополнительно возвращает тег аутентификации.

CBC · Шифрование: результат (hex)—
Эквивалентный код
import { sm4 } from 'sm-crypto-wasm';

await sm4.ensureLoaded();
sm4.encryptCBC(key, iv, 'abc');                    // любая длина, внутри PKCS#7
sm4.encryptCTR(key, iv, 'abc');                    // потоковый режим, то же, что decryptCTR
const { ct, tag } = sm4.encryptGCM(key, iv, 'abc', aad);
sm4.decryptGCM(key, iv, ct, tag, aad);             // при подмене — DecryptionError

Ошибки и граничные случаи ​

СитуацияБросается
key не 16 байтInvalidKeyError
iv в CBC / CFB / OFB не 16 байтInvalidKeyError
iv в GCM пустойInvalidKeyError
длина открытого текста в ECB не ненулевая кратная 16RangeError
длина шифротекста в ECB не ненулевая кратная 16InvalidCiphertextError
длина шифротекста в CBC не кратна 16InvalidCiphertextError
выравнивание PKCS#7 после расшифрования в CBC некорректноDecryptionError
tag в GCM не 16 байтInvalidCiphertextError
аутентификация GCM не прошла (изменён шифротекст / тег / AAD)DecryptionError

Аргументы, которые вообще не являются криптографическими объектами (неверные значения перечислений и подобное), сохраняют нативные TypeError / RangeError.

Лицензия Apache-2.0