feat: add a new subcommand - encrypt (#16)

This commit is contained in:
Dawid Wysokiński 2022-05-22 09:13:24 +02:00 committed by GitHub
parent f59e1db9a8
commit 7632df40da
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 68 additions and 47 deletions

View File

@ -22,7 +22,7 @@ const (
minIterations = 140000
)
func Encrypt(password, plaintext []byte) ([]byte, error) {
func Encrypt(plaintext, password []byte) ([]byte, error) {
iter := make([]byte, iterationLen)
iv := make([]byte, ivLen)
salt := make([]byte, saltLen)
@ -74,7 +74,7 @@ func randIterations() (int, error) {
return int(iterations.Int64() + minIterations), nil
}
func Decrypt(password, text []byte) ([]byte, error) {
func Decrypt(text, password []byte) ([]byte, error) {
iterations := text[:iterationLen]
salt := text[iterationLen : iterationLen+saltLen]
iv := text[iterationLen+saltLen : iterationLen+saltLen+ivLen]
@ -112,8 +112,8 @@ type Entry struct {
Type string `json:"type"`
}
func DecryptAsEntries(password, text []byte) ([]Entry, error) {
result, err := Decrypt(password, text)
func DecryptAsEntries(text, password []byte) ([]Entry, error) {
result, err := Decrypt(text, password)
if err != nil {
return nil, fmt.Errorf("decrypt: %w", err)
}

View File

@ -28,17 +28,17 @@ func TestEncryptDecrypt(t *testing.T) {
assert.Nil(t, err)
password := []byte("password22231")
encrypted, err := internal.Encrypt(password, entriesJSON)
encrypted, err := internal.Encrypt(entriesJSON, password)
assert.Nil(t, err)
decrypted, err := internal.Decrypt(password, encrypted)
decrypted, err := internal.Decrypt(encrypted, password)
assert.Nil(t, err)
var result []internal.Entry
err = json.Unmarshal(decrypted, &result)
assert.Nil(t, err)
assert.Equal(t, entries, result)
decryptedEntries, err := internal.DecryptAsEntries(password, encrypted)
decryptedEntries, err := internal.DecryptAsEntries(encrypted, password)
assert.Nil(t, err)
assert.Equal(t, entries, decryptedEntries)
}

101
main.go
View File

@ -37,11 +37,7 @@ func newApp() (*cli.App, error) {
Usage: "2FA App compatible with andOTP backup file format",
Version: Version,
Action: func(c *cli.Context) error {
var err error
password := []byte(c.String("password"))
if len(password) == 0 {
password, err = readPasswordFromStdin()
}
password, err := getPassword(c)
if err != nil {
return err
}
@ -51,7 +47,7 @@ func newApp() (*cli.App, error) {
return fmt.Errorf("something went wrong while reading file: %w", err)
}
entries, err := internal.DecryptAsEntries(password, b)
entries, err := internal.DecryptAsEntries(b, password)
if err != nil {
return fmt.Errorf("something went wrong while decrypting file: %w", err)
}
@ -67,7 +63,7 @@ func newApp() (*cli.App, error) {
&cli.StringFlag{
Name: "path",
Aliases: []string{"p"},
Usage: "path to andotp backup file",
Usage: "path to andOTP backup file",
Required: false,
DefaultText: "$HOME/.otp_accounts.json",
Value: path.Join(dirname, ".otp_accounts.json"),
@ -80,6 +76,7 @@ func newApp() (*cli.App, error) {
},
EnableBashCompletion: true,
Commands: []*cli.Command{
newEncryptCommand(),
newDecryptCommand(),
},
}, nil
@ -87,39 +84,9 @@ func newApp() (*cli.App, error) {
func newDecryptCommand() *cli.Command {
return &cli.Command{
Name: "decrypt",
Usage: "Decrypts backup file generated by andotp",
Action: func(c *cli.Context) error {
var err error
password := []byte(c.String("password"))
if len(password) == 0 {
password, err = readPasswordFromStdin()
}
if err != nil {
return err
}
b, err := os.ReadFile(c.String("path"))
if err != nil {
return fmt.Errorf("something went wrong while reading file: %w", err)
}
result, err := internal.Decrypt(password, b)
if err != nil {
return fmt.Errorf("something went wrong while decrypting file: %w", err)
}
output := c.String("output")
if output != "" {
if err := os.WriteFile(output, result, 0600); err != nil {
return fmt.Errorf("something went wrong while saving file: %w", err)
}
} else {
fmt.Print(string(result))
}
return nil
},
Name: "decrypt",
Usage: "Decrypts the specified backup file",
Action: newEncryptDecryptActionFunc(internal.Decrypt),
Flags: []cli.Flag{
&cli.StringFlag{
Name: "output",
@ -131,6 +98,60 @@ func newDecryptCommand() *cli.Command {
}
}
func newEncryptCommand() *cli.Command {
return &cli.Command{
Name: "encrypt",
Usage: "Encrypts the specified file file using AES-256",
Action: newEncryptDecryptActionFunc(internal.Encrypt),
Flags: []cli.Flag{
&cli.StringFlag{
Name: "output",
Usage: "Write to file instead of stdout",
Aliases: []string{"o"},
Required: false,
},
},
}
}
func newEncryptDecryptActionFunc(fn func(text, password []byte) ([]byte, error)) cli.ActionFunc {
return func(c *cli.Context) error {
password, err := getPassword(c)
if err != nil {
return err
}
b, err := os.ReadFile(c.String("path"))
if err != nil {
return fmt.Errorf("something went wrong while reading file: %w", err)
}
result, err := fn(b, password)
if err != nil {
return fmt.Errorf("something went wrong while processing file: %w", err)
}
output := c.String("output")
if output != "" {
if err := os.WriteFile(output, result, 0600); err != nil {
return fmt.Errorf("something went wrong while saving result: %w", err)
}
} else {
fmt.Print(string(result))
}
return nil
}
}
func getPassword(c *cli.Context) ([]byte, error) {
password := []byte(c.String("password"))
if len(password) == 0 {
return readPasswordFromStdin()
}
return password, nil
}
func readPasswordFromStdin() ([]byte, error) {
fmt.Print("Password: ")