github.com/u-root/u-root@v7.0.1-0.20200915234505-ad7babab0a8e+incompatible/cmds/exp/validate/validate.go (about)

     1  // Copyright 2016-2017 the u-root Authors. All rights reserved
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  // This program validates a file by verifying a checksum and a signature file.
     6  //
     7  // Synopsis:
     8  //     validate [OPTIONS...] FILE PUBLIC_KEY_FILE
     9  //
    10  // Description:
    11  //     Return code: 0-OK, 1-Any error, 2-Bad signature, 3-Bad checksum
    12  //
    13  // Options:
    14  //     -a:        signature is ASCII armored
    15  //     -i FILE:   checksum file
    16  //     -alg FILE: algorithms to check
    17  //     -v:        verbose
    18  package main
    19  
    20  import (
    21  	"crypto"
    22  	"flag"
    23  	"fmt"
    24  	"io/ioutil"
    25  	"log"
    26  	"os"
    27  	"strings"
    28  
    29  	_ "crypto/md5"
    30  	_ "crypto/sha1"
    31  	_ "crypto/sha256"
    32  	_ "crypto/sha512"
    33  
    34  	_ "golang.org/x/crypto/md4"
    35  	_ "golang.org/x/crypto/openpgp"
    36  	_ "golang.org/x/crypto/ripemd160"
    37  	_ "golang.org/x/crypto/sha3"
    38  )
    39  
    40  var (
    41  	algs = map[string]crypto.Hash{
    42  		"MD4":       crypto.MD4,
    43  		"MD5":       crypto.MD5,
    44  		"SHA1":      crypto.SHA1,
    45  		"SHA224":    crypto.SHA224,
    46  		"SHA256":    crypto.SHA256,
    47  		"SHA384":    crypto.SHA384,
    48  		"SHA512":    crypto.SHA512,
    49  		"RIPEMD160": crypto.RIPEMD160,
    50  		"SHA3_224":  crypto.SHA3_224,
    51  		"SHA3_256":  crypto.SHA3_256,
    52  		"SHA3_384":  crypto.SHA3_384,
    53  		"SHA3_512":  crypto.SHA3_512,
    54  	}
    55  
    56  	armored    = flag.Bool("a", false, "signature is ASCII armored")
    57  	sumfile    = flag.String("i", "", "checksum file")
    58  	alg        = flag.String("alg", "", "algorithms to check")
    59  	verbose    = flag.Bool("v", false, "verbose")
    60  	debug      = func(string, ...interface{}) {}
    61  	try, tried []string
    62  )
    63  
    64  func init() {
    65  	for v := range algs {
    66  		try = append(try, v)
    67  	}
    68  }
    69  
    70  func one(n string, b []byte, sig string) bool {
    71  	if !algs[n].Available() {
    72  		log.Printf("Hash %v did not get linked in", n)
    73  		return false
    74  	}
    75  	debug("Check alg %v", n)
    76  	checker := algs[n].New()
    77  	checker.Write(b)
    78  	r := checker.Sum([]byte{})
    79  	debug("sum is %d bytes %v\n", len(r), r)
    80  	tried = append(tried, n)
    81  
    82  	// There has to be a better way.
    83  	sumText := ""
    84  	for _, v := range r {
    85  		sumText += fmt.Sprintf("%02x", v)
    86  	}
    87  	debug("Compare to %v", sumText)
    88  	if sumText == sig {
    89  		return true
    90  	}
    91  	return false
    92  }
    93  
    94  func sign(n string, k crypto.PrivateKey, b []byte, sig string) bool {
    95  	if !algs[n].Available() {
    96  		log.Printf("Hash %v did not get linked in", n)
    97  		return false
    98  	}
    99  	debug("Check alg %v", n)
   100  	b, err := k.(crypto.Signer).Sign(nil, b, algs[n])
   101  	log.Printf("For %v we get %v %v", n, b, err)
   102  	return false
   103  }
   104  
   105  func main() {
   106  	flag.Parse()
   107  	if flag.NArg() < 2 {
   108  		log.Fatalf("Need at least a file to be validated and one public key")
   109  	}
   110  
   111  	if *verbose {
   112  		debug = log.Printf
   113  	}
   114  
   115  	v, f := flag.Args()[0], flag.Args()[1]
   116  
   117  	sigData, err := ioutil.ReadFile(v)
   118  	if err != nil {
   119  		log.Fatalf("%v", err)
   120  	}
   121  
   122  	if *alg != "" {
   123  		try = []string{}
   124  		for _, v := range strings.Split(*alg, ",") {
   125  			try = append(try, v)
   126  		}
   127  	}
   128  
   129  	log.Printf("Try %v", try)
   130  
   131  	sig := strings.Split(string(sigData), " ")
   132  
   133  	debug("Signature is %v len %v", sig[0], len(sig[0]))
   134  
   135  	b, err := ioutil.ReadFile(f)
   136  	if err != nil {
   137  		log.Fatalf("%s: %v", f, err)
   138  	}
   139  
   140  	if *sumfile != "" {
   141  		sum, err := ioutil.ReadFile(*sumfile)
   142  		if err != nil {
   143  			log.Fatalf("Can't read sumfile: %v", *sumfile)
   144  		}
   145  		for i := range try {
   146  			if sign(try[i], crypto.PrivateKey(sum), b, sig[0]) {
   147  				fmt.Printf("%v\n", try[i])
   148  				os.Exit(0)
   149  			}
   150  			debug("not ok")
   151  		}
   152  
   153  	} else {
   154  		for i := range try {
   155  			debug("Check %v", try[i])
   156  			if one(try[i], b, sig[0]) {
   157  				fmt.Printf("%v\n", try[i])
   158  				os.Exit(0)
   159  			}
   160  			// Sometimes it's not a file in the standard format, but some binary thing.
   161  			// Check that too.
   162  			if one(try[i], b, string(sigData)) {
   163  				fmt.Printf("%v\n", try[i])
   164  				os.Exit(0)
   165  			}
   166  			debug("not ok")
   167  		}
   168  	}
   169  	log.Fatalf("No matches found for %v", tried)
   170  }