diff --git a/client/core/account.go b/client/core/account.go index 89ccc9de1a..0d76e31233 100644 --- a/client/core/account.go +++ b/client/core/account.go @@ -63,6 +63,7 @@ func (c *Core) AccountExport(pw []byte, host string) (*Account, error) { if err != nil { return nil, codedError(passwordErr, err) } + defer crypter.Close() host, err = addrHost(host) if err != nil { return nil, newError(addressParseErr, "error parsing address: %w", err) diff --git a/client/core/core.go b/client/core/core.go index 62fd6fc54f..21fcb6cb67 100644 --- a/client/core/core.go +++ b/client/core/core.go @@ -1440,6 +1440,7 @@ func (c *Core) encryptionKey(pw []byte) (encrypt.Crypter, error) { if err != nil { return nil, fmt.Errorf("outer key deserialization error: %w", err) } + defer outerCrypter.Close() innerKey, err := outerCrypter.Decrypt(creds.EncInnerKey) if err != nil { return nil, fmt.Errorf("inner key decryption error: %w", err) @@ -1711,6 +1712,7 @@ func (c *Core) CreateWallet(appPW, walletPW []byte, form *WalletForm) error { if err != nil { return err } + defer crypter.Close() walletDef, err := walletDefinition(assetID, form.Type) if err != nil { @@ -1821,6 +1823,7 @@ func (c *Core) createSeededWallet(assetID uint32, crypter encrypt.Crypter, form if err != nil { return nil, err } + defer encode.ClearBytes(seed) c.log.Infof("Initializing a built-in %s wallet", unbip(assetID)) if err = asset.CreateWallet(assetID, &asset.CreateWalletParams{ @@ -2052,6 +2055,7 @@ func (c *Core) OpenWallet(assetID uint32, appPW []byte) error { if err != nil { return err } + defer crypter.Close() wallet, err := c.connectedWallet(assetID) if err != nil { return fmt.Errorf("OpenWallet: wallet not found for %d -> %s: %w", assetID, unbip(assetID), err) @@ -2173,6 +2177,7 @@ func (c *Core) ReconfigureWallet(appPW, newWalletPW []byte, form *WalletForm) er if err != nil { return newError(authErr, "ReconfigureWallet password error: %w", err) } + defer crypter.Close() assetID := form.AssetID walletDef, err := walletDefinition(assetID, form.Type) if err != nil { @@ -2395,6 +2400,7 @@ func (c *Core) SetWalletPassword(appPW []byte, assetID uint32, newPW []byte) err if err != nil { return newError(authErr, "SetWalletPassword password error: %w", err) } + defer crypter.Close() // Check that the specified wallet exists. c.walletMtx.Lock() @@ -2685,6 +2691,7 @@ func (c *Core) DiscoverAccount(dexAddr string, appPW []byte, certI interface{}) if err != nil { return nil, false, codedError(passwordErr, err) } + defer crypter.Close() var ready bool dc, err := c.tempDexConnection(host, certI) @@ -2781,6 +2788,7 @@ func (c *Core) Register(form *RegisterForm) (*RegisterResult, error) { if err != nil { return nil, codedError(passwordErr, err) } + defer crypter.Close() if form.Addr == "" { return nil, newError(emptyHostErr, "no dex address specified") } @@ -3190,6 +3198,7 @@ func (c *Core) ExportSeed(pw []byte) ([]byte, error) { if err != nil { return nil, fmt.Errorf("ExportSeed password error: %w", err) } + defer crypter.Close() creds := c.creds() if creds == nil { @@ -3230,6 +3239,7 @@ func (c *Core) generateCredentials(pw, seed []byte) (encrypt.Crypter, *db.Primar } else if len(seed) != seedLen { return nil, nil, fmt.Errorf("invalid seed length %d. expected %d", len(seed), seedLen) } + defer encode.ClearBytes(seed) encSeed, err := innerCrypter.Encrypt(seed) if err != nil { @@ -3273,6 +3283,7 @@ func (c *Core) Login(pw []byte) (*LoginResult, error) { if err != nil { return nil, err } + defer crypter.Close() // Attempt to connect to and retrieve balance from all known wallets. It is // not an error if we can't connect, unless we need the wallet for active @@ -3886,6 +3897,7 @@ func (c *Core) Withdraw(pw []byte, assetID uint32, value uint64, address string) if err != nil { return nil, fmt.Errorf("Withdraw password error: %w", err) } + defer crypter.Close() if value == 0 { return nil, fmt.Errorf("cannot withdraw zero %s", unbip(assetID)) } @@ -4046,6 +4058,7 @@ func (c *Core) Trade(pw []byte, form *TradeForm) (*Order, error) { if err != nil { return nil, fmt.Errorf("Trade password error: %w", err) } + defer crypter.Close() dc, err := c.connectedDEX(form.Host) if err != nil { return nil, err diff --git a/client/core/types.go b/client/core/types.go index b9926aa309..80236c5d56 100644 --- a/client/core/types.go +++ b/client/core/types.go @@ -633,6 +633,7 @@ func (a *dexAccount) setupCryptoV2(creds *db.PrimaryCredentials, crypter encrypt if err != nil { return fmt.Errorf("seed decryption error: %w", err) } + defer encode.ClearBytes(seed) dexPkB := a.dexPubKey.SerializeCompressed() // And because I'm neurotic. diff --git a/client/rpcserver/handlers.go b/client/rpcserver/handlers.go index c51ff2f53c..148c1f2348 100644 --- a/client/rpcserver/handlers.go +++ b/client/rpcserver/handlers.go @@ -127,9 +127,7 @@ func handleInit(s *RPCServer, params *RawParams) *msgjson.ResponsePayload { } defer func() { appPass.Clear() - if len(seed) > 0 { - seed.Clear() - } + seed.Clear() }() if err := s.core.InitializeClient(appPass, seed); err != nil { errMsg := fmt.Sprintf("unable to initialize client: %v", err) @@ -206,9 +204,9 @@ func handleOpenWallet(s *RPCServer, params *RawParams) *msgjson.ResponsePayload if err != nil { return usage(openWalletRoute, err) } + defer form.appPass.Clear() err = s.core.OpenWallet(form.assetID, form.appPass) - form.appPass.Clear() // AppPass not needed after this, clear if err != nil { errMsg := fmt.Sprintf("error unlocking %s wallet: %v", dex.BipIDSymbol(form.assetID), err) @@ -609,6 +607,7 @@ func handleAppSeed(s *RPCServer, params *RawParams) *msgjson.ResponsePayload { } defer appPass.Clear() seed, err := s.core.ExportSeed(appPass) + defer encode.ClearBytes(seed) if err != nil { errMsg := fmt.Sprintf("unable to retrieve app seed: %v", err) resErr := msgjson.NewError(msgjson.RPCExportSeedError, errMsg) @@ -616,12 +615,6 @@ func handleAppSeed(s *RPCServer, params *RawParams) *msgjson.ResponsePayload { } // Zero seed and hex representation after use. seedHex := fmt.Sprintf("%x", seed[:]) - defer func() { - for i := range seed { - seed[i] = 0 - } - seedHex = "" - }() return createResponse(appSeedRoute, seedHex, nil) } diff --git a/client/webserver/api.go b/client/webserver/api.go index 2e56a8a9ea..30f39a226f 100644 --- a/client/webserver/api.go +++ b/client/webserver/api.go @@ -17,9 +17,12 @@ import ( "decred.org/dcrdex/dex/encode" ) +var zero = encode.ClearBytes + // apiDiscoverAccount is the handler for the '/discoveracct' API request. func (s *WebServer) apiDiscoverAccount(w http.ResponseWriter, r *http.Request) { form := new(registrationForm) + defer form.Password.Clear() if !readPost(w, r, form) { return } @@ -29,6 +32,7 @@ func (s *WebServer) apiDiscoverAccount(w http.ResponseWriter, r *http.Request) { s.writeAPIError(w, fmt.Errorf("password error: %w", err)) return } + defer zero(pass) exchangeInfo, paid, err := s.core.DiscoverAccount(form.Addr, pass, cert) if err != nil { s.writeAPIError(w, err) @@ -111,6 +115,7 @@ func (s *WebServer) apiRegister(w http.ResponseWriter, r *http.Request) { s.writeAPIError(w, fmt.Errorf("password error: %w", err)) return } + defer zero(pass) _, err = s.core.Register(&core.RegisterForm{ Addr: reg.Addr, Cert: []byte(reg.Cert), @@ -131,10 +136,8 @@ func (s *WebServer) apiRegister(w http.ResponseWriter, r *http.Request) { // apiNewWallet is the handler for the '/newwallet' API request. func (s *WebServer) apiNewWallet(w http.ResponseWriter, r *http.Request) { form := new(newWalletForm) - defer func() { - form.AppPW.Clear() - form.Pass.Clear() - }() + defer form.AppPW.Clear() + defer form.Pass.Clear() if !readPost(w, r, form) { return } @@ -148,6 +151,7 @@ func (s *WebServer) apiNewWallet(w http.ResponseWriter, r *http.Request) { s.writeAPIError(w, fmt.Errorf("password error: %w", err)) return } + defer zero(pass) // Wallet does not exist yet. Try to create it. err = s.core.CreateWallet(pass, form.Pass, &core.WalletForm{ AssetID: form.AssetID, @@ -206,6 +210,7 @@ func (s *WebServer) apiOpenWallet(w http.ResponseWriter, r *http.Request) { s.writeAPIError(w, fmt.Errorf("password error: %w", err)) return } + defer zero(pass) err = s.core.OpenWallet(form.AssetID, pass) if err != nil { s.writeAPIError(w, fmt.Errorf("error unlocking %s wallet: %w", unbip(form.AssetID), err)) @@ -275,6 +280,7 @@ func (s *WebServer) apiTrade(w http.ResponseWriter, r *http.Request) { s.writeAPIError(w, fmt.Errorf("password error: %w", err)) return } + defer zero(pass) ord, err := s.core.Trade(pass, form.Order) if err != nil { s.writeAPIError(w, fmt.Errorf("error placing order: %w", err)) @@ -304,6 +310,7 @@ func (s *WebServer) apiAccountExport(w http.ResponseWriter, r *http.Request) { s.writeAPIError(w, fmt.Errorf("password error: %w", err)) return } + defer zero(pass) account, err := s.core.AccountExport(pass, form.Host) if err != nil { s.writeAPIError(w, fmt.Errorf("error exporting account: %w", err)) @@ -325,6 +332,7 @@ func (s *WebServer) apiExportSeed(w http.ResponseWriter, r *http.Request) { form := &struct { Pass encode.PassBytes `json:"pass"` }{} + defer form.Pass.Clear() if !readPost(w, r, form) { return } @@ -334,6 +342,7 @@ func (s *WebServer) apiExportSeed(w http.ResponseWriter, r *http.Request) { s.writeAPIError(w, fmt.Errorf("error exporting seed: %w", err)) return } + defer zero(seed) writeJSON(w, &struct { OK bool `json:"ok"` Seed dex.Bytes `json:"seed"` @@ -356,6 +365,7 @@ func (s *WebServer) apiAccountImport(w http.ResponseWriter, r *http.Request) { s.writeAPIError(w, fmt.Errorf("password error: %w", err)) return } + defer zero(pass) err = s.core.AccountImport(pass, form.Account) if err != nil { s.writeAPIError(w, fmt.Errorf("error importing account: %w", err)) @@ -375,6 +385,7 @@ func (s *WebServer) apiAccountDisable(w http.ResponseWriter, r *http.Request) { // Disable account. err := s.core.AccountDisable(form.Pass, form.Host) + zero(form.Pass) if err != nil { s.writeAPIError(w, fmt.Errorf("error disabling account: %w", err)) return @@ -395,6 +406,7 @@ func (s *WebServer) apiCancel(w http.ResponseWriter, r *http.Request) { s.writeAPIError(w, fmt.Errorf("password error: %w", err)) return } + defer zero(pass) err = s.core.Cancel(pass, form.OrderID) if err != nil { s.writeAPIError(w, fmt.Errorf("error cancelling order %s: %w", form.OrderID, err)) @@ -424,6 +436,7 @@ func (s *WebServer) apiCloseWallet(w http.ResponseWriter, r *http.Request) { func (s *WebServer) apiInit(w http.ResponseWriter, r *http.Request) { init := new(initForm) defer init.Pass.Clear() + defer zero(init.Seed) if !readPost(w, r, init) { return } @@ -646,6 +659,7 @@ func (s *WebServer) apiChangeAppPass(w http.ResponseWriter, r *http.Request) { log.Errorf("unable to cache password: %w", err) clearCookie(pwKeyCK, w) } else { + zero(key) setCookie(pwKeyCK, hex.EncodeToString(key), w) } } @@ -675,6 +689,7 @@ func (s *WebServer) apiReconfig(w http.ResponseWriter, r *http.Request) { s.writeAPIError(w, fmt.Errorf("password error: %w", err)) return } + defer zero(pass) // Update wallet settings. err = s.core.ReconfigureWallet(pass, form.NewWalletPW, &core.WalletForm{ AssetID: form.AssetID, @@ -790,9 +805,11 @@ func (s *WebServer) apiPreOrder(w http.ResponseWriter, r *http.Request) { writeJSON(w, resp, s.indent) } -// apiActuallyLogin logs the user in. +// apiActuallyLogin logs the user in. login form private data is expected to be +// cleared by the caller. func (s *WebServer) actuallyLogin(w http.ResponseWriter, r *http.Request, login *loginForm) { pass, err := s.resolvePass(login.Pass, r) + defer zero(pass) if err != nil { s.writeAPIError(w, fmt.Errorf("password error: %w", err)) return @@ -814,6 +831,7 @@ func (s *WebServer) actuallyLogin(w http.ResponseWriter, r *http.Request, login return } setCookie(pwKeyCK, hex.EncodeToString(key), w) + zero(key) } else { // If dexc was shutdown and restarted, the old pw key cookie might // need to be cleared. diff --git a/client/webserver/webserver.go b/client/webserver/webserver.go index 2e5d78638e..5e680c9745 100644 --- a/client/webserver/webserver.go +++ b/client/webserver/webserver.go @@ -481,6 +481,7 @@ func (s *WebServer) authorize() string { b := make([]byte, 32) rand.Read(b) token := hex.EncodeToString(b) + zero(b) s.authMtx.Lock() s.authTokens[token] = true s.authMtx.Unlock() @@ -589,6 +590,7 @@ func (s *WebServer) getCachedPasswordUsingRequest(r *http.Request) ([]byte, erro func (s *WebServer) cacheAppPassword(appPW []byte, authToken string) ([]byte, error) { key := encode.RandomBytes(16) crypter := encrypt.NewCrypter(key) + defer crypter.Close() encryptedPass, err := crypter.Encrypt(appPW) if err != nil { return nil, fmt.Errorf("error encrypting password: %v", err)