1// SPDX-License-Identifier: ISC 2// Copyright (c) 2014-2020 Bitmark Inc. 3// Use of this source code is governed by an ISC 4// license that can be found in the LICENSE file. 5 6package configuration 7 8import ( 9 "encoding/json" 10 "os" 11 "path/filepath" 12 13 "github.com/bitmark-inc/bitmarkd/account" 14 "github.com/bitmark-inc/bitmarkd/fault" 15) 16 17// Configuration - configuration file data format 18type Configuration struct { 19 DefaultIdentity string `json:"default_identity"` 20 TestNet bool `json:"testnet"` 21 Connections []string `json:"connections"` 22 Identities map[string]Identity `json:"identities"` 23} 24 25// Identity - mix of plain and encrypted data 26type Identity struct { 27 Description string `json:"description"` 28 Account string `json:"account"` 29 Data string `json:"data"` 30 Salt string `json:"salt"` 31} 32 33// Load - read the configuration 34func Load(filename string) (*Configuration, error) { 35 36 options := &Configuration{} 37 38 err := readConfiguration(filename, options) 39 if nil != err { 40 return nil, err 41 } 42 return options, nil 43} 44 45// generic JSON decoder 46func readConfiguration(filename string, options interface{}) error { 47 48 filename, err := filepath.Abs(filepath.Clean(filename)) 49 if nil != err { 50 return err 51 } 52 53 f, err := os.Open(filename) 54 if nil != err { 55 return err 56 } 57 defer f.Close() 58 59 dec := json.NewDecoder(f) 60 err = dec.Decode(options) 61 if nil != err { 62 return err 63 } 64 65 return nil 66} 67 68// Identity - find identity for a given name 69func (config *Configuration) Identity(name string) (*Identity, error) { 70 71 // account names cannot be identities to prevent confusion 72 _, err := account.AccountFromBase58(name) 73 if nil == err { 74 return nil, fault.InvalidIdentityName 75 } 76 77 id, ok := config.Identities[name] 78 if !ok { 79 return nil, fault.IdentityNameNotFound 80 } 81 82 return &id, nil 83} 84 85// Account - find identity for a given name and convert to an account 86func (config *Configuration) Account(name string) (*account.Account, error) { 87 // check if valid account in Base58 first 88 // to prevent identifiers masquerading as accounts 89 acc, err := account.AccountFromBase58(name) 90 if nil == err { 91 return acc, nil 92 } 93 94 // otherwise lookup as an identifier 95 id, err := config.Identity(name) 96 if nil != err { 97 return nil, err 98 } 99 100 acc, err = account.AccountFromBase58(id.Account) 101 102 return acc, err 103} 104 105// Private - find identity decrypt all data for a given name 106func (config *Configuration) Private(password string, name string) (*Private, error) { 107 id, err := config.Identity(name) 108 if nil != err { 109 return nil, err 110 } 111 112 return decryptIdentity(password, id) 113} 114 115// AddIdentity - store encrypted identity 116func (config *Configuration) AddIdentity(name string, description string, seed string, password string) error { 117 118 if _, ok := config.Identities[name]; ok { 119 return fault.IdentityNameAlreadyExists 120 } 121 122 salt, secretKey, err := hashPassword(password) 123 if nil != err { 124 return err 125 } 126 127 encrypted, err := encryptData(seed, secretKey) 128 if nil != err { 129 return err 130 } 131 132 private, err := account.PrivateKeyFromBase58Seed(seed) 133 if nil != err { 134 return err 135 } 136 137 config.Identities[name] = Identity{ 138 Description: description, 139 Account: private.Account().String(), 140 Data: encrypted, 141 Salt: salt.String(), 142 } 143 144 return nil 145} 146 147// AddReceiveOnlyIdentity - store public-only identity 148func (config *Configuration) AddReceiveOnlyIdentity(name string, description string, acc string) error { 149 150 if _, ok := config.Identities[name]; ok { 151 return fault.IdentityNameAlreadyExists 152 } 153 154 _, err := account.AccountFromBase58(acc) 155 if nil != err { 156 return err 157 } 158 159 config.Identities[name] = Identity{ 160 Description: description, 161 Account: acc, 162 Data: "", 163 Salt: "", 164 } 165 166 return nil 167} 168