1package fdh 2 3import ( 4 "crypto" 5 "hash" 6 "math" 7 "strconv" 8 "sync" 9) 10 11// digest represents the partial evaluation of a Full Domain Hash checksum. 12type 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. 21func 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), 36 } 37 d.Reset() 38 return &d 39} 40 41// Reset resets the Hash to its initial state. 42func (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. 50func (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) 55func (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 61func (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 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 82func (d *digest) Sum(in []byte) []byte { 83 hash := d.checkSum() 84 return append(in, hash[:]...) 85} 86 87func (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}) 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. 101func Sum(h crypto.Hash, bitlen int, message []byte) []byte { 102 hash := New(h, bitlen) 103 hash.Write(message) 104 return hash.Sum(nil) 105} 106