package main import ( "encoding/base64" "os" "reflect" "testing" ) func TestRealityVectors(t *testing.T) { raw, err := os.ReadFile("testdata/reality_vectors.json") if err != nil { t.Fatal(err) } var vectors []KeyPair if err := unmarshalJSONUseNumber(raw, &vectors); err != nil { t.Fatal(err) } if len(vectors) == 0 { t.Fatal("no vectors") } for _, v := range vectors { got, err := realityPublicKeyFromPrivate(v.PrivateKey) if err != nil { t.Fatalf("derive from %s: %v", v.PrivateKey, err) } if got != v.PublicKey { t.Errorf("private %s: got public %s, want %s", v.PrivateKey, got, v.PublicKey) } } } func TestGenerateRealityKeyPairFormat(t *testing.T) { pair := GenerateRealityKeyPair() for _, k := range []string{pair.PrivateKey, pair.PublicKey} { if len(k) != 43 { t.Errorf("key %q length %d, want 43", k, len(k)) } raw, err := base64.RawURLEncoding.DecodeString(k) if err != nil { t.Errorf("key %q not base64url: %v", k, err) } if len(raw) != 32 { t.Errorf("key %q decodes to %d bytes, want 32", k, len(raw)) } } derived, err := realityPublicKeyFromPrivate(pair.PrivateKey) if err != nil { t.Fatal(err) } if derived != pair.PublicKey { t.Errorf("generated pair inconsistent: derived %s, public %s", derived, pair.PublicKey) } } func TestGoldenResources(t *testing.T) { raw, err := os.ReadFile("testdata/golden_resources.json") if err != nil { t.Fatal(err) } var golden map[string]any if err := unmarshalJSONUseNumber(raw, &golden); err != nil { t.Fatal(err) } exitPassword := "AwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8gISI=" rfNode := &Node{ ID: "nod_rf", Name: "RF-Reality", ServerID: "srv_entry", ExitServerID: StrPtr("srv_exit"), Protocol: "vless-reality-vision", Topology: "forward", ExitProtocol: StrPtr("shadowsocks-2022-aes256"), NetworkMode: "ipv4", RealityServerName: "www.amd.com", RealityDestPort: 443, Port: 23456, ExitPort: IntPtr(23457), Status: "active", ResourceID: "res_rf", ExitResourceID: "res_rfx", WSPath: StrPtr("/rfpath"), Reality: &RealityConfig{KeyID: "reality-nod_rf", ServerName: "www.amd.com", DestPort: 443}, ExitCredentials: &ExitCredentials{ClientID: "11111111-2222-3333-4444-0000000000ff", Password: exitPassword, SocksUser: "ng_0123456789"}, } rfClients := []*NodeClient{ {NodeID: "nod_rf", CustomerID: "cus_1", Credentials: Credentials{ClientID: "11111111-2222-3333-4444-000000000001", RelayPassword: "unused1", ShortID: "abcd0001ef01"}}, {NodeID: "nod_rf", CustomerID: "cus_2", Credentials: Credentials{ClientID: "11111111-2222-3333-4444-000000000002", RelayPassword: "unused2", ShortID: "abcd0002ef01"}}, } entryServer := &Server{ID: "srv_entry", PublicAddress: "203.0.113.10"} exitServer := &Server{ID: "srv_exit", PublicAddress: "exit.example.com"} exitServerLocal := &Server{ID: "srv_exit", PublicAddress: "203.0.113.20"} ssNode := &Node{ ID: "nod_ss", Name: "SS-Direct", ServerID: "srv_entry", Protocol: "shadowsocks-2022-aes128", Topology: "direct", NetworkMode: "ipv4", Port: 23458, Status: "active", ResourceID: "res_ss", WSPath: StrPtr("/sspath"), SSServerKey: StrPtr("CwwNDg8QERITFBUWFxgZGg=="), } ssClients := []*NodeClient{ {NodeID: "nod_ss", CustomerID: "cus_1", Credentials: Credentials{ClientID: "11111111-2222-3333-4444-000000000001", RelayPassword: "FRYXGBkaGxwdHh8gISIjJA==", ShortID: "abcd0001ef01"}}, } ssServer := &Server{ID: "srv_entry", PublicAddress: "203.0.113.11"} rfEntry, err := BuildNodeResource(BuildNodeResourceParams{ ResourceID: "res_rf", TagPrefix: tagForNode(rfNode), Node: rfNode, NodeClients: rfClients, Server: entryServer, ExitServer: exitServer, }) if err != nil { t.Fatal(err) } rfExit, err := BuildNodeExitResource(BuildNodeExitResourceParams{ ResourceID: "res_rfx", TagPrefix: tagForNode(rfNode), Node: rfNode, Server: exitServerLocal, }) if err != nil { t.Fatal(err) } ssEntry, err := BuildNodeResource(BuildNodeResourceParams{ ResourceID: "res_ss", TagPrefix: tagForNode(ssNode), Node: ssNode, NodeClients: ssClients, Server: ssServer, }) if err != nil { t.Fatal(err) } for name, got := range map[string]map[string]any{"rfEntry": rfEntry, "rfExit": rfExit, "ssEntry": ssEntry} { rawGot, err := marshalJSON(got) if err != nil { t.Fatal(err) } var decoded any if err := unmarshalJSONUseNumber(rawGot, &decoded); err != nil { t.Fatal(err) } if !reflect.DeepEqual(decoded, golden[name]) { t.Errorf("%s mismatch:\n got: %s\nwant: %v", name, rawGot, golden[name]) } } }