Подписи в Ethereum: ECDSA, ecrecover, personal_sign и EIP-712
Как устроена подпись (r, s, v), как контракт проверяет её через ecrecover, чем различаются personal_sign и EIP-712, что такое permit и EIP-1271.
В этой статье
В статье криптография с открытым ключом мы узнали, что подпись доказывает авторство. Теперь посмотрим, как она устроена в Ethereum, как её проверяет контракт и какие ловушки подстерегают разработчика и пользователя.
Формат подписи
Подпись ECDSA на кривой secp256k1 состоит из трёх значений:
r- 32 байта;s- 32 байта;v- 1 байт (27 или 28; иногда 0 или 1) - «подсказка», какой из двух возможных открытых ключей имелся в виду.
Всего 65 байт. Подписывается всегда хеш сообщения (32 байта), а не само сообщение.
ecrecover: восстановление подписанта
В EVM есть встроенная функция (прекомпилят по адресу 0x01) ecrecover(hash, v, r, s), которая по хешу и подписи возвращает адрес подписавшего:
address signer = ecrecover(digest, v, r, s);
require(signer != address(0) && signer == expectedSigner, "bad signature");Важно: при неверной подписи ecrecover возвращает нулевой адрес, а не ошибку. Если не проверить != address(0), а ожидаемый подписант по ошибке окажется нулевым, подпись «пройдёт». Поэтому в реальных контрактах используют проверенные библиотеки (например, ECDSA из OpenZeppelin).
Ловушка: изменяемость подписи
Для каждой подписи (r, s) существует «зеркальная» (r, n - s), тоже валидная. Это делает подпись изменяемой (malleable): злоумышленник может получить другую валидную подпись, не зная ключа. Если контракт использует хеш подписи как идентификатор («эта подпись уже использована»), такой приём обходит защиту. Решение: принимать только «нижнюю» половину s (требование EIP-2) и защищаться от повтора по nonce, а не по хешу подписи.
personal_sign: безопасная обёртка для сообщений
Если подписывать произвольные 32 байта, их можно выдать за хеш транзакции, и пользователь невольно подпишет транзакцию. Поэтому для текстовых сообщений кошельки добавляют префикс:
"\x19Ethereum Signed Message:\n" + длина + сообщениеПодписывается хеш от этой строки. Префикс гарантирует, что подпись нельзя использовать как подпись транзакции. Так работает вход через кошелёк («Sign-In with Ethereum»).
Минус: пользователь видит набор символов или простой текст и не может увидеть структуру данных.
EIP-712: структурированные данные
EIP-712 решает проблему «слепой подписи». Подписываемая структура имеет тип и поля, и кошелёк показывает их читаемо: получатель, сумма, срок.
Хеш для подписи строится так:
digest = keccak256("\x19\x01" ‖ domainSeparator ‖ hashStruct(message))Составные части:
- hashStruct - хеш данных с учётом типа структуры.
- domainSeparator - хеш «домена»:
name,version,chainId,verifyingContract.
Домен нужен, чтобы подпись была привязана к конкретному приложению и сети. Подпись, сделанная для контракта A на сети 1, не пройдёт для контракта B или для той же копии на сети 8453. Это защита от межсетевого и межконтрактного повтора (почему chainId важен).
Защита от повтора внутри контракта
Даже с доменом ту же подпись можно прислать дважды. Поэтому в подписываемых данных обычно есть:
- nonce - порядковый номер, который контракт увеличивает после использования;
- deadline - срок годности подписи.
Контракт проверяет их, и повтор невозможен.
Пример: permit (EIP-2612)
Обычно перевод токенов контракту требует двух транзакций: approve, затем вызов (см. approve). Токены со стандартом permit позволяют сделать это одной: владелец подписывает сообщение EIP-712 Permit(owner, spender, value, nonce, deadline), а контракт передаёт подпись токену, и разрешение выдаётся без отдельной транзакции от владельца.
token.permit(owner, spender, value, deadline, v, r, s);
token.transferFrom(owner, address(this), value);Удобно, но и опасно: мошенник может уговорить подписать Permit, и тогда токены можно списать без единой транзакции от вас (атаки). Смотрите в окне кошелька, кому и сколько разрешено.
EIP-1271: подписи контрактных кошельков
У смарт-кошелька (например, мультиподписи) нет закрытого ключа, поэтому ecrecover для него не работает. Стандарт EIP-1271 вводит функцию isValidSignature(hash, signature), которую вызывают на адресе-контракте: если она вернёт магическое значение 0x1626ba7e, подпись считается верной. Контракты, принимающие подписи, должны поддерживать оба способа: ecrecover для обычных адресов и EIP-1271 для контрактов.
Мета-транзакции
Подпись позволяет одному человеку (релееру) отправить в сеть транзакцию от имени другого и заплатить газ. Пользователь лишь подписывает намерение. Это основа «бесгазовых» операций и абстракции аккаунтов.
Ключевые выводы
- Подпись всегда относится к хешу; важно, что именно хешируется.
- Используйте EIP-712: читаемая структура + привязка к домену.
- Всегда включайте
nonceиdeadline, проверяйтеchainId. - Не доверяйте голому
ecrecover: проверяйте нулевой адрес и формуs. - Пользователь должен читать, что подписывает.
Что почитать дальше
Увидим, как вся эта математика применяется в реальной схеме контролируемых прав: шаблоны контроля доступа. Или обратимся к самой частой уязвимости контрактов: каталог уязвимостей.
Читайте также
- 1 · Основы
Криптография с открытым ключом: как подпись доказывает, что это вы
Как работают закрытый и открытый ключи, откуда берётся адрес, что такое цифровая подпись ECDSA и почему кошельку не нужен пароль. С наглядным примером.
3 мин чтения - 3 · Под капотом
Абстракция аккаунтов (ERC-4337): умные кошельки и спонсирование газа
Что такое абстракция аккаунтов и ERC-4337: UserOperation, bundler, paymaster, преимущества смарт-кошелька перед EOA, соцвосстановление и риски.
3 мин чтения
Упоминается в статьях
- Хеш-функции простыми словами: отпечаток данных, который нельзя подделать
- Криптография с открытым ключом: как подпись доказывает, что это вы
- Словарь терминов: блокчейн, кошельки, смарт-контракты и эскроу от A до Я
- Фишинг и отравление адреса: как работают атаки и как от них защититься
- События и логи: eth_getLogs, bloom-фильтры и ограничения публичных RPC
- ERC-20 с нуля: пишем токен на Solidity и разбираем каждую строку
- Шаблоны контроля доступа в смарт-контрактах: owner, роли и стороны сделки
- Каталог уязвимостей смарт-контрактов: 12 классических ошибок и защита от них
- Абстракция аккаунтов (ERC-4337): умные кошельки и спонсирование газа
- HD-кошельки: как из 12 слов получаются все ваши ключи (BIP-39, BIP-32, BIP-44)
Готовы попробовать?
Создайте первую сделку за пару минут. Деньги блокируются в смарт-контракте и выплачиваются по правилам, которые видны заранее.

