Как проверить подпись, сделанную кошельком Trezor - PullRequest
0 голосов
/ 11 января 2020

Я хочу проверить сообщение, подписанное моим аппаратным кошельком trezor. В основном у меня есть эта информация.

.venv/bin/trezorctl btc get-public-node -n 0
Passphrase required: 
Confirm your passphrase: 
node.depth: 1
node.fingerprint: ea66f037
node.child_num: 0
node.chain_code: e02030f2a7dfb474d53a96cb26febbbe3bd3b9756f4e0a820146ff1fb4e0bd99
node.public_key: 026b4cc594c849a0d9a124725997604bc6a0ec8f100b621b1eaed4c6094619fc46
xpub: xpub69cRfCiJ5BVzesfFdsTgEb29SskY74wYfjTRw5kdctGN2xp1HF4udTP21t68PAQ4CBq1Rn3wAsWr84wiDiRmmSZLwkEkv4qK5T5Y7EXebyQ

$ .venv/bin/trezorctl btc sign-message 'aaa' -n 0
Please confirm action on your Trezor device
Passphrase required: 
Confirm your passphrase: 
message: aaa
address: 17DB2Q3oZVkQAffkpFvF4cwsXggu39iKdQ
signature: IHQ7FDJy6zjwMImIsFcHGdhVxAH7ozoEoelN2EfgKZZ0JVAbvnGN/w8zxiMivqkO8ijw8fXeCMDt0K2OW7q2GF0=

Я хотел использовать python3 -ecdsa. Когда я хочу проверить подпись с любым действительным ключом publi c, я получаю AssertionError: (65, 64), потому что базовый код подписи base64.b64 составляет 65 байтов, но должен быть 64. Когда я хочу загрузить node.public_key в ecdsa.VerifyingKey, я получаю AssertionError: (32, 64), потому что bytes.fromhex возвращает 32 байта, но каждый найденный пример использует 64 байта для ключа publi c. Возможно, мне нужно преобразовать bip32 xpub в ключ publi c, но я действительно не знаю, как.

Solutiion

python -ecdsa должно быть в версии 0.14 или выше для обработки сжатого формата ключа publi c.

import ecdsa
import base64
import hashlib

class DoubleSha256:

    def __init__(self, *args, **kwargs):
        self._m = hashlib.sha256(*args, **kwargs)

    def __getattr__(self, attr):
        if attr == 'digest':
            return self.double_digest
        return getattr(self._m, attr)

    def double_digest(self):
        m = hashlib.sha256()
        m.update(self._m.digest())
        return m.digest()


def pad_message(message):
    return "\x18Bitcoin Signed Message:\n".encode('UTF-8') + bytes([len(message)]) + message.encode('UTF-8')


public_key_hex = '026b4cc594c849a0d9a124725997604bc6a0ec8f100b621b1eaed4c6094619fc46'
public_key = bytes.fromhex(public_key_hex)
message = pad_message('aaa')
sig = base64.b64decode('IHQ7FDJy6zjwMImIsFcHGdhVxAH7ozoEoelN2EfgKZZ0JVAbvnGN/w8zxiMivqkO8ijw8fXeCMDt0K2OW7q2GF0=')

vk = ecdsa.VerifyingKey.from_string(public_key, curve=ecdsa.SECP256k1)
print(vk.verify(sig[1:], message, hashfunc=DoubleSha256))

1 Ответ

0 голосов
/ 11 января 2020

Publi c -key. Математически ключ эллипти c publi c - это точка на кривой. Для эллиптической кривой c, используемой Bitcoin, secp256k1, а также других кривых в стиле X9 (форма Вейерштрасса), существуют (на практике) два стандартных представления, первоначально установленных X9.62 и используется многими другими:

  • несжатый формат: состоит из одного октета со значением 0x04, за которым следуют два блока размера, равного размеру порядка кривой, содержащему (аффинные) координаты X и Y. Для secp256k1 это 1 + 32x2 = 65 октетов

  • сжатый формат: состоит из одного октета со значением 0x02 или 0x03, указывающим четность координаты Y, за которым следует блок размером, равным tot Порядок кривой, содержащий координату X. Для secp256k1 это 1 + 32 = 33 октета

Публичный ключ c, выданный вашим трезором, является второй формой, 0x02 + 32 октета = 33 октета. Не 32.

Я никогда не видел библиотеку X9E C (ECDSA и / или ECDH), которая не принимает хотя бы стандартную несжатую форму, и обычно обе. Вполне возможно, что ваша python библиотека ожидает только несжатую форму без начального 0x04, но если это так, эта необоснованная и довольно рискованная нестандартность, если в коде do c или коде не приведено очень хорошее объяснение, заставит меня с подозрением относиться к ее качественный. Если вам нужно преобразовать сжатую форму в несжатую, вы должны реализовать уравнение кривой, которое для secp256k1 можно найти в стандартных ссылках, не говоря уже о многих реализациях. Вычислите x^3 + a*x + b, возьмите квадрат root в F_p и выберите положительное или отрицательное значение, которое имеет правильную четность (в соответствии с начальным байтом здесь 0x02).

'xpub' является проверкой base58 кодирование иерархического детерминированного ключа c, который является не просто ключом E C (DSA), но добавляет метаданные для процесса получения ключа. Если вы base58 расшифруете его и удалите чек, вы получите (в шестнадцатеричном виде):

0488B21E01EA66F03700000000E02030F2A7DFB474D53A96CB26FEBBBE3BD3B9756F4E0A820146FF1FB4E0BD99026B4CC594C849A0D9A124725997604BC6A0EC8F100B621B1EAED4C6094619FC46good

, который разбивается именно так, как показывал ваш дисплей:

0488B21E  fixed prefix 
01  .depth 
EA66F037  .fingerprint
00000000  .child_num
E02030F2A7DFB474D53A96CB26FEBBBE3BD3B9756F4E0A820146FF1FB4E0BD99  .chain_code
026B4CC594C849A0D9A124725997604BC6A0EC8F100B621B1EAED4C6094619FC46  .public_key

Подтверждая это, отношении palemd160 sha256 из (байтов, которые показаны в шестнадцатирично-) 026B4CC594C849A0D9A124725997604BC6A0EC8F100B621B1EAED4C6094619FC46 есть (байты показаны в шестнадцатирично-) 441e1d2adf9ff2a6075d71d0d8782228e0df47f8 и префиксы версии байт 00 для маи nnet в том, что и кодирования base58check дает адрес 17DB2Q3oZVkQAffkpFvF4cwsXggu39iKdQ, как показано на рисунке.

Подпись. Математически сигнатура ECDSA типа X9.62 - это два целых числа, называемые r и s. Существует два различных стандарта для их представления, и Bitcoin использует оба варианта с вариациями:

  • Формат ASN.1 DER. DER - это кодировка общего назначения, которая содержит метаданные 'tag' и 'length' и данные переменной длины в зависимости от значений цифр c, здесь r и s; для secp256k1 в целом это кодирование обычно составляет от 70 до 72 октетов, но иногда меньше. Тем не менее, чтобы избежать определенных атак «податливости», текущая Bitcoin требует использования значений «s», меньших половины порядка кривой, обычно называемого «low-s», что уменьшает максимальную длину кодировки ASN.1 DER до 71 октета. , Bitcoin использует это для транзакции подписей и добавляет байт 'sigha sh', следующий сразу за ним (в скрипте сценария 'scriptsig'), указывающий определенные параметры того, как была вычислена подпись (и, таким образом, должны быть проверены).

  • «обычный» или формат P1363. Это фиксированная длина и состоит просто из значений r и s как блоков фиксированной длины; для secp256k1 это 64 октета. Bitcoin использует это для подписей сообщений, но добавляет байт '1047 * восстановления * к началу , который позволяет определить publickey из сообщения и подписи, если необходимо, что составляет всего 65 октетов. ,

См. https://bitcoin.stackexchange.com/questions/38351/ecdsa-v-r-s-what-is-v/38909 и https://bitcoin.stackexchange.com/questions/12554/why-the-signature-is-always-65-13232-bytes-long.

Если ваша библиотека python предназначена для ECDSA общего назначения, а не Bitcoin и требует 64-байтовую подпись, это почти наверняка является «простым» форматом, который соответствует сигнатуре сообщения Bitcoin (здесь декодируется из base64) с удалением первого байта.

...