1package glacier
2
3import (
4	"crypto/sha256"
5	"io"
6
7	"github.com/aws/aws-sdk-go/internal/sdkio"
8)
9
10const bufsize = 1024 * 1024
11
12// Hash contains information about the tree-hash and linear hash of a
13// Glacier payload. This structure is generated by ComputeHashes().
14type Hash struct {
15	TreeHash   []byte
16	LinearHash []byte
17}
18
19// ComputeHashes computes the tree-hash and linear hash of a seekable reader r.
20//
21// See http://docs.aws.amazon.com/amazonglacier/latest/dev/checksum-calculations.html for more information.
22func ComputeHashes(r io.ReadSeeker) Hash {
23	start, _ := r.Seek(0, sdkio.SeekCurrent) // Read the whole stream
24	defer r.Seek(start, sdkio.SeekStart)     // Rewind stream at end
25
26	buf := make([]byte, bufsize)
27	hashes := [][]byte{}
28	hsh := sha256.New()
29
30	for {
31		// Build leaf nodes in 1MB chunks
32		n, err := io.ReadAtLeast(r, buf, bufsize)
33		if n == 0 {
34			break
35		}
36
37		tmpHash := sha256.Sum256(buf[:n])
38		hashes = append(hashes, tmpHash[:])
39		hsh.Write(buf[:n]) // Track linear hash while we're at it
40
41		if err != nil {
42			break // This is the last chunk
43		}
44	}
45
46	return Hash{
47		LinearHash: hsh.Sum(nil),
48		TreeHash:   ComputeTreeHash(hashes),
49	}
50}
51
52// ComputeTreeHash builds a tree hash root node given a slice of
53// hashes. Glacier tree hash to be derived from SHA256 hashes of 1MB
54// chucks of the data.
55//
56// See http://docs.aws.amazon.com/amazonglacier/latest/dev/checksum-calculations.html for more information.
57func ComputeTreeHash(hashes [][]byte) []byte {
58	if hashes == nil || len(hashes) == 0 {
59		return nil
60	}
61
62	for len(hashes) > 1 {
63		tmpHashes := [][]byte{}
64
65		for i := 0; i < len(hashes); i += 2 {
66			if i+1 <= len(hashes)-1 {
67				tmpHash := append(append([]byte{}, hashes[i]...), hashes[i+1]...)
68				tmpSum := sha256.Sum256(tmpHash)
69				tmpHashes = append(tmpHashes, tmpSum[:])
70			} else {
71				tmpHashes = append(tmpHashes, hashes[i])
72			}
73		}
74
75		hashes = tmpHashes
76	}
77
78	return hashes[0]
79}
80