internal/artifact/checksum.go (54 lines of code) (raw):
package artifact
import (
"bytes"
"encoding/hex"
"errors"
"fmt"
"hash"
"strings"
)
type checksumVerifier struct {
expect []byte
digest hash.Hash
}
type nopChecksumVerifier struct{}
type ChecksumError struct {
Expect []byte
Actual []byte
}
// ParseGNUChecksum parses r as GNU checksum format and returns the checksum value. GNU checksums
// are a space separated digest and filename:
//
// <digest> <filename>
func ParseGNUChecksum(gnuChecksum []byte) ([]byte, error) {
ch, _, found := strings.Cut(string(gnuChecksum), " ")
if !found {
return nil, errors.New("invalid gnu checksum")
}
checksum, err := hex.DecodeString(ch)
if err != nil {
return nil, err
}
return checksum, nil
}
// NewChecksumError creates an error of type ChecksumError.
func NewChecksumError(src Source) error {
return ChecksumError{
Expect: src.ExpectedChecksum(),
Actual: src.ActualChecksum(),
}
}
// Actual satisfies Checksumer.
func (c checksumVerifier) ActualChecksum() []byte {
return c.digest.Sum(nil)
}
// Expected satisfies Checksumer.
func (c checksumVerifier) ExpectedChecksum() []byte {
return c.expect
}
// VerifyChecksum satisfies Checksumer. Calling verify before the associated Source has been
// completely read will cause an incomplete digest to be used and result in a false return.
func (c checksumVerifier) VerifyChecksum() bool {
return bytes.Equal(c.ActualChecksum(), c.ExpectedChecksum())
}
// Error implements the error interface.
func (ce ChecksumError) Error() string {
return fmt.Sprintf("checksum mismatch (expect != actual): %s != %s", string(ce.Expect), string(ce.Actual))
}
// Is implements the errors.Is interface.
func (ce ChecksumError) Is(err error) bool {
_, ok := err.(ChecksumError)
return ok
}
func (nopChecksumVerifier) VerifyChecksum() bool { return true }
func (nopChecksumVerifier) ExpectedChecksum() []byte { return nil }
func (nopChecksumVerifier) ActualChecksum() []byte { return nil }