verify.go 1.7 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758
  1. package update
  2. import (
  3. "crypto/sha256"
  4. "crypto/subtle"
  5. "encoding/hex"
  6. "fmt"
  7. "io"
  8. "os"
  9. "strings"
  10. )
  11. // ParseSHA256SUMS scans the contents of a GNU-style sha256sums file (one
  12. // "<hash> <filename>" line per entry) and returns the hex digest recorded for
  13. // filename. Both the binary ("hash name") and text ("hash *name") forms are
  14. // accepted. An empty content or a missing entry yields an error.
  15. func ParseSHA256SUMS(content, filename string) (string, error) {
  16. for _, line := range strings.Split(strings.ReplaceAll(content, "\r\n", "\n"), "\n") {
  17. line = strings.TrimSpace(line)
  18. if line == "" || strings.HasPrefix(line, "#") {
  19. continue
  20. }
  21. // Format: "<64-hex> [ *]name". Split on the first run of whitespace.
  22. fields := strings.Fields(line)
  23. if len(fields) < 2 {
  24. continue
  25. }
  26. hash := fields[0]
  27. name := strings.TrimPrefix(strings.Join(fields[1:], " "), "*")
  28. if name == filename {
  29. if len(hash) != 64 {
  30. return "", fmt.Errorf("update: malformed sha256 %q for %s", hash, filename)
  31. }
  32. return strings.ToLower(hash), nil
  33. }
  34. }
  35. return "", fmt.Errorf("update: %s not found in SHA256SUMS", filename)
  36. }
  37. // VerifyFileSHA256 hashes the file at path and reports whether its hex digest
  38. // matches expectedHex (constant-time comparison).
  39. func VerifyFileSHA256(path, expectedHex string) (bool, error) {
  40. f, err := os.Open(path)
  41. if err != nil {
  42. return false, err
  43. }
  44. defer f.Close()
  45. h := sha256.New()
  46. if _, err := io.Copy(h, f); err != nil {
  47. return false, err
  48. }
  49. actual := h.Sum(nil)
  50. want, err := hex.DecodeString(strings.TrimSpace(expectedHex))
  51. if err != nil {
  52. return false, fmt.Errorf("update: invalid expected hash: %w", err)
  53. }
  54. return subtle.ConstantTimeCompare(actual, want) == 1, nil
  55. }