mirror of
https://github.com/opencloud-eu/opencloud.git
synced 2026-01-24 13:58:12 -05:00
142 lines
3.2 KiB
Go
142 lines
3.2 KiB
Go
// Copyright 2020 The OPA Authors. All rights reserved.
|
|
// Use of this source code is governed by an Apache2
|
|
// license that can be found in the LICENSE file.
|
|
|
|
package bundle
|
|
|
|
import (
|
|
"bytes"
|
|
"crypto/md5"
|
|
"crypto/sha1"
|
|
"crypto/sha256"
|
|
"crypto/sha512"
|
|
"encoding/json"
|
|
"fmt"
|
|
"hash"
|
|
"io"
|
|
"sort"
|
|
"strings"
|
|
)
|
|
|
|
// HashingAlgorithm represents a subset of hashing algorithms implemented in Go
|
|
type HashingAlgorithm string
|
|
|
|
// Supported values for HashingAlgorithm
|
|
const (
|
|
MD5 HashingAlgorithm = "MD5"
|
|
SHA1 HashingAlgorithm = "SHA-1"
|
|
SHA224 HashingAlgorithm = "SHA-224"
|
|
SHA256 HashingAlgorithm = "SHA-256"
|
|
SHA384 HashingAlgorithm = "SHA-384"
|
|
SHA512 HashingAlgorithm = "SHA-512"
|
|
SHA512224 HashingAlgorithm = "SHA-512-224"
|
|
SHA512256 HashingAlgorithm = "SHA-512-256"
|
|
)
|
|
|
|
// String returns the string representation of a HashingAlgorithm
|
|
func (alg HashingAlgorithm) String() string {
|
|
return string(alg)
|
|
}
|
|
|
|
// SignatureHasher computes a signature digest for a file with (structured or unstructured) data and policy
|
|
type SignatureHasher interface {
|
|
HashFile(v interface{}) ([]byte, error)
|
|
}
|
|
|
|
type hasher struct {
|
|
h func() hash.Hash // hash function factory
|
|
}
|
|
|
|
// NewSignatureHasher returns a signature hasher suitable for a particular hashing algorithm
|
|
func NewSignatureHasher(alg HashingAlgorithm) (SignatureHasher, error) {
|
|
h := &hasher{}
|
|
|
|
switch alg {
|
|
case MD5:
|
|
h.h = md5.New
|
|
case SHA1:
|
|
h.h = sha1.New
|
|
case SHA224:
|
|
h.h = sha256.New224
|
|
case SHA256:
|
|
h.h = sha256.New
|
|
case SHA384:
|
|
h.h = sha512.New384
|
|
case SHA512:
|
|
h.h = sha512.New
|
|
case SHA512224:
|
|
h.h = sha512.New512_224
|
|
case SHA512256:
|
|
h.h = sha512.New512_256
|
|
default:
|
|
return nil, fmt.Errorf("unsupported hashing algorithm: %s", alg)
|
|
}
|
|
|
|
return h, nil
|
|
}
|
|
|
|
// HashFile hashes the file content, JSON or binary, both in golang native format.
|
|
func (h *hasher) HashFile(v interface{}) ([]byte, error) {
|
|
hf := h.h()
|
|
walk(v, hf)
|
|
return hf.Sum(nil), nil
|
|
}
|
|
|
|
// walk hashes the file content, JSON or binary, both in golang native format.
|
|
//
|
|
// Computation for unstructured documents is a hash of the document.
|
|
//
|
|
// Computation for the types of structured JSON document is as follows:
|
|
//
|
|
// object: Hash {, then each key (in alphabetical order) and digest of the value, then comma (between items) and finally }.
|
|
//
|
|
// array: Hash [, then digest of the value, then comma (between items) and finally ].
|
|
func walk(v interface{}, h io.Writer) {
|
|
|
|
switch x := v.(type) {
|
|
case map[string]interface{}:
|
|
_, _ = h.Write([]byte("{"))
|
|
|
|
var keys []string
|
|
for k := range x {
|
|
keys = append(keys, k)
|
|
}
|
|
sort.Strings(keys)
|
|
|
|
for i, key := range keys {
|
|
if i > 0 {
|
|
_, _ = h.Write([]byte(","))
|
|
}
|
|
|
|
_, _ = h.Write(encodePrimitive(key))
|
|
_, _ = h.Write([]byte(":"))
|
|
walk(x[key], h)
|
|
}
|
|
|
|
_, _ = h.Write([]byte("}"))
|
|
case []interface{}:
|
|
_, _ = h.Write([]byte("["))
|
|
|
|
for i, e := range x {
|
|
if i > 0 {
|
|
_, _ = h.Write([]byte(","))
|
|
}
|
|
walk(e, h)
|
|
}
|
|
|
|
_, _ = h.Write([]byte("]"))
|
|
case []byte:
|
|
_, _ = h.Write(x)
|
|
default:
|
|
_, _ = h.Write(encodePrimitive(x))
|
|
}
|
|
}
|
|
|
|
func encodePrimitive(v interface{}) []byte {
|
|
var buf bytes.Buffer
|
|
encoder := json.NewEncoder(&buf)
|
|
encoder.SetEscapeHTML(false)
|
|
_ = encoder.Encode(v)
|
|
return []byte(strings.Trim(buf.String(), "\n"))
|
|
}
|