encrypt: more paranoia in FetchStreaming

Change-Id: Ie072a56015eb8a921328eccc6684647d2a153df3
This commit is contained in:
Brad Fitzpatrick 2013-06-15 20:37:33 -07:00
parent 3176c5be43
commit 1fddf9579a
1 changed files with 33 additions and 12 deletions

View File

@ -166,34 +166,53 @@ func (s *storage) ReceiveBlob(plainBR *blobref.BlobRef, source io.Reader) (sb bl
return blobref.SizedBlobRef{plainBR, plainSize}, nil return blobref.SizedBlobRef{plainBR, plainSize}, nil
} }
func (s *storage) FetchStreaming(b *blobref.BlobRef) (file io.ReadCloser, size int64, err error) { func (s *storage) FetchStreaming(plainBR *blobref.BlobRef) (file io.ReadCloser, size int64, err error) {
meta, err := s.fetchMeta(b) meta, err := s.fetchMeta(plainBR)
if err != nil { if err != nil {
return nil, 0, err return nil, 0, err
} }
rc, _, err := s.blobs.FetchStreaming(meta.EncBlobRef) encData, _, err := s.blobs.FetchStreaming(meta.EncBlobRef)
if err != nil { if err != nil {
log.Printf("encrypt: plaintext %s's encrypted %v blob not found", b, meta.EncBlobRef) log.Printf("encrypt: plaintext %s's encrypted %v blob not found", plainBR, meta.EncBlobRef)
return return
} }
defer encData.Close()
// Quick sanity check that the blob begins with the same IV we
// have in our metadata.
blobIV := make([]byte, len(meta.IV)) blobIV := make([]byte, len(meta.IV))
_, err = io.ReadFull(rc, blobIV) _, err = io.ReadFull(encData, blobIV)
if err != nil { if err != nil {
return nil, 0, fmt.Errorf("Error reading off IV header from blob: %v", err) return nil, 0, fmt.Errorf("Error reading off IV header from blob: %v", err)
} }
if !bytes.Equal(blobIV, meta.IV) { if !bytes.Equal(blobIV, meta.IV) {
return nil, 0, fmt.Errorf("Blob and meta IV don't match") return nil, 0, fmt.Errorf("Blob and meta IV don't match")
} }
// Slurp the whole blob into memory to validate its plaintext
// checksum (no tampered bits) before returning it. Clients
// should be the party doing this in the general case, but
// we'll be extra paranoid and always do it here, at the cost
// of sometimes having it be done twice.
var plain bytes.Buffer
plainHash := plainBR.Hash()
plainSize, err := io.Copy(io.MultiWriter(&plain, plainHash), cipher.StreamReader{
S: cipher.NewCTR(s.block, meta.IV),
R: encData,
})
if err != nil {
return nil, 0, err
}
if !plainBR.HashMatches(plainHash) {
return nil, 0, blobserver.ErrCorruptBlob
}
return struct { return struct {
io.Reader *bytes.Reader
io.Closer io.Closer
}{ }{
Closer: rc, bytes.NewReader(plain.Bytes()),
Reader: cipher.StreamReader{ dummyCloser,
S: cipher.NewCTR(s.block, meta.IV), }, plainSize, nil
R: rc,
},
}, meta.PlainSize, nil
} }
func (s *storage) EnumerateBlobs(dest chan<- blobref.SizedBlobRef, after string, limit int, wait time.Duration) error { func (s *storage) EnumerateBlobs(dest chan<- blobref.SizedBlobRef, after string, limit int, wait time.Duration) error {
@ -284,6 +303,8 @@ func parseMetaValue(v string) (mv *metaValue, err error) {
return mv, nil return mv, nil
} }
var dummyCloser io.Closer = ioutil.NopCloser(nil)
func init() { func init() {
blobserver.RegisterStorageConstructor("encrypt", blobserver.StorageConstructor(newFromConfig)) blobserver.RegisterStorageConstructor("encrypt", blobserver.StorageConstructor(newFromConfig))
} }