// 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 }