mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-08-17 01:13:45 +00:00
Update from go-ethereum.
This commit is contained in:
commit
79348902e5
38 changed files with 630 additions and 407 deletions
28
.travis.yml
28
.travis.yml
|
|
@ -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:
|
||||||
|
|
|
||||||
|
|
@ -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 {
|
||||||
|
|
|
||||||
|
|
@ -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 {
|
||||||
|
|
|
||||||
|
|
@ -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}
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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 {
|
||||||
|
|
|
||||||
|
|
@ -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.
|
||||||
|
|
|
||||||
|
|
@ -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) {
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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) {
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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()
|
||||||
|
|
|
||||||
|
|
@ -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()
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
12
eth/api.go
12
eth/api.go
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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))
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
20
vendor/github.com/btcsuite/btcd/btcec/README.md
generated
vendored
20
vendor/github.com/btcsuite/btcd/btcec/README.md
generated
vendored
|
|
@ -1,11 +1,9 @@
|
||||||
btcec
|
btcec
|
||||||
=====
|
=====
|
||||||
|
|
||||||
[]
|
[](https://travis-ci.org/btcsuite/btcec)
|
||||||
(https://travis-ci.org/btcsuite/btcec) [![ISC License]
|
[](http://copyfree.org)
|
||||||
(http://img.shields.io/badge/license-ISC-blue.svg)](http://copyfree.org)
|
[](http://godoc.org/github.com/btcsuite/btcd/btcec)
|
||||||
[]
|
|
||||||
(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.
|
||||||
|
|
||||||
|
|
|
||||||
12
vendor/github.com/btcsuite/btcd/btcec/btcec.go
generated
vendored
12
vendor/github.com/btcsuite/btcd/btcec/btcec.go
generated
vendored
|
|
@ -36,8 +36,9 @@ var (
|
||||||
// interface from crypto/elliptic.
|
// interface from crypto/elliptic.
|
||||||
type KoblitzCurve struct {
|
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,9 +748,9 @@ func NAF(k []byte) ([]byte, []byte) {
|
||||||
}
|
}
|
||||||
if carry {
|
if carry {
|
||||||
retPos[0] = 1
|
retPos[0] = 1
|
||||||
|
return retPos, retNeg
|
||||||
}
|
}
|
||||||
|
return retPos[1:], retNeg[1:]
|
||||||
return retPos, retNeg
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ScalarMult returns k*(Bx, By) where k is a big endian integer.
|
// ScalarMult returns k*(Bx, By) where k is a big endian integer.
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
201
vendor/github.com/btcsuite/btcd/btcec/field.go
generated
vendored
201
vendor/github.com/btcsuite/btcd/btcec/field.go
generated
vendored
|
|
@ -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
|
||||||
|
|
|
||||||
63
vendor/github.com/btcsuite/btcd/btcec/genprecomps.go
generated
vendored
63
vendor/github.com/btcsuite/btcd/btcec/genprecomps.go
generated
vendored
|
|
@ -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)
|
|
||||||
}
|
|
||||||
9
vendor/github.com/btcsuite/btcd/btcec/pubkey.go
generated
vendored
9
vendor/github.com/btcsuite/btcd/btcec/pubkey.go
generated
vendored
|
|
@ -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.
|
||||||
|
|
|
||||||
2
vendor/github.com/btcsuite/btcd/btcec/secp256k1.go
generated
vendored
2
vendor/github.com/btcsuite/btcd/btcec/secp256k1.go
generated
vendored
File diff suppressed because one or more lines are too long
15
vendor/github.com/btcsuite/btcd/btcec/signature.go
generated
vendored
15
vendor/github.com/btcsuite/btcd/btcec/signature.go
generated
vendored
|
|
@ -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
6
vendor/vendor.json
vendored
|
|
@ -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=",
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -36,13 +36,11 @@ 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
|
Data []byte
|
||||||
AESNonce []byte
|
Nonce uint64
|
||||||
Data []byte
|
|
||||||
Nonce uint64
|
|
||||||
|
|
||||||
pow float64 // Message-specific PoW as described in the Whisper specification.
|
pow float64 // Message-specific PoW as described in the Whisper specification.
|
||||||
hash common.Hash // Cached hash of the envelope to avoid rehashing every time.
|
hash common.Hash // Cached hash of the envelope to avoid rehashing every time.
|
||||||
|
|
@ -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,
|
Data: msg.Raw,
|
||||||
AESNonce: aesNonce,
|
Nonce: 0,
|
||||||
Data: msg.Raw,
|
|
||||||
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
|
||||||
}
|
}
|
||||||
|
|
|
||||||
64
whisper/whisperv6/envelope_test.go
Normal file
64
whisper/whisperv6/envelope_test.go
Normal 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(¶ms)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to create new message with seed %d: %s.", seed, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
e, err := msg.Wrap(¶ms)
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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 {
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
@ -69,9 +70,8 @@ type ReceivedMessage struct {
|
||||||
Dst *ecdsa.PublicKey // Message recipient (identity used to decode the message)
|
Dst *ecdsa.PublicKey // Message recipient (identity used to decode the message)
|
||||||
Topic TopicType
|
Topic TopicType
|
||||||
|
|
||||||
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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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,16 +626,11 @@ 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
|
if isP2P {
|
||||||
// currently supported version, we can not decrypt it,
|
w.p2pMsgQueue <- envelope
|
||||||
// and therefore just ignore this message
|
} else {
|
||||||
if envelope.Ver() <= EnvelopeVersion {
|
w.checkOverflow()
|
||||||
if isP2P {
|
w.messageQueue <- envelope
|
||||||
w.p2pMsgQueue <- envelope
|
|
||||||
} else {
|
|
||||||
w.checkOverflow()
|
|
||||||
w.messageQueue <- envelope
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue