Формат подписи в SJCL

В SJCL (Stanford Javascript Crypto Library) подписи строятся на основе асимметричной криптографии и используются для проверки целостности и подлинности данных. Основная идея — одна сторона создаёт подпись с использованием приватного ключа, а другая проверяет её с помощью публичного ключа.

SJCL не навязывает единый формат хранения подписи как «строку с метаданными», вместо этого работа строится вокруг структурированных объектов и сериализации данных в JSON или бинарные представления.


Базовая структура подписи

В контексте SJCL подпись обычно рассматривается как результат работы алгоритма цифровой подписи, например DSA или ECDSA.

Типичная структура включает:

  • исходное сообщение (message)
  • хеш сообщения
  • приватный ключ (для создания подписи)
  • публичный ключ (для проверки)
  • сама подпись (signature), которая представляет собой пару чисел

В ECDSA-подобной схеме подпись формируется как:

  • r — координата, полученная из точки на эллиптической кривой
  • s — значение, зависящее от хеша сообщения и приватного ключа

Именно пара (r, s) является базовым «форматом подписи» в SJCL.


Представление подписи в JS-объектах

В SJCL подпись обычно хранится как объект:

{
  r: "bigint-or-bitArray",
  s: "bigint-or-bitArray"
}

На практике SJCL часто использует bitArray — внутренний формат хранения битовых последовательностей.

Пример:

var signature = {
  r: sjcl.bn.fromBits(rBits),
  s: sjcl.bn.fromBits(sBits)
};

Где:

  • sjcl.bn — big number реализация библиотеки
  • fromBits — преобразование из bitArray в число

Формат bitArray как основа подписи

Ключевая особенность SJCL — использование bitArray для всех бинарных данных.

Подпись в сыром виде может быть представлена как:

[r || s]

То есть последовательное объединение двух чисел в бинарной форме.

Пример структуры bitArray:

var sigBits = rBits.concat(sBits);

где:

  • concat объединяет два массива бит
  • результат может быть сериализован в base64 или hex

Сериализация подписи

Чтобы передавать подпись по сети, SJCL предоставляет механизмы преобразования:

В hex

var hexSignature = sjcl.codec.hex.fromBits(sigBits);

В base64

var base64Signature = sjcl.codec.base64.fromBits(sigBits);

В JSON-совместимом виде

var jsonSignature = JSON.stringify({
  sig: sjcl.codec.base64.fromBits(sigBits)
});

В большинстве веб-приложений именно base64 используется как стандартный формат передачи.


Формат подписи в ECDSA внутри SJCL

Если рассматривать ECDSA, то подпись формируется следующим образом:

  1. Вычисляется хеш сообщения:
var hash = sjcl.hash.sha256.hash(message);
  1. Генерируется случайное число k

  2. Вычисляется точка на кривой:

    • (x1, y1) = k * G
  3. r = x1 mod n

  4. s = k⁻¹ (hash + r * privKey) mod n

Именно (r, s) и есть итоговый формат подписи.


Внутренний формат SJCL для кривых

SJCL хранит кривые и параметры в объектной форме:

sjcl.ecc.curves.c256

Подпись связывается с конкретной кривой, поэтому её корректная интерпретация невозможна без контекста кривой.


Кодирование и декодирование подписи

Кодирование

function encodeSignature(sig) {
  return sjcl.codec.base64.fromBits(
    sig.r.toBits().concat(sig.s.toBits())
  );
}

Декодирование

function decodeSignature(str) {
  var bits = sjcl.codec.base64.toBits(str);

  var rBits = sjcl.bitArray.bitSlice(bits, 0, 256);
  var sBits = sjcl.bitArray.bitSlice(bits, 256);

  return {
    r: sjcl.bn.fromBits(rBits),
    s: sjcl.bn.fromBits(sBits)
  };
}

Здесь важно учитывать длину кривой (например, 256 бит), которая определяет границу разбиения.


Проверка подписи и её формат

Проверка подписи в SJCL использует публичный ключ:

pub.verify(hash, signature);

где signature — объект с полями r и s.

Внутри библиотеки происходит:

  • восстановление точек кривой
  • вычисление u1 и u2
  • проверка равенства точки на кривой

Если подпись была изменена хотя бы на 1 бит, проверка провалится.


Важные особенности формата

1. Отсутствие стандартизированного контейнера

SJCL не использует ASN.1 DER формат, как OpenSSL. Подпись — это «чистые числа».


2. Зависимость от кривой

Одна и та же подпись без знания параметров кривой не интерпретируется.


3. Потеря информации при неправильной сериализации

Если перепутать порядок r и s или использовать неправильную длину битов, подпись становится недействительной.


4. Отсутствие встроенной метаинформации

Формат подписи не содержит:

  • алгоритма
  • хеша
  • идентификатора кривой

Это всё должно храниться отдельно.


Практический формат хранения подписи в приложениях

В реальных системах поверх SJCL обычно вводится собственный контейнер:

{
  "alg": "ECDSA",
  "curve": "c256",
  "sig": "base64(r||s)",
  "hash": "SHA-256"
}

SJCL отвечает только за математическую часть, а структура — на уровне приложения.


Ошибки при работе с форматом подписи

Несоответствие длины bitArray

Если r и s имеют разную длину, объединение приводит к смещению границ.

Неправильная кодировка

hex и base64 нельзя смешивать при проверке.

Повторное использование nonce k

Хотя это не проблема формата напрямую, это разрушает подпись математически, делая её восстановимой.


Итоговая структура подписи в SJCL

Фактически формат можно свести к следующему:

  • два числа: r и s
  • бинарное представление через bitArray
  • сериализация в base64/hex при передаче
  • отсутствие встроенного метаформата
  • зависимость от параметров эллиптической кривой

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