//go:build linux || freebsd // +build linux freebsd package crypto import ( "crypto/sha256" "crypto/sha512" "encoding/hex" "fmt" "hash" "io" "os" "golang.org/x/crypto/blake2b" "golang.org/x/crypto/sha3" ) // ChecksumType defines the checksum type type ChecksumType string const ( SHA256 ChecksumType = "sha256" SHA512 ChecksumType = "sha512" BLAKE2b ChecksumType = "blake2b" SHA3_256 ChecksumType = "sha3-256" SHA3_512 ChecksumType = "sha3-512" MD5 ChecksumType = "md5" ) // ChecksumVerifier verifies a file checksum type ChecksumVerifier struct { hashType ChecksumType hasher hash.Hash expected string } // NewChecksumVerifier creates new verifier func NewChecksumVerifier(hashType ChecksumType, expected string) (*ChecksumVerifier, error) { var hasher hash.Hash switch hashType { case SHA256: hasher = sha256.New() case SHA512: hasher = sha512.New() case BLAKE2b: h, err := blake2b.New256(nil) if err != nil { return nil, fmt.Errorf("failed to create blake2b hasher: %w", err) } hasher = h case SHA3_256: hasher = sha3.New256() case SHA3_512: hasher = sha3.New512() case MD5: return nil, fmt.Errorf("md5 is deprecated and not supported") default: return nil, fmt.Errorf("unsupported hash type: %s", hashType) } return &ChecksumVerifier{ hashType: hashType, hasher: hasher, expected: expected, }, nil } // VerifyFile verifies the checksum of a file func (cv *ChecksumVerifier) VerifyFile(path string) (bool, string, error) { file, err := os.Open(path) if err != nil { return false, "", fmt.Errorf("failed to open file: %w", err) } defer file.Close() cv.hasher.Reset() _, err = io.Copy(cv.hasher, file) if err != nil { return false, "", fmt.Errorf("failed to read file: %w", err) } actual := hex.EncodeToString(cv.hasher.Sum(nil)) match := actual == cv.expected return match, actual, nil } // VerifyReader verifies checksum from a reader (streaming) func (cv *ChecksumVerifier) VerifyReader(r io.Reader) (string, error) { cv.hasher.Reset() _, err := io.Copy(cv.hasher, r) if err != nil { return "", err } return hex.EncodeToString(cv.hasher.Sum(nil)), nil } // Hasher returns the underlying hasher for streaming verification func (cv *ChecksumVerifier) Hasher() hash.Hash { return cv.hasher } // Expected returns the expected checksum func (cv *ChecksumVerifier) Expected() string { return cv.expected } // Type returns the hash function type func (cv *ChecksumVerifier) Type() ChecksumType { return cv.hashType } // ComputeFileChecksum calculates the checksum of a file func ComputeFileChecksum(path string, hashType ChecksumType) (string, error) { file, err := os.Open(path) if err != nil { return "", err } defer file.Close() var hasher hash.Hash switch hashType { case SHA256: hasher = sha256.New() case SHA512: hasher = sha512.New() case BLAKE2b: h, err := blake2b.New256(nil) if err != nil { return "", fmt.Errorf("failed to create blake2b hasher: %w", err) } hasher = h case SHA3_256: hasher = sha3.New256() case SHA3_512: hasher = sha3.New512() default: return "", fmt.Errorf("unsupported hash type: %s", hashType) } _, err = io.Copy(hasher, file) if err != nil { return "", err } return hex.EncodeToString(hasher.Sum(nil)), nil }