package blobgen import ( "crypto/sha256" "fmt" "hash" "io" "filippo.io/age" "github.com/klauspost/compress/zstd" ) // Reader wraps decompression and decryption with SHA256 verification type Reader struct { reader io.Reader decompressor *zstd.Decoder decryptor io.Reader hasher hash.Hash teeReader io.Reader bytesRead int64 } // NewReader creates a new Reader that decrypts, decompresses, and verifies data func NewReader(r io.Reader, identity age.Identity) (*Reader, error) { // Create decryption reader decReader, err := age.Decrypt(r, identity) if err != nil { return nil, fmt.Errorf("creating decryption reader: %w", err) } // Create decompression reader decompressor, err := zstd.NewReader(decReader) if err != nil { return nil, fmt.Errorf("creating decompression reader: %w", err) } // Create SHA256 hasher hasher := sha256.New() // Create tee reader that reads from decompressor and writes to hasher teeReader := io.TeeReader(decompressor, hasher) return &Reader{ reader: r, decompressor: decompressor, decryptor: decReader, hasher: hasher, teeReader: teeReader, }, nil } // Read implements io.Reader func (r *Reader) Read(p []byte) (n int, err error) { n, err = r.teeReader.Read(p) r.bytesRead += int64(n) return n, err } // Close closes the decompressor func (r *Reader) Close() error { r.decompressor.Close() return nil } // Sum256 returns the SHA256 hash of all data read func (r *Reader) Sum256() []byte { return r.hasher.Sum(nil) } // BytesRead returns the number of uncompressed bytes read func (r *Reader) BytesRead() int64 { return r.bytesRead }