package digest import ( "crypto/sha256" "crypto/sha512" "hash" "io" "io/ioutil" "github.com/docker/docker/pkg/tarsum" ) // Verifier presents a general verification interface to be used with message // digests and other byte stream verifications. Users instantiate a Verifier // from one of the various methods, write the data under test to it then check // the result with the Verified method. type Verifier interface { io.Writer // Verified will return true if the content written to Verifier matches // the digest. Verified() bool } // NewDigestVerifier returns a verifier that compares the written bytes // against a passed in digest. func NewDigestVerifier(d Digest) (Verifier, error) { if err := d.Validate(); err != nil { return nil, err } alg := d.Algorithm() switch alg { case "sha256", "sha384", "sha512": return hashVerifier{ hash: newHash(alg), digest: d, }, nil default: // Assume we have a tarsum. version, err := tarsum.GetVersionFromTarsum(string(d)) if err != nil { return nil, err } 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 { return nil, 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() { if _, err := io.Copy(ioutil.Discard, ts); err != nil { pr.CloseWithError(err) } else { pr.Close() } }() return &tarsumVerifier{ digest: d, ts: ts, pr: pr, pw: pw, }, nil } } // NewLengthVerifier returns a verifier that returns true when the number of // read bytes equals the expected parameter. func NewLengthVerifier(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 "sha384": return sha512.New384() case "sha512": return sha512.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)) }