summaryrefslogtreecommitdiff
path: root/crypto/hashes.go
blob: 04db757f394a866234ff139fa5fcae06ac193af1 (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
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
// Copyright (C) 2019-2023 Algorand, Inc.
// This file is part of go-algorand
//
// go-algorand is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// go-algorand is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with go-algorand.  If not, see <https://www.gnu.org/licenses/>.

package crypto

import (
	"crypto/sha256"
	"crypto/sha512"
	"errors"
	"fmt"
	"hash"

	"github.com/algorand/go-algorand/protocol"
	"github.com/algorand/go-sumhash"
)

// HashType represents different hash functions
type HashType uint16

// Validate verifies that the hash type is in a valid range.
func (h HashType) Validate() error {
	if h >= MaxHashType {
		return protocol.ErrInvalidObject
	}
	return nil
}

// types of hashes
const (
	Sha512_256 HashType = iota
	Sumhash
	Sha256
	MaxHashType
)

// MaxHashDigestSize is used to bound the max digest size. it is important to change it if a hash with
// a longer output is introduced.
const MaxHashDigestSize = SumhashDigestSize

// size of each hash
const (
	Sha512_256Size    = sha512.Size256
	SumhashDigestSize = sumhash.Sumhash512DigestSize
	Sha256Size        = sha256.Size
)

// HashFactory is responsible for generating new hashes accordingly to the type it stores.
//
//msgp:postunmarshalcheck HashFactory Validate
type HashFactory struct {
	_struct struct{} `codec:",omitempty,omitemptyarray"`

	HashType HashType `codec:"t"`
}

var errUnknownHash = errors.New("unknown hash type")

func (h HashType) String() string {
	switch h {
	case Sha512_256:
		return "sha512_256"
	case Sumhash:
		return "sumhash"
	case Sha256:
		return "sha256"
	default:
		return ""
	}
}

// UnmarshalHashType decodes a string into the HashType enum
func UnmarshalHashType(s string) (HashType, error) {
	switch s {
	case "sha512_256":
		return Sha512_256, nil
	case "sumhash":
		return Sumhash, nil
	case "sha256":
		return Sha256, nil
	default:
		return 0, fmt.Errorf("HashType not supported: %s", s)
	}
}

// NewHash generates a new hash.Hash to use.
func (z HashFactory) NewHash() hash.Hash {
	switch z.HashType {

	case Sha512_256:
		return sha512.New512_256()
	case Sumhash:
		return sumhash.New512(nil)
	case Sha256:
		return sha256.New()
	// This shouldn't be reached, when creating a new hash, one would know the type of hash they wanted,
	// in addition to that, unmarshalling of the hashFactory verifies the HashType of the factory.
	default:
		return invalidHash{}
	}
}

// Validate states whether the HashFactory is valid, and is safe to use.
func (z *HashFactory) Validate() error {
	return z.HashType.Validate()
}

// GenericHashObj Makes it easier to sum using hash interface and Hashable interface
func GenericHashObj[H Hashable](hsh hash.Hash, h H) []byte {
	rep := HashRep(h)
	return hashBytes(hsh, rep)
}

func hashBytes(hash hash.Hash, m []byte) []byte {
	hash.Reset()
	hash.Write(m)
	outhash := hash.Sum(nil)
	return outhash
}

// InvalidHash is used to identify errors on the factory.
// this function will return nil slice
type invalidHash struct {
}

// Write writes bytes into the hash function. this function will return an error
func (h invalidHash) Write(p []byte) (n int, err error) {
	return 0, errUnknownHash
}

// Sum returns an empty slice since this is an empty hash function
func (h invalidHash) Sum(b []byte) []byte {
	return nil
}

// Reset this function has no state so it is empty
func (h invalidHash) Reset() {
}

// Size the current size of the function is always 0
func (h invalidHash) Size() int {
	return 0
}

// BlockSize returns zero since this is an empty hash function
func (h invalidHash) BlockSize() int {
	return 0
}