From a85fd790ab62825c8683b2ec0177981a81f43dbe Mon Sep 17 00:00:00 2001 From: bfbd Date: Mon, 17 Jul 2017 21:34:11 +0800 Subject: [PATCH] address v0.12: Modify import packages. --- .gitignore | 3 +- README.md | 36 ++++- address.go | 180 ++++++++++++------------ vendor/github.com/njones/bitcoin-crypto | 1 + 4 files changed, 125 insertions(+), 95 deletions(-) create mode 160000 vendor/github.com/njones/bitcoin-crypto 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