-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathdata.go
143 lines (115 loc) · 3.66 KB
/
data.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
132
133
134
135
136
137
138
139
140
141
142
143
package gotra
import (
"bytes"
"github.com/coyim/gotrax"
"github.com/otrv4/ed448"
"golang.org/x/crypto/salsa20"
)
type dataMessage struct {
senderInstanceTag uint32
receiverInstanceTag uint32
flags uint8
pn uint32
ratchetId uint32
messageId uint32
ecdh ed448.Point
dh *dhPublicKey
nonce [24]byte
msg []byte
mac [64]byte
oldMacKeys []byte
}
func (m *dataMessage) validate(tag uint32) error {
// TODO: implement
// - check the instance tags
// - we will do the other checking later on
return nil
}
func (c *conversation) createHeartbeatDataMessage() ValidMessage {
// No message and no TLVs - look up if this is actually the correct format
return c.createDataMessage([]byte{0x00}, []*tlv{})
}
func (c *conversation) createDataMessage(m []byte, tt []*tlv) ValidMessage {
c.maybeRatchetSender()
dm := &dataMessage{}
dm.senderInstanceTag = c.getInstanceTag()
dm.receiverInstanceTag = c.otherInstanceTag
// TODO: we should probably set pn somewhere
// TODO: we need to set ignore unreadable here
// dm.flags =
dm.ecdh = c.our_ecdh.Pub.K()
dm.dh = c.our_dh.pub
dm.messageId = c.ratchetJ
dm.ratchetId = c.ratchetId - 1
mke, mkm := c.deriveCurrentMK(c.sendingChainKey)
c.sendingChainKey = gotrax.Kdf(usageNextChainKey, 64, c.sendingChainKey)
// TODO: Securely delete the old sending chain key
// TODO: don't ignore error here
gotrax.RandomInto(c, dm.nonce[:])
mm := m
if len(tt) > 0 {
mm = append(append(mm, 0x00), serializeTLVs(tt)...)
}
dm.msg = make([]byte, len(mm))
var key [32]byte
copy(key[:], mke)
salsa20.XORKeyStream(dm.msg, mm, dm.nonce[:], &key)
copy(dm.mac[:], gotrax.Kdf(usageAuthenticator, 64, append(mkm, gotrax.Kdf(usageDataMessageSections, 64, dm.serializeForMac())...)))
// TODO: securely delete mke and mkm - oh wait, shouldn't we keep mkm for revealing?
c.ratchetJ++
return ValidMessage(dm.serialize())
}
func (c *conversation) receivedDataMessage(dm *dataMessage) (plain MessagePlaintext, toSend []ValidMessage, err error) {
// TODO: check for out of order messages
// TODO: what happens if we receive messageId = 1 for a new ratchet?
// This is probably a spec problem
if c.their_ecdh == nil || !dm.ecdh.Equals(c.their_ecdh) {
// TODO: we need to rotate ratchet here
// TODO: store message keys for previous ratchet, based on dm.pn
c.their_ecdh = dm.ecdh
c.their_dh = dm.dh.k
c.ratchetReceiver()
}
// TODO: store missing messages here
mke, mkm := c.deriveCurrentMK(c.receivingChainKey)
auth := gotrax.Kdf(usageAuthenticator, 64, append(mkm, gotrax.Kdf(usageDataMessageSections, 64, dm.serializeForMac())...))
if !bytes.Equal(auth, dm.mac[:]) {
// TODO: handle this better
// TODO: delete mke and mkm safely
return nil, nil, nil
}
c.receivingChainKey = gotrax.Kdf(usageNextChainKey, 64, c.receivingChainKey)
// TODO: securely delete the old chain key
c.ratchetK++
msg := make([]byte, len(dm.msg))
var key [32]byte
copy(key[:], mke)
salsa20.XORKeyStream(msg, dm.msg, dm.nonce[:], &key)
mm, t := parseMessageData(msg)
// TODO: don't ignore return values here
c.processTLVs(t)
return mm, nil, nil
}
func parseMessageData(m []byte) (MessagePlaintext, []*tlv) {
msgp := bytes.SplitN(m, []byte{0x00}, 2)
switch len(msgp) {
case 0:
return MessagePlaintext{}, nil
case 1:
return MessagePlaintext(msgp[0]), nil
default:
return MessagePlaintext(msgp[0]), parseTlvs(msgp[1])
}
}
func parseTlvs(tt []byte) []*tlv {
res := []*tlv{}
var ok bool
for len(tt) > 0 {
t := &tlv{}
if tt, ok = t.deserialize(tt); !ok {
return nil
}
res = append(res, t)
}
return res
}