aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRose Hogenson <rosehogenson@posteo.net>2025-10-18 18:49:29 -0700
committerRose Hogenson <rosehogenson@posteo.net>2025-10-18 18:49:29 -0700
commitc241eeb8ad4baafa75691ffb140029ec54cb349e (patch)
tree7617f782900c5c1895e58db5e255f5bcaa22fcf1
parent6569b910521fc8a221bb93b41892e2b762a8f48a (diff)
downloadsym-c241eeb8ad4baafa75691ffb140029ec54cb349e.tar.zst
Get rid of the base64 encoding
User can always pipe into base64 anyway. This removes some complexity
-rw-r--r--internal/sym/dec.go46
-rw-r--r--internal/sym/dec_test.go2
-rw-r--r--internal/sym/enc.go104
-rw-r--r--internal/sym/enc_test.go55
-rw-r--r--internal/sym/sym_test.go46
5 files changed, 39 insertions, 214 deletions
diff --git a/internal/sym/dec.go b/internal/sym/dec.go
index 4e290d4..657da35 100644
--- a/internal/sym/dec.go
+++ b/internal/sym/dec.go
@@ -1,9 +1,6 @@
package sym
import (
- "bufio"
- "bytes"
- "encoding/base64"
"encoding/binary"
"errors"
"flag"
@@ -13,30 +10,7 @@ import (
"strings"
)
-type asciiReader struct {
- r *bufio.Reader
- line []byte
-}
-
-func (r *asciiReader) Read(b []byte) (int, error) {
- for len(r.line) == 0 {
- line, err := r.r.ReadSlice('\n')
- if len(line) == 0 {
- return 0, err
- }
- if bytes.HasPrefix(line, []byte("-")) {
- continue
- }
- if r.line, err = base64.StdEncoding.AppendDecode(line[:0], bytes.TrimSuffix(line, []byte("\n"))); err != nil {
- return 0, err
- }
- }
- n := copy(b, r.line)
- r.line = r.line[n:]
- return n, nil
-}
-
-func decryptBinary(w io.Writer, r io.Reader, password string) error {
+func decrypt(w io.Writer, r io.Reader, password string) error {
fileFormat := make([]byte, 4)
if _, err := io.ReadFull(r, fileFormat); err != nil {
return err
@@ -55,24 +29,6 @@ func decryptBinary(w io.Writer, r io.Reader, password string) error {
return err
}
-func decrypt(w io.Writer, r io.Reader, password string) error {
- bufReader := bufio.NewReader(r)
- b, err := bufReader.Peek(1)
- if err != nil {
- if err == io.EOF {
- return fmt.Errorf("no input")
- }
- return err
- }
- if b[0] == 0x80 {
- return decryptBinary(w, bufReader, password)
- }
- if b[0] != '-' {
- return errors.New("invalid input")
- }
- return decryptBinary(w, &asciiReader{r: bufReader}, password)
-}
-
type decryptFlags struct {
password string
force bool
diff --git a/internal/sym/dec_test.go b/internal/sym/dec_test.go
index b38497f..e1d6d9b 100644
--- a/internal/sym/dec_test.go
+++ b/internal/sym/dec_test.go
@@ -270,7 +270,7 @@ func TestDecryptOptions_Run_Stdin(t *testing.T) {
const password = "asdf"
content := []byte("test contents")
encrypted := new(bytes.Buffer)
- if err := testEncryptOptions.encryptBinary(encrypted, bytes.NewReader(content), password); err != nil {
+ if err := testEncryptOptions.encrypt(encrypted, bytes.NewReader(content), password); err != nil {
t.Fatalf("Failed to encrypt: %s", err)
}
gotContentBuf := new(bytes.Buffer)
diff --git a/internal/sym/enc.go b/internal/sym/enc.go
index 689df7b..623174c 100644
--- a/internal/sym/enc.go
+++ b/internal/sym/enc.go
@@ -1,9 +1,7 @@
package sym
import (
- "bufio"
"crypto/rand"
- "encoding/base64"
"encoding/binary"
"errors"
"flag"
@@ -15,81 +13,15 @@ import (
"roseh.moe/pkg/wordlist"
)
-type asciiWriter struct {
- w *bufio.Writer
- buf [2]byte
- nBuf int
- n int
-}
-
-func (w *asciiWriter) writeBase64(b []byte) (int, error) {
- _, err := w.w.Write(base64.StdEncoding.AppendEncode(w.w.AvailableBuffer(), b))
- return len(b), err
-}
-
-func (w *asciiWriter) Write(b []byte) (int, error) {
- const lineSizeBytes = 60
-
- nn := 0
- if w.nBuf > 0 && w.nBuf+len(b) >= 3 {
- leadingChunk := make([]byte, 3)
- n := copy(leadingChunk, w.buf[:w.nBuf])
- w.nBuf = 0
- n = copy(leadingChunk[n:], b)
- b = b[n:]
- n, err := w.writeBase64(leadingChunk)
- nn += n
- w.n += n
- if err != nil {
- return nn, err
- }
- }
- for len(b) >= 3 {
- chunkSize := lineSizeBytes - w.n
- if chunkSize > len(b) {
- chunkSize = len(b) - len(b)%3
- }
- n, err := w.writeBase64(b[:chunkSize])
- nn += n
- w.n += n
- if err != nil {
- return nn, err
- }
- b = b[n:]
- if w.n == lineSizeBytes {
- w.w.WriteByte('\n')
- w.n = 0
- }
- }
- n := copy(w.buf[:], b)
- nn += n
- w.nBuf = n
- return nn, nil
-}
-
-func (w *asciiWriter) Close() error {
- if w.nBuf > 0 {
- if _, err := w.writeBase64(w.buf[:w.nBuf]); err != nil {
- return err
- }
- if err := w.w.WriteByte('\n'); err != nil {
- return err
- }
- }
- return w.w.Flush()
-}
-
type encryptFlags struct {
generatePassword bool
password string
- asciiOutput bool
force bool
}
func (f *encryptFlags) RegisterFlags(fs *flag.FlagSet) {
fs.BoolVar(&f.generatePassword, "g", false, "generate a secure password automatically (password will be printed to stderr)")
fs.StringVar(&f.password, "p", "", "use the specified password; if not provided, enc will prompt for a password")
- fs.BoolVar(&f.asciiOutput, "a", false, "output in base64, default is binary output")
fs.BoolVar(&f.force, "f", false, "overwrite output files even if they already exist")
}
@@ -111,7 +43,7 @@ var DefaultEncryptOptions = EncryptOptions{
stdout: os.Stdout,
}
-func (o *EncryptOptions) encryptBinary(w io.Writer, r io.Reader, password string) error {
+func (o *EncryptOptions) encrypt(w io.Writer, r io.Reader, password string) error {
if _, err := io.WriteString(w, magic); err != nil {
return err
}
@@ -137,40 +69,22 @@ func (o *EncryptOptions) encryptBinary(w io.Writer, r io.Reader, password string
return writer.Close()
}
-func (o *EncryptOptions) encryptBase64(w io.Writer, r io.Reader, password string) error {
- bufWriter := bufio.NewWriter(w)
- if _, err := bufWriter.WriteString(`-------------------------- Begin encrypted text block --------------------------
--------------------------- am i cool like gpg? ---------------------------------
-`); err != nil {
- return err
- }
- base64Writer := &asciiWriter{w: bufWriter}
- if err := o.encryptBinary(base64Writer, r, password); err != nil {
- return err
- }
- return base64Writer.Close()
-}
-
func (o *EncryptOptions) encryptFile(fileName string, password string) (err error) {
f, err := os.Open(fileName)
if err != nil {
return err
}
defer f.Close()
- ext := ".enc"
- if o.asciiOutput {
- ext = ".enc.txt"
- }
fileOpts := os.O_CREATE | os.O_WRONLY
if o.force {
fileOpts |= os.O_TRUNC
} else {
fileOpts |= os.O_EXCL
}
- fOut, err := os.OpenFile(fileName+ext, fileOpts, 0644)
+ fOut, err := os.OpenFile(fileName+".enc", fileOpts, 0644)
if err != nil {
if errors.Is(err, os.ErrExist) {
- return fmt.Errorf("output file %q exists (use -f to overwrite)", fileName+ext)
+ return fmt.Errorf("output file %q exists (use -f to overwrite)", fileName+".enc")
}
return err
}
@@ -180,12 +94,7 @@ func (o *EncryptOptions) encryptFile(fileName string, password string) (err erro
os.Remove(fOut.Name())
}
}()
- if o.asciiOutput {
- err = o.encryptBase64(fOut, f, password)
- } else {
- err = o.encryptBinary(fOut, f, password)
- }
- if err != nil {
+ if err = o.encrypt(fOut, f, password); err != nil {
return fmt.Errorf("encrypt %q: %s", fileName, err)
}
return fOut.Close()
@@ -252,10 +161,7 @@ func (o *EncryptOptions) Run(args ...string) error {
}
}
if len(args) == 0 {
- if o.asciiOutput {
- return o.encryptBase64(o.stdout, o.stdin, password)
- }
- return o.encryptBinary(o.stdout, o.stdin, password)
+ return o.encrypt(o.stdout, o.stdin, password)
}
for _, fileName := range args {
if err := o.encryptFile(fileName, password); err != nil {
diff --git a/internal/sym/enc_test.go b/internal/sym/enc_test.go
index e2ec6a8..3a0c493 100644
--- a/internal/sym/enc_test.go
+++ b/internal/sym/enc_test.go
@@ -71,12 +71,11 @@ func TestEncryptOptions_RegisterFlags(t *testing.T) {
var o EncryptOptions
fs := flag.NewFlagSet("test", flag.ContinueOnError)
o.RegisterFlags(fs)
- const cmd = "-g -p asdf -a -f"
+ const cmd = "-g -p asdf -f"
fs.Parse(strings.Split(cmd, " "))
want := encryptFlags{
generatePassword: true,
password: "asdf",
- asciiOutput: true,
force: true,
}
if o.encryptFlags != want {
@@ -168,40 +167,24 @@ func TestEncryptOptions_Run_GeneratePassword(t *testing.T) {
func TestEncryptOptions_Run_Stdin(t *testing.T) {
t.Parallel()
- for _, tc := range []struct {
- desc string
- ascii bool
- }{{
- desc: "Binary",
- ascii: false,
- }, {
- desc: "ASCII",
- ascii: true,
- }} {
- t.Run(tc.desc, func(t *testing.T) {
- t.Parallel()
-
- const (
- input = "test input"
- password = "asdf"
- )
- stdout := new(strings.Builder)
- opts := testEncryptOptions
- opts.password = password
- opts.asciiOutput = tc.ascii
- opts.stdin = strings.NewReader(input)
- opts.stdout = stdout
- if err := opts.Run(); err != nil {
- t.Errorf("enc(+%v) failed: %s", opts, err)
- }
- got := new(strings.Builder)
- if err := decrypt(got, strings.NewReader(stdout.String()), password); err != nil {
- t.Errorf("Failed to decrypt stdout content %q: %s", stdout, err)
- }
- if got, want := got.String(), input; got != want {
- t.Errorf("Encrypt round-trip to stdout returned incorrect contents: %q, want %q", got, want)
- }
- })
+ const (
+ input = "test input"
+ password = "asdf"
+ )
+ stdout := new(strings.Builder)
+ opts := testEncryptOptions
+ opts.password = password
+ opts.stdin = strings.NewReader(input)
+ opts.stdout = stdout
+ if err := opts.Run(); err != nil {
+ t.Errorf("enc(+%v) failed: %s", opts, err)
+ }
+ got := new(strings.Builder)
+ if err := decrypt(got, strings.NewReader(stdout.String()), password); err != nil {
+ t.Errorf("Failed to decrypt stdout content %q: %s", stdout, err)
+ }
+ if got, want := got.String(), input; got != want {
+ t.Errorf("Encrypt round-trip to stdout returned incorrect contents: %q, want %q", got, want)
}
}
diff --git a/internal/sym/sym_test.go b/internal/sym/sym_test.go
index 98ae025..704d477 100644
--- a/internal/sym/sym_test.go
+++ b/internal/sym/sym_test.go
@@ -57,39 +57,19 @@ func TestEncryptDecrypt(t *testing.T) {
for i := range buf {
buf[i] = byte(i)
}
- for _, tc := range []struct {
- desc string
- ascii bool
- }{{
- desc: "Binary",
- ascii: false,
- }, {
- desc: "ASCII",
- ascii: true,
- }} {
- t.Run(tc.desc, func(t *testing.T) {
- t.Parallel()
- fileName := filepath.Join(t.TempDir(), "file")
- mustWriteFile(t, fileName, buf)
- const password = "karp cache tidal mars fed rajah uses graze pobox flew"
- encOpts := testEncryptOptions
- encOpts.asciiOutput = tc.ascii
- if err := encOpts.encryptFile(fileName, password); err != nil {
- t.Fatalf("EncryptFile failed: %s", err)
- }
- mustRemove(t, fileName)
- ext := ".enc"
- if tc.ascii {
- ext = ".enc.txt"
- }
- if err := DefaultDecryptOptions.decryptFile(fileName+ext, password); err != nil {
- t.Fatalf("DecryptFile failed: %s", err)
- }
- gotContents := mustReadFile(t, fileName)
- if !bytes.Equal(gotContents, buf) {
- t.Errorf("contents differ")
- }
- })
+ fileName := filepath.Join(t.TempDir(), "file")
+ mustWriteFile(t, fileName, buf)
+ const password = "karp cache tidal mars fed rajah uses graze pobox flew"
+ if err := testEncryptOptions.encryptFile(fileName, password); err != nil {
+ t.Fatalf("EncryptFile failed: %s", err)
+ }
+ mustRemove(t, fileName)
+ if err := DefaultDecryptOptions.decryptFile(fileName+".enc", password); err != nil {
+ t.Fatalf("DecryptFile failed: %s", err)
+ }
+ gotContents := mustReadFile(t, fileName)
+ if !bytes.Equal(gotContents, buf) {
+ t.Errorf("contents differ")
}
}