aboutsummaryrefslogtreecommitdiff
path: root/chksum/chksum.go
blob: 589c8046184dc8df28e5ce4307a7088f1bc11b59 (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
117
118
// Package chksum implements checksum file validator
package chksum

import (
	"bufio"
	"bytes"
	"crypto/md5"
	"crypto/sha1"
	"crypto/sha256"
	"crypto/sha512"
	"encoding/hex"
	"errors"
	"hash"
	"io"
	"log"
	"os"
	"path"
	"regexp"
)

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

type hashDef struct {
	newHash func() hash.Hash
	decode  func(string) ([]byte, error)
}

var hashes = map[string]hashDef{
	"SHA512/256": {sha512.New512_256, hex.DecodeString},
	"SHA512":     {sha512.New, hex.DecodeString},
	"SHA256":     {sha256.New, hex.DecodeString},
	"SHA1":       {sha1.New, hex.DecodeString},
	"MD5":        {md5.New, hex.DecodeString},
	"SIZE":       {NewSize, ParseSize},
}

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 := hash.decode(match[3])
		if err != nil {
			return nil, err
		}
		cs := Checksum{
			FileName: path.Clean(match[2]),
			Bytes:    bytes,
			Hash:     hash.newHash(),
		}
		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
}