package chksum import ( "bufio" "bytes" "crypto/md5" "crypto/sha1" "crypto/sha256" "crypto/sha512" "encoding/hex" "errors" "hash" "io" "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 var hashes = map[string]func() hash.Hash{ "SHA512": sha512.New, "SHA256": sha256.New, "SHA1": sha1.New, "MD5": md5.New, } 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(r io.Reader) (Checklist, error) { var checklist Checklist re := regexp.MustCompile(`(\w+) \(([^)]+)\) = (\w+)`) scanner := bufio.NewScanner(r) for scanner.Scan() { match := re.FindStringSubmatch(scanner.Text()) if len(match) != 4 { return nil, ErrParse } hash, ok := hashes[match[1]] if !ok { 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(), } checklist = append(checklist, cs) } return checklist, scanner.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 }