1
0
Fork 0
mirror of https://github.com/FrankerFaceZ/FrankerFaceZ.git synced 2025-06-27 21:05:53 +00:00

Extract form sealing to a package

This commit is contained in:
Kane York 2017-09-15 16:40:40 -07:00
parent ced892fd1a
commit 1c55e8fca7
9 changed files with 119 additions and 117 deletions

View file

@ -15,6 +15,7 @@ import (
"sync"
"time"
"github.com/FrankerFaceZ/FrankerFaceZ/socketserver/server/naclform"
cache "github.com/patrickmn/go-cache"
"golang.org/x/crypto/nacl/box"
)
@ -33,8 +34,7 @@ type backendInfo struct {
addTopicURL string
announceStartupURL string
sharedKey [32]byte
serverID int
secureForm naclform.ServerInfo
lastSuccess map[string]time.Time
lastSuccessLock sync.Mutex
@ -45,7 +45,7 @@ var Backend *backendInfo
func setupBackend(config *ConfigFile) *backendInfo {
b := new(backendInfo)
Backend = b
b.serverID = config.ServerID
b.secureForm.ServerID = config.ServerID
b.HTTPClient.Timeout = 60 * time.Second
b.baseURL = config.BackendURL
@ -68,7 +68,7 @@ func setupBackend(config *ConfigFile) *backendInfo {
copy(theirPublic[:], config.BackendPublicKey)
copy(ourPrivate[:], config.OurPrivateKey)
box.Precompute(&b.sharedKey, &theirPublic, &ourPrivate)
box.Precompute(&b.secureForm.SharedKey, &theirPublic, &ourPrivate)
return b
}
@ -119,7 +119,7 @@ func (backend *backendInfo) SendRemoteCommand(remoteCommand, data string, auth A
formData.Set("authenticated", "0")
}
sealedForm, err := backend.SealRequest(formData)
sealedForm, err := backend.secureForm.Seal(formData)
if err != nil {
return "", err
}
@ -171,7 +171,7 @@ func (backend *backendInfo) SendRemoteCommand(remoteCommand, data string, auth A
// SendAggregatedData sends aggregated emote usage and following data to the backend server.
func (backend *backendInfo) SendAggregatedData(form url.Values) error {
sealedForm, err := backend.SealRequest(form)
sealedForm, err := backend.secureForm.Seal(form)
if err != nil {
return err
}
@ -228,7 +228,7 @@ func (backend *backendInfo) sendTopicNotice(topic string, added bool) error {
formData.Set("added", "f")
}
sealedForm, err := backend.SealRequest(formData)
sealedForm, err := backend.secureForm.Seal(formData)
if err != nil {
return err
}

View file

@ -18,14 +18,14 @@ func TestSealRequest(t *testing.T) {
"QuickBrownFox": []string{"LazyDog"},
}
sealedValues, err := b.SealRequest(values)
sealedValues, err := b.secureForm.Seal(values)
if err != nil {
t.Fatal(err)
}
// sealedValues.Encode()
// id=0&msg=KKtbng49dOLLyjeuX5AnXiEe6P0uZwgeP_7mMB5vhP-wMAAPZw%3D%3D&nonce=-wRbUnifscisWUvhm3gBEXHN5QzrfzgV
unsealedValues, err := b.UnsealRequest(sealedValues)
unsealedValues, err := b.secureForm.Unseal(sealedValues)
if err != nil {
t.Fatal(err)
}

View file

@ -184,7 +184,7 @@ func C2SPing(*websocket.Conn, *ClientInfo, ClientMessage) (ClientMessage, error)
func C2SSetUser(_ *websocket.Conn, client *ClientInfo, msg ClientMessage) (ClientMessage, error) {
username, err := msg.ArgumentsAsString()
if err != nil {
return
return ClientMessage{}, err
}
username = copyString(username)
@ -221,7 +221,7 @@ func C2SReady(_ *websocket.Conn, client *ClientInfo, msg ClientMessage) (ClientM
func C2SSubscribe(_ *websocket.Conn, client *ClientInfo, msg ClientMessage) (ClientMessage, error) {
channel, err := msg.ArgumentsAsString()
if err != nil {
return
return ClientMessage{}, err
}
channel = PubSubChannelPool.Intern(channel)
@ -248,7 +248,7 @@ func C2SSubscribe(_ *websocket.Conn, client *ClientInfo, msg ClientMessage) (Cli
func C2SUnsubscribe(_ *websocket.Conn, client *ClientInfo, msg ClientMessage) (ClientMessage, error) {
channel, err := msg.ArgumentsAsString()
if err != nil {
return
return ClientMessage{}, err
}
channel = PubSubChannelPool.Intern(channel)

View file

@ -104,7 +104,7 @@ func SetupServerAndHandle(config *ConfigFile, serveMux *http.ServeMux) {
serveMux.HandleFunc("/cached_pub", HTTPBackendCachedPublish)
serveMux.HandleFunc("/get_sub_count", HTTPGetSubscriberCount)
announceForm, err := Backend.SealRequest(url.Values{
announceForm, err := Backend.secureForm.Seal(url.Values{
"startup": []string{"1"},
})
if err != nil {

View file

@ -0,0 +1,96 @@
package naclform
import (
"bytes"
"crypto/rand"
"encoding/base64"
"errors"
"net/url"
"strconv"
"strings"
"golang.org/x/crypto/nacl/box"
)
var ErrorShortNonce = errors.New("Nonce too short.")
var ErrorInvalidSignature = errors.New("Invalid signature or contents")
type ServerInfo struct {
SharedKey [32]byte
ServerID int
}
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 (i *ServerInfo) Seal(form url.Values) (url.Values, error) {
var nonce [24]byte
var err error
err = fillCryptoRandom(nonce[:])
if err != nil {
return nil, err
}
cipherMsg := box.SealAfterPrecomputation(nil, []byte(form.Encode()), &nonce, &i.SharedKey)
bufMessage := new(bytes.Buffer)
enc := base64.NewEncoder(base64.URLEncoding, bufMessage)
enc.Write(cipherMsg)
enc.Close()
cipherString := bufMessage.String()
bufNonce := new(bytes.Buffer)
enc = base64.NewEncoder(base64.URLEncoding, bufNonce)
enc.Write(nonce[:])
enc.Close()
nonceString := bufNonce.String()
retval := url.Values{
"nonce": []string{nonceString},
"msg": []string{cipherString},
"id": []string{strconv.Itoa(i.ServerID)},
}
return retval, nil
}
func (i *ServerInfo) Unseal(form url.Values) (url.Values, error) {
var nonce [24]byte
nonceString := form.Get("nonce")
dec := base64.NewDecoder(base64.URLEncoding, strings.NewReader(nonceString))
count, err := dec.Read(nonce[:])
if err != nil {
return nil, err
}
if count != 24 {
return nil, ErrorShortNonce
}
cipherString := form.Get("msg")
dec = base64.NewDecoder(base64.URLEncoding, strings.NewReader(cipherString))
cipherBuffer := new(bytes.Buffer)
cipherBuffer.ReadFrom(dec)
message, ok := box.OpenAfterPrecomputation(nil, cipherBuffer.Bytes(), &nonce, &i.SharedKey)
if !ok {
return nil, ErrorInvalidSignature
}
retValues, err := url.ParseQuery(string(message))
if err != nil {
return nil, ErrorInvalidSignature
}
return retValues, nil
}

View file

@ -123,7 +123,7 @@ func saveLastMessage(cmd Command, channel string, expires time.Time, data string
func HTTPBackendDropBacklog(w http.ResponseWriter, r *http.Request) {
r.ParseForm()
formData, err := Backend.UnsealRequest(r.Form)
formData, err := Backend.secureForm.Unseal(r.Form)
if err != nil {
w.WriteHeader(403)
fmt.Fprintf(w, "Error: %v", err)
@ -160,7 +160,7 @@ func rateLimitFromRequest(r *http.Request) (rate.Limiter, error) {
// If the 'expires' parameter is not specified, the message will not expire (though it is only kept in-memory).
func HTTPBackendCachedPublish(w http.ResponseWriter, r *http.Request) {
r.ParseForm()
formData, err := Backend.UnsealRequest(r.Form)
formData, err := Backend.secureForm.Unseal(r.Form)
if err != nil {
w.WriteHeader(403)
fmt.Fprintf(w, "Error: %v", err)
@ -227,7 +227,7 @@ func HTTPBackendCachedPublish(w http.ResponseWriter, r *http.Request) {
// If "scope" is "global", then "channel" is not used.
func HTTPBackendUncachedPublish(w http.ResponseWriter, r *http.Request) {
r.ParseForm()
formData, err := Backend.UnsealRequest(r.Form)
formData, err := Backend.secureForm.Unseal(r.Form)
if err != nil {
w.WriteHeader(403)
fmt.Fprintf(w, "Error: %v", err)
@ -292,7 +292,7 @@ func HTTPBackendUncachedPublish(w http.ResponseWriter, r *http.Request) {
// A "global" option is not available, use fetch(/stats).CurrentClientCount instead.
func HTTPGetSubscriberCount(w http.ResponseWriter, r *http.Request) {
r.ParseForm()
formData, err := Backend.UnsealRequest(r.Form)
formData, err := Backend.secureForm.Unseal(r.Form)
if err != nil {
w.WriteHeader(403)
fmt.Fprintf(w, "Error: %v", err)

View file

@ -97,7 +97,7 @@ func (er *TExpectedBackendRequest) String() string {
if MethodIsPost == "" {
return er.Path
}
return fmt.Sprint("%s %s: %s", MethodIsPost, er.Path, er.PostForm.Encode())
return fmt.Sprintf("%s %s: %s", MethodIsPost, er.Path, er.PostForm.Encode())
}
type TBackendRequestChecker struct {
@ -123,7 +123,7 @@ func (backend *TBackendRequestChecker) ServeHTTP(w http.ResponseWriter, r *http.
r.ParseForm()
unsealedForm, err := Backend.UnsealRequest(r.PostForm)
unsealedForm, err := Backend.secureForm.Unseal(r.PostForm)
if err != nil {
backend.tb.Errorf("Failed to unseal backend request: %v", err)
}
@ -276,7 +276,7 @@ func TSealForSavePubMsg(tb testing.TB, cmd Command, channel string, arguments in
}
form.Set("time", strconv.FormatInt(time.Now().Unix(), 10))
sealed, err := Backend.SealRequest(form)
sealed, err := Backend.secureForm.Seal(form)
if err != nil {
tb.Error(err)
return nil, err
@ -300,7 +300,7 @@ func TSealForUncachedPubMsg(tb testing.TB, cmd Command, channel string, argument
form.Set("time", time.Now().Format(time.UnixDate))
form.Set("scope", scope)
sealed, err := Backend.SealRequest(form)
sealed, err := Backend.secureForm.Seal(form)
if err != nil {
tb.Error(err)
return nil, err

View file

@ -64,7 +64,7 @@ type ClientMessage struct {
origArguments string
}
func (cm ClientMessage) Reply(cmd string, args interface{}) ClientMessage {
func (cm ClientMessage) Reply(cmd Command, args interface{}) ClientMessage {
return ClientMessage{
MessageID: cm.MessageID,
Command: cmd,
@ -72,7 +72,7 @@ func (cm ClientMessage) Reply(cmd string, args interface{}) ClientMessage {
}
}
func (cm ClientMessage) ReplyJSON(cmd string, argsJSON string) ClientMessage {
func (cm ClientMessage) ReplyJSON(cmd Command, argsJSON string) ClientMessage {
n := ClientMessage{
MessageID: cm.MessageID,
Command: cmd,

View file

@ -1,103 +1,9 @@
package server
import (
"bytes"
"crypto/rand"
"encoding/base64"
"errors"
"net/url"
"strconv"
"strings"
"golang.org/x/crypto/nacl/box"
)
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 copyString(s string) string {
return string([]byte(s))
}
func (backend *backendInfo) SealRequest(form url.Values) (url.Values, error) {
var nonce [24]byte
var err error
err = FillCryptoRandom(nonce[:])
if err != nil {
return nil, err
}
cipherMsg := box.SealAfterPrecomputation(nil, []byte(form.Encode()), &nonce, &backend.sharedKey)
bufMessage := new(bytes.Buffer)
enc := base64.NewEncoder(base64.URLEncoding, bufMessage)
enc.Write(cipherMsg)
enc.Close()
cipherString := bufMessage.String()
bufNonce := new(bytes.Buffer)
enc = base64.NewEncoder(base64.URLEncoding, bufNonce)
enc.Write(nonce[:])
enc.Close()
nonceString := bufNonce.String()
retval := url.Values{
"nonce": []string{nonceString},
"msg": []string{cipherString},
"id": []string{strconv.Itoa(Backend.serverID)},
}
return retval, nil
}
var ErrorShortNonce = errors.New("Nonce too short.")
var ErrorInvalidSignature = errors.New("Invalid signature or contents")
func (backend *backendInfo) UnsealRequest(form url.Values) (url.Values, error) {
var nonce [24]byte
nonceString := form.Get("nonce")
dec := base64.NewDecoder(base64.URLEncoding, strings.NewReader(nonceString))
count, err := dec.Read(nonce[:])
if err != nil {
Statistics.BackendVerifyFails++
return nil, err
}
if count != 24 {
Statistics.BackendVerifyFails++
return nil, ErrorShortNonce
}
cipherString := form.Get("msg")
dec = base64.NewDecoder(base64.URLEncoding, strings.NewReader(cipherString))
cipherBuffer := new(bytes.Buffer)
cipherBuffer.ReadFrom(dec)
message, ok := box.OpenAfterPrecomputation(nil, cipherBuffer.Bytes(), &nonce, &backend.sharedKey)
if !ok {
Statistics.BackendVerifyFails++
return nil, ErrorInvalidSignature
}
retValues, err := url.ParseQuery(string(message))
if err != nil {
Statistics.BackendVerifyFails++
return nil, ErrorInvalidSignature
}
return retValues, nil
}
func AddToSliceS(ary *[]string, val string) bool {
slice := *ary
for _, v := range slice {