Signature

如何在 Ruby 中使用 secp256k1 簽署消息?

  • February 10, 2022

我正在嘗試使用 secp256k1 在 Ruby 中為乙太坊編寫一個符號函式。

我可以訪問這個似乎適用於比特幣的 sign_compact 函式:

https://github.com/lian/bitcoin-ruby/blob/master/lib/bitcoin/ffi/secp256k1.rb#L207-L232

我正在嘗試使用它來簽署乙太坊,但似乎缺少一些步驟。具體來說,與頭部、r 值和 s 值有關。有人似乎在這裡使用 OpenSSL 而不是 secp256k1 正確地執行此操作:

https://github.com/se3000/ruby-eth/blob/master/lib/eth/open_ssl.rb#L58-L97

似乎不同的行是:

if signature.get_array_of_pointer(0, 2).all?{|i| BN_num_bits(i) <= 256 }
 4.times{|i|
   head = [ Eth.v_base + i ].pack("C")
   if public_key_hex == recover_public_key_from_signature(hash, [head, r, s].join, i, pubkey_compressed)
     rec_id = i; break
    end
 }
end

我將如何使用 secp256k1 sign_compact 函式對乙太坊消息進行簽名?我需要為簽名更改什麼嗎?或者有什麼地方可以找到這個協議?

我從未編寫過 Ruby,但我對比特幣和乙太坊簽名略知一二。我希望我能幫助你。

比特幣簽名由兩部分組成:(r,s). 乙太坊簽名由三部分組成: (v,r,s).

只有一個字節的額外值v允許從簽名中導出公鑰。只有四個可能的值,v因此您指向的程式碼會遍歷所有四個可能的值來檢查哪個是正確的。如果恢復的公鑰與提供的公鑰匹配(sign_compact函式的輸入),我們就找到了正確的rec_id值,然後將這個公鑰與簽名一起返回。所以函式最終返回(v,r,s)而不是僅僅返回(r,s)

也許黃皮書也可以為這個問題提供一些啟示: 在此處輸入圖像描述

在我看來,比特幣中的headersign_compact實際上設置為這個恢復位(假設函式呼叫secp256k1_ecdsa_recoverable_signature_serialize_compact設置了這個值)。所以我相信這個函式sign_compact實際上會返回你需要的東西。

查看 Ruby gem rbsecp256k1/examples第 40-51 行):

# Signs a personal message with the given private key.
#
# @param private_key [Secp256k1::PrivateKey] key to use for signing.
# @param chain_id [Integer] unique identifier for chain.
# @return [String] binary signature data including recovery id v at end.
def sign(private_key, chain_id)
 ctx = Secp256k1::Context.new
 signature, recovery_id = ctx.sign_recoverable(private_key, hash).compact
 result = signature.bytes
 result = result.append(Chains.to_v(recovery_id, chain_id))
 result.pack('c*')
end

這是一個相當複雜的範例,明確解決了您的問題。

由於您特別詢問了ethgem,我最近完全刪除了 OpenSSL 程式碼並專門在 Secp256k1 中實現了簽名和恢復(第 65-83 行):

# Signs arbitrary data without validation. Should not be used unless really
# desired. See also: {Key.personal_sign}, {Key.sign_typed_data}, and
# {Signature.recover}.
#
# @param blob [Object] that arbitrary data to be signed.
# @param chain_id [Integer] the chain id the signature should be generated on.
# @return [String] a hexa-decimal signature.
def sign(blob, chain_id = nil)
 context = Secp256k1::Context.new
 compact, recovery_id = context.sign_recoverable(@private_key, blob).compact
 signature = compact.bytes
 v = Chain.to_v recovery_id, chain_id
 is_leading_zero = true
 [v].pack("N").unpack("C*").each do |byte|
   is_leading_zero = false if byte > 0 and is_leading_zero
   signature.append byte unless is_leading_zero and byte === 0
 end
 Util.bin_to_hex signature.pack "c*"
end

它在Eth::Key類中,還包含一個personal_signand sign_typed_data

引用自:https://ethereum.stackexchange.com/questions/11515