Фаззинг криптографических входных данных

Криптографические библиотеки в JavaScript особенно чувствительны к некорректным входным данным: любые неожиданные байтовые последовательности, обрезанные строки, неверные кодировки или частично повреждённые структуры могут приводить не только к исключениям, но и к некорректной криптографической обработке. В случае Stanford JavaScript Crypto Library (SJCL) это особенно критично, поскольку библиотека рассчитана на строго определённые форматы входов — битовые массивы, корректно сериализованные JSON-структуры и валидные ключи.

SJCL оперирует несколькими типами данных:

  • битовые массивы (sjcl.bitArray)
  • строки в UTF-8 и hex-представлении
  • ключи AES/PRP
  • параметры PBKDF2, HMAC
  • сериализованные объекты (encrypt/decrypt payload)

Каждый из этих типов предполагает строгую структуру. Например, sjcl.encrypt ожидает корректный пароль и валидный plaintext, а sjcl.decrypt — строго форматированный JSON с полями iv, salt, ct, iter, ks, mode, adata.

Фаззинг в этом контексте означает систематическую подачу некорректных, случайных или полуслучайных входов для выявления:

  • падений (throw, undefined behavior)
  • деградации производительности (например, экспоненциальные циклы)
  • логических ошибок (успешное расшифрование мусора)
  • криптографических несоответствий (невалидные MAC, но успешный decrypt)

Особенности обработки данных в SJCL, влияющие на фаззинг

SJCL не использует нативные бинарные типы JavaScript (ArrayBuffer в ранних версиях), а работает через абстракцию битовых массивов:

var sjcl = require("sjcl");

var bits = sjcl.codec.utf8String.toBits("test");

Это означает, что многие ошибки возникают не на уровне типов JS, а на уровне преобразования:

  • неправильная длина массива (не кратна 32 битам)
  • NaN или undefined в битовых операциях
  • переполнения при побитовых сдвигах
  • некорректные padding-схемы

Подготовка SJCL к фаззингу

Перед запуском фаззинга важно обернуть целевые функции в безопасные ловушки:

function safeDecrypt(password, data) {
    try {
        return sjcl.decrypt(password, data);
    } catch (e) {
        return null;
    }
}

Однако для полноценного тестирования такая защита должна быть отключена или логироваться, иначе ошибки будут скрыты.


Генерация некорректных входов

Фаззинг криптографических функций SJCL требует не просто случайных строк, а структурно разнообразных входов.

1. Бинарный мусор

function randomBitArray(len) {
    var arr = [];
    for (var i = 0; i < len; i++) {
        arr.push(Math.floor(Math.random() * 0xffffffff));
    }
    return arr;
}

Используется для тестирования sjcl.bitArray операций:

  • AND/OR/XOR
  • shift/rotate
  • bit slicing

2. Фаззинг JSON-структур шифротекста

SJCL encrypt возвращает JSON:

{
  "iv": "...",
  "v": 1,
  "iter": 1000,
  "ks": 128,
  "ct": "..."
}

Фаззинг модификаций:

function fuzzCiphertext(ct) {
    let obj = JSON.parse(ct);

    const keys = Object.keys(obj);
    const randomKey = keys[Math.floor(Math.random() * keys.length)];

    obj[randomKey] = Math.random().toString(36);

    return JSON.stringify(obj);
}

Типичные мутации:

  • удаление обязательных полей
  • подмена base64 строк мусором
  • изменение iteration count на отрицательные значения
  • вставка бинарных символов

3. UTF-8 и codec fuzzing

SJCL активно использует кодеки:

  • sjcl.codec.utf8String
  • sjcl.codec.hex
  • sjcl.codec.base64

Фаззинг здесь включает:

const inputs = [
    "\u0000\u0000\u0000",
    "\uffff\uffff",
    "????",
    "\x00\xff\x10\x80",
    String.fromCharCode(...Array(256).keys())
];

Особенно важно проверять:

  • некорректные surrogate pairs
  • обрезанные UTF-8 последовательности
  • смешанные кодировки

Интеграция с fuzzing-движками

Для SJCL чаще всего используют:

  • jsfuzz
  • fast-check (property-based fuzzing)
  • custom loop-based fuzzers
  • AFL++ через Node bridge

Пример простого fuzz-loop:

const sjcl = require("sjcl");

for (let i = 0; i < 100000; i++) {
    try {
        let password = randomBitArray(4).toString();
        let data = fuzzCiphertext(sjcl.encrypt("pass", "data"));

        sjcl.decrypt(password, data);
    } catch (e) {
        // логируем только неожиданные ошибки
        if (!(e instanceof Error)) {
            console.log("Unexpected:", e);
        }
    }
}

Фаззинг криптографических ключей

Ключи в SJCL проходят через KDF (обычно PBKDF2). Ошибки возникают при:

  • нулевой энтропии
  • слишком коротких паролях
  • невалидных salt значениях

Фаззинг ключей:

function fuzzPassword() {
    const variants = [
        "",
        "\u0000\u0000\u0000",
        "a".repeat(10000),
        null,
        undefined,
        { toString: () => "pass" }
    ];
    return variants[Math.floor(Math.random() * variants.length)];
}

Обнаружение логических уязвимостей

Особенно важно не только искать падения, но и логические ошибки:

  • успешное расшифрование с неправильным ключом
  • совпадение MAC при повреждённом ciphertext
  • игнорирование salt или IV

Пример проверки:

const enc = sjcl.encrypt("password", "secret");

const mutated = JSON.parse(enc);
mutated.ct = mutated.ct.replace(/A/g, "B");

const dec = sjcl.decrypt("password", JSON.stringify(mutated));

Если декодирование прошло успешно — это сигнал о проблеме в проверке целостности.


Детектирование ошибок исполнения битовых операций

SJCL активно использует побитовые операции, что делает его чувствительным к:

  • signed 32-bit overflow
  • NaN propagation
  • shift beyond bounds

Фаззинг таких случаев:

for (let i = -1000; i < 1000; i++) {
    try {
        sjcl.bitArray.bitSlice([i, i*2, i*3], i, i+64);
    } catch (e) {}
}

Дифференциальный фаззинг

Эффективный метод для криптографии:

  • сравнение SJCL с другим реализацией (например, crypto.subtle)

Пример:

const sjclOut = sjcl.decrypt(pass, data);
const webcryptoOut = await subtleDecrypt(pass, data);

if (sjclOut !== webcryptoOut) {
    console.log("Mismatch detected");
}

Минимизация входов для воспроизводимости

После нахождения ошибки важно сократить вход:

  • удаление лишних байтов
  • бинарный поиск по изменению массива
  • стабилизация seed генератора

Типовые классы багов, выявляемых фаззингом SJCL

  • некорректный parsing JSON ciphertext
  • нарушение padding oracle защиты
  • ошибки UTF-8 декодирования
  • неправильная обработка больших iteration count
  • сбои при нестандартных bitArray длинах

Практическая модель фаззинга SJCL

На практике эффективная схема выглядит как конвейер:

  1. Генерация входа (структурного, а не полностью случайного)

  2. Мутация ciphertext / key / codec

  3. Запуск encrypt/decrypt

  4. Проверка:

    • отсутствие исключений
    • корректность MAC
    • консистентность round-trip
  5. Логирование минимального воспроизводимого случая


Фаззинг криптографических входов в SJCL особенно ценен тем, что библиотека часто используется как “reference implementation” в проектах, где безопасность зависит не только от алгоритма, но и от корректности обработки нестандартных данных. Даже небольшие отклонения в обработке битовых массивов или сериализации могут приводить к серьёзным последствиям на уровне протоколов, построенных поверх SJCL.