Перейти к содержимому
ecdsa.com

ECDSA или EdDSA: выбор между P-256 и Ed25519 для подписей

Обе схемы — подписи на эллиптических кривых с 64-байтным результатом и 128-битной стойкостью. Различаются они тем, насколько безопасно ведут себя при сбоях, где поддерживаются и насколько строго совпадают решения разных проверяющих сторон.

Когда сравнивают ECDSA и EdDSA, обычно имеют в виду ECDSA на кривой P-256 и EdDSA на Ed25519: на этих двух схемах подписи построены почти все современные системы, использующие эллиптические кривые. Они одинаково стойкие и одинаково компактные, поэтому выбор определяется безопасностью реализации, с одной стороны, и поддержкой в экосистеме — с другой.

Коротко о главном

ECDSA (P-256)EdDSA (Ed25519)
СтандартFIPS 186-5, § 6; SEC 1; ANSI X9.62RFC 8032; FIPS 186-5, § 7 (допускается с 2023 года)
Форма кривойКороткая форма Вейерштрасса, простой порядок (кофактор 1)Скрученная форма Эдвардса, кофактор 8
Стойкостьоколо 128 битоколо 128 бит
Закрытый ключ32-байтный скаляр32-байтный seed
Открытый ключ65 байт в несжатой форме, 33 в сжатой, 91 в виде SPKI DER32 байта, 44 в виде SPKI DER
Подпись64 байта в формате raw r‖s, 70–72 байта в DER64 байта R‖S, единственная кодировка
NonceСлучайный для каждой подписи или детерминированный по RFC 6979По построению выводится из ключа и сообщения
ПластичностьДа: (r, n − s) тоже проходит проверкуНет, если проверяющая сторона требует, чтобы S было меньше порядка группы
Подписей / проверок в секунду, OpenSSL 3.6.5около 87 000 / 29 000около 60 000 / 25 000
Подписей / проверок в секунду, @noble/curves 2.3около 3 800 / 870около 3 100 / 1 280
JOSE / COSEES256 / −7Ed25519 (RFC 9864) / −19, прежний EdDSA / −8
Схема в TLS 1.3ecdsa_secp256r1_sha256 (0x0403)ed25519 (0x0807)
Публично доверенные TLS-сертификатыДаНет (Baseline Requirements допускают только RSA и P-256/384/521)

Скорость измерялась на одном ядре Apple M4 Max с помощью OpenSSL 3.6.5 (openssl speed), а для строки с чистым JavaScript — с помощью @noble/curves 2.3.0 на Node.js 26.10.

Чем различается устройство схем

Обе схемы — подписи на эллиптических кривых, основанные на задаче дискретного логарифмирования, но их разработали с разницей примерно в 20 лет (ECDSA — в 1992 году, EdDSA — в 2011-м), и это заметно.

Nonce

Каждая подпись ECDSA расходует своё секретное одноразовое число — nonce k. Если k повторится для двух сообщений или всего лишь окажется смещённым на несколько бит во множестве подписей, закрытый ключ можно вычислить; именно так утёк ключ подписи PlayStation 3 и так были опустошены многие криптовалютные кошельки. FIPS 186-5 допускает либо случайный k из одобренного генератора, либо детерминированный вывод по RFC 6979, и большинство современных библиотек по умолчанию используют один из этих способов, но сама возможность такого отказа заложена в схему.

EdDSA устраняет эту проблему: nonce — это хеш от половины секретного ключа и сообщения (RFC 8032, § 5.1.6). Если подписать одно и то же сообщение дважды, получится одна и та же подпись, а разные сообщения всегда получают разные nonce, причём генератор случайных чисел в этом не участвует. Обратная сторона — атаки с внесением сбоев: сбой при повторном подписании того же сообщения может раскрыть ключ, поэтому некоторые аппаратные кошельки и постквантовый стандарт ML-DSA предпочитают хеджированное подписание, в котором подмешиваются ещё и свежие случайные данные.

Кодировка и пластичность

У подписи ECDSA две кодировки: raw r‖s (JOSE, WebCrypto, COSE, PKCS #11) и ASN.1 DER SEQUENCE (X.509, TLS, OpenSSL, Java, assertion в WebAuthn). Путаница между ними — самая распространённая причина ошибки «invalid signature», и руководство по DER и raw подробно её разбирает. Кроме того, подписи ECDSA пластичны: любой может превратить (r, s) в столь же верную (r, n − s), поэтому Bitcoin и Ethereum требуют low-S.

У Ed25519 одна 64-байтная кодировка, а RFC 8032, § 5.1.7 требует, чтобы S было меньше порядка группы, и это устраняет очевидную пластичность.

Граничные случаи проверки

Проверка ECDSA определена однозначно: убедиться, что r и s лежат в диапазоне 1…n−1, вычислить u₁·G + u₂·Q и сравнить координату x результата с r. Пропуск проверки диапазона фатален: Java версий 15–18 принимала подпись из одних нулей для любого сообщения (CVE-2022-21449).

С проверкой EdDSA связана более тонкая проблема. Поскольку у Edwards25519 кофактор 8, RFC 8032 допускает как проверку с кофактором [8][S]B = [8]R + [8][k]A, так и проверку без кофактора [S]B = R + [k]A, а ещё библиотеки по-разному обрабатывают точки малого порядка и кодировки, отличные от канонической формы. Для честно созданных подписей результаты совпадают, для специально подобранных входных данных — нет. Системы, в которых многие участники должны одинаково решать, действительна ли подпись (например, блокчейны), закрепляют один конкретный набор правил; обычно выбирают ZIP 215 от Zcash.

Где какая схема поддерживается

ECDSA P-256 — наименьший общий знаменатель для оборудования и веб-платформы. Эту схему поддерживает WebCrypto в любом браузере, её предлагают (часто как единственный вариант) Secure Enclave от Apple и большинство TPM, HSM, смарт-карт и типов ключей в облачных KMS, ключи доступа по умолчанию используют именно её (COSE −7), а Baseline Requirements CA/Browser Forum (§ 6.1.5) для публично доверенных TLS-сертификатов допускают только RSA и ECDSA на P-256, P-384 и P-521.

В программных экосистемах доминирует Ed25519. OpenSSH по умолчанию генерирует ключи Ed25519 начиная с версии 9.5 (октябрь 2023 года); Signal, WireGuard, Tor, age и большинство новых протоколов построены на кривых 25519 (X25519 для обмена ключами, Ed25519 для подписей); ключи OpenPGP v6 поддерживают Ed25519 нативно (RFC 9580); а WebCrypto поддерживает эту схему начиная с Firefox 129, Safari 17 и Chrome 137. В JOSE RFC 9864 заменяет полиморфный идентификатор EdDSA полностью определённым Ed25519.

Для систем, сертифицированных по FIPS, обе схемы одобрены в FIPS 186-5, хотя старые сертифицированные модули могут не поддерживать EdDSA, так как появились раньше. В долгосрочной перспективе обе схемы не устоят перед квантовым компьютером, и NIST IR 8547 (пока это черновик) предлагает запретить обе после 2035 года.

Производительность

Принято считать, что Ed25519 намного быстрее ECDSA. Так было, пока реализации P-256 были универсальными; к сегодняшнему OpenSSL, в котором есть вручную оптимизированный код для P-256, это не относится. На одном ядре Apple M4 Max OpenSSL 3.6.5 создавал около 87 000 подписей P-256 в секунду против примерно 60 000 для Ed25519 и проверял около 29 000 подписей против 25 000. На чистом JavaScript (@noble/curves 2.3.0) Ed25519 проверяет подписи примерно на 50 % быстрее, а P-256 немного быстрее подписывает. В любом случае речь идёт о микросекундах, и это редко на что-то влияет; важно то, что обе схемы на один-два порядка быстрее подписи RSA.

Скрипт ниже, запущенный на Node.js 26.10, в нескольких строках показывает практические различия.

Node.js: размеры и детерминированность подписей Ed25519 и ECDSA P-256
import { generateKeyPairSync, sign, verify } from "node:crypto";

const message = Buffer.from("transfer 100 to alice");

const ed = generateKeyPairSync("ed25519");
const ec = generateKeyPairSync("ec", { namedCurve: "P-256" });

// Ed25519: the algorithm argument is null because the hash is fixed (SHA-512).
const ed1 = sign(null, message, ed.privateKey);
const ed2 = sign(null, message, ed.privateKey);

// ECDSA P-256 with SHA-256, raw r||s output as used by JOSE and WebCrypto.
const ec1 = sign("sha256", message, { key: ec.privateKey, dsaEncoding: "ieee-p1363" });
const ec2 = sign("sha256", message, { key: ec.privateKey, dsaEncoding: "ieee-p1363" });

console.log("Ed25519 signature bytes:", ed1.length, "deterministic:", ed1.equals(ed2));
console.log("ECDSA P-256 signature bytes:", ec1.length, "deterministic:", ec1.equals(ec2));
console.log("Ed25519 public key (SPKI DER) bytes:", ed.publicKey.export({ type: "spki", format: "der" }).length);
console.log("P-256 public key (SPKI DER) bytes:", ec.publicKey.export({ type: "spki", format: "der" }).length);
console.log("both verify:", verify(null, message, ed.publicKey, ed1) && verify("sha256", message, { key: ec.publicKey, dsaEncoding: "ieee-p1363" }, ec1));
Результат
Ed25519 signature bytes: 64 deterministic: true
ECDSA P-256 signature bytes: 64 deterministic: false
Ed25519 public key (SPKI DER) bytes: 44
P-256 public key (SPKI DER) bytes: 91
both verify: true

ECDSA в Node подписывает со случайными nonce, поэтому две подписи одного и того же сообщения различаются, но обе проходят проверку. Инструмент проверки подписей принимает обе схемы и показывает, какую кодировку он распознал.

Что когда выбирать

  • Пользовательские ключи и ключи хостов SSH: Ed25519 или ed25519-sk с ключом безопасности FIDO. ECDSA используйте только для старых серверов или оборудования, которое не поддерживает Ed25519.
  • Ключи доступа и WebAuthn: принимайте обе схемы (COSE −7 и −8 или −19), но рассчитывайте на то, что платформенные аутентификаторы выдадут ES256.
  • Публично доверенные TLS-сертификаты: ECDSA P-256 (или P-384); Baseline Requirements не допускают Ed25519.
  • JWT и API-токены: ES256 — для максимальной совместимости и для ключей, которые хранятся в облачном KMS; Ed25519 — там, где все получатели токенов поддерживают RFC 8037 или RFC 9864. Подробнее об этом — на странице ES256 или EdDSA для JWT.
  • Ключи в Secure Enclave, TPM, смарт-картах или большинстве HSM: ECDSA P-256, потому что именно эту схему предлагает оборудование.
  • Блокчейны: здесь выбор не за вами; следуйте правилам сети (ECDSA или Schnorr на secp256k1 для Bitcoin и Ethereum, Ed25519 для Solana).
  • Новые протоколы, в которых от проверки подписи зависит консенсус: Ed25519 с явно заданным правилом проверки, например ZIP 215.

Заметки о миграции

Ключ P-256 нельзя преобразовать в ключ Ed25519 и наоборот: у них разные кривые и разные форматы ключей. Переход с одного на другой — это ротация ключей.

  1. Генерируйте новые ключи там, где они будут храниться. С помощью openssl genpkey -algorithm ED25519 или ssh-keygen -t ed25519 либо внутри HSM, если он поддерживает Ed25519. Прежде чем планировать переход, проверьте поддержку в прошивке.
  2. На переходный период опубликуйте оба ключа. В JWKS задайте каждому ключу собственные kid и alg; в authorized_keys добавьте новую строку, прежде чем удалять старую; для сертификатов запросите новый сертификат на новый ключ.
  3. Обновите проверяющие стороны раньше подписантов. Проверяющие стороны должны принимать новый идентификатор алгоритма (Ed25519 или EdDSA в JOSE, OID 1.3.101.112 в X.509, ssh-ed25519 в SSH), но по-прежнему сверяться с закреплённым списком разрешённых алгоритмов и никогда не позволять токену или сообщению выбирать алгоритм.
  4. Переключите подписание на новый ключ, затем выведите старый из оборота, когда истечёт срок действия всего, что им подписано.
  5. Следите за кодировками. Код для ECDSA часто преобразует подписи между DER и raw; код для Ed25519 этого делать не должен, а размер «закрытого ключа» Ed25519 зависит от API (32-байтный seed или 64 байта: seed плюс открытый ключ).

Обратный переход, с Ed25519 на ECDSA, случается в основном тогда, когда ключи переносят в оборудование. Используйте детерминированные или хеджированные nonce, требуйте low-S, если подписи служат идентификаторами, и проверьте обработку DER и raw с помощью конвертера DER и raw.

Источники

Все сравнения