151 lines
3.2 KiB
Go
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
|
|
}
|