-
Notifications
You must be signed in to change notification settings - Fork 14
/
conversation.go
188 lines (152 loc) · 4.85 KB
/
conversation.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
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
package otr3
import (
"io"
"time"
)
type msgState int
const (
plainText msgState = iota
encrypted
finished
)
var (
queryMarker = []byte("?OTR")
errorMarker = []byte("?OTR Error:")
msgMarker = []byte("?OTR:")
)
// Conversation contains all the information for a specific connection between two peers in an IM system.
// Policies are not supposed to change once a conversation has been used
type Conversation struct {
version otrVersion
Rand io.Reader
msgState msgState
whitespaceState whitespaceState
lastMessageStateChange time.Time
ourInstanceTag uint32
theirInstanceTag uint32
ssid [8]byte
ourKeys []PrivateKey
ourCurrentKey PrivateKey
theirKey PublicKey
ake *ake
smp smp
keys keyManagementContext
Policies policies
heartbeat heartbeatContext
resend resendContext
injections injections
fragmentSize uint16
fragmentationContext fragmentationContext
smpEventHandler SMPEventHandler
errorMessageHandler ErrorMessageHandler
messageEventHandler MessageEventHandler
securityEventHandler SecurityEventHandler
receivedKeyHandler ReceivedKeyHandler
debug bool
sentRevealSig bool
friendlyQueryMessage string
}
// NewConversationWithVersion creates a new conversation with the given version
func NewConversationWithVersion(v int) *Conversation {
var vv otrVersion
switch v {
case 2:
vv = otrV2{}
case 3:
vv = otrV3{}
}
return &Conversation{version: vv}
}
func (c *Conversation) messageHeader(msgType byte) ([]byte, error) {
return c.version.messageHeader(c, msgType)
}
func (c *Conversation) parseMessageHeader(msg messageWithHeader) ([]byte, []byte, error) {
return c.version.parseMessageHeader(c, msg)
}
func (c *Conversation) resolveVersionFromFragment(fragment []byte) error {
versions := 1 << versionFromFragment(fragment)
return c.commitToVersionFrom(versions)
}
func (c *Conversation) parseFragmentPrefix(data []byte) ([]byte, bool, bool) {
if err := c.resolveVersionFromFragment(data); err != nil {
return data, true, false
}
return c.version.parseFragmentPrefix(c, data)
}
func (c *Conversation) wrapMessageHeader(msgType byte, msg []byte) (messageWithHeader, error) {
header, err := c.messageHeader(msgType)
if err != nil {
return nil, err
}
return append(header, msg...), nil
}
// IsEncrypted returns true if the current conversation is private
func (c *Conversation) IsEncrypted() bool {
return c.msgState == encrypted
}
// End ends a secure conversation by generating a termination message for
// the peer and switches to unencrypted communication.
func (c *Conversation) End() (toSend []ValidMessage, err error) {
previousMsgState := c.msgState
if c.msgState == encrypted {
c.smp.wipe()
// Error can only happen when Rand reader is broken
toSend, _, err = c.createSerializedDataMessage(nil, messageFlagIgnoreUnreadable, []tlv{{tlvType: tlvTypeDisconnected}})
}
c.lastMessageStateChange = time.Time{}
c.ake = nil
c.msgState = plainText
defer c.signalSecurityEventIf(previousMsgState == encrypted, GoneInsecure)
c.keys.ourCurrentDHKeys.wipe()
c.keys.ourPreviousDHKeys.wipe()
wipeBigInt(c.keys.theirCurrentDHPubKey)
return
}
// SetOurKeys assigns our private keys to the conversation
func (c *Conversation) SetOurKeys(ourKeys []PrivateKey) {
c.ourKeys = ourKeys
}
// GetOurKeys returns all our keys for the current conversation
func (c *Conversation) GetOurKeys() []PrivateKey {
return c.ourKeys
}
// GetOurCurrentKey returns the currently chosen key for us
func (c *Conversation) GetOurCurrentKey() PrivateKey {
return c.ourCurrentKey
}
// GetTheirKey returns the public key of the other peer in this conversation
func (c *Conversation) GetTheirKey() PublicKey {
return c.theirKey
}
// GetSSID returns the SSID of this Conversation
func (c *Conversation) GetSSID() [8]byte {
return c.ssid
}
// SetSMPEventHandler assigns handler for SMPEvent
func (c *Conversation) SetSMPEventHandler(handler SMPEventHandler) {
c.smpEventHandler = handler
}
// SetErrorMessageHandler assigns handler for ErrorMessage
func (c *Conversation) SetErrorMessageHandler(handler ErrorMessageHandler) {
c.errorMessageHandler = handler
}
// SetMessageEventHandler assigns handler for MessageEvent
func (c *Conversation) SetMessageEventHandler(handler MessageEventHandler) {
c.messageEventHandler = handler
}
// SetSecurityEventHandler assigns handler for SecurityEvent
func (c *Conversation) SetSecurityEventHandler(handler SecurityEventHandler) {
c.securityEventHandler = handler
}
// InitializeInstanceTag sets our instance tag for this conversation. If the argument is zero we will create a new instance tag and return it
// The instance tag created or set will be returned
func (c *Conversation) InitializeInstanceTag(tag uint32) uint32 {
if tag == 0 {
if e := c.generateInstanceTag(); e != nil {
return 0
}
} else {
c.ourInstanceTag = tag
}
return c.ourInstanceTag
}