aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--README.md2
-rw-r--r--cmd/mist/assets/qml/browser.qml32
-rw-r--r--cmd/mist/assets/qml/main.qml63
-rw-r--r--cmd/mist/assets/qml/views/whisper.qml1
-rw-r--r--cmd/mist/ui_lib.go9
-rw-r--r--crypto/crypto.go112
-rw-r--r--crypto/crypto_test.go14
-rw-r--r--crypto/key.go9
-rw-r--r--crypto/key_store_passphrase.go46
-rw-r--r--crypto/key_store_test.go13
-rw-r--r--whisper/peer.go2
11 files changed, 184 insertions, 119 deletions
diff --git a/README.md b/README.md
index 4830d9303..a824e9001 100644
--- a/README.md
+++ b/README.md
@@ -46,7 +46,7 @@ Go Ethereum comes with several binaries found in
* `mist` Official Ethereum Browser
* `ethereum` Ethereum CLI
* `ethtest` test tool which runs with the [tests](https://github.com/ethereum/testes) suit:
- `ethtest "`cat myfile.json`"`.
+ `cat file | ethtest`.
* `evm` is a generic Ethereum Virtual Machine: `evm -code 60ff60ff -gas
10000 -price 0 -dump`. See `-h` for a detailed description.
diff --git a/cmd/mist/assets/qml/browser.qml b/cmd/mist/assets/qml/browser.qml
index c8f291e22..7056dbbf3 100644
--- a/cmd/mist/assets/qml/browser.qml
+++ b/cmd/mist/assets/qml/browser.qml
@@ -9,15 +9,16 @@ import Ethereum 1.0
Rectangle {
id: window
- objectName: "browserView"
anchors.fill: parent
color: "#00000000"
- property var title: "Browser"
+ property var title: "DApps"
property var iconSource: "../browser.png"
property var menuItem
+ property var hideUrl: true
property alias url: webview.url
+ property alias windowTitle: webview.title
property alias webView: webview
property var cleanPath: false
@@ -66,8 +67,7 @@ Rectangle {
webview.url = "http://etherian.io"
}
- signal messages(var messages, int id);
- onMessages: {
+ function messages(messages, id) {
// Bit of a cheat to get proper JSON
var m = JSON.parse(JSON.parse(JSON.stringify(messages)))
webview.postEvent("eth_changed", id, m);
@@ -164,22 +164,10 @@ Rectangle {
id: webview
anchors.fill: parent
- function injectJs(js) {
- webview.experimental.navigatorQtObjectEnabled = true;
- webview.experimental.evaluateJavaScript(js)
- webview.experimental.javascriptEnabled = true;
- }
-
function sendMessage(data) {
webview.experimental.postMessage(JSON.stringify(data))
}
- Component.onCompleted: {
- for (var i in experimental.preferences) {
- console.log(i)
- }
- }
-
experimental.preferences.javascriptEnabled: true
experimental.preferences.webAudioEnabled: true
experimental.preferences.pluginsEnabled: true
@@ -219,8 +207,7 @@ Rectangle {
}
experimental.userScripts: ["../ext/q.js", "../ext/ethereum.js/lib/web3.js", "../ext/ethereum.js/lib/qt.js", "../ext/setup.js"]
experimental.onMessageReceived: {
- console.log("[onMessageReceived]: ", message.data)
- // TODO move to messaging.js
+ //console.log("[onMessageReceived]: ", message.data)
var data = JSON.parse(message.data)
try {
@@ -350,13 +337,13 @@ Rectangle {
case "eth_newFilterString":
require(1)
- var id = eth.newFilterString(data.args[0])
+ var id = eth.newFilterString(data.args[0], window)
postData(data._id, id);
break;
case "eth_newFilter":
require(1)
- var id = eth.newFilter(data.args[0])
+ var id = eth.newFilter(data.args[0], window)
postData(data._id, id);
break;
@@ -425,11 +412,9 @@ Rectangle {
}
}
-
function post(seed, data) {
postData(data._id, data)
}
-
function require(args, num) {
if(args.length < num) {
throw("required argument count of "+num+" got "+args.length);
@@ -441,12 +426,10 @@ Rectangle {
function postEvent(event, id, data) {
webview.experimental.postMessage(JSON.stringify({data: data, _id: id, _event: event}))
}
-
function onWatchedCb(data, id) {
var messages = JSON.parse(data)
postEvent("watched:"+id, messages)
}
-
function onNewBlockCb(block) {
postEvent("block:new", block)
}
@@ -460,7 +443,6 @@ Rectangle {
}
}
-
Rectangle {
id: sizeGrip
color: "gray"
diff --git a/cmd/mist/assets/qml/main.qml b/cmd/mist/assets/qml/main.qml
index 7b207cbcc..c6b524549 100644
--- a/cmd/mist/assets/qml/main.qml
+++ b/cmd/mist/assets/qml/main.qml
@@ -13,7 +13,6 @@ ApplicationWindow {
id: root
property var ethx : Eth.ethx
- property var browser
width: 1200
height: 820
@@ -21,6 +20,7 @@ ApplicationWindow {
title: "Mist"
+ /*
// This signal is used by the filter API. The filter API connects using this signal handler from
// the different QML files and plugins.
signal messages(var messages, int id);
@@ -30,6 +30,7 @@ ApplicationWindow {
messages(data, receiverSeed);
root.browser.view.messages(data, receiverSeed);
}
+ */
TextField {
id: copyElementHax
@@ -45,8 +46,6 @@ ApplicationWindow {
// Takes care of loading all default plugins
Component.onCompleted: {
var wallet = addPlugin("./views/wallet.qml", {noAdd: true, close: false, section: "ethereum", active: true});
- var browser = addPlugin("./browser.qml", {noAdd: true, close: false, section: "ethereum", active: true});
- root.browser = browser;
addPlugin("./views/miner.qml", {noAdd: true, close: false, section: "ethereum", active: true});
addPlugin("./views/transaction.qml", {noAdd: true, close: false, section: "legacy"});
@@ -55,17 +54,17 @@ ApplicationWindow {
addPlugin("./views/pending_tx.qml", {noAdd: true, close: false, section: "legacy"});
addPlugin("./views/info.qml", {noAdd: true, close: false, section: "legacy"});
- addPlugin("./views/jeffcoin/jeffcoin.qml", {noAdd: true, close: false, section: "apps"})
-
mainSplit.setView(wallet.view, wallet.menuItem);
+ newBrowserTab("http://etherian.io");
+
// Command setup
gui.sendCommand(0)
}
function activeView(view, menuItem) {
mainSplit.setView(view, menuItem)
- if (view.objectName === "browserView") {
+ if (view.hideUrl) {
urlPane.visible = false;
mainView.anchors.top = rootView.top
} else {
@@ -119,6 +118,13 @@ ApplicationWindow {
}
}
+ function newBrowserTab(url) {
+ var window = addPlugin("./browser.qml", {noAdd: true, close: true, section: "apps", active: true});
+ window.view.url = url;
+ window.menuItem.title = "Browser Tab";
+ activeView(window.view, window.menuItem);
+ }
+
menuBar: MenuBar {
Menu {
title: "File"
@@ -130,13 +136,6 @@ ApplicationWindow {
}
}
- /*
- MenuItem {
- text: "Browser"
- onTriggered: eth.openBrowser()
- }
- */
-
MenuItem {
text: "Add plugin"
onTriggered: {
@@ -146,6 +145,14 @@ ApplicationWindow {
}
}
+ MenuItem {
+ text: "New tab"
+ shortcut: "Ctrl+t"
+ onTriggered: {
+ newBrowserTab("http://etherian.io");
+ }
+ }
+
MenuSeparator {}
MenuItem {
@@ -205,21 +212,6 @@ ApplicationWindow {
}
MenuSeparator {}
-
- /*
- MenuItem {
- id: miningSpeed
- text: "Mining: Turbo"
- onTriggered: {
- gui.toggleTurboMining()
- if(text == "Mining: Turbo") {
- text = "Mining: Normal";
- } else {
- text = "Mining: Turbo";
- }
- }
- }
- */
}
Menu {
@@ -350,9 +342,6 @@ ApplicationWindow {
views[i].menuItem.setSelection(false)
}
view.visible = true
-
- //menu.border.color = "#CCCCCC"
- //menu.color = "#FFFFFFFF"
menu.setSelection(true)
}
@@ -512,7 +501,15 @@ ApplicationWindow {
this.view.destroy()
this.destroy()
+ for (var i = 0; i < mainSplit.views.length; i++) {
+ var view = mainSplit.views[i];
+ if (view.menuItem === this) {
+ mainSplit.views.splice(i, 1);
+ break;
+ }
+ }
gui.removePlugin(this.path)
+ activeView(mainSplit.views[0].view, mainSplit.views[0].menuItem);
}
}
}
@@ -576,7 +573,7 @@ ApplicationWindow {
Text {
- text: "APPS"
+ text: "NET"
font.bold: true
anchors {
left: parent.left
@@ -653,7 +650,7 @@ ApplicationWindow {
Keys.onReturnPressed: {
if(/^https?/.test(this.text)) {
- activeView(root.browser.view, root.browser.menuItem);
+ newBrowserTab(this.text);
} else {
addPlugin(this.text, {close: true, section: "apps"})
}
diff --git a/cmd/mist/assets/qml/views/whisper.qml b/cmd/mist/assets/qml/views/whisper.qml
index 029376c45..dc097b806 100644
--- a/cmd/mist/assets/qml/views/whisper.qml
+++ b/cmd/mist/assets/qml/views/whisper.qml
@@ -18,7 +18,6 @@ Rectangle {
property var identity: ""
Component.onCompleted: {
identity = shh.newIdentity()
- console.log("New identity:", identity)
var t = shh.watch({}, root)
}
diff --git a/cmd/mist/ui_lib.go b/cmd/mist/ui_lib.go
index 596f8442a..e0321f6dd 100644
--- a/cmd/mist/ui_lib.go
+++ b/cmd/mist/ui_lib.go
@@ -316,20 +316,15 @@ func (self *UiLib) NewFilter(object map[string]interface{}, view *qml.Common) (i
filter := qt.NewFilterFromMap(object, self.eth)
filter.MessageCallback = func(messages state.Messages) {
view.Call("messages", xeth.ToJSMessages(messages), id)
- //self.win.Root().Call("invokeFilterCallback", xeth.ToJSMessages(messages), id)
}
id = self.filterManager.InstallFilter(filter)
return id
}
-func (self *UiLib) NewFilterString(typ string) (id int) {
+func (self *UiLib) NewFilterString(typ string, view *qml.Common) (id int) {
filter := core.NewFilter(self.eth)
filter.BlockCallback = func(block *types.Block) {
- if self.win != nil && self.win.Root() != nil {
- self.win.Root().Call("invokeFilterCallback", "{}", id)
- } else {
- fmt.Println("QML is lagging")
- }
+ view.Call("messages", "{}", id)
}
id = self.filterManager.InstallFilter(filter)
return id
diff --git a/crypto/crypto.go b/crypto/crypto.go
index ac84c6204..4b2cc7bb4 100644
--- a/crypto/crypto.go
+++ b/crypto/crypto.go
@@ -1,11 +1,20 @@
package crypto
import (
+ "crypto/aes"
+ "crypto/cipher"
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
"crypto/sha256"
+ "fmt"
+ "encoding/hex"
+ "encoding/json"
+ "errors"
+
+ "code.google.com/p/go-uuid/uuid"
+ "code.google.com/p/go.crypto/pbkdf2"
"code.google.com/p/go.crypto/ripemd160"
"github.com/ethereum/go-ethereum/crypto/secp256k1"
"github.com/ethereum/go-ethereum/crypto/sha3"
@@ -101,7 +110,11 @@ func SigToPub(hash, sig []byte) *ecdsa.PublicKey {
}
func Sign(hash []byte, prv *ecdsa.PrivateKey) (sig []byte, err error) {
- sig, err = secp256k1.Sign(hash, prv.D.Bytes())
+ if len(hash) != 32 {
+ return nil, fmt.Errorf("hash is required to be exactly 32 bytes (%d)", len(hash))
+ }
+
+ sig, err = secp256k1.Sign(hash, ethutil.LeftPadBytes(prv.D.Bytes(), prv.Params().BitSize/8))
return
}
@@ -113,3 +126,100 @@ func Decrypt(prv *ecdsa.PrivateKey, ct []byte) ([]byte, error) {
key := ecies.ImportECDSA(prv)
return key.Decrypt(rand.Reader, ct, nil, nil)
}
+
+// creates a Key and stores that in the given KeyStore by decrypting a presale key JSON
+func ImportPreSaleKey(keyStore KeyStore2, keyJSON []byte, password string) (*Key, error) {
+ key, err := decryptPreSaleKey(keyJSON, password)
+ if err != nil {
+ return nil, err
+ }
+ id := uuid.NewRandom()
+ key.Id = &id
+ err = keyStore.StoreKey(key, password)
+ return key, err
+}
+
+func decryptPreSaleKey(fileContent []byte, password string) (key *Key, err error) {
+ preSaleKeyStruct := struct {
+ EncSeed string
+ EthAddr string
+ Email string
+ BtcAddr string
+ }{}
+ err = json.Unmarshal(fileContent, &preSaleKeyStruct)
+ if err != nil {
+ return nil, err
+ }
+ encSeedBytes, err := hex.DecodeString(preSaleKeyStruct.EncSeed)
+ iv := encSeedBytes[:16]
+ cipherText := encSeedBytes[16:]
+ /*
+ See https://github.com/ethereum/pyethsaletool
+
+ pyethsaletool generates the encryption key from password by
+ 2000 rounds of PBKDF2 with HMAC-SHA-256 using password as salt (:().
+ 16 byte key length within PBKDF2 and resulting key is used as AES key
+ */
+ passBytes := []byte(password)
+ derivedKey := pbkdf2.Key(passBytes, passBytes, 2000, 16, sha256.New)
+ plainText, err := aesCBCDecrypt(derivedKey, cipherText, iv)
+ ethPriv := Sha3(plainText)
+ ecKey := ToECDSA(ethPriv)
+ key = &Key{
+ Id: nil,
+ PrivateKey: ecKey,
+ }
+ derivedAddr := ethutil.Bytes2Hex(key.Address())
+ expectedAddr := preSaleKeyStruct.EthAddr
+ if derivedAddr != expectedAddr {
+ err = errors.New("decrypted addr not equal to expected addr")
+ }
+ return key, err
+}
+
+func aesCBCDecrypt(key []byte, cipherText []byte, iv []byte) (plainText []byte, err error) {
+ aesBlock, err := aes.NewCipher(key)
+ if err != nil {
+ return plainText, err
+ }
+ decrypter := cipher.NewCBCDecrypter(aesBlock, iv)
+ paddedPlainText := make([]byte, len(cipherText))
+ decrypter.CryptBlocks(paddedPlainText, cipherText)
+ plainText = PKCS7Unpad(paddedPlainText)
+ if plainText == nil {
+ err = errors.New("Decryption failed: PKCS7Unpad failed after decryption")
+ }
+ return plainText, err
+}
+
+// From https://leanpub.com/gocrypto/read#leanpub-auto-block-cipher-modes
+func PKCS7Pad(in []byte) []byte {
+ padding := 16 - (len(in) % 16)
+ if padding == 0 {
+ padding = 16
+ }
+ for i := 0; i < padding; i++ {
+ in = append(in, byte(padding))
+ }
+ return in
+}
+
+func PKCS7Unpad(in []byte) []byte {
+ if len(in) == 0 {
+ return nil
+ }
+
+ padding := in[len(in)-1]
+ if int(padding) > len(in) || padding > aes.BlockSize {
+ return nil
+ } else if padding == 0 {
+ return nil
+ }
+
+ for i := len(in) - 1; i > len(in)-int(padding)-1; i-- {
+ if in[i] != padding {
+ return nil
+ }
+ }
+ return in[:len(in)-int(padding)]
+}
diff --git a/crypto/crypto_test.go b/crypto/crypto_test.go
index 349bc31ae..441733f93 100644
--- a/crypto/crypto_test.go
+++ b/crypto/crypto_test.go
@@ -52,7 +52,7 @@ func BenchmarkSha3(b *testing.B) {
}
func Test0Key(t *testing.T) {
-
+ t.Skip()
key := ethutil.Hex2Bytes("1111111111111111111111111111111111111111111111111111111111111111")
p, err := secp256k1.GeneratePubKey(key)
@@ -60,3 +60,15 @@ func Test0Key(t *testing.T) {
fmt.Printf("%x\n", p)
fmt.Printf("%v %x\n", err, addr)
}
+
+func TestInvalidSign(t *testing.T) {
+ _, err := Sign(make([]byte, 1), nil)
+ if err == nil {
+ t.Errorf("expected sign with hash 1 byte to error")
+ }
+
+ _, err = Sign(make([]byte, 33), nil)
+ if err == nil {
+ t.Errorf("expected sign with hash 33 byte to error")
+ }
+}
diff --git a/crypto/key.go b/crypto/key.go
index d371ad4dc..ca29b691f 100644
--- a/crypto/key.go
+++ b/crypto/key.go
@@ -57,7 +57,7 @@ type encryptedKeyJSON struct {
func (k *Key) Address() []byte {
pubBytes := FromECDSAPub(&k.PrivateKey.PublicKey)
- return Sha3(pubBytes)[12:]
+ return Sha3(pubBytes[1:])[12:]
}
func (k *Key) MarshalJSON() (j []byte, err error) {
@@ -99,9 +99,10 @@ func NewKey(rand io.Reader) *Key {
privateKeyMarshalled := elliptic.Marshal(S256(), x, y)
privateKeyECDSA := ToECDSA(privateKeyMarshalled)
- key := new(Key)
id := uuid.NewRandom()
- key.Id = &id
- key.PrivateKey = privateKeyECDSA
+ key := &Key{
+ Id: &id,
+ PrivateKey: privateKeyECDSA,
+ }
return key
}
diff --git a/crypto/key_store_passphrase.go b/crypto/key_store_passphrase.go
index e30a0a785..80bf49d68 100644
--- a/crypto/key_store_passphrase.go
+++ b/crypto/key_store_passphrase.go
@@ -178,22 +178,10 @@ func DecryptKey(ks keyStorePassphrase, keyId *uuid.UUID, auth string) (keyBytes
if err != nil {
return nil, err
}
-
- AES256Block, err := aes.NewCipher(derivedKey)
+ plainText, err := aesCBCDecrypt(derivedKey, cipherText, iv)
if err != nil {
return nil, err
}
-
- AES256CBCDecrypter := cipher.NewCBCDecrypter(AES256Block, iv)
- paddedPlainText := make([]byte, len(cipherText))
- AES256CBCDecrypter.CryptBlocks(paddedPlainText, cipherText)
-
- plainText := PKCS7Unpad(paddedPlainText)
- if plainText == nil {
- err = errors.New("Decryption failed: PKCS7Unpad failed after decryption")
- return nil, err
- }
-
keyBytes = plainText[:len(plainText)-32]
keyBytesHash := plainText[len(plainText)-32:]
if !bytes.Equal(Sha3(keyBytes), keyBytesHash) {
@@ -211,35 +199,3 @@ func getEntropyCSPRNG(n int) []byte {
}
return mainBuff
}
-
-// From https://leanpub.com/gocrypto/read#leanpub-auto-block-cipher-modes
-func PKCS7Pad(in []byte) []byte {
- padding := 16 - (len(in) % 16)
- if padding == 0 {
- padding = 16
- }
- for i := 0; i < padding; i++ {
- in = append(in, byte(padding))
- }
- return in
-}
-
-func PKCS7Unpad(in []byte) []byte {
- if len(in) == 0 {
- return nil
- }
-
- padding := in[len(in)-1]
- if int(padding) > len(in) || padding > aes.BlockSize {
- return nil
- } else if padding == 0 {
- return nil
- }
-
- for i := len(in) - 1; i > len(in)-int(padding)-1; i-- {
- if in[i] != padding {
- return nil
- }
- }
- return in[:len(in)-int(padding)]
-}
diff --git a/crypto/key_store_test.go b/crypto/key_store_test.go
index 16c0e476d..54efc739a 100644
--- a/crypto/key_store_test.go
+++ b/crypto/key_store_test.go
@@ -83,3 +83,16 @@ func TestKeyStorePassphraseDecryptionFail(t *testing.T) {
t.Fatal(err)
}
}
+
+func TestImportPreSaleKey(t *testing.T) {
+ // file content of a presale key file generated with:
+ // python pyethsaletool.py genwallet
+ // with password "foo"
+ fileContent := "{\"encseed\": \"26d87f5f2bf9835f9a47eefae571bc09f9107bb13d54ff12a4ec095d01f83897494cf34f7bed2ed34126ecba9db7b62de56c9d7cd136520a0427bfb11b8954ba7ac39b90d4650d3448e31185affcd74226a68f1e94b1108e6e0a4a91cdd83eba\", \"ethaddr\": \"d4584b5f6229b7be90727b0fc8c6b91bb427821f\", \"email\": \"gustav.simonsson@gmail.com\", \"btcaddr\": \"1EVknXyFC68kKNLkh6YnKzW41svSRoaAcx\"}"
+ ks := NewKeyStorePassphrase(DefaultDataDir())
+ pass := "foo"
+ _, err := ImportPreSaleKey(ks, []byte(fileContent), pass)
+ if err != nil {
+ t.Fatal(err)
+ }
+}
diff --git a/whisper/peer.go b/whisper/peer.go
index f82cc6e3e..332ddd22a 100644
--- a/whisper/peer.go
+++ b/whisper/peer.go
@@ -81,7 +81,7 @@ func (self *peer) broadcast(envelopes []*Envelope) error {
if err := self.ws.WriteMsg(msg); err != nil {
return err
}
- self.peer.Infoln("broadcasted", i, "message(s)")
+ self.peer.DebugDetailln("broadcasted", i, "message(s)")
}
return nil