Issues (26)

Severity
1
package fdh
2
3
import (
4
	"crypto"
5
	"hash"
6
	"math"
7
	"strconv"
8
	"sync"
9
)
10
11
// digest represents the partial evaluation of a Full Domain Hash checksum.
12
type digest struct {
13
	base  crypto.Hash
14
	bits  int
15
	parts []hash.Hash
16
	final bool // Unlike many hash functions Full Domain Hashes are "finalized" after a call to Sum() and cannot be furthur written to.
17
}
18
19
// New returns a hash.Hash for computing a Full Domain Hash checksum, given a base hash function and a target bit length.
20
// It will panic if the bitlen is not a multiple of the hash length or if the hash library is not imported.
21
func New(h crypto.Hash, bitlen int) hash.Hash {
22
	if !h.Available() {
23
		panic("fdh: requested hash function #" + strconv.Itoa(int(h)) + " is unavailable. Make sure your hash function is proprely imported.")
24
	} else if bitlen%8 != 0 {
25
		panic("fdh: hash digest size should be a multiple of 8")
26
	} else if bitlen <= 0 {
27
		panic("fdh: hash digest size cannot be less or equal to zero")
28
	}
29
30
	numparts := int(math.Ceil(float64(bitlen) / float64((h.Size() * 8))))
31
32
	d := digest{
33
		base:  h,
34
		bits:  bitlen,
35
		parts: make([]hash.Hash, numparts, numparts),
0 ignored issues
show
should use make([]hash.Hash, numparts) instead (S1019)
Loading history...
36
	}
37
	d.Reset()
38
	return &d
39
}
40
41
// Reset resets the Hash to its initial state.
42
func (d *digest) Reset() {
43
	for i := range d.parts {
44
		d.parts[i] = d.base.New()
45
	}
46
	d.final = false
47
}
48
49
// BlockSize returns the hash's underlying block size.
50
func (d *digest) BlockSize() int {
51
	return d.parts[0].BlockSize()
52
}
53
54
// Size returns the number of bytes Sum will return. This will be the same as the bitlen (with conversion from bits to bytes)
55
func (d *digest) Size() int {
56
	return d.bits / 8
57
}
58
59
// Add more data to the running hash.
60
// Once Sum() is called the hash is finalized and writing to the hash will panic
61
func (d *digest) Write(p []byte) (int, error) {
62
	if d.final {
63
		panic("Cannot write to Full Domain Hash after a call has been made to Sum() and the hash has been finalized.")
64
	}
65
66
	// Write to each component hash asyncronously
67
	var wg sync.WaitGroup
68
	for i, h := range d.parts {
69
		wg.Add(1)
70
		go func(i int, h hash.Hash) {
71
			h.Write(p) // Hashes in crypto library don't return errors
0 ignored issues
show
error return value not checked (h.Write(p) // Hashes in crypto library don't return errors)
Loading history...
72
			wg.Done()
73
		}(i, h)
74
	}
75
	wg.Wait()
76
77
	return len(p), nil
78
}
79
80
// Sum appends the current hash to b and returns the resulting slice.
81
// Once Sum is called, the hash is finalized and can no longer be written to
82
func (d *digest) Sum(in []byte) []byte {
83
	hash := d.checkSum()
84
	return append(in, hash[:]...)
85
}
86
87
func (d *digest) checkSum() []byte {
88
	var sum []byte
89
	for i, h := range d.parts {
90
		if !d.final {
91
			finalByte := byte(i)
92
			h.Write([]byte{finalByte})
0 ignored issues
show
error return value not checked (h.Write([]bytefinalByte))
Loading history...
93
		}
94
		sum = append(sum, h.Sum(nil)...)
95
	}
96
	d.final = true
97
	return sum[:d.bits/8]
98
}
99
100
// Sum returns the the Full Domain Hash checksum of the data.
101
func Sum(h crypto.Hash, bitlen int, message []byte) []byte {
102
	hash := New(h, bitlen)
103
	hash.Write(message)
0 ignored issues
show
error return value not checked (hash.Write(message))
Loading history...
104
	return hash.Sum(nil)
105
}
106