楕円曲線暗号 公開鍵暗号 秘密鍵と公開鍵 署名と検証 署名アルゴリズム、署名ハッシュ、ダブルSHA-1
プログラミング・ビットコイン ―ゼロからビットコインをプログラムする方法 (Jimmy Song(著)、中川 卓俊(監修)、住田 和則(監修)、中村 昭雄(監修)、星野 靖子(翻訳)、オライリー・ジャパン)の3章(楕円曲線暗号)、3.11(署名と検証)、3.11.3(署名の検証)の練習問題6の解答をPytrhonではなくGoで求めてみる。
コード
package main
import (
"bitcoin/ecc"
"fmt"
"math/big"
"os"
)
func pErr(err error) {
if err != nil {
fmt.Println(err)
os.Exit(1)
}
}
func pow(x *big.Int) *big.Int {
i := big.NewInt(1)
exp := new(big.Int)
exp.Set(ecc.N)
exp.Sub(exp, big.NewInt(2))
base := new(big.Int)
base.Set(x)
one := big.NewInt(1)
two := big.NewInt(2)
for exp.Cmp(one) != 0 {
r := new(big.Int)
r.Set(exp)
r.Mod(exp, two)
if r.Cmp(one) == 0 {
i.Mul(i, base)
exp.Sub(exp, one)
}
base.Mul(base, base)
base.Mod(base, ecc.N)
exp.Div(exp, two)
}
i.Mul(i, base)
i.Mod(i, ecc.N)
return i
}
func main() {
z := new(big.Int)
z.SetString("bc62d4b80d9e36da29c16c5d4d9f11731f36052c72401a76c23c0fb5a9b74423", 16)
r := new(big.Int)
r.SetString("37206a0610995c58074999cb9767b87af4c4978db68c06e8e6e81d282047a7c6", 16)
s := new(big.Int)
s.SetString("8ca63759c1157ebeaec0d03cecca119fc9a75bf8e6d0fa65c841c8e2738cdaec", 16)
point, err := ecc.NewS256PointString(
false,
"04519fac3d910ca7e7138f7013706f619fa8f033e6ec6e09370ea38cee6a7574", "82b51eab8c27c66e26c858a079bcdf4f1ada34cec420cafc7eac1a42216fb6c4",
16,
)
pErr(err)
// n := new(big.Int)
// n.Set(ecc.N)
// n.Div(n, big.NewInt(2))
sInv := pow(s)
u := new(big.Int)
u.Set(z)
u.Mul(u, sInv)
u.Mod(u, ecc.N)
v := new(big.Int)
v.Set(r)
v.Mul(v, sInv)
v.Mod(v, ecc.N)
l := ecc.G.ScalarMul(u)
t := point.ScalarMul(v)
l, err = l.Add(t)
fmt.Println(l.X.Num.Cmp(r) == 0)
// 練習問題6
fmt.Println("練習問題6")
p, err := ecc.NewS256PointString(
false,
"887387e452b8eacc4acfde10d9aaf7f6d9a0f975aabb10d006e4da568744d06c",
"61de6d95231cd89026e286df3b6ae4a894a3378e393e93a0f45b666329a0ae34",
16,
)
pErr(err)
// signature 1, 2
signs := []struct {
z, r, s string
}{
{
"ec208baa0fc1c19f708a9ca96fdeff3ac3f230bb4a7ba4aede4942ad003c0f60",
"ac8d1c87e51d0d441be8b3dd5b05c8795b48875dffe00b7ffcfac23010d3a395",
"68342ceff8935ededd102dd876ffd6ba72d6a427a3edb13d26eb0781cb423c4",
},
{
"7c076ff316692a3d7eb3c3bb0f8b1488cf72e1afcd929e29307032997a838a3d",
"eff69ef2b1bd93a66ed5219add4fb51e11a840f404876325a1e8ffe0529a2c",
"c7207fee197d27c618aea621406f6bf5ef6fca38681d82b2f06fddbdce6feab6",
},
}
for i, sign := range signs {
fmt.Println("signature", i+1)
z := new(big.Int)
z.SetString(sign.z, 16)
r := new(big.Int)
r.SetString(sign.r, 16)
s := new(big.Int)
s.SetString(sign.s, 16)
sInv := new(big.Int)
sInv.Set(pow(s))
u := new(big.Int)
u.Set(z)
u.Mul(u, sInv)
u.Mod(u, ecc.N)
v := new(big.Int)
v.Set(r)
v.Mul(v, sInv)
v.Mod(v, ecc.N)
l1 := ecc.G.ScalarMul(u)
l2 := p.ScalarMul(v)
l, err := l1.Add(l2)
pErr(err)
fmt.Println(l.X.Num.Cmp(r) == 0)
}
}
入出力結果(Terminal, Zsh)
% go run ./main.go
true
練習問題6
signature 1
true
signature 2
true
%
ということで、2つの署名はどちらも有効。