1
0
Fork 0
forked from External/grumble

Throw the code out there. :)

This commit is contained in:
Mikkel Krautz 2010-09-20 15:14:22 +02:00
commit 73ab596ae6
27 changed files with 3565 additions and 0 deletions

1
AUTHORS Normal file
View file

@ -0,0 +1 @@
Mikkel Krautz <mikkel@krautz.dk>

32
LICENSE Normal file
View file

@ -0,0 +1,32 @@
//
// Grumble - an implementation of Murmur in Go
//
// Copyright (c) 2010 The Grumble Authors
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
//
// - Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
// - Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
// - Neither the name of the Mumble Developers nor the names of its
// contributors may be used to endorse or promote products derived from this
// software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// `AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR
// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//

40
Makefile Normal file
View file

@ -0,0 +1,40 @@
# Copyright (c) 2010 The Grumble Authors
# The use of this source code is goverened by a BSD-style
# license that can be found in the LICENSE-file.
include $(GOROOT)/src/Make.inc
TARG = grumble
PACKAGES = \
pkg/packetdatastream \
pkg/cryptstate \
pkg/mumbleproto
GCFLAGS = -Ipkg/cryptstate/_obj -Ipkg/packetdatastream/_obj -Ipkg/mumbleproto/_obj
LDFLAGS = -Lpkg/cryptstate/_obj -Lpkg/packetdatastream/_obj -Lpkg/mumbleproto/_obj
GOFILES = \
grumble.go \
message.go \
tlsserver.go \
server.go \
client.go
.PHONY: grumble
grumble: pkg
$(GC) $(GCFLAGS) -o $(TARG).$(O) $(GOFILES)
$(LD) $(LDFLAGS) -o $(TARG) $(TARG).$(O)
.PHONY: pkg
pkg:
for dir in $(PACKAGES); do $(MAKE) -C $$dir; done
.PHONY: pkgclean
pkgclean:
for dir in $(PACKAGES); do $(MAKE) -C $$dir clean; done
.PHONY: clean
clean: pkgclean
rm -f grumble
rm -f *.$(O)

5
README Normal file
View file

@ -0,0 +1,5 @@
What is Grumble?
================
Grumble is an implementation of Murmur (the server component of Mumble, an
open-source VoIP application) in the Go language.

290
client.go Normal file
View file

@ -0,0 +1,290 @@
// Copyright (c) 2010 The Grumble Authors
// The use of this source code is goverened by a BSD-style
// license that can be found in the LICENSE-file.
package main
import (
"net"
"bufio"
"log"
"os"
"encoding/binary"
"goprotobuf.googlecode.com/hg/proto"
"mumbleproto"
"cryptstate"
"packetdatastream"
)
// A client connection
type ClientConnection struct {
// Connection-related
tcpaddr *net.TCPAddr
udpaddr *net.UDPAddr
conn net.Conn
reader *bufio.Reader
writer *bufio.Writer
state int
server *Server
msgchan chan *Message
udprecv chan []byte
disconnected bool
crypt *cryptstate.CryptState
codecs []int32
udp bool
// Personal
Session uint32
Username string
Tokens []string
}
// Something invalid happened on the wire.
func (client *ClientConnection) Panic(reason string) {
client.disconnected = true
// fixme(mkrautz): we should inform the server "handler" method through a channel of this event,
// so it can perform a proper disconnect.
}
// Read a protobuf message from a client
func (client *ClientConnection) readProtoMessage() (msg *Message, err os.Error) {
var length uint32
var kind uint16
// Read the message type (16-bit big-endian unsigned integer)
err = binary.Read(client.reader, binary.BigEndian, &kind)
if err != nil {
client.Panic("Unable to read packet kind")
return
}
// Read the message length (32-bit big-endian unsigned integer)
err = binary.Read(client.reader, binary.BigEndian, &length)
if err != nil {
client.Panic("Unable to read packet length")
return
}
buf := make([]byte, length)
_, err = client.reader.Read(buf)
if err != nil {
client.Panic("Unable to read packet content")
return
}
msg = &Message{
buf: buf,
kind: kind,
client: client,
}
return
}
// Send a protobuf-encoded message
func (c *ClientConnection) sendProtoMessage(kind uint16, msg interface{}) (err os.Error) {
d, err := proto.Marshal(msg)
if err != nil {
return
}
c.msgchan <- &Message{
buf: d,
kind: kind,
}
return
}
// UDP receiver.
func (client *ClientConnection) udpreceiver() {
for {
buf := <-client.udprecv
kind := (buf[0] >> 5) & 0x07;
switch kind {
case UDPMessageVoiceSpeex: fallthrough;
case UDPMessageVoiceCELTAlpha: fallthrough;
case UDPMessageVoiceCELTBeta:
kind := buf[0] & 0xe0
target := buf[0] & 0x1f
var counter uint8
outbuf := make([]byte, 1024)
incoming := packetdatastream.New(buf[1:1+(len(buf)-1)])
outgoing := packetdatastream.New(outbuf[1:1+(len(outbuf)-1)])
_ = incoming.GetUint32()
for {
counter = incoming.Next8()
incoming.Skip(int(counter & 0x7f))
if !((counter & 0x80) != 0 && incoming.IsValid()) {
break
}
}
outgoing.PutUint32(client.Session)
outgoing.PutBytes(buf[1:1+(len(buf)-1)])
// Sever loopback
if target == 0x1f {
outbuf[0] = kind
client.sendUdp(&Message{
buf: outbuf[0:1+outgoing.Size()],
client: client,
})
}
case UDPMessagePing:
client.server.udpsend <- &Message{
buf: buf,
client: client,
}
}
}
}
func (client *ClientConnection) sendUdp(msg *Message) {
if client.udp {
// Send as UDP
log.Stdoutf("Sent UDP!")
client.server.udpsend <- msg
} else {
// Tunnel through TCP
log.Stdoutf("Sent TCP!")
msg.kind = MessageUDPTunnel
client.msgchan <- msg
}
}
//
// Sender Goroutine
//
func (client *ClientConnection) sender() {
for {
msg := <-client.msgchan
// First, we write out the message type as a big-endian uint16
err := binary.Write(client.writer, binary.BigEndian, msg.kind)
if err != nil {
client.Panic("Unable to write message type to client")
return
}
// Then the length of the protobuf message
err = binary.Write(client.writer, binary.BigEndian, uint32(len(msg.buf)))
if err != nil {
client.Panic("Unable to write message length to client")
return
}
// At last, write the buffer itself
_, err = client.writer.Write(msg.buf)
if err != nil {
client.Panic("Unable to write message content to client")
return
}
// Flush the write buffer
err = client.writer.Flush()
if err != nil {
client.Panic("Unable to flush client write buffer")
return
}
}
}
// Receiver Goroutine
func (client *ClientConnection) receiver() {
for {
// The version handshake is done. Forward this message to the synchronous request handler.
if client.state == StateClientAuthenticated || client.state == StateClientSentVersion {
// Try to read the next message in the pool
msg, err := client.readProtoMessage()
if err != nil {
return
}
// Special case UDPTunnel messages. They're high priority and shouldn't
// go through our synchronous path.
if msg.kind == MessageUDPTunnel {
client.udp = false
client.udprecv <- msg.buf
} else {
client.server.incoming <- msg
}
}
// The client has just connected. Before it sends its authentication
// information we must send it our version information so it knows
// what version of the protocol it should speak.
if client.state == StateClientConnected {
client.sendProtoMessage(MessageVersion, &mumbleproto.Version{
Version: proto.Uint32(0x10203),
Release: proto.String("1.2.2"),
})
// fixme(mkrautz): Re-add OS information... Does it break anything? It seems like
// the client discards the version message if there is no OS information in it.
client.state = StateServerSentVersion
continue
} else if client.state == StateServerSentVersion {
msg, err := client.readProtoMessage()
if err != nil {
return
}
version := &mumbleproto.Version{}
err = proto.Unmarshal(msg.buf, version)
if err != nil {
client.Panic("Unable to unmarshal client version packet.")
return
}
// Don't really do anything with it...
client.state = StateClientSentVersion
}
}
}
// Send the channel list to a client.
func (client *ClientConnection) sendChannelList() {
server := client.server
root := server.root
// Start at the root channel.
err := client.sendProtoMessage(MessageChannelState, &mumbleproto.ChannelState{
ChannelId: proto.Uint32(uint32(root.Id)),
Name: proto.String(root.Name),
Description: proto.String(root.Description),
})
if err != nil {
// panic!
log.Stdoutf("poanic!")
}
}
// Send the userlist to a client.
func (client *ClientConnection) sendUserList() {
server := client.server
server.cmutex.RLock()
defer server.cmutex.RUnlock()
for x := range server.clients.Iter() {
user := x.(*ClientConnection)
err := user.sendProtoMessage(MessageUserState, &mumbleproto.UserState{
Session: proto.Uint32(client.Session),
Name: proto.String(client.Username),
ChannelId: proto.Uint32(0),
})
if err != nil {
log.Stdoutf("unable to send!")
continue
}
}
}

9
grumble.crt Normal file
View file

@ -0,0 +1,9 @@
-----BEGIN CERTIFICATE-----
MIIBNTCB4KADAgECAgQmyMwOMAsGCSqGSIb3DQEBBTASMRAwDgYDVQQDDAdncnVt
YmxlMB4XDTEwMDMyNTE1MzE0N1oXDTEwMDQyNDE1MzE0N1owEjEQMA4GA1UEAwwH
Z3J1bWJsZTBcMA0GCSqGSIb3DQEBAQUAA0sAMEgCQQDH0Hy33EgJbHBupPXfoukc
b3CRwP891PPYdsDyCXoxWcYYSTC3BNC8/b2u+qtaE2EAcnfMwuNJBTMvn9UCXwwV
AgMBAAGjIDAeMAsGA1UdDwQEAwIBtjAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3
DQEBBQUAA0EAFjT5pPcVmXmDghqkZHZQcHsqSnIIH1MbULzNFErPHNabvdOp6gw1
ycGs6CI1gdO3EuF+cHvS1ocm9ylWULf2cA==
-----END CERTIFICATE-----

43
grumble.go Normal file
View file

@ -0,0 +1,43 @@
// Copyright (c) 2010 The Grumble Authors
// The use of this source code is goverened by a BSD-style
// license that can be found in the LICENSE-file.
package main
import (
"flag"
"fmt"
"os"
)
var help *bool = flag.Bool("help", false, "Show this help")
var port *int = flag.Int("port", 64738, "Default port to listen on")
var host *string = flag.String("host", "0.0.0.0", "Default host to listen on")
func usage() {
fmt.Fprintf(os.Stderr, "usage: grumble [options]\n")
flag.PrintDefaults()
}
func main() {
flag.Parse()
if *help == true {
usage()
return
}
// Create our default server
m, err := NewServer(*host, *port)
if err != nil {
return
}
// And launch it.
go m.ListenAndMurmur()
// Listen forever
sleeper := make(chan int)
zzz := <-sleeper
if zzz > 0 {
}
}

10
grumble.key Normal file
View file

@ -0,0 +1,10 @@
-----BEGIN RSA PRIVATE KEY-----
MIIBOgIBAAJBAMfQfLfcSAlscG6k9d+i6RxvcJHA/z3U89h2wPIJejFZxhhJMLcE
0Lz9va76q1oTYQByd8zC40kFMy+f1QJfDBUCAwEAAQJBALuKJDTRTM+DdvdyXs96
8T5eHhK/SRF4uTHXK/tAB+8eQdSx7D2jsEASX+XSvO5Oc78jFTdG/9EYLW85nv2f
yQECIQDnRvppEVbvSVHbOZXTGCH85Raq+triXrQjT8qrQ2RAVQIhAN0sgv6rM8n0
enIu7lhgQq4islgskCeagTZmI8V1/FzBAiBQBOPRBHnSssiKlCL9dYUU7eJo6ABh
gCjNaucRWHDQPQIgfVwvW116WyuhA3sqSRk2cjDkWSnZAzmFp2m5OMCpK4ECIG93
VI6MTx5PJvOg2Nd6+6qbKF4uFNe/n0CHZ3QtDqhK
-----END RSA PRIVATE KEY-----

180
message.go Normal file
View file

@ -0,0 +1,180 @@
// Grumble - an implementation of Murmur in Go
// Copyright (c) 2010 The Grumble Authors
// The use of this source code is goverened by a BSD-style
// license that can be found in the LICENSE-file.
package main
import (
"log"
"mumbleproto"
"goprotobuf.googlecode.com/hg/proto"
"net"
"container/list"
"cryptstate"
)
// These are the different kinds of messages
// that are defined for the Mumble protocol
const (
MessageVersion = iota
MessageUDPTunnel
MessageAuthenticate
MessagePing
MessageReject
MessageServerSync
MessageChannelRemove
MessageChannelState
MessageUserRemove
MessageUserState
MessageBanList
MessageTextMessage
MessagePermissionDenied
MessageACL
MessageQueryUsers
MessageCryptSetup
MessageContextActionAdd
MessageContextAction
MessageUserList
MessageVoiceTarget
MessagePermissionQuery
MessageCodecVersion
MessageUserStats
MessageRequestBlob
MessageServerConfig
)
const (
UDPMessageVoiceCELTAlpha = iota
UDPMessagePing
UDPMessageVoiceSpeex
UDPMessageVoiceCELTBeta
)
type Message struct {
buf []byte
// Kind denotes a message kind for TCP packets. This field
// is ignored for UDP packets.
kind uint16
// For UDP datagrams one of these fiels have to be filled out.
// If there is no connection established, address must be used.
// If the datagram comes from an already-connected client, the
// client field should point to that client.
client *ClientConnection
address net.Addr
}
func (server *Server) handleCryptSetup(client *ClientConnection, msg *Message) {
cs := &mumbleproto.CryptSetup{}
err := proto.Unmarshal(msg.buf, cs)
if err != nil {
client.Panic(err.String())
return
}
// No client nonce. This means the client
// is requesting that we re-sync our nonces.
if len(cs.ClientNonce) == 0 {
log.Stdoutf("Requested crypt-nonce resync")
cs.ClientNonce = make([]byte, cryptstate.AESBlockSize)
if copy(cs.ClientNonce, client.crypt.EncryptIV[0:]) != cryptstate.AESBlockSize {
return
}
client.sendProtoMessage(MessageCryptSetup, cs)
} else {
log.Stdoutf("Received client nonce")
if len(cs.ClientNonce) != cryptstate.AESBlockSize {
return
}
client.crypt.Resync += 1
if copy(client.crypt.DecryptIV[0:], cs.ClientNonce) != cryptstate.AESBlockSize {
return
}
log.Stdoutf("Crypt re-sync successful")
}
}
func (server *Server) handlePingMessage(client *ClientConnection, msg *Message) {
ping := &mumbleproto.Ping{}
err := proto.Unmarshal(msg.buf, ping)
if err != nil {
client.Panic(err.String())
return
}
// Phony response for ping messages. We don't keep stats
// for this yet.
client.sendProtoMessage(MessagePing, &mumbleproto.Ping{
Timestamp: ping.Timestamp,
Good: proto.Uint32(uint32(client.crypt.Good)),
Late: proto.Uint32(uint32(client.crypt.Late)),
Lost: proto.Uint32(uint32(client.crypt.Lost)),
Resync: proto.Uint32(uint32(client.crypt.Resync)),
})
}
func (server *Server) handleChannelAddMessage(client *ClientConnection, msg *Message) {
}
func (server *Server) handleChannelRemoveMessage(client *ClientConnection, msg *Message) {
}
func (server *Server) handleChannelStateMessage(client *ClientConnection, msg *Message) {
}
func (server *Server) handleUserRemoveMessage(client *ClientConnection, msg *Message) {
}
func (server *Server) handleUserStateMessage(client *ClientConnection, msg *Message) {
}
func (server *Server) handleBanListMessage(client *ClientConnection, msg *Message) {
}
func (server *Server) handleTextMessage(client *ClientConnection, msg *Message) {
txtmsg := &mumbleproto.TextMessage{}
err := proto.Unmarshal(msg.buf, txtmsg)
if err != nil {
client.Panic(err.String())
return
}
// Receiver list...
users := list.New()
for i := 0; i < len(txtmsg.Session); i++ {
// Lookup user by ID
user := server.getClientConnection(txtmsg.Session[i])
if user != nil {
users.PushBack(user)
}
}
for x := range users.Iter() {
user := x.(*ClientConnection)
user.sendProtoMessage(MessageTextMessage, &mumbleproto.TextMessage{
Actor: proto.Uint32(client.Session),
Message: txtmsg.Message,
})
}
}
func (server *Server) handleAclMessage(client *ClientConnection, msg *Message) {
}
// User query
func (server *Server) handleQueryUsers(client *ClientConnection, msg *Message) {
}
// User stats message. Shown in the Mumble client when a
// user right clicks a user and selects 'User Information'.
func (server *Server) handleUserStatsMessage(client *ClientConnection, msg *Message) {
stats := &mumbleproto.UserStats{}
err := proto.Unmarshal(msg.buf, stats)
if err != nil {
client.Panic(err.String())
}
}

6
pkg/cryptstate/Makefile Normal file
View file

@ -0,0 +1,6 @@
include $(GOROOT)/src/Make.inc
TARG = cryptstate
GOFILES = cryptstate.go
include $(GOROOT)/src/Make.pkg

View file

@ -0,0 +1,360 @@
// Grumble - an implementation of Murmur in Go
// Copyright (c) 2010 The Grumble Authors
// The use of this source code is goverened by a BSD-style
// license that can be found in the LICENSE-file.
package cryptstate
import (
"crypto/aes"
"crypto/rand"
"os"
)
const AESBlockSize = 16
const DecryptHistorySize = 0x100
type CryptState struct {
RawKey [AESBlockSize]byte
EncryptIV [AESBlockSize]byte
DecryptIV [AESBlockSize]byte
decryptHistory [DecryptHistorySize]byte
Good int
Late int
Lost int
Resync int
RemoteGood int
RemoteLate int
RemoteLost int
RemoteResync int
cipher *aes.Cipher
}
func New() (cs *CryptState, err os.Error) {
cs = new(CryptState)
for i := 0; i < DecryptHistorySize; i++ {
cs.decryptHistory[i] = 0
}
return
}
func (cs *CryptState) GenerateKey() (err os.Error) {
rand.Read(cs.RawKey[0:])
rand.Read(cs.EncryptIV[0:])
rand.Read(cs.DecryptIV[0:])
cs.cipher, err = aes.NewCipher(cs.RawKey[0:])
if err != nil {
return
}
return
}
func (cs *CryptState) SetKey(key []byte, eiv []byte, div[]byte) (err os.Error) {
if copy(cs.RawKey[0:], key[0:]) != AESBlockSize {
err = os.NewError("Unable to copy key")
return
}
if copy(cs.EncryptIV[0:], eiv[0:]) != AESBlockSize {
err = os.NewError("Unable to copy EIV")
return
}
if copy(cs.DecryptIV[0:], div[0:]) != AESBlockSize {
err = os.NewError("Unable to copy DIV")
return
}
cs.cipher, err = aes.NewCipher(cs.RawKey[0:])
if err != nil {
return
}
return
}
func (cs *CryptState) Decrypt(src, dst []byte) (err os.Error) {
if len(src) < 4 {
err = os.NewError("Crypted length too short to decrypt")
return
}
plain_len := len(src) - 4
if len(dst) != plain_len {
err = os.NewError("plain_len and src len mismatch")
return
}
var saveiv [AESBlockSize]byte
var tag [AESBlockSize]byte
var ivbyte byte
var restore bool
lost := 0
late := 0
ivbyte = src[0]
restore = false
if copy(saveiv[0:], cs.DecryptIV[0:]) != AESBlockSize {
err = os.NewError("Copy failed")
return
}
if byte(cs.DecryptIV[0] + 1) == ivbyte {
// In order as expected
if ivbyte > cs.DecryptIV[0] {
cs.DecryptIV[0] = ivbyte
} else if ivbyte < cs.DecryptIV[0] {
cs.DecryptIV[0] = ivbyte
for i := 1; i < AESBlockSize; i++ {
cs.DecryptIV[i] += 1
if cs.DecryptIV[i] > 0 {
break
}
}
} else {
err = os.NewError("invalid ivbyte")
}
} else {
// Out of order or repeat
var diff int
diff = int(ivbyte - cs.DecryptIV[0])
if diff > 128 {
diff = diff - 256
} else if diff < -128 {
diff = diff + 256
}
if ivbyte < cs.DecryptIV[0] && diff > -30 && diff < 0 {
// Late packet, but no wraparound
late = 1
lost = -1
cs.DecryptIV[0] = ivbyte
restore = true
} else if ivbyte > cs.DecryptIV[0] && diff > -30 && diff < 0 {
// Last was 0x02, here comes 0xff from last round
late = 1
lost = -1
cs.DecryptIV[0] = ivbyte
for i := 1; i < AESBlockSize; i++ {
cs.DecryptIV[0] -= 1
if cs.DecryptIV[0] > 0 {
break
}
}
restore = true
} else if ivbyte > cs.DecryptIV[0] && diff > 0 {
// Lost a few packets, but beyond that we're good.
lost = int(ivbyte - cs.DecryptIV[0] - 1)
cs.DecryptIV[0] = ivbyte
} else if ivbyte < cs.DecryptIV[0] && diff > 0 {
// Lost a few packets, and wrapped around
lost = int(256 - int(cs.DecryptIV[0]) + int(ivbyte) - 1)
cs.DecryptIV[0] = ivbyte
for i := 1; i < AESBlockSize; i++ {
cs.DecryptIV[0] += 1
if cs.DecryptIV[0] > 0 {
break
}
}
} else {
err = os.NewError("No matching ivbyte")
return
}
if cs.decryptHistory[cs.DecryptIV[0]] == cs.DecryptIV[0] {
if copy(cs.DecryptIV[0:], saveiv[0:]) != AESBlockSize {
err = os.NewError("Failed to copy AESBlockSize bytes")
return
}
}
}
cs.OCBDecrypt(src[4:], dst[0:], cs.DecryptIV[0:], tag[0:])
for i := 0; i < 3; i++ {
if tag[i] != src[i+1] {
if copy(cs.DecryptIV[0:], saveiv[0:]) != AESBlockSize {
err = os.NewError("Error while trying to recover from error")
return
}
err = os.NewError("tag mismatch")
return
}
}
cs.decryptHistory[cs.DecryptIV[0]] = cs.DecryptIV[0]
if restore {
if copy(cs.DecryptIV[0:], saveiv[0:]) != AESBlockSize {
err = os.NewError("Error while trying to recover IV")
return
}
}
cs.Good += 1
cs.Late += late
cs.Lost += lost
// restart timer
return
}
func (cs *CryptState) Encrypt(src, dst []byte) {
var tag [AESBlockSize]byte
// First, increase our IV
for i := 0; i < AESBlockSize; i++ {
cs.EncryptIV[i] += 1;
if cs.EncryptIV[i] > 0 {
break;
}
}
cs.OCBEncrypt(src, dst[4:], cs.EncryptIV[0:], tag[0:])
dst[0] = cs.EncryptIV[0]
dst[1] = tag[0];
dst[2] = tag[1];
dst[3] = tag[2];
return
}
func zeros(block []byte) {
for i := 0; i < AESBlockSize; i++ {
block[i] = 0
}
}
func xor(dst []byte, a []byte, b []byte) {
for i := 0; i < AESBlockSize; i++ {
dst[i] = a[i] ^ b[i]
}
}
func times2(block []byte) {
carry := (block[0] >> 7) & 0x1
for i := 0; i < AESBlockSize-1; i++ {
block[i] = (block[i] << 1) | ((block[i+1] >> 7) & 0x1)
}
block[AESBlockSize-1] = (block[AESBlockSize-1] << 1) ^ (carry * 135)
}
func times3(block []byte) {
carry := (block[0] >> 7) & 0x1;
for i := 0; i < AESBlockSize-1; i++ {
block[i] ^= (block[i] << 1) | ((block[i+1] >> 7) & 0x1)
}
block[AESBlockSize-1] ^= ((block[AESBlockSize-1] << 1) ^ (carry * 135))
}
func (cs *CryptState) OCBEncrypt(src []byte, dst []byte, nonce []byte, tag []byte) (err os.Error) {
var delta [AESBlockSize]byte
var checksum [AESBlockSize]byte
var tmp [AESBlockSize]byte
var pad [AESBlockSize]byte
off := 0
cs.cipher.Encrypt(cs.EncryptIV[0:], delta[0:])
zeros(checksum[0:])
remain := len(src)
for remain > AESBlockSize {
times2(delta[0:])
xor(tmp[0:], delta[0:], src[off:off+AESBlockSize])
cs.cipher.Encrypt(tmp[0:], tmp[0:])
xor(dst[off:off+AESBlockSize], delta[0:], tmp[0:])
xor(checksum[0:], checksum[0:], src[off:off+AESBlockSize])
remain -= AESBlockSize
off += AESBlockSize
}
times2(delta[0:])
zeros(tmp[0:])
num := remain * 8
tmp[AESBlockSize-2] = uint8((uint32(num) >> 8) & 0xff)
tmp[AESBlockSize-1] = uint8(num & 0xff)
xor(tmp[0:], tmp[0:], delta[0:])
cs.cipher.Encrypt(tmp[0:], pad[0:])
copied := copy(tmp[0:], src[off:])
if copied != remain {
err = os.NewError("Copy failed")
return
}
if copy(tmp[copied:], pad[copied:]) != (AESBlockSize-remain) {
err = os.NewError("Copy failed")
return
}
xor(checksum[0:], checksum[0:], tmp[0:])
xor(tmp[0:], pad[0:], tmp[0:])
if copy(dst[off:], tmp[0:]) != remain {
err = os.NewError("Copy failed")
return
}
times3(delta[0:])
xor(tmp[0:], delta[0:], checksum[0:])
cs.cipher.Encrypt(tmp[0:], tag[0:])
return
}
func (cs *CryptState) OCBDecrypt(encrypted []byte, plain []byte, nonce []byte, tag []byte) (err os.Error) {
var checksum [AESBlockSize]byte
var delta [AESBlockSize]byte
var tmp [AESBlockSize]byte
var pad [AESBlockSize]byte
off := 0
cs.cipher.Encrypt(nonce[0:], delta[0:])
zeros(checksum[0:])
remain := len(encrypted)
for remain > AESBlockSize {
times2(delta[0:])
xor(tmp[0:], delta[0:], encrypted[off:off+AESBlockSize])
cs.cipher.Decrypt(tmp[0:], tmp[0:])
xor(plain[off:off+AESBlockSize], delta[0:], tmp[0:])
xor(checksum[0:], checksum[0:], plain[off:off+AESBlockSize])
off += AESBlockSize
remain -= AESBlockSize
}
times2(delta[0:])
zeros(tmp[0:])
num := remain * 8
tmp[AESBlockSize-2] = uint8((uint32(num) >> 8) & 0xff)
tmp[AESBlockSize-1] = uint8(num & 0xff)
xor(tmp[0:], tmp[0:], delta[0:])
cs.cipher.Encrypt(tmp[0:], pad[0:])
for i := 0; i < AESBlockSize; i++ {
tmp[i] = 0
}
copied := copy(tmp[0:remain], encrypted[off:off+remain])
if copied != remain {
err = os.NewError("Copy failed")
return
}
xor(tmp[0:], tmp[0:], pad[0:])
xor(checksum[0:], checksum[0:], tmp[0:])
copied = copy(plain[off:off+remain], tmp[0:remain])
if copied != remain {
err = os.NewError("Copy failed")
return
}
times3(delta[0:])
xor(tmp[0:], delta[0:], checksum[0:])
cs.cipher.Encrypt(tmp[0:], tag[0:])
return
}

View file

@ -0,0 +1,147 @@
package cryptstate
import (
"testing"
)
func BlockCompare(a []byte, b []byte) (match bool) {
if len(a) != len(b) {
return
}
for i := 0; i < len(a); i++ {
if a[i] != b[i] {
return
}
}
match = true
return
}
func TestTimes2(t *testing.T) {
msg := [AESBlockSize]byte{
0x80, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe,
}
expected := [AESBlockSize]byte{
0x01, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7b,
}
times2(msg[0:])
if BlockCompare(msg[0:], expected[0:]) == false {
t.Errorf("times2 produces invalid output: %v, expected: %v", msg, expected)
}
}
func TestTimes3(t *testing.T) {
msg := [AESBlockSize]byte{
0x80, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe,
}
expected := [AESBlockSize]byte {
0x81, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x85,
}
times3(msg[0:])
if BlockCompare(msg[0:], expected[0:]) == false {
t.Errorf("times3 produces invalid output: %v, expected: %v", msg, expected)
}
}
func TestZeros(t *testing.T) {
var msg [AESBlockSize]byte
zeros(msg[0:])
for i := 0; i < len(msg); i++ {
if msg[i] != 0 {
t.Errorf("zeros does not zero slice.")
}
}
}
func TestXor(t *testing.T) {
msg := [AESBlockSize]byte{
0x80, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe,
}
var out [AESBlockSize]byte
xor(out[0:], msg[0:], msg[0:])
for i := 0; i < len(out); i++ {
if out[i] != 0 {
t.Errorf("XOR broken")
}
}
}
func TestEncrypt(t *testing.T) {
msg := [15]byte {
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
}
key := [AESBlockSize]byte {
0x96, 0x8b, 0x1b, 0x0c, 0x53, 0x1e, 0x1f, 0x80, 0xa6, 0x1d, 0xcb, 0x27, 0x94, 0x09, 0x6f, 0x32,
}
eiv := [AESBlockSize]byte {
0x1e, 0x2a, 0x9b, 0xd0, 0x2d, 0xa6, 0x8e, 0x46, 0x26, 0x85, 0x83, 0xe9, 0x14, 0x2a, 0xff, 0x2a,
}
div := [AESBlockSize]byte {
0x73, 0x99, 0x9d, 0xa2, 0x03, 0x70, 0x00, 0x96, 0xef, 0x55, 0x06, 0x7a, 0x8b, 0xbe, 0x00, 0x07,
}
expected := [19]byte {
0x1f, 0xfc, 0xdd, 0xb4, 0x68, 0x13, 0x68, 0xb7, 0x92, 0x67, 0xca, 0x2d, 0xba, 0xb7, 0x0d, 0x44, 0xdf, 0x32, 0xd4,
}
expected_eiv := [AESBlockSize]byte {
0x1f, 0x2a, 0x9b, 0xd0, 0x2d, 0xa6, 0x8e, 0x46, 0x26, 0x85, 0x83, 0xe9, 0x14, 0x2a, 0xff, 0x2a,
}
cs, err := New()
if err != nil {
t.Errorf("%v", err)
}
out := make([]byte, 19)
cs.SetKey(key[0:], eiv[0:], div[0:])
cs.Encrypt(msg[0:], out[0:])
if BlockCompare(out[0:], expected[0:]) == false {
t.Errorf("Mismatch in output")
}
if BlockCompare(cs.EncryptIV[0:], expected_eiv[0:]) == false {
t.Errorf("EIV mismatch")
}
}
func TestDecrypt(t *testing.T) {
key := [AESBlockSize]byte {
0x96, 0x8b, 0x1b, 0x0c, 0x53, 0x1e, 0x1f, 0x80, 0xa6, 0x1d, 0xcb, 0x27, 0x94, 0x09, 0x6f, 0x32,
}
eiv := [AESBlockSize]byte {
0x1e, 0x2a, 0x9b, 0xd0, 0x2d, 0xa6, 0x8e, 0x46, 0x26, 0x85, 0x83, 0xe9, 0x14, 0x2a, 0xff, 0x2a,
}
div := [AESBlockSize]byte {
0x73, 0x99, 0x9d, 0xa2, 0x03, 0x70, 0x00, 0x96, 0xef, 0x55, 0x06, 0x7a, 0x8b, 0xbe, 0x00, 0x07,
}
crypted := [19]byte {
0x1f, 0xfc, 0xdd, 0xb4, 0x68, 0x13, 0x68, 0xb7, 0x92, 0x67, 0xca, 0x2d, 0xba, 0xb7, 0x0d, 0x44, 0xdf, 0x32, 0xd4,
}
expected := [15]byte {
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
}
post_div := [AESBlockSize]byte {
0x1f, 0x2a, 0x9b, 0xd0, 0x2d, 0xa6, 0x8e, 0x46, 0x26, 0x85, 0x83, 0xe9, 0x14, 0x2a, 0xff, 0x2a,
}
cs, err := New()
if err != nil {
t.Errorf("%v", err)
}
out := make([]byte, 15)
cs.SetKey(key[0:], div[0:], eiv[0:])
cs.Decrypt(crypted[0:], out[0:])
if BlockCompare(out[0:], expected[0:]) == false {
t.Errorf("Mismatch in output")
}
if BlockCompare(cs.DecryptIV[0:], post_div[0:]) == false {
t.Errorf("Mismatch in DIV")
}
}

View file

@ -0,0 +1,322 @@
/* Copyright (C) 2005-2010, Thorvald Natvig <thorvald@natvig.com>
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
- Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
- Neither the name of the Mumble Developers nor the names of its
contributors may be used to endorse or promote products derived from this
software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/*
* This code implements OCB-AES128.
* In the US, OCB is covered by patents. The inventor has given a license
* to all programs distributed under the GPL.
* Mumble is BSD (revised) licensed, meaning you can use the code in a
* closed-source program. If you do, you'll have to either replace
* OCB with something else or get yourself a license.
*/
#include "CryptState.h"
#include <openssl/rand.h>
#include <stdint.h>
#include <string.h>
namespace MumbleClient {
CryptState::CryptState() {
for (int i = 0; i < 0x100; i++)
decrypt_history[i] = 0;
bInit = false;
uiGood = uiLate = uiLost = uiResync = 0;
uiRemoteGood = uiRemoteLate = uiRemoteLost = uiRemoteResync = 0;
}
bool CryptState::isValid() const {
return bInit;
}
void CryptState::genKey() {
RAND_bytes(raw_key, AES_BLOCK_SIZE);
RAND_bytes(encrypt_iv, AES_BLOCK_SIZE);
RAND_bytes(decrypt_iv, AES_BLOCK_SIZE);
AES_set_encrypt_key(raw_key, 128, &encrypt_key);
AES_set_decrypt_key(raw_key, 128, &decrypt_key);
bInit = true;
}
void CryptState::setKey(const unsigned char* rkey, const unsigned char* eiv, const unsigned char* div) {
memcpy(raw_key, rkey, AES_BLOCK_SIZE);
memcpy(encrypt_iv, eiv, AES_BLOCK_SIZE);
memcpy(decrypt_iv, div, AES_BLOCK_SIZE);
AES_set_encrypt_key(raw_key, 128, &encrypt_key);
AES_set_decrypt_key(raw_key, 128, &decrypt_key);
bInit = true;
}
void CryptState::setDecryptIV(const unsigned char* iv) {
memcpy(decrypt_iv, iv, AES_BLOCK_SIZE);
}
const unsigned char* CryptState::getEncryptIV() const {
return encrypt_iv;
}
void CryptState::encrypt(const unsigned char* source, unsigned char* dst, unsigned int plain_length) {
unsigned char tag[AES_BLOCK_SIZE];
// First, increase our IV.
for (int i = 0; i < AES_BLOCK_SIZE; i++)
if (++encrypt_iv[i])
break;
ocb_encrypt(source, dst+4, plain_length, encrypt_iv, tag);
dst[0] = encrypt_iv[0];
dst[1] = tag[0];
dst[2] = tag[1];
dst[3] = tag[2];
}
bool CryptState::decrypt(const unsigned char* source, unsigned char* dst, unsigned int crypted_length) {
if (crypted_length < 4)
return false;
unsigned int plain_length = crypted_length - 4;
unsigned char saveiv[AES_BLOCK_SIZE];
unsigned char ivbyte = source[0];
bool restore = false;
unsigned char tag[AES_BLOCK_SIZE];
int lost = 0;
int late = 0;
memcpy(saveiv, decrypt_iv, AES_BLOCK_SIZE);
if (((decrypt_iv[0] + 1) & 0xFF) == ivbyte) {
// In order as expected.
if (ivbyte > decrypt_iv[0]) {
decrypt_iv[0] = ivbyte;
} else if (ivbyte < decrypt_iv[0]) {
decrypt_iv[0] = ivbyte;
for (int i = 1;i < AES_BLOCK_SIZE; i++)
if (++decrypt_iv[i])
break;
} else {
return false;
}
} else {
// This is either out of order or a repeat.
int diff = ivbyte - decrypt_iv[0];
if (diff > 128)
diff = diff-256;
else if (diff < -128)
diff = diff+256;
if ((ivbyte < decrypt_iv[0]) && (diff > -30) && (diff < 0)) {
// Late packet, but no wraparound.
late = 1;
lost = -1;
decrypt_iv[0] = ivbyte;
restore = true;
} else if ((ivbyte > decrypt_iv[0]) && (diff > -30) && (diff < 0)) {
// Last was 0x02, here comes 0xff from last round
late = 1;
lost = -1;
decrypt_iv[0] = ivbyte;
for (int i = 1; i < AES_BLOCK_SIZE; i++)
if (decrypt_iv[i]--)
break;
restore = true;
} else if ((ivbyte > decrypt_iv[0]) && (diff > 0)) {
// Lost a few packets, but beyond that we're good.
lost = ivbyte - decrypt_iv[0] - 1;
decrypt_iv[0] = ivbyte;
} else if ((ivbyte < decrypt_iv[0]) && (diff > 0)) {
// Lost a few packets, and wrapped around
lost = 256 - decrypt_iv[0] + ivbyte - 1;
decrypt_iv[0] = ivbyte;
for (int i = 1; i < AES_BLOCK_SIZE; i++)
if (++decrypt_iv[i])
break;
} else {
return false;
}
if (decrypt_history[decrypt_iv[0]] == decrypt_iv[1]) {
memcpy(decrypt_iv, saveiv, AES_BLOCK_SIZE);
return false;
}
}
ocb_decrypt(source + 4, dst, plain_length, decrypt_iv, tag);
if (memcmp(tag, source + 1, 3) != 0) {
memcpy(decrypt_iv, saveiv, AES_BLOCK_SIZE);
return false;
}
decrypt_history[decrypt_iv[0]] = decrypt_iv[1];
if (restore)
memcpy(decrypt_iv, saveiv, AES_BLOCK_SIZE);
uiGood++;
uiLate += late;
uiLost += lost;
return true;
}
#if defined(__LP64__)
#define BLOCKSIZE 2
#define SHIFTBITS 63
typedef uint64_t subblock;
#ifdef __x86_64__
static inline uint64_t SWAP64(register uint64_t __in) { register uint64_t __out; __asm__("bswap %q0" : "=r"(__out) : "0"(__in)); return __out; }
#else
#define SWAP64(x) ((static_cast<uint64_t>(x) << 56) | \
((static_cast<uint64_t>(x) << 40) & 0xff000000000000ULL) | \
((static_cast<uint64_t>(x) << 24) & 0xff0000000000ULL) | \
((static_cast<uint64_t>(x) << 8) & 0xff00000000ULL) | \
((static_cast<uint64_t>(x) >> 8) & 0xff000000ULL) | \
((static_cast<uint64_t>(x) >> 24) & 0xff0000ULL) | \
((static_cast<uint64_t>(x) >> 40) & 0xff00ULL) | \
((static_cast<uint64_t>(x) >> 56)))
#endif
#define SWAPPED(x) SWAP64(x)
#else
#define BLOCKSIZE 4
#define SHIFTBITS 31
typedef uint32_t subblock;
#define SWAPPED(x) htonl(x)
#endif
typedef subblock keyblock[BLOCKSIZE];
#define HIGHBIT (1<<SHIFTBITS);
static void inline XOR(subblock* dst, const subblock* a, const subblock* b) {
for (int i = 0; i < BLOCKSIZE; i++)
dst[i] = a[i] ^ b[i];
}
static void inline S2(subblock* block) {
subblock carry = SWAPPED(block[0]) >> SHIFTBITS;
for (int i = 0; i < BLOCKSIZE - 1; i++)
block[i] = SWAPPED((SWAPPED(block[i]) << 1) | (SWAPPED(block[i + 1]) >> SHIFTBITS));
block[BLOCKSIZE - 1] = SWAPPED((SWAPPED(block[BLOCKSIZE - 1]) << 1) ^(carry * 0x87));
}
static void inline S3(subblock* block) {
subblock carry = SWAPPED(block[0]) >> SHIFTBITS;
for (int i = 0; i < BLOCKSIZE - 1; i++)
block[i] ^= SWAPPED((SWAPPED(block[i]) << 1) | (SWAPPED(block[i + 1]) >> SHIFTBITS));
block[BLOCKSIZE - 1] ^= SWAPPED((SWAPPED(block[BLOCKSIZE - 1]) << 1) ^(carry * 0x87));
}
static void inline ZERO(keyblock &block) {
for (int i = 0; i < BLOCKSIZE; i++)
block[i] = 0;
}
#define AESencrypt(src,dst,key) AES_encrypt(reinterpret_cast<const unsigned char *>(src),reinterpret_cast<unsigned char *>(dst), key);
#define AESdecrypt(src,dst,key) AES_decrypt(reinterpret_cast<const unsigned char *>(src),reinterpret_cast<unsigned char *>(dst), key);
void CryptState::ocb_encrypt(const unsigned char* plain, unsigned char* encrypted, unsigned int len, const unsigned char* nonce, unsigned char* tag) {
keyblock checksum, delta, tmp, pad;
// Initialize
AESencrypt(nonce, delta, &encrypt_key);
ZERO(checksum);
while (len > AES_BLOCK_SIZE) {
S2(delta);
XOR(tmp, delta, reinterpret_cast<const subblock *>(plain));
AESencrypt(tmp, tmp, &encrypt_key);
XOR(reinterpret_cast<subblock *>(encrypted), delta, tmp);
XOR(checksum, checksum, reinterpret_cast<const subblock *>(plain));
len -= AES_BLOCK_SIZE;
plain += AES_BLOCK_SIZE;
encrypted += AES_BLOCK_SIZE;
}
S2(delta);
ZERO(tmp);
tmp[BLOCKSIZE - 1] = SWAPPED(len * 8);
XOR(tmp, tmp, delta);
AESencrypt(tmp, pad, &encrypt_key);
memcpy(tmp, plain, len);
memcpy(reinterpret_cast<unsigned char *>(tmp) + len, reinterpret_cast<const unsigned char *>(pad) + len, AES_BLOCK_SIZE - len);
XOR(checksum, checksum, tmp);
XOR(tmp, pad, tmp);
memcpy(encrypted, tmp, len);
S3(delta);
XOR(tmp, delta, checksum);
AESencrypt(tmp, tag, &encrypt_key);
}
void CryptState::ocb_decrypt(const unsigned char* encrypted, unsigned char* plain, unsigned int len, const unsigned char* nonce, unsigned char* tag) {
keyblock checksum, delta, tmp, pad;
// Initialize
AESencrypt(nonce, delta, &encrypt_key);
ZERO(checksum);
while (len > AES_BLOCK_SIZE) {
S2(delta);
XOR(tmp, delta, reinterpret_cast<const subblock *>(encrypted));
AESdecrypt(tmp, tmp, &decrypt_key);
XOR(reinterpret_cast<subblock *>(plain), delta, tmp);
XOR(checksum, checksum, reinterpret_cast<const subblock *>(plain));
len -= AES_BLOCK_SIZE;
plain += AES_BLOCK_SIZE;
encrypted += AES_BLOCK_SIZE;
}
S2(delta);
ZERO(tmp);
tmp[BLOCKSIZE - 1] = SWAPPED(len * 8);
XOR(tmp, tmp, delta);
AESencrypt(tmp, pad, &encrypt_key);
memset(tmp, 0, AES_BLOCK_SIZE);
memcpy(tmp, encrypted, len);
XOR(tmp, tmp, pad);
XOR(checksum, checksum, tmp);
memcpy(plain, tmp, len);
S3(delta);
XOR(tmp, delta, checksum);
AESencrypt(tmp, tag, &encrypt_key);
}
} // end namespace MumbleClient

View file

@ -0,0 +1,77 @@
/* Copyright (C) 2005-2010, Thorvald Natvig <thorvald@natvig.com>
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
- Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
- Neither the name of the Mumble Developers nor the names of its
contributors may be used to endorse or promote products derived from this
software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef _CRYPTSTATE_H
#define _CRYPTSTATE_H
#include <openssl/aes.h>
namespace MumbleClient {
class CryptState {
public:
unsigned char raw_key[AES_BLOCK_SIZE];
unsigned char encrypt_iv[AES_BLOCK_SIZE];
unsigned char decrypt_iv[AES_BLOCK_SIZE];
unsigned char decrypt_history[0x100];
unsigned int uiGood;
unsigned int uiLate;
unsigned int uiLost;
unsigned int uiResync;
unsigned int uiRemoteGood;
unsigned int uiRemoteLate;
unsigned int uiRemoteLost;
unsigned int uiRemoteResync;
AES_KEY encrypt_key;
AES_KEY decrypt_key;
bool bInit;
public:
CryptState();
bool isValid() const;
void genKey();
void setKey(const unsigned char* rkey, const unsigned char* eiv, const unsigned char* div);
void setDecryptIV(const unsigned char* iv);
const unsigned char* getEncryptIV() const;
void ocb_encrypt(const unsigned char* plain, unsigned char* encrypted, unsigned int len, const unsigned char* nonce, unsigned char* tag);
void ocb_decrypt(const unsigned char* encrypted, unsigned char* plain, unsigned int len, const unsigned char* nonce, unsigned char* tag);
bool decrypt(const unsigned char* source, unsigned char* dst, unsigned int crypted_length);
void encrypt(const unsigned char* source, unsigned char* dst, unsigned int plain_length);
};
} // end namespace MumbleClient
#endif

View file

@ -0,0 +1,9 @@
.PHONY: default
default:
g++ test.cpp CryptState.cpp -o test -lcrypto
g++ test2.cpp CryptState.cpp -o test2 -lcrypto
g++ test3.cpp CryptState.cpp -o test3 -lcrypto
.PHONY: clean
clean:
rm -f test test2 test3

View file

@ -0,0 +1 @@
C++ code for generating some of the test vectors used in cryptstate_test.go

View file

@ -0,0 +1,28 @@
#include "CryptState.h"
#include <stdio.h>
unsigned char msg[] = {
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
};
static void DumpBytes(unsigned char *bytes, unsigned int len, const char *name) {
printf("unsigned char %s[] = { ", name);
for (int i = 0; i < len; i++) {
printf("0x%.2x, ", bytes[i]);
}
printf("}\n");
}
int main(int argc, char *argv[]) {
MumbleClient::CryptState cs;
cs.genKey();
DumpBytes(cs.raw_key, AES_BLOCK_SIZE, "rawkey");
DumpBytes(cs.encrypt_iv, AES_BLOCK_SIZE, "encrypt_iv");
DumpBytes(cs.decrypt_iv, AES_BLOCK_SIZE, "decrypt_iv");
unsigned char buf[19];
cs.encrypt(msg, &buf[0], 15);
DumpBytes(buf, 19, "crypted");
}

View file

@ -0,0 +1,36 @@
#include "CryptState.h"
#include <stdio.h>
unsigned char msg[] = {
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
};
unsigned char rawkey[] = { 0x96, 0x8b, 0x1b, 0x0c, 0x53, 0x1e, 0x1f, 0x80, 0xa6, 0x1d, 0xcb, 0x27, 0x94, 0x09, 0x6f, 0x32, };
unsigned char encrypt_iv[] = { 0x1e, 0x2a, 0x9b, 0xd0, 0x2d, 0xa6, 0x8e, 0x46, 0x26, 0x85, 0x83, 0xe9, 0x14, 0x2a, 0xff, 0x2a, };
unsigned char decrypt_iv[] = { 0x73, 0x99, 0x9d, 0xa2, 0x03, 0x70, 0x00, 0x96, 0xef, 0x55, 0x06, 0x7a, 0x8b, 0xbe, 0x00, 0x07, };
unsigned char crypted[] = { 0x1f, 0xfc, 0xdd, 0xb4, 0x68, 0x13, 0x68, 0xb7, 0x92, 0x67, 0xca, 0x2d, 0xba, 0xb7, 0x0d, 0x44, 0xdf, 0x32, 0xd4, };
static void DumpBytes(unsigned char *bytes, unsigned int len, const char *name) {
printf("unsigned char %s[] = { ", name);
for (int i = 0; i < len; i++) {
printf("0x%.2x, ", bytes[i]);
}
printf("}\n");
}
int main(int argc, char *argv[]) {
MumbleClient::CryptState cs;
// cs.genKey();
cs.setKey(rawkey, encrypt_iv, decrypt_iv);
DumpBytes(cs.raw_key, AES_BLOCK_SIZE, "rawkey");
DumpBytes(cs.encrypt_iv, AES_BLOCK_SIZE, "encrypt_iv");
DumpBytes(cs.decrypt_iv, AES_BLOCK_SIZE, "decrypt_iv");
unsigned char buf[19];
cs.encrypt(msg, &buf[0], 15);
DumpBytes(buf, 19, "crypted");
DumpBytes(cs.encrypt_iv, AES_BLOCK_SIZE, "post_eiv");
}

View file

@ -0,0 +1,34 @@
#include "CryptState.h"
#include <stdio.h>
unsigned char msg[] = {
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
};
unsigned char rawkey[] = { 0x96, 0x8b, 0x1b, 0x0c, 0x53, 0x1e, 0x1f, 0x80, 0xa6, 0x1d, 0xcb, 0x27, 0x94, 0x09, 0x6f, 0x32, };
unsigned char encrypt_iv[] = { 0x1e, 0x2a, 0x9b, 0xd0, 0x2d, 0xa6, 0x8e, 0x46, 0x26, 0x85, 0x83, 0xe9, 0x14, 0x2a, 0xff, 0x2a, };
unsigned char decrypt_iv[] = { 0x73, 0x99, 0x9d, 0xa2, 0x03, 0x70, 0x00, 0x96, 0xef, 0x55, 0x06, 0x7a, 0x8b, 0xbe, 0x00, 0x07, };
unsigned char crypted[] = { 0x1f, 0xfc, 0xdd, 0xb4, 0x68, 0x13, 0x68, 0xb7, 0x92, 0x67, 0xca, 0x2d, 0xba, 0xb7, 0x0d, 0x44, 0xdf, 0x32, 0xd4, };
static void DumpBytes(unsigned char *bytes, unsigned int len, const char *name) {
printf("unsigned char %s[] = { ", name);
for (int i = 0; i < len; i++) {
printf("0x%.2x, ", bytes[i]);
}
printf("}\n");
}
int main(int argc, char *argv[]) {
MumbleClient::CryptState cs;
cs.setKey(rawkey, decrypt_iv, encrypt_iv);
DumpBytes(cs.raw_key, AES_BLOCK_SIZE, "rawkey");
DumpBytes(cs.encrypt_iv, AES_BLOCK_SIZE, "encrypt_iv");
DumpBytes(cs.decrypt_iv, AES_BLOCK_SIZE, "decrypt_iv");
unsigned char buf[15];
cs.decrypt(crypted, &buf[0], 19);
DumpBytes(buf, 15, "plain");
DumpBytes(cs.decrypt_iv, AES_BLOCK_SIZE, "post_div");
}

7
pkg/mumbleproto/Makefile Normal file
View file

@ -0,0 +1,7 @@
include $(GOROOT)/src/Make.inc
TARG = mumbleproto
GOFILES = Mumble.pb.go
include $(GOROOT)/src/Make.pkg
include $(GOROOT)/src/pkg/goprotobuf.googlecode.com/hg/Make.protobuf

View file

@ -0,0 +1,502 @@
// Code generated by protoc-gen-go from "Mumble.proto"
// DO NOT EDIT!
package mumbleproto
import proto "goprotobuf.googlecode.com/hg/proto"
// Reference proto import to suppress error if it's not otherwise used.
var _ = proto.GetString
type Reject_RejectType int32
const (
Reject_None = 0
Reject_WrongVersion = 1
Reject_InvalidUsername = 2
Reject_WrongUserPW = 3
Reject_WrongServerPW = 4
Reject_UsernameInUse = 5
Reject_ServerFull = 6
Reject_NoCertificate = 7
)
var Reject_RejectType_name = map[int32] string {
0: "None",
1: "WrongVersion",
2: "InvalidUsername",
3: "WrongUserPW",
4: "WrongServerPW",
5: "UsernameInUse",
6: "ServerFull",
7: "NoCertificate",
}
var Reject_RejectType_value = map[string] int32 {
"None": 0,
"WrongVersion": 1,
"InvalidUsername": 2,
"WrongUserPW": 3,
"WrongServerPW": 4,
"UsernameInUse": 5,
"ServerFull": 6,
"NoCertificate": 7,
}
func NewReject_RejectType(x int32) *Reject_RejectType {
e := Reject_RejectType(x)
return &e
}
type PermissionDenied_DenyType int32
const (
PermissionDenied_Text = 0
PermissionDenied_Permission = 1
PermissionDenied_SuperUser = 2
PermissionDenied_ChannelName = 3
PermissionDenied_TextTooLong = 4
PermissionDenied_H9K = 5
PermissionDenied_TemporaryChannel = 6
PermissionDenied_MissingCertificate = 7
PermissionDenied_UserName = 8
PermissionDenied_ChannelFull = 9
)
var PermissionDenied_DenyType_name = map[int32] string {
0: "Text",
1: "Permission",
2: "SuperUser",
3: "ChannelName",
4: "TextTooLong",
5: "H9K",
6: "TemporaryChannel",
7: "MissingCertificate",
8: "UserName",
9: "ChannelFull",
}
var PermissionDenied_DenyType_value = map[string] int32 {
"Text": 0,
"Permission": 1,
"SuperUser": 2,
"ChannelName": 3,
"TextTooLong": 4,
"H9K": 5,
"TemporaryChannel": 6,
"MissingCertificate": 7,
"UserName": 8,
"ChannelFull": 9,
}
func NewPermissionDenied_DenyType(x int32) *PermissionDenied_DenyType {
e := PermissionDenied_DenyType(x)
return &e
}
type ContextActionAdd_Context int32
const (
ContextActionAdd_Server = 1
ContextActionAdd_Channel = 2
ContextActionAdd_User = 4
)
var ContextActionAdd_Context_name = map[int32] string {
1: "Server",
2: "Channel",
4: "User",
}
var ContextActionAdd_Context_value = map[string] int32 {
"Server": 1,
"Channel": 2,
"User": 4,
}
func NewContextActionAdd_Context(x int32) *ContextActionAdd_Context {
e := ContextActionAdd_Context(x)
return &e
}
type Version struct {
Version *uint32 "PB(varint,1,opt,name=version)"
Release *string "PB(bytes,2,opt,name=release)"
Os *string "PB(bytes,3,opt,name=os)"
OsVersion *string "PB(bytes,4,opt,name=os_version)"
XXX_unrecognized []byte
}
func (this *Version) Reset() {
*this = Version{}
}
type UDPTunnel struct {
Packet []byte "PB(bytes,1,req,name=packet)"
XXX_unrecognized []byte
}
func (this *UDPTunnel) Reset() {
*this = UDPTunnel{}
}
type Authenticate struct {
Username *string "PB(bytes,1,opt,name=username)"
Password *string "PB(bytes,2,opt,name=password)"
Tokens []string "PB(bytes,3,rep,name=tokens)"
CeltVersions []int32 "PB(varint,4,rep,name=celt_versions)"
XXX_unrecognized []byte
}
func (this *Authenticate) Reset() {
*this = Authenticate{}
}
type Ping struct {
Timestamp *uint64 "PB(varint,1,opt,name=timestamp)"
Good *uint32 "PB(varint,2,opt,name=good)"
Late *uint32 "PB(varint,3,opt,name=late)"
Lost *uint32 "PB(varint,4,opt,name=lost)"
Resync *uint32 "PB(varint,5,opt,name=resync)"
UdpPackets *uint32 "PB(varint,6,opt,name=udp_packets)"
TcpPackets *uint32 "PB(varint,7,opt,name=tcp_packets)"
UdpPingAvg *float32 "PB(fixed32,8,opt,name=udp_ping_avg)"
UdpPingVar *float32 "PB(fixed32,9,opt,name=udp_ping_var)"
TcpPingAvg *float32 "PB(fixed32,10,opt,name=tcp_ping_avg)"
TcpPingVar *float32 "PB(fixed32,11,opt,name=tcp_ping_var)"
XXX_unrecognized []byte
}
func (this *Ping) Reset() {
*this = Ping{}
}
type Reject struct {
Type *Reject_RejectType "PB(varint,1,opt,name=type,enum=mumbleproto.Reject_RejectType)"
Reason *string "PB(bytes,2,opt,name=reason)"
XXX_unrecognized []byte
}
func (this *Reject) Reset() {
*this = Reject{}
}
type ServerConfig struct {
MaxBandwidth *uint32 "PB(varint,1,opt,name=max_bandwidth)"
WelcomeText *string "PB(bytes,2,opt,name=welcome_text)"
AllowHtml *bool "PB(varint,3,opt,name=allow_html)"
MessageLength *uint32 "PB(varint,4,opt,name=message_length)"
ImageMessageLength *uint32 "PB(varint,5,opt,name=image_message_length)"
XXX_unrecognized []byte
}
func (this *ServerConfig) Reset() {
*this = ServerConfig{}
}
type ServerSync struct {
Session *uint32 "PB(varint,1,opt,name=session)"
MaxBandwidth *uint32 "PB(varint,2,opt,name=max_bandwidth)"
WelcomeText *string "PB(bytes,3,opt,name=welcome_text)"
Permissions *uint64 "PB(varint,4,opt,name=permissions)"
XXX_unrecognized []byte
}
func (this *ServerSync) Reset() {
*this = ServerSync{}
}
type ChannelRemove struct {
ChannelId *uint32 "PB(varint,1,req,name=channel_id)"
XXX_unrecognized []byte
}
func (this *ChannelRemove) Reset() {
*this = ChannelRemove{}
}
type ChannelState struct {
ChannelId *uint32 "PB(varint,1,opt,name=channel_id)"
Parent *uint32 "PB(varint,2,opt,name=parent)"
Name *string "PB(bytes,3,opt,name=name)"
Links []uint32 "PB(varint,4,rep,name=links)"
Description *string "PB(bytes,5,opt,name=description)"
LinksAdd []uint32 "PB(varint,6,rep,name=links_add)"
LinksRemove []uint32 "PB(varint,7,rep,name=links_remove)"
Temporary *bool "PB(varint,8,opt,name=temporary,def=0)"
Position *int32 "PB(varint,9,opt,name=position,def=0)"
DescriptionHash []byte "PB(bytes,10,opt,name=description_hash)"
XXX_unrecognized []byte
}
func (this *ChannelState) Reset() {
*this = ChannelState{}
}
const Default_ChannelState_Temporary bool = false
const Default_ChannelState_Position int32 = 0
type UserRemove struct {
Session *uint32 "PB(varint,1,req,name=session)"
Actor *uint32 "PB(varint,2,opt,name=actor)"
Reason *string "PB(bytes,3,opt,name=reason)"
Ban *bool "PB(varint,4,opt,name=ban)"
XXX_unrecognized []byte
}
func (this *UserRemove) Reset() {
*this = UserRemove{}
}
type UserState struct {
Session *uint32 "PB(varint,1,opt,name=session)"
Actor *uint32 "PB(varint,2,opt,name=actor)"
Name *string "PB(bytes,3,opt,name=name)"
UserId *uint32 "PB(varint,4,opt,name=user_id)"
ChannelId *uint32 "PB(varint,5,opt,name=channel_id)"
Mute *bool "PB(varint,6,opt,name=mute)"
Deaf *bool "PB(varint,7,opt,name=deaf)"
Suppress *bool "PB(varint,8,opt,name=suppress)"
SelfMute *bool "PB(varint,9,opt,name=self_mute)"
SelfDeaf *bool "PB(varint,10,opt,name=self_deaf)"
Texture []byte "PB(bytes,11,opt,name=texture)"
PluginContext []byte "PB(bytes,12,opt,name=plugin_context)"
PluginIdentity *string "PB(bytes,13,opt,name=plugin_identity)"
Comment *string "PB(bytes,14,opt,name=comment)"
Hash *string "PB(bytes,15,opt,name=hash)"
CommentHash []byte "PB(bytes,16,opt,name=comment_hash)"
TextureHash []byte "PB(bytes,17,opt,name=texture_hash)"
XXX_unrecognized []byte
}
func (this *UserState) Reset() {
*this = UserState{}
}
type BanList struct {
Bans []*BanList_BanEntry "PB(bytes,1,rep,name=bans)"
Query *bool "PB(varint,2,opt,name=query,def=0)"
XXX_unrecognized []byte
}
func (this *BanList) Reset() {
*this = BanList{}
}
const Default_BanList_Query bool = false
type BanList_BanEntry struct {
Address []byte "PB(bytes,1,req,name=address)"
Mask *uint32 "PB(varint,2,req,name=mask)"
Name *string "PB(bytes,3,opt,name=name)"
Hash *string "PB(bytes,4,opt,name=hash)"
Reason *string "PB(bytes,5,opt,name=reason)"
Start *string "PB(bytes,6,opt,name=start)"
Duration *uint32 "PB(varint,7,opt,name=duration)"
XXX_unrecognized []byte
}
func (this *BanList_BanEntry) Reset() {
*this = BanList_BanEntry{}
}
type TextMessage struct {
Actor *uint32 "PB(varint,1,opt,name=actor)"
Session []uint32 "PB(varint,2,rep,name=session)"
ChannelId []uint32 "PB(varint,3,rep,name=channel_id)"
TreeId []uint32 "PB(varint,4,rep,name=tree_id)"
Message *string "PB(bytes,5,req,name=message)"
XXX_unrecognized []byte
}
func (this *TextMessage) Reset() {
*this = TextMessage{}
}
type PermissionDenied struct {
Permission *uint32 "PB(varint,1,opt,name=permission)"
ChannelId *uint32 "PB(varint,2,opt,name=channel_id)"
Session *uint32 "PB(varint,3,opt,name=session)"
Reason *string "PB(bytes,4,opt,name=reason)"
Type *PermissionDenied_DenyType "PB(varint,5,opt,name=type,enum=mumbleproto.PermissionDenied_DenyType)"
Name *string "PB(bytes,6,opt,name=name)"
XXX_unrecognized []byte
}
func (this *PermissionDenied) Reset() {
*this = PermissionDenied{}
}
type ACL struct {
ChannelId *uint32 "PB(varint,1,req,name=channel_id)"
InheritAcls *bool "PB(varint,2,opt,name=inherit_acls,def=1)"
Groups []*ACL_ChanGroup "PB(bytes,3,rep,name=groups)"
Acls []*ACL_ChanACL "PB(bytes,4,rep,name=acls)"
Query *bool "PB(varint,5,opt,name=query,def=0)"
XXX_unrecognized []byte
}
func (this *ACL) Reset() {
*this = ACL{}
}
const Default_ACL_InheritAcls bool = true
const Default_ACL_Query bool = false
type ACL_ChanGroup struct {
Name *string "PB(bytes,1,req,name=name)"
Inherited *bool "PB(varint,2,opt,name=inherited,def=1)"
Inherit *bool "PB(varint,3,opt,name=inherit,def=1)"
Inheritable *bool "PB(varint,4,opt,name=inheritable,def=1)"
Add []uint32 "PB(varint,5,rep,name=add)"
Remove []uint32 "PB(varint,6,rep,name=remove)"
InheritedMembers []uint32 "PB(varint,7,rep,name=inherited_members)"
XXX_unrecognized []byte
}
func (this *ACL_ChanGroup) Reset() {
*this = ACL_ChanGroup{}
}
const Default_ACL_ChanGroup_Inherited bool = true
const Default_ACL_ChanGroup_Inherit bool = true
const Default_ACL_ChanGroup_Inheritable bool = true
type ACL_ChanACL struct {
ApplyHere *bool "PB(varint,1,opt,name=apply_here,def=1)"
ApplySubs *bool "PB(varint,2,opt,name=apply_subs,def=1)"
Inherited *bool "PB(varint,3,opt,name=inherited,def=1)"
UserId *uint32 "PB(varint,4,opt,name=user_id)"
Group *string "PB(bytes,5,opt,name=group)"
Grant *uint32 "PB(varint,6,opt,name=grant)"
Deny *uint32 "PB(varint,7,opt,name=deny)"
XXX_unrecognized []byte
}
func (this *ACL_ChanACL) Reset() {
*this = ACL_ChanACL{}
}
const Default_ACL_ChanACL_ApplyHere bool = true
const Default_ACL_ChanACL_ApplySubs bool = true
const Default_ACL_ChanACL_Inherited bool = true
type QueryUsers struct {
Ids []uint32 "PB(varint,1,rep,name=ids)"
Names []string "PB(bytes,2,rep,name=names)"
XXX_unrecognized []byte
}
func (this *QueryUsers) Reset() {
*this = QueryUsers{}
}
type CryptSetup struct {
Key []byte "PB(bytes,1,opt,name=key)"
ClientNonce []byte "PB(bytes,2,opt,name=client_nonce)"
ServerNonce []byte "PB(bytes,3,opt,name=server_nonce)"
XXX_unrecognized []byte
}
func (this *CryptSetup) Reset() {
*this = CryptSetup{}
}
type ContextActionAdd struct {
Action *string "PB(bytes,1,req,name=action)"
Text *string "PB(bytes,2,req,name=text)"
Context *uint32 "PB(varint,3,opt,name=context)"
XXX_unrecognized []byte
}
func (this *ContextActionAdd) Reset() {
*this = ContextActionAdd{}
}
type ContextAction struct {
Session *uint32 "PB(varint,1,opt,name=session)"
ChannelId *uint32 "PB(varint,2,opt,name=channel_id)"
Action *string "PB(bytes,3,req,name=action)"
XXX_unrecognized []byte
}
func (this *ContextAction) Reset() {
*this = ContextAction{}
}
type UserList struct {
Users []*UserList_User "PB(bytes,1,rep,name=users)"
XXX_unrecognized []byte
}
func (this *UserList) Reset() {
*this = UserList{}
}
type UserList_User struct {
UserId *uint32 "PB(varint,1,req,name=user_id)"
Name *string "PB(bytes,2,opt,name=name)"
XXX_unrecognized []byte
}
func (this *UserList_User) Reset() {
*this = UserList_User{}
}
type VoiceTarget struct {
Id *uint32 "PB(varint,1,opt,name=id)"
Targets []*VoiceTarget_Target "PB(bytes,2,rep,name=targets)"
XXX_unrecognized []byte
}
func (this *VoiceTarget) Reset() {
*this = VoiceTarget{}
}
type VoiceTarget_Target struct {
Session []uint32 "PB(varint,1,rep,name=session)"
ChannelId *uint32 "PB(varint,2,opt,name=channel_id)"
Group *string "PB(bytes,3,opt,name=group)"
Links *bool "PB(varint,4,opt,name=links,def=0)"
Children *bool "PB(varint,5,opt,name=children,def=0)"
XXX_unrecognized []byte
}
func (this *VoiceTarget_Target) Reset() {
*this = VoiceTarget_Target{}
}
const Default_VoiceTarget_Target_Links bool = false
const Default_VoiceTarget_Target_Children bool = false
type PermissionQuery struct {
ChannelId *uint32 "PB(varint,1,opt,name=channel_id)"
Permissions *uint32 "PB(varint,2,opt,name=permissions)"
Flush *bool "PB(varint,3,opt,name=flush,def=0)"
XXX_unrecognized []byte
}
func (this *PermissionQuery) Reset() {
*this = PermissionQuery{}
}
const Default_PermissionQuery_Flush bool = false
type CodecVersion struct {
Alpha *int32 "PB(varint,1,req,name=alpha)"
Beta *int32 "PB(varint,2,req,name=beta)"
PreferAlpha *bool "PB(varint,3,req,name=prefer_alpha,def=1)"
XXX_unrecognized []byte
}
func (this *CodecVersion) Reset() {
*this = CodecVersion{}
}
const Default_CodecVersion_PreferAlpha bool = true
type UserStats struct {
Session *uint32 "PB(varint,1,opt,name=session)"
StatsOnly *bool "PB(varint,2,opt,name=stats_only,def=0)"
Certificates [][]byte "PB(bytes,3,rep,name=certificates)"
FromClient *UserStats_Stats "PB(bytes,4,opt,name=from_client)"
FromServer *UserStats_Stats "PB(bytes,5,opt,name=from_server)"
UdpPackets *uint32 "PB(varint,6,opt,name=udp_packets)"
TcpPackets *uint32 "PB(varint,7,opt,name=tcp_packets)"
UdpPingAvg *float32 "PB(fixed32,8,opt,name=udp_ping_avg)"
UdpPingVar *float32 "PB(fixed32,9,opt,name=udp_ping_var)"
TcpPingAvg *float32 "PB(fixed32,10,opt,name=tcp_ping_avg)"
TcpPingVar *float32 "PB(fixed32,11,opt,name=tcp_ping_var)"
Version *Version "PB(bytes,12,opt,name=version)"
CeltVersions []int32 "PB(varint,13,rep,name=celt_versions)"
Address []byte "PB(bytes,14,opt,name=address)"
Bandwidth *uint32 "PB(varint,15,opt,name=bandwidth)"
Onlinesecs *uint32 "PB(varint,16,opt,name=onlinesecs)"
Idlesecs *uint32 "PB(varint,17,opt,name=idlesecs)"
StrongCertificate *bool "PB(varint,18,opt,name=strong_certificate,def=0)"
XXX_unrecognized []byte
}
func (this *UserStats) Reset() {
*this = UserStats{}
}
const Default_UserStats_StatsOnly bool = false
const Default_UserStats_StrongCertificate bool = false
type UserStats_Stats struct {
Good *uint32 "PB(varint,1,opt,name=good)"
Late *uint32 "PB(varint,2,opt,name=late)"
Lost *uint32 "PB(varint,3,opt,name=lost)"
Resync *uint32 "PB(varint,4,opt,name=resync)"
XXX_unrecognized []byte
}
func (this *UserStats_Stats) Reset() {
*this = UserStats_Stats{}
}
type RequestBlob struct {
SessionTexture []uint32 "PB(varint,1,rep,name=session_texture)"
SessionComment []uint32 "PB(varint,2,rep,name=session_comment)"
ChannelDescription []uint32 "PB(varint,3,rep,name=channel_description)"
XXX_unrecognized []byte
}
func (this *RequestBlob) Reset() {
*this = RequestBlob{}
}
func init() {
proto.RegisterEnum("mumbleproto.Reject_RejectType", Reject_RejectType_name, Reject_RejectType_value)
proto.RegisterEnum("mumbleproto.PermissionDenied_DenyType", PermissionDenied_DenyType_name, PermissionDenied_DenyType_value)
proto.RegisterEnum("mumbleproto.ContextActionAdd_Context", ContextActionAdd_Context_name, ContextActionAdd_Context_value)
}

View file

@ -0,0 +1,274 @@
package mumbleproto;
option optimize_for = SPEED;
message Version {
optional uint32 version = 1;
optional string release = 2;
optional string os = 3;
optional string os_version = 4;
}
message UDPTunnel {
required bytes packet = 1;
}
message Authenticate {
optional string username = 1;
optional string password = 2;
repeated string tokens = 3;
repeated int32 celt_versions = 4;
}
message Ping {
optional uint64 timestamp = 1;
optional uint32 good = 2;
optional uint32 late = 3;
optional uint32 lost = 4;
optional uint32 resync = 5;
optional uint32 udp_packets = 6;
optional uint32 tcp_packets = 7;
optional float udp_ping_avg = 8;
optional float udp_ping_var = 9;
optional float tcp_ping_avg = 10;
optional float tcp_ping_var = 11;
}
message Reject {
enum RejectType {
None = 0;
WrongVersion = 1;
InvalidUsername = 2;
WrongUserPW = 3;
WrongServerPW = 4;
UsernameInUse = 5;
ServerFull = 6;
NoCertificate = 7;
}
optional RejectType type = 1;
optional string reason = 2;
}
message ServerConfig {
optional uint32 max_bandwidth = 1;
optional string welcome_text = 2;
optional bool allow_html = 3;
optional uint32 message_length = 4;
optional uint32 image_message_length = 5;
}
message ServerSync {
optional uint32 session = 1;
optional uint32 max_bandwidth = 2;
optional string welcome_text = 3;
optional uint64 permissions = 4;
}
message ChannelRemove {
required uint32 channel_id = 1;
}
message ChannelState {
optional uint32 channel_id = 1;
optional uint32 parent = 2;
optional string name = 3;
repeated uint32 links = 4;
optional string description = 5;
repeated uint32 links_add = 6;
repeated uint32 links_remove = 7;
optional bool temporary = 8 [default = false];
optional int32 position = 9 [default = 0];
optional bytes description_hash = 10;
}
message UserRemove {
required uint32 session = 1;
optional uint32 actor = 2;
optional string reason = 3;
optional bool ban = 4;
}
message UserState {
optional uint32 session = 1;
optional uint32 actor = 2;
optional string name = 3;
optional uint32 user_id = 4;
optional uint32 channel_id = 5;
optional bool mute = 6;
optional bool deaf = 7;
optional bool suppress = 8;
optional bool self_mute = 9;
optional bool self_deaf = 10;
optional bytes texture = 11;
optional bytes plugin_context = 12;
optional string plugin_identity = 13;
optional string comment = 14;
optional string hash = 15;
optional bytes comment_hash = 16;
optional bytes texture_hash = 17;
}
message BanList {
message BanEntry {
required bytes address = 1;
required uint32 mask = 2;
optional string name = 3;
optional string hash = 4;
optional string reason = 5;
optional string start = 6;
optional uint32 duration = 7;
}
repeated BanEntry bans = 1;
optional bool query = 2 [default = false];
}
message TextMessage {
optional uint32 actor = 1;
repeated uint32 session = 2;
repeated uint32 channel_id = 3;
repeated uint32 tree_id = 4;
required string message = 5;
}
message PermissionDenied {
enum DenyType {
Text = 0;
Permission = 1;
SuperUser = 2;
ChannelName = 3;
TextTooLong = 4;
H9K = 5;
TemporaryChannel = 6;
MissingCertificate = 7;
UserName = 8;
ChannelFull = 9;
}
optional uint32 permission = 1;
optional uint32 channel_id = 2;
optional uint32 session = 3;
optional string reason = 4;
optional DenyType type = 5;
optional string name = 6;
}
message ACL {
message ChanGroup {
required string name = 1;
optional bool inherited = 2 [default = true];
optional bool inherit = 3 [default = true];
optional bool inheritable = 4 [default = true];
repeated uint32 add = 5;
repeated uint32 remove = 6;
repeated uint32 inherited_members = 7;
}
message ChanACL {
optional bool apply_here = 1 [default = true];
optional bool apply_subs = 2 [default = true];
optional bool inherited = 3 [default = true];
optional uint32 user_id = 4;
optional string group = 5;
optional uint32 grant = 6;
optional uint32 deny = 7;
}
required uint32 channel_id = 1;
optional bool inherit_acls = 2 [default = true];
repeated ChanGroup groups = 3;
repeated ChanACL acls = 4;
optional bool query = 5 [default = false];
}
message QueryUsers {
repeated uint32 ids = 1;
repeated string names = 2;
}
message CryptSetup {
optional bytes key = 1;
optional bytes client_nonce = 2;
optional bytes server_nonce = 3;
}
message ContextActionAdd {
enum Context {
Server = 0x01;
Channel = 0x02;
User = 0x04;
}
required string action = 1;
required string text = 2;
optional uint32 context = 3;
}
message ContextAction {
optional uint32 session = 1;
optional uint32 channel_id = 2;
required string action = 3;
}
message UserList {
message User {
required uint32 user_id = 1;
optional string name = 2;
}
repeated User users = 1;
}
message VoiceTarget {
message Target {
repeated uint32 session = 1;
optional uint32 channel_id = 2;
optional string group = 3;
optional bool links = 4 [default = false];
optional bool children = 5 [default = false];
}
optional uint32 id = 1;
repeated Target targets = 2;
}
message PermissionQuery {
optional uint32 channel_id = 1;
optional uint32 permissions = 2;
optional bool flush = 3 [default = false];
}
message CodecVersion {
required int32 alpha = 1;
required int32 beta = 2;
required bool prefer_alpha = 3 [default = true];
}
message UserStats {
message Stats {
optional uint32 good = 1;
optional uint32 late = 2;
optional uint32 lost = 3;
optional uint32 resync = 4;
}
optional uint32 session = 1;
optional bool stats_only = 2 [default = false];
repeated bytes certificates = 3;
optional Stats from_client = 4;
optional Stats from_server = 5;
optional uint32 udp_packets = 6;
optional uint32 tcp_packets = 7;
optional float udp_ping_avg = 8;
optional float udp_ping_var = 9;
optional float tcp_ping_avg = 10;
optional float tcp_ping_var = 11;
optional Version version = 12;
repeated int32 celt_versions = 13;
optional bytes address = 14;
optional uint32 bandwidth = 15;
optional uint32 onlinesecs = 16;
optional uint32 idlesecs = 17;
optional bool strong_certificate = 18 [default = false];
}
message RequestBlob {
repeated uint32 session_texture = 1;
repeated uint32 session_comment = 2;
repeated uint32 channel_description = 3;
}

View file

@ -0,0 +1,6 @@
include $(GOROOT)/src/Make.inc
TARG = packetdatastream
GOFILES = packetdatastream.go
include $(GOROOT)/src/Make.pkg

View file

@ -0,0 +1,326 @@
// Grumble - an implementation of Murmur in Go
// Copyright (c) 2010 The Grumble Authors
// The use of this source code is goverened by a BSD-style
// license that can be found in the LICENSE-file.
package packetdatastream
import (
"math"
)
type PacketDataStream struct {
Buf []byte
offset int
maxsize int
overshoot int
ok bool
}
func New(buf []byte) (pds *PacketDataStream) {
pds = new(PacketDataStream)
pds.Buf = buf
pds.maxsize = len(buf)
pds.ok = true
return
}
func (pds *PacketDataStream) IsValid() bool {
return pds.ok
}
func (pds *PacketDataStream) Skip(skip int) {
if pds.Left() >= skip {
pds.offset += skip
} else {
pds.ok = false
}
}
// Returns number of bytes remaining in
// the buffer.
func (pds *PacketDataStream) Left() int {
return int(pds.maxsize - pds.offset)
}
// Returns the size of the currently-assembled data
// stream
func (pds *PacketDataStream) Size() int {
return pds.offset
}
// Get the next byte from the PacketDataStream as a uint64
func (pds *PacketDataStream) next() (ret uint64) {
if pds.offset < pds.maxsize {
ret = uint64(pds.Buf[pds.offset])
pds.offset += 1
return
} else {
pds.ok = false
}
return 0
}
// Get the next byte from the PacketDataStream as a byte (uint8)
func (pds *PacketDataStream) Next8() (ret uint8) {
if pds.offset < pds.maxsize {
ret = uint8(pds.Buf[pds.offset])
pds.offset += 1
return
} else {
pds.ok = false
}
return 0
}
// Put a byte (represented in an uint64) into the
// PacketDataStream.
func (pds *PacketDataStream) append(val uint64) {
if (val > 0xff) {
pds.ok = false
return
}
if pds.offset < pds.maxsize {
pds.Buf[pds.offset] = byte(val)
pds.offset += 1
} else {
pds.ok = false
pds.overshoot++
}
}
// Add a variably-sized integer to the PacketDataStream.
// The PacketDataStream will figure out the most efficient
// encoding based on the binary representation of the value.
func (pds *PacketDataStream) addVarint(val uint64) {
i := val
if (i & 0x8000000000000000) != 0 && ^i < 0x100000000 {
// Signed number
i = ^i
if i <= 0x3 {
// Short for -1 to -4
pds.append(0xfc | i)
} else {
pds.append(0xf8)
}
}
if i < 0x80 {
// Needs top bit clear
pds.append(i)
} else if i < 0x4000 {
// Needs two top bits clear
pds.append((i >> 8) | 0x80)
pds.append(i & 0xff)
} else if i < 0x10000000 {
// Needs three top bits clear
pds.append((i >> 16) | 0xc0)
pds.append((i >> 8) & 0xff)
pds.append(i & 0xff)
} else if i < 0x100000000 {
// Full 32 bit integer
pds.append(0xf0)
pds.append((i >> 24) & 0xff)
pds.append((i >> 16) & 0xff)
pds.append((i >> 8) & 0xff)
pds.append(i & 0xff)
} else {
// 64 bit val
pds.append(0xf4)
pds.append((i >> 56) & 0xff)
pds.append((i >> 48) & 0xff)
pds.append((i >> 40) & 0xff)
pds.append((i >> 32) & 0xff)
pds.append((i >> 24) & 0xff)
pds.append((i >> 16) & 0xff)
pds.append((i >> 8) & 0xff)
pds.append(i & 0xff)
}
}
func (pds *PacketDataStream) getVarint() (i uint64) {
v := pds.next()
if (v & 0x80) == 0x00 {
i = (v & 0x7f)
} else if (v & 0xc0) == 0x80 {
i = (v & 0x3f) << 8 | pds.next()
} else if (v & 0xf0) == 0xf0 {
switch v & 0xfc {
case 0xf0:
i = pds.next() << 24 | pds.next() << 16 | pds.next() << 8 | pds.next()
case 0xf4:
i = pds.next() << 56 | pds.next() << 48 | pds.next() << 40 | pds.next() << 32 | pds.next() << 24 | pds.next() << 16 | pds.next() << 8 | pds.next()
case 0xf8:
i = ^pds.getVarint()
case 0xfc:
i = ^(v & 0x03)
default:
pds.ok = false
i = 0
}
} else if (v & 0xf0) == 0xe0 {
i = (v & 0x0f) << 24 | pds.next() << 16 | pds.next() << 8 | pds.next()
} else if (v & 0xe0) == 0xc0 {
i = (v & 0x1f) << 16 | pds.next() << 8 | pds.next()
}
return
}
// Read a uint64 from the PacketDataStream
func (pds *PacketDataStream) GetUint64() uint64 {
return pds.getVarint()
}
// Write a uint64 to the PacketDataStream
func (pds *PacketDataStream) PutUint64(val uint64) {
pds.addVarint(val)
}
// Read a uint32 from the PacketDataStream
func (pds *PacketDataStream) GetUint32() uint32 {
return uint32(pds.getVarint())
}
// Write a uint32 to the PacketDataStream
func (pds *PacketDataStream) PutUint32(val uint32) {
pds.addVarint(uint64(val))
}
// Read a uint16 from the PacketDataStream
func (pds *PacketDataStream) GetUint16() uint16 {
return uint16(pds.getVarint())
}
// Write a uint16 to the PacketDataStream
func (pds *PacketDataStream) PutUint16(val uint16) {
pds.addVarint(uint64(val))
}
// Read a uint8 from the PacketDataStream
func (pds *PacketDataStream) GetUint8() uint8 {
varint := pds.getVarint()
return uint8(varint)
}
// Write a uint8 to the PacketDataStream
func (pds *PacketDataStream) PutUint8(val uint8) {
pds.addVarint(uint64(val))
}
// Read a int64 from the PacketDataStream
func (pds *PacketDataStream) GetInt64() int64 {
return int64(pds.getVarint())
}
// Write a int64 to the PacketDataStream
func (pds *PacketDataStream) PutInt64(val int64) {
pds.addVarint(uint64(val))
}
// Read a int32 from the PacketDataStream
func (pds *PacketDataStream) GetInt32() int32 {
return int32(pds.getVarint())
}
// Write a int32 to the PacketDataStream
func (pds *PacketDataStream) PutInt32(val int32) {
pds.addVarint(uint64(val))
}
// Read a int16 from the PacketDataStream
func (pds *PacketDataStream) GetInt16() int16 {
return int16(pds.getVarint())
}
// Write a int16 to the PacketDataStream
func (pds *PacketDataStream) PutInt16(val int16) {
pds.addVarint(uint64(val))
}
// Read a int8 from the PacketDataStream
func (pds *PacketDataStream) GetInt8() int8 {
return int8(pds.getVarint())
}
// Write a int8 to the PacketDataStream
func (pds *PacketDataStream) PutInt8(val int8) {
pds.addVarint(uint64(val))
}
// Read a float32 from the PacketDataStream
func (pds *PacketDataStream) GetFloat32() float32 {
if pds.Left() < 4 {
pds.ok = false
return 0
}
var val uint32
val = uint32(pds.Next8()) << 24 | uint32(pds.Next8()) << 16 | uint32(pds.Next8()) << 8 | uint32(pds.Next8())
return math.Float32frombits(val)
}
// Write a float32 to the PacketDataStream
func (pds *PacketDataStream) PutFloat32(val float32) {
bits := math.Float32bits(val)
pds.append(uint64((bits >> 24) & 0xff))
pds.append(uint64((bits >> 16) & 0xff))
pds.append(uint64((bits >> 8) & 0xff))
pds.append(uint64(bits & 0xff))
}
// Read a float64 from the PacketDataStream.
func (pds *PacketDataStream) GetFloat64() float64 {
if pds.Left() < 8 {
pds.ok = false
return 0
}
var val uint64
val = uint64(pds.Next8()) << 56 | uint64(pds.Next8()) << 48 | uint64(pds.Next8()) << 40 | uint64(pds.Next8()) << 32 | uint64(pds.Next8()) << 24 | uint64(pds.Next8()) << 16 | uint64(pds.Next8()) << 8 | uint64(pds.Next8())
return math.Float64frombits(val)
}
// Write a float64 to the PacketDataStream
func (pds *PacketDataStream) PutFloat64(val float64) {
bits := math.Float64bits(val)
pds.append((bits >> 56) & 0xff)
pds.append((bits >> 48) & 0xff)
pds.append((bits >> 40) & 0xff)
pds.append((bits >> 32) & 0xff)
pds.append((bits >> 24) & 0xff)
pds.append((bits >> 16) & 0xff)
pds.append((bits >> 8) & 0xff)
pds.append(bits & 0xff)
}
// Copy a buffer out of the PacketDataStream into dst.
func (pds *PacketDataStream) CopyBytes(dst []byte) {
if pds.Left() >= len(dst) {
if copy(dst, pds.Buf[pds.offset:pds.offset+len(dst)]) != len(dst) {
pds.ok = false
}
} else {
pds.ok = false
}
}
// Put a buffer src into the PacketDataStream at the
// current offset.
func (pds *PacketDataStream) PutBytes(src []byte) {
if pds.Left() >= len(src) {
if copy(pds.Buf[pds.offset:pds.offset+len(src)], src) != len(src) {
pds.ok = false
} else {
pds.offset += len(src)
}
} else {
pds.overshoot += len(src) - pds.Left()
pds.ok = false
}
}

View file

@ -0,0 +1,144 @@
package packetdatastream
import (
"testing"
"math"
"crypto/rand"
)
func TestSelfUint8(t *testing.T) {
buf := make([]byte, 500)
pds := New(buf)
for i := uint8(0); i < 0xff; i++ {
pds.PutUint8(i)
if !pds.IsValid() {
t.Errorf("Invalid PDS")
return
}
}
pds2 := New(pds.Buf)
for i := uint8(0); i < 0xff; i++ {
val := pds2.GetUint8()
if val != i {
t.Errorf("Mismatch (read: %v, expected: %v)", val, i)
return
}
}
}
func TestSelfUint64(t *testing.T) {
buf := make([]byte, 500)
pds := New(buf)
for i := uint64(1 << 54); i < (uint64(1 << 54)+10); i++ {
pds.PutUint64(i)
if !pds.IsValid() {
t.Errorf("Invalid PDS")
return
}
}
pds2 := New(buf)
for i := uint64(1 << 54); i < (uint64(1 << 54)+10); i++ {
val := pds2.GetUint64()
if !pds.IsValid() {
t.Errorf("Invalid PDS")
}
if val != i {
t.Errorf("Mismatch (read: %v, expected: %v)", val, i)
return
}
}
}
func TestSelfMumbleVoicePacket(t *testing.T) {
buf := make([]byte, 500)
pds := New(buf)
data := make([]byte, 54)
rand.Read(data)
pds.PutUint32(1)
pds.PutBytes(data)
pds2 := New(buf)
if pds2.GetUint32() != 1 {
t.Errorf("Session mismatch")
}
outbuf := make([]byte, 54)
pds2.CopyBytes(outbuf)
if !pds.IsValid() {
t.Errorf("Invalid PDS")
return
}
for i := 0; i < 54; i++ {
if outbuf[i] != data[i] {
t.Errorf("Voice data mismatch (got %v, expected %v)", outbuf[i], data[i])
return
}
}
}
func TestSelfFloat64(t *testing.T) {
buf := make([]byte, 500)
pds := New(buf)
pds2 := New(buf)
pds.PutFloat64(math.Pi)
pi := pds2.GetFloat64()
if !pds.IsValid() || !pds2.IsValid() {
t.Errorf("Invalid PDS")
return
}
if pi != float64(math.Pi) {
t.Errorf("Unexpected result. Got %v, expected %v", pi, float64(math.Pi))
return
}
}
func TestSelfFloat32(t *testing.T) {
buf := make([]byte, 500)
pds := New(buf)
pds2 := New(buf)
pds.PutFloat32(math.E)
e := pds2.GetFloat32()
if !pds.IsValid() || !pds2.IsValid() {
t.Errorf("Invalid PDS")
}
if e != float32(math.E) {
t.Errorf("Unexpected result. Got %v, expected %v", e, float32(math.E))
}
}
func TestSelfBytes(t *testing.T) {
msg := [15]byte{ 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf, }
buf := make([]byte, 500)
pds := New(buf)
pds2 := New(buf)
pds.PutBytes(msg[0:])
out := make([]byte, 15)
pds2.CopyBytes(out)
if !pds.IsValid() || !pds.IsValid() {
t.Errorf("Invalid PDS")
return
}
for i := 0; i < 15; i++ {
if msg[i] != out[i] {
t.Errorf("Mismatch at index %v. Got %v, expected %v", i, out[i], msg[i])
return
}
}
}

588
server.go Normal file
View file

@ -0,0 +1,588 @@
// Copyright (c) 2010 The Grumble Authors
// The use of this source code is goverened by a BSD-style
// license that can be found in the LICENSE-file.
package main
import (
"log"
"crypto/tls"
"os"
"net"
"bufio"
"bytes"
"encoding/binary"
"container/list"
"sync"
"goprotobuf.googlecode.com/hg/proto"
"mumbleproto"
"cryptstate"
)
// The default port a Murmur server listens on
const DefaultPort = 64738
const UDPPacketSize = 1024
const CeltCompatBitstream = -2147483638
// Client connection states
const (
StateClientConnected = iota
StateServerSentVersion
StateClientSentVersion
StateClientAuthenticated
StateClientDead
)
// A Murmur server instance
type Server struct {
listener tls.Listener
address string
port int
udpconn *net.UDPConn
incoming chan *Message
outgoing chan *Message
udpsend chan *Message
// Config-related
MaxUsers int
MaxBandwidth uint32
session uint32
// A list of all connected clients
cmutex *sync.RWMutex
clients *list.List
// Codec information
AlphaCodec int32
BetaCodec int32
PreferAlphaCodec bool
root *Channel
}
// A Mumble channel
type Channel struct {
Id int
Name string
Description string
Temporary bool
Position int
Channels *list.List
}
// Allocate a new Murmur instance
func NewServer(addr string, port int) (s *Server, err os.Error) {
s = new(Server)
s.address = addr
s.port = port
// Create the list of connected clients
s.cmutex = new(sync.RWMutex)
s.clients = list.New()
s.outgoing = make(chan *Message)
s.incoming = make(chan *Message)
s.udpsend = make(chan *Message)
s.MaxBandwidth = 300000
s.MaxUsers = 10
// Allocate the root channel
s.root = &Channel{
Id: 0,
Name: "Root",
}
go s.handler()
go s.multiplexer()
return
}
// Called by the server to initiate a new client connection.
func (server *Server) NewClient(conn net.Conn) (err os.Error) {
client := new(ClientConnection)
// Get the address of the connected client
if addr := conn.RemoteAddr(); addr != nil {
client.tcpaddr = addr.(*net.TCPAddr)
log.Stdoutf("client connected: %s", client.tcpaddr.String())
}
client.server = server
client.conn = conn
client.reader = bufio.NewReader(client.conn)
client.writer = bufio.NewWriter(client.conn)
client.state = StateClientConnected
client.msgchan = make(chan *Message)
client.udprecv = make(chan []byte)
// New client connection....
server.session += 1
client.Session = server.session
// Add it to the list of connected clients
server.cmutex.Lock()
server.clients.PushBack(client)
server.cmutex.Unlock()
go client.receiver()
go client.udpreceiver()
go client.sender()
return
}
// Lookup a client by it's session id. Optimize this by using a map.
func (server *Server) getClientConnection(session uint32) (client *ClientConnection) {
server.cmutex.RLock()
defer server.cmutex.RUnlock()
for x := range server.clients.Iter() {
user := x.(*ClientConnection)
if user.Session == session {
return user
}
}
return nil
}
// This is the synchronous request handler for all incoming messages.
func (server *Server) handler() {
for {
msg := <-server.incoming
client := msg.client
if client.state == StateClientAuthenticated {
server.handleIncomingMessage(client, msg)
} else if client.state == StateClientSentVersion {
server.handleAuthenticate(client, msg)
}
}
}
func (server *Server) handleAuthenticate(client *ClientConnection, msg *Message) {
// Is this message not an authenticate message? If not, discard it...
if msg.kind != MessageAuthenticate {
client.Panic("Unexpected message. Expected Authenticate.")
return
}
auth := &mumbleproto.Authenticate{}
err := proto.Unmarshal(msg.buf, auth)
if err != nil {
client.Panic("Unable to unmarshal Authenticate message.")
return
}
// Did we get a username?
if auth.Username == nil {
client.Panic("No username in auth message...")
return
}
client.Username = *auth.Username
// Setup the cryptstate for the client.
client.crypt, err = cryptstate.New()
if err != nil {
client.Panic(err.String())
return
}
err = client.crypt.GenerateKey()
if err != nil {
client.Panic(err.String())
return
}
// Send CryptState information to the client so it can establish an UDP connection
// (if it wishes)...
err = client.sendProtoMessage(MessageCryptSetup, &mumbleproto.CryptSetup{
Key: client.crypt.RawKey[0:],
ClientNonce: client.crypt.DecryptIV[0:],
ServerNonce: client.crypt.EncryptIV[0:],
})
if err != nil {
client.Panic(err.String())
}
client.codecs = auth.CeltVersions
server.updateCodecVersions()
client.sendChannelList()
client.state = StateClientAuthenticated
// Broadcast that we, the client, entered a channel...
err = server.broadcastProtoMessage(MessageUserState, &mumbleproto.UserState{
Session: proto.Uint32(client.Session),
Name: proto.String(client.Username),
ChannelId: proto.Uint32(0),
})
if err != nil {
client.Panic(err.String())
}
server.sendUserList(client)
err = client.sendProtoMessage(MessageServerSync, &mumbleproto.ServerSync{
Session: proto.Uint32(client.Session),
MaxBandwidth: proto.Uint32(server.MaxBandwidth),
})
if err != nil {
client.Panic(err.String())
return
}
err = client.sendProtoMessage(MessageServerConfig, &mumbleproto.ServerConfig{
AllowHtml: proto.Bool(true),
MessageLength: proto.Uint32(1000),
ImageMessageLength: proto.Uint32(1000),
})
if err != nil {
client.Panic(err.String())
return
}
client.state = StateClientAuthenticated
}
func (server *Server) updateCodecVersions() {
codecusers := map[int32]int{}
var winner int32
var count int
server.cmutex.RLock()
defer server.cmutex.RUnlock()
for x := range server.clients.Iter() {
client := x.(*ClientConnection)
for i := 0; i < len(client.codecs); i++ {
codecusers[client.codecs[i]] += 1
}
}
// result?
for codec, users := range codecusers {
if users > count {
count = users
winner = codec
}
}
var current int32
if server.PreferAlphaCodec {
current = server.AlphaCodec
} else {
current = server.BetaCodec
}
if winner == current {
return
}
if winner == CeltCompatBitstream {
server.PreferAlphaCodec = true
} else {
server.PreferAlphaCodec = !server.PreferAlphaCodec
}
if (server.PreferAlphaCodec) {
server.AlphaCodec = winner
} else {
server.BetaCodec = winner
}
err := server.broadcastProtoMessage(MessageCodecVersion, &mumbleproto.CodecVersion{
Alpha: proto.Int32(server.AlphaCodec),
Beta: proto.Int32(server.BetaCodec),
PreferAlpha: proto.Bool(server.PreferAlphaCodec),
})
if err != nil {
log.Stdoutf("Unable to broadcast..")
return
}
log.Stdoutf("CELT codec switch %v %v (PreferAlpha %v)", server.AlphaCodec, server.BetaCodec, server.PreferAlphaCodec)
return
}
func (server *Server) sendUserList(client *ClientConnection) {
server.cmutex.RLock()
defer server.cmutex.RUnlock()
for x := range server.clients.Iter() {
user := x.(*ClientConnection)
if user.state != StateClientAuthenticated {
continue
}
err := client.sendProtoMessage(MessageUserState, &mumbleproto.UserState{
Session: proto.Uint32(user.Session),
Name: proto.String(user.Username),
ChannelId: proto.Uint32(0),
})
log.Stdoutf("Sent One User...")
if err != nil {
log.Stdoutf("unable to send!")
continue
}
}
}
func (server *Server) broadcastProtoMessage(kind uint16, msg interface{}) (err os.Error) {
server.cmutex.RLock()
defer server.cmutex.RUnlock()
for x := range server.clients.Iter() {
client := x.(*ClientConnection)
if client.state != StateClientAuthenticated {
continue
}
err :=client.sendProtoMessage(kind, msg)
if err != nil {
return
}
}
return
}
func (server *Server) handleIncomingMessage(client *ClientConnection, msg *Message) {
log.Stdoutf("Handle Incoming Message")
switch msg.kind {
case MessagePing:
server.handlePingMessage(msg.client, msg)
case MessageChannelRemove:
server.handlePingMessage(msg.client, msg)
case MessageChannelState:
server.handleChannelStateMessage(msg.client, msg)
case MessageUserState:
server.handleUserStateMessage(msg.client, msg)
case MessageUserRemove:
server.handleUserRemoveMessage(msg.client, msg)
case MessageBanList:
server.handleBanListMessage(msg.client, msg)
case MessageTextMessage:
server.handleTextMessage(msg.client, msg)
case MessageACL:
server.handleAclMessage(msg.client, msg)
case MessageQueryUsers:
server.handleQueryUsers(msg.client, msg)
case MessageCryptSetup:
server.handleCryptSetup(msg.client, msg)
case MessageContextActionAdd:
log.Stdoutf("MessageContextActionAdd from client")
case MessageContextAction:
log.Stdoutf("MessageContextAction from client")
case MessageUserList:
log.Stdoutf("MessageUserList from client")
case MessageVoiceTarget:
log.Stdoutf("MessageVoiceTarget from client")
case MessagePermissionQuery:
log.Stdoutf("MessagePermissionQuery from client")
case MessageCodecVersion:
log.Stdoutf("MessageCodecVersion from client")
case MessageUserStats:
server.handleUserStatsMessage(msg.client, msg)
case MessageRequestBlob:
log.Stdoutf("MessageRequestBlob from client")
case MessageServerConfig:
log.Stdoutf("MessageServerConfig from client")
}
}
func (server *Server) multiplexer() {
for {
_ = <-server.outgoing
log.Stdoutf("recvd message to multiplex")
}
}
func (s *Server) SetupUDP() (err os.Error) {
addr := &net.UDPAddr{
Port: s.port,
}
s.udpconn, err = net.ListenUDP("udp", addr)
if err != nil {
return
}
return
}
func (s *Server) SendUDP() {
for {
msg := <-s.udpsend
if msg.client != nil {
// These are to be crypted...
crypted := make([]byte, len(msg.buf)+4)
msg.client.crypt.Encrypt(msg.buf, crypted)
s.udpconn.WriteTo(crypted, msg.client.udpaddr)
} else if msg.address != nil {
s.udpconn.WriteTo(msg.buf, msg.address)
} else {
// Skipping
}
}
}
// Listen for and handle UDP packets.
func (server *Server) ListenUDP() {
buf := make([]byte, UDPPacketSize)
for {
nread, remote, err := server.udpconn.ReadFrom(buf)
if err != nil {
// Not much to do here. This is bad, of course. Should we panic this server instance?
continue
}
udpaddr, ok := remote.(*net.UDPAddr)
if !ok {
log.Stdoutf("No UDPAddr in read packet. Disabling UDP. (Windows?)")
return
}
// Length 12 is for ping datagrams from the ConnectDialog.
if nread == 12 {
readbuf := bytes.NewBuffer(buf)
var (
tmp32 uint32
rand uint64
)
_ = binary.Read(readbuf, binary.BigEndian, &tmp32)
_ = binary.Read(readbuf, binary.BigEndian, &rand)
buffer := bytes.NewBuffer(make([]byte, 0, 24))
_ = binary.Write(buffer, binary.BigEndian, uint32((1<<16)|(2<<8)|2))
_ = binary.Write(buffer, binary.BigEndian, rand)
_ = binary.Write(buffer, binary.BigEndian, uint32(server.clients.Len()))
_ = binary.Write(buffer, binary.BigEndian, uint32(server.MaxUsers))
_ = binary.Write(buffer, binary.BigEndian, uint32(server.MaxBandwidth))
server.udpsend <- &Message{
buf: buffer.Bytes(),
address: udpaddr,
}
} else {
var match *ClientConnection
plain := make([]byte, nread-4)
decrypted := false
// First, check if any of our clients match the net.UDPAddr...
server.cmutex.RLock()
for x := range server.clients.Iter() {
client := x.(*ClientConnection)
if client.udpaddr.String() == udpaddr.String() {
match = client
}
}
server.cmutex.RUnlock()
// No matching client found. We must try to decrypt...
if match == nil {
server.cmutex.RLock()
for x := range server.clients.Iter() {
client := x.(*ClientConnection)
// Try to decrypt.
err = client.crypt.Decrypt(buf[0:nread], plain[0:])
if err != nil {
// Decryption failed. Try another client...
continue
}
// Decryption succeeded.
decrypted = true
// If we were able to successfully decrpyt, add
// the UDPAddr to the ClientConnection struct.
log.Stdoutf("Client UDP connection established.")
client.udpaddr = remote.(*net.UDPAddr)
match = client
break
}
server.cmutex.RUnlock()
}
// We were not able to find a client that could decrypt the incoming
// packet. Log it?
if match == nil {
continue
}
if !decrypted {
err = match.crypt.Decrypt(buf[0:nread], plain[0:])
if err != nil {
log.Stdoutf("Unable to decrypt from client..")
}
}
match.udp = true
match.udprecv <- plain
}
}
}
// The accept loop of the server.
func (s *Server) ListenAndMurmur() {
// Setup our UDP listener and spawn our reader and writer goroutines
s.SetupUDP()
go s.ListenUDP()
go s.SendUDP()
// Create a new listening TLS socket.
l := NewTLSListener(s.port)
if l == nil {
log.Stderrf("Unable to create TLS listener")
return
}
log.Stderrf("Created new Murmur instance on port %v", s.port)
// The main accept loop. Basically, we block
// until we get a new client connection, and
// when we do get a new connection, we spawn
// a new Go-routine to handle the client.
for {
// New client connected
conn, err := l.Accept()
if err != nil {
log.Stderrf("unable to accept()")
}
tls, ok := conn.(*tls.Conn)
if !ok {
log.Stderrf("Not tls :(")
}
// Force the TLS handshake to get going. We'd like
// this to happen as soon as possible, so we can get
// at client certificates sooner.
tls.Handshake()
// Create a new client connection from our *tls.Conn
// which wraps net.TCPConn.
err = s.NewClient(conn)
if err != nil {
log.Stderrf("Unable to start new client")
}
log.Stdoutf("num clients = %v", s.clients.Len())
}
}

88
tlsserver.go Normal file
View file

@ -0,0 +1,88 @@
package main
import (
"log"
"time"
"net"
"crypto/tls"
"crypto/rand"
"encoding/pem"
"crypto/x509"
"io/ioutil"
)
func NewTLSListener(port int) (rl *tls.Listener) {
rl = nil
// Load the certificate
pemBytes, err := ioutil.ReadFile("grumble.crt")
if err != nil {
log.Stderr("Failed to read server.crt:", err)
return
}
// Decode the certificate
cert, _ := pem.Decode(pemBytes)
if cert == nil {
log.Stderr("Failed to parse server.crt")
return
}
// Load the private key
keyBytes, err := ioutil.ReadFile("grumble.key")
if err != nil {
log.Stderr("Failed to read server.key.insecure: %s", err)
return
}
// Decode the private key
pkPEM, _ := pem.Decode(keyBytes)
if pkPEM == nil {
log.Stderrf("Failed to parse server.key.insecure: %s", err)
return
}
// Determine if we are an RSA private key
if pkPEM.Type != "RSA PRIVATE KEY" {
log.Stderrf("server.key.insecure is not an RSA private key. Found '%s'",
pkPEM.Type)
return
}
// Check if the PEM file has headers. This will typically
// mean that it requires a passphrase to decrypt it. For now,
// let us just assume that people will decrypt them for us, so
// we can use them without too much work.
if len(pkPEM.Headers) != 0 {
log.Stderr("server.key.insecure has headers and is probably encrypted.")
return
}
// Parse the PKCS12 private key.
priv, err := x509.ParsePKCS1PrivateKey(pkPEM.Bytes)
if err != nil {
log.Stderrf("Invalid key in server.key.insecure: %s", err)
return
}
// Create a new TLS config.
config := new(tls.Config)
config.Rand = rand.Reader
config.Time = time.Seconds
config.Certificates = make([]tls.Certificate, 1)
config.Certificates[0].Certificate = [][]byte{cert.Bytes}
config.Certificates[0].PrivateKey = priv
l, err := net.ListenTCP("tcp", &net.TCPAddr{
net.ParseIP("0.0.0.0"),
port,
})
if err != nil {
log.Stderrf("Cannot bind: %s\n", err)
return
}
rl = tls.NewListener(l, config)
return
}