08eea0f0e4
To address increasing complexity in code that handles signatures, this PR discards all notion of "different" signature types at the library level. Both the crypto and accounts package is reduced to only be able to produce plain canonical secp256k1 signatures. This makes the crpyto APIs much cleaner, simpler and harder to abuse.
247 lines
7.3 KiB
Go
247 lines
7.3 KiB
Go
// Copyright 2014 The go-ethereum Authors
|
|
// This file is part of the go-ethereum library.
|
|
//
|
|
// The go-ethereum library is free software: you can redistribute it and/or modify
|
|
// it under the terms of the GNU Lesser General Public License as published by
|
|
// the Free Software Foundation, either version 3 of the License, or
|
|
// (at your option) any later version.
|
|
//
|
|
// The go-ethereum library is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU Lesser General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU Lesser General Public License
|
|
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
package crypto
|
|
|
|
import (
|
|
"bytes"
|
|
"crypto/ecdsa"
|
|
"encoding/hex"
|
|
"fmt"
|
|
"io/ioutil"
|
|
"math/big"
|
|
"os"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/ethereum/go-ethereum/common"
|
|
"github.com/ethereum/go-ethereum/crypto/secp256k1"
|
|
)
|
|
|
|
var testAddrHex = "970e8128ab834e8eac17ab8e3812f010678cf791"
|
|
var testPrivHex = "289c2857d4598e37fb9647507e47a309d6133539bf21a8b9cb6df88fd5232032"
|
|
|
|
// These tests are sanity checks.
|
|
// They should ensure that we don't e.g. use Sha3-224 instead of Sha3-256
|
|
// and that the sha3 library uses keccak-f permutation.
|
|
func TestSha3(t *testing.T) {
|
|
msg := []byte("abc")
|
|
exp, _ := hex.DecodeString("4e03657aea45a94fc7d47ba826c8d667c0d1e6e33a64a036ec44f58fa12d6c45")
|
|
checkhash(t, "Sha3-256", func(in []byte) []byte { return Keccak256(in) }, msg, exp)
|
|
}
|
|
|
|
func TestSha3Hash(t *testing.T) {
|
|
msg := []byte("abc")
|
|
exp, _ := hex.DecodeString("4e03657aea45a94fc7d47ba826c8d667c0d1e6e33a64a036ec44f58fa12d6c45")
|
|
checkhash(t, "Sha3-256-array", func(in []byte) []byte { h := Keccak256Hash(in); return h[:] }, msg, exp)
|
|
}
|
|
|
|
func TestSha256(t *testing.T) {
|
|
msg := []byte("abc")
|
|
exp, _ := hex.DecodeString("ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad")
|
|
checkhash(t, "Sha256", Sha256, msg, exp)
|
|
}
|
|
|
|
func TestRipemd160(t *testing.T) {
|
|
msg := []byte("abc")
|
|
exp, _ := hex.DecodeString("8eb208f7e05d987a9b044a8e98c6b087f15a0bfc")
|
|
checkhash(t, "Ripemd160", Ripemd160, msg, exp)
|
|
}
|
|
|
|
func BenchmarkSha3(b *testing.B) {
|
|
a := []byte("hello world")
|
|
amount := 1000000
|
|
start := time.Now()
|
|
for i := 0; i < amount; i++ {
|
|
Keccak256(a)
|
|
}
|
|
|
|
fmt.Println(amount, ":", time.Since(start))
|
|
}
|
|
|
|
func Test0Key(t *testing.T) {
|
|
key := common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000000")
|
|
_, err := secp256k1.GeneratePubKey(key)
|
|
if err == nil {
|
|
t.Errorf("expected error due to zero privkey")
|
|
}
|
|
}
|
|
|
|
func TestSign(t *testing.T) {
|
|
key, _ := HexToECDSA(testPrivHex)
|
|
addr := common.HexToAddress(testAddrHex)
|
|
|
|
msg := Keccak256([]byte("foo"))
|
|
sig, err := Sign(msg, key)
|
|
if err != nil {
|
|
t.Errorf("Sign error: %s", err)
|
|
}
|
|
recoveredPub, err := Ecrecover(msg, sig)
|
|
if err != nil {
|
|
t.Errorf("ECRecover error: %s", err)
|
|
}
|
|
pubKey := ToECDSAPub(recoveredPub)
|
|
recoveredAddr := PubkeyToAddress(*pubKey)
|
|
if addr != recoveredAddr {
|
|
t.Errorf("Address mismatch: want: %x have: %x", addr, recoveredAddr)
|
|
}
|
|
|
|
// should be equal to SigToPub
|
|
recoveredPub2, err := SigToPub(msg, sig)
|
|
if err != nil {
|
|
t.Errorf("ECRecover error: %s", err)
|
|
}
|
|
recoveredAddr2 := PubkeyToAddress(*recoveredPub2)
|
|
if addr != recoveredAddr2 {
|
|
t.Errorf("Address mismatch: want: %x have: %x", addr, recoveredAddr2)
|
|
}
|
|
}
|
|
|
|
func TestInvalidSign(t *testing.T) {
|
|
if _, err := Sign(make([]byte, 1), nil); err == nil {
|
|
t.Errorf("expected sign with hash 1 byte to error")
|
|
}
|
|
if _, err := Sign(make([]byte, 33), nil); err == nil {
|
|
t.Errorf("expected sign with hash 33 byte to error")
|
|
}
|
|
}
|
|
|
|
func TestNewContractAddress(t *testing.T) {
|
|
key, _ := HexToECDSA(testPrivHex)
|
|
addr := common.HexToAddress(testAddrHex)
|
|
genAddr := PubkeyToAddress(key.PublicKey)
|
|
// sanity check before using addr to create contract address
|
|
checkAddr(t, genAddr, addr)
|
|
|
|
caddr0 := CreateAddress(addr, 0)
|
|
caddr1 := CreateAddress(addr, 1)
|
|
caddr2 := CreateAddress(addr, 2)
|
|
checkAddr(t, common.HexToAddress("333c3310824b7c685133f2bedb2ca4b8b4df633d"), caddr0)
|
|
checkAddr(t, common.HexToAddress("8bda78331c916a08481428e4b07c96d3e916d165"), caddr1)
|
|
checkAddr(t, common.HexToAddress("c9ddedf451bc62ce88bf9292afb13df35b670699"), caddr2)
|
|
}
|
|
|
|
func TestLoadECDSAFile(t *testing.T) {
|
|
keyBytes := common.FromHex(testPrivHex)
|
|
fileName0 := "test_key0"
|
|
fileName1 := "test_key1"
|
|
checkKey := func(k *ecdsa.PrivateKey) {
|
|
checkAddr(t, PubkeyToAddress(k.PublicKey), common.HexToAddress(testAddrHex))
|
|
loadedKeyBytes := FromECDSA(k)
|
|
if !bytes.Equal(loadedKeyBytes, keyBytes) {
|
|
t.Fatalf("private key mismatch: want: %x have: %x", keyBytes, loadedKeyBytes)
|
|
}
|
|
}
|
|
|
|
ioutil.WriteFile(fileName0, []byte(testPrivHex), 0600)
|
|
defer os.Remove(fileName0)
|
|
|
|
key0, err := LoadECDSA(fileName0)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
checkKey(key0)
|
|
|
|
// again, this time with SaveECDSA instead of manual save:
|
|
err = SaveECDSA(fileName1, key0)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer os.Remove(fileName1)
|
|
|
|
key1, err := LoadECDSA(fileName1)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
checkKey(key1)
|
|
}
|
|
|
|
func TestValidateSignatureValues(t *testing.T) {
|
|
check := func(expected bool, v byte, r, s *big.Int) {
|
|
if ValidateSignatureValues(v, r, s, false) != expected {
|
|
t.Errorf("mismatch for v: %d r: %d s: %d want: %v", v, r, s, expected)
|
|
}
|
|
}
|
|
minusOne := big.NewInt(-1)
|
|
one := common.Big1
|
|
zero := common.Big0
|
|
secp256k1nMinus1 := new(big.Int).Sub(secp256k1.N, common.Big1)
|
|
|
|
// correct v,r,s
|
|
check(true, 0, one, one)
|
|
check(true, 1, one, one)
|
|
// incorrect v, correct r,s,
|
|
check(false, 2, one, one)
|
|
check(false, 3, one, one)
|
|
|
|
// incorrect v, combinations of incorrect/correct r,s at lower limit
|
|
check(false, 2, zero, zero)
|
|
check(false, 2, zero, one)
|
|
check(false, 2, one, zero)
|
|
check(false, 2, one, one)
|
|
|
|
// correct v for any combination of incorrect r,s
|
|
check(false, 0, zero, zero)
|
|
check(false, 0, zero, one)
|
|
check(false, 0, one, zero)
|
|
|
|
check(false, 1, zero, zero)
|
|
check(false, 1, zero, one)
|
|
check(false, 1, one, zero)
|
|
|
|
// correct sig with max r,s
|
|
check(true, 0, secp256k1nMinus1, secp256k1nMinus1)
|
|
// correct v, combinations of incorrect r,s at upper limit
|
|
check(false, 0, secp256k1.N, secp256k1nMinus1)
|
|
check(false, 0, secp256k1nMinus1, secp256k1.N)
|
|
check(false, 0, secp256k1.N, secp256k1.N)
|
|
|
|
// current callers ensures r,s cannot be negative, but let's test for that too
|
|
// as crypto package could be used stand-alone
|
|
check(false, 0, minusOne, one)
|
|
check(false, 0, one, minusOne)
|
|
}
|
|
|
|
func checkhash(t *testing.T, name string, f func([]byte) []byte, msg, exp []byte) {
|
|
sum := f(msg)
|
|
if bytes.Compare(exp, sum) != 0 {
|
|
t.Fatalf("hash %s mismatch: want: %x have: %x", name, exp, sum)
|
|
}
|
|
}
|
|
|
|
func checkAddr(t *testing.T, addr0, addr1 common.Address) {
|
|
if addr0 != addr1 {
|
|
t.Fatalf("address mismatch: want: %x have: %x", addr0, addr1)
|
|
}
|
|
}
|
|
|
|
// test to help Python team with integration of libsecp256k1
|
|
// skip but keep it after they are done
|
|
func TestPythonIntegration(t *testing.T) {
|
|
kh := "289c2857d4598e37fb9647507e47a309d6133539bf21a8b9cb6df88fd5232032"
|
|
k0, _ := HexToECDSA(kh)
|
|
k1 := FromECDSA(k0)
|
|
|
|
msg0 := Keccak256([]byte("foo"))
|
|
sig0, _ := secp256k1.Sign(msg0, k1)
|
|
|
|
msg1 := common.FromHex("00000000000000000000000000000000")
|
|
sig1, _ := secp256k1.Sign(msg0, k1)
|
|
|
|
fmt.Printf("msg: %x, privkey: %x sig: %x\n", msg0, k1, sig0)
|
|
fmt.Printf("msg: %x, privkey: %x sig: %x\n", msg1, k1, sig1)
|
|
}
|