楕円曲線暗号 秘密鍵、メッセージの署名のGoによる実装
プログラミング・ビットコイン ―ゼロからビットコインをプログラムする方法 (Jimmy Song(著)、中川 卓俊(監修)、住田 和則(監修)、中村 昭雄(監修)、星野 靖子(翻訳)、オライリー・ジャパン)の3章(楕円曲線暗号)、3.11(署名と検証)、3.11.7(メッセージの署名のプログラミング)をPythonではなくGoでコード書いてみる。
コード
ecc_test.go
package ecc
import (
"math/big"
"math/rand"
"testing"
"time"
)
// ...
func TestPrivateKeySign(t *testing.T) {
r := rand.New(rand.NewSource(time.Now().UnixNano()))
n := new(big.Int)
n.Set(N())
n.Add(n, big.NewInt(1))
m := new(big.Int)
m.SetString("115792089237316195423570985008687907853269984665640564039457584007913129639936", 10)
secret := new(big.Int)
secret.Rand(r, n)
pk := NewPrivateKey(secret)
z := new(big.Int)
z.Rand(r, m)
sig := pk.Sign(z)
got := pk.Verify(z, sig)
want := true
if got != want {
t.Errorf("%v.Verify(%v, %v) got %v, want %v", pk, z, sig, got, want)
}
}
ecc.go
// Package ecc (Elliptic Curve Cryptography, 楕円曲線暗号)
package ecc
import (
"fmt"
"math/big"
"math/rand"
"time"
)
// ...
// PrivateKey 秘密鍵
type PrivateKey struct {
Secret *big.Int
S256Point
}
// NewPrivateKey ...
func NewPrivateKey(secret *big.Int) PrivateKey {
s := new(big.Int)
s.Set(secret)
return PrivateKey{Secret: s, S256Point: G.ScalarMul(s)}
}
// Hex 16進数の文字列表現
func (pk PrivateKey) Hex() string {
return fmt.Sprintf("%064x", pk.Secret)
}
// Sign 署名
func (pk PrivateKey) Sign(z *big.Int) Signature {
rnd := rand.New(rand.NewSource(time.Now().UnixNano()))
k := new(big.Int)
k.Rand(rnd, N())
r := new(big.Int)
r.Set(G.ScalarMul(k).X.Num)
kInv := pow(k)
s := new(big.Int)
s.Set(r)
s.Mul(s, pk.Secret)
s.Add(s, z)
s.Mul(s, kInv)
s.Mod(s, N())
n := new(big.Int)
n.Div(N(), big.NewInt(2))
if s.Cmp(n) == 1 {
s.Sub(N(), s)
}
return NewSignature(r, s)
}
入出力結果(Terminal, Zsh)
% go test
PASS
ok bitcoin/ecc 24.484s
% go test
PASS
ok bitcoin/ecc 6.998s
% go test
PASS
ok bitcoin/ecc 6.757s
% go test
PASS
ok bitcoin/ecc 4.963s
%