check / check (pull_request) Successful in 1m22s
Production encryption and decryption already run through blobgen; the crypto package (Encryptor, Decryptor, UpdateRecipients, the fx Module) and Vaultik.GetEncryptor/GetDecryptor had no production caller. Delete crypto and route verify --deep through the same blobgen reader restore uses: it parses the age key once and reads both the database (still streamed to a temp file) and every blob through blobgen.NewReader. The second, blob-ID hash step is now one exported function, blobgen.DoubleSHA256, called by the three former copies. Writer.Sum256 (the double hash) becomes Writer.ContentID so it no longer shares the name Sum256 with Reader.Sum256 (the single plaintext hash). CompressData and CompressStream, unused outside tests, are deleted. Delete the unused, never-adopted secret/config newtypes in internal/types (the redacting AgeSecretKey and AWSSecretAccessKey plus AgeRecipient, S3Endpoint, BucketName, S3Prefix, AWSRegion, AWSAccessKeyID). Delete the uncalled CleanupIncompleteSnapshots (and deleteSnapshot, its only caller, now dead) and correct ARCHITECTURE.md. The only multi-recipient test moves to blobgen; the pre-#131 encrypted-bytes hash test is removed. Model: opus-4-8
78 lines
1.8 KiB
Go
78 lines
1.8 KiB
Go
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) (int, 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 single SHA-256 of the plaintext read so far. This is the
|
|
// first hash only; the stored object name is its double hash, which callers
|
|
// obtain by passing this digest to DoubleSHA256.
|
|
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
|
|
}
|