forked from TrueCloudLab/distribution
132 lines
2.5 KiB
Go
132 lines
2.5 KiB
Go
|
package digest
|
||
|
|
||
|
import (
|
||
|
"crypto/md5"
|
||
|
"crypto/sha1"
|
||
|
"crypto/sha256"
|
||
|
"hash"
|
||
|
"io"
|
||
|
"io/ioutil"
|
||
|
|
||
|
"github.com/docker/docker/pkg/tarsum"
|
||
|
)
|
||
|
|
||
|
type Verifier interface {
|
||
|
io.Writer
|
||
|
|
||
|
// Verified will return true if the content written to Verifier matches
|
||
|
// the digest.
|
||
|
Verified() bool
|
||
|
|
||
|
// Planned methods:
|
||
|
// Err() error
|
||
|
// Reset()
|
||
|
}
|
||
|
|
||
|
func DigestVerifier(d Digest) Verifier {
|
||
|
alg := d.Algorithm()
|
||
|
switch alg {
|
||
|
case "md5", "sha1", "sha256":
|
||
|
return hashVerifier{
|
||
|
hash: newHash(alg),
|
||
|
digest: d,
|
||
|
}
|
||
|
default:
|
||
|
// Assume we have a tarsum.
|
||
|
version, err := tarsum.GetVersionFromTarsum(string(d))
|
||
|
if err != nil {
|
||
|
panic(err) // Always assume valid tarsum at this point.
|
||
|
}
|
||
|
|
||
|
pr, pw := io.Pipe()
|
||
|
|
||
|
// TODO(stevvooe): We may actually want to ban the earlier versions of
|
||
|
// tarsum. That decision may not be the place of the verifier.
|
||
|
|
||
|
ts, err := tarsum.NewTarSum(pr, true, version)
|
||
|
if err != nil {
|
||
|
panic(err)
|
||
|
}
|
||
|
|
||
|
// TODO(sday): Ick! A goroutine per digest verification? We'll have to
|
||
|
// get the tarsum library to export an io.Writer variant.
|
||
|
go func() {
|
||
|
io.Copy(ioutil.Discard, ts)
|
||
|
pw.Close()
|
||
|
}()
|
||
|
|
||
|
return &tarsumVerifier{
|
||
|
digest: d,
|
||
|
ts: ts,
|
||
|
pr: pr,
|
||
|
pw: pw,
|
||
|
}
|
||
|
}
|
||
|
|
||
|
panic("unsupported digest: " + d)
|
||
|
}
|
||
|
|
||
|
// LengthVerifier returns a verifier that returns true when the number of read
|
||
|
// bytes equals the expected parameter.
|
||
|
func LengthVerifier(expected int64) Verifier {
|
||
|
return &lengthVerifier{
|
||
|
expected: expected,
|
||
|
}
|
||
|
}
|
||
|
|
||
|
type lengthVerifier struct {
|
||
|
expected int64 // expected bytes read
|
||
|
len int64 // bytes read
|
||
|
}
|
||
|
|
||
|
func (lv *lengthVerifier) Write(p []byte) (n int, err error) {
|
||
|
n = len(p)
|
||
|
lv.len += int64(n)
|
||
|
return n, err
|
||
|
}
|
||
|
|
||
|
func (lv *lengthVerifier) Verified() bool {
|
||
|
return lv.expected == lv.len
|
||
|
}
|
||
|
|
||
|
func newHash(name string) hash.Hash {
|
||
|
switch name {
|
||
|
case "sha256":
|
||
|
return sha256.New()
|
||
|
case "sha1":
|
||
|
return sha1.New()
|
||
|
case "md5":
|
||
|
return md5.New()
|
||
|
default:
|
||
|
panic("unsupport algorithm: " + name)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
type hashVerifier struct {
|
||
|
digest Digest
|
||
|
hash hash.Hash
|
||
|
}
|
||
|
|
||
|
func (hv hashVerifier) Write(p []byte) (n int, err error) {
|
||
|
return hv.hash.Write(p)
|
||
|
}
|
||
|
|
||
|
func (hv hashVerifier) Verified() bool {
|
||
|
return hv.digest == NewDigest(hv.digest.Algorithm(), hv.hash)
|
||
|
}
|
||
|
|
||
|
type tarsumVerifier struct {
|
||
|
digest Digest
|
||
|
ts tarsum.TarSum
|
||
|
pr *io.PipeReader
|
||
|
pw *io.PipeWriter
|
||
|
}
|
||
|
|
||
|
func (tv *tarsumVerifier) Write(p []byte) (n int, err error) {
|
||
|
return tv.pw.Write(p)
|
||
|
}
|
||
|
|
||
|
func (tv *tarsumVerifier) Verified() bool {
|
||
|
return tv.digest == Digest(tv.ts.Sum(nil))
|
||
|
}
|