code.vegaprotocol.io/vega@v0.79.0/cmd/vegawallet/commands/key_isolate.go (about) 1 // Copyright (C) 2023 Gobalsky Labs Limited 2 // 3 // This program is free software: you can redistribute it and/or modify 4 // it under the terms of the GNU Affero General Public License as 5 // published by the Free Software Foundation, either version 3 of the 6 // License, or (at your option) any later version. 7 // 8 // This program is distributed in the hope that it will be useful, 9 // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 // GNU Affero General Public License for more details. 12 // 13 // You should have received a copy of the GNU Affero General Public License 14 // along with this program. If not, see <http://www.gnu.org/licenses/>. 15 16 package cmd 17 18 import ( 19 "context" 20 "errors" 21 "fmt" 22 "io" 23 24 "code.vegaprotocol.io/vega/cmd/vegawallet/commands/cli" 25 "code.vegaprotocol.io/vega/cmd/vegawallet/commands/flags" 26 "code.vegaprotocol.io/vega/cmd/vegawallet/commands/printer" 27 "code.vegaprotocol.io/vega/wallet/api" 28 "code.vegaprotocol.io/vega/wallet/wallets" 29 30 "github.com/spf13/cobra" 31 ) 32 33 var ( 34 isolateKeyLong = cli.LongDesc(` 35 Extract the specified key pair into an isolated wallet. 36 37 An isolated wallet is a wallet that contains a single key pair and that 38 has been stripped from its cryptographic node. 39 40 Removing the cryptographic node from the wallet minimizes the impact of a 41 stolen wallet, as it makes it impossible to retrieve or generate keys out 42 of it. 43 44 This creates a wallet that is only able to sign and verify transactions. 45 46 This adds an extra layer of security. 47 `) 48 49 isolateKeyExample = cli.Examples(` 50 # Isolate a key pair 51 {{.Software}} key isolate --wallet WALLET --pubkey PUBKEY 52 `) 53 54 isolatedWalletPassphraseOptions = flags.PassphraseOptions{ 55 Name: "isolated wallet", 56 Description: "When isolating the wallet, you can choose a brand-new passphrase, or reuse the original one.", 57 } 58 ) 59 60 type IsolateKeyHandler func(api.AdminIsolateKeyParams, string) (isolateKeyResult, error) 61 62 type isolateKeyResult struct { 63 Wallet string `json:"wallet"` 64 FilePath string `json:"filePath"` 65 } 66 67 func NewCmdIsolateKey(w io.Writer, rf *RootFlags) *cobra.Command { 68 h := func(params api.AdminIsolateKeyParams, passphrase string) (isolateKeyResult, error) { 69 ctx := context.Background() 70 71 walletStore, err := wallets.InitialiseStore(rf.Home, false) 72 if err != nil { 73 return isolateKeyResult{}, fmt.Errorf("could not initialise wallets store: %w", err) 74 } 75 defer walletStore.Close() 76 77 if _, errDetails := api.NewAdminUnlockWallet(walletStore).Handle(ctx, api.AdminUnlockWalletParams{ 78 Wallet: params.Wallet, 79 Passphrase: passphrase, 80 }); errDetails != nil { 81 return isolateKeyResult{}, errors.New(errDetails.Data) 82 } 83 84 rawResult, errDetails := api.NewAdminIsolateKey(walletStore).Handle(ctx, params) 85 if errDetails != nil { 86 return isolateKeyResult{}, errors.New(errDetails.Data) 87 } 88 result := rawResult.(api.AdminIsolateKeyResult) 89 return isolateKeyResult{ 90 Wallet: result.Wallet, 91 FilePath: walletStore.GetWalletPath(result.Wallet), 92 }, nil 93 } 94 95 return BuildCmdIsolateKey(w, h, rf) 96 } 97 98 func BuildCmdIsolateKey(w io.Writer, handler IsolateKeyHandler, rf *RootFlags) *cobra.Command { 99 f := &IsolateKeyFlags{} 100 101 cmd := &cobra.Command{ 102 Use: "isolate", 103 Short: "Extract the specified key pair into an isolated wallet", 104 Long: isolateKeyLong, 105 Example: isolateKeyExample, 106 RunE: func(_ *cobra.Command, _ []string) error { 107 req, pass, err := f.Validate() 108 if err != nil { 109 return err 110 } 111 112 resp, err := handler(req, pass) 113 if err != nil { 114 return err 115 } 116 117 switch rf.Output { 118 case flags.InteractiveOutput: 119 PrintIsolateKeyResponse(w, resp) 120 case flags.JSONOutput: 121 return printer.FprintJSON(w, resp) 122 } 123 124 return nil 125 }, 126 } 127 128 cmd.Flags().StringVarP(&f.Wallet, 129 "wallet", "w", 130 "", 131 "Wallet holding the public key", 132 ) 133 cmd.Flags().StringVarP(&f.PubKey, 134 "pubkey", "k", 135 "", 136 "Public key to isolate (hex-encoded)", 137 ) 138 cmd.Flags().StringVarP(&f.PassphraseFile, 139 "passphrase-file", "p", 140 "", 141 "Path to the file containing the wallet's passphrase", 142 ) 143 cmd.Flags().StringVar(&f.IsolatedWalletPassphraseFile, 144 "isolated-wallet-passphrase-file", 145 "", 146 "Path to the file containing the new isolated wallet's passphrase", 147 ) 148 149 autoCompleteWallet(cmd, rf.Home, "wallet") 150 151 return cmd 152 } 153 154 type IsolateKeyFlags struct { 155 Wallet string 156 PubKey string 157 PassphraseFile string 158 IsolatedWalletPassphraseFile string 159 } 160 161 func (f *IsolateKeyFlags) Validate() (api.AdminIsolateKeyParams, string, error) { 162 if len(f.Wallet) == 0 { 163 return api.AdminIsolateKeyParams{}, "", flags.MustBeSpecifiedError("wallet") 164 } 165 166 if len(f.PubKey) == 0 { 167 return api.AdminIsolateKeyParams{}, "", flags.MustBeSpecifiedError("pubkey") 168 } 169 170 passphrase, err := flags.GetPassphrase(f.PassphraseFile) 171 if err != nil { 172 return api.AdminIsolateKeyParams{}, "", err 173 } 174 175 newPassphrase, err := flags.GetConfirmedPassphraseWithContext(isolatedWalletPassphraseOptions, f.IsolatedWalletPassphraseFile) 176 if err != nil { 177 return api.AdminIsolateKeyParams{}, "", err 178 } 179 180 return api.AdminIsolateKeyParams{ 181 Wallet: f.Wallet, 182 PublicKey: f.PubKey, 183 IsolatedWalletPassphrase: newPassphrase, 184 }, passphrase, nil 185 } 186 187 func PrintIsolateKeyResponse(w io.Writer, resp isolateKeyResult) { 188 p := printer.NewInteractivePrinter(w) 189 190 str := p.String() 191 defer p.Print(str) 192 str.CheckMark().Text("Key pair has been isolated in wallet ").Bold(resp.Wallet).Text(" at: ").SuccessText(resp.FilePath).NextLine() 193 str.CheckMark().SuccessText("Key isolation succeeded").NextLine() 194 }