// TestIntegrationBlankEncryption probes the encryption process when the user // supplies a blank encryption key during the encryption process. func TestIntegrationBlankEncryption(t *testing.T) { // Create the wallet. wt, err := createBlankWalletTester("TestIntegrationBlankEncryption") if err != nil { t.Fatal(err) } defer wt.closeWt() // Encrypt the wallet using a blank key. seed, err := wt.wallet.Encrypt(crypto.TwofishKey{}) if err != nil { t.Error(err) } // Try unlocking the wallet using a blank key. err = wt.wallet.Unlock(crypto.TwofishKey{}) if err != modules.ErrBadEncryptionKey { t.Fatal(err) } // Try unlocking the wallet using the correct key. err = wt.wallet.Unlock(crypto.TwofishKey(crypto.HashObject(seed))) if err != nil { t.Fatal(err) } err = wt.wallet.Lock() if err != nil { t.Fatal(err) } postEncryptionTesting(wt.miner, wt.wallet, crypto.TwofishKey(crypto.HashObject(seed))) }
// encryptionKeys enumerates the possible encryption keys that can be derived // from an input string. func encryptionKeys(seedStr string) (validKeys []crypto.TwofishKey) { dicts := []mnemonics.DictionaryID{"english", "german", "japanese"} for _, dict := range dicts { seed, err := modules.StringToSeed(seedStr, dict) if err != nil { continue } validKeys = append(validKeys, crypto.TwofishKey(crypto.HashObject(seed))) } validKeys = append(validKeys, crypto.TwofishKey(crypto.HashObject(seedStr))) return validKeys }
// initEncryption checks that the provided encryption key is the valid // encryption key for the wallet. If encryption has not yet been established // for the wallet, an encryption key is created. func (w *Wallet) initEncryption(masterKey crypto.TwofishKey) (modules.Seed, error) { // Check if the wallet encryption key has already been set. if len(w.persist.EncryptionVerification) != 0 { return modules.Seed{}, errReencrypt } // Create a random seed and use it to generate the seed file for the // wallet. var seed modules.Seed _, err := rand.Read(seed[:]) if err != nil { return modules.Seed{}, err } // If the input key is blank, use the seed to create the master key. // Otherwise, use the input key. if masterKey == (crypto.TwofishKey{}) { masterKey = crypto.TwofishKey(crypto.HashObject(seed)) } err = w.createSeed(masterKey, seed) if err != nil { return modules.Seed{}, err } // Establish the encryption verification using the masterKey. After this // point, the wallet is encrypted. uk := uidEncryptionKey(masterKey, w.persist.UID) encryptionBase := make([]byte, encryptionVerificationLen) w.persist.EncryptionVerification, err = uk.EncryptBytes(encryptionBase) if err != nil { return modules.Seed{}, err } err = w.saveSettings() if err != nil { return modules.Seed{}, err } return seed, nil }
// walletInitHandler handles API calls to /wallet/init. func (api *API) walletInitHandler(w http.ResponseWriter, req *http.Request, _ httprouter.Params) { var encryptionKey crypto.TwofishKey if req.FormValue("encryptionpassword") != "" { encryptionKey = crypto.TwofishKey(crypto.HashObject(req.FormValue("encryptionpassword"))) } seed, err := api.wallet.Encrypt(encryptionKey) if err != nil { WriteError(w, Error{"error when calling /wallet/init: " + err.Error()}, http.StatusBadRequest) return } dictID := mnemonics.DictionaryID(req.FormValue("dictionary")) if dictID == "" { dictID = "english" } seedStr, err := modules.SeedToString(seed, dictID) if err != nil { WriteError(w, Error{"error when calling /wallet/init: " + err.Error()}, http.StatusBadRequest) return } WriteJSON(w, WalletInitPOST{ PrimarySeed: seedStr, }) }
// walletEncryptHandlerPOST handles a POST call to /wallet/encrypt. func (srv *Server) walletEncryptHandlerPOST(w http.ResponseWriter, req *http.Request) { var encryptionKey crypto.TwofishKey if req.FormValue("encryptionpassword") != "" { encryptionKey = crypto.TwofishKey(crypto.HashObject(req.FormValue("encryptionpassword"))) } seed, err := srv.wallet.Encrypt(encryptionKey) if err != nil { writeError(w, "error when calling /wallet/encrypt: "+err.Error(), http.StatusBadRequest) return } dictID := mnemonics.DictionaryID(req.FormValue("dictionary")) if dictID == "" { dictID = "english" } seedStr, err := modules.SeedToString(seed, dictID) if err != nil { writeError(w, "error when calling /wallet/encrypt: "+err.Error(), http.StatusBadRequest) return } writeJSON(w, WalletEncryptPOST{ PrimarySeed: seedStr, }) }
// uidEncryptionKey creates an encryption key that is used to decrypt a // specific key file. func uidEncryptionKey(masterKey crypto.TwofishKey, uid UniqueID) crypto.TwofishKey { return crypto.TwofishKey(crypto.HashAll(masterKey, uid)) }
// deriveKey derives the key used to encrypt and decrypt a specific file piece. func deriveKey(masterKey crypto.TwofishKey, chunkIndex, pieceIndex uint64) crypto.TwofishKey { return crypto.TwofishKey(crypto.HashAll(masterKey, chunkIndex, pieceIndex)) }
// TestLoadSeed checks that a seed can be successfully recovered from a wallet, // and then remain available on subsequent loads of the wallet. func TestLoadSeed(t *testing.T) { if testing.Short() { t.SkipNow() } wt, err := createWalletTester("TestLoadSeed") if err != nil { t.Fatal(err) } seed, _, err := wt.wallet.PrimarySeed() if err != nil { t.Fatal(err) } allSeeds, err := wt.wallet.AllSeeds() if err != nil { t.Fatal(err) } if len(allSeeds) != 1 { t.Error("AllSeeds should be returning the primary seed.") } if allSeeds[0] != seed { t.Error("AllSeeds returned the wrong seed") } dir := filepath.Join(build.TempDir(modules.WalletDir, "TestLoadSeed - 0"), modules.WalletDir) w, err := New(wt.cs, wt.tpool, dir) if err != nil { t.Fatal(err) } newSeed, err := w.Encrypt(crypto.TwofishKey{}) if err != nil { t.Fatal(err) } err = w.Unlock(crypto.TwofishKey(crypto.HashObject(newSeed))) if err != nil { t.Fatal(err) } // Balance of wallet should be 0. siacoinBal, _, _ := w.ConfirmedBalance() if siacoinBal.Cmp(types.NewCurrency64(0)) != 0 { t.Error("fresh wallet should not have a balance") } err = w.LoadSeed(crypto.TwofishKey(crypto.HashObject(newSeed)), seed) if err != nil { t.Fatal(err) } allSeeds, err = w.AllSeeds() if err != nil { t.Fatal(err) } if len(allSeeds) != 2 { t.Error("AllSeeds should be returning the primary seed with the recovery seed.") } if !bytes.Equal(allSeeds[0][:], newSeed[:]) { t.Error("AllSeeds returned the wrong seed") } if !bytes.Equal(allSeeds[1][:], seed[:]) { t.Error("AllSeeds returned the wrong seed") } // Rather than worry about a rescan, which isn't implemented and has // synchronization difficulties, just load a new wallet from the same // settings file - the same effect is achieved without the difficulties. w2, err := New(wt.cs, wt.tpool, dir) if err != nil { t.Fatal(err) } err = w2.Unlock(crypto.TwofishKey(crypto.HashObject(newSeed))) if err != nil { t.Fatal(err) } siacoinBal2, _, _ := w2.ConfirmedBalance() if siacoinBal2.Cmp(types.NewCurrency64(0)) <= 0 { t.Error("wallet failed to load a seed with money in it") } allSeeds, err = w2.AllSeeds() if err != nil { t.Fatal(err) } if len(allSeeds) != 2 { t.Error("AllSeeds should be returning the primary seed with the recovery seed.") } if !bytes.Equal(allSeeds[0][:], newSeed[:]) { t.Error("AllSeeds returned the wrong seed") } if !bytes.Equal(allSeeds[1][:], seed[:]) { t.Error("AllSeeds returned the wrong seed") } }
// TestPrimarySeed checks that the correct seed is returned when calling // PrimarySeed. func TestPrimarySeed(t *testing.T) { if testing.Short() { t.SkipNow() } // Start with a blank wallet tester. wt, err := createBlankWalletTester("TestPrimarySeed") if err != nil { t.Fatal(err) } // Create a seed and unlock the wallet. seed, err := wt.wallet.Encrypt(crypto.TwofishKey{}) if err != nil { t.Fatal(err) } err = wt.wallet.Unlock(crypto.TwofishKey(crypto.HashObject(seed))) if err != nil { t.Fatal(err) } // Try getting an address, see that the seed advances correctly. primarySeed, progress, err := wt.wallet.PrimarySeed() if err != nil { t.Fatal(err) } if !bytes.Equal(primarySeed[:], seed[:]) { t.Error("PrimarySeed is returning a value inconsitent with the seed returned by Encrypt") } if progress != 0 { t.Error("primary seed is returning the wrong progress") } _, err = wt.wallet.NextAddress() if err != nil { t.Fatal(err) } _, progress, err = wt.wallet.PrimarySeed() if err != nil { t.Fatal(err) } if progress != 1 { t.Error("primary seed is returning the wrong progress") } // Lock then unlock the wallet and check the responses. err = wt.wallet.Lock() if err != nil { t.Fatal(err) } _, _, err = wt.wallet.PrimarySeed() if err != modules.ErrLockedWallet { t.Error("unexpected err:", err) } err = wt.wallet.Unlock(crypto.TwofishKey(crypto.HashObject(seed))) if err != nil { t.Fatal(err) } primarySeed, progress, err = wt.wallet.PrimarySeed() if err != nil { t.Fatal(err) } if !bytes.Equal(primarySeed[:], seed[:]) { t.Error("PrimarySeed is returning a value inconsitent with the seed returned by Encrypt") } if progress != 1 { t.Error("progress reporting an unexpected value") } }
// TestPrimarySeed checks that the correct seed is returned when calling // PrimarySeed. func TestPrimarySeed(t *testing.T) { if testing.Short() { t.SkipNow() } // Create a wallet and fetch the seed at startup. dir := build.TempDir(modules.WalletDir, "TestPrimarySeed") g, err := gateway.New(":0", filepath.Join(dir, modules.GatewayDir)) if err != nil { t.Fatal(err) } cs, err := consensus.New(g, filepath.Join(dir, modules.ConsensusDir)) if err != nil { t.Fatal(err) } tp, err := transactionpool.New(cs, g) if err != nil { t.Fatal(err) } w, err := New(cs, tp, filepath.Join(dir, modules.WalletDir)) if err != nil { t.Fatal(err) } seed, err := w.Encrypt(crypto.TwofishKey{}) if err != nil { t.Fatal(err) } err = w.Unlock(crypto.TwofishKey(crypto.HashObject(seed))) if err != nil { t.Fatal(err) } primarySeed, progress, err := w.PrimarySeed() if err != nil { t.Fatal(err) } if !bytes.Equal(primarySeed[:], seed[:]) { t.Error("PrimarySeed is returning a value inconsitent with the seed returned by Encrypt") } if progress != 0 { t.Error("primary seed is returning the wrong progress") } _, err = w.NextAddress() if err != nil { t.Fatal(err) } _, progress, err = w.PrimarySeed() if err != nil { t.Fatal(err) } if progress != 1 { t.Error("primary seed is returning the wrong progress") } // Lock then unlock the wallet and check the responses. err = w.Lock() if err != nil { t.Fatal(err) } _, _, err = w.PrimarySeed() if err != modules.ErrLockedWallet { t.Error("unexpected err:", err) } err = w.Unlock(crypto.TwofishKey(crypto.HashObject(seed))) if err != nil { t.Fatal(err) } primarySeed, progress, err = w.PrimarySeed() if err != nil { t.Fatal(err) } if !bytes.Equal(primarySeed[:], seed[:]) { t.Error("PrimarySeed is returning a value inconsitent with the seed returned by Encrypt") } if progress != 1 { t.Error("progress reporting an unexpected value") } }
// unlockKey creates a wallet unlocking key from the input master key. func unlockKey(masterKey crypto.TwofishKey) crypto.TwofishKey { return crypto.TwofishKey(crypto.HashAll(masterKey, unlockModifier)) }
// seedFileEncryptionKey creates an encryption key that is used to decrypt a // specific key file. func seedFileEncryptionKey(masterKey crypto.TwofishKey, sfuid SeedFileUID) crypto.TwofishKey { return crypto.TwofishKey(crypto.HashAll(masterKey, seedModifier, sfuid)) }