github.com/avahowell/sia@v0.5.1-beta.0.20160524050156-83dcc3d37c94/modules/wallet/unseeded.go (about) 1 package wallet 2 3 import ( 4 "bytes" 5 "crypto/rand" 6 "errors" 7 "path/filepath" 8 9 "github.com/NebulousLabs/Sia/crypto" 10 "github.com/NebulousLabs/Sia/encoding" 11 "github.com/NebulousLabs/Sia/modules" 12 "github.com/NebulousLabs/Sia/persist" 13 "github.com/NebulousLabs/Sia/types" 14 ) 15 16 const ( 17 // The header for all siag files. Do not change. Because siag was created 18 // early in development, compatibility with siag requires manually handling 19 // the headers and version instead of using the persist package. 20 SiagFileHeader = "siag" 21 SiagFileExtension = ".siakey" 22 SiagFileVersion = "1.0" 23 ) 24 25 var ( 26 ErrInconsistentKeys = errors.New("keyfiles provided that are for different addresses") 27 ErrInsufficientKeys = errors.New("not enough keys provided to spend the siafunds") 28 ErrNoKeyfile = errors.New("no keyfile has been presented") 29 ErrUnknownHeader = errors.New("file contains the wrong header") 30 ErrUnknownVersion = errors.New("file has an unknown version number") 31 32 errAllDuplicates = errors.New("old wallet has no new seeds") 33 errDuplicateSpendableKey = errors.New("key has already been loaded into the wallet") 34 ) 35 36 // A SiagKeyPair is the struct representation of the bytes that get saved to 37 // disk by siag when a new keyfile is created. 38 type SiagKeyPair struct { 39 Header string 40 Version string 41 Index int // should be uint64 - too late now 42 SecretKey crypto.SecretKey 43 UnlockConditions types.UnlockConditions 44 } 45 46 // SavedKey033x is the persist structure that was used to save and load private 47 // keys in versions v0.3.3.x for siad. 48 type SavedKey033x struct { 49 SecretKey crypto.SecretKey 50 UnlockConditions types.UnlockConditions 51 Visible bool 52 } 53 54 // initUnseededKeys loads all of the unseeded keys into the wallet after the 55 // wallet gets unlocked. 56 func (w *Wallet) initUnseededKeys(masterKey crypto.TwofishKey) error { 57 for _, uk := range w.persist.UnseededKeys { 58 // Verify that the decryption key is correct. 59 encKey := uidEncryptionKey(masterKey, uk.UID) 60 expectedDecryptedVerification := make([]byte, crypto.EntropySize) 61 decryptedVerification, err := encKey.DecryptBytes(uk.EncryptionVerification) 62 if err != nil { 63 return err 64 } 65 if !bytes.Equal(expectedDecryptedVerification, decryptedVerification) { 66 return modules.ErrBadEncryptionKey 67 } 68 69 // Decrypt the spendable key and add it to the wallet. 70 encodedKey, err := encKey.DecryptBytes(uk.SpendableKey) 71 if err != nil { 72 return err 73 } 74 var sk spendableKey 75 err = encoding.Unmarshal(encodedKey, &sk) 76 if err != nil { 77 return err 78 } 79 w.keys[sk.UnlockConditions.UnlockHash()] = sk 80 } 81 return nil 82 } 83 84 // loadSpendableKey loads a spendable key into the wallet's persist structure. 85 func (w *Wallet) loadSpendableKey(masterKey crypto.TwofishKey, sk spendableKey) error { 86 // Duplication is detected by looking at the set of unlock conditions. If 87 // the wallet is locked, correct deduplication is uncertain. 88 if !w.unlocked { 89 return modules.ErrLockedWallet 90 } 91 92 // Check for duplicates. 93 _, exists := w.keys[sk.UnlockConditions.UnlockHash()] 94 if exists { 95 return errDuplicateSpendableKey 96 } 97 98 // TODO: Check that the key is actually spendable. 99 100 // Create a UID and encryption verification. 101 var skf SpendableKeyFile 102 _, err := rand.Read(skf.UID[:]) 103 if err != nil { 104 return err 105 } 106 encryptionKey := uidEncryptionKey(masterKey, skf.UID) 107 plaintextVerification := make([]byte, encryptionVerificationLen) 108 skf.EncryptionVerification, err = encryptionKey.EncryptBytes(plaintextVerification) 109 if err != nil { 110 return err 111 } 112 113 // Encrypt and save the key. 114 skf.SpendableKey, err = encryptionKey.EncryptBytes(encoding.Marshal(sk)) 115 if err != nil { 116 return err 117 } 118 w.persist.UnseededKeys = append(w.persist.UnseededKeys, skf) 119 // w.keys[sk.UnlockConditions.UnlockHash()] = sk -> aids with duplicate 120 // detection, but causes db inconsistency. Rescanning is probably the 121 // solution. 122 return nil 123 } 124 125 // loadSiagKeys loads a set of siag keyfiles into the wallet, so that the 126 // wallet may spend the siafunds. 127 func (w *Wallet) loadSiagKeys(masterKey crypto.TwofishKey, keyfiles []string) error { 128 // Load the keyfiles from disk. 129 if len(keyfiles) < 1 { 130 return ErrNoKeyfile 131 } 132 skps := make([]SiagKeyPair, len(keyfiles)) 133 for i, keyfile := range keyfiles { 134 err := encoding.ReadFile(keyfile, &skps[i]) 135 if err != nil { 136 return err 137 } 138 139 if skps[i].Header != SiagFileHeader { 140 return ErrUnknownHeader 141 } 142 if skps[i].Version != SiagFileVersion { 143 return ErrUnknownVersion 144 } 145 } 146 147 // Check that all of the loaded files have the same address, and that there 148 // are enough to create the transaction. 149 baseUnlockHash := skps[0].UnlockConditions.UnlockHash() 150 for _, skp := range skps { 151 if skp.UnlockConditions.UnlockHash() != baseUnlockHash { 152 return ErrInconsistentKeys 153 } 154 } 155 if uint64(len(skps)) < skps[0].UnlockConditions.SignaturesRequired { 156 return ErrInsufficientKeys 157 } 158 // Drop all unneeded keys. 159 skps = skps[0:skps[0].UnlockConditions.SignaturesRequired] 160 161 // Merge the keys into a single spendableKey and save it to the wallet. 162 var sk spendableKey 163 sk.UnlockConditions = skps[0].UnlockConditions 164 for _, skp := range skps { 165 sk.SecretKeys = append(sk.SecretKeys, skp.SecretKey) 166 } 167 err := w.loadSpendableKey(masterKey, sk) 168 if err != nil { 169 return err 170 } 171 err = w.saveSettingsSync() 172 if err != nil { 173 return err 174 } 175 return w.createBackup(filepath.Join(w.persistDir, "Sia Wallet Encrypted Backup - "+persist.RandomSuffix()+settingsFileSuffix)) 176 } 177 178 // LoadSiagKeys loads a set of siag-generated keys into the wallet. 179 func (w *Wallet) LoadSiagKeys(masterKey crypto.TwofishKey, keyfiles []string) error { 180 w.mu.Lock() 181 defer w.mu.Unlock() 182 err := w.checkMasterKey(masterKey) 183 if err != nil { 184 return err 185 } 186 return w.loadSiagKeys(masterKey, keyfiles) 187 } 188 189 // Load033xWallet loads a v0.3.3.x wallet as an unseeded key, such that the 190 // funds become spendable to the current wallet. 191 func (w *Wallet) Load033xWallet(masterKey crypto.TwofishKey, filepath033x string) error { 192 w.mu.Lock() 193 defer w.mu.Unlock() 194 err := w.checkMasterKey(masterKey) 195 if err != nil { 196 return err 197 } 198 199 var savedKeys []SavedKey033x 200 err = encoding.ReadFile(filepath033x, &savedKeys) 201 if err != nil { 202 return err 203 } 204 var seedsLoaded int 205 for _, savedKey := range savedKeys { 206 spendKey := spendableKey{ 207 UnlockConditions: savedKey.UnlockConditions, 208 SecretKeys: []crypto.SecretKey{savedKey.SecretKey}, 209 } 210 err = w.loadSpendableKey(masterKey, spendKey) 211 if err != nil && err != errDuplicateSpendableKey { 212 return err 213 } 214 if err == nil { 215 seedsLoaded++ 216 } 217 } 218 err = w.saveSettingsSync() 219 if err != nil { 220 return err 221 } 222 if seedsLoaded == 0 { 223 return errAllDuplicates 224 } 225 return w.createBackup(filepath.Join(w.persistDir, "Sia Wallet Encrypted Backup - "+persist.RandomSuffix()+settingsFileSuffix)) 226 }