aboutsummaryrefslogtreecommitdiff
path: root/objects/id.go
blob: 047fadaa764aa80f18f1a727a453858bb1ad980d (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
package objects

import (
	"bytes"
	"encoding/hex"
	"errors"
	"fmt"
	"golang.org/x/crypto/sha3"
	"hash"
	"io"
	"strings"
)

type ObjectIdAlgo string

const (
	OIdAlgoSHA3_256 ObjectIdAlgo = "sha3-256"
	OIdAlgoDefault               = OIdAlgoSHA3_256
)

var allowedAlgos = map[ObjectIdAlgo]struct{}{OIdAlgoSHA3_256: {}}

func (algo ObjectIdAlgo) checkAlgo() bool {
	_, ok := allowedAlgos[algo]
	return ok
}

func (algo ObjectIdAlgo) sumLength() int {
	if algo != OIdAlgoSHA3_256 {
		panic("Only sha3-256 is implemented!")
	}

	return 32
}

// ObjectId identifies an object using a cryptogrpahic hash
type ObjectId struct {
	Algo ObjectIdAlgo
	Sum  []byte
}

func (oid ObjectId) Wellformed() bool {
	return oid.Algo.checkAlgo() && len(oid.Sum) == oid.Algo.sumLength()
}

func (oid ObjectId) String() string {
	return fmt.Sprintf("%s:%s", oid.Algo, hex.EncodeToString(oid.Sum))
}

func ParseObjectId(s string) (oid ObjectId, err error) {
	parts := strings.SplitN(s, ":", 2)
	if len(parts) != 2 {
		err = errors.New("Invalid ObjectId format")
		return
	}

	oid.Algo = ObjectIdAlgo(parts[0])

	oid.Sum, err = hex.DecodeString(parts[1])
	if err != nil {
		return
	}

	if !oid.Wellformed() {
		err = errors.New("Object ID is malformed")
	}

	return
}

// Set implements flag.Value for ObjectId
func (oid *ObjectId) Set(s string) (err error) {
	*oid, err = ParseObjectId(s)
	return
}

func MustParseObjectId(s string) ObjectId {
	id, err := ParseObjectId(s)
	if err != nil {
		panic(err)
	}
	return id
}

func (a ObjectId) Equals(b ObjectId) bool {
	return a.Algo == b.Algo && bytes.Equal(a.Sum, b.Sum)
}

// ObjectIdGenerator generates an ObjectId from the binary representation of an object
type ObjectIdGenerator interface {
	io.Writer // Must not fail
	GetId() ObjectId
}

func (algo ObjectIdAlgo) Generator() ObjectIdGenerator {
	if algo != OIdAlgoSHA3_256 {
		panic("Only sha3-256 is implemented!")
	}

	return hashObjectIdGenerator{
		algo: algo,
		Hash: sha3.New256(),
	}
}

type hashObjectIdGenerator struct {
	algo ObjectIdAlgo
	hash.Hash
}

func (h hashObjectIdGenerator) GetId() ObjectId {
	return ObjectId{
		Algo: h.algo,
		Sum:  h.Sum([]byte{}),
	}
}

func (oid ObjectId) VerifyObject(o RawObject) bool {
	gen := oid.Algo.Generator()
	if err := o.Serialize(gen); err != nil {
		panic(err)
	}

	return gen.GetId().Equals(oid)
}