From 3a8f0c2c566aa05a60c11151e8875c0f940f783d Mon Sep 17 00:00:00 2001
From: Vinay Gopalan <vinay@hashicorp.com>
Date: Tue, 30 Nov 2021 10:45:30 -0800
Subject: [PATCH] add initial stub for encrypt

---
 command/commands.go |   5 ++
 command/encrypt.go  | 137 ++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 142 insertions(+)
 create mode 100644 command/encrypt.go

diff --git a/command/commands.go b/command/commands.go
index dc08c4a74..a2e18cdbc 100644
--- a/command/commands.go
+++ b/command/commands.go
@@ -283,6 +283,11 @@ func initCommands(ui, serverCmdUi cli.Ui, runOpts *RunOptions) {
 				BaseCommand: getBaseCommand(),
 			}, nil
 		},
+		"encrypt": func() (cli.Command, error) {
+			return &EncryptCommand{
+				BaseCommand: getBaseCommand(),
+			}, nil
+		},
 		"lease": func() (cli.Command, error) {
 			return &LeaseCommand{
 				BaseCommand: getBaseCommand(),
diff --git a/command/encrypt.go b/command/encrypt.go
new file mode 100644
index 000000000..1fb133555
--- /dev/null
+++ b/command/encrypt.go
@@ -0,0 +1,137 @@
+package command
+
+import (
+	"fmt"
+	"log"
+	"strings"
+
+	"crypto/aes"
+	"crypto/cipher"
+	"crypto/rand"
+	"encoding/hex"
+	"io"
+	"io/ioutil"
+
+	"github.com/mitchellh/cli"
+)
+
+var _ cli.Command = (*EncryptCommand)(nil)
+
+type EncryptCommand struct {
+	*BaseCommand
+
+	aes128 bool
+	// outfile string
+}
+
+func (c *EncryptCommand) Synopsis() string {
+	return "Encrypts a file using AES 128bit or 256bit encryption."
+}
+
+func (c *EncryptCommand) Help() string {
+	helpText := `
+Usage: vault encrypt [options] [filename]
+
+  Encrypts a file using AES encryption.
+
+  Encrypt a single file:
+
+      $ vault encrypt -o foo.enc foo.txt
+
+`
+	return strings.TrimSpace(helpText)
+}
+
+func (c *EncryptCommand) Flags() *FlagSets {
+	set := c.flagSet(FlagSetHTTP)
+	f := set.NewFlagSet("Command Options")
+
+	f.BoolVar(&BoolVar{
+		Name:    "aes128",
+		Target:  &c.aes128,
+		Default: false,
+		Usage:   "Encrypt the file using 128bit encryption instead of the default aes256.",
+	})
+
+	// f.StringVar(&StringVar{
+	// 	Name:    "out",
+	// 	Aliases: []string{"o"},
+	// 	Target:  &c.outfile,
+	// 	Default: "output.enc",
+	// 	Usage:   "Specify the name of the output file.",
+	// })
+
+	return set
+}
+
+func (c *EncryptCommand) Run(args []string) int {
+	f := c.Flags()
+
+	if err := f.Parse(args); err != nil {
+		c.UI.Error(err.Error())
+		return 1
+	}
+
+	args = f.Args()
+	switch {
+	case len(args) < 1:
+		c.UI.Error(fmt.Sprintf("Not enough arguments (expected 1, got %d)", len(args)))
+		return 1
+	case len(args) > 1:
+		c.UI.Error(fmt.Sprintf("Too many arguments (expected 1, got %d)", len(args)))
+		return 1
+	}
+
+	filename := strings.TrimSpace(args[0])
+	data, err := ioutil.ReadFile(filename)
+	if err != nil {
+		log.Fatal(err)
+	}
+
+	if !c.aes128 {
+		key := make([]byte, 32)
+		if _, err := rand.Read(key); err != nil {
+			fmt.Errorf(err.Error())
+		}
+
+		keyString := hex.EncodeToString(key)
+
+		// TODO Write keyString to file
+		fmt.Printf("Key written to file: %s", keyString)
+		encrypt(data, key)
+
+	} else {
+		// Create 128bit key
+	}
+	return 0
+}
+
+func encrypt(dataToEncrypt []byte, key []byte) {
+
+	// Create a new Cipher Block using key
+	block, err := aes.NewCipher(key)
+	if err != nil {
+		fmt.Errorf(err.Error())
+	}
+
+	// Create a new GCM
+	aesGCM, err := cipher.NewGCM(block)
+	if err != nil {
+		fmt.Errorf(err.Error())
+	}
+
+	// Create a nonce from GCM
+	nonce := make([]byte, aesGCM.NonceSize())
+	if _, err = io.ReadFull(rand.Reader, nonce); err != nil {
+		fmt.Errorf(err.Error())
+	}
+
+	// Encrypt and write to file
+	ciphertext := aesGCM.Seal(nonce, nonce, dataToEncrypt, nil)
+
+	err = ioutil.WriteFile("output.enc", ciphertext, 0644)
+	if err != nil {
+		fmt.Errorf(err.Error())
+	}
+
+}
-- 
GitLab