diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..52b1fd8 --- /dev/null +++ b/.env.example @@ -0,0 +1 @@ +AES_KEY_GCM=<32-character-secret-key> \ No newline at end of file diff --git a/.gitignore b/.gitignore index 6f7d0ec..47093f6 100755 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,7 @@ # dont upload to github +.env +.env.* +!.env.example bin # c++ diff --git a/README.md b/README.md index 92d099a..9ca129e 100755 --- a/README.md +++ b/README.md @@ -34,6 +34,14 @@ make sure the go version shows up. otherwise just run manually. ``` "31,0,0,360,0,200,200,60,100200200630013" ``` +### Environment Configuration + +Copy `.env.example` to `.env`, then set the `AES_KEY_GCM` value. + +> **Note** +> - `AES_KEY_GCM` must be exactly **32 characters**. +> - Use the **same key** on both the robot and the base station. +> - Do not upload the `.env` file to GitHub. ### Author > Danu andrean diff --git a/go.mod b/go.mod index 1a76b50..225b74f 100755 --- a/go.mod +++ b/go.mod @@ -2,4 +2,7 @@ module Basestation go 1.13 -require github.com/gorilla/websocket v1.5.0 +require ( + github.com/gorilla/websocket v1.5.0 + github.com/joho/godotenv v1.5.1 +) diff --git a/go.sum b/go.sum index e5a03d4..86beca6 100755 --- a/go.sum +++ b/go.sum @@ -1,2 +1,4 @@ github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0= +github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= diff --git a/source/manager.go b/source/manager.go index 9049c80..a51dfe4 100755 --- a/source/manager.go +++ b/source/manager.go @@ -5,8 +5,8 @@ import ( "net" "net/http" "strconv" - "time" "strings" + "time" "github.com/gorilla/websocket" ) @@ -42,7 +42,7 @@ var gameController = GameController{} var timeout int64 = 5 func Init() { - gameController.VERSION = 1 + gameController.VERSION = 2 execute.robotExecute = "0" execute.privilege = 10 // higher is lower } @@ -76,94 +76,95 @@ func RefereeBoxHandler() { func ClientHandler() { - addr := net.UDPAddr{ - Port: 8124, - IP: net.ParseIP(GetIP()), - } - - ser, err := net.ListenUDP("udp", &addr) - if err != nil { - fmt.Printf("Some error %v\n", err) - return - } - - for { - - bytes := make([]byte, 512) - - n, remoteaddr, err := ser.ReadFromUDP(bytes) - if err != nil { - fmt.Printf("[ERROR] ReadFromUDP: %v\n", err) - continue - } - - raw := strings.TrimSpace(string(bytes[:n])) - if raw == "" { - fmt.Println("[WARN] Received empty data") - continue - } - - parts := strings.Split(raw, "|") - if len(parts) < 3 { - fmt.Printf("[ERROR] Incomplete encrypted data: %s\n", raw) - continue - } - - cipher := parts[0] - tag := parts[1] - iv := parts[2] - plaintext, err := DecryptAESGCM(iv, tag, cipher) + addr := net.UDPAddr{ + Port: 8124, + IP: net.ParseIP(GetIP()), + } - if err != nil { - fmt.Printf("[ERROR] Decryption failed: %v\n", err) - continue - } + ser, err := net.ListenUDP("udp", &addr) + if err != nil { + fmt.Printf("Some error %v\n", err) + return + } - fmt.Printf("[OK] Decrypted data: %s\n", plaintext) + for { - received := CleanString(plaintext) - dataAfterParseLoc := ParseLoc(received) + bytes := make([]byte, 512) - s := Split(received) - swap := Swap(s[10]) + n, remoteaddr, err := ser.ReadFromUDP(bytes) + if err != nil { + fmt.Printf("[ERROR] ReadFromUDP: %v\n", err) + continue + } - var container string - for i := 0; i < 9; i++ { - container = container + s[i] - } + raw := strings.TrimSpace(string(bytes[:n])) + if raw == "" { + fmt.Println("[WARN] Received empty data") + continue + } - container = CleanString(container) - swap = CleanString(swap) + // panjang minimal: IV(24) + TAG(32) + if len(raw) < 56 { + fmt.Printf("[ERROR] Data terlalu pendek: %s\n", raw) + continue + } - if container == swap { + // slicing FIXED (HEX) + iv := raw[0:24] // 12 byte + tag := raw[24:56] // 16 byte + cipher := raw[56:] // sisanya - id := GetID(dataAfterParseLoc) + plaintext, err := DecryptAESGCM(iv, tag, cipher) + if err != nil { + fmt.Printf("[ERROR] Decryption failed: %v\n", err) + continue + } - t := time.Now() + fmt.Printf("[OK] Decrypted data: %s\n", plaintext) - switch id[0] { - case '1': - times.timeR1 = t.Unix() - staging.R1 = dataAfterParseLoc - case '2': - times.timeR2 = t.Unix() - staging.R2 = dataAfterParseLoc - case '3': - times.timeR3 = t.Unix() - staging.R3 = dataAfterParseLoc - case '4': - times.timeR4 = t.Unix() - staging.R4 = dataAfterParseLoc - case '5': - times.timeR5 = t.Unix() - staging.R5 = dataAfterParseLoc - } + received := CleanString(plaintext) + dataAfterParseLoc := ParseLoc(received) - rvRobot := WhoIsExecute(id) - go ClientResponse(ser, remoteaddr, rvRobot) - } + s := Split(received) + swap := Swap(s[10]) - } + var container string + for i := 0; i < 9; i++ { + container = container + s[i] + } + + container = CleanString(container) + swap = CleanString(swap) + + if container == swap { + + id := GetID(dataAfterParseLoc) + + t := time.Now() + + switch id[0] { + case '1': + times.timeR1 = t.Unix() + staging.R1 = dataAfterParseLoc + case '2': + times.timeR2 = t.Unix() + staging.R2 = dataAfterParseLoc + case '3': + times.timeR3 = t.Unix() + staging.R3 = dataAfterParseLoc + case '4': + times.timeR4 = t.Unix() + staging.R4 = dataAfterParseLoc + case '5': + times.timeR5 = t.Unix() + staging.R5 = dataAfterParseLoc + } + + rvRobot := WhoIsExecute(id) + go ClientResponse(ser, remoteaddr, rvRobot) + } + + } } func ClientResponse(conn *net.UDPConn, addr *net.UDPAddr, rvRobot string) { @@ -331,4 +332,4 @@ func GetPrivilegeRobot(data string) int { return 5 } return 10 -} +} \ No newline at end of file diff --git a/source/utilities.go b/source/utilities.go index d828cb4..ce5b607 100755 --- a/source/utilities.go +++ b/source/utilities.go @@ -9,6 +9,10 @@ import ( "unicode" "net" "fmt" + "os" + "log" + + "github.com/joho/godotenv" ) func CleanString(data string) string { @@ -85,8 +89,18 @@ func GetIP() string { } func DecryptAESGCM(ivHex, tagHex, cipherHex string) (string, error) { - key := []byte("R5C9u@D!A7xP#LQ2mZ8F$wKJH4S1ErT0") + err := godotenv.Load() + if err != nil { + log.Fatal(".env tidak ditemukan") + } + + keyEnv := os.Getenv("AES_KEY_GCM") + if len(keyEnv) != 32 { + log.Fatal("AES_KEY_GCM tidak valid") + } + + key := []byte(keyEnv) // Convert HEX → BYTES iv, err := hex.DecodeString(ivHex) @@ -129,4 +143,4 @@ func DecryptAESGCM(ivHex, tagHex, cipherHex string) (string, error) { } return string(plain), nil -} +} \ No newline at end of file