summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorShulhan <ms@kilabit.info>2023-09-20 01:15:55 +0700
committerShulhan <ms@kilabit.info>2023-09-20 01:28:24 +0700
commita201862eec862f013b65864b6b5c74f5e77374ce (patch)
treeb98dea9dca49d55ee3a1aad7ab9a9914ceac8069
parent332ca03f703b2b380aa1075e60e3cd1cad938a73 (diff)
downloadawwan-a201862eec862f013b65864b6b5c74f5e77374ce.tar.xz
all: add method Encrypt to Awwan
The Encrypt method encrypt the file using private key from file "{{.BaseDir}}/.awwan.key". The encrypted file output will be on the same file path with ".vault" extension.
-rw-r--r--.gitignore2
-rw-r--r--awwan.go85
-rw-r--r--awwan_test.go80
-rw-r--r--go.mod6
-rw-r--r--go.sum4
-rw-r--r--testdata/encrypt-with-passphrase/.awwan.env2
-rw-r--r--testdata/encrypt-with-passphrase/.awwan.key39
-rw-r--r--testdata/encrypt-with-passphrase/.ssh/empty0
l---------testdata/encrypt-without-passphrase/.awwan.env1
-rw-r--r--testdata/encrypt-without-passphrase/.awwan.key38
-rw-r--r--testdata/encrypt-without-passphrase/.ssh/empty0
l---------testdata/encrypt-without-rsa/.awwan.env1
-rw-r--r--testdata/encrypt-without-rsa/.awwan.key8
-rw-r--r--testdata/encrypt-without-rsa/.ssh/empty0
14 files changed, 259 insertions, 7 deletions
diff --git a/.gitignore b/.gitignore
index d8c228e..96957aa 100644
--- a/.gitignore
+++ b/.gitignore
@@ -12,3 +12,5 @@
/awwan
/cover.html
/cover.out
+/testdata/encrypt-with-passphrase/.awwan.env.vault
+/testdata/encrypt-without-passphrase/.awwan.env.vault
diff --git a/awwan.go b/awwan.go
index a09a477..27c1c71 100644
--- a/awwan.go
+++ b/awwan.go
@@ -5,16 +5,21 @@ package awwan
import (
"bytes"
+ "crypto"
+ "crypto/rand"
+ "crypto/rsa"
+ "crypto/sha256"
"fmt"
+ "io"
"log"
"os"
"path/filepath"
+ "git.sr.ht/~shulhan/awwan/internal"
+ libcrypto "github.com/shuLhan/share/lib/crypto"
"github.com/shuLhan/share/lib/http"
"github.com/shuLhan/share/lib/memfs"
"github.com/shuLhan/share/lib/ssh/config"
-
- "git.sr.ht/~shulhan/awwan/internal"
)
// Version current version of this module (library and program).
@@ -36,6 +41,9 @@ const (
defTmpDir = "/tmp"
)
+// defFilePrivateKey define the default private key file name.
+const defFilePrivateKey = `.awwan.key`
+
var (
cmdMagicGet = []byte("#get:")
cmdMagicPut = []byte("#put:")
@@ -56,6 +64,14 @@ type Awwan struct {
httpd *http.Server // The HTTP server.
memfsBase *memfs.MemFS // The files caches.
+ // privateKey define the key for encrypt and decrypt command.
+ privateKey *rsa.PrivateKey
+
+ // termrw define the ReadWriter to prompt and read passphrase for
+ // privateKey.
+ // This field should be nil, only used during testing.
+ termrw io.ReadWriter
+
bufout bytes.Buffer
buferr bytes.Buffer
}
@@ -87,6 +103,49 @@ func New(baseDir string) (aww *Awwan, err error) {
return aww, nil
}
+// Encrypt the file using private key from file "{{.BaseDir}}/.awwan.key".
+// The encrypted file output will be on the same file path with ".vault"
+// extension.
+func (aww *Awwan) Encrypt(file string) (err error) {
+ var (
+ logp = `Encrypt`
+ fileVault = file + `.vault`
+ )
+
+ if aww.privateKey == nil {
+ err = aww.loadPrivateKey()
+ if err != nil {
+ return fmt.Errorf(`%s: %w`, logp, err)
+ }
+ }
+
+ var src []byte
+
+ src, err = os.ReadFile(file)
+ if err != nil {
+ return fmt.Errorf(`%s: %w`, logp, err)
+ }
+
+ var (
+ hash = sha256.New()
+ label = []byte(`awwan`)
+
+ ciphertext []byte
+ )
+
+ ciphertext, err = rsa.EncryptOAEP(hash, rand.Reader, &aww.privateKey.PublicKey, src, label)
+ if err != nil {
+ return fmt.Errorf(`%s: %w`, logp, err)
+ }
+
+ err = os.WriteFile(fileVault, ciphertext, 0600)
+ if err != nil {
+ return fmt.Errorf(`%s: %w`, logp, err)
+ }
+
+ return nil
+}
+
// Local execute the script in the local machine using shell.
func (aww *Awwan) Local(req *Request) (err error) {
if len(req.lineRange.list) == 0 {
@@ -325,6 +384,28 @@ func (aww *Awwan) loadSshConfig() (err error) {
return nil
}
+// loadPrivateKey from file "{{.BaseDir}}/.awwan.key".
+func (aww *Awwan) loadPrivateKey() (err error) {
+ var (
+ fileKey = filepath.Join(aww.BaseDir, defFilePrivateKey)
+
+ pkey crypto.PrivateKey
+ ok bool
+ )
+
+ pkey, err = libcrypto.LoadPrivateKeyInteractive(aww.termrw, fileKey)
+ if err != nil {
+ return err
+ }
+
+ aww.privateKey, ok = pkey.(*rsa.PrivateKey)
+ if !ok {
+ return fmt.Errorf(`the private key type must be RSA, got %T`, pkey)
+ }
+
+ return nil
+}
+
// lookupBaseDir find the directory that contains ".ssh" directory from
// current working directory until "/", as the base working directory of
// awwan.
diff --git a/awwan_test.go b/awwan_test.go
new file mode 100644
index 0000000..a216fbf
--- /dev/null
+++ b/awwan_test.go
@@ -0,0 +1,80 @@
+package awwan
+
+import (
+ "os"
+ "path/filepath"
+ "testing"
+
+ "github.com/shuLhan/share/lib/test"
+ "github.com/shuLhan/share/lib/test/mock"
+)
+
+func TestAwwanEncrypt(t *testing.T) {
+ type testCase struct {
+ baseDir string
+ file string
+ passphrase string
+ expError string
+ }
+
+ var cases = []testCase{{
+ baseDir: filepath.Join(`testdata`, `encrypt-with-passphrase`),
+ file: `.awwan.env`,
+ passphrase: "s3cret\r",
+ }, {
+ baseDir: filepath.Join(`testdata`, `encrypt-with-passphrase`),
+ file: `.awwan.env`,
+ passphrase: "invalids3cret\r",
+ expError: `Encrypt: LoadPrivateKeyInteractive: x509: decryption password incorrect`,
+ }, {
+ baseDir: filepath.Join(`testdata`, `encrypt-without-rsa`),
+ file: `.awwan.env`,
+ passphrase: "s3cret\r",
+ expError: `Encrypt: the private key type must be RSA, got *ed25519.PrivateKey`,
+ }, {
+ baseDir: filepath.Join(`testdata`, `encrypt-without-passphrase`),
+ file: `.awwan.env`,
+ }}
+
+ var (
+ mockrw = mock.ReadWriter{}
+
+ c testCase
+ aww *Awwan
+ err error
+ )
+
+ for _, c = range cases {
+ var (
+ filePlain = filepath.Join(c.baseDir, c.file)
+ fileVault = filepath.Join(c.baseDir, `.awwan.env.vault`)
+ )
+
+ _ = os.Remove(fileVault)
+
+ aww, err = New(c.baseDir)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ if len(c.passphrase) != 0 {
+ // Write the passphrase to standard input to be read
+ // interactively.
+ mockrw.BufRead.WriteString(c.passphrase)
+ aww.termrw = &mockrw
+ } else {
+ aww.termrw = nil
+ }
+
+ err = aww.Encrypt(filePlain)
+ if err != nil {
+ test.Assert(t, `Encrypt`, c.expError, err.Error())
+ continue
+ }
+
+ _, err = os.Stat(fileVault)
+ if err != nil {
+ test.Assert(t, `os.Stat`, c.expError, err.Error())
+ }
+ }
+}
diff --git a/go.mod b/go.mod
index 14b088a..df6edef 100644
--- a/go.mod
+++ b/go.mod
@@ -8,17 +8,17 @@ go 1.20
require (
git.sr.ht/~shulhan/ciigo v0.10.0
github.com/evanw/esbuild v0.17.10
- github.com/shuLhan/share v0.49.2-0.20230917095357-d3957eb89349
- golang.org/x/crypto v0.13.0
- golang.org/x/term v0.12.0
+ github.com/shuLhan/share v0.49.2-0.20230919175205-3b7ac793264a
)
require (
git.sr.ht/~shulhan/asciidoctor-go v0.4.1 // indirect
github.com/yuin/goldmark v1.5.4 // indirect
github.com/yuin/goldmark-meta v1.1.0 // indirect
+ golang.org/x/crypto v0.13.0 // indirect
golang.org/x/net v0.15.0 // indirect
golang.org/x/sys v0.12.0 // indirect
+ golang.org/x/term v0.12.0 // indirect
gopkg.in/yaml.v2 v2.3.0 // indirect
)
diff --git a/go.sum b/go.sum
index e063aad..3d859ee 100644
--- a/go.sum
+++ b/go.sum
@@ -4,8 +4,8 @@ git.sr.ht/~shulhan/ciigo v0.10.0 h1:s1SJ3/NzBcbOLmEZ4z1Cx9Vf7ZdDIvm45b7KMCZKzEY=
git.sr.ht/~shulhan/ciigo v0.10.0/go.mod h1:cG6av+ywJZZp96F43kmLB2QWjm2hYiahbsbeTX/vlgk=
github.com/evanw/esbuild v0.17.10 h1:RMwM8ehohA6RSgWVirjnsZ+u9ttNt0gWfRLYCxUbAoc=
github.com/evanw/esbuild v0.17.10/go.mod h1:iINY06rn799hi48UqEnaQvVfZWe6W9bET78LbvN8VWk=
-github.com/shuLhan/share v0.49.2-0.20230917095357-d3957eb89349 h1:2KUGziFe7WIWzGvaO9UfC2+5XWvvw096dIc1Rt5tW2g=
-github.com/shuLhan/share v0.49.2-0.20230917095357-d3957eb89349/go.mod h1:xgun5BOQHu/CrOu5oGUAtKGlSuKVOEL1O10YhPYCQsc=
+github.com/shuLhan/share v0.49.2-0.20230919175205-3b7ac793264a h1:7ohg2TOplcnbhfGiIdkPb1fn3D3Qph0ZDRMOkxwM580=
+github.com/shuLhan/share v0.49.2-0.20230919175205-3b7ac793264a/go.mod h1:i5/MNWUy/B+b76J96VTNG8HLE/xZ8mVy+i0oR51y6Jc=
github.com/yuin/goldmark v1.5.4 h1:2uY/xC0roWy8IBEGLgB1ywIoEJFGmRrX21YQcvGZzjU=
github.com/yuin/goldmark v1.5.4/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
github.com/yuin/goldmark-meta v1.1.0 h1:pWw+JLHGZe8Rk0EGsMVssiNb/AaPMHfSRszZeUeiOUc=
diff --git a/testdata/encrypt-with-passphrase/.awwan.env b/testdata/encrypt-with-passphrase/.awwan.env
new file mode 100644
index 0000000..a769b9d
--- /dev/null
+++ b/testdata/encrypt-with-passphrase/.awwan.env
@@ -0,0 +1,2 @@
+[secret]
+pass = thisisasecret
diff --git a/testdata/encrypt-with-passphrase/.awwan.key b/testdata/encrypt-with-passphrase/.awwan.key
new file mode 100644
index 0000000..7ff257d
--- /dev/null
+++ b/testdata/encrypt-with-passphrase/.awwan.key
@@ -0,0 +1,39 @@
+-----BEGIN OPENSSH PRIVATE KEY-----
+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
+ovQk9w==
+-----END OPENSSH PRIVATE KEY-----
diff --git a/testdata/encrypt-with-passphrase/.ssh/empty b/testdata/encrypt-with-passphrase/.ssh/empty
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/testdata/encrypt-with-passphrase/.ssh/empty
diff --git a/testdata/encrypt-without-passphrase/.awwan.env b/testdata/encrypt-without-passphrase/.awwan.env
new file mode 120000
index 0000000..21a511f
--- /dev/null
+++ b/testdata/encrypt-without-passphrase/.awwan.env
@@ -0,0 +1 @@
+../encrypt-with-passphrase/.awwan.env \ No newline at end of file
diff --git a/testdata/encrypt-without-passphrase/.awwan.key b/testdata/encrypt-without-passphrase/.awwan.key
new file mode 100644
index 0000000..068bb84
--- /dev/null
+++ b/testdata/encrypt-without-passphrase/.awwan.key
@@ -0,0 +1,38 @@
+-----BEGIN OPENSSH PRIVATE KEY-----
+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
+Wp7eVxh5y6ynhzAAAACm1zQGluc3Bpcm8=
+-----END OPENSSH PRIVATE KEY-----
diff --git a/testdata/encrypt-without-passphrase/.ssh/empty b/testdata/encrypt-without-passphrase/.ssh/empty
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/testdata/encrypt-without-passphrase/.ssh/empty
diff --git a/testdata/encrypt-without-rsa/.awwan.env b/testdata/encrypt-without-rsa/.awwan.env
new file mode 120000
index 0000000..21a511f
--- /dev/null
+++ b/testdata/encrypt-without-rsa/.awwan.env
@@ -0,0 +1 @@
+../encrypt-with-passphrase/.awwan.env \ No newline at end of file
diff --git a/testdata/encrypt-without-rsa/.awwan.key b/testdata/encrypt-without-rsa/.awwan.key
new file mode 100644
index 0000000..6c39222
--- /dev/null
+++ b/testdata/encrypt-without-rsa/.awwan.key
@@ -0,0 +1,8 @@
+-----BEGIN OPENSSH PRIVATE KEY-----
+b3BlbnNzaC1rZXktdjEAAAAACmFlczI1Ni1jdHIAAAAGYmNyeXB0AAAAGAAAABCU+ACQ9d
+uYYESV1IV3sSvpAAAAGAAAAAEAAAAzAAAAC3NzaC1lZDI1NTE5AAAAICkvBCCVBn8YcRz2
+PfpyqUULPqlxtXRMleHcRjMgttZPAAAAkIcYu8n88JwJ2P2OWLK0EX8T03SGf8cGrORozG
+Rsgd9BsJx5J9BKDOQgoeylPnoNxs031bnhDzHiDn3AvSuXl7inVgjxGyoiqsavZbv7m+Fh
+8hmEaIM0y7Uq0Vl8WFRCeHE5L3ESGfOewP4k2timIb/SD3B/0Zmhff/5bZYKxjB9/s2tb3
+h9RYKhcFzSK22Vnw==
+-----END OPENSSH PRIVATE KEY-----
diff --git a/testdata/encrypt-without-rsa/.ssh/empty b/testdata/encrypt-without-rsa/.ssh/empty
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/testdata/encrypt-without-rsa/.ssh/empty