Files
goget/internal/crypto/checksum.go
T

151 lines
3.2 KiB
Go

//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
}