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

libsecp256k1 (Bitcoin Core)

libsecp256k1 из Bitcoin Core — эталонная реализация secp256k1: непрозрачные подписи, которые сериализуются в компактный формат или строгий DER, nonce по RFC 6979, low-S и при подписи, и при проверке, восстановление открытого ключа и Schnorr по BIP-340.
Лицензия
MIT
Проверенная версия
0.5.0 ·
Исходный код
github.com/bitcoin-core/secp256k1
Документация
github.com/bitcoin-core/secp256k1/tree/master/include

Совместимость вкратце

ECDSA

EdDSA
none
Форматы открытых ключей
sec1-uncompressedsec1-compressed

libsecp256k1 поддерживает одну кривую и делает это тщательно: подпись с постоянным временем выполнения, исчерпывающие тесты и формальная проверка корректности части арифметики поля. Её используют Bitcoin Core, многие кошельки и привязки, например coincurve и бэкенд cgo в go-ethereum. Факты на этой странице проверены на пакете Alpine 3.24.2: версия 0.5.0 с модулями recovery, extrakeys и schnorrsig; сам проект к тому времени выпустил уже 0.8.0.

Подписи хранятся в непрозрачной структуре; кодировка выбирается при сериализации (serialize_compact даёт 64 байта r‖s, serialize_der — DER), и разбор так же явный. parse_der строгий: следуя правилам BIP-66 из Bitcoin, он принял минимальный DER и отклонил ту же подпись с лишним ведущим нулём в r. Для старых подписей с нестрогой кодировкой нужен нестрогий парсер, который лежит в каталоге contrib репозитория.

Функция подписи принимает 32-байтовый хеш (сама библиотека сообщение никогда не хеширует), использует RFC 6979 (результат совпал с независимой реализацией) и всегда выдаёт low-S. Строгая часть — проверка: зеркальная форма high-S верной подписи проверку не прошла, а secp256k1_ecdsa_signature_normalize существует именно для того, чтобы исправлять подписи от универсальных реализаций. Если подпись из OpenSSL, Java или облачного KMS проходит проверку примерно в половине случаев, причина в high-S; пример нормализует подпись перед проверкой.

Открытые ключи — точки SEC1: 65 байт в несжатой форме или 33 байта в сжатой; 64-байтовое значение x‖y отклоняется, а SPKI, PEM и JWK не поддерживаются. Модуль recovery восстанавливает открытый ключ по подписи (на этом основаны адреса Ethereum), а модуль schnorrsig реализует BIP-340. Для подписей сообщений Bitcoin есть проверка сообщений Bitcoin, для событий Nostr — проверка событий Nostr.

Факты и проверки, на которых они основаны

Ничего из приведённого ниже не скопировано из документации. 20 октября 2026 г. проверка content/libraries/verify/libsecp256k1/run.sh запустила libsecp256k1 (Bitcoin Core) 0.5.0 в образе контейнера alpine:3.24.2; содержимое страницы должно совпадать с выводом проверки, а у каждого факта приведены соответствующие строки вывода.

  • Кодировка подписи ECDSA

    structured

    Подпись хранится в непрозрачной структуре; serialize_compact записывает 64 байта r‖s, а serialize_der — DER.

    Что записала проверка
    fact ecdsa-signature-encoding = structured
    evidence ecdsa-signature-encoding: secp256k1_ecdsa_signature is opaque: serialize_compact gives 64 bytes, serialize_der 70 bytes
  • Nonce в ECDSA

    rfc6979

    RFC 6979: с учётом нормализации s подпись совпала с результатом независимой реализации RFC 6979.

    Что записала проверка
    fact ecdsa-nonce = rfc6979
    evidence ecdsa-nonce: two signatures identical: yes; equal to the reference RFC 6979 signature with s normalized: yes
  • Low-S при подписи

    low-s

    Среди 64 подписей нет ни одного значения high-S.

    Что записала проверка
    fact ecdsa-low-s-sign = low-s
  • High-S при проверке

    rejects-high-s

    secp256k1_ecdsa_verify отклонила (r, n − s); после secp256k1_ecdsa_signature_normalize подпись прошла проверку.

    Что записала проверка
    fact ecdsa-low-s-verify = rejects-high-s
    evidence ecdsa-low-s-verify: verify on (r, n - s): 0; signature_normalize reports it was high: 1; normalized verifies: 1
  • Что хеширует вызов подписи

    takes-digest

    secp256k1_ecdsa_sign принимает 32-байтовый хеш сообщения.

    Что записала проверка
    fact ecdsa-prehash = takes-digest
    evidence ecdsa-prehash: secp256k1_ecdsa_sign takes a 32-byte message hash (msghash32)
  • Хеш-функция по умолчанию

    none

    Внутри функций подписи и проверки хеширование не выполняется.

    Что записала проверка
    fact ecdsa-default-hash = none
  • Строгость разбора DER

    rejects-non-minimal

    parse_der принял минимальный DER и отклонил кодировку с лишним ведущим нулевым байтом в r.

    Что записала проверка
    fact der-strictness = rejects-non-minimal
    evidence der-strictness: parse_der on minimal DER: 1; with a redundant leading zero in r: 0
  • Некорректная подпись ECDSA

    returns-error

    Некорректные входные данные отсеиваются на этапе разбора (parse_der возвращает 0); в verify попадают только разобранные подписи.

    Что записала проверка
    fact ecdsa-malformed-signature = returns-error
    evidence ecdsa-malformed-signature: parsing is a separate call: parse_der on 3 bytes returned 0; verify only ever sees parsed signatures
  • Кривые ECDSA

    secp256k1

    Только secp256k1.

    Что записала проверка
    fact ecdsa-curves = secp256k1
  • Восстановление открытого ключа

    supported

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

    Что записала проверка
    fact public-key-recovery = supported
  • Подписи Schnorr

    BIP-340

    Модуль schnorrsig создал подпись BIP-340 и проверил её по открытому ключу x-only.

    Что записала проверка
    fact schnorr = BIP-340
  • EdDSA

    none

    EdDSA нет.

    Что записала проверка
    fact eddsa = none
  • Постквантовые алгоритмы

    none

    Постквантовых алгоритмов нет.

    Что записала проверка
    fact pqc = none
  • Форматы открытых ключей

    sec1-uncompressedsec1-compressed

    ec_pubkey_parse принимает 65-байтовые несжатые и 33-байтовые сжатые точки SEC1; 64-байтовое значение x‖y было отклонено.

    Что записала проверка
    fact public-key-formats = sec1-uncompressed, sec1-compressed
    evidence public-key-formats: ec_pubkey_parse: 65-byte uncompressed 1, 33-byte compressed 1, 64-byte x||y 0
  • Форматы закрытых ключей

    raw-scalar

    Секретные ключи — 32-байтовые скаляры в формате raw.

    Что записала проверка
    fact private-key-formats = raw-scalar

Использование

Подпишите 32-байтовый хеш, разберите подпись в том виде, в каком она пришла бы из другой системы, нормализуйте s и проверьте подпись.
#include <secp256k1.h>
#include <stdio.h>
#include <string.h>

int main(void) {
    secp256k1_context *ctx = secp256k1_context_create(SECP256K1_CONTEXT_NONE);
    unsigned char seckey[32], msghash[32];
    memset(seckey, 0x11, 32);   /* use a random 32-byte key in real code */
    memset(msghash, 0x22, 32);  /* the caller hashes: libsecp256k1 signs a 32-byte digest */

    secp256k1_pubkey pub;
    secp256k1_ecdsa_signature sig;
    if (!secp256k1_ec_pubkey_create(ctx, &pub, seckey) || !secp256k1_ecdsa_sign(ctx, &sig, msghash, seckey, NULL, NULL)) return 1;

    /* A signature from elsewhere: parse (strict DER or 64-byte compact), then normalize s,
       because verify rejects high-S signatures that OpenSSL or Java produce half the time. */
    unsigned char compact[64];
    secp256k1_ecdsa_signature_serialize_compact(ctx, compact, &sig);
    secp256k1_ecdsa_signature parsed;
    if (!secp256k1_ecdsa_signature_parse_compact(ctx, &parsed, compact)) return 1;
    secp256k1_ecdsa_signature_normalize(ctx, &parsed, &parsed);

    printf("verified: %d\n", secp256k1_ecdsa_verify(ctx, &parsed, msghash, &pub));
    secp256k1_context_destroy(ctx);
    return 0;
}

Тестовый стенд для фрагментов кода запускает каждый пример без изменений в образе контейнера alpine:3.24.2, и запуск завершается ошибкой, если вывод не совпадает.

Подготовка
apk add --no-cache -q build-base libsecp256k1-dev=0.5.0-r1 >/dev/null
Команда
cc -o usage usage.c -lsecp256k1 && ./usage
Вывод должен совпадать (регулярное выражение)
verified: 1

Библиотеки