aboutsummaryrefslogtreecommitdiff
path: root/chksum/chksum.go
blob: 84b72b93f8eed0ef3fe23fd683233a5a15f7cd7b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
// Package chksum implements checksum file validator
package chksum

import (
	"bufio"
	"bytes"
	"crypto"
	"encoding/hex"
	"errors"
	"hash"
	"io"
	"log"
	"os"
	"path"
	"regexp"

	// crypto.RegisterHash
	_ "crypto/md5"
	_ "crypto/sha1"
	_ "crypto/sha256"
	_ "crypto/sha512"
)

var (
	ErrChecksum = errors.New("invalid checksum")
	ErrHashAlg  = errors.New("unknown hash algorithm")
	ErrParse    = errors.New("invalid hash entry")
)

/* checksum file format
	SHA512 (filename) = hex-encoded checksum
   or
	SHA256 (filename) = hex-encoded checksum
*/

type Checksum struct {
	FileName string
	Bytes    []byte
	Hash     hash.Hash
}

type Checklist []Checksum

// known hashes
var hashes = map[string]crypto.Hash{
	"MD5":        crypto.MD5,
	"SHA1":       crypto.SHA1,
	"SHA256":     crypto.SHA256,
	"SHA512":     crypto.SHA512,
	"SHA512/256": crypto.SHA512_256,
}

func ParseFile(fname string) (Checklist, error) {
	fd, err := os.Open(fname)
	if err != nil {
		return nil, err
	}
	defer fd.Close()
	return parse(fd)
}

func Parse(data []byte) (Checklist, error) {
	r := bytes.NewReader(data)
	return parse(r)
}

var line = regexp.MustCompile(`([^ ]+) \(([^)]+)\) = ([0-9A-Fa-f]+)`)

func parse(r io.Reader) (Checklist, error) {
	var checklist Checklist
	s := bufio.NewScanner(r)
	for s.Scan() {
		match := line.FindStringSubmatch(s.Text())
		if len(match) != 4 {
			return nil, ErrParse
		}
		hash, ok := hashes[match[1]]
		if !ok {
			log.Println(match)
			return nil, ErrHashAlg
		}
		bytes, err := hex.DecodeString(match[3])
		if err != nil {
			return nil, err
		}
		cs := Checksum{
			FileName: path.Clean(match[2]),
			Bytes:    bytes,
			Hash:     hash.New(),
		}
		checklist = append(checklist, cs)
	}
	return checklist, s.Err()
}

func (c Checksum) Check() error {
	fd, err := os.Open(c.FileName)
	if err != nil {
		return err
	}
	defer fd.Close()
	io.Copy(c.Hash, fd)
	if !bytes.Equal(c.Hash.Sum(nil), c.Bytes) {
		return ErrChecksum
	}
	return nil
}

func (c Checklist) Check() error {
	for _, cs := range c {
		if err := cs.Check(); err != nil {
			return err
		}
	}
	return nil
}