-
Notifications
You must be signed in to change notification settings - Fork 84
/
metadata.go
84 lines (72 loc) · 2.22 KB
/
metadata.go
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
package authentication
// File implements metadata serialization.
// Metadata can include shared state as well as metadata sent explicitly over the wire. In order to
// authenticate metadata cryptographically, it must be encoded into []byte. The conversion must be
// injective: no two sets of metadata can result in the same []byte.
import (
"crypto/sha256"
"encoding/binary"
"errors"
"hash"
"github.com/teslamotors/vehicle-command/pkg/protocol/protobuf/signatures"
)
var (
// errOutOfOrderMetadata indicates a programming error (as opposed to a run-time error) and
// therefore is not exported.
errOutOfOrderMetadata = errors.New("metadata items need to be added in increasing tag order")
// ErrMetadataFieldTooLong indicates an authenticated field (such as a verifier name) is too
// long to be compatible with the serialization format.
ErrMetadataFieldTooLong = errors.New("metadata fields can't be more than 255 bytes long")
)
type metadata struct {
Context hash.Hash
fields map[signatures.Tag]bool
last signatures.Tag
}
// Add a (tag, value) pair to the list of metadata values.
func (m *metadata) Add(tag signatures.Tag, value []byte) error {
if tag < m.last {
return errOutOfOrderMetadata
}
if value == nil {
return nil
}
if len(value) > 255 {
return ErrMetadataFieldTooLong
}
m.last = tag
m.Context.Write([]byte{byte(tag)})
m.Context.Write([]byte{byte(len(value))})
m.Context.Write(value)
m.fields[tag] = true
return nil
}
func (m *metadata) AddUint32(tag signatures.Tag, value uint32) error {
var buffer [4]byte
binary.BigEndian.PutUint32(buffer[:], value)
return m.Add(tag, buffer[:])
}
func newMetadata() *metadata {
return newMetadataHash(sha256.New())
}
func newMetadataHash(context hash.Hash) *metadata {
meta := metadata{
Context: context,
fields: make(map[signatures.Tag]bool),
}
return &meta
}
// Contains returns true if every tag on the provided list has been added.
func (m *metadata) Contains(tags []signatures.Tag) bool {
for _, tag := range tags {
if _, ok := m.fields[tag]; !ok {
return false
}
}
return true
}
func (m *metadata) Checksum(message []byte) []byte {
m.Context.Write([]byte{byte(signatures.Tag_TAG_END)})
m.Context.Write(message)
return m.Context.Sum(nil)
}