github.com/hashicorp/packer@v1.14.3/packer/plugin-getter/checksum.go (about)

     1  // Copyright (c) HashiCorp, Inc.
     2  // SPDX-License-Identifier: BUSL-1.1
     3  
     4  package plugingetter
     5  
     6  import (
     7  	"bytes"
     8  	"encoding/hex"
     9  	"fmt"
    10  	"hash"
    11  	"io"
    12  	"os"
    13  	"strings"
    14  )
    15  
    16  // A ChecksumError is returned when a checksum differs
    17  type ChecksumError struct {
    18  	Hash     hash.Hash
    19  	Actual   []byte
    20  	Expected []byte
    21  	File     string
    22  }
    23  
    24  func (cerr *ChecksumError) Error() string {
    25  	if cerr == nil {
    26  		return "<nil>"
    27  	}
    28  	return fmt.Sprintf(
    29  		"Checksums (%T) did not match.\nExpected: %s\nGot     : %s\n",
    30  		cerr.Hash, // ex: *sha256.digest
    31  		hex.EncodeToString(cerr.Expected),
    32  		hex.EncodeToString(cerr.Actual),
    33  	)
    34  }
    35  
    36  type Checksum []byte
    37  
    38  func (c Checksum) String() string { return hex.EncodeToString(c) }
    39  
    40  type FileChecksum struct {
    41  	Filename string
    42  	Expected Checksum
    43  	Checksummer
    44  }
    45  
    46  type Checksummer struct {
    47  	// Something like md5 or sha256
    48  	Type string
    49  	// Hash function
    50  	hash.Hash
    51  }
    52  
    53  func (c *Checksummer) FileExt() string {
    54  	return "_" + strings.ToUpper(c.Type) + "SUM"
    55  }
    56  
    57  // GetCacheChecksumOfFile will extract the checksum from file `filePath + c.FileExt()`.
    58  // It expects the checksum file to only contains the checksum and nothing else.
    59  func (c *Checksummer) GetCacheChecksumOfFile(filePath string) ([]byte, error) {
    60  	checksumFile := filePath + c.FileExt()
    61  
    62  	f, err := os.Open(checksumFile)
    63  	if err != nil {
    64  		return nil, err
    65  	}
    66  	defer f.Close()
    67  	return c.ParseChecksum(f)
    68  }
    69  
    70  // ParseChecksum expects the checksum reader to only contain the checksum and
    71  // nothing else.
    72  func (c *Checksummer) ParseChecksum(f io.Reader) (Checksum, error) {
    73  	res := make([]byte, c.Hash.Size())
    74  	_, err := hex.NewDecoder(f).Read(res)
    75  	if err == io.EOF {
    76  		err = nil
    77  	}
    78  	return res, err
    79  }
    80  
    81  // ChecksumFile compares the expected checksum to the checksum of the file in
    82  // filePath using the hash function.
    83  func (c *Checksummer) ChecksumFile(expected []byte, filePath string) error {
    84  	f, err := os.Open(filePath)
    85  	if err != nil {
    86  		return fmt.Errorf("Checksum: failed to open file for checksum: %s", err)
    87  	}
    88  	defer f.Close()
    89  	err = c.Checksum(expected, f)
    90  	if cerr, ok := err.(*ChecksumError); ok {
    91  		cerr.File = filePath
    92  	}
    93  	return err
    94  }
    95  
    96  func (c *Checksummer) Sum(f io.Reader) ([]byte, error) {
    97  	c.Hash.Reset()
    98  	if _, err := io.Copy(c.Hash, f); err != nil {
    99  		return nil, fmt.Errorf("Failed to hash: %s", err)
   100  	}
   101  	return c.Hash.Sum(nil), nil
   102  }
   103  
   104  func (c *Checksummer) Checksum(expected []byte, f io.Reader) error {
   105  	actual, err := c.Sum(f)
   106  	if err != nil {
   107  		return err
   108  	}
   109  
   110  	if !bytes.Equal(actual, expected) {
   111  		return &ChecksumError{
   112  			Hash:     c.Hash,
   113  			Actual:   actual,
   114  			Expected: expected,
   115  		}
   116  	}
   117  
   118  	return nil
   119  }