safing-portbase/crypto/hash/hash.go
2018-08-13 14:05:58 +02:00

133 lines
2.9 KiB
Go

// Copyright Safing ICS Technologies GmbH. Use of this source code is governed by the AGPL license that can be found in the LICENSE file.
package hash
import (
"bytes"
"encoding/base64"
"encoding/hex"
"errors"
"fmt"
"io"
"github.com/Safing/safing-core/formats/varint"
)
type Hash struct {
Algorithm Algorithm
Sum []byte
}
func FromBytes(bytes []byte) (*Hash, int, error) {
hash := &Hash{}
alg, read, err := varint.Unpack8(bytes)
hash.Algorithm = Algorithm(alg)
if err != nil {
return nil, 0, errors.New(fmt.Sprintf("hash: failed to parse: %s", err))
}
// TODO: check if length is correct
hash.Sum = bytes[read:]
return hash, 0, nil
}
func (h *Hash) Bytes() []byte {
return append(varint.Pack8(uint8(h.Algorithm)), h.Sum...)
}
func FromSafe64(s string) (*Hash, error) {
bytes, err := base64.RawURLEncoding.DecodeString(s)
if err != nil {
return nil, errors.New(fmt.Sprintf("hash: failed to parse: %s", err))
}
hash, _, err := FromBytes(bytes)
return hash, err
}
func (h *Hash) Safe64() string {
return base64.RawURLEncoding.EncodeToString(h.Bytes())
}
func FromHex(s string) (*Hash, error) {
bytes, err := hex.DecodeString(s)
if err != nil {
return nil, errors.New(fmt.Sprintf("hash: failed to parse: %s", err))
}
hash, _, err := FromBytes(bytes)
return hash, err
}
func (h *Hash) Hex() string {
return hex.EncodeToString(h.Bytes())
}
func (h *Hash) Equal(other *Hash) bool {
if h.Algorithm != other.Algorithm {
return false
}
return bytes.Equal(h.Sum, other.Sum)
}
func Sum(data []byte, alg Algorithm) *Hash {
hasher := alg.New()
hasher.Write(data)
return &Hash{
Algorithm: alg,
Sum: hasher.Sum(nil),
}
}
func SumString(data string, alg Algorithm) *Hash {
hasher := alg.New()
io.WriteString(hasher, data)
return &Hash{
Algorithm: alg,
Sum: hasher.Sum(nil),
}
}
func SumReader(reader io.Reader, alg Algorithm) (*Hash, error) {
hasher := alg.New()
_, err := io.Copy(hasher, reader)
if err != nil {
return nil, err
}
return &Hash{
Algorithm: alg,
Sum: hasher.Sum(nil),
}, nil
}
func SumAndCompare(data []byte, other Hash) (bool, *Hash) {
newHash := Sum(data, other.Algorithm)
return other.Equal(newHash), newHash
}
func SumReaderAndCompare(reader io.Reader, other Hash) (bool, *Hash, error) {
newHash, err := SumReader(reader, other.Algorithm)
if err != nil {
return false, nil, err
}
return other.Equal(newHash), newHash, nil
}
func RecommendedAlg(strengthInBits uint16) Algorithm {
strengthInBytes := uint8(strengthInBits / 8)
if strengthInBits%8 != 0 {
strengthInBytes++
}
if strengthInBytes == 0 {
strengthInBytes = uint8(0xFF)
}
chosenAlg := orderedByRecommendation[0]
for _, alg := range orderedByRecommendation {
strength := alg.SecurityStrength()
if strength < strengthInBytes {
break
}
chosenAlg = alg
if strength == strengthInBytes {
break
}
}
return chosenAlg
}