diff --git a/socketserver/internal/server/backend.go b/socketserver/internal/server/backend.go index 24766025..af4668cf 100644 --- a/socketserver/internal/server/backend.go +++ b/socketserver/internal/server/backend.go @@ -25,6 +25,7 @@ var responseCache *cache.Cache var getBacklogUrl string var backendSharedKey [32]byte +var serverId int var messageBufferPool sync.Pool @@ -38,7 +39,7 @@ func SetupBackend(config *Config) { getBacklogUrl = fmt.Sprintf("%s/backlog", backendUrl) - messageBufferPool.New = NewByteBuffer + messageBufferPool.New = New4KByteBuffer var keys CryptoKeysBuf file, err := os.Open(config.NaclKeysFile) @@ -51,31 +52,18 @@ func SetupBackend(config *Config) { log.Fatal(err) } - box.Precompute(&backendSharedKey, &keys.TheirPublicKey, &keys.OurPrivateKey) + var theirPublic, ourPrivate [32]byte + copy(theirPublic[:], keys.TheirPublicKey) + copy(ourPrivate[:], keys.OurPrivateKey) + serverId = keys.ServerId + + box.Precompute(&backendSharedKey, &theirPublic, &ourPrivate) } 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 { @@ -156,15 +144,16 @@ func GenerateKeys(outputFile, serverId, theirPublicStr string) { if err != nil { log.Fatal(err) } - output.OurPublicKey, output.OurPrivateKey = *ourPublic, *ourPrivate + output.OurPublicKey, output.OurPrivateKey = ourPublic[:], ourPrivate[:] if theirPublicStr != "" { - reader := base64.NewDecoder(base64.RawURLEncoding, strings.NewReader(theirPublicStr)) + reader := base64.NewDecoder(base64.StdEncoding, strings.NewReader(theirPublicStr)) theirPublic, err := ioutil.ReadAll(reader) if err != nil { log.Fatal(err) } - copy(output.TheirPublicKey[:], theirPublic) + log.Print(theirPublic) + output.TheirPublicKey = theirPublic } file, err := os.Create(outputFile) diff --git a/socketserver/internal/server/backend_test.go b/socketserver/internal/server/backend_test.go new file mode 100644 index 00000000..3a2d5a7b --- /dev/null +++ b/socketserver/internal/server/backend_test.go @@ -0,0 +1,40 @@ +package server +import ( + "testing" + "net/url" + "golang.org/x/crypto/nacl/box" + "crypto/rand" +) + +func TestSealRequest(t *testing.T) { + senderPublic, senderPrivate, err := box.GenerateKey(rand.Reader) + if err != nil { + t.Fatal(err) + } + receiverPublic, receiverPrivate, err := box.GenerateKey(rand.Reader) + if err != nil { + t.Fatal(err) + } + + messageBufferPool.New = New4KByteBuffer + + values := url.Values{ + "QuickBrownFox": []string{"LazyDog"}, + } + + box.Precompute(&backendSharedKey, receiverPublic, senderPrivate) + sealedValues, err := SealRequest(values) + if err != nil { + t.Fatal(err) + } + + box.Precompute(&backendSharedKey, senderPublic, receiverPrivate) + unsealedValues, err := UnsealRequest(sealedValues) + if err != nil { + t.Fatal(err) + } + + if unsealedValues.Get("QuickBrownFox") != "LazyDog" { + t.Errorf("Failed to round-trip, got back %v", unsealedValues) + } +} diff --git a/socketserver/internal/server/types.go b/socketserver/internal/server/types.go index 046afcca..be3145e1 100644 --- a/socketserver/internal/server/types.go +++ b/socketserver/internal/server/types.go @@ -9,9 +9,9 @@ import ( const CryptoBoxKeyLength = 32 type CryptoKeysBuf struct { - OurPrivateKey [CryptoBoxKeyLength]byte - OurPublicKey [CryptoBoxKeyLength]byte - TheirPublicKey [CryptoBoxKeyLength]byte + OurPrivateKey []byte + OurPublicKey []byte + TheirPublicKey []byte ServerId int } diff --git a/socketserver/internal/server/utils.go b/socketserver/internal/server/utils.go index 1d6d437a..a3853cd0 100644 --- a/socketserver/internal/server/utils.go +++ b/socketserver/internal/server/utils.go @@ -2,6 +2,14 @@ package server import ( "crypto/rand" + "net/url" + "golang.org/x/crypto/nacl/box" + "bytes" + "encoding/base64" + "strconv" + "strings" + "errors" + "log" ) func FillCryptoRandom(buf []byte) error { @@ -16,8 +24,76 @@ func FillCryptoRandom(buf []byte) error { return nil } -func NewByteBuffer() interface{} { - return make([]byte, 1024) +func New4KByteBuffer() interface{} { + return make([]byte, 0, 4096) +} + +func 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, &backendSharedKey) + + 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(serverId)}, + } + + return retval, nil +} + +var ErrorShortNonce = errors.New("Nonce too short.") +var ErrorInvalidSignature = errors.New("Invalid signature or contents") + +func 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 { + 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, &backendSharedKey) + if !ok { + return nil, ErrorInvalidSignature + } + + retValues, err := url.ParseQuery(string(message)) + if err != nil { + // Assume that the signature was accidentally correct but the contents were garbage + log.Print(err) + return nil, ErrorInvalidSignature + } + + return retValues, nil } func AddToSliceS(ary *[]string, val string) bool {