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
}
|