forked from vertcoin-project/one-click-miner-vnext
-
Notifications
You must be signed in to change notification settings - Fork 13
/
keyfile.go
136 lines (116 loc) · 3.48 KB
/
keyfile.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
package keyfile
import (
"bytes"
"crypto/rand"
"fmt"
"os"
"path/filepath"
"github.com/btcsuite/btcd/btcec"
"github.com/btcsuite/btcd/txscript"
"github.com/btcsuite/btcutil"
"github.com/btcsuite/btcutil/base58"
"github.com/vertiond/verthash-one-click-miner/logging"
"github.com/vertiond/verthash-one-click-miner/networks"
"github.com/vertiond/verthash-one-click-miner/util"
"golang.org/x/crypto/nacl/secretbox"
"golang.org/x/crypto/scrypt"
)
// KeyFileValid returns true if there is a valid initialized keyfile
// available
func KeyFileValid() bool {
return len(loadPublicKey()) == 33
}
func CreateKeyFile(pass string) error {
filename := keyFile()
// Create random key
priv32 := new([32]byte)
_, err := rand.Read(priv32[:])
if err != nil {
return err
}
// Derive pubkey
_, pub := btcec.PrivKeyFromBytes(btcec.S256(), priv32[:])
salt := new([24]byte) // salt for scrypt / nonce for secretbox
dk32 := new([32]byte) // derived key from scrypt
//get 24 random bytes for scrypt salt (and secretbox nonce)
_, err = rand.Read(salt[:])
if err != nil {
return err
}
// next use the pass and salt to make a 32-byte derived key
dk, err := scrypt.Key([]byte(pass), salt[:], 16384, 8, 1, 32)
if err != nil {
return err
}
copy(dk32[:], dk[:])
enckey := append(salt[:], secretbox.Seal(nil, priv32[:], salt, dk32)...)
return os.WriteFile(filename, append(pub.SerializeCompressed(), enckey...), 0600)
}
func keyFile() string {
return filepath.Join(util.DataDirectory(), "dogekeyfile.hex")
}
func loadPublicKey() []byte {
filename := keyFile()
b, err := os.ReadFile(filename)
if err != nil {
logging.Infof("Error reading keyfile: %s", err.Error())
return []byte{}
}
if len(b) != 105 {
logging.Infof("Keyfile had wrong length. Expected 129, got %d", len(b))
return []byte{}
}
ret := make([]byte, 33)
copy(ret, b[:33])
b = nil
return ret
}
func GetPublicKey() []byte {
pub := loadPublicKey()
return pub
}
func GetAddress() string {
pub := loadPublicKey()
return base58.CheckEncode(btcutil.Hash160(pub), networks.Active.Base58P2PKHVersion)
}
func GetScript() ([]byte, error) {
pub := loadPublicKey()
return txscript.NewScriptBuilder().AddOp(txscript.OP_DUP).
AddOp(txscript.OP_HASH160).AddData(btcutil.Hash160(pub)).
AddOp(txscript.OP_EQUALVERIFY).AddOp(txscript.OP_CHECKSIG).Script()
}
func LoadPrivateKey(password string) ([]byte, error) {
filename := keyFile()
keyfile, err := os.ReadFile(filename)
if err != nil {
return []byte{}, err
}
if len(keyfile) != 105 {
return []byte{}, fmt.Errorf("Key length error for %s ", filename)
}
enckey := keyfile[33:]
// enckey is actually encrypted, get derived key from pass and salt
// first extract salt
salt := new([24]byte) // salt (also nonce for secretbox)
dk32 := new([32]byte) // derived key array
copy(salt[:], enckey[:24]) // first 24 bytes are scrypt salt/box nonce
dk, err := scrypt.Key([]byte(password), salt[:], 16384, 8, 1, 32) // derive key
if err != nil {
return []byte{}, err
}
copy(dk32[:], dk[:]) // copy into fixed size array
// nonce for secretbox is the same as scrypt salt. Seems fine. Really.
priv, worked := secretbox.Open(nil, enckey[24:], salt, dk32)
if !worked {
return []byte{}, fmt.Errorf("Decryption failed for %s ", filename)
}
return priv, nil
}
func TestPassword(password string) bool {
priv, err := LoadPrivateKey(password)
if err != nil {
return false
}
_, pub := btcec.PrivKeyFromBytes(btcec.S256(), priv)
return bytes.Equal(loadPublicKey(), pub.SerializeCompressed())
}