-
Notifications
You must be signed in to change notification settings - Fork 4
/
Copy pathprotocol_v2.go
131 lines (111 loc) · 2.97 KB
/
protocol_v2.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
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
package votifier
import (
"bytes"
"crypto/hmac"
"crypto/sha256"
"encoding/binary"
"encoding/json"
"errors"
"strconv"
"strings"
)
const (
v2Magic int16 = 0x733A
)
type votifier2Wrapper struct {
Payload string `json:"payload"`
Signature []byte `json:"signature"`
}
type votifier2Inner struct {
ServiceName string `json:"serviceName"`
Username string `json:"username"`
Address string `json:"address"`
Timestamp int64 `json:"timestamp"`
Challenge string `json:"challenge"`
}
// ServiceTokenIdentifier defines a function for identifying a token for a service.
type ServiceTokenIdentifier func(string) string
func deserializev2(msg []byte, tokenFunc ServiceTokenIdentifier, challenge string) (*Vote, error) {
reader := bytes.NewReader(msg)
// verify v2 magic
var magicRead int16
err := binary.Read(reader, binary.BigEndian, &magicRead)
if err != nil {
return nil, err
}
if magicRead != v2Magic {
return nil, errors.New("v2 magic mismatch")
}
// read message length
var bytes int16
if err = binary.Read(reader, binary.BigEndian, &bytes); err != nil {
return nil, err
}
// now for the fun part
var wrapper votifier2Wrapper
if err = json.NewDecoder(reader).Decode(&wrapper); err != nil {
return nil, err
}
var vote votifier2Inner
if err = json.NewDecoder(strings.NewReader(wrapper.Payload)).Decode(&vote); err != nil {
return nil, err
}
// validate challenge
if vote.Challenge != challenge {
return nil, errors.New("challenge invalid")
}
// validate HMAC
m := hmac.New(sha256.New, []byte(tokenFunc(vote.ServiceName)))
m.Write([]byte(wrapper.Payload))
s := m.Sum(nil)
if !hmac.Equal(s, wrapper.Signature) {
return nil, errors.New("signature invalid")
}
return &Vote{
ServiceName: vote.ServiceName,
Address: vote.Address,
Username: vote.Username,
Timestamp: strconv.FormatInt(vote.Timestamp, 10),
}, nil
}
func (v Vote) serializev2(token string, challenge string) (*[]byte, error) {
ts, err := strconv.ParseInt(v.Timestamp, 10, 64)
if err != nil {
// do our best
ts = 0
}
inner := votifier2Inner{
ServiceName: v.ServiceName,
Address: v.Address,
Username: v.Username,
Timestamp: ts,
Challenge: challenge,
}
// encode inner vote and generate outer package
var innerBuf bytes.Buffer
if err = json.NewEncoder(&innerBuf).Encode(inner); err != nil {
return nil, err
}
innerJSON := innerBuf.String()
m := hmac.New(sha256.New, []byte(token))
innerBuf.WriteTo(m)
wrapper := votifier2Wrapper{
Payload: innerJSON,
Signature: m.Sum(nil),
}
// assemble full package
var wrapperBuf bytes.Buffer
if err = json.NewEncoder(&wrapperBuf).Encode(wrapper); err != nil {
return nil, err
}
var finalBuf bytes.Buffer
if err = binary.Write(&finalBuf, binary.BigEndian, v2Magic); err != nil {
return nil, err
}
if err = binary.Write(&finalBuf, binary.BigEndian, int16(wrapperBuf.Len())); err != nil {
return nil, err
}
wrapperBuf.WriteTo(&finalBuf)
finalBytes := finalBuf.Bytes()
return &finalBytes, nil
}