Вычисление общего секрета

В основе большинства современных протоколов защищённого обмена ключами лежит задача получения общего секрета между двумя сторонами при наличии небезопасного канала связи. Библиотека SJCL реализует этот механизм через поддержку эллиптической криптографии и классического подхода Диффи–Хеллмана, адаптированного для JavaScript-окружения.


Базовая модель общего секрета

Вычисление общего секрета опирается на асимметричную криптографию:

  • каждая сторона генерирует пару ключей: приватный и публичный
  • публичный ключ передаётся по открытым каналам
  • приватный ключ остаётся в секрете
  • на основе чужого публичного ключа и собственного приватного вычисляется общий секрет

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


Эллиптическая криптография в SJCL

SJCL использует реализацию эллиптических кривых через модуль sjcl.ecc. На практике применяется схема ElGamal и ECDH (Elliptic Curve Diffie–Hellman).

Основные компоненты:

  • sjcl.ecc.curves.c256 — стандартная кривая (например, 256-битная)
  • sjcl.ecc.elGamal — реализация ключевой пары
  • методы generateKeys() и dh() для обмена секретом

Генерация ключевой пары

Каждая сторона начинает с создания ключевой пары:

const keypairA = sjcl.ecc.elGamal.generateKeys(256);
const keypairB = sjcl.ecc.elGamal.generateKeys(256);

Внутренняя структура:

  • keypairA.sec — приватный ключ
  • keypairA.pub — публичный ключ

Публичный ключ можно безопасно передавать другой стороне.


Передача публичных ключей

После генерации происходит обмен публичными компонентами:

const pubA = keypairA.pub;
const pubB = keypairB.pub;

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


Вычисление общего секрета

Ключевая операция выполняется через метод dh() (Diffie–Hellman):

const secretA = keypairA.sec.dh(pubB);
const secretB = keypairB.sec.dh(pubA);

Свойство результата

secretA и secretB будут идентичны, несмотря на различие входных параметров.

Это значение представляет собой точку на эллиптической кривой или её внутреннее представление в виде большого числа.


Преобразование общего секрета

Сырой общий секрет не используется напрямую для шифрования. Его необходимо преобразовать в симметричный ключ.

Обычно применяется хеширование:

const hashedSecretA = sjcl.hash.sha256.hash(secretA);
const hashedSecretB = sjcl.hash.sha256.hash(secretB);

Результат:

  • фиксированная длина ключа
  • устойчивость к утечкам структуры эллиптической точки
  • пригодность для AES или других симметричных алгоритмов

Получение ключа для симметричного шифрования

После хеширования ключ может быть использован в AES:

const key = new sjcl.cipher.aes(hashedSecretA);

Теперь возможно шифрование данных между сторонами, которые никогда напрямую не передавали ключ.


Полный цикл вычисления общего секрета

Типичный процесс обмена выглядит следующим образом:

  1. Генерация ключевых пар
  2. Обмен публичными ключами
  3. Вычисление ECDH-значения
  4. Хеширование общего секрета
  5. Получение симметричного ключа

Внутреннее представление данных

SJCL оперирует несколькими уровнями абстракции:

  • sjcl.bn — большие числа
  • объекты точек эллиптической кривой
  • сериализованные представления публичных ключей

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

S = a * B = b * A

где:

  • a, b — приватные ключи
  • A, B — публичные точки

Особенности реализации dh() в SJCL

Метод dh():

  • выполняет скалярное умножение точки кривой
  • возвращает результат в формате, совместимом с криптографическими хешами
  • не предназначен для прямого использования без KDF

Пример:

const sharedPoint = keypairA.sec.dh(keypairB.pub);

Проблема сырого секрета

Без дополнительной обработки общий секрет может:

  • содержать избыточную структуру
  • иметь предсказуемые биты в зависимости от кривой
  • быть уязвимым к атаке через повторное использование

Поэтому используется KDF-подход: хеширование или PBKDF2.


Использование PBKDF2 для усиления секрета

SJCL содержит реализацию PBKDF2:

const derivedKey = sjcl.misc.pbkdf2(secretA, "salt", 1000, 256);

Параметры:

  • salt — дополнительная энтропия
  • число итераций — усложнение атак перебора
  • длина ключа — итоговый размер

Совместимость с протоколами обмена

Механизм ECDH в SJCL используется как базовый строительный блок для:

  • TLS-подобных протоколов
  • end-to-end шифрования сообщений
  • генерации сессионных ключей

Сериализация публичных ключей

Для передачи публичного ключа используется JSON-представление:

const exported = sjcl.ecc.elGamal.publicKey.prototype.toJSON.call(keypairA.pub);

И обратное восстановление:

const restored = new sjcl.ecc.elGamal.publicKey(256, exported);

Ошибки при вычислении общего секрета

Типичные проблемы:

  • несоответствие кривых у сторон
  • повторное использование приватного ключа
  • отсутствие нормализации результата dh()
  • попытка использовать необработанный результат как AES-ключ

Устойчивость схемы

Безопасность вычисления общего секрета в SJCL основана на:

  • сложности задачи дискретного логарифмирования на эллиптических кривых
  • невозможности восстановить приватный ключ из публичного
  • однонаправленности операции dh()

Промежуточное представление данных в памяти

При выполнении dh():

  • создаются временные объекты точек
  • выполняются операции сложения и умножения в поле
  • результат сериализуется в массив слов

Это важно для понимания производительности в JavaScript-среде.


Интеграция с другими модулями SJCL

Общий секрет часто передаётся в:

  • sjcl.cipher.aes
  • sjcl.mode.gcm
  • sjcl.codec для сериализации

Типичный поток:

ECDH → SHA256 → AES key → encryption

Криптографическая связка операций

Полный криптографический конвейер в SJCL для общего секрета:

KeyGen → PublicKeyExchange → DH → KDF → SymmetricCrypto

Каждый этап снижает риск утечки информации и повышает стойкость всей схемы.