-
Notifications
You must be signed in to change notification settings - Fork 2
/
account.go
122 lines (98 loc) · 2.78 KB
/
account.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
// Package accounts provides types for working with libonomy blockchain accounts
package accounts
import (
"crypto/aes"
"encoding/hex"
"errors"
"github.com/libonomy/wallet-cli/os/crypto"
"github.com/libonomy/wallet-cli/os/log"
"github.com/libonomy/wallet-cli/os/p2p/config"
)
// Registry maintains a list of known locked and unlocked accounts.
type Registry struct {
All map[string]*Account
Unlocked map[string]*Account
}
// Account is an end user blockchain account
type Account struct {
PrivKey crypto.PrivateKey
PubKey crypto.PublicKey
cryptoData CryptoData
kdParams crypto.KDParams
NetworkID int8
}
var (
// Accounts Registry app singleton
Accounts *Registry
)
func init() {
Accounts = &Registry{
All: make(map[string]*Account),
Unlocked: make(map[string]*Account),
}
}
// NewAccount Creates a new account using the provided passphrase.
// Clients should persist newly created accounts - without this the account only lasts for one app session.
func NewAccount(passphrase string) (*Account, error) {
// account crypto data
priv, pub, err := crypto.GenerateKeyPair()
if err != nil {
return nil, err
}
// derive key from passphrase
kdfParams := crypto.DefaultCypherParams
// add new salt to params
saltData, err := crypto.GetRandomBytes(kdfParams.SaltLen)
if err != nil {
return nil, errors.New("failed to generate random salt")
}
kdfParams.Salt = hex.EncodeToString(saltData)
dk, err := crypto.DeriveKeyFromPassword(passphrase, kdfParams)
if err != nil {
return nil, err
}
// extract 16 bytes aes-128-ctr key from the derived key
aesKey := dk[:16]
// data to encrypt
privKeyBytes := priv.Bytes()
// compute nonce
nonce, err := crypto.GetRandomBytes(aes.BlockSize)
if err != nil {
return nil, err
}
// aes encrypt data
cipherText, err := crypto.AesCTRXOR(aesKey, privKeyBytes, nonce)
if err != nil {
log.Error("Failed to encrypt private key", err)
return nil, err
}
// use last 16 bytes from derived key and cipher text to create a mac
mac := crypto.Sha256(dk[16:32], cipherText)
// store aes cipher data
cryptoData := CryptoData{
Cipher: "AES-128-CTR", // 16 bytes key
CipherText: hex.EncodeToString(cipherText),
CipherIv: hex.EncodeToString(nonce),
Mac: hex.EncodeToString(mac),
}
// store kd data
kdParams := crypto.KDParams{
N: kdfParams.N,
R: kdfParams.R,
P: kdfParams.P,
SaltLen: kdfParams.SaltLen,
DKLen: kdfParams.DKLen,
Salt: kdfParams.Salt,
}
NetworkID := config.ConfigValues.NetworkID
// save all data in newly created account obj
acct := &Account{priv,
pub,
cryptoData,
kdParams,
NetworkID}
Accounts.All[acct.String()] = acct
// newly created account are unlocked in the current app session
Accounts.Unlocked[acct.String()] = acct
return acct, nil
}