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:
parent
ced892fd1a
commit
1c55e8fca7
9 changed files with 119 additions and 117 deletions
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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 {
|
||||
|
|
96
socketserver/server/naclform/seal.go
Normal file
96
socketserver/server/naclform/seal.go
Normal 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
|
||||
}
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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 {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue