func (s *loaderServer) CreateWallet(ctx context.Context, req *pb.CreateWalletRequest) ( *pb.CreateWalletResponse, error) { defer func() { zero.Bytes(req.PrivatePassphrase) zero.Bytes(req.Seed) }() // Use an insecure public passphrase when the request's is empty. pubPassphrase := req.PublicPassphrase if len(pubPassphrase) == 0 { pubPassphrase = []byte(wallet.InsecurePubPassphrase) } wallet, err := s.loader.CreateNewWallet(pubPassphrase, req.PrivatePassphrase, req.Seed) if err != nil { return nil, translateError(err) } s.mu.Lock() if s.rpcClient != nil { wallet.SynchronizeRPC(s.rpcClient) } s.mu.Unlock() return &pb.CreateWalletResponse{}, nil }
func (s *walletServer) ImportPrivateKey(ctx context.Context, req *pb.ImportPrivateKeyRequest) ( *pb.ImportPrivateKeyResponse, error) { defer zero.Bytes(req.Passphrase) wif, err := btcutil.DecodeWIF(req.PrivateKeyWif) if err != nil { return nil, grpc.Errorf(codes.InvalidArgument, "Invalid WIF-encoded private key: %v", err) } lock := make(chan time.Time, 1) defer func() { lock <- time.Time{} // send matters, not the value }() err = s.wallet.Unlock(req.Passphrase, lock) if err != nil { return nil, translateError(err) } // At the moment, only the special-cased import account can be used to // import keys. if req.Account != waddrmgr.ImportedAddrAccount { return nil, grpc.Errorf(codes.InvalidArgument, "Only the imported account accepts private key imports") } _, err = s.wallet.ImportPrivateKey(wif, nil, req.Rescan) if err != nil { return nil, translateError(err) } return &pb.ImportPrivateKeyResponse{}, nil }
func (s *walletServer) ChangePassphrase(ctx context.Context, req *pb.ChangePassphraseRequest) ( *pb.ChangePassphraseResponse, error) { defer func() { zero.Bytes(req.OldPassphrase) zero.Bytes(req.NewPassphrase) }() err := s.wallet.Manager.ChangePassphrase(req.OldPassphrase, req.NewPassphrase, req.Key != pb.ChangePassphraseRequest_PUBLIC, &waddrmgr.DefaultScryptOptions) if err != nil { return nil, translateError(err) } return &pb.ChangePassphraseResponse{}, nil }
// lock zeroes the associated clear text private key. func (a *scriptAddress) lock() { // Zero and nil the clear text script associated with this address. a.scriptMutex.Lock() zero.Bytes(a.scriptCT) a.scriptCT = nil a.scriptMutex.Unlock() }
func (s *walletServer) NextAccount(ctx context.Context, req *pb.NextAccountRequest) ( *pb.NextAccountResponse, error) { defer zero.Bytes(req.Passphrase) if req.AccountName == "" { return nil, grpc.Errorf(codes.InvalidArgument, "account name may not be empty") } lock := make(chan time.Time, 1) defer func() { lock <- time.Time{} // send matters, not the value }() err := s.wallet.Unlock(req.Passphrase, lock) if err != nil { return nil, translateError(err) } account, err := s.wallet.NextAccount(req.AccountName) if err != nil { return nil, translateError(err) } return &pb.NextAccountResponse{AccountNumber: account}, nil }
// PrivKey returns the private key for the address. It can fail if the address // manager is watching-only or locked, or the address does not have any keys. // // This is part of the ManagedPubKeyAddress interface implementation. func (a *managedAddress) PrivKey() (*btcec.PrivateKey, error) { // No private keys are available for a watching-only address manager. if a.manager.watchingOnly { return nil, managerError(ErrWatchingOnly, errWatchingOnly, nil) } a.manager.mtx.Lock() defer a.manager.mtx.Unlock() // Account manager must be unlocked to decrypt the private key. if a.manager.locked { return nil, managerError(ErrLocked, errLocked, nil) } // Decrypt the key as needed. Also, make sure it's a copy since the // private key stored in memory can be cleared at any time. Otherwise // the returned private key could be invalidated from under the caller. privKeyCopy, err := a.unlock(a.manager.cryptoKeyPriv) if err != nil { return nil, err } privKey, _ := btcec.PrivKeyFromBytes(btcec.S256(), privKeyCopy) zero.Bytes(privKeyCopy) return privKey, nil }
// lock zeroes the associated clear text private key. func (a *managedAddress) lock() { // Zero and nil the clear text private key associated with this // address. a.privKeyMutex.Lock() zero.Bytes(a.privKeyCT) a.privKeyCT = nil a.privKeyMutex.Unlock() }
func (s *loaderServer) StartBtcdRpc(ctx context.Context, req *pb.StartBtcdRpcRequest) ( *pb.StartBtcdRpcResponse, error) { defer zero.Bytes(req.Password) defer s.mu.Unlock() s.mu.Lock() if s.rpcClient != nil { return nil, grpc.Errorf(codes.FailedPrecondition, "RPC client already created") } networkAddress, err := cfgutil.NormalizeAddress(req.NetworkAddress, s.activeNet.RPCClientPort) if err != nil { return nil, grpc.Errorf(codes.InvalidArgument, "Network address is ill-formed: %v", err) } // Error if the wallet is already syncing with the network. wallet, walletLoaded := s.loader.LoadedWallet() if walletLoaded && wallet.SynchronizingToNetwork() { return nil, grpc.Errorf(codes.FailedPrecondition, "wallet is loaded and already synchronizing") } rpcClient, err := chain.NewRPCClient(s.activeNet.Params, networkAddress, req.Username, string(req.Password), req.Certificate, len(req.Certificate) == 0, 1) if err != nil { return nil, translateError(err) } err = rpcClient.Start() if err != nil { if err == btcrpcclient.ErrInvalidAuth { return nil, grpc.Errorf(codes.InvalidArgument, "Invalid RPC credentials: %v", err) } else { return nil, grpc.Errorf(codes.NotFound, "Connection to RPC server failed: %v", err) } } s.rpcClient = rpcClient if walletLoaded { wallet.SynchronizeRPC(rpcClient) } return &pb.StartBtcdRpcResponse{}, nil }
// decryptExtendedKey uses Manager.Decrypt() to decrypt the encrypted byte slice and return // an extended (public or private) key representing it. // // This method must be called with the Pool's manager unlocked. func (p *Pool) decryptExtendedKey(keyType waddrmgr.CryptoKeyType, encrypted []byte) (*hdkeychain.ExtendedKey, error) { decrypted, err := p.manager.Decrypt(keyType, encrypted) if err != nil { str := fmt.Sprintf("cannot decrypt key %v", encrypted) return nil, newError(ErrCrypto, str, err) } result, err := hdkeychain.NewKeyFromString(string(decrypted)) zero.Bytes(decrypted) if err != nil { str := fmt.Sprintf("cannot get key from string %v", decrypted) return nil, newError(ErrKeyChain, str, err) } return result, nil }
// BUGS: // - InputIndexes request field is ignored. func (s *walletServer) SignTransaction(ctx context.Context, req *pb.SignTransactionRequest) ( *pb.SignTransactionResponse, error) { defer zero.Bytes(req.Passphrase) var tx wire.MsgTx err := tx.Deserialize(bytes.NewReader(req.SerializedTransaction)) if err != nil { return nil, grpc.Errorf(codes.InvalidArgument, "Bytes do not represent a valid raw transaction: %v", err) } lock := make(chan time.Time, 1) defer func() { lock <- time.Time{} // send matters, not the value }() err = s.wallet.Unlock(req.Passphrase, lock) if err != nil { return nil, translateError(err) } invalidSigs, err := s.wallet.SignTransaction(&tx, txscript.SigHashAll, nil, nil, nil) if err != nil { return nil, translateError(err) } invalidInputIndexes := make([]uint32, len(invalidSigs)) for i, e := range invalidSigs { invalidInputIndexes[i] = e.InputIndex } var serializedTransaction bytes.Buffer serializedTransaction.Grow(tx.SerializeSize()) err = tx.Serialize(&serializedTransaction) if err != nil { return nil, translateError(err) } resp := &pb.SignTransactionResponse{ Transaction: serializedTransaction.Bytes(), UnsignedInputIndexes: invalidInputIndexes, } return resp, nil }
// deriveKey fills out the Key field. func (sk *SecretKey) deriveKey(password *[]byte) error { key, err := scrypt.Key(*password, sk.Parameters.Salt[:], sk.Parameters.N, sk.Parameters.R, sk.Parameters.P, len(sk.Key)) if err != nil { return err } copy(sk.Key[:], key) zero.Bytes(key) // I'm not a fan of forced garbage collections, but scrypt allocates a // ton of memory and calling it back to back without a GC cycle in // between means you end up needing twice the amount of memory. For // example, if your scrypt parameters are such that you require 1GB and // you call it twice in a row, without this you end up allocating 2GB // since the first GB probably hasn't been released yet. debug.FreeOSMemory() return nil }