package zsig import ( "bufio" "bytes" "crypto/sha512" "encoding/hex" "errors" "fmt" "io" "strconv" "strings" "time" ) const ( Alg = "SHA512/256" BlockSize = 65536 ) type ZHeader struct { Date time.Time KeyFile string Alg string BlockSize int64 Sums [][]byte } func (h ZHeader) MarshalText() ([]byte, error) { buf := new(bytes.Buffer) fmt.Fprintf(buf, "date=%v\n", h.Date.Format(time.RFC3339)) fmt.Fprintf(buf, "key=%v\n", h.KeyFile) fmt.Fprintf(buf, "algorithm=%v\n", h.Alg) fmt.Fprintf(buf, "blocksize=%v\n\n", h.BlockSize) for _, sum := range h.Sums { fmt.Fprintf(buf, "%x\n", sum) } return buf.Bytes(), nil } func ParseBytes(data []byte) (ZHeader, error) { r := bytes.NewReader(data) return Parse(r) } // TODO func (z *ZHeader) UnmarshalText(data []byte) error { return nil } func Parse(r io.Reader) (ZHeader, error) { var h ZHeader s := bufio.NewScanner(r) for s.Scan() { line := s.Text() switch { case strings.HasPrefix(line, "date="): t, err := time.Parse(time.RFC3339, line[5:]) if err != nil { return ZHeader{}, err } h.Date = t case strings.HasPrefix(line, "key="): h.KeyFile = line[4:] case strings.HasPrefix(line, "algorithm="): h.Alg = line[10:] case strings.HasPrefix(line, "blocksize="): i, err := strconv.ParseInt(line[10:], 10, 64) if err != nil { return ZHeader{}, err } h.BlockSize = i case line == "": for s.Scan() { line = s.Text() sum, err := hex.DecodeString(line) if err != nil { return ZHeader{}, err } h.Sums = append(h.Sums, sum) } } } return h, s.Err() } func (z ZHeader) Verify(r io.Reader) error { if z.Alg != Alg { return errors.New("invalid hash algorithm") } h := sha512.New512_256() // from z.Alg blockCount := len(z.Sums) for _, sum := range z.Sums { h.Reset() n, err := io.CopyN(h, r, z.BlockSize) if n == 0 && err == io.EOF { break } if err != nil && err != io.EOF { return err } if !bytes.Equal(sum, h.Sum(nil)) { return errors.New("sum mismatch") } blockCount-- } if blockCount != 0 { return errors.New("count mismatch") } return nil } func NewHeader(r io.Reader) (*ZHeader, error) { hdr := &ZHeader{ Date: time.Now(), Alg: Alg, BlockSize: BlockSize, } h := sha512.New512_256() for { h.Reset() n, err := io.CopyN(h, r, hdr.BlockSize) if n == 0 && err == io.EOF { break } if err != nil && err != io.EOF { return nil, err } hdr.Sums = append(hdr.Sums, h.Sum(nil)) } return hdr, nil }