計算機科学のブログ

楕円曲線暗号 秘密鍵、メッセージの署名の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
%