From da2191789a4db9a68ed5f078aca2ac49d55e7740 Mon Sep 17 00:00:00 2001 From: Filippo Valsorda Date: Fri, 26 Dec 2025 21:35:42 +0100 Subject: [PATCH] age: add ExampleDecryptReaderAt with zip.NewReader --- age.go | 4 ++-- age_test.go | 36 ++++++++++++++++++++++++++++++++++++ testdata/example.zip.age | 6 ++++++ 3 files changed, 44 insertions(+), 2 deletions(-) create mode 100644 testdata/example.zip.age diff --git a/age.go b/age.go index 16d9bf6..13a2802 100644 --- a/age.go +++ b/age.go @@ -284,8 +284,8 @@ func Decrypt(src io.Reader, identities ...Identity) (io.Reader, error) { // DecryptReaderAt takes an underlying [io.ReaderAt] and its total encrypted // size, and returns a ReaderAt of the decrypted plaintext and the plaintext // size. These can be used for example to instantiate an [io.SectionReader], -// which implements [io.Reader] and [io.Seeker]. Note that ReaderAt by -// definition disregards the seek position of src. +// which implements [io.Reader] and [io.Seeker], or for [zip.NewReader]. +// Note that ReaderAt by definition disregards the seek position of src. // // The ReadAt method of the returned ReaderAt can be called concurrently. // The ReaderAt will internally cache the most recently decrypted chunk. diff --git a/age_test.go b/age_test.go index bbe9e26..7cded16 100644 --- a/age_test.go +++ b/age_test.go @@ -5,10 +5,12 @@ package age_test import ( + "archive/zip" "bytes" "errors" "fmt" "io" + "io/fs" "log" "os" "slices" @@ -191,6 +193,40 @@ func TestEncryptDecryptScrypt(t *testing.T) { } } +func ExampleDecryptReaderAt() { + identity, err := age.ParseX25519Identity(privateKey) + if err != nil { + log.Fatalf("Failed to parse private key: %v", err) + } + + f, err := os.Open("testdata/example.zip.age") + if err != nil { + log.Fatalf("Failed to open file: %v", err) + } + stat, err := f.Stat() + if err != nil { + log.Fatalf("Failed to stat file: %v", err) + } + + r, size, err := age.DecryptReaderAt(f, stat.Size(), identity) + if err != nil { + log.Fatalf("Failed to open encrypted file: %v", err) + } + + z, err := zip.NewReader(r, size) + if err != nil { + log.Fatalf("Failed to open zip: %v", err) + } + contents, err := fs.ReadFile(z, "example.txt") + if err != nil { + log.Fatalf("Failed to read file from zip: %v", err) + } + + fmt.Printf("File contents: %q\n", contents) + // Output: + // File contents: "Black lives matter." +} + func TestParseIdentities(t *testing.T) { tests := []struct { name string diff --git a/testdata/example.zip.age b/testdata/example.zip.age new file mode 100644 index 0000000..7a798ce --- /dev/null +++ b/testdata/example.zip.age @@ -0,0 +1,6 @@ +age-encryption.org/v1 +-> X25519 5CD81lZA72aQi0v6EnniOGkwaswpZ0AxCZNdiUVzP04 +ol9DvdkiZWeRI4vMKRBVNxowDKwir4UPqYinSM5zqUI +--- 2tyNGCaPoT6UnuOy7sQJf1eXn4pb7z2ukSgTDIxrJxU +W +dtUbT0(yKAPdr1M~ kX>c܍[$9, G{턚Ftk*}9TޱhLЏW-RdˣHSdUĚFsÈ2y+)]/,k=8(XRA01RY k4N6tvbc _F0dvx $X~ / \ No newline at end of file