Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion doc/age-inspect.1
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
.\" generated with Ronn-NG/v0.9.1
.\" http://github.com/apjanke/ronn-ng/tree/0.9.1
.TH "AGE\-INSPECT" "1" "December 2025" ""
.TH "AGE\-INSPECT" "1" "January 2026" ""
.SH "NAME"
\fBage\-inspect\fR \- inspect age(1) encrypted files
.SH "SYNOPSIS"
Expand Down
2 changes: 1 addition & 1 deletion doc/age-inspect.1.html

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion doc/age-keygen.1
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
.\" generated with Ronn-NG/v0.9.1
.\" http://github.com/apjanke/ronn-ng/tree/0.9.1
.TH "AGE\-KEYGEN" "1" "December 2025" ""
.TH "AGE\-KEYGEN" "1" "January 2026" ""
.SH "NAME"
\fBage\-keygen\fR \- generate age(1) key pairs
.SH "SYNOPSIS"
Expand Down
2 changes: 1 addition & 1 deletion doc/age-keygen.1.html

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion doc/age-plugin-batchpass.1
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
.\" generated with Ronn-NG/v0.9.1
.\" http://github.com/apjanke/ronn-ng/tree/0.9.1
.TH "AGE\-PLUGIN\-BATCHPASS" "1" "December 2025" ""
.TH "AGE\-PLUGIN\-BATCHPASS" "1" "January 2026" ""
.SH "NAME"
\fBage\-plugin\-batchpass\fR \- non\-interactive passphrase encryption plugin for age(1)
.SH "SYNOPSIS"
Expand Down
2 changes: 1 addition & 1 deletion doc/age-plugin-batchpass.1.html

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion doc/age.1
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
.\" generated with Ronn-NG/v0.9.1
.\" http://github.com/apjanke/ronn-ng/tree/0.9.1
.TH "AGE" "1" "December 2025" ""
.TH "AGE" "1" "January 2026" ""
.SH "NAME"
\fBage\fR \- simple, modern, and secure file encryption
.SH "SYNOPSIS"
Expand Down
2 changes: 1 addition & 1 deletion doc/age.1.html

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

22 changes: 13 additions & 9 deletions internal/stream/stream.go
Original file line number Diff line number Diff line change
Expand Up @@ -189,7 +189,10 @@ func NewEncryptWriter(key []byte, dst io.Writer) (*EncryptWriter, error) {
if err != nil {
return nil, err
}
return &EncryptWriter{a: aead, dst: dst}, nil
// We write at most ChunkSize bytes and
// need extra capacity to encrypt in place.
const capacity = ChunkSize + chacha20poly1305.Overhead
return &EncryptWriter{a: aead, dst: dst, buf: *bytes.NewBuffer(make([]byte, 0, capacity))}, nil
}

func (w *EncryptWriter) Write(p []byte) (n int, err error) {
Expand Down Expand Up @@ -246,7 +249,7 @@ func (w *EncryptWriter) flushChunk(last bool) error {
if last {
setLastChunkFlag(&w.nonce)
}
w.buf.Grow(chacha20poly1305.Overhead)
// We know w.buf.Bytes() has enough capacity for the overhead.
ciphertext := w.a.Seal(w.buf.Bytes()[:0], w.nonce[:], w.buf.Bytes(), nil)
_, err := w.dst.Write(ciphertext)
incNonce(&w.nonce)
Expand All @@ -272,7 +275,11 @@ func NewEncryptReader(key []byte, src io.Reader) (*EncryptReader, error) {
if err != nil {
return nil, err
}
return &EncryptReader{a: aead, src: src}, nil
// We read at most ChunkSize + 1 bytes from src and
// need extra capacity to encrypt in place and
// to prevent r.buf.ReadFrom from growing the buffer.
const capacity = ChunkSize + 1 + max(chacha20poly1305.Overhead, bytes.MinRead)
return &EncryptReader{a: aead, src: src, buf: *bytes.NewBuffer(make([]byte, 0, capacity))}, nil
}

func (r *EncryptReader) Read(p []byte) (int, error) {
Expand Down Expand Up @@ -312,16 +319,14 @@ func (r *EncryptReader) feedBuffer() error {
return err
}

if last := r.buf.Len() <= ChunkSize; last {
if r.buf.Len() <= ChunkSize {
setLastChunkFlag(&r.nonce)

// After Grow, we know r.buf.Bytes() has enough capacity for the
// overhead. We encrypt in place and then do a Write to include the
// We know r.buf.Bytes() has enough capacity for the overhead.
// We encrypt in place and then do a Write to include the
// overhead in the buffer.
r.buf.Grow(chacha20poly1305.Overhead)
plaintext := r.buf.Bytes()
r.a.Seal(plaintext[:0], r.nonce[:], plaintext, nil)
incNonce(&r.nonce)
r.buf.Write(plaintext[len(plaintext) : len(plaintext)+chacha20poly1305.Overhead])
r.ready = r.buf.Len()

Expand All @@ -335,7 +340,6 @@ func (r *EncryptReader) feedBuffer() error {
panic("stream: internal error: unexpected buffer length")
}
tailByte := r.buf.Bytes()[ChunkSize]
r.buf.Grow(chacha20poly1305.Overhead)
plaintext := r.buf.Bytes()[:ChunkSize]
r.a.Seal(plaintext[:0], r.nonce[:], plaintext, nil)
incNonce(&r.nonce)
Expand Down