internal/age: remove EncryptWithArmor and armor support in Decrypt

The caller can take care of the armor. For consistency move the
responsibility to close the armor to the caller, and make the stream
Writer not propagate Close.

This also will also allow us to spin the armor implementation out into
its won package that imports format, without getting an import loop from
format.Parse magically invoking armor decoding.

Less magic in the API, more magic in the CLI.
This commit is contained in:
Filippo Valsorda
2020-05-17 13:47:13 -04:00
parent 7088a73234
commit a7c4274d23
5 changed files with 26 additions and 49 deletions

View File

@@ -7,6 +7,7 @@
package main
import (
"bufio"
"bytes"
"flag"
"fmt"
@@ -16,6 +17,7 @@ import (
"strings"
"filippo.io/age/internal/age"
"filippo.io/age/internal/format"
"golang.org/x/crypto/ssh/terminal"
)
@@ -208,7 +210,13 @@ func encryptPass(pass string, in io.Reader, out io.Writer, armor bool) {
func encrypt(recipients []age.Recipient, in io.Reader, out io.Writer, armor bool) {
ageEncrypt := age.Encrypt
if armor {
ageEncrypt = age.EncryptWithArmor
a := format.ArmoredWriter(out)
defer func() {
if err := a.Close(); err != nil {
logFatalf("Error: %v", err)
}
}()
out = a
}
w, err := ageEncrypt(out, recipients...)
if err != nil {
@@ -239,6 +247,14 @@ func decrypt(keys []string, in io.Reader, out io.Writer) {
identities = append(identities, ids...)
}
rr := bufio.NewReader(in)
armorHeader := "-----BEGIN AGE ENCRYPTED FILE-----"
if start, _ := rr.Peek(len(armorHeader)); string(start) == armorHeader {
in = format.ArmoredReader(rr)
} else {
in = rr
}
r, err := age.Decrypt(in, identities...)
if err != nil {
logFatalf("Error: %v", err)

View File

@@ -36,19 +36,6 @@ type Recipient interface {
}
func Encrypt(dst io.Writer, recipients ...Recipient) (io.WriteCloser, error) {
// stream.Writer takes a WriteCloser, and will propagate Close calls (so
// that the ArmoredWriter will get closed), but we don't want to expose
// that behavior to our caller.
dstCloser := format.NopCloser(dst)
return encrypt(dstCloser, recipients...)
}
func EncryptWithArmor(dst io.Writer, recipients ...Recipient) (io.WriteCloser, error) {
dstCloser := format.ArmoredWriter(dst)
return encrypt(dstCloser, recipients...)
}
func encrypt(dst io.WriteCloser, recipients ...Recipient) (io.WriteCloser, error) {
if len(recipients) == 0 {
return nil, errors.New("no recipients specified")
}

View File

@@ -112,13 +112,6 @@ func Parse(input io.Reader) (*Header, io.Reader, error) {
h := &Header{}
rr := bufio.NewReader(input)
// TODO: find a way to communicate to the caller that the file was armored,
// as they might not appreciate the malleability.
if start, _ := rr.Peek(len(armorPreamble)); string(start) == armorPreamble {
input = ArmoredReader(rr)
rr = bufio.NewReader(input)
}
line, err := rr.ReadString('\n')
if err != nil {
return nil, nil, errorf("failed to read intro: %v", err)

View File

@@ -132,14 +132,14 @@ func setLastChunkFlag(nonce *[chacha20poly1305.NonceSize]byte) {
type Writer struct {
a cipher.AEAD
dst io.WriteCloser
dst io.Writer
unwritten []byte // backed by buf
buf [encChunkSize]byte
nonce [chacha20poly1305.NonceSize]byte
err error
}
func NewWriter(key []byte, dst io.WriteCloser) (*Writer, error) {
func NewWriter(key []byte, dst io.Writer) (*Writer, error) {
aead, err := chacha20poly1305.New(key)
if err != nil {
return nil, err
@@ -178,21 +178,19 @@ func (w *Writer) Write(p []byte) (n int, err error) {
return total, nil
}
// Close will flush the last chunk and call the underlying
// WriteCloser's Close method.
// Close flushes the last chunk. It does not close the underlying Writer.
func (w *Writer) Close() error {
if w.err != nil {
return w.err
}
err := w.flushChunk(lastChunk)
if err != nil {
w.err = err
return err
w.err = w.flushChunk(lastChunk)
if w.err != nil {
return w.err
}
w.err = errors.New("stream.Writer is already closed")
return w.dst.Close()
w.err = errors.New("stream.Writer is already closed")
return nil
}
const (

View File

@@ -10,10 +10,8 @@ import (
"bytes"
"crypto/rand"
"fmt"
"io"
"testing"
"filippo.io/age/internal/format"
"filippo.io/age/internal/stream"
"golang.org/x/crypto/chacha20poly1305"
)
@@ -40,19 +38,7 @@ func testRoundTrip(t *testing.T, stepSize, length int) {
t.Fatal(err)
}
var closed bool
bufCloser := struct {
io.Writer
format.CloserFunc
}{
Writer: buf,
CloserFunc: func() error {
closed = true
return nil
},
}
w, err := stream.NewWriter(key, bufCloser)
w, err := stream.NewWriter(key, buf)
if err != nil {
t.Fatal(err)
}
@@ -84,9 +70,6 @@ func testRoundTrip(t *testing.T, stepSize, length int) {
if err := w.Close(); err != nil {
t.Error("Close returned an error:", err)
}
if !closed {
t.Error("(*stream.Writer).Close didn't close the underlying WriteCloser")
}
t.Logf("buffer size: %d", buf.Len())