aboutsummaryrefslogtreecommitdiff
path: root/zsig/header.go
blob: bc16cb1a302c507a27c20cff214ead9a784d7e34 (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
119
120
121
122
123
124
125
126
127
128
129
130
131
132
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
}