git.sr.ht/~pingoo/stdx@v0.0.0-20240218134121-094174641f6e/tools/hasher/main.go (about)

     1  package main
     2  
     3  import (
     4  	"crypto/sha512"
     5  	"encoding/hex"
     6  	"fmt"
     7  	"hash"
     8  	"io"
     9  	"os"
    10  
    11  	"crypto/sha256"
    12  
    13  	"git.sr.ht/~pingoo/stdx/cobra"
    14  	"github.com/zeebo/blake3"
    15  	"golang.org/x/crypto/blake2b"
    16  )
    17  
    18  const (
    19  	version = "0.1.0"
    20  
    21  	AlgorithmSha256  = "sha256"
    22  	AlgorithmSha512  = "sha512"
    23  	AlgorithmBlake3  = "blake3"
    24  	AlgorithmBlake2b = "blake2b"
    25  )
    26  
    27  var (
    28  	algorithmArg string
    29  	stdinArg     bool
    30  )
    31  
    32  func init() {
    33  	rootCmd.Flags().StringVarP(&algorithmArg, "algorithm", "a", AlgorithmSha256, "Algorithm to use. Valid values are [sha256, sha512, blake3, blake2b]")
    34  	rootCmd.Flags().BoolVar(&stdinArg, "stdin", false, "Read data from stdin")
    35  }
    36  
    37  func main() {
    38  	err := rootCmd.Execute()
    39  	if err != nil {
    40  		fmt.Fprintln(os.Stdout, err.Error())
    41  		os.Exit(1)
    42  	}
    43  }
    44  
    45  var rootCmd = &cobra.Command{
    46  	Use:     "hash [flags] [files...]",
    47  	Short:   "Hash and verify files",
    48  	Version: version,
    49  	// SilenceUsage:  true,
    50  	SilenceErrors: true,
    51  	RunE: func(cmd *cobra.Command, args []string) (err error) {
    52  		if stdinArg {
    53  			var hash []byte
    54  
    55  			hash, err = hashData(os.Stdin, algorithmArg)
    56  			if err != nil {
    57  				return
    58  			}
    59  			fmt.Println(hex.EncodeToString(hash))
    60  
    61  			return
    62  		}
    63  
    64  		if len(args) < 1 {
    65  			cmd.Help()
    66  			return
    67  		}
    68  
    69  		for _, file := range args {
    70  			var hash []byte
    71  
    72  			hash, err = hashFile(file, algorithmArg)
    73  			if err != nil {
    74  				return
    75  			}
    76  			fmt.Printf("%s  %s\n", hex.EncodeToString(hash), file)
    77  		}
    78  
    79  		return
    80  	},
    81  }
    82  
    83  func hashFile(filePath, algorithm string) (outputHash []byte, err error) {
    84  	var file *os.File
    85  
    86  	file, err = os.Open(filePath)
    87  	if err != nil {
    88  		err = fmt.Errorf("opening file %s: %w", filePath, err)
    89  		return
    90  	}
    91  	defer file.Close()
    92  
    93  	outputHash, err = hashData(file, algorithm)
    94  	if err != nil {
    95  		err = fmt.Errorf("file %s: %w", filePath, err)
    96  		return
    97  	}
    98  
    99  	return
   100  }
   101  
   102  func hashData(data io.Reader, algorithm string) (outputHash []byte, err error) {
   103  	var hasher hash.Hash
   104  
   105  	switch algorithm {
   106  	case AlgorithmSha256:
   107  		hasher = sha256.New()
   108  	case AlgorithmSha512:
   109  		hasher = sha512.New()
   110  	case AlgorithmBlake2b:
   111  		hasher, err = blake2b.New(32, nil)
   112  		if err != nil {
   113  			err = fmt.Errorf("error when initializing blake2b hashing function: %w", err)
   114  			return
   115  		}
   116  	case AlgorithmBlake3:
   117  		hasher = blake3.New()
   118  
   119  	default:
   120  		err = fmt.Errorf("%s is not a supported hashing algorithm", algorithm)
   121  		return
   122  	}
   123  
   124  	_, err = io.Copy(hasher, data)
   125  	if err != nil {
   126  		err = fmt.Errorf("error when hashing: %w", err)
   127  		return
   128  	}
   129  
   130  	outputHash = hasher.Sum(nil)
   131  
   132  	return
   133  }