From c241eeb8ad4baafa75691ffb140029ec54cb349e Mon Sep 17 00:00:00 2001 From: Rose Hogenson Date: Sat, 18 Oct 2025 18:49:29 -0700 Subject: Get rid of the base64 encoding User can always pipe into base64 anyway. This removes some complexity --- internal/sym/dec.go | 46 +-------------------- internal/sym/dec_test.go | 2 +- internal/sym/enc.go | 104 +++-------------------------------------------- internal/sym/enc_test.go | 55 +++++++++---------------- internal/sym/sym_test.go | 46 ++++++--------------- 5 files changed, 39 insertions(+), 214 deletions(-) (limited to 'internal') 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") } } -- cgit v1.3.1