ECIES: Elliptic Curve Integrated Encryption Scheme

ECIES (Elliptic Curve Integrated Encryption Scheme) — гибридная криптографическая схема, сочетающая преимущества асимметричного шифрования на эллиптических кривых и симметричных алгоритмов. Основная цель — обеспечить конфиденциальность, целостность и аутентичность передаваемых данных.

Ключевая идея:

  • Эллиптические кривые используются для безопасного согласования общего секрета
  • Симметричное шифрование применяется для обработки данных
  • MAC (Message Authentication Code) гарантирует целостность

Структура ECIES:

  1. Генерация эфемерной пары ключей
  2. Вычисление общего секрета (ECDH)
  3. Производство ключей (KDF)
  4. Шифрование сообщения
  5. Генерация MAC

Поддержка ECIES в SJCL

В библиотеке SJCL отсутствует прямой «высокоуровневый» API ECIES, однако все необходимые примитивы присутствуют:

  • Эллиптические кривые: sjcl.ecc
  • Обмен ключами: ECDH
  • KDF: sjcl.misc.hkdf или PBKDF2
  • Симметричное шифрование: AES
  • MAC: HMAC

Это требует ручной сборки схемы ECIES.


Генерация ключей

Используется стандартная генерация ключей на эллиптической кривой:

var keys = sjcl.ecc.elGamal.generateKeys(256);
var publicKey = keys.pub;
var privateKey = keys.sec;

Поддерживаемые кривые:

  • c192
  • c224
  • c256 (наиболее распространённый выбор)
  • c384
  • c521

Эфемерный ключ

Для каждого шифрования создаётся временная пара ключей:

var ephemeral = sjcl.ecc.elGamal.generateKeys(256);

Это обеспечивает:

  • прямую секретность (forward secrecy)
  • защиту от повторного использования ключей

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

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

var sharedSecret = ephemeral.sec.dh(publicKey);

Результат — битовый массив (bitArray), который нельзя использовать напрямую.


Производство ключей (KDF)

Общий секрет преобразуется в ключи шифрования и MAC:

var derivedKey = sjcl.misc.hkdf(sharedSecret, 256);

Разделение ключей:

var encryptionKey = sjcl.bitArray.bitSlice(derivedKey, 0, 128);
var macKey = sjcl.bitArray.bitSlice(derivedKey, 128, 256);

Шифрование сообщения

Используется AES (обычно в режиме CCM или GCM):

var iv = sjcl.random.randomWords(4);
var plaintext = sjcl.codec.utf8String.toBits("secret message");

var ciphertext = sjcl.mode.ccm.encrypt(
    new sjcl.cipher.aes(encryptionKey),
    plaintext,
    iv
);

Генерация MAC

Для защиты целостности:

var mac = new sjcl.misc.hmac(macKey).encrypt(ciphertext);

Итоговый формат сообщения

Шифротекст состоит из:

  • публичного эфемерного ключа
  • IV
  • ciphertext
  • MAC

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

{
    ephemeralPub: ephemeral.pub.get(),
    iv: iv,
    ciphertext: ciphertext,
    mac: mac
}

Расшифрование

1. Восстановление эфемерного ключа

var ephemeralPub = new sjcl.ecc.elGamal.publicKey(
    sjcl.ecc.curves.c256,
    received.ephemeralPub
);

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

var sharedSecret = privateKey.dh(ephemeralPub);

3. Производство ключей

var derivedKey = sjcl.misc.hkdf(sharedSecret, 256);

var encryptionKey = sjcl.bitArray.bitSlice(derivedKey, 0, 128);
var macKey = sjcl.bitArray.bitSlice(derivedKey, 128, 256);

4. Проверка MAC

var computedMac = new sjcl.misc.hmac(macKey).encrypt(received.ciphertext);

if (!sjcl.bitArray.equal(computedMac, received.mac)) {
    throw new Error("MAC verification failed");
}

5. Расшифрование

var plaintext = sjcl.mode.ccm.decrypt(
    new sjcl.cipher.aes(encryptionKey),
    received.ciphertext,
    received.iv
);

var message = sjcl.codec.utf8String.fromBits(plaintext);

Важные аспекты безопасности

Разделение ключей

Использование одного ключа для шифрования и MAC недопустимо. ECIES строго требует разделения.

Эфемерные ключи

Каждая операция шифрования должна использовать новый ключ.

Проверка MAC перед расшифрованием

Это защищает от атак типа padding oracle и модификации данных.

Криптографически стойкий генератор

Используется:

sjcl.random.randomWords(...)

Перед использованием необходимо инициализировать энтропию.


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

Работа с bitArray

Все данные представлены в формате sjcl.bitArray. Для конверсии:

sjcl.codec.utf8String.toBits(str);
sjcl.codec.hex.fromBits(bits);

Отсутствие встроенного ECIES

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

  • увеличивает гибкость
  • требует аккуратной реализации

Выбор режима AES

Рекомендуется:

  • CCM (встроен в SJCL)
  • GCM (при наличии поддержки)

Расширения ECIES

Добавление ассоциированных данных (AAD)

Позволяет аутентифицировать дополнительные данные:

sjcl.mode.ccm.encrypt(cipher, data, iv, aad);

Контекстная привязка

KDF может включать:

  • идентификаторы сторон
  • метаданные протокола

Поддержка разных кривых

Можно выбирать разные уровни безопасности:

sjcl.ecc.curves.c384
sjcl.ecc.curves.c521

Типичные ошибки

  • Повторное использование IV
  • Использование одного ключа для всех операций
  • Отсутствие проверки MAC
  • Неправильное разделение ключей
  • Прямое использование sharedSecret без KDF

Практическая схема ECIES в SJCL

Сводный алгоритм:

  1. Генерация эфемерного ключа
  2. ECDH → общий секрет
  3. HKDF → два ключа
  4. AES → шифрование
  5. HMAC → аутентификация
  6. Передача: (ephemeralPub, iv, ciphertext, mac)

Расшифрование:

  1. Восстановление эфемерного ключа
  2. ECDH
  3. HKDF
  4. Проверка MAC
  5. AES decrypt

Преимущества ECIES

  • Высокая производительность (за счёт симметричного шифрования)
  • Компактные ключи (ECC)
  • Forward secrecy
  • Гибкость конфигурации

Ограничения

  • Требует аккуратной реализации
  • Нет встроенного API в SJCL
  • Ошибки в KDF или MAC критичны

Минимальный пример структуры

function eciesEncrypt(pubKey, message) {
    var eph = sjcl.ecc.elGamal.generateKeys(256);
    var secret = eph.sec.dh(pubKey);

    var key = sjcl.misc.hkdf(secret, 256);
    var encKey = sjcl.bitArray.bitSlice(key, 0, 128);
    var macKey = sjcl.bitArray.bitSlice(key, 128, 256);

    var iv = sjcl.random.randomWords(4);
    var pt = sjcl.codec.utf8String.toBits(message);

    var ct = sjcl.mode.ccm.encrypt(new sjcl.cipher.aes(encKey), pt, iv);
    var mac = new sjcl.misc.hmac(macKey).encrypt(ct);

    return {
        eph: eph.pub.get(),
        iv: iv,
        ct: ct,
        mac: mac
    };
}

Такой подход формирует полноценную реализацию ECIES на основе примитивов SJCL.