diff --git a/.gitignore b/.gitignore
index 3219bdb..f46854f 100644
--- a/.gitignore
+++ b/.gitignore
@@ -3,7 +3,6 @@
*.dll
*.so
*.dylib
-*.idea
# Test binary, build with `go test -c`
*.test
@@ -13,3 +12,5 @@
# Project-local glide cache, RE: https://github.com/Masterminds/glide/issues/736
.glide/
+
+.idea/
diff --git a/README.md b/README.md
index 491dc68..d41dac9 100644
--- a/README.md
+++ b/README.md
@@ -1,2 +1,36 @@
# gowallet
-A bitcoin wallet application written in golang.
+
+A bitcoin wallet application written in golang.
+Supports random wallet and brain wallet.
+
+The brain wallet uses a secret phrase and a salt phrase to generate the private key.
+
+Secret phrase at least 16 characters, containing uppercase letters, lowercase letters, numbers, and special characters.
+Salt phrase at least 6 characters.
+
+The secret phrase and the salt phrase support a hex notation similar to '\xFF' or '\xff' to represent a character.
+
+It is advisable to use more complex secret phrases and to write secret phrases on paper.
+It is also recommended that salt phrases be memorized in the brain.
+
+
+Usage of address:
+ -b Brain wallet mode.
+ -brain
+ Brain wallet mode.
+ -o string
+ Output file name.
+ -output string
+ Output file name.
+
+
+# go钱包
+go钱包是用GO语言编写的比特币钱包软件。支持随机钱包和脑钱包。
+
+**脑钱包使用一个秘密短语和一个盐短语生成私钥。**
+秘密短语至少16个字符,包含大写字母,小写字母,数字和特殊字符。
+盐短语至少6个字符。
+秘密短语和盐短语允许使用类似于'\xFF'这样的十六进制表示法表示一个字符
+
+建议使用较为复杂的秘密短语并将秘密短语记在纸上。
+同时建议将盐短语记在脑中。
diff --git a/address.go b/address.go
index fc46650..7c9c90c 100644
--- a/address.go
+++ b/address.go
@@ -2,137 +2,115 @@ package main
import (
"bytes"
+ "crypto/rand"
"crypto/sha1"
"crypto/sha256"
+ "encoding/hex"
"errors"
"flag"
"fmt"
"io/ioutil"
- "crypto/rand"
"os"
"regexp"
- "syscall"
- "./secp256k1/bitecdsa"
- "./secp256k1/bitelliptic"
"github.com/btcsuite/btcutil/base58"
"github.com/fatih/color"
+ "github.com/njones/bitcoin-crypto/bitelliptic"
"golang.org/x/crypto/pbkdf2"
"golang.org/x/crypto/ripemd160"
"golang.org/x/crypto/scrypt"
"golang.org/x/crypto/ssh/terminal"
)
-// WarpWallet encryption:
-// 1. s1 ← scrypt(key=passphrase||0x1, salt=salt||0x1, N=218, r=8, p=1, dkLen=32)
-// 2. s2 ← PBKDF2(key=passphrase||0x2, salt=salt||0x2, c=216, dkLen=32)
-// 3. private_key ← s1 ⊕ s2
-// 4. Generate public_key from private_key using standard Bitcoin EC crypto
-// 5. Output (private_key, public_key)
-
-//脑钱包使用一个秘密短语和一个盐短语生成私钥。
-//秘密短语至少16个字符,包含大写字母,小写字母,数字和特殊字符。
-//盐短语至少6个字符。
-//建议使用较为复杂的秘密短语并将秘密短语记在纸上。
-//同时建议将盐短语记在脑中。
-
const brainWalletTip = `
The brain wallet uses a secret phrase and a salt phrase to generate the private key.
Secret phrase at least 16 characters, containing uppercase letters, lowercase letters, numbers, and special characters.
Salt phrase at least 6 characters.
+Secret phrases and salt phrases allow the use of hexadecimal notation similar to ' \xff ' to represent a character.
It is advisable to use more complex secret phrases and to write secret phrases on paper.
It is also recommended that salt phrases be memorized in the brain.`
-const debug = true
+const debug = false
//Parse command line parameters
-func parseCommandParams() (private string, brain bool, output string) {
- flag.StringVar(&private, "private", "", "Private key wif string for test.")
+func parseCommandParams() (brain bool, output string) {
flag.BoolVar(&brain, "brain", false, "Brain wallet mode.")
- flag.BoolVar(&brain, "b", false, "...")
+ flag.BoolVar(&brain, "b", false, "Brain wallet mode.")
- flag.StringVar(&output, "output", "", "Output file name. (optional)")
- flag.StringVar(&output, "o", "", "...")
+ flag.StringVar(&output, "output", "", "Output file name.")
+ flag.StringVar(&output, "o", "", "Output file name.")
flag.Parse()
return
}
func main() {
- private, brain, output := parseCommandParams()
-
- var private_key [32]byte
- if private == "" {
- if brain == true {
- // Brain wallet
- secret, salt, err := inputBrainWalletSecret(brainWalletTip)
- if err != nil {
- println(err.Error())
- return
- }
- //secret, salt = []byte("www.aiportal.net"), []byte("aiportal")
- private_key, err = generateBrainWalletKey(secret, salt)
- if err != nil {
- println(err.Error())
- return
- }
- } else {
- // Random private key.
- private_key_bytes, err := generateRandomBytes(32)
- if err == nil {
- copy(private_key[:], private_key_bytes)
- } else {
- println(err)
- return
- }
+ brain, output := parseCommandParams()
+
+ var seed []byte
+ if brain == true {
+ // Brain wallet
+ secret, salt, err := inputBrainWalletSecret(brainWalletTip)
+ if err != nil {
+ return
+ }
+ seed, err = generateBrainWalletSeed(secret, salt)
+ if err != nil {
+ println(err.Error())
+ return
}
} else {
- // Private key from WIF string.
- private_key_bytes, _, _ := base58.CheckDecode(private)
- copy(private_key[:], private_key_bytes)
+ // Random wallet.
+ var err error
+ seed, err = generateRandomBytes(32)
+ if err != nil {
+ println(err.Error())
+ return
+ }
}
- private_wif := base58.CheckEncode(private_key[:], 0x80)
+ private_wif, address_wif, err := GenerateWalletWif(seed)
+ if err != nil {
+ println(err.Error())
+ return
+ }
+ println("")
println("private: " + private_wif)
-
- public_key := computePublicKey(private_key)
- public_wif := base58.CheckEncode(public_key[:], 0x00)
- println("address: " + public_wif)
+ println("address: " + address_wif)
if output != "" {
- err := ioutil.WriteFile(output, []byte(public_wif), os.ModeAppend)
+ ln := fmt.Sprintf("private: %s\naddress: %s", private_wif, address_wif)
+ err := ioutil.WriteFile(output, []byte(ln), os.ModeAppend)
if err != nil {
- fmt.Printf("Failed to write to file. %s", err)
+ println(err.Error())
}
}
}
-// Compute the public key from private key.
-func computePublicKey(privateKey [32]byte) []byte {
-
- reader := bytes.NewReader(privateKey[:])
- key, err := bitecdsa.GenerateKey(bitelliptic.S256(), reader)
+// Generate wallet private key and address
+func GenerateWalletWif(seed []byte) (privateWif string, addressWif string, err error) {
+ reader := bytes.NewReader(seed)
+ private_bytes, x, y, err := bitelliptic.S256().GenerateKey(reader)
if err != nil {
- println(err)
- return []byte{}
+ return
}
+ privateWif = base58.CheckEncode(private_bytes, 0x80)
- var public_key = [65]byte{0x04}
- x_bytes := key.X.Bytes()
- y_bytes := key.Y.Bytes()
- copy(public_key[33-len(x_bytes):], x_bytes)
- copy(public_key[65-len(y_bytes):], y_bytes)
+ var public_bytes = [65]byte{0x04}
+ copy(public_bytes[33 - len(x.Bytes()):], x.Bytes())
+ copy(public_bytes[65 - len(y.Bytes()):], y.Bytes())
- public_key_sha := sha256.Sum256(public_key[:])
+ public_sha := sha256.Sum256(public_bytes[:])
ripeHash := ripemd160.New()
- ripeHash.Write(public_key_sha[:])
- public_key_ripe := ripeHash.Sum(nil)
+ ripeHash.Write(public_sha[:])
+ public_ripe := ripeHash.Sum(nil)
- return public_key_ripe[:]
+ addressWif = base58.CheckEncode(public_ripe, 0x00)
+ return
}
//Generate secure random private key seed.
@@ -154,6 +132,7 @@ func inputBrainWalletSecret(tip string) (secret []byte, salt []byte, err error)
color.Yellow(tip)
println("")
+ terminal.MakeRaw(int(os.Stdin.Fd()))
t := terminal.NewTerminal(os.Stdin, "")
// Secret
@@ -196,7 +175,7 @@ func inputBrainWalletSecret(tip string) (secret []byte, salt []byte, err error)
}
if debug { print(salt1) }
println("")
- if len(salt1) < 6 {
+ if len(escapeHexString(salt1)) < 6 {
color.HiRed(" Salt at least 6 characters.")
err = errInput
return
@@ -217,6 +196,10 @@ func inputBrainWalletSecret(tip string) (secret []byte, salt []byte, err error)
secret = escapeHexString(secret1)
salt = escapeHexString(salt1)
+ if debug {
+ fmt.Printf("secret: %X\n", secret)
+ fmt.Printf("salt: %X\n", salt)
+ }
return
}
@@ -238,20 +221,29 @@ func escapeHexString(str string) []byte {
}
//Check secret strength
-func checkSecretStrength(secret string) (valid bool) {
+func checkSecretStrength(secret string) bool {
number, _ := regexp.MatchString("[0-9]+", secret)
lower, _ := regexp.MatchString("[a-z]+", secret)
upper, _ := regexp.MatchString("[A-Z]+", secret)
special, _ := regexp.MatchString("[^0-9a-zA-Z ]", secret)
- valid = number && lower && upper && special
- return
+ return number && lower && upper && special
}
-// Generate private key from secret and salt
-func generateBrainWalletKey(secret []byte, salt []byte) (key [32]byte, err error) {
-
- if len(secret) == 0 || len(salt) < 0 {
- err = errors.New("empty secret or salt")
+// Generate wallet seed from secret and salt
+func generateBrainWalletSeed(secret []byte, salt []byte) (seed []byte, err error) {
+ // WarpWallet encryption:
+ // 1. s1 ← scrypt(key=passphrase||0x1, salt=salt||0x1, N=218, r=8, p=1, dkLen=32)
+ // 2. s2 ← PBKDF2(key=passphrase||0x2, salt=salt||0x2, c=216, dkLen=32)
+ // 3. private_key ← s1 ⊕ s2
+ // 4. Generate public_key from private_key using standard Bitcoin EC crypto
+ // 5. Output (private_key, public_key)
+
+ if len(secret) == 0 {
+ err = errors.New("Empty secret")
+ return
+ }
+ if len(salt) < 0 {
+ err = errors.New("Empty salt")
return
}
@@ -261,32 +253,34 @@ func generateBrainWalletKey(secret []byte, salt []byte) (key [32]byte, err error
secret1[i] = v | 0x1
secret2[i] = v | 0x2
}
+
salt1 := make([]byte, len(salt))
salt2 := make([]byte, len(salt))
for i, v := range salt {
salt1[i] = v | 0x1
salt2[i] = v | 0x2
}
- key1, err := scrypt.Key(secret1, salt1, 16384, 8, 1, 32)
+
+ s1, err := scrypt.Key(secret1, salt1, 16384, 8, 1, 32)
if err != nil {
return
}
- key2 := pbkdf2.Key(secret2, salt2, 4096, 32, sha1.New)
+ s2 := pbkdf2.Key(secret2, salt2, 4096, 32, sha1.New)
- pk1, err := bitecdsa.GenerateKey(bitelliptic.S256(), bytes.NewReader(key1))
+ _, x1, y1, err := bitelliptic.S256().GenerateKey(bytes.NewReader(s1))
if err != nil {
return
}
- pk2, err := bitecdsa.GenerateKey(bitelliptic.S256(), bytes.NewReader(key2))
+ _, x2, y2, err := bitelliptic.S256().GenerateKey(bytes.NewReader(s2))
if err != nil {
return
}
- x, y := bitelliptic.S256().Add(pk1.X, pk1.Y, pk2.X, pk2.Y)
- key_bytes := []byte{0x04}
- key_bytes = append(key_bytes, x.Bytes()...)
- key_bytes = append(key_bytes, y.Bytes()...)
- key = sha256.Sum256(key_bytes[:])
+ x, y := bitelliptic.S256().Add(x1, y1, x2, y2)
+
+ seed = []byte{0x04}
+ seed = append(seed, x.Bytes()...)
+ seed = append(seed, y.Bytes()...)
return
}
diff --git a/vendor/github.com/njones/bitcoin-crypto b/vendor/github.com/njones/bitcoin-crypto
new file mode 160000
index 0000000..ea1e694
--- /dev/null
+++ b/vendor/github.com/njones/bitcoin-crypto
@@ -0,0 +1 @@
+Subproject commit ea1e694702736efbe373f7b98d7cb36f95f8dae6