diff --git a/doc/age-inspect.1 b/doc/age-inspect.1 index 8f1472ba..72da5a7c 100644 --- a/doc/age-inspect.1 +++ b/doc/age-inspect.1 @@ -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" diff --git a/doc/age-inspect.1.html b/doc/age-inspect.1.html index b2adea83..79007386 100644 --- a/doc/age-inspect.1.html +++ b/doc/age-inspect.1.html @@ -195,7 +195,7 @@

AUTHORS

  1. -
  2. December 2025
  3. +
  4. January 2026
  5. age-inspect(1)
diff --git a/doc/age-keygen.1 b/doc/age-keygen.1 index 93495b08..2f65cc7a 100644 --- a/doc/age-keygen.1 +++ b/doc/age-keygen.1 @@ -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" diff --git a/doc/age-keygen.1.html b/doc/age-keygen.1.html index 57958bd2..70543c3e 100644 --- a/doc/age-keygen.1.html +++ b/doc/age-keygen.1.html @@ -150,7 +150,7 @@

AUTHORS

  1. -
  2. December 2025
  3. +
  4. January 2026
  5. age-keygen(1)
diff --git a/doc/age-plugin-batchpass.1 b/doc/age-plugin-batchpass.1 index cd2d653a..47dacb64 100644 --- a/doc/age-plugin-batchpass.1 +++ b/doc/age-plugin-batchpass.1 @@ -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" diff --git a/doc/age-plugin-batchpass.1.html b/doc/age-plugin-batchpass.1.html index b549d57c..79a5ea0a 100644 --- a/doc/age-plugin-batchpass.1.html +++ b/doc/age-plugin-batchpass.1.html @@ -174,7 +174,7 @@

AUTHORS

  1. -
  2. December 2025
  3. +
  4. January 2026
  5. age-plugin-batchpass(1)
diff --git a/doc/age.1 b/doc/age.1 index a36470ba..743e5239 100644 --- a/doc/age.1 +++ b/doc/age.1 @@ -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" diff --git a/doc/age.1.html b/doc/age.1.html index 0656f162..ec9968bf 100644 --- a/doc/age.1.html +++ b/doc/age.1.html @@ -461,7 +461,7 @@

AUTHORS

  1. -
  2. December 2025
  3. +
  4. January 2026
  5. age(1)
diff --git a/internal/stream/stream.go b/internal/stream/stream.go index 967cdc21..e212c204 100644 --- a/internal/stream/stream.go +++ b/internal/stream/stream.go @@ -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) { @@ -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) @@ -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) { @@ -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() @@ -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)