package service import ( "bufio" "context" "fmt" "io" "os" "path/filepath" "sync" "github.com/pkg/errors" "github.com/urfave/cli" ) func encryptCommands(client *Client) []cli.Command { return []cli.Command{ cli.Command{ Name: "encrypt", Usage: "Encrypt", Flags: []cli.Flag{ cli.StringSliceFlag{Name: "recipient, r", Usage: "recipients"}, cli.StringFlag{Name: "sender, signer, s", Usage: "signer (or anonymous)"}, cli.BoolFlag{Name: "armor, a", Usage: "armored"}, cli.StringFlag{Name: "in, i", Usage: "file to read"}, cli.StringFlag{Name: "out, o", Usage: "file to write, defaults to .enc"}, cli.StringFlag{Name: "mode, m", Usage: "encryption mode: signcrypt, encrypt or default (signcrypt if signing, encrypt otherwise)"}, cli.BoolFlag{Hidden: true, Name: "no-signer-recipient", Usage: "don't add signer to recipients"}, }, Action: func(c *cli.Context) error { if c.String("in") == "" && c.String("out") != "" { return errors.Errorf("-out option is unsupported without -in") } mode, err := encryptModeFromString(c.String("mode")) if err != nil { return err } options := &EncryptOptions{ Mode: mode, Armored: c.Bool("armor"), NoSenderRecipient: c.Bool("no-sender-recipient"), } if c.String("in") != "" { return encryptFileForCLI(c, client, options) } reader := bufio.NewReader(os.Stdin) writer := os.Stdout encryptClient, err := client.RPCClient().EncryptStream(context.TODO()) if err != nil { return err } if err := encryptClient.Send(&EncryptInput{ Recipients: c.StringSlice("recipient"), Sender: c.String("sender"), Options: options, }); err != nil { return err } var readErr error go func() { _, inErr := readFrom(reader, 1024*1024, func(b []byte) error { if len(b) > 0 { if err := encryptClient.Send(&EncryptInput{Data: b}); err != nil { return err } } else { if err := encryptClient.CloseSend(); err != nil { return err } } return nil }) if inErr != nil { readErr = inErr } }() for { resp, recvErr := encryptClient.Recv() if recvErr != nil { if recvErr == io.EOF { // Return readErr, if set from readStdin above return readErr } return recvErr } _, writeErr := writer.Write(resp.Data) if writeErr != nil { return writeErr } } }, }, cli.Command{ Name: "decrypt", Usage: "Decrypt", Flags: []cli.Flag{ cli.StringFlag{Name: "in, i", Usage: "file to read"}, cli.StringFlag{Name: "out, o", Usage: "file to write"}, }, Action: func(c *cli.Context) error { if c.String("in") != "" { dec, err := decryptFileForCLI(c, client) if err != nil { return err } if dec.Sender != nil { fmtVerifiedEncrypt(client.out, dec.Sender, dec.Mode) } if dec.Out != c.String("out") { fmt.Fprintf(client.out, "out: %s\n", dec.Out) } return nil } reader := bufio.NewReader(os.Stdin) writer := os.Stdout decryptClient, err := NewDecryptStreamClient(context.TODO(), client.RPCClient()) if err != nil { return err } var openErr error go func() { _, inErr := readFrom(reader, 1024*1024, func(b []byte) error { if len(b) > 0 { if err := decryptClient.Send(&DecryptInput{Data: b}); err != nil { return err } } else { if err := decryptClient.CloseSend(); err != nil { return err } } return nil }) if inErr != nil { openErr = inErr } }() wgOpen := sync.WaitGroup{} wgOpen.Add(1) go func() { showSender := true for { resp, recvErr := decryptClient.Recv() if recvErr != nil { if recvErr == io.EOF { break } openErr = recvErr break } if showSender && resp.Sender != nil { fmtVerifiedEncrypt(client.out, resp.Sender, resp.Mode) showSender = false } if len(resp.Data) == 0 { break } if err := writeAll(writer, resp.Data); err != nil { openErr = err break } } wgOpen.Done() }() wgOpen.Wait() return openErr }, }, } } func encryptFileForCLI(c *cli.Context, client *Client, options *EncryptOptions) error { return encryptFile(client, c.String("in"), c.String("out"), c.StringSlice("recipient"), c.String("sender"), options) } func encryptFile(client *Client, in string, out string, recipients []string, sender string, options *EncryptOptions) error { in, err := filepath.Abs(in) if err != nil { return err } if out != "" { out, err = filepath.Abs(out) if err != nil { return err } } encryptClient, err := client.RPCClient().EncryptFile(context.TODO()) if err != nil { return err } if err := encryptClient.Send(&EncryptFileInput{ In: in, Out: out, Recipients: recipients, Sender: sender, Options: options, }); err != nil { return err } _, recvErr := encryptClient.Recv() if recvErr != nil { // if recvErr == io.EOF { // break // } return recvErr } // if err := encryptClient.CloseSend(); err != nil { // return err // } return nil } func decryptFileForCLI(c *cli.Context, client *Client) (*DecryptFileOutput, error) { return decryptFile(client, c.String("in"), c.String("out")) } func decryptFile(client *Client, in string, out string) (*DecryptFileOutput, error) { if in == "" { return nil, errors.Errorf("in not specified") } in, err := filepath.Abs(in) if err != nil { return nil, err } if out != "" { out, err = filepath.Abs(out) if err != nil { return nil, err } } decryptClient, err := client.RPCClient().DecryptFile(context.TODO()) if err != nil { return nil, err } if err := decryptClient.Send(&DecryptFileInput{ In: in, Out: out, }); err != nil { return nil, err } resp, recvErr := decryptClient.Recv() if recvErr != nil { // if recvErr == io.EOF { // break // } return nil, recvErr } // if err := decryptClient.CloseSend(); err != nil { // return err // } return resp, nil } func encryptModeFromString(s string) (EncryptMode, error) { switch s { case "", "default": return DefaultEncrypt, nil case "encrypt": return SaltpackEncrypt, nil case "signcrypt": return SaltpackSigncrypt, nil default: return DefaultEncrypt, errors.Errorf("invalid mode %q", s) } }