Как подписать сообщение в Python так же, как оно подписано в Javascript, используя кривую ECDSA secp256k1? - PullRequest
1 голос
/ 29 сентября 2019

Я пытаюсь подписать байтовый массив в python таким же образом, как это происходит в криптографической библиотеке с secp256k1 от NodeJS

Это код на NodeJS / Browser:

const secp256k1 = require('secp256k1')

var message = [2, 118, 145, 101, 166, 249, 149, 13, 2, 58, 65, 94, 230, 104, 184, 11, 185, 107, 92, 154, 226, 3, 93, 151, 189, 251, 68, 243, 86, 23, 90, 68, 255, 111, 3, 0, 0, 0, 0, 0, 0, 187, 226, 2, 0, 0, 0, 1, 0, 0, 0, 0, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 4, 0, 84, 101, 115, 116, 105, 0, 0, 0, 0, 0, 0, 0];

var private_key_buffer = [122, 241, 114, 103, 51, 227, 157, 149, 221, 126, 157, 173, 31, 111, 43, 118, 208, 71, 123, 59, 96, 68, 57, 177, 53, 59, 151, 188, 36, 167, 40, 68]

const signature = secp256k1.sign(SHA3BUF(message), private_key_buffer)

Это моя реализация в python:

import hashlib
import ecdsa

message = bytearray([2, 118, 145, 101, 166, 249, 149, 13, 2, 58, 65, 94, 230, 104, 184, 11, 185, 107, 92, 154, 226, 3, 93, 151, 189, 251, 68, 243, 86, 23, 90, 68, 255, 111, 3, 0, 0, 0, 0, 0, 0, 187, 226, 2, 0, 0, 0, 1, 0, 0, 0, 0, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 4, 0, 84, 101, 115, 116, 105, 0, 0, 0, 0, 0, 0, 0])

private_key_buffer = bytearray([122, 241, 114, 103, 51, 227, 157, 149, 221, 126, 157, 173, 31, 111, 43, 118, 208, 71, 123, 59, 96, 68, 57, 177, 53, 59, 151, 188, 36, 167, 40, 68])

signinKey = ecdsa.SigningKey.from_string(private_key_buffer, curve=ecdsa.SECP256k1)

signature = signinKey.sign_deterministic(message, hashfunc=hashlib.sha3_256)

, но по какой-то причине подпись, которую я получаю в коде javascript, отличается от кода python:

java script signature: [23, 54, 64, 151, 95, 33, 200, 66, 246, 166, 144, 182, 81, 179, 124, 223, 250, 50, 137, 169, 45, 181, 197, 74, 225, 207, 116, 125, 50, 241, 38, 52, 118, 215, 252, 94, 191, 154, 200, 195, 152, 73, 1, 197, 158, 24, 72, 177, 118, 39, 241, 82, 114, 107, 25, 106, 67, 205, 202, 4, 7, 57, 82, 237]

python script signature: [213, 69, 97, 237, 85, 226, 217, 201, 51, 14, 220, 92, 105, 59, 54, 92, 87, 88, 233, 147, 191, 15, 21, 86, 134, 202, 205, 223, 83, 134, 70, 39, 10, 19, 147, 20, 181, 180, 88, 103, 79, 55, 144, 98, 84, 2, 224, 127, 192, 200, 200, 250, 170, 129, 67, 99, 163, 72, 92, 253, 109, 108, 104, 206]

Так как я могу сделать так, чтобы код Python выводил ту же сигнатуру кода JS?

1 Ответ

2 голосов
/ 30 сентября 2019

Для детерминированного ECDSA, как описано в RFC6979 , алгоритм хеширования используется в двух местах: один алгоритм (H1) используется для хеширования сообщения, другой (H2) для определения значения k.k - это параметр в алгоритме подписи, роль которого описана, например, в RFC6979, раздел 2.4 или также здесь .Для недетерминированного варианта k определяется случайным образом, для детерминированного варианта, как описано в RFC6979.

RFC6979 не указывает, что H1 и H2 должны быть разными, см. RFC6979, раздел 3.6 .Тем не менее, имеет смысл, что реализация предлагает возможность определять оба алгоритма хеширования отдельно.

  1. Реализация Python в ECDSA , как правило, , позволяет применять два разных алгоритма хеширования,До того, как это будет показано во втором случае, следующий вариант, который соответствует опубликованному Python-коду, применяет тот же алгоритм хеширования H1 = H2 = SHA3-256.Алгоритм хэширования, указанный в sign_deterministic -методе, определяет как H1, так и H2:

    import hashlib
    import ecdsa
    
    message = b'Everything should be made as simple as possible, but not simpler.'
    private_key_buffer = bytearray.fromhex('0000000000000000000000000000000000000000000000000000000000000001') 
    
    sk = ecdsa.SigningKey.from_string(private_key_buffer, curve=ecdsa.SECP256k1) 
    signature = sk.sign_deterministic(message, hashfunc=hashlib.sha3_256)
    
    print(signature.hex())
    

    Подпись:

    r = 88ecdbc6a2762e7ad1160f7c984cd61385ff07982280538dd7d2103be2dce720
    s = c1487df9feab7afda6e6115bdd4d9c5316e3f917a3235a5e47aee09624491304
    
  2. следующий вариант использует H1 = SHA3-256 для хеширования сообщения и H2 = SHA256 для k -определения.Это возможно, заменив sign_deterministic -метод на sign_digest_deterministic -метод, который позволяет отдельно хешировать сообщение на H1.Алгоритм хеширования, указанный в sign_digest_deterministic -методе, определяет только H2:

    import hashlib
    import ecdsa
    
    message = b'Everything should be made as simple as possible, but not simpler.'
    private_key_buffer = bytearray.fromhex('0000000000000000000000000000000000000000000000000000000000000001') 
    
    digest = hashlib.sha3_256()
    digest.update(message)
    hash = digest.digest()
    
    sk = ecdsa.SigningKey.from_string(private_key_buffer, curve=ecdsa.SECP256k1) 
    signature = sk.sign_digest_deterministic(hash, hashfunc=hashlib.sha256)
    
    print(signature.hex())
    

    Подпись:

    r = 64b10395957b78d3bd3db279e5fa4ebee36b58dd1becace4bc2d7e3a04cf6259
    s = 19f1eee7495064ac679d7b64ab7213b921b650c0a3746f2938ffeede0ff1f2e8
    
  3. Следующий кодфункционально идентичен опубликованному NodeJS-коду:

    const secp256k1 = require('secp256k1')
    const sha3 = require('js-sha3')
    
    message = 'Everything should be made as simple as possible, but not simpler.'
    private_key_buffer = Buffer.from('0000000000000000000000000000000000000000000000000000000000000001','hex')
    
    digest = sha3.sha3_256;
    hash = Buffer.from(digest(message), 'hex')
    
    signature = secp256k1.sign(hash, private_key_buffer)
    console.log(signature.signature.toString('hex'))
    

    и генерирует такую ​​же сигнатуру, как во 2-м случае, т.е., очевидно, H2 = SHA256.Я не нашел способ изменить это на SHA3-256 без особых усилий.Однако согласно документации можно заменить генератор по умолчанию , который реализует RFC6979.Это также должно изменить H2, но может стоить дороже.

В итоге: Самый простой способ исправить несовместимость обоих кодов - это изменить Python-код, как описано выше во втором случае, т. е. использовать sign_digest_deterministic -метод.Затем сообщение хэшируется с SHA3-256, генерация k происходит с SHA256.Более дорогой альтернативой было бы реализовать собственный генератор для включения генерации k с SHA3-256 в NodeJS-коде.Или, конечно, вы пытаетесь найти другую ECDSA-библиотеку для NodeJS-кода, которая позволяет вам определять H1 и H2 отдельно, аналогично Python-коду.

...