1
0
Fork 0
mirror of https://github.com/FrankerFaceZ/FrankerFaceZ.git synced 2025-09-13 16:46:55 +00:00

Switch to nacl cryptobox

This commit is contained in:
Kane York 2015-10-25 12:40:07 -07:00
parent 401f66f15b
commit c6a3c120c6
6 changed files with 144 additions and 60 deletions

View file

@ -1,6 +1,7 @@
package server
import (
"golang.org/x/crypto/nacl/box"
"net/http"
"time"
"fmt"
@ -9,9 +10,12 @@ import (
"strconv"
"io/ioutil"
"encoding/json"
"crypto/tls"
"crypto/x509"
"sync"
"log"
"os"
"crypto/rand"
"encoding/base64"
"strings"
)
var backendHttpClient http.Client
@ -20,6 +24,10 @@ var responseCache *cache.Cache
var getBacklogUrl string
var backendSharedKey [32]byte
var messageBufferPool sync.Pool
func SetupBackend(config *Config) {
backendHttpClient.Timeout = 60 * time.Second
backendUrl = config.BackendUrl
@ -29,26 +37,45 @@ func SetupBackend(config *Config) {
responseCache = cache.New(60 * time.Second, 120 * time.Second)
getBacklogUrl = fmt.Sprintf("%s/backlog", backendUrl)
}
func SetupBackendCertificates(config *Config, certPool x509.CertPool) {
myCert, err := tls.LoadX509KeyPair(config.BackendClientCertFile, config.BackendClientKeyFile)
messageBufferPool.New = NewByteBuffer
var keys CryptoKeysBuf
file, err := os.Open(config.NaclKeysFile)
if err != nil {
log.Fatal(err)
}
tlsConfig := tls.Config{
Certificates: []tls.Certificate{myCert},
RootCAs: certPool,
dec := json.NewDecoder(file)
err = dec.Decode(&keys)
if err != nil {
log.Fatal(err)
}
tlsConfig.BuildNameToCertificate()
transport := &http.Transport{TLSClientConfig: tlsConfig}
backendHttpClient.Transport = transport
box.Precompute(&backendSharedKey, &keys.TheirPublicKey, &keys.OurPrivateKey)
}
func getCacheKey(remoteCommand, data string) string {
return fmt.Sprintf("%s/%s", remoteCommand, data)
}
func SealRequest(form url.Values) ([]byte, error) {
asString := form.Encode()
var nonce [24]byte
var err error
err = FillCryptoRandom(nonce[:])
if err != nil {
return nil, err
}
message := []byte(asString)
out := make([]byte, len(message) + box.Overhead)
box.SealAfterPrecomputation(out, message, &nonce, &backendSharedKey)
// TODO
return nil, nil
}
func RequestRemoteDataCached(remoteCommand, data string, auth AuthInfo) (string, error) {
cached, ok := responseCache.Get(getCacheKey(remoteCommand, data))
if ok {
@ -57,7 +84,7 @@ func RequestRemoteDataCached(remoteCommand, data string, auth AuthInfo) (string,
return RequestRemoteData(remoteCommand, data, auth)
}
func RequestRemoteData(remoteCommand, data string, auth AuthInfo) (string, error) {
func RequestRemoteData(remoteCommand, data string, auth AuthInfo) (responseStr string, err error) {
destUrl := fmt.Sprintf("%s/cmd/%s", backendUrl, remoteCommand)
var authKey string
if auth.UsernameValidated {
@ -70,9 +97,6 @@ func RequestRemoteData(remoteCommand, data string, auth AuthInfo) (string, error
"clientData": []string{data},
authKey: []string{auth.TwitchUsername},
}
if gconfig.BasicAuthPassword != "" {
formData["password"] = gconfig.BasicAuthPassword
}
resp, err := backendHttpClient.PostForm(destUrl, formData)
if err != nil {
@ -85,7 +109,7 @@ func RequestRemoteData(remoteCommand, data string, auth AuthInfo) (string, error
return "", err
}
responseJson := string(respBytes)
responseStr = string(respBytes)
if resp.Header.Get("FFZ-Cache") != "" {
durSecs, err := strconv.ParseInt(resp.Header.Get("FFZ-Cache"), 10, 64)
@ -93,10 +117,10 @@ func RequestRemoteData(remoteCommand, data string, auth AuthInfo) (string, error
return "", fmt.Errorf("The RPC server returned a non-integer cache duration: %v", err)
}
duration := time.Duration(durSecs) * time.Second
responseCache.Set(getCacheKey(remoteCommand, data), responseJson, duration)
responseCache.Set(getCacheKey(remoteCommand, data), responseStr, duration)
}
return responseJson, nil
return
}
func FetchBacklogData(chatSubs, channelSubs []string) ([]ClientMessage, error) {
@ -117,4 +141,43 @@ func FetchBacklogData(chatSubs, channelSubs []string) ([]ClientMessage, error) {
}
return messages, nil
}
}
func GenerateKeys(outputFile, serverId, theirPublicStr string) {
var err error
output := CryptoKeysBuf{}
output.ServerId, err = strconv.Atoi(serverId)
if err != nil {
log.Fatal(err)
}
ourPublic, ourPrivate, err := box.GenerateKey(rand.Reader)
if err != nil {
log.Fatal(err)
}
output.OurPublicKey, output.OurPrivateKey = *ourPublic, *ourPrivate
if theirPublicStr != "" {
reader := base64.NewDecoder(base64.RawURLEncoding, strings.NewReader(theirPublicStr))
theirPublic, err := ioutil.ReadAll(reader)
if err != nil {
log.Fatal(err)
}
copy(output.TheirPublicKey[:], theirPublic)
}
file, err := os.Create(outputFile)
if err != nil {
log.Fatal(err)
}
enc := json.NewEncoder(file)
err = enc.Encode(output)
if err != nil {
log.Fatal(err)
}
err = file.Close()
if err != nil {
log.Fatal(err)
}
}

View file

@ -10,24 +10,19 @@ import (
"encoding/json"
"fmt"
"sync"
"crypto/x509"
"io/ioutil"
"log"
)
const MAX_PACKET_SIZE = 1024
type Config struct {
// SSL
// SSL/TLS
SSLCertificateFile string
SSLKeyFile string
UseSSL bool
// CA for client validation (pub/sub commands only)
BackendRootCertFile string
BackendClientCertFile string
BackendClientKeyFile string
// Password for client validation (pub/sub commands only)
BasicAuthPassword string
// NaCl keys for backend messages
NaclKeysFile string
// Hostname of the socket server
SocketOrigin string
@ -93,7 +88,7 @@ func setupServer(config *Config, tlsConfig *tls.Config) *websocket.Server {
gconfig = config
sockConf, err := websocket.NewConfig("/", config.SocketOrigin)
if err != nil {
panic(err)
log.Fatal(err)
}
SetupBackend(config)
@ -101,25 +96,13 @@ func setupServer(config *Config, tlsConfig *tls.Config) *websocket.Server {
if config.UseSSL {
cert, err := tls.LoadX509KeyPair(config.SSLCertificateFile, config.SSLKeyFile)
if err != nil {
panic(err)
log.Fatal(err)
}
tlsConfig.Certificates = []tls.Certificate{cert}
tlsConfig.ServerName = config.SocketOrigin
tlsConfig.BuildNameToCertificate()
sockConf.TlsConfig = tlsConfig
certBytes, err := ioutil.ReadFile(config.BackendRootCertFile)
if err != nil {
panic(err)
}
clientCA, err := x509.ParseCertificate(certBytes)
if err != nil {
panic(err)
}
certPool := x509.NewCertPool()
certPool.AddCert(clientCA)
tlsConfig.ClientCAs = certPool
SetupBackendCertificates(config, certPool)
}
sockServer := &websocket.Server{}
@ -214,7 +197,7 @@ func HandleSocketConnection(conn *websocket.Conn) {
// Launch message draining goroutine - we aren't out of the pub/sub records
go func() {
for _ := range _serverMessageChan {}
for _ = range _serverMessageChan {}
}()
// Stop getting messages...

View file

@ -46,9 +46,7 @@ func PublishToWatchers(channel string, msg ClientMessage) {
}
func HandlePublishRequest(w http.ResponseWriter, r *http.Request) {
if r.TLS {
PeerCertificates
}
// TODO - box.Open()
}
// Add a channel to the subscriptions while holding a read-lock to the map.
@ -63,7 +61,7 @@ func _subscribeWhileRlocked(which map[string]*SubscriberList, channelName string
rlocker.Unlock()
wlocker.Lock()
list = &SubscriberList{}
list.Members = &[]chan <- ClientMessage{value} // Create it populated, to avoid reaper
list.Members = []chan <- ClientMessage{value} // Create it populated, to avoid reaper
which[channelName] = list
wlocker.Unlock()
rlocker.Lock()
@ -84,7 +82,7 @@ func SubscribeBatch(client *ClientInfo, chatSubs, channelSubs []string) {
rlocker := ChatSubscriptionLock.RLocker()
rlocker.Lock()
for _, v := range chatSubs {
_subscribeWhileRlocked(ChatSubscriptionInfo, v, mchan, rlocker, ChatSubscriptionLock)
_subscribeWhileRlocked(ChatSubscriptionInfo, v, mchan, rlocker, &ChatSubscriptionLock)
}
rlocker.Unlock()
}
@ -92,7 +90,7 @@ func SubscribeBatch(client *ClientInfo, chatSubs, channelSubs []string) {
rlocker := WatchingSubscriptionLock.RLocker()
rlocker.Lock()
for _, v := range channelSubs {
_subscribeWhileRlocked(WatchingSubscriptionInfo, v, mchan, rlocker, WatchingSubscriptionLock)
_subscribeWhileRlocked(WatchingSubscriptionInfo, v, mchan, rlocker, &WatchingSubscriptionLock)
}
rlocker.Unlock()
}

View file

@ -6,6 +6,15 @@ import (
"time"
)
const CryptoBoxKeyLength = 32
type CryptoKeysBuf struct {
OurPrivateKey [CryptoBoxKeyLength]byte
OurPublicKey [CryptoBoxKeyLength]byte
TheirPublicKey [CryptoBoxKeyLength]byte
ServerId int
}
type ClientMessage struct {
// Message ID. Increments by 1 for each message sent from the client.
// When replying to a command, the message ID must be echoed.

View file

@ -1,8 +1,25 @@
package server
import (
"crypto/rand"
)
func FillCryptoRandom(buf []byte) error {
remaining := len(buf)
for remaining > 0 {
count, err := rand.Read(buf)
if err != nil {
return err
}
remaining -= count
}
return nil
}
func NewByteBuffer() interface{} {
return make([]byte, 1024)
}
func AddToSliceS(ary *[]string, val string) bool {
slice := *ary
for _, v := range slice {