2018/01/20

ECDSAの署名+αから公開鍵を算出したい (9)

自作の公開鍵復元が動かないので、先生を動かすことにした。

https://github.com/bitcoinjs/bitcoinjs-lib/tree/v2.1.4


recoverPubKeyを持つのがv2なので、この辺りを使った。
こんな感じで書くと、動くことが分かった。


var ecdsa = require('./src/ecdsa')
var BigInteger = require('bigi')
var ECSignature = require('./src/ecsignature')


//pubkey= 03becec41f68d77fde9e972c79aa0e6e4e818bd3046276969e79374ec0561ba459
//hash=b881ab3c470b9398adb5ea6cb360d145df702fa0bc0a6c012219fa93e709adad
//  r=047c1b1111bbe0fc09cf2ed28dd1abd613c02b747af8edd1274886654259bbfb
//  s=1d00ca3fc10a111a5a3ea0669992e4ad9a2f1c402ec5fd29d9e1348ab0584fa2

var e = BigInteger.fromHex('b881ab3c470b9398adb5ea6cb360d145df702fa0bc0a6c012219fa93e709adad')
var r = BigInteger.fromHex('047c1b1111bbe0fc09cf2ed28dd1abd613c02b747af8edd1274886654259bbfb')
var s = BigInteger.fromHex('1d00ca3fc10a111a5a3ea0669992e4ad9a2f1c402ec5fd29d9e1348ab0584fa2')
var signature = new ECSignature(r, s)
var i = 0

var Qprime = ecdsa.recoverPubKey(e, signature, i)
var QprimeHex = Qprime.getEncoded().toString('hex')
console.log('Qprime=', QprimeHex)

$ node recover_pub.js
Qprime= 03becec41f68d77fde9e972c79aa0e6e4e818bd3046276969e79374ec0561ba459

さすが先生ですわ・・・。
自分の結果と比較するために、データを残しておこう。

i=0
03becec41f68d77fde9e972c79aa0e6e4e818bd3046276969e79374ec0561ba459

i=1
0383a8ef00cdbdc68b4176c72be1b2173b522633ba525f7a89334e4bab6cec89b4

i=2
03ccb2ec2eca77c75955cd5334e9f0248adf51dcb37744fef36a9152dd468cbe37

i=3
02082151f0695b3b98128e4326e7ddd1f44264448d39723b10ba56976c44fe5dba

i=4
例外発生

うちの子はこうだ。

i=0
0283a8ef00cdbdc68b4176c72be1b2173b522633ba525f7a89334e4bab6cec89b4

i=1
02becec41f68d77fde9e972c79aa0e6e4e818bd3046276969e79374ec0561ba459

i=2
03f23664e578b9cc1f8eb81e47ce3eb037043a5a92f0a3fc09ebd2ca121f862e05

i=3
03b06c50b0579e2142f56756c29445d7762539368be48918db2d6bd4f2f562b51d

i=0とi=1の結果が逆のような形になっている。
i=2と3は共通点がない。

この辺りに突破口がないだろうか。


うーん、-eの値が違う。

先生
e   = b881ab3c470b9398adb5ea6cb360d145df702fa0bc0a6c012219fa93e709adad
eNeg= 477e54c3b8f46c67524a15934c9f2eb8db3ead45f33e343a9db863f8e92c9394



e    = B881AB3C470B9398ADB5EA6CB360D145DF702FA0BC0A6C012219FA93E709ADAD
eNeg = 477E54C3B8F46C67524A15934C9F2EBA208FD05F43F593FEDDE6056B18F64E82

実は、前回まで私は (0 - e) % Pを計算していたのだが、ログに出してみるとeと同じ値だったのだ。。。
Y座標の符号を逆にするときの反省を生かして(P - e) % Pにしたのが今回。
それでも、微妙に値が違っている。


先生は、e.negate().mod(n)で計算している。
だいたい、一番最後の桁を足しても、0xD + 0x4は0じゃないやん。。。

やけになって、PではなくNでやってみた。


e    = B881AB3C470B9398ADB5EA6CB360D145DF702FA0BC0A6C012219FA93E709ADAD
eNeg = 477E54C3B8F46C67524A15934C9F2EB8DB3EAD45F33E343A9DB863F8E92C9394

理屈はわからんが、値が一致してしまったではないか。

i=0
03becec41f68d77fde9e972c79aa0e6e4e818bd3046276969e79374ec0561ba459

i=1
0383a8ef00cdbdc68b4176c72be1b2173b522633ba525f7a89334e4bab6cec89b4

i=2
02b06c50b0579e2142f56756c29445d7762539368be48918db2d6bd4f2f562b51d

i=3
02f23664e578b9cc1f8eb81e47ce3eb037043a5a92f0a3fc09ebd2ca121f862e05

うーん、i=2と3がまだ不一致だ。


これは、Rが不一致なためのようだ。

先生(i=2)
x= 01047c1b1111bbe0fc09cf2ed28dd1abd4ce6f085b2a418e0ce71ae4f2128ffd3c
R= 02047c1b1111bbe0fc09cf2ed28dd1abd4ce6f085b2a418e0ce71ae4f31290010d


x = 01047C1B1111BBE0FC09CF2ED28DD1ABD4CE6F085B2A418E0CE71AE4F2128FFD3C
R.x = 01047C1B1111BBE0FC09CF2ED28DD1ABD4CE6F085B2A418E0CE71AE4F2128FFD
R.y = 7F6ACBA2D227DCA73DDC17A895454F00A97F34A87967BECB6535287E0E5C436C

xは同じ値で、Rはxの先頭に0x02を付けたもののはずだが、先生はそうしていない。
そして、私もそうなっていないのだが、なんでだ??

あ、表示桁が足りてない・・・。
xにNを足したとき、33byteになってしまったのだ。
単純なバグだな。

i=2
03ccb2ec2eca77c75955cd5334e9f0248adf51dcb37744fef36a9152dd468cbe37

i=3
02082151f0695b3b98128e4326e7ddd1f44264448d39723b10ba56976c44fe5dba

一致した。


で、だ。

ハッシュ値eに対する-eを求めるとき、N - eして値が一致したけど、それでよいのだろうか?
secp256k1の法に従うことにはなるのだろうけど、そこが感覚的に分かっていない。

(sR - eG)を求めるとき、APIとしては (a * P + b * Q)しかないためにbをマイナスにしようとしただけなのだ。
だから、単純に(0 - e)を求めたのだが、これは当然負の数で、それは0~N-1の世界にいないからダメなのか?


module 負」で検索すると、そういう計算自体が微妙なところらしい。
言語間で違うということは、ライブラリだと言語に関係なく違いが生じるということか。

うーん、難しいことはやらずにAPIだけ使い回して完了させる予定だったのに、最後に悩みが残る結果になってしまった。
楕円曲線の簡単な説明をしてくれるサイトでも読むか。

0 件のコメント:

コメントを投稿

コメントありがとうございます。
スパムかもしれない、と私が思ったら、
申し訳ないですが勝手に削除することもあります。