計算機科学のブログ

楕円曲線暗号 公開鍵暗号 秘密鍵と公開鍵 署名と検証 署名アルゴリズム、署名ハッシュ、ダブル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つの署名はどちらも有効。