42 lines
1.2 KiB
Go
42 lines
1.2 KiB
Go
package submit
|
|
|
|
import (
|
|
"bytes"
|
|
"crypto/sha256"
|
|
"errors"
|
|
"fmt"
|
|
"hash"
|
|
"io"
|
|
)
|
|
|
|
// ErrDigestMismatch reports bytes that do not hash to the SHA-256 their sender
|
|
// computed over them (the request's Content-Digest): they changed on the way.
|
|
// The API answers 400 digest_mismatch, and the client sends them again.
|
|
var ErrDigestMismatch = errors.New("submit: the bytes that arrived do not match the digest they were sent with")
|
|
|
|
// VerifyDigest passes r through and, where it ends, answers ErrDigestMismatch in
|
|
// place of io.EOF unless what went by hashes to want, a SHA-256. The stores
|
|
// treat that like any read that breaks off: UploadPart cuts the part back off,
|
|
// and UploadContext never lets the blob replace the one before it. So bytes
|
|
// changed on the way are never kept.
|
|
func VerifyDigest(r io.Reader, want []byte) io.Reader {
|
|
return &digestReader{r: r, h: sha256.New(), want: want}
|
|
}
|
|
|
|
type digestReader struct {
|
|
r io.Reader
|
|
h hash.Hash
|
|
want []byte
|
|
}
|
|
|
|
func (d *digestReader) Read(p []byte) (int, error) {
|
|
n, err := d.r.Read(p)
|
|
d.h.Write(p[:n])
|
|
if err == io.EOF {
|
|
if got := d.h.Sum(nil); !bytes.Equal(got, d.want) {
|
|
return n, fmt.Errorf("%w: they hash to %x, sent as %x", ErrDigestMismatch, got, d.want)
|
|
}
|
|
}
|
|
return n, err
|
|
}
|