Skip to content

Commit

Permalink
feat: password-protected keys support
Browse files Browse the repository at this point in the history
  • Loading branch information
caarlos0 committed Mar 8, 2022
1 parent 71a05c0 commit 22c1f13
Show file tree
Hide file tree
Showing 5 changed files with 49 additions and 45 deletions.
38 changes: 0 additions & 38 deletions cmd/melt/id_rsa

This file was deleted.

22 changes: 19 additions & 3 deletions cmd/melt/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package main
import (
"crypto/ed25519"
"encoding/pem"
"errors"
"fmt"
"io"
"os"
Expand All @@ -13,6 +14,7 @@ import (
"github.com/mattn/go-isatty"
"github.com/muesli/coral"
"golang.org/x/crypto/ssh"
"golang.org/x/term"
)

var (
Expand All @@ -31,7 +33,7 @@ var (
Example: "melt backup ~/.ssh/id_ed25519",
Args: coral.ExactArgs(1),
RunE: func(cmd *coral.Command, args []string) error {
mnemonic, err := backup(args[0])
mnemonic, err := backup(args[0], nil)
if err != nil {
return err
}
Expand Down Expand Up @@ -92,14 +94,28 @@ func maybeFile(s string) string {
return string(bts)
}

func backup(path string) (string, error) {
func backup(path string, pwd []byte) (string, error) {
bts, err := os.ReadFile(path)
if err != nil {
return "", fmt.Errorf("could not read key: %w", err)
}

key, err := ssh.ParseRawPrivateKey(bts)
var key interface{}
if pwd == nil {
key, err = ssh.ParseRawPrivateKey(bts)
} else {
key, err = ssh.ParseRawPrivateKeyWithPassphrase(bts, pwd)
}
if err != nil {
pwderr := &ssh.PassphraseMissingError{}
if errors.As(err, &pwderr) {
fmt.Fprintf(os.Stderr, "Enter the password to decrypt %q: ", path)
pwd, err := term.ReadPassword(int(os.Stdin.Fd()))
if err != nil {
return "", fmt.Errorf("could not read password for key: %w", err)
}
return backup(path, pwd)
}
return "", fmt.Errorf("could not parse key: %w", err)
}

Expand Down
25 changes: 21 additions & 4 deletions cmd/melt/main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,27 +20,44 @@ func TestBackupRestoreKnownKey(t *testing.T) {
const expectedSum = "ba34175ef608633b29f046b40cce596dd221347b77abba40763eef2e7ae51fe9"

t.Run("backup", func(t *testing.T) {
mnemonic, err := backup("testdata/id_ed25519", nil)
is := is.New(t)
mnemonic, err := backup("testdata/id_ed25519")
is.NoErr(err)
is.Equal(mnemonic, strings.Join(strings.Fields(expectedMnemonic), " "))
})

t.Run("backup file that does not exist", func(t *testing.T) {
_, err := backup("nope")
_, err := backup("nope", nil)
is.New(t).True(err != nil)
})

t.Run("backup invalid ssh key", func(t *testing.T) {
_, err := backup("testdata/not-a-key")
_, err := backup("testdata/not-a-key", nil)
is.New(t).True(err != nil)
})

t.Run("backup key of another type", func(t *testing.T) {
_, err := backup("testdata/id_rsa")
_, err := backup("testdata/id_rsa", nil)
is.New(t).True(err != nil)
})

t.Run("backup key without password", func(t *testing.T) {
_, err := backup("testdata/pwd_id_ed25519", nil)
is := is.New(t)
is.True(err != nil)
})

t.Run("backup key with password", func(t *testing.T) {
const expectedMnemonic = `assume knee laundry logic soft fit quantum
puppy vault snow author alien famous comfort neglect habit
emerge fabric trophy wine hold inquiry clown govern`

mnemonic, err := backup("testdata/pwd_id_ed25519", []byte("asd"))
is := is.New(t)
is.NoErr(err)
is.Equal(mnemonic, strings.Join(strings.Fields(expectedMnemonic), " "))
})

t.Run("restore", func(t *testing.T) {
is := is.New(t)
path := filepath.Join(t.TempDir(), "key")
Expand Down
8 changes: 8 additions & 0 deletions cmd/melt/testdata/pwd_id_ed25519
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
-----BEGIN OPENSSH PRIVATE KEY-----
b3BlbnNzaC1rZXktdjEAAAAACmFlczI1Ni1jdHIAAAAGYmNyeXB0AAAAGAAAABBnlzGNfk
mM3f0qJgdEHufbAAAAEAAAAAEAAAAzAAAAC3NzaC1lZDI1NTE5AAAAIC7fLAHYurOuCxI9
f9cNQnoB9ErLerbYPebfpw+DapxBAAAAkDC4nF/SF4oHZFMOHh+Up17Y1iXcOyDyfoGD9g
0rZMTeuzm5ftpXuVmyUeDZjc17KEU5q9+zguI60XACuYO+JI10rhK/1ZaWIE9ucGhr5Gka
7dqOp7HUydHlvU2tiMqkpdlxVHA8jErHY0rWElN3awOpmkPA1AxSotZhDCmi6o9+EsPrTP
xx8dnflg5zZE2KRA==
-----END OPENSSH PRIVATE KEY-----
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ require (
github.com/muesli/coral v1.0.0
github.com/tyler-smith/go-bip39 v1.1.0
golang.org/x/crypto v0.0.0-20220307211146-efcb8507fb70
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1
)

require (
Expand Down

0 comments on commit 22c1f13

Please sign in to comment.