File: conversation.go

package info (click to toggle)
golang-github-twstrike-otr3 0.0~git20161015.0.744856d-3.1
  • links: PTS, VCS
  • area: main
  • in suites: bookworm, bullseye, forky, sid, trixie
  • size: 1,080 kB
  • sloc: ansic: 127; makefile: 76
file content (186 lines) | stat: -rw-r--r-- 4,938 bytes parent folder | download | duplicates (3)
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
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{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 {
		c.generateInstanceTag()
	} else {
		c.ourInstanceTag = tag
	}
	return c.ourInstanceTag
}