通过加密的比特币 ECDSA 签名。sign/4 产生了错误的签名(不兼容 RFC6979)
Bitcoin ECDSA signing via crypto.sign/4 is producing wrong signature (not RFC6979 compatible)
我正在尝试对比特币交易进行签名,部分过程是使用 sha256 和私钥对其进行签名。
这是我使用的 Elixir/Erlang 代码:
signed_data = :crypto.sign(:ecdsa, :sha256, data, [prk, :secp256k1]) |> Base.encode16
产生以下字符串:
3046022100AFF0CCC46F08C1A2D304483C0D9B97348EEEB7D415E394A512B87A73CB69AA660221009DB8FD52692FD0ADF16F0BB8EA870C9424F1BCD22EB6755388883DE62FBC01BD
问题是这个签名在我用于 BlockCypher 的参考实现上失败了。
他们在示例中提供的签名是这样的:
3044022045734b7593ed805dd95d3dfb86658afdb647b6693a740ffc9b2aa3d37cc6c06e0220324e5024acc51550e287564c308e280b8978305198d49cb2df8d07e7822b2563
所以对于相同的 data/privateKey 输入参数,我的签名长了 2 个字节。我在K部分多了一个字节,在S部分多了1个字节。
我怀疑 Erlang 加密库没有遵循比特币期望的 RFC6979。
这是一个 link 有效的签名者实现:https://github.com/blockcypher/btcutils/tree/master/signer
问题是:有没有办法让 Erlang 加密为此工作,或者有没有我应该使用的替代 Erlang/Elixir 库。
我能找到的最接近的是:https://github.com/trustatom-oss/erlang-secp256k1
它不是原生的 Erlang 代码,而是使用 NIF 和纯 C。但总比没有好。
将它留在这里以防万一有人执行本机 Erlang/Elixir 实现。如果你这样做,请 ping 我。
ECDSA 签名描述了椭圆曲线内的一个点。在比特币的例子中,曲线是 secp256k1。对于实数,这样的曲线看起来或多或少是这样的:
曲线内的点由两个值或 "coordinates" 明确指定:R 和 S。对于单个 R 值,有两个可能的有效 S 值。
2015 年 10 月,有人开始利用这种双重性,一次又一次地操纵已经广播的交易,以淹没比特币网络并导致其故障。
由于这种可延展性的潜在风险,Blockcypher 和比特币领域的其他著名成员 immediately decided to embrace 到 BIP-0062。
BIP-0062 强制执行 "low S values"。这意味着必须丢弃包含 S 分量大于特定值的签名的交易。这样的最高值是:
0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0
如果您想确保您的交易始终包含有效的 S 值,您需要检查生成的 S 值是否大于最高值。如果是这样,只需将当前的S值删除为以下值:
0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141
这是在 Elixir 中进行此类转换的函数示例。
def enforce_low_s sig do
len = sig |> :binary.at(1)
rlen = sig |> :binary.at(3)
r = sig |> :binary.part(4, rlen)
slen = sig |> :binary.at(rlen + 5)
s = sig |> :binary.part(rlen + 6, slen)
sint = s |> :binary.decode_unsigned
if sint > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0 do
s = (0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - sint) |> :binary.encode_unsigned
slen = s |> :binary.bin_to_list |> Enum.count
len = rlen + slen + 4
end
<<48, len, 2>> <> <<rlen>> <> r <> <<2, slen>> <> s
end
因此,完整的签名过程如下所示:
priv_key = "yourhexprivatekey"
|> Base.decode16!
msg = "Lorem Ipsum dolor sit amet..."
sig = :crypto.sign(:sha256, msg, [priv_key, :secp256k1])
|> enforce_low_s
|> Base.encode16
我正在尝试对比特币交易进行签名,部分过程是使用 sha256 和私钥对其进行签名。
这是我使用的 Elixir/Erlang 代码:
signed_data = :crypto.sign(:ecdsa, :sha256, data, [prk, :secp256k1]) |> Base.encode16
产生以下字符串:
3046022100AFF0CCC46F08C1A2D304483C0D9B97348EEEB7D415E394A512B87A73CB69AA660221009DB8FD52692FD0ADF16F0BB8EA870C9424F1BCD22EB6755388883DE62FBC01BD
问题是这个签名在我用于 BlockCypher 的参考实现上失败了。
他们在示例中提供的签名是这样的:
3044022045734b7593ed805dd95d3dfb86658afdb647b6693a740ffc9b2aa3d37cc6c06e0220324e5024acc51550e287564c308e280b8978305198d49cb2df8d07e7822b2563
所以对于相同的 data/privateKey 输入参数,我的签名长了 2 个字节。我在K部分多了一个字节,在S部分多了1个字节。
我怀疑 Erlang 加密库没有遵循比特币期望的 RFC6979。
这是一个 link 有效的签名者实现:https://github.com/blockcypher/btcutils/tree/master/signer
问题是:有没有办法让 Erlang 加密为此工作,或者有没有我应该使用的替代 Erlang/Elixir 库。
我能找到的最接近的是:https://github.com/trustatom-oss/erlang-secp256k1
它不是原生的 Erlang 代码,而是使用 NIF 和纯 C。但总比没有好。
将它留在这里以防万一有人执行本机 Erlang/Elixir 实现。如果你这样做,请 ping 我。
ECDSA 签名描述了椭圆曲线内的一个点。在比特币的例子中,曲线是 secp256k1。对于实数,这样的曲线看起来或多或少是这样的:
曲线内的点由两个值或 "coordinates" 明确指定:R 和 S。对于单个 R 值,有两个可能的有效 S 值。
2015 年 10 月,有人开始利用这种双重性,一次又一次地操纵已经广播的交易,以淹没比特币网络并导致其故障。
由于这种可延展性的潜在风险,Blockcypher 和比特币领域的其他著名成员 immediately decided to embrace 到 BIP-0062。
BIP-0062 强制执行 "low S values"。这意味着必须丢弃包含 S 分量大于特定值的签名的交易。这样的最高值是:
0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0
如果您想确保您的交易始终包含有效的 S 值,您需要检查生成的 S 值是否大于最高值。如果是这样,只需将当前的S值删除为以下值:
0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141
这是在 Elixir 中进行此类转换的函数示例。
def enforce_low_s sig do
len = sig |> :binary.at(1)
rlen = sig |> :binary.at(3)
r = sig |> :binary.part(4, rlen)
slen = sig |> :binary.at(rlen + 5)
s = sig |> :binary.part(rlen + 6, slen)
sint = s |> :binary.decode_unsigned
if sint > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0 do
s = (0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - sint) |> :binary.encode_unsigned
slen = s |> :binary.bin_to_list |> Enum.count
len = rlen + slen + 4
end
<<48, len, 2>> <> <<rlen>> <> r <> <<2, slen>> <> s
end
因此,完整的签名过程如下所示:
priv_key = "yourhexprivatekey"
|> Base.decode16!
msg = "Lorem Ipsum dolor sit amet..."
sig = :crypto.sign(:sha256, msg, [priv_key, :secp256k1])
|> enforce_low_s
|> Base.encode16