2021-01-13 03:00:16 +00:00
|
|
|
package update
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"bytes"
|
|
|
|
|
"crypto/sha256"
|
|
|
|
|
"crypto/sha512"
|
2025-09-09 22:13:30 +00:00
|
|
|
"encoding/hex"
|
2021-11-22 14:13:26 +00:00
|
|
|
"fmt"
|
2021-01-13 03:00:16 +00:00
|
|
|
"hash"
|
|
|
|
|
"io"
|
|
|
|
|
"os"
|
2025-09-09 22:13:30 +00:00
|
|
|
"strings"
|
2021-01-13 03:00:16 +00:00
|
|
|
|
2025-09-09 22:13:30 +00:00
|
|
|
"github.com/rs/zerolog/log"
|
2021-01-13 03:00:16 +00:00
|
|
|
"github.com/theupdateframework/go-tuf/data"
|
|
|
|
|
)
|
|
|
|
|
|
2022-02-23 17:58:07 +00:00
|
|
|
// checkFileHash checks the file at the local path against the provided hash functions.
|
|
|
|
|
func checkFileHash(meta *data.TargetFileMeta, localPath string) error {
|
|
|
|
|
metaHash, localHash, err := fileHashes(meta, localPath)
|
2021-01-13 03:00:16 +00:00
|
|
|
if err != nil {
|
2022-02-23 17:58:07 +00:00
|
|
|
return fmt.Errorf("failed to calculate local file hash: %s", err)
|
2021-01-13 03:00:16 +00:00
|
|
|
}
|
2022-02-23 17:58:07 +00:00
|
|
|
if !bytes.Equal(localHash, metaHash) {
|
|
|
|
|
return fmt.Errorf("hash %x does not match expected: %x", localHash, metaHash)
|
|
|
|
|
}
|
|
|
|
|
return nil
|
|
|
|
|
}
|
2021-01-13 03:00:16 +00:00
|
|
|
|
2022-02-23 17:58:07 +00:00
|
|
|
func fileHashes(meta *data.TargetFileMeta, localPath string) (metaHash []byte, localHash []byte, err error) {
|
|
|
|
|
hashFn, metaHash, err := selectHashFunction(meta)
|
2021-01-13 03:00:16 +00:00
|
|
|
if err != nil {
|
2022-02-23 17:58:07 +00:00
|
|
|
return nil, nil, err
|
2021-01-13 03:00:16 +00:00
|
|
|
}
|
|
|
|
|
|
2022-02-23 17:58:07 +00:00
|
|
|
f, err := os.Open(localPath)
|
|
|
|
|
if err != nil {
|
2025-09-09 22:13:30 +00:00
|
|
|
// If tar.gz doesn't exist but a hash file does, use the cached hash file
|
|
|
|
|
if os.IsNotExist(err) && strings.HasSuffix(localPath, ".tar.gz") {
|
|
|
|
|
cachedHash, err := readCachedHash(localPath, meta)
|
|
|
|
|
if err == nil {
|
|
|
|
|
return metaHash, cachedHash, nil
|
|
|
|
|
}
|
|
|
|
|
log.Info().Err(err).Msg("failed to read cached hash file")
|
|
|
|
|
}
|
2022-02-23 17:58:07 +00:00
|
|
|
return nil, nil, fmt.Errorf("open file for hash: %w", err)
|
2021-01-13 03:00:16 +00:00
|
|
|
}
|
2022-02-23 17:58:07 +00:00
|
|
|
defer f.Close()
|
2021-01-13 03:00:16 +00:00
|
|
|
|
2022-02-23 17:58:07 +00:00
|
|
|
if _, err := io.Copy(hashFn, f); err != nil {
|
|
|
|
|
return nil, nil, fmt.Errorf("read file for hash: %w", err)
|
2021-01-13 03:00:16 +00:00
|
|
|
}
|
2022-02-23 17:58:07 +00:00
|
|
|
return metaHash, hashFn.Sum(nil), nil
|
2021-01-13 03:00:16 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// selectHashFunction returns the first matching hash function and expected
|
2021-12-22 23:57:09 +00:00
|
|
|
// hash, otherwise returning an error if no matching hash can be found.
|
2021-02-20 19:24:44 +00:00
|
|
|
//
|
|
|
|
|
// SHA512 is preferred, and SHA256 is returned if 512 is not available.
|
2021-01-13 03:00:16 +00:00
|
|
|
func selectHashFunction(meta *data.TargetFileMeta) (hash.Hash, []byte, error) {
|
|
|
|
|
for hashName, hashVal := range meta.Hashes {
|
2021-02-20 19:24:44 +00:00
|
|
|
if hashName == "sha512" {
|
2021-01-13 03:00:16 +00:00
|
|
|
return sha512.New(), hashVal, nil
|
2021-02-20 19:24:44 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for hashName, hashVal := range meta.Hashes {
|
|
|
|
|
if hashName == "sha256" {
|
2021-01-13 03:00:16 +00:00
|
|
|
return sha256.New(), hashVal, nil
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-11-22 14:13:26 +00:00
|
|
|
return nil, nil, fmt.Errorf("no matching hash function found: %v", meta.HashAlgorithms())
|
2021-01-13 03:00:16 +00:00
|
|
|
}
|
2025-09-09 22:13:30 +00:00
|
|
|
|
|
|
|
|
// readCachedHash reads a cached hash from a .sha512 file
|
|
|
|
|
// created during packaging when the tar.gz was removed to save space.
|
|
|
|
|
func readCachedHash(tarGzPath string, meta *data.TargetFileMeta) ([]byte, error) {
|
|
|
|
|
// Check if TUF metadata has SHA512 (currently the only hash file used)
|
|
|
|
|
for hashName := range meta.Hashes {
|
|
|
|
|
if hashName == "sha512" {
|
|
|
|
|
hashPath := tarGzPath + ".sha512"
|
|
|
|
|
var hashHex []byte
|
|
|
|
|
var err error
|
|
|
|
|
if hashHex, err = os.ReadFile(hashPath); err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
return hex.DecodeString(strings.TrimSpace(string(hashHex)))
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return nil, fmt.Errorf("no cached hash file found for %s", tarGzPath)
|
|
|
|
|
}
|