Update from go-ethereum.

This commit is contained in:
Yohan Graterol 2017-12-08 16:54:09 -05:00
commit 79348902e5
38 changed files with 630 additions and 407 deletions

View file

@ -8,7 +8,6 @@ matrix:
sudo: required sudo: required
go: 1.7.x go: 1.7.x
script: script:
- sudo -E apt-get -yq --no-install-suggests --no-install-recommends --force-yes install fuse
- sudo modprobe fuse - sudo modprobe fuse
- sudo chmod 666 /dev/fuse - sudo chmod 666 /dev/fuse
- sudo chown root:$USER /etc/fuse.conf - sudo chown root:$USER /etc/fuse.conf
@ -20,7 +19,6 @@ matrix:
sudo: required sudo: required
go: 1.8.x go: 1.8.x
script: script:
- sudo -E apt-get -yq --no-install-suggests --no-install-recommends --force-yes install fuse
- sudo modprobe fuse - sudo modprobe fuse
- sudo chmod 666 /dev/fuse - sudo chmod 666 /dev/fuse
- sudo chown root:$USER /etc/fuse.conf - sudo chown root:$USER /etc/fuse.conf
@ -33,7 +31,6 @@ matrix:
sudo: required sudo: required
go: 1.9.x go: 1.9.x
script: script:
- sudo -E apt-get -yq --no-install-suggests --no-install-recommends --force-yes install fuse
- sudo modprobe fuse - sudo modprobe fuse
- sudo chmod 666 /dev/fuse - sudo chmod 666 /dev/fuse
- sudo chown root:$USER /etc/fuse.conf - sudo chown root:$USER /etc/fuse.conf
@ -42,7 +39,6 @@ matrix:
- os: osx - os: osx
go: 1.9.x go: 1.9.x
sudo: required
script: script:
- brew update - brew update
- brew install caskroom/cask/brew-cask - brew install caskroom/cask/brew-cask
@ -53,15 +49,12 @@ matrix:
# This builder only tests code linters on latest version of Go # This builder only tests code linters on latest version of Go
- os: linux - os: linux
dist: trusty dist: trusty
sudo: required
go: 1.9.x go: 1.9.x
env: env:
- lint - lint
git:
submodules: false # avoid cloning ethereum/tests
script: script:
- sudo -E apt-get -yq --no-install-suggests --no-install-recommends --force-yes install fuse
- sudo modprobe fuse
- sudo chmod 666 /dev/fuse
- sudo chown root:$USER /etc/fuse.conf
- go run build/ci.go lint - go run build/ci.go lint
# This builder does the Ubuntu PPA and Linux Azure uploads # This builder does the Ubuntu PPA and Linux Azure uploads
@ -72,6 +65,8 @@ matrix:
env: env:
- ubuntu-ppa - ubuntu-ppa
- azure-linux - azure-linux
git:
submodules: false # avoid cloning ethereum/tests
addons: addons:
apt: apt:
packages: packages:
@ -104,12 +99,13 @@ matrix:
# This builder does the Linux Azure MIPS xgo uploads # This builder does the Linux Azure MIPS xgo uploads
- os: linux - os: linux
dist: trusty dist: trusty
sudo: required
services: services:
- docker - docker
go: 1.9.x go: 1.9.x
env: env:
- azure-linux-mips - azure-linux-mips
git:
submodules: false # avoid cloning ethereum/tests
script: script:
- go run build/ci.go xgo --alltools -- --targets=linux/mips --ldflags '-extldflags "-static"' -v - go run build/ci.go xgo --alltools -- --targets=linux/mips --ldflags '-extldflags "-static"' -v
- for bin in build/bin/*-linux-mips; do mv -f "${bin}" "${bin/-linux-mips/}"; done - for bin in build/bin/*-linux-mips; do mv -f "${bin}" "${bin/-linux-mips/}"; done
@ -146,6 +142,8 @@ matrix:
env: env:
- azure-android - azure-android
- maven-android - maven-android
git:
submodules: false # avoid cloning ethereum/tests
before_install: before_install:
- curl https://storage.googleapis.com/golang/go1.9.2.linux-amd64.tar.gz | tar -xz - curl https://storage.googleapis.com/golang/go1.9.2.linux-amd64.tar.gz | tar -xz
- export PATH=`pwd`/go/bin:$PATH - export PATH=`pwd`/go/bin:$PATH
@ -169,6 +167,8 @@ matrix:
- azure-osx - azure-osx
- azure-ios - azure-ios
- cocoapods-ios - cocoapods-ios
git:
submodules: false # avoid cloning ethereum/tests
script: script:
- go run build/ci.go install - go run build/ci.go install
- go run build/ci.go archive -type tar -signer OSX_SIGNING_KEY -upload gethstore/builds - go run build/ci.go archive -type tar -signer OSX_SIGNING_KEY -upload gethstore/builds
@ -193,15 +193,11 @@ matrix:
go: 1.9.x go: 1.9.x
env: env:
- azure-purge - azure-purge
git:
submodules: false # avoid cloning ethereum/tests
script: script:
- go run build/ci.go purge -store gethstore/builds -days 14 - go run build/ci.go purge -store gethstore/builds -days 14
install:
- go get golang.org/x/tools/cmd/cover
script:
- go run build/ci.go install
- go run build/ci.go test -coverage
notifications: notifications:
webhooks: webhooks:
urls: urls:

View file

@ -20,6 +20,7 @@ import (
"fmt" "fmt"
"os" "os"
"os/signal" "os/signal"
"path/filepath"
"strings" "strings"
"github.com/EthereumCommonwealth/go-callisto/cmd/utils" "github.com/EthereumCommonwealth/go-callisto/cmd/utils"
@ -114,8 +115,15 @@ func localConsole(ctx *cli.Context) error {
func remoteConsole(ctx *cli.Context) error { func remoteConsole(ctx *cli.Context) error {
// Attach to a remotely running geth instance and start the JavaScript console // Attach to a remotely running geth instance and start the JavaScript console
endpoint := ctx.Args().First() endpoint := ctx.Args().First()
if endpoint == "" && ctx.GlobalIsSet(utils.DataDirFlag.Name) { if endpoint == "" {
endpoint = fmt.Sprintf("%s/geth.ipc", ctx.GlobalString(utils.DataDirFlag.Name)) path := node.DefaultDataDir()
if ctx.GlobalIsSet(utils.DataDirFlag.Name) {
path = ctx.GlobalString(utils.DataDirFlag.Name)
}
if path != "" && ctx.GlobalBool(utils.TestnetFlag.Name) {
path = filepath.Join(path, "testnet")
}
endpoint = fmt.Sprintf("%s/geth.ipc", path)
} }
client, err := dialRPC(endpoint) client, err := dialRPC(endpoint)
if err != nil { if err != nil {

View file

@ -17,9 +17,7 @@
// Package common contains various helper functions. // Package common contains various helper functions.
package common package common
import ( import "encoding/hex"
"encoding/hex"
)
func ToHex(b []byte) string { func ToHex(b []byte) string {
hex := Bytes2Hex(b) hex := Bytes2Hex(b)
@ -55,14 +53,24 @@ func CopyBytes(b []byte) (copiedBytes []byte) {
return return
} }
func HasHexPrefix(str string) bool { func hasHexPrefix(str string) bool {
l := len(str) return len(str) >= 2 && str[0] == '0' && (str[1] == 'x' || str[1] == 'X')
return l >= 2 && str[0:2] == "0x"
} }
func IsHex(str string) bool { func isHexCharacter(c byte) bool {
l := len(str) return ('0' <= c && c <= '9') || ('a' <= c && c <= 'f') || ('A' <= c && c <= 'F')
return l >= 4 && l%2 == 0 && str[0:2] == "0x" }
func isHex(str string) bool {
if len(str)%2 != 0 {
return false
}
for _, c := range []byte(str) {
if !isHexCharacter(c) {
return false
}
}
return true
} }
func Bytes2Hex(d []byte) string { func Bytes2Hex(d []byte) string {

View file

@ -34,19 +34,6 @@ func (s *BytesSuite) TestCopyBytes(c *checker.C) {
c.Assert(res1, checker.DeepEquals, exp1) c.Assert(res1, checker.DeepEquals, exp1)
} }
func (s *BytesSuite) TestIsHex(c *checker.C) {
data1 := "a9e67e"
exp1 := false
res1 := IsHex(data1)
c.Assert(res1, checker.DeepEquals, exp1)
data2 := "0xa9e67e00"
exp2 := true
res2 := IsHex(data2)
c.Assert(res2, checker.DeepEquals, exp2)
}
func (s *BytesSuite) TestLeftPadBytes(c *checker.C) { func (s *BytesSuite) TestLeftPadBytes(c *checker.C) {
val1 := []byte{1, 2, 3, 4} val1 := []byte{1, 2, 3, 4}
exp1 := []byte{0, 0, 0, 0, 1, 2, 3, 4} exp1 := []byte{0, 0, 0, 0, 1, 2, 3, 4}
@ -78,6 +65,27 @@ func TestFromHex(t *testing.T) {
} }
} }
func TestIsHex(t *testing.T) {
tests := []struct {
input string
ok bool
}{
{"", true},
{"0", false},
{"00", true},
{"a9e67e", true},
{"A9E67E", true},
{"0xa9e67e", false},
{"a9e67e001", false},
{"0xHELLO_MY_NAME_IS_STEVEN_@#$^&*", false},
}
for _, test := range tests {
if ok := isHex(test.input); ok != test.ok {
t.Errorf("isHex(%q) = %v, want %v", test.input, ok, test.ok)
}
}
}
func TestFromHexOddLength(t *testing.T) { func TestFromHexOddLength(t *testing.T) {
input := "0x1" input := "0x1"
expected := []byte{1} expected := []byte{1}

View file

@ -150,13 +150,10 @@ func HexToAddress(s string) Address { return BytesToAddress(FromHex(s)) }
// IsHexAddress verifies whether a string can represent a valid hex-encoded // IsHexAddress verifies whether a string can represent a valid hex-encoded
// Ethereum address or not. // Ethereum address or not.
func IsHexAddress(s string) bool { func IsHexAddress(s string) bool {
if len(s) == 2+2*AddressLength && IsHex(s) { if hasHexPrefix(s) {
return true s = s[2:]
} }
if len(s) == 2*AddressLength && IsHex("0x"+s) { return len(s) == 2*AddressLength && isHex(s)
return true
}
return false
} }
// Get the string representation of the underlying address // Get the string representation of the underlying address

View file

@ -35,6 +35,30 @@ func TestBytesConversion(t *testing.T) {
} }
} }
func TestIsHexAddress(t *testing.T) {
tests := []struct {
str string
exp bool
}{
{"0x5aaeb6053f3e94c9b9a09f33669435e7ef1beaed", true},
{"5aaeb6053f3e94c9b9a09f33669435e7ef1beaed", true},
{"0X5aaeb6053f3e94c9b9a09f33669435e7ef1beaed", true},
{"0XAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", true},
{"0xAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", true},
{"0x5aaeb6053f3e94c9b9a09f33669435e7ef1beaed1", false},
{"0x5aaeb6053f3e94c9b9a09f33669435e7ef1beae", false},
{"5aaeb6053f3e94c9b9a09f33669435e7ef1beaed11", false},
{"0xxaaeb6053f3e94c9b9a09f33669435e7ef1beaed", false},
}
for _, test := range tests {
if result := IsHexAddress(test.str); result != test.exp {
t.Errorf("IsHexAddress(%s) == %v; expected %v",
test.str, result, test.exp)
}
}
}
func TestHashJsonValidation(t *testing.T) { func TestHashJsonValidation(t *testing.T) {
var tests = []struct { var tests = []struct {
Prefix string Prefix string

View file

@ -192,6 +192,7 @@ func (c *Console) init(preload []string) error {
if obj := admin.Object(); obj != nil { // make sure the admin api is enabled over the interface if obj := admin.Object(); obj != nil { // make sure the admin api is enabled over the interface
obj.Set("sleepBlocks", bridge.SleepBlocks) obj.Set("sleepBlocks", bridge.SleepBlocks)
obj.Set("sleep", bridge.Sleep) obj.Set("sleep", bridge.Sleep)
obj.Set("clearHistory", c.clearHistory)
} }
// Preload any JavaScript files before starting the console // Preload any JavaScript files before starting the console
for _, path := range preload { for _, path := range preload {
@ -216,6 +217,16 @@ func (c *Console) init(preload []string) error {
return nil return nil
} }
func (c *Console) clearHistory() {
c.history = nil
c.prompter.ClearHistory()
if err := os.Remove(c.histPath); err != nil {
fmt.Fprintln(c.printer, "can't delete history file:", err)
} else {
fmt.Fprintln(c.printer, "history file deleted")
}
}
// consoleOutput is an override for the console.log and console.error methods to // consoleOutput is an override for the console.log and console.error methods to
// stream the output into the configured output stream instead of stdout. // stream the output into the configured output stream instead of stdout.
func (c *Console) consoleOutput(call otto.FunctionCall) otto.Value { func (c *Console) consoleOutput(call otto.FunctionCall) otto.Value {

View file

@ -68,6 +68,7 @@ func (p *hookedPrompter) PromptConfirm(prompt string) (bool, error) {
} }
func (p *hookedPrompter) SetHistory(history []string) {} func (p *hookedPrompter) SetHistory(history []string) {}
func (p *hookedPrompter) AppendHistory(command string) {} func (p *hookedPrompter) AppendHistory(command string) {}
func (p *hookedPrompter) ClearHistory() {}
func (p *hookedPrompter) SetWordCompleter(completer WordCompleter) {} func (p *hookedPrompter) SetWordCompleter(completer WordCompleter) {}
// tester is a console test environment for the console tests to operate on. // tester is a console test environment for the console tests to operate on.

View file

@ -51,6 +51,9 @@ type UserPrompter interface {
// if and only if the prompt to append was a valid command. // if and only if the prompt to append was a valid command.
AppendHistory(command string) AppendHistory(command string)
// ClearHistory clears the entire history
ClearHistory()
// SetWordCompleter sets the completion function that the prompter will call to // SetWordCompleter sets the completion function that the prompter will call to
// fetch completion candidates when the user presses tab. // fetch completion candidates when the user presses tab.
SetWordCompleter(completer WordCompleter) SetWordCompleter(completer WordCompleter)
@ -158,6 +161,11 @@ func (p *terminalPrompter) AppendHistory(command string) {
p.State.AppendHistory(command) p.State.AppendHistory(command)
} }
// ClearHistory clears the entire history
func (p *terminalPrompter) ClearHistory() {
p.State.ClearHistory()
}
// SetWordCompleter sets the completion function that the prompter will call to // SetWordCompleter sets the completion function that the prompter will call to
// fetch completion candidates when the user presses tab. // fetch completion candidates when the user presses tab.
func (p *terminalPrompter) SetWordCompleter(completer WordCompleter) { func (p *terminalPrompter) SetWordCompleter(completer WordCompleter) {

View file

@ -137,7 +137,7 @@ func isProtectedV(V *big.Int) bool {
return true return true
} }
// DecodeRLP implements rlp.Encoder // EncodeRLP implements rlp.Encoder
func (tx *Transaction) EncodeRLP(w io.Writer) error { func (tx *Transaction) EncodeRLP(w io.Writer) error {
return rlp.Encode(w, &tx.data) return rlp.Encode(w, &tx.data)
} }

View file

@ -20,12 +20,10 @@ import (
"bytes" "bytes"
"crypto/ecdsa" "crypto/ecdsa"
"encoding/hex" "encoding/hex"
"fmt"
"io/ioutil" "io/ioutil"
"math/big" "math/big"
"os" "os"
"testing" "testing"
"time"
"github.com/EthereumCommonwealth/go-callisto/common" "github.com/EthereumCommonwealth/go-callisto/common"
) )
@ -44,13 +42,9 @@ func TestKeccak256Hash(t *testing.T) {
func BenchmarkSha3(b *testing.B) { func BenchmarkSha3(b *testing.B) {
a := []byte("hello world") a := []byte("hello world")
amount := 1000000 for i := 0; i < b.N; i++ {
start := time.Now()
for i := 0; i < amount; i++ {
Keccak256(a) Keccak256(a)
} }
fmt.Println(amount, ":", time.Since(start))
} }
func TestSign(t *testing.T) { func TestSign(t *testing.T) {

View file

@ -46,6 +46,55 @@ static int secp256k1_ecdsa_recover_pubkey(
return secp256k1_ec_pubkey_serialize(ctx, pubkey_out, &outputlen, &pubkey, SECP256K1_EC_UNCOMPRESSED); return secp256k1_ec_pubkey_serialize(ctx, pubkey_out, &outputlen, &pubkey, SECP256K1_EC_UNCOMPRESSED);
} }
// secp256k1_ecdsa_verify_enc verifies an encoded compact signature.
//
// Returns: 1: signature is valid
// 0: signature is invalid
// Args: ctx: pointer to a context object (cannot be NULL)
// In: sigdata: pointer to a 64-byte signature (cannot be NULL)
// msgdata: pointer to a 32-byte message (cannot be NULL)
// pubkeydata: pointer to public key data (cannot be NULL)
// pubkeylen: length of pubkeydata
static int secp256k1_ecdsa_verify_enc(
const secp256k1_context* ctx,
const unsigned char *sigdata,
const unsigned char *msgdata,
const unsigned char *pubkeydata,
size_t pubkeylen
) {
secp256k1_ecdsa_signature sig;
secp256k1_pubkey pubkey;
if (!secp256k1_ecdsa_signature_parse_compact(ctx, &sig, sigdata)) {
return 0;
}
if (!secp256k1_ec_pubkey_parse(ctx, &pubkey, pubkeydata, pubkeylen)) {
return 0;
}
return secp256k1_ecdsa_verify(ctx, &sig, msgdata, &pubkey);
}
// secp256k1_decompress_pubkey decompresses a public key.
//
// Returns: 1: public key is valid
// 0: public key is invalid
// Args: ctx: pointer to a context object (cannot be NULL)
// Out: pubkey_out: the serialized 65-byte public key (cannot be NULL)
// In: pubkeydata: pointer to 33 bytes of compressed public key data (cannot be NULL)
static int secp256k1_decompress_pubkey(
const secp256k1_context* ctx,
unsigned char *pubkey_out,
const unsigned char *pubkeydata
) {
secp256k1_pubkey pubkey;
if (!secp256k1_ec_pubkey_parse(ctx, &pubkey, pubkeydata, 33)) {
return 0;
}
size_t outputlen = 65;
return secp256k1_ec_pubkey_serialize(ctx, pubkey_out, &outputlen, &pubkey, SECP256K1_EC_UNCOMPRESSED);
}
// secp256k1_pubkey_scalar_mul multiplies a point by a scalar in constant time. // secp256k1_pubkey_scalar_mul multiplies a point by a scalar in constant time.
// //
// Returns: 1: multiplication was successful // Returns: 1: multiplication was successful

View file

@ -38,6 +38,7 @@ import "C"
import ( import (
"errors" "errors"
"math/big"
"unsafe" "unsafe"
) )
@ -55,6 +56,7 @@ var (
ErrInvalidSignatureLen = errors.New("invalid signature length") ErrInvalidSignatureLen = errors.New("invalid signature length")
ErrInvalidRecoveryID = errors.New("invalid signature recovery id") ErrInvalidRecoveryID = errors.New("invalid signature recovery id")
ErrInvalidKey = errors.New("invalid private key") ErrInvalidKey = errors.New("invalid private key")
ErrInvalidPubkey = errors.New("invalid public key")
ErrSignFailed = errors.New("signing failed") ErrSignFailed = errors.New("signing failed")
ErrRecoverFailed = errors.New("recovery failed") ErrRecoverFailed = errors.New("recovery failed")
) )
@ -119,6 +121,33 @@ func RecoverPubkey(msg []byte, sig []byte) ([]byte, error) {
return pubkey, nil return pubkey, nil
} }
// VerifySignature checks that the given pubkey created signature over message.
// The signature should be in [R || S] format.
func VerifySignature(pubkey, msg, signature []byte) bool {
if len(msg) != 32 || len(signature) != 64 || len(pubkey) == 0 {
return false
}
sigdata := (*C.uchar)(unsafe.Pointer(&signature[0]))
msgdata := (*C.uchar)(unsafe.Pointer(&msg[0]))
keydata := (*C.uchar)(unsafe.Pointer(&pubkey[0]))
return C.secp256k1_ecdsa_verify_enc(context, sigdata, msgdata, keydata, C.size_t(len(pubkey))) != 0
}
// DecompressPubkey parses a public key in the 33-byte compressed format.
// It returns non-nil coordinates if the public key is valid.
func DecompressPubkey(pubkey []byte) (X, Y *big.Int) {
if len(pubkey) != 33 {
return nil, nil
}
buf := make([]byte, 65)
bufdata := (*C.uchar)(unsafe.Pointer(&buf[0]))
pubkeydata := (*C.uchar)(unsafe.Pointer(&pubkey[0]))
if C.secp256k1_decompress_pubkey(context, bufdata, pubkeydata) == 0 {
return nil, nil
}
return new(big.Int).SetBytes(buf[1:33]), new(big.Int).SetBytes(buf[33:])
}
func checkSignature(sig []byte) error { func checkSignature(sig []byte) error {
if len(sig) != 65 { if len(sig) != 65 {
return ErrInvalidSignatureLen return ErrInvalidSignatureLen

View file

@ -27,10 +27,12 @@ import (
"github.com/EthereumCommonwealth/go-callisto/crypto/secp256k1" "github.com/EthereumCommonwealth/go-callisto/crypto/secp256k1"
) )
// Ecrecover returns the uncompressed public key that created the given signature.
func Ecrecover(hash, sig []byte) ([]byte, error) { func Ecrecover(hash, sig []byte) ([]byte, error) {
return secp256k1.RecoverPubkey(hash, sig) return secp256k1.RecoverPubkey(hash, sig)
} }
// SigToPub returns the public key that created the given signature.
func SigToPub(hash, sig []byte) (*ecdsa.PublicKey, error) { func SigToPub(hash, sig []byte) (*ecdsa.PublicKey, error) {
s, err := Ecrecover(hash, sig) s, err := Ecrecover(hash, sig)
if err != nil { if err != nil {
@ -58,6 +60,22 @@ func Sign(hash []byte, prv *ecdsa.PrivateKey) (sig []byte, err error) {
return secp256k1.Sign(hash, seckey) return secp256k1.Sign(hash, seckey)
} }
// VerifySignature checks that the given public key created signature over hash.
// The public key should be in compressed (33 bytes) or uncompressed (65 bytes) format.
// The signature should have the 64 byte [R || S] format.
func VerifySignature(pubkey, hash, signature []byte) bool {
return secp256k1.VerifySignature(pubkey, hash, signature)
}
// DecompressPubkey parses a public key in the 33-byte compressed format.
func DecompressPubkey(pubkey []byte) (*ecdsa.PublicKey, error) {
x, y := secp256k1.DecompressPubkey(pubkey)
if x == nil {
return nil, fmt.Errorf("invalid public key")
}
return &ecdsa.PublicKey{X: x, Y: y, Curve: S256()}, nil
}
// S256 returns an instance of the secp256k1 curve. // S256 returns an instance of the secp256k1 curve.
func S256() elliptic.Curve { func S256() elliptic.Curve {
return secp256k1.S256() return secp256k1.S256()

View file

@ -21,11 +21,14 @@ package crypto
import ( import (
"crypto/ecdsa" "crypto/ecdsa"
"crypto/elliptic" "crypto/elliptic"
"errors"
"fmt" "fmt"
"math/big"
"github.com/btcsuite/btcd/btcec" "github.com/btcsuite/btcd/btcec"
) )
// Ecrecover returns the uncompressed public key that created the given signature.
func Ecrecover(hash, sig []byte) ([]byte, error) { func Ecrecover(hash, sig []byte) ([]byte, error) {
pub, err := SigToPub(hash, sig) pub, err := SigToPub(hash, sig)
if err != nil { if err != nil {
@ -35,6 +38,7 @@ func Ecrecover(hash, sig []byte) ([]byte, error) {
return bytes, err return bytes, err
} }
// SigToPub returns the public key that created the given signature.
func SigToPub(hash, sig []byte) (*ecdsa.PublicKey, error) { func SigToPub(hash, sig []byte) (*ecdsa.PublicKey, error) {
// Convert to btcec input format with 'recovery id' v at the beginning. // Convert to btcec input format with 'recovery id' v at the beginning.
btcsig := make([]byte, 65) btcsig := make([]byte, 65)
@ -71,6 +75,33 @@ func Sign(hash []byte, prv *ecdsa.PrivateKey) ([]byte, error) {
return sig, nil return sig, nil
} }
// VerifySignature checks that the given public key created signature over hash.
// The public key should be in compressed (33 bytes) or uncompressed (65 bytes) format.
// The signature should have the 64 byte [R || S] format.
func VerifySignature(pubkey, hash, signature []byte) bool {
if len(signature) != 64 {
return false
}
sig := &btcec.Signature{R: new(big.Int).SetBytes(signature[:32]), S: new(big.Int).SetBytes(signature[32:])}
key, err := btcec.ParsePubKey(pubkey, btcec.S256())
if err != nil {
return false
}
return sig.Verify(hash, key)
}
// DecompressPubkey parses a public key in the 33-byte compressed format.
func DecompressPubkey(pubkey []byte) (*ecdsa.PublicKey, error) {
if len(pubkey) != 33 {
return nil, errors.New("invalid compressed public key length")
}
key, err := btcec.ParsePubKey(pubkey, btcec.S256())
if err != nil {
return nil, err
}
return key.ToECDSA(), nil
}
// S256 returns an instance of the secp256k1 curve. // S256 returns an instance of the secp256k1 curve.
func S256() elliptic.Curve { func S256() elliptic.Curve {
return btcec.S256() return btcec.S256()

View file

@ -18,19 +18,95 @@ package crypto
import ( import (
"bytes" "bytes"
"encoding/hex"
"testing" "testing"
"github.com/EthereumCommonwealth/go-callisto/common"
"github.com/EthereumCommonwealth/go-callisto/common/hexutil"
) )
func TestRecoverSanity(t *testing.T) { var (
msg, _ := hex.DecodeString("ce0677bb30baa8cf067c88db9811f4333d131bf8bcf12fe7065d211dce971008") testmsg = hexutil.MustDecode("0xce0677bb30baa8cf067c88db9811f4333d131bf8bcf12fe7065d211dce971008")
sig, _ := hex.DecodeString("90f27b8b488db00b00606796d2987f6a5f59ae62ea05effe84fef5b8b0e549984a691139ad57a3f0b906637673aa2f63d1f55cb1a69199d4009eea23ceaddc9301") testsig = hexutil.MustDecode("0x90f27b8b488db00b00606796d2987f6a5f59ae62ea05effe84fef5b8b0e549984a691139ad57a3f0b906637673aa2f63d1f55cb1a69199d4009eea23ceaddc9301")
pubkey1, _ := hex.DecodeString("04e32df42865e97135acfb65f3bae71bdc86f4d49150ad6a440b6f15878109880a0a2b2667f7e725ceea70c673093bf67663e0312623c8e091b13cf2c0f11ef652") testpubkey = hexutil.MustDecode("0x04e32df42865e97135acfb65f3bae71bdc86f4d49150ad6a440b6f15878109880a0a2b2667f7e725ceea70c673093bf67663e0312623c8e091b13cf2c0f11ef652")
pubkey2, err := Ecrecover(msg, sig) testpubkeyc = hexutil.MustDecode("0x02e32df42865e97135acfb65f3bae71bdc86f4d49150ad6a440b6f15878109880a")
)
func TestEcrecover(t *testing.T) {
pubkey, err := Ecrecover(testmsg, testsig)
if err != nil { if err != nil {
t.Fatalf("recover error: %s", err) t.Fatalf("recover error: %s", err)
} }
if !bytes.Equal(pubkey1, pubkey2) { if !bytes.Equal(pubkey, testpubkey) {
t.Errorf("pubkey mismatch: want: %x have: %x", pubkey1, pubkey2) t.Errorf("pubkey mismatch: want: %x have: %x", testpubkey, pubkey)
}
}
func TestVerifySignature(t *testing.T) {
sig := testsig[:len(testsig)-1] // remove recovery id
if !VerifySignature(testpubkey, testmsg, sig) {
t.Errorf("can't verify signature with uncompressed key")
}
if !VerifySignature(testpubkeyc, testmsg, sig) {
t.Errorf("can't verify signature with compressed key")
}
if VerifySignature(nil, testmsg, sig) {
t.Errorf("signature valid with no key")
}
if VerifySignature(testpubkey, nil, sig) {
t.Errorf("signature valid with no message")
}
if VerifySignature(testpubkey, testmsg, nil) {
t.Errorf("nil signature valid")
}
if VerifySignature(testpubkey, testmsg, append(common.CopyBytes(sig), 1, 2, 3)) {
t.Errorf("signature valid with extra bytes at the end")
}
if VerifySignature(testpubkey, testmsg, sig[:len(sig)-2]) {
t.Errorf("signature valid even though it's incomplete")
}
}
func TestDecompressPubkey(t *testing.T) {
key, err := DecompressPubkey(testpubkeyc)
if err != nil {
t.Fatal(err)
}
if uncompressed := FromECDSAPub(key); !bytes.Equal(uncompressed, testpubkey) {
t.Errorf("wrong public key result: got %x, want %x", uncompressed, testpubkey)
}
if _, err := DecompressPubkey(nil); err == nil {
t.Errorf("no error for nil pubkey")
}
if _, err := DecompressPubkey(testpubkeyc[:5]); err == nil {
t.Errorf("no error for incomplete pubkey")
}
if _, err := DecompressPubkey(append(common.CopyBytes(testpubkeyc), 1, 2, 3)); err == nil {
t.Errorf("no error for pubkey with extra bytes at the end")
}
}
func BenchmarkEcrecoverSignature(b *testing.B) {
for i := 0; i < b.N; i++ {
if _, err := Ecrecover(testmsg, testsig); err != nil {
b.Fatal("ecrecover error", err)
}
}
}
func BenchmarkVerifySignature(b *testing.B) {
sig := testsig[:len(testsig)-1] // remove recovery id
for i := 0; i < b.N; i++ {
if !VerifySignature(testpubkey, testmsg, sig) {
b.Fatal("verify error")
}
}
}
func BenchmarkDecompressPubkey(b *testing.B) {
for i := 0; i < b.N; i++ {
if _, err := DecompressPubkey(testpubkeyc); err != nil {
b.Fatal(err)
}
} }
} }

View file

@ -615,14 +615,18 @@ func (api *PrivateDebugAPI) StorageRangeAt(ctx context.Context, blockHash common
if st == nil { if st == nil {
return StorageRangeResult{}, fmt.Errorf("account %x doesn't exist", contractAddress) return StorageRangeResult{}, fmt.Errorf("account %x doesn't exist", contractAddress)
} }
return storageRangeAt(st, keyStart, maxResult), nil return storageRangeAt(st, keyStart, maxResult)
} }
func storageRangeAt(st state.Trie, start []byte, maxResult int) StorageRangeResult { func storageRangeAt(st state.Trie, start []byte, maxResult int) (StorageRangeResult, error) {
it := trie.NewIterator(st.NodeIterator(start)) it := trie.NewIterator(st.NodeIterator(start))
result := StorageRangeResult{Storage: storageMap{}} result := StorageRangeResult{Storage: storageMap{}}
for i := 0; i < maxResult && it.Next(); i++ { for i := 0; i < maxResult && it.Next(); i++ {
e := storageEntry{Value: common.BytesToHash(it.Value)} _, content, _, err := rlp.Split(it.Value)
if err != nil {
return StorageRangeResult{}, err
}
e := storageEntry{Value: common.BytesToHash(content)}
if preimage := st.GetKey(it.Key); preimage != nil { if preimage := st.GetKey(it.Key); preimage != nil {
preimage := common.BytesToHash(preimage) preimage := common.BytesToHash(preimage)
e.Key = &preimage e.Key = &preimage
@ -634,7 +638,7 @@ func storageRangeAt(st state.Trie, start []byte, maxResult int) StorageRangeResu
next := common.BytesToHash(it.Key) next := common.BytesToHash(it.Key)
result.NextKey = &next result.NextKey = &next
} }
return result return result, nil
} }
// GetModifiedAccountsByumber returns all accounts that have changed between the // GetModifiedAccountsByumber returns all accounts that have changed between the

View file

@ -79,7 +79,10 @@ func TestStorageRangeAt(t *testing.T) {
}, },
} }
for _, test := range tests { for _, test := range tests {
result := storageRangeAt(state.StorageTrie(addr), test.start, test.limit) result, err := storageRangeAt(state.StorageTrie(addr), test.start, test.limit)
if err != nil {
t.Error(err)
}
if !reflect.DeepEqual(result, test.want) { if !reflect.DeepEqual(result, test.want) {
t.Fatalf("wrong result for range 0x%x.., limit %d:\ngot %s\nwant %s", t.Fatalf("wrong result for range 0x%x.., limit %d:\ngot %s\nwant %s",
test.start, test.limit, dumper.Sdump(result), dumper.Sdump(&test.want)) test.start, test.limit, dumper.Sdump(result), dumper.Sdump(&test.want))

View file

@ -157,7 +157,7 @@ func (s *dialstate) removeStatic(n *discover.Node) {
} }
func (s *dialstate) newTasks(nRunning int, peers map[discover.NodeID]*Peer, now time.Time) []task { func (s *dialstate) newTasks(nRunning int, peers map[discover.NodeID]*Peer, now time.Time) []task {
if s.start == (time.Time{}) { if s.start.IsZero() {
s.start = now s.start = now
} }

View file

@ -330,7 +330,7 @@ func (self *KadDb) load(path string, cb func(*NodeRecord, Node) error) (err erro
} }
} }
n++ n++
if (node.After == time.Time{}) { if node.After.IsZero() {
node.After = time.Now() node.After = time.Now()
} }
self.index[node.Addr] = node self.index[node.Addr] = node

View file

@ -1,11 +1,9 @@
btcec btcec
===== =====
[![Build Status](https://travis-ci.org/btcsuite/btcd.png?branch=master)] [![Build Status](https://travis-ci.org/btcsuite/btcd.png?branch=master)](https://travis-ci.org/btcsuite/btcec)
(https://travis-ci.org/btcsuite/btcec) [![ISC License] [![ISC License](http://img.shields.io/badge/license-ISC-blue.svg)](http://copyfree.org)
(http://img.shields.io/badge/license-ISC-blue.svg)](http://copyfree.org) [![GoDoc](https://godoc.org/github.com/btcsuite/btcd/btcec?status.png)](http://godoc.org/github.com/btcsuite/btcd/btcec)
[![GoDoc](https://godoc.org/github.com/btcsuite/btcd/btcec?status.png)]
(http://godoc.org/github.com/btcsuite/btcd/btcec)
Package btcec implements elliptic curve cryptography needed for working with Package btcec implements elliptic curve cryptography needed for working with
Bitcoin (secp256k1 only for now). It is designed so that it may be used with the Bitcoin (secp256k1 only for now). It is designed so that it may be used with the
@ -27,23 +25,19 @@ $ go get -u github.com/btcsuite/btcd/btcec
## Examples ## Examples
* [Sign Message] * [Sign Message](http://godoc.org/github.com/btcsuite/btcd/btcec#example-package--SignMessage)
(http://godoc.org/github.com/btcsuite/btcd/btcec#example-package--SignMessage)
Demonstrates signing a message with a secp256k1 private key that is first Demonstrates signing a message with a secp256k1 private key that is first
parsed form raw bytes and serializing the generated signature. parsed form raw bytes and serializing the generated signature.
* [Verify Signature] * [Verify Signature](http://godoc.org/github.com/btcsuite/btcd/btcec#example-package--VerifySignature)
(http://godoc.org/github.com/btcsuite/btcd/btcec#example-package--VerifySignature)
Demonstrates verifying a secp256k1 signature against a public key that is Demonstrates verifying a secp256k1 signature against a public key that is
first parsed from raw bytes. The signature is also parsed from raw bytes. first parsed from raw bytes. The signature is also parsed from raw bytes.
* [Encryption] * [Encryption](http://godoc.org/github.com/btcsuite/btcd/btcec#example-package--EncryptMessage)
(http://godoc.org/github.com/btcsuite/btcd/btcec#example-package--EncryptMessage)
Demonstrates encrypting a message for a public key that is first parsed from Demonstrates encrypting a message for a public key that is first parsed from
raw bytes, then decrypting it using the corresponding private key. raw bytes, then decrypting it using the corresponding private key.
* [Decryption] * [Decryption](http://godoc.org/github.com/btcsuite/btcd/btcec#example-package--DecryptMessage)
(http://godoc.org/github.com/btcsuite/btcd/btcec#example-package--DecryptMessage)
Demonstrates decrypting a message using a private key that is first parsed Demonstrates decrypting a message using a private key that is first parsed
from raw bytes. from raw bytes.

View file

@ -38,6 +38,7 @@ type KoblitzCurve struct {
*elliptic.CurveParams *elliptic.CurveParams
q *big.Int q *big.Int
H int // cofactor of the curve. H int // cofactor of the curve.
halfOrder *big.Int // half the order N
// byteSize is simply the bit size / 8 and is provided for convenience // byteSize is simply the bit size / 8 and is provided for convenience
// since it is calculated repeatedly. // since it is calculated repeatedly.
@ -747,10 +748,10 @@ func NAF(k []byte) ([]byte, []byte) {
} }
if carry { if carry {
retPos[0] = 1 retPos[0] = 1
}
return retPos, retNeg return retPos, retNeg
} }
return retPos[1:], retNeg[1:]
}
// ScalarMult returns k*(Bx, By) where k is a big endian integer. // ScalarMult returns k*(Bx, By) where k is a big endian integer.
// Part of the elliptic.Curve interface. // Part of the elliptic.Curve interface.
@ -912,9 +913,10 @@ func initS256() {
secp256k1.Gx = fromHex("79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798") secp256k1.Gx = fromHex("79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798")
secp256k1.Gy = fromHex("483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8") secp256k1.Gy = fromHex("483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8")
secp256k1.BitSize = 256 secp256k1.BitSize = 256
secp256k1.H = 1
secp256k1.q = new(big.Int).Div(new(big.Int).Add(secp256k1.P, secp256k1.q = new(big.Int).Div(new(big.Int).Add(secp256k1.P,
big.NewInt(1)), big.NewInt(4)) big.NewInt(1)), big.NewInt(4))
secp256k1.H = 1
secp256k1.halfOrder = new(big.Int).Rsh(secp256k1.N, 1)
// Provided for convenience since this gets computed repeatedly. // Provided for convenience since this gets computed repeatedly.
secp256k1.byteSize = secp256k1.BitSize / 8 secp256k1.byteSize = secp256k1.BitSize / 8

View file

@ -100,10 +100,6 @@ const (
// fieldPrimeWordOne is word one of the secp256k1 prime in the // fieldPrimeWordOne is word one of the secp256k1 prime in the
// internal field representation. It is used during negation. // internal field representation. It is used during negation.
fieldPrimeWordOne = 0x3ffffbf fieldPrimeWordOne = 0x3ffffbf
// primeLowBits is the lower 2*fieldBase bits of the secp256k1 prime in
// its standard normalized form. It is used during modular reduction.
primeLowBits = 0xffffefffffc2f
) )
// fieldVal implements optimized fixed-precision arithmetic over the // fieldVal implements optimized fixed-precision arithmetic over the
@ -250,39 +246,15 @@ func (f *fieldVal) SetHex(hexString string) *fieldVal {
// performs fast modular reduction over the secp256k1 prime by making use of the // performs fast modular reduction over the secp256k1 prime by making use of the
// special form of the prime. // special form of the prime.
func (f *fieldVal) Normalize() *fieldVal { func (f *fieldVal) Normalize() *fieldVal {
// The field representation leaves 6 bits of overflow in each // The field representation leaves 6 bits of overflow in each word so
// word so intermediate calculations can be performed without needing // intermediate calculations can be performed without needing to
// to propagate the carry to each higher word during the calculations. // propagate the carry to each higher word during the calculations. In
// In order to normalize, first we need to "compact" the full 256-bit // order to normalize, we need to "compact" the full 256-bit value to
// value to the right and treat the additional 64 leftmost bits as // the right while propagating any carries through to the high order
// the magnitude. // word.
m := f.n[0] //
t0 := m & fieldBaseMask // Since this field is doing arithmetic modulo the secp256k1 prime, we
m = (m >> fieldBase) + f.n[1] // also need to perform modular reduction over the prime.
t1 := m & fieldBaseMask
m = (m >> fieldBase) + f.n[2]
t2 := m & fieldBaseMask
m = (m >> fieldBase) + f.n[3]
t3 := m & fieldBaseMask
m = (m >> fieldBase) + f.n[4]
t4 := m & fieldBaseMask
m = (m >> fieldBase) + f.n[5]
t5 := m & fieldBaseMask
m = (m >> fieldBase) + f.n[6]
t6 := m & fieldBaseMask
m = (m >> fieldBase) + f.n[7]
t7 := m & fieldBaseMask
m = (m >> fieldBase) + f.n[8]
t8 := m & fieldBaseMask
m = (m >> fieldBase) + f.n[9]
t9 := m & fieldMSBMask
m = m >> fieldMSBBits
// At this point, if the magnitude is greater than 0, the overall value
// is greater than the max possible 256-bit value. In particular, it is
// "how many times larger" than the max value it is. Since this field
// is doing arithmetic modulo the secp256k1 prime, we need to perform
// modular reduction over the prime.
// //
// Per [HAC] section 14.3.4: Reduction method of moduli of special form, // Per [HAC] section 14.3.4: Reduction method of moduli of special form,
// when the modulus is of the special form m = b^t - c, highly efficient // when the modulus is of the special form m = b^t - c, highly efficient
@ -298,98 +270,87 @@ func (f *fieldVal) Normalize() *fieldVal {
// //
// The algorithm presented in the referenced section typically repeats // The algorithm presented in the referenced section typically repeats
// until the quotient is zero. However, due to our field representation // until the quotient is zero. However, due to our field representation
// we already know at least how many times we would need to repeat as // we already know to within one reduction how many times we would need
// it's the value currently in m. Thus we can simply multiply the // to repeat as it's the uppermost bits of the high order word. Thus we
// magnitude by the field representation of the prime and do a single // can simply multiply the magnitude by the field representation of the
// iteration. Notice that nothing will be changed when the magnitude is // prime and do a single iteration. After this step there might be an
// zero, so we could skip this in that case, however always running // additional carry to bit 256 (bit 22 of the high order word).
// regardless allows it to run in constant time. t9 := f.n[9]
r := t0 + m*977 m := t9 >> fieldMSBBits
t0 = r & fieldBaseMask t9 = t9 & fieldMSBMask
r = (r >> fieldBase) + t1 + m*64 t0 := f.n[0] + m*977
t1 = r & fieldBaseMask t1 := (t0 >> fieldBase) + f.n[1] + (m << 6)
r = (r >> fieldBase) + t2 t0 = t0 & fieldBaseMask
t2 = r & fieldBaseMask t2 := (t1 >> fieldBase) + f.n[2]
r = (r >> fieldBase) + t3 t1 = t1 & fieldBaseMask
t3 = r & fieldBaseMask t3 := (t2 >> fieldBase) + f.n[3]
r = (r >> fieldBase) + t4 t2 = t2 & fieldBaseMask
t4 = r & fieldBaseMask t4 := (t3 >> fieldBase) + f.n[4]
r = (r >> fieldBase) + t5 t3 = t3 & fieldBaseMask
t5 = r & fieldBaseMask t5 := (t4 >> fieldBase) + f.n[5]
r = (r >> fieldBase) + t6 t4 = t4 & fieldBaseMask
t6 = r & fieldBaseMask t6 := (t5 >> fieldBase) + f.n[6]
r = (r >> fieldBase) + t7 t5 = t5 & fieldBaseMask
t7 = r & fieldBaseMask t7 := (t6 >> fieldBase) + f.n[7]
r = (r >> fieldBase) + t8 t6 = t6 & fieldBaseMask
t8 = r & fieldBaseMask t8 := (t7 >> fieldBase) + f.n[8]
r = (r >> fieldBase) + t9 t7 = t7 & fieldBaseMask
t9 = r & fieldMSBMask t9 = (t8 >> fieldBase) + t9
t8 = t8 & fieldBaseMask
// At this point, the result will be in the range 0 <= result <= // At this point, the magnitude is guaranteed to be one, however, the
// prime + (2^64 - c). Therefore, one more subtraction of the prime // value could still be greater than the prime if there was either a
// might be needed if the current result is greater than or equal to the // carry through to bit 256 (bit 22 of the higher order word) or the
// prime. The following does the final reduction in constant time. // value is greater than or equal to the field characteristic. The
// Note that the if/else here intentionally does the bitwise OR with // following determines if either or these conditions are true and does
// zero even though it won't change the value to ensure constant time // the final reduction in constant time.
// between the branches. //
var mask int32 // Note that the if/else statements here intentionally do the bitwise
lowBits := uint64(t1)<<fieldBase | uint64(t0) // operators even when it won't change the value to ensure constant time
if lowBits < primeLowBits { // between the branches. Also note that 'm' will be zero when neither
mask |= -1 // of the aforementioned conditions are true and the value will not be
// changed when 'm' is zero.
m = 1
if t9 == fieldMSBMask {
m &= 1
} else { } else {
mask |= 0 m &= 0
} }
if t2 < fieldBaseMask { if t2&t3&t4&t5&t6&t7&t8 == fieldBaseMask {
mask |= -1 m &= 1
} else { } else {
mask |= 0 m &= 0
} }
if t3 < fieldBaseMask { if ((t0+977)>>fieldBase + t1 + 64) > fieldBaseMask {
mask |= -1 m &= 1
} else { } else {
mask |= 0 m &= 0
} }
if t4 < fieldBaseMask { if t9>>fieldMSBBits != 0 {
mask |= -1 m |= 1
} else { } else {
mask |= 0 m |= 0
} }
if t5 < fieldBaseMask { t0 = t0 + m*977
mask |= -1 t1 = (t0 >> fieldBase) + t1 + (m << 6)
} else { t0 = t0 & fieldBaseMask
mask |= 0 t2 = (t1 >> fieldBase) + t2
} t1 = t1 & fieldBaseMask
if t6 < fieldBaseMask { t3 = (t2 >> fieldBase) + t3
mask |= -1 t2 = t2 & fieldBaseMask
} else { t4 = (t3 >> fieldBase) + t4
mask |= 0 t3 = t3 & fieldBaseMask
} t5 = (t4 >> fieldBase) + t5
if t7 < fieldBaseMask { t4 = t4 & fieldBaseMask
mask |= -1 t6 = (t5 >> fieldBase) + t6
} else { t5 = t5 & fieldBaseMask
mask |= 0 t7 = (t6 >> fieldBase) + t7
} t6 = t6 & fieldBaseMask
if t8 < fieldBaseMask { t8 = (t7 >> fieldBase) + t8
mask |= -1 t7 = t7 & fieldBaseMask
} else { t9 = (t8 >> fieldBase) + t9
mask |= 0 t8 = t8 & fieldBaseMask
} t9 = t9 & fieldMSBMask // Remove potential multiple of 2^256.
if t9 < fieldMSBMask {
mask |= -1
} else {
mask |= 0
}
lowBits -= ^uint64(mask) & primeLowBits
t0 = uint32(lowBits & fieldBaseMask)
t1 = uint32((lowBits >> fieldBase) & fieldBaseMask)
t2 = t2 & uint32(mask)
t3 = t3 & uint32(mask)
t4 = t4 & uint32(mask)
t5 = t5 & uint32(mask)
t6 = t6 & uint32(mask)
t7 = t7 & uint32(mask)
t8 = t8 & uint32(mask)
t9 = t9 & uint32(mask)
// Finally, set the normalized and reduced words. // Finally, set the normalized and reduced words.
f.n[0] = t0 f.n[0] = t0

View file

@ -1,63 +0,0 @@
// Copyright 2015 The btcsuite developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
// This file is ignored during the regular build due to the following build tag.
// It is called by go generate and used to automatically generate pre-computed
// tables used to accelerate operations.
// +build ignore
package main
import (
"bytes"
"compress/zlib"
"encoding/base64"
"fmt"
"log"
"os"
"github.com/btcsuite/btcd/btcec"
)
func main() {
fi, err := os.Create("secp256k1.go")
if err != nil {
log.Fatal(err)
}
defer fi.Close()
// Compress the serialized byte points.
serialized := btcec.S256().SerializedBytePoints()
var compressed bytes.Buffer
w := zlib.NewWriter(&compressed)
if _, err := w.Write(serialized); err != nil {
fmt.Println(err)
os.Exit(1)
}
w.Close()
// Encode the compressed byte points with base64.
encoded := make([]byte, base64.StdEncoding.EncodedLen(compressed.Len()))
base64.StdEncoding.Encode(encoded, compressed.Bytes())
fmt.Fprintln(fi, "// Copyright (c) 2015 The btcsuite developers")
fmt.Fprintln(fi, "// Use of this source code is governed by an ISC")
fmt.Fprintln(fi, "// license that can be found in the LICENSE file.")
fmt.Fprintln(fi)
fmt.Fprintln(fi, "package btcec")
fmt.Fprintln(fi)
fmt.Fprintln(fi, "// Auto-generated file (see genprecomps.go)")
fmt.Fprintln(fi, "// DO NOT EDIT")
fmt.Fprintln(fi)
fmt.Fprintf(fi, "var secp256k1BytePoints = %q\n", string(encoded))
a1, b1, a2, b2 := btcec.S256().EndomorphismVectors()
fmt.Println("The following values are the computed linearly " +
"independent vectors needed to make use of the secp256k1 " +
"endomorphism:")
fmt.Printf("a1: %x\n", a1)
fmt.Printf("b1: %x\n", b1)
fmt.Printf("a2: %x\n", a2)
fmt.Printf("b2: %x\n", b2)
}

View file

@ -54,6 +54,15 @@ const (
pubkeyHybrid byte = 0x6 // y_bit + x coord + y coord pubkeyHybrid byte = 0x6 // y_bit + x coord + y coord
) )
// IsCompressedPubKey returns true the the passed serialized public key has
// been encoded in compressed format, and false otherwise.
func IsCompressedPubKey(pubKey []byte) bool {
// The public key is only compressed if it is the correct length and
// the format (first byte) is one of the compressed pubkey values.
return len(pubKey) == PubKeyBytesLenCompressed &&
(pubKey[0]&^byte(0x1) == pubkeyCompressed)
}
// ParsePubKey parses a public key for a koblitz curve from a bytestring into a // ParsePubKey parses a public key for a koblitz curve from a bytestring into a
// ecdsa.Publickey, verifying that it is valid. It supports compressed, // ecdsa.Publickey, verifying that it is valid. It supports compressed,
// uncompressed and hybrid signature formats. // uncompressed and hybrid signature formats.

File diff suppressed because one or more lines are too long

View file

@ -29,10 +29,6 @@ type Signature struct {
} }
var ( var (
// Curve order and halforder, used to tame ECDSA malleability (see BIP-0062)
order = new(big.Int).Set(S256().N)
halforder = new(big.Int).Rsh(order, 1)
// Used in RFC6979 implementation when testing the nonce for correctness // Used in RFC6979 implementation when testing the nonce for correctness
one = big.NewInt(1) one = big.NewInt(1)
@ -51,8 +47,8 @@ var (
func (sig *Signature) Serialize() []byte { func (sig *Signature) Serialize() []byte {
// low 'S' malleability breaker // low 'S' malleability breaker
sigS := sig.S sigS := sig.S
if sigS.Cmp(halforder) == 1 { if sigS.Cmp(S256().halfOrder) == 1 {
sigS = new(big.Int).Sub(order, sigS) sigS = new(big.Int).Sub(S256().N, sigS)
} }
// Ensure the encoded bytes for the r and s values are canonical and // Ensure the encoded bytes for the r and s values are canonical and
// thus suitable for DER encoding. // thus suitable for DER encoding.
@ -62,7 +58,7 @@ func (sig *Signature) Serialize() []byte {
// total length of returned signature is 1 byte for each magic and // total length of returned signature is 1 byte for each magic and
// length (6 total), plus lengths of r and s // length (6 total), plus lengths of r and s
length := 6 + len(rb) + len(sb) length := 6 + len(rb) + len(sb)
b := make([]byte, length, length) b := make([]byte, length)
b[0] = 0x30 b[0] = 0x30
b[1] = byte(length - 2) b[1] = byte(length - 2)
@ -420,7 +416,8 @@ func RecoverCompact(curve *KoblitzCurve, signature,
func signRFC6979(privateKey *PrivateKey, hash []byte) (*Signature, error) { func signRFC6979(privateKey *PrivateKey, hash []byte) (*Signature, error) {
privkey := privateKey.ToECDSA() privkey := privateKey.ToECDSA()
N := order N := S256().N
halfOrder := S256().halfOrder
k := nonceRFC6979(privkey.D, hash) k := nonceRFC6979(privkey.D, hash)
inv := new(big.Int).ModInverse(k, N) inv := new(big.Int).ModInverse(k, N)
r, _ := privkey.Curve.ScalarBaseMult(k.Bytes()) r, _ := privkey.Curve.ScalarBaseMult(k.Bytes())
@ -438,7 +435,7 @@ func signRFC6979(privateKey *PrivateKey, hash []byte) (*Signature, error) {
s.Mul(s, inv) s.Mul(s, inv)
s.Mod(s, N) s.Mod(s, N)
if s.Cmp(halforder) == 1 { if s.Cmp(halfOrder) == 1 {
s.Sub(N, s) s.Sub(N, s)
} }
if s.Sign() == 0 { if s.Sign() == 0 {

6
vendor/vendor.json vendored
View file

@ -51,10 +51,10 @@
"revisionTime": "2017-02-10T01:56:32Z" "revisionTime": "2017-02-10T01:56:32Z"
}, },
{ {
"checksumSHA1": "fIpm6Vr5a8kgr22gWkQx7vKUTyU=", "checksumSHA1": "gZQ6HheWahvZzIc3phBnOwoWHjE=",
"path": "github.com/btcsuite/btcd/btcec", "path": "github.com/btcsuite/btcd/btcec",
"revision": "d06c0bb181529331be8f8d9350288c420d9e60e4", "revision": "2e60448ffcc6bf78332d1fe590260095f554dd78",
"revisionTime": "2017-02-01T21:25:25Z" "revisionTime": "2017-11-28T15:02:46Z"
}, },
{ {
"checksumSHA1": "cDMtzKmdTx4CcIpP4broa+16X9g=", "checksumSHA1": "cDMtzKmdTx4CcIpP4broa+16X9g=",

View file

@ -17,14 +17,16 @@
package whisperv6 package whisperv6
import ( import (
"crypto/sha256"
"testing" "testing"
"github.com/EthereumCommonwealth/go-callisto/crypto" "github.com/EthereumCommonwealth/go-callisto/crypto"
"golang.org/x/crypto/pbkdf2"
) )
func BenchmarkDeriveKeyMaterial(b *testing.B) { func BenchmarkDeriveKeyMaterial(b *testing.B) {
for i := 0; i < b.N; i++ { for i := 0; i < b.N; i++ {
deriveKeyMaterial([]byte("test"), 0) pbkdf2.Key([]byte("test"), nil, 65356, aesKeyLength, sha256.New)
} }
} }

View file

@ -36,15 +36,15 @@ import (
const ( const (
EnvelopeVersion = uint64(0) EnvelopeVersion = uint64(0)
ProtocolVersion = uint64(5) ProtocolVersion = uint64(6)
ProtocolVersionStr = "5.0" ProtocolVersionStr = "6.0"
ProtocolName = "shh" ProtocolName = "shh"
statusCode = 0 // used by whisper protocol statusCode = 0 // used by whisper protocol
messagesCode = 1 // normal whisper message messagesCode = 1 // normal whisper message
p2pCode = 2 // peer-to-peer message (to be consumed by the peer, but not forwarded any further) p2pCode = 2 // peer-to-peer message (to be consumed by the peer, but not forwarded any further)
p2pRequestCode = 3 // peer-to-peer message, used by Dapp protocol p2pRequestCode = 3 // peer-to-peer message, used by Dapp protocol
NumberOfMessageCodes = 64 NumberOfMessageCodes = 128
paddingMask = byte(3) paddingMask = byte(3)
signatureFlag = byte(4) signatureFlag = byte(4)
@ -67,6 +67,8 @@ const (
DefaultTTL = 50 // seconds DefaultTTL = 50 // seconds
SynchAllowance = 10 // seconds SynchAllowance = 10 // seconds
EnvelopeHeaderLength = 20
) )
type unknownVersionError uint64 type unknownVersionError uint64

View file

@ -36,11 +36,9 @@ import (
// Envelope represents a clear-text data packet to transmit through the Whisper // Envelope represents a clear-text data packet to transmit through the Whisper
// network. Its contents may or may not be encrypted and signed. // network. Its contents may or may not be encrypted and signed.
type Envelope struct { type Envelope struct {
Version []byte
Expiry uint32 Expiry uint32
TTL uint32 TTL uint32
Topic TopicType Topic TopicType
AESNonce []byte
Data []byte Data []byte
Nonce uint64 Nonce uint64
@ -51,49 +49,29 @@ type Envelope struct {
// size returns the size of envelope as it is sent (i.e. public fields only) // size returns the size of envelope as it is sent (i.e. public fields only)
func (e *Envelope) size() int { func (e *Envelope) size() int {
return 20 + len(e.Version) + len(e.AESNonce) + len(e.Data) return EnvelopeHeaderLength + len(e.Data)
} }
// rlpWithoutNonce returns the RLP encoded envelope contents, except the nonce. // rlpWithoutNonce returns the RLP encoded envelope contents, except the nonce.
func (e *Envelope) rlpWithoutNonce() []byte { func (e *Envelope) rlpWithoutNonce() []byte {
res, _ := rlp.EncodeToBytes([]interface{}{e.Version, e.Expiry, e.TTL, e.Topic, e.AESNonce, e.Data}) res, _ := rlp.EncodeToBytes([]interface{}{e.Expiry, e.TTL, e.Topic, e.Data})
return res return res
} }
// NewEnvelope wraps a Whisper message with expiration and destination data // NewEnvelope wraps a Whisper message with expiration and destination data
// included into an envelope for network forwarding. // included into an envelope for network forwarding.
func NewEnvelope(ttl uint32, topic TopicType, aesNonce []byte, msg *sentMessage) *Envelope { func NewEnvelope(ttl uint32, topic TopicType, msg *sentMessage) *Envelope {
env := Envelope{ env := Envelope{
Version: make([]byte, 1),
Expiry: uint32(time.Now().Add(time.Second * time.Duration(ttl)).Unix()), Expiry: uint32(time.Now().Add(time.Second * time.Duration(ttl)).Unix()),
TTL: ttl, TTL: ttl,
Topic: topic, Topic: topic,
AESNonce: aesNonce,
Data: msg.Raw, Data: msg.Raw,
Nonce: 0, Nonce: 0,
} }
if EnvelopeVersion < 256 {
env.Version[0] = byte(EnvelopeVersion)
} else {
panic("please increase the size of Envelope.Version before releasing this version")
}
return &env return &env
} }
func (e *Envelope) IsSymmetric() bool {
return len(e.AESNonce) > 0
}
func (e *Envelope) isAsymmetric() bool {
return !e.IsSymmetric()
}
func (e *Envelope) Ver() uint64 {
return bytesToUintLittleEndian(e.Version)
}
// Seal closes the envelope by spending the requested amount of time as a proof // Seal closes the envelope by spending the requested amount of time as a proof
// of work on hashing the data. // of work on hashing the data.
func (e *Envelope) Seal(options *MessageParams) error { func (e *Envelope) Seal(options *MessageParams) error {
@ -209,7 +187,7 @@ func (e *Envelope) OpenAsymmetric(key *ecdsa.PrivateKey) (*ReceivedMessage, erro
// OpenSymmetric tries to decrypt an envelope, potentially encrypted with a particular key. // OpenSymmetric tries to decrypt an envelope, potentially encrypted with a particular key.
func (e *Envelope) OpenSymmetric(key []byte) (msg *ReceivedMessage, err error) { func (e *Envelope) OpenSymmetric(key []byte) (msg *ReceivedMessage, err error) {
msg = &ReceivedMessage{Raw: e.Data} msg = &ReceivedMessage{Raw: e.Data}
err = msg.decryptSymmetric(key, e.AESNonce) err = msg.decryptSymmetric(key)
if err != nil { if err != nil {
msg = nil msg = nil
} }
@ -218,12 +196,18 @@ func (e *Envelope) OpenSymmetric(key []byte) (msg *ReceivedMessage, err error) {
// Open tries to decrypt an envelope, and populates the message fields in case of success. // Open tries to decrypt an envelope, and populates the message fields in case of success.
func (e *Envelope) Open(watcher *Filter) (msg *ReceivedMessage) { func (e *Envelope) Open(watcher *Filter) (msg *ReceivedMessage) {
if e.isAsymmetric() { // The API interface forbids filters doing both symmetric and
// asymmetric encryption.
if watcher.expectsAsymmetricEncryption() && watcher.expectsSymmetricEncryption() {
return nil
}
if watcher.expectsAsymmetricEncryption() {
msg, _ = e.OpenAsymmetric(watcher.KeyAsym) msg, _ = e.OpenAsymmetric(watcher.KeyAsym)
if msg != nil { if msg != nil {
msg.Dst = &watcher.KeyAsym.PublicKey msg.Dst = &watcher.KeyAsym.PublicKey
} }
} else if e.IsSymmetric() { } else if watcher.expectsSymmetricEncryption() {
msg, _ = e.OpenSymmetric(watcher.KeySym) msg, _ = e.OpenSymmetric(watcher.KeySym)
if msg != nil { if msg != nil {
msg.SymKeyHash = crypto.Keccak256Hash(watcher.KeySym) msg.SymKeyHash = crypto.Keccak256Hash(watcher.KeySym)
@ -240,7 +224,6 @@ func (e *Envelope) Open(watcher *Filter) (msg *ReceivedMessage) {
msg.TTL = e.TTL msg.TTL = e.TTL
msg.Sent = e.Expiry - e.TTL msg.Sent = e.Expiry - e.TTL
msg.EnvelopeHash = e.Hash() msg.EnvelopeHash = e.Hash()
msg.EnvelopeVersion = e.Ver()
} }
return msg return msg
} }

View file

@ -0,0 +1,64 @@
// Copyright 2017 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/>.
// Contains the tests associated with the Whisper protocol Envelope object.
package whisperv6
import (
mrand "math/rand"
"testing"
"github.com/EthereumCommonwealth/go-callisto/crypto"
)
func TestEnvelopeOpenAcceptsOnlyOneKeyTypeInFilter(t *testing.T) {
symKey := make([]byte, aesKeyLength)
mrand.Read(symKey)
asymKey, err := crypto.GenerateKey()
if err != nil {
t.Fatalf("failed GenerateKey with seed %d: %s.", seed, err)
}
params := MessageParams{
PoW: 0.01,
WorkTime: 1,
TTL: uint32(mrand.Intn(1024)),
Payload: make([]byte, 50),
KeySym: symKey,
Dst: nil,
}
mrand.Read(params.Payload)
msg, err := NewSentMessage(&params)
if err != nil {
t.Fatalf("failed to create new message with seed %d: %s.", seed, err)
}
e, err := msg.Wrap(&params)
if err != nil {
t.Fatalf("Failed to Wrap the message in an envelope with seed %d: %s", seed, err)
}
f := Filter{KeySym: symKey, KeyAsym: asymKey}
decrypted := e.Open(&f)
if decrypted != nil {
t.Fatalf("Managed to decrypt a message with an invalid filter, seed %d", seed)
}
}

View file

@ -53,6 +53,10 @@ func NewFilters(w *Whisper) *Filters {
} }
func (fs *Filters) Install(watcher *Filter) (string, error) { func (fs *Filters) Install(watcher *Filter) (string, error) {
if watcher.KeySym != nil && watcher.KeyAsym != nil {
return "", fmt.Errorf("filters must choose between symmetric and asymmetric keys")
}
if watcher.Messages == nil { if watcher.Messages == nil {
watcher.Messages = make(map[common.Hash]*ReceivedMessage) watcher.Messages = make(map[common.Hash]*ReceivedMessage)
} }
@ -175,6 +179,9 @@ func (f *Filter) Retrieve() (all []*ReceivedMessage) {
return all return all
} }
// MatchMessage checks if the filter matches an already decrypted
// message (i.e. a Message that has already been handled by
// MatchEnvelope when checked by a previous filter)
func (f *Filter) MatchMessage(msg *ReceivedMessage) bool { func (f *Filter) MatchMessage(msg *ReceivedMessage) bool {
if f.PoW > 0 && msg.PoW < f.PoW { if f.PoW > 0 && msg.PoW < f.PoW {
return false return false
@ -188,17 +195,15 @@ func (f *Filter) MatchMessage(msg *ReceivedMessage) bool {
return false return false
} }
// MatchEvelope checks if it's worth decrypting the message. If
// it returns `true`, client code is expected to attempt decrypting
// the message and subsequently call MatchMessage.
func (f *Filter) MatchEnvelope(envelope *Envelope) bool { func (f *Filter) MatchEnvelope(envelope *Envelope) bool {
if f.PoW > 0 && envelope.pow < f.PoW { if f.PoW > 0 && envelope.pow < f.PoW {
return false return false
} }
if f.expectsAsymmetricEncryption() && envelope.isAsymmetric() {
return f.MatchTopic(envelope.Topic) return f.MatchTopic(envelope.Topic)
} else if f.expectsSymmetricEncryption() && envelope.IsSymmetric() {
return f.MatchTopic(envelope.Topic)
}
return false
} }
func (f *Filter) MatchTopic(topic TopicType) bool { func (f *Filter) MatchTopic(topic TopicType) bool {

View file

@ -229,6 +229,36 @@ func TestInstallIdenticalFilters(t *testing.T) {
} }
} }
func TestInstallFilterWithSymAndAsymKeys(t *testing.T) {
InitSingleTest()
w := New(&Config{})
filters := NewFilters(w)
filter1, _ := generateFilter(t, true)
asymKey, err := crypto.GenerateKey()
if err != nil {
t.Fatalf("Unable to create asymetric keys: %v", err)
}
// Copy the first filter since some of its fields
// are randomly gnerated.
filter := &Filter{
KeySym: filter1.KeySym,
KeyAsym: asymKey,
Topics: filter1.Topics,
PoW: filter1.PoW,
AllowP2P: filter1.AllowP2P,
Messages: make(map[common.Hash]*ReceivedMessage),
}
_, err = filters.Install(filter)
if err == nil {
t.Fatalf("Error detecting that a filter had both an asymmetric and symmetric key, with seed %d", seed)
}
}
func TestComparePubKey(t *testing.T) { func TestComparePubKey(t *testing.T) {
InitSingleTest() InitSingleTest()
@ -312,12 +342,6 @@ func TestMatchEnvelope(t *testing.T) {
t.Fatalf("failed MatchEnvelope() symmetric with seed %d.", seed) t.Fatalf("failed MatchEnvelope() symmetric with seed %d.", seed)
} }
// asymmetric + matching topic: mismatch
match = fasym.MatchEnvelope(env)
if match {
t.Fatalf("failed MatchEnvelope() asymmetric with seed %d.", seed)
}
// symmetric + matching topic + insufficient PoW: mismatch // symmetric + matching topic + insufficient PoW: mismatch
fsym.PoW = env.PoW() + 1.0 fsym.PoW = env.PoW() + 1.0
match = fsym.MatchEnvelope(env) match = fsym.MatchEnvelope(env)

View file

@ -61,6 +61,7 @@ type ReceivedMessage struct {
Payload []byte Payload []byte
Padding []byte Padding []byte
Signature []byte Signature []byte
Salt []byte
PoW float64 // Proof of work as described in the Whisper spec PoW float64 // Proof of work as described in the Whisper spec
Sent uint32 // Time when the message was posted into the network Sent uint32 // Time when the message was posted into the network
@ -71,7 +72,6 @@ type ReceivedMessage struct {
SymKeyHash common.Hash // The Keccak256Hash of the key, associated with the Topic SymKeyHash common.Hash // The Keccak256Hash of the key, associated with the Topic
EnvelopeHash common.Hash // Message envelope hash to act as a unique id EnvelopeHash common.Hash // Message envelope hash to act as a unique id
EnvelopeVersion uint64
} }
func isMessageSigned(flags byte) bool { func isMessageSigned(flags byte) bool {
@ -196,31 +196,31 @@ func (msg *sentMessage) encryptAsymmetric(key *ecdsa.PublicKey) error {
// encryptSymmetric encrypts a message with a topic key, using AES-GCM-256. // encryptSymmetric encrypts a message with a topic key, using AES-GCM-256.
// nonce size should be 12 bytes (see cipher.gcmStandardNonceSize). // nonce size should be 12 bytes (see cipher.gcmStandardNonceSize).
func (msg *sentMessage) encryptSymmetric(key []byte) (nonce []byte, err error) { func (msg *sentMessage) encryptSymmetric(key []byte) (err error) {
if !validateSymmetricKey(key) { if !validateSymmetricKey(key) {
return nil, errors.New("invalid key provided for symmetric encryption") return errors.New("invalid key provided for symmetric encryption")
} }
block, err := aes.NewCipher(key) block, err := aes.NewCipher(key)
if err != nil { if err != nil {
return nil, err return err
} }
aesgcm, err := cipher.NewGCM(block) aesgcm, err := cipher.NewGCM(block)
if err != nil { if err != nil {
return nil, err return err
} }
// never use more than 2^32 random nonces with a given key // never use more than 2^32 random nonces with a given key
nonce = make([]byte, aesgcm.NonceSize()) salt := make([]byte, aesgcm.NonceSize())
_, err = crand.Read(nonce) _, err = crand.Read(salt)
if err != nil { if err != nil {
return nil, err return err
} else if !validateSymmetricKey(nonce) { } else if !validateSymmetricKey(salt) {
return nil, errors.New("crypto/rand failed to generate nonce") return errors.New("crypto/rand failed to generate salt")
} }
msg.Raw = aesgcm.Seal(nil, nonce, msg.Raw, nil) msg.Raw = append(aesgcm.Seal(nil, salt, msg.Raw, nil), salt...)
return nonce, nil return nil
} }
// Wrap bundles the message into an Envelope to transmit over the network. // Wrap bundles the message into an Envelope to transmit over the network.
@ -233,11 +233,10 @@ func (msg *sentMessage) Wrap(options *MessageParams) (envelope *Envelope, err er
return nil, err return nil, err
} }
} }
var nonce []byte
if options.Dst != nil { if options.Dst != nil {
err = msg.encryptAsymmetric(options.Dst) err = msg.encryptAsymmetric(options.Dst)
} else if options.KeySym != nil { } else if options.KeySym != nil {
nonce, err = msg.encryptSymmetric(options.KeySym) err = msg.encryptSymmetric(options.KeySym)
} else { } else {
err = errors.New("unable to encrypt the message: neither symmetric nor assymmetric key provided") err = errors.New("unable to encrypt the message: neither symmetric nor assymmetric key provided")
} }
@ -245,7 +244,7 @@ func (msg *sentMessage) Wrap(options *MessageParams) (envelope *Envelope, err er
return nil, err return nil, err
} }
envelope = NewEnvelope(options.TTL, options.Topic, nonce, msg) envelope = NewEnvelope(options.TTL, options.Topic, msg)
if err = envelope.Seal(options); err != nil { if err = envelope.Seal(options); err != nil {
return nil, err return nil, err
} }
@ -254,7 +253,14 @@ func (msg *sentMessage) Wrap(options *MessageParams) (envelope *Envelope, err er
// decryptSymmetric decrypts a message with a topic key, using AES-GCM-256. // decryptSymmetric decrypts a message with a topic key, using AES-GCM-256.
// nonce size should be 12 bytes (see cipher.gcmStandardNonceSize). // nonce size should be 12 bytes (see cipher.gcmStandardNonceSize).
func (msg *ReceivedMessage) decryptSymmetric(key []byte, nonce []byte) error { func (msg *ReceivedMessage) decryptSymmetric(key []byte) error {
// In v6, symmetric messages are expected to contain the 12-byte
// "salt" at the end of the payload.
if len(msg.Raw) < AESNonceLength {
return errors.New("missing salt or invalid payload in symmetric message")
}
salt := msg.Raw[len(msg.Raw)-AESNonceLength:]
block, err := aes.NewCipher(key) block, err := aes.NewCipher(key)
if err != nil { if err != nil {
return err return err
@ -263,15 +269,16 @@ func (msg *ReceivedMessage) decryptSymmetric(key []byte, nonce []byte) error {
if err != nil { if err != nil {
return err return err
} }
if len(nonce) != aesgcm.NonceSize() { if len(salt) != aesgcm.NonceSize() {
log.Error("decrypting the message", "AES nonce size", len(nonce)) log.Error("decrypting the message", "AES salt size", len(salt))
return errors.New("wrong AES nonce size") return errors.New("wrong AES salt size")
} }
decrypted, err := aesgcm.Open(nil, nonce, msg.Raw, nil) decrypted, err := aesgcm.Open(nil, salt, msg.Raw[:len(msg.Raw)-AESNonceLength], nil)
if err != nil { if err != nil {
return err return err
} }
msg.Raw = decrypted msg.Raw = decrypted
msg.Salt = salt
return nil return nil
} }

View file

@ -174,10 +174,8 @@ func TestMessageSeal(t *testing.T) {
t.Fatalf("failed to create new message with seed %d: %s.", seed, err) t.Fatalf("failed to create new message with seed %d: %s.", seed, err)
} }
params.TTL = 1 params.TTL = 1
aesnonce := make([]byte, 12)
mrand.Read(aesnonce)
env := NewEnvelope(params.TTL, params.Topic, aesnonce, msg) env := NewEnvelope(params.TTL, params.Topic, msg)
if err != nil { if err != nil {
t.Fatalf("failed Wrap with seed %d: %s.", seed, err) t.Fatalf("failed Wrap with seed %d: %s.", seed, err)
} }
@ -242,7 +240,12 @@ func singleEnvelopeOpenTest(t *testing.T, symmetric bool) {
t.Fatalf("failed Wrap with seed %d: %s.", seed, err) t.Fatalf("failed Wrap with seed %d: %s.", seed, err)
} }
f := Filter{KeyAsym: key, KeySym: params.KeySym} var f Filter
if symmetric {
f = Filter{KeySym: params.KeySym}
} else {
f = Filter{KeyAsym: key}
}
decrypted := env.Open(&f) decrypted := env.Open(&f)
if decrypted == nil { if decrypted == nil {
t.Fatalf("failed to open with seed %d.", seed) t.Fatalf("failed to open with seed %d.", seed)

View file

@ -367,7 +367,9 @@ func (w *Whisper) AddSymKeyFromPassword(password string) (string, error) {
return "", fmt.Errorf("failed to generate unique ID") return "", fmt.Errorf("failed to generate unique ID")
} }
derived, err := deriveKeyMaterial([]byte(password), EnvelopeVersion) // kdf should run no less than 0.1 seconds on an average computer,
// because it's an once in a session experience
derived := pbkdf2.Key([]byte(password), nil, 65356, aesKeyLength, sha256.New)
if err != nil { if err != nil {
return "", err return "", err
} }
@ -587,17 +589,6 @@ func (wh *Whisper) add(envelope *Envelope) (bool, error) {
return false, fmt.Errorf("huge messages are not allowed [%x]", envelope.Hash()) return false, fmt.Errorf("huge messages are not allowed [%x]", envelope.Hash())
} }
if len(envelope.Version) > 4 {
return false, fmt.Errorf("oversized version [%x]", envelope.Hash())
}
aesNonceSize := len(envelope.AESNonce)
if aesNonceSize != 0 && aesNonceSize != AESNonceLength {
// the standard AES GCM nonce size is 12 bytes,
// but constant gcmStandardNonceSize cannot be accessed (not exported)
return false, fmt.Errorf("wrong size of AESNonce: %d bytes [env: %x]", aesNonceSize, envelope.Hash())
}
if envelope.PoW() < wh.MinPow() { if envelope.PoW() < wh.MinPow() {
log.Debug("envelope with low PoW dropped", "PoW", envelope.PoW(), "hash", envelope.Hash().Hex()) log.Debug("envelope with low PoW dropped", "PoW", envelope.PoW(), "hash", envelope.Hash().Hex())
return false, nil // drop envelope without error return false, nil // drop envelope without error
@ -635,10 +626,6 @@ func (wh *Whisper) add(envelope *Envelope) (bool, error) {
// postEvent queues the message for further processing. // postEvent queues the message for further processing.
func (w *Whisper) postEvent(envelope *Envelope, isP2P bool) { func (w *Whisper) postEvent(envelope *Envelope, isP2P bool) {
// if the version of incoming message is higher than
// currently supported version, we can not decrypt it,
// and therefore just ignore this message
if envelope.Ver() <= EnvelopeVersion {
if isP2P { if isP2P {
w.p2pMsgQueue <- envelope w.p2pMsgQueue <- envelope
} else { } else {
@ -646,7 +633,6 @@ func (w *Whisper) postEvent(envelope *Envelope, isP2P bool) {
w.messageQueue <- envelope w.messageQueue <- envelope
} }
} }
}
// checkOverflow checks if message queue overflow occurs and reports it if necessary. // checkOverflow checks if message queue overflow occurs and reports it if necessary.
func (w *Whisper) checkOverflow() { func (w *Whisper) checkOverflow() {
@ -830,19 +816,6 @@ func BytesToUintBigEndian(b []byte) (res uint64) {
return res return res
} }
// deriveKeyMaterial derives symmetric key material from the key or password.
// pbkdf2 is used for security, in case people use password instead of randomly generated keys.
func deriveKeyMaterial(key []byte, version uint64) (derivedKey []byte, err error) {
if version == 0 {
// kdf should run no less than 0.1 seconds on average compute,
// because it's a once in a session experience
derivedKey := pbkdf2.Key(key, nil, 65356, aesKeyLength, sha256.New)
return derivedKey, nil
} else {
return nil, unknownVersionError(version)
}
}
// GenerateRandomID generates a random string, which is then returned to be used as a key id // GenerateRandomID generates a random string, which is then returned to be used as a key id
func GenerateRandomID() (id string, err error) { func GenerateRandomID() (id string, err error) {
buf := make([]byte, keyIdSize) buf := make([]byte, keyIdSize)

View file

@ -19,11 +19,13 @@ package whisperv6
import ( import (
"bytes" "bytes"
"crypto/ecdsa" "crypto/ecdsa"
"crypto/sha256"
mrand "math/rand" mrand "math/rand"
"testing" "testing"
"time" "time"
"github.com/EthereumCommonwealth/go-callisto/common" "github.com/EthereumCommonwealth/go-callisto/common"
"golang.org/x/crypto/pbkdf2"
) )
func TestWhisperBasic(t *testing.T) { func TestWhisperBasic(t *testing.T) {
@ -79,14 +81,7 @@ func TestWhisperBasic(t *testing.T) {
} }
var derived []byte var derived []byte
ver := uint64(0xDEADBEEF) derived = pbkdf2.Key([]byte(peerID), nil, 65356, aesKeyLength, sha256.New)
if _, err := deriveKeyMaterial(peerID, ver); err != unknownVersionError(ver) {
t.Fatalf("failed deriveKeyMaterial with param = %v: %s.", peerID, err)
}
derived, err = deriveKeyMaterial(peerID, 0)
if err != nil {
t.Fatalf("failed second deriveKeyMaterial with param = %v: %s.", peerID, err)
}
if !validateSymmetricKey(derived) { if !validateSymmetricKey(derived) {
t.Fatalf("failed validateSymmetricKey with param = %v.", derived) t.Fatalf("failed validateSymmetricKey with param = %v.", derived)
} }