|
|
@@ -548,3 +548,79 @@ func TestNewDeviceRandomTrailersAndDisableCookiesRoundTrip(t *testing.T) {
|
|
|
t.Fatal("timed out waiting for the server side to finish")
|
|
|
}
|
|
|
}
|
|
|
+
|
|
|
+// TestValidatedObfuscationAlwaysApplies pins the contract ValidateObfuscation
|
|
|
+// exists for: whatever it accepts, amneziawg-go's own IpcSet must accept too.
|
|
|
+func TestValidatedObfuscationAlwaysApplies(t *testing.T) {
|
|
|
+ priv, pub, err := wireguard.GenerateWireguardKeypair()
|
|
|
+ if err != nil {
|
|
|
+ t.Fatalf("server keypair: %v", err)
|
|
|
+ }
|
|
|
+ _, peerPub, err := wireguard.GenerateWireguardKeypair()
|
|
|
+ if err != nil {
|
|
|
+ t.Fatalf("peer keypair: %v", err)
|
|
|
+ }
|
|
|
+ base := amneziawg.Obfuscation31{Jc: 4, Jmin: 40, Jmax: 70, S1: 20, S2: 30, S3: 20, S4: 20}
|
|
|
+
|
|
|
+ cases := []struct {
|
|
|
+ name string
|
|
|
+ mut func(*amneziawg.Obfuscation31)
|
|
|
+ }{
|
|
|
+ {"generated defaults", func(o *amneziawg.Obfuscation31) { *o = amneziawg.GenerateObfuscation31() }},
|
|
|
+ {"S1 over uint16", func(o *amneziawg.Obfuscation31) { o.S1 = 70000 }},
|
|
|
+ {"S2 over uint16", func(o *amneziawg.Obfuscation31) { o.S2 = 70000 }},
|
|
|
+ {"negative Jc", func(o *amneziawg.Obfuscation31) { o.Jc = -1 }},
|
|
|
+ {"negative Jmin and Jmax", func(o *amneziawg.Obfuscation31) { o.Jmin, o.Jmax = -5, -1 }},
|
|
|
+ {"Jc over uint32", func(o *amneziawg.Obfuscation31) { o.Jc = 5000000000 }},
|
|
|
+ {"I1 unknown tag", func(o *amneziawg.Obfuscation31) { o.I1 = "<rand 100>" }},
|
|
|
+ {"I1 missing close", func(o *amneziawg.Obfuscation31) { o.I1 = "<r 100" }},
|
|
|
+ {"I1 empty tag", func(o *amneziawg.Obfuscation31) { o.I1 = "<>" }},
|
|
|
+ // The specs validateObfChain deliberately accepts must really apply.
|
|
|
+ {"I1 chained tags", func(o *amneziawg.Obfuscation31) { o.I1 = "<b ff00><r 10>" }},
|
|
|
+ {"I1 valueless tag", func(o *amneziawg.Obfuscation31) { o.I1 = "<t><rc 5>" }},
|
|
|
+ {"I1 no tags at all", func(o *amneziawg.Obfuscation31) { o.I1 = "plain text" }},
|
|
|
+ }
|
|
|
+
|
|
|
+ for i, tc := range cases {
|
|
|
+ t.Run(tc.name, func(t *testing.T) {
|
|
|
+ o := base
|
|
|
+ tc.mut(&o)
|
|
|
+ if err := amneziawg.ValidateObfuscation(o); err != nil {
|
|
|
+ return // rejected before saving, which is the whole point
|
|
|
+ }
|
|
|
+ inst := amneziawg.Instance{
|
|
|
+ Id: 88, InterfaceName: "awgcontract", ListenPort: 58900 + i,
|
|
|
+ PrivateKey: priv, PublicKey: pub,
|
|
|
+ Address: []string{"10.198.0.1/24"}, MTU: 1420,
|
|
|
+ Obfuscation: o,
|
|
|
+ Peers: []amneziawg.Peer{{
|
|
|
+ Email: "[email protected]", PublicKey: peerPub,
|
|
|
+ AllowedIPs: []string{"10.198.0.2/32"},
|
|
|
+ }},
|
|
|
+ }
|
|
|
+ opts := DeviceOptions{
|
|
|
+ HeaderProtectionKey: o.HeaderProtectionKey,
|
|
|
+ ContentPaddingAddition: o.ContentPaddingAddition,
|
|
|
+ RekeyAfterTime: o.RekeyAfterTime,
|
|
|
+ RekeyTimeout: o.RekeyTimeout,
|
|
|
+ RejectAfterTime: o.RejectAfterTime,
|
|
|
+ KeepaliveTimeout: o.KeepaliveTimeout,
|
|
|
+ MaxHandshakeAttempts: o.MaxHandshakeAttempts,
|
|
|
+ RandomTrailers: o.RandomTrailers,
|
|
|
+ DisableCookies: o.DisableCookies,
|
|
|
+ }
|
|
|
+ dev, err := newUnconfiguredDevice(inst, opts)
|
|
|
+ if err != nil {
|
|
|
+ t.Fatalf("newUnconfiguredDevice: %v", err)
|
|
|
+ }
|
|
|
+ defer dev.Close()
|
|
|
+ conf, err := buildUAPIConfig(inst, opts)
|
|
|
+ if err != nil {
|
|
|
+ t.Fatalf("buildUAPIConfig: %v", err)
|
|
|
+ }
|
|
|
+ if err := dev.IpcSet(conf); err != nil {
|
|
|
+ t.Fatalf("ValidateObfuscation accepted this config but amneziawg-go rejected it: %v", err)
|
|
|
+ }
|
|
|
+ })
|
|
|
+ }
|
|
|
+}
|