aboutsummaryrefslogtreecommitdiffstats
path: root/crypto
diff options
context:
space:
mode:
Diffstat (limited to 'crypto')
-rw-r--r--crypto/crypto.go95
-rw-r--r--crypto/crypto_test.go36
-rw-r--r--crypto/ecies/asn1.go6
-rw-r--r--crypto/ecies/ecies_test.go41
-rw-r--r--crypto/ecies/params.go6
-rw-r--r--crypto/encrypt_decrypt_test.go56
-rw-r--r--crypto/secp256k1/secp256.go10
-rw-r--r--crypto/signature_cgo.go64
-rw-r--r--crypto/signature_nocgo.go77
-rw-r--r--crypto/signature_test.go36
10 files changed, 239 insertions, 188 deletions
diff --git a/crypto/crypto.go b/crypto/crypto.go
index ce45ebd38..9d67d82e1 100644
--- a/crypto/crypto.go
+++ b/crypto/crypto.go
@@ -20,22 +20,21 @@ import (
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
- "crypto/sha256"
- "fmt"
+ "encoding/hex"
+ "errors"
"io"
"io/ioutil"
"math/big"
"os"
- "encoding/hex"
- "errors"
-
"github.com/ethereum/go-ethereum/common"
- "github.com/ethereum/go-ethereum/crypto/ecies"
- "github.com/ethereum/go-ethereum/crypto/secp256k1"
"github.com/ethereum/go-ethereum/crypto/sha3"
"github.com/ethereum/go-ethereum/rlp"
- "golang.org/x/crypto/ripemd160"
+)
+
+var (
+ secp256k1_N, _ = new(big.Int).SetString("fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141", 16)
+ secp256k1_halfN = new(big.Int).Div(secp256k1_N, big.NewInt(2))
)
func Keccak256(data ...[]byte) []byte {
@@ -56,7 +55,6 @@ func Keccak256Hash(data ...[]byte) (h common.Hash) {
}
// Deprecated: For backward compatibility as other packages depend on these
-func Sha3(data ...[]byte) []byte { return Keccak256(data...) }
func Sha3Hash(data ...[]byte) common.Hash { return Keccak256Hash(data...) }
// Creates an ethereum address given the bytes and the nonce
@@ -65,39 +63,16 @@ func CreateAddress(b common.Address, nonce uint64) common.Address {
return common.BytesToAddress(Keccak256(data)[12:])
}
-func Sha256(data []byte) []byte {
- hash := sha256.Sum256(data)
-
- return hash[:]
-}
-
-func Ripemd160(data []byte) []byte {
- ripemd := ripemd160.New()
- ripemd.Write(data)
-
- return ripemd.Sum(nil)
-}
-
-// Ecrecover returns the public key for the private key that was used to
-// calculate the signature.
-//
-// Note: secp256k1 expects the recover id to be either 0, 1. Ethereum
-// signatures have a recover id with an offset of 27. Callers must take
-// this into account and if "recovering" from an Ethereum signature adjust.
-func Ecrecover(hash, sig []byte) ([]byte, error) {
- return secp256k1.RecoverPubkey(hash, sig)
-}
-
-// New methods using proper ecdsa keys from the stdlib
+// ToECDSA creates a private key with the given D value.
func ToECDSA(prv []byte) *ecdsa.PrivateKey {
if len(prv) == 0 {
return nil
}
priv := new(ecdsa.PrivateKey)
- priv.PublicKey.Curve = secp256k1.S256()
+ priv.PublicKey.Curve = S256()
priv.D = common.BigD(prv)
- priv.PublicKey.X, priv.PublicKey.Y = secp256k1.S256().ScalarBaseMult(prv)
+ priv.PublicKey.X, priv.PublicKey.Y = priv.PublicKey.Curve.ScalarBaseMult(prv)
return priv
}
@@ -112,15 +87,15 @@ func ToECDSAPub(pub []byte) *ecdsa.PublicKey {
if len(pub) == 0 {
return nil
}
- x, y := elliptic.Unmarshal(secp256k1.S256(), pub)
- return &ecdsa.PublicKey{Curve: secp256k1.S256(), X: x, Y: y}
+ x, y := elliptic.Unmarshal(S256(), pub)
+ return &ecdsa.PublicKey{Curve: S256(), X: x, Y: y}
}
func FromECDSAPub(pub *ecdsa.PublicKey) []byte {
if pub == nil || pub.X == nil || pub.Y == nil {
return nil
}
- return elliptic.Marshal(secp256k1.S256(), pub.X, pub.Y)
+ return elliptic.Marshal(S256(), pub.X, pub.Y)
}
// HexToECDSA parses a secp256k1 private key.
@@ -164,7 +139,7 @@ func SaveECDSA(file string, key *ecdsa.PrivateKey) error {
}
func GenerateKey() (*ecdsa.PrivateKey, error) {
- return ecdsa.GenerateKey(secp256k1.S256(), rand.Reader)
+ return ecdsa.GenerateKey(S256(), rand.Reader)
}
// ValidateSignatureValues verifies whether the signature values are valid with
@@ -175,49 +150,11 @@ func ValidateSignatureValues(v byte, r, s *big.Int, homestead bool) bool {
}
// reject upper range of s values (ECDSA malleability)
// see discussion in secp256k1/libsecp256k1/include/secp256k1.h
- if homestead && s.Cmp(secp256k1.HalfN) > 0 {
+ if homestead && s.Cmp(secp256k1_halfN) > 0 {
return false
}
// Frontier: allow s to be in full N range
- return r.Cmp(secp256k1.N) < 0 && s.Cmp(secp256k1.N) < 0 && (v == 0 || v == 1)
-}
-
-func SigToPub(hash, sig []byte) (*ecdsa.PublicKey, error) {
- s, err := Ecrecover(hash, sig)
- if err != nil {
- return nil, err
- }
-
- x, y := elliptic.Unmarshal(secp256k1.S256(), s)
- return &ecdsa.PublicKey{Curve: secp256k1.S256(), X: x, Y: y}, nil
-}
-
-// Sign calculates an ECDSA signature.
-//
-// This function is susceptible to chosen plaintext attacks that can leak
-// information about the private key that is used for signing. Callers must
-// be aware that the given hash cannot be chosen by an adversery. Common
-// solution is to hash any input before calculating the signature.
-//
-// The produced signature is in the [R || S || V] format where V is 0 or 1.
-func Sign(data []byte, prv *ecdsa.PrivateKey) (sig []byte, err error) {
- if len(data) != 32 {
- return nil, fmt.Errorf("hash is required to be exactly 32 bytes (%d)", len(data))
- }
-
- seckey := common.LeftPadBytes(prv.D.Bytes(), prv.Params().BitSize/8)
- defer zeroBytes(seckey)
- sig, err = secp256k1.Sign(data, seckey)
- return
-}
-
-func Encrypt(pub *ecdsa.PublicKey, message []byte) ([]byte, error) {
- return ecies.Encrypt(rand.Reader, ecies.ImportECDSAPublic(pub), message, nil, nil)
-}
-
-func Decrypt(prv *ecdsa.PrivateKey, ct []byte) ([]byte, error) {
- key := ecies.ImportECDSA(prv)
- return key.Decrypt(rand.Reader, ct, nil, nil)
+ return r.Cmp(secp256k1_N) < 0 && s.Cmp(secp256k1_N) < 0 && (v == 0 || v == 1)
}
func PubkeyToAddress(p ecdsa.PublicKey) common.Address {
diff --git a/crypto/crypto_test.go b/crypto/crypto_test.go
index d4d309849..e518ac22d 100644
--- a/crypto/crypto_test.go
+++ b/crypto/crypto_test.go
@@ -28,7 +28,6 @@ import (
"time"
"github.com/ethereum/go-ethereum/common"
- "github.com/ethereum/go-ethereum/crypto/secp256k1"
)
var testAddrHex = "970e8128ab834e8eac17ab8e3812f010678cf791"
@@ -37,30 +36,12 @@ var testPrivHex = "289c2857d4598e37fb9647507e47a309d6133539bf21a8b9cb6df88fd5232
// 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
@@ -170,7 +151,7 @@ func TestValidateSignatureValues(t *testing.T) {
minusOne := big.NewInt(-1)
one := common.Big1
zero := common.Big0
- secp256k1nMinus1 := new(big.Int).Sub(secp256k1.N, common.Big1)
+ secp256k1nMinus1 := new(big.Int).Sub(secp256k1_N, common.Big1)
// correct v,r,s
check(true, 0, one, one)
@@ -197,9 +178,9 @@ func TestValidateSignatureValues(t *testing.T) {
// 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)
+ 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
@@ -225,14 +206,13 @@ func checkAddr(t *testing.T, addr0, addr1 common.Address) {
func TestPythonIntegration(t *testing.T) {
kh := "289c2857d4598e37fb9647507e47a309d6133539bf21a8b9cb6df88fd5232032"
k0, _ := HexToECDSA(kh)
- k1 := FromECDSA(k0)
msg0 := Keccak256([]byte("foo"))
- sig0, _ := secp256k1.Sign(msg0, k1)
+ sig0, _ := Sign(msg0, k0)
msg1 := common.FromHex("00000000000000000000000000000000")
- sig1, _ := secp256k1.Sign(msg0, k1)
+ sig1, _ := Sign(msg0, k0)
- fmt.Printf("msg: %x, privkey: %x sig: %x\n", msg0, k1, sig0)
- fmt.Printf("msg: %x, privkey: %x sig: %x\n", msg1, k1, sig1)
+ t.Logf("msg: %x, privkey: %s sig: %x\n", msg0, kh, sig0)
+ t.Logf("msg: %x, privkey: %s sig: %x\n", msg1, kh, sig1)
}
diff --git a/crypto/ecies/asn1.go b/crypto/ecies/asn1.go
index 508a645cd..d3e77d849 100644
--- a/crypto/ecies/asn1.go
+++ b/crypto/ecies/asn1.go
@@ -42,7 +42,7 @@ import (
"hash"
"math/big"
- "github.com/ethereum/go-ethereum/crypto/secp256k1"
+ ethcrypto "github.com/ethereum/go-ethereum/crypto"
)
var (
@@ -120,7 +120,7 @@ func (curve secgNamedCurve) Equal(curve2 secgNamedCurve) bool {
func namedCurveFromOID(curve secgNamedCurve) elliptic.Curve {
switch {
case curve.Equal(secgNamedCurveS256):
- return secp256k1.S256()
+ return ethcrypto.S256()
case curve.Equal(secgNamedCurveP256):
return elliptic.P256()
case curve.Equal(secgNamedCurveP384):
@@ -139,7 +139,7 @@ func oidFromNamedCurve(curve elliptic.Curve) (secgNamedCurve, bool) {
return secgNamedCurveP384, true
case elliptic.P521():
return secgNamedCurveP521, true
- case secp256k1.S256():
+ case ethcrypto.S256():
return secgNamedCurveS256, true
}
diff --git a/crypto/ecies/ecies_test.go b/crypto/ecies/ecies_test.go
index 3b3517baf..7c454aa73 100644
--- a/crypto/ecies/ecies_test.go
+++ b/crypto/ecies/ecies_test.go
@@ -31,7 +31,6 @@ package ecies
import (
"bytes"
- "crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
"crypto/sha256"
@@ -42,7 +41,7 @@ import (
"math/big"
"testing"
- "github.com/ethereum/go-ethereum/crypto/secp256k1"
+ "github.com/ethereum/go-ethereum/crypto"
)
var dumpEnc bool
@@ -150,7 +149,7 @@ func TestSharedKey(t *testing.T) {
func TestSharedKeyPadding(t *testing.T) {
// sanity checks
prv0 := hexKey("1adf5c18167d96a1f9a0b1ef63be8aa27eaf6032c233b2b38f7850cf5b859fd9")
- prv1 := hexKey("97a076fc7fcd9208240668e31c9abee952cbb6e375d1b8febc7499d6e16f1a")
+ prv1 := hexKey("0097a076fc7fcd9208240668e31c9abee952cbb6e375d1b8febc7499d6e16f1a")
x0, _ := new(big.Int).SetString("1a8ed022ff7aec59dc1b440446bdda5ff6bcb3509a8b109077282b361efffbd8", 16)
x1, _ := new(big.Int).SetString("6ab3ac374251f638d0abb3ef596d1dc67955b507c104e5f2009724812dc027b8", 16)
y0, _ := new(big.Int).SetString("e040bd480b1deccc3bc40bd5b1fdcb7bfd352500b477cb9471366dbd4493f923", 16)
@@ -354,7 +353,7 @@ func BenchmarkGenSharedKeyP256(b *testing.B) {
// Benchmark the generation of S256 shared keys.
func BenchmarkGenSharedKeyS256(b *testing.B) {
- prv, err := GenerateKey(rand.Reader, secp256k1.S256(), nil)
+ prv, err := GenerateKey(rand.Reader, crypto.S256(), nil)
if err != nil {
fmt.Println(err.Error())
b.FailNow()
@@ -597,6 +596,29 @@ func TestBasicKeyValidation(t *testing.T) {
}
}
+func TestBox(t *testing.T) {
+ prv1 := hexKey("4b50fa71f5c3eeb8fdc452224b2395af2fcc3d125e06c32c82e048c0559db03f")
+ prv2 := hexKey("d0b043b4c5d657670778242d82d68a29d25d7d711127d17b8e299f156dad361a")
+ pub2 := &prv2.PublicKey
+
+ message := []byte("Hello, world.")
+ ct, err := Encrypt(rand.Reader, pub2, message, nil, nil)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ pt, err := prv2.Decrypt(rand.Reader, ct, nil, nil)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if !bytes.Equal(pt, message) {
+ t.Fatal("ecies: plaintext doesn't match message")
+ }
+ if _, err = prv1.Decrypt(rand.Reader, ct, nil, nil); err == nil {
+ t.Fatal("ecies: encryption should not have succeeded")
+ }
+}
+
// Verify GenerateShared against static values - useful when
// debugging changes in underlying libs
func TestSharedKeyStatic(t *testing.T) {
@@ -628,11 +650,10 @@ func TestSharedKeyStatic(t *testing.T) {
}
}
-// TODO: remove after refactoring packages crypto and crypto/ecies
func hexKey(prv string) *PrivateKey {
- priv := new(ecdsa.PrivateKey)
- priv.PublicKey.Curve = secp256k1.S256()
- priv.D, _ = new(big.Int).SetString(prv, 16)
- priv.PublicKey.X, priv.PublicKey.Y = secp256k1.S256().ScalarBaseMult(priv.D.Bytes())
- return ImportECDSA(priv)
+ key, err := crypto.HexToECDSA(prv)
+ if err != nil {
+ panic(err)
+ }
+ return ImportECDSA(key)
}
diff --git a/crypto/ecies/params.go b/crypto/ecies/params.go
index 511c53ebc..826d90c84 100644
--- a/crypto/ecies/params.go
+++ b/crypto/ecies/params.go
@@ -42,11 +42,11 @@ import (
"fmt"
"hash"
- "github.com/ethereum/go-ethereum/crypto/secp256k1"
+ ethcrypto "github.com/ethereum/go-ethereum/crypto"
)
var (
- DefaultCurve = secp256k1.S256()
+ DefaultCurve = ethcrypto.S256()
ErrUnsupportedECDHAlgorithm = fmt.Errorf("ecies: unsupported ECDH algorithm")
ErrUnsupportedECIESParameters = fmt.Errorf("ecies: unsupported ECIES parameters")
)
@@ -100,7 +100,7 @@ var (
)
var paramsFromCurve = map[elliptic.Curve]*ECIESParams{
- secp256k1.S256(): ECIES_AES128_SHA256,
+ ethcrypto.S256(): ECIES_AES128_SHA256,
elliptic.P256(): ECIES_AES128_SHA256,
elliptic.P384(): ECIES_AES256_SHA384,
elliptic.P521(): ECIES_AES256_SHA512,
diff --git a/crypto/encrypt_decrypt_test.go b/crypto/encrypt_decrypt_test.go
deleted file mode 100644
index fcf40b70f..000000000
--- a/crypto/encrypt_decrypt_test.go
+++ /dev/null
@@ -1,56 +0,0 @@
-// 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"
- "fmt"
- "testing"
-
- "github.com/ethereum/go-ethereum/common"
-)
-
-func TestBox(t *testing.T) {
- prv1 := ToECDSA(common.Hex2Bytes("4b50fa71f5c3eeb8fdc452224b2395af2fcc3d125e06c32c82e048c0559db03f"))
- prv2 := ToECDSA(common.Hex2Bytes("d0b043b4c5d657670778242d82d68a29d25d7d711127d17b8e299f156dad361a"))
- pub2 := ToECDSAPub(common.Hex2Bytes("04bd27a63c91fe3233c5777e6d3d7b39204d398c8f92655947eb5a373d46e1688f022a1632d264725cbc7dc43ee1cfebde42fa0a86d08b55d2acfbb5e9b3b48dc5"))
-
- message := []byte("Hello, world.")
- ct, err := Encrypt(pub2, message)
- if err != nil {
- fmt.Println(err.Error())
- t.FailNow()
- }
-
- pt, err := Decrypt(prv2, ct)
- if err != nil {
- fmt.Println(err.Error())
- t.FailNow()
- }
-
- if !bytes.Equal(pt, message) {
- fmt.Println("ecies: plaintext doesn't match message")
- t.FailNow()
- }
-
- _, err = Decrypt(prv1, pt)
- if err == nil {
- fmt.Println("ecies: encryption should not have succeeded")
- t.FailNow()
- }
-
-}
diff --git a/crypto/secp256k1/secp256.go b/crypto/secp256k1/secp256.go
index 4284115e2..1a152a670 100644
--- a/crypto/secp256k1/secp256.go
+++ b/crypto/secp256k1/secp256.go
@@ -42,17 +42,9 @@ import (
"unsafe"
)
-var (
- context *C.secp256k1_context
- N *big.Int
- HalfN *big.Int
-)
+var context *C.secp256k1_context
func init() {
- N, _ = new(big.Int).SetString("fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141", 16)
- // N / 2 == 57896044618658097711785492504343953926418782139537452191302581570759080747168
- HalfN, _ = new(big.Int).SetString("7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a0", 16)
-
// around 20 ms on a modern CPU.
context = C.secp256k1_context_create_sign_verify()
C.secp256k1_context_set_illegal_callback(context, C.callbackFunc(C.secp256k1GoPanicIllegal), nil)
diff --git a/crypto/signature_cgo.go b/crypto/signature_cgo.go
new file mode 100644
index 000000000..5faa6061f
--- /dev/null
+++ b/crypto/signature_cgo.go
@@ -0,0 +1,64 @@
+// Copyright 2016 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/>.
+
+// +build !nacl,!js,!nocgo
+
+package crypto
+
+import (
+ "crypto/ecdsa"
+ "crypto/elliptic"
+ "fmt"
+
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/crypto/secp256k1"
+)
+
+func Ecrecover(hash, sig []byte) ([]byte, error) {
+ return secp256k1.RecoverPubkey(hash, sig)
+}
+
+func SigToPub(hash, sig []byte) (*ecdsa.PublicKey, error) {
+ s, err := Ecrecover(hash, sig)
+ if err != nil {
+ return nil, err
+ }
+
+ x, y := elliptic.Unmarshal(S256(), s)
+ return &ecdsa.PublicKey{Curve: S256(), X: x, Y: y}, nil
+}
+
+// Sign calculates an ECDSA signature.
+//
+// This function is susceptible to chosen plaintext attacks that can leak
+// information about the private key that is used for signing. Callers must
+// be aware that the given hash cannot be chosen by an adversery. Common
+// solution is to hash any input before calculating the signature.
+//
+// The produced signature is in the [R || S || V] format where V is 0 or 1.
+func Sign(hash []byte, prv *ecdsa.PrivateKey) (sig []byte, err error) {
+ if len(hash) != 32 {
+ return nil, fmt.Errorf("hash is required to be exactly 32 bytes (%d)", len(hash))
+ }
+ seckey := common.LeftPadBytes(prv.D.Bytes(), prv.Params().BitSize/8)
+ defer zeroBytes(seckey)
+ return secp256k1.Sign(hash, seckey)
+}
+
+// S256 returns an instance of the secp256k1 curve.
+func S256() elliptic.Curve {
+ return secp256k1.S256()
+}
diff --git a/crypto/signature_nocgo.go b/crypto/signature_nocgo.go
new file mode 100644
index 000000000..47880aaf4
--- /dev/null
+++ b/crypto/signature_nocgo.go
@@ -0,0 +1,77 @@
+// Copyright 2016 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/>.
+
+// +build nacl js nocgo
+
+package crypto
+
+import (
+ "crypto/ecdsa"
+ "crypto/elliptic"
+ "fmt"
+
+ "github.com/btcsuite/btcd/btcec"
+)
+
+func Ecrecover(hash, sig []byte) ([]byte, error) {
+ pub, err := SigToPub(hash, sig)
+ if err != nil {
+ return nil, err
+ }
+ bytes := (*btcec.PublicKey)(pub).SerializeUncompressed()
+ return bytes, err
+}
+
+func SigToPub(hash, sig []byte) (*ecdsa.PublicKey, error) {
+ // Convert to btcec input format with 'recovery id' v at the beginning.
+ btcsig := make([]byte, 65)
+ btcsig[0] = sig[64] + 27
+ copy(btcsig[1:], sig)
+
+ pub, _, err := btcec.RecoverCompact(btcec.S256(), btcsig, hash)
+ return (*ecdsa.PublicKey)(pub), err
+}
+
+// Sign calculates an ECDSA signature.
+//
+// This function is susceptible to chosen plaintext attacks that can leak
+// information about the private key that is used for signing. Callers must
+// be aware that the given hash cannot be chosen by an adversery. Common
+// solution is to hash any input before calculating the signature.
+//
+// The produced signature is in the [R || S || V] format where V is 0 or 1.
+func Sign(hash []byte, prv *ecdsa.PrivateKey) ([]byte, error) {
+ if len(hash) != 32 {
+ return nil, fmt.Errorf("hash is required to be exactly 32 bytes (%d)", len(hash))
+ }
+ if prv.Curve != btcec.S256() {
+ return nil, fmt.Errorf("private key curve is not secp256k1")
+ }
+ sig, err := btcec.SignCompact(btcec.S256(), (*btcec.PrivateKey)(prv), hash, false)
+ if err != nil {
+ return nil, err
+ }
+ // Convert to Ethereum signature format with 'recovery id' v at the end.
+ v := sig[0] - 27
+ copy(sig, sig[1:])
+ sig[64] = v
+ return sig, nil
+}
+
+// S256 returns an instance of the secp256k1 curve.
+func S256() elliptic.Curve {
+ return btcec.S256()
+}
diff --git a/crypto/signature_test.go b/crypto/signature_test.go
new file mode 100644
index 000000000..ca7eefe99
--- /dev/null
+++ b/crypto/signature_test.go
@@ -0,0 +1,36 @@
+// Copyright 2016 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"
+ "encoding/hex"
+ "testing"
+)
+
+func TestRecoverSanity(t *testing.T) {
+ msg, _ := hex.DecodeString("ce0677bb30baa8cf067c88db9811f4333d131bf8bcf12fe7065d211dce971008")
+ sig, _ := hex.DecodeString("90f27b8b488db00b00606796d2987f6a5f59ae62ea05effe84fef5b8b0e549984a691139ad57a3f0b906637673aa2f63d1f55cb1a69199d4009eea23ceaddc9301")
+ pubkey1, _ := hex.DecodeString("04e32df42865e97135acfb65f3bae71bdc86f4d49150ad6a440b6f15878109880a0a2b2667f7e725ceea70c673093bf67663e0312623c8e091b13cf2c0f11ef652")
+ pubkey2, err := Ecrecover(msg, sig)
+ if err != nil {
+ t.Fatalf("recover error: %s", err)
+ }
+ if !bytes.Equal(pubkey1, pubkey2) {
+ t.Errorf("pubkey mismatch: want: %x have: %x", pubkey1, pubkey2)
+ }
+}