knative.dev/pkg@v0.0.0-20260602142205-ac97e43f6622/network/tls/config_test.go (about)

     1  /*
     2  Copyright 2026 The Knative Authors
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package tls
    18  
    19  import (
    20  	cryptotls "crypto/tls"
    21  	"testing"
    22  )
    23  
    24  func Test_parseVersion(t *testing.T) {
    25  	tests := []struct {
    26  		name    string
    27  		input   string
    28  		want    uint16
    29  		wantErr bool
    30  	}{
    31  		{name: "TLS 1.2", input: "1.2", want: cryptotls.VersionTLS12},
    32  		{name: "TLS 1.3", input: "1.3", want: cryptotls.VersionTLS13},
    33  		{name: "unsupported version", input: "1.0", wantErr: true},
    34  		{name: "unsupported version 1.1", input: "1.1", wantErr: true},
    35  		{name: "trailing space", input: "1.2 ", wantErr: true},
    36  		{name: "empty string", input: "", wantErr: true},
    37  		{name: "garbage", input: "abc", wantErr: true},
    38  	}
    39  
    40  	for _, tc := range tests {
    41  		t.Run(tc.name, func(t *testing.T) {
    42  			got, err := parseVersion(tc.input)
    43  			if tc.wantErr {
    44  				if err == nil {
    45  					t.Fatalf("parseVersion(%q) = %d, want error", tc.input, got)
    46  				}
    47  				return
    48  			}
    49  			if err != nil {
    50  				t.Fatalf("parseVersion(%q) unexpected error: %v", tc.input, err)
    51  			}
    52  			if got != tc.want {
    53  				t.Errorf("parseVersion(%q) = %d, want %d", tc.input, got, tc.want)
    54  			}
    55  		})
    56  	}
    57  }
    58  
    59  func Test_parseCipherSuites(t *testing.T) {
    60  	tests := []struct {
    61  		name    string
    62  		input   string
    63  		want    []uint16
    64  		wantErr bool
    65  	}{
    66  		{
    67  			name:  "single suite",
    68  			input: "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
    69  			want:  []uint16{cryptotls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256},
    70  		},
    71  		{
    72  			name:  "multiple suites",
    73  			input: "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384",
    74  			want: []uint16{
    75  				cryptotls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
    76  				cryptotls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
    77  			},
    78  		},
    79  		{
    80  			name:  "whitespace trimmed",
    81  			input: " TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 , TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 ",
    82  			want: []uint16{
    83  				cryptotls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
    84  				cryptotls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
    85  			},
    86  		},
    87  		{
    88  			name:  "empty parts skipped",
    89  			input: "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,,",
    90  			want:  []uint16{cryptotls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256},
    91  		},
    92  		{
    93  			name:    "unknown suite",
    94  			input:   "DOES_NOT_EXIST",
    95  			wantErr: true,
    96  		},
    97  		{
    98  			name: "empty string",
    99  			want: []uint16{},
   100  		},
   101  	}
   102  
   103  	for _, tc := range tests {
   104  		t.Run(tc.name, func(t *testing.T) {
   105  			got, err := parseCipherSuites(tc.input)
   106  			if tc.wantErr {
   107  				if err == nil {
   108  					t.Fatalf("parseCipherSuites(%q) = %v, want error", tc.input, got)
   109  				}
   110  				return
   111  			}
   112  			if err != nil {
   113  				t.Fatalf("parseCipherSuites(%q) unexpected error: %v", tc.input, err)
   114  			}
   115  			if len(got) != len(tc.want) {
   116  				t.Fatalf("parseCipherSuites(%q) returned %d suites, want %d", tc.input, len(got), len(tc.want))
   117  			}
   118  			for i := range tc.want {
   119  				if got[i] != tc.want[i] {
   120  					t.Errorf("parseCipherSuites(%q)[%d] = %d, want %d", tc.input, i, got[i], tc.want[i])
   121  				}
   122  			}
   123  		})
   124  	}
   125  }
   126  
   127  func Test_parseCurvePreferences(t *testing.T) {
   128  	tests := []struct {
   129  		name    string
   130  		input   string
   131  		want    []cryptotls.CurveID
   132  		wantErr bool
   133  	}{
   134  		{
   135  			name:  "Go constant name X25519",
   136  			input: "X25519",
   137  			want:  []cryptotls.CurveID{cryptotls.X25519},
   138  		},
   139  		{
   140  			name:  "Go constant name CurveP256",
   141  			input: "CurveP256",
   142  			want:  []cryptotls.CurveID{cryptotls.CurveP256},
   143  		},
   144  		{
   145  			name:  "standard name P-256",
   146  			input: "P-256",
   147  			want:  []cryptotls.CurveID{cryptotls.CurveP256},
   148  		},
   149  		{
   150  			name:  "multiple curves with mixed naming",
   151  			input: "X25519,P-256,CurveP384",
   152  			want: []cryptotls.CurveID{
   153  				cryptotls.X25519,
   154  				cryptotls.CurveP256,
   155  				cryptotls.CurveP384,
   156  			},
   157  		},
   158  		{
   159  			name:  "whitespace trimmed",
   160  			input: " X25519 , CurveP256 ",
   161  			want: []cryptotls.CurveID{
   162  				cryptotls.X25519,
   163  				cryptotls.CurveP256,
   164  			},
   165  		},
   166  		{
   167  			name:  "all curves by standard name",
   168  			input: "P-256,P-384,P-521,X25519",
   169  			want: []cryptotls.CurveID{
   170  				cryptotls.CurveP256,
   171  				cryptotls.CurveP384,
   172  				cryptotls.CurveP521,
   173  				cryptotls.X25519,
   174  			},
   175  		},
   176  		{
   177  			name:  "post-quantum hybrid X25519MLKEM768",
   178  			input: "X25519MLKEM768",
   179  			want:  []cryptotls.CurveID{cryptotls.X25519MLKEM768},
   180  		},
   181  		{
   182  			name:    "unknown curve",
   183  			input:   "CurveP128",
   184  			wantErr: true,
   185  		},
   186  		{
   187  			name: "empty string",
   188  			want: []cryptotls.CurveID{},
   189  		},
   190  	}
   191  
   192  	for _, tc := range tests {
   193  		t.Run(tc.name, func(t *testing.T) {
   194  			got, err := parseCurvePreferences(tc.input)
   195  			if tc.wantErr {
   196  				if err == nil {
   197  					t.Fatalf("parseCurvePreferences(%q) = %v, want error", tc.input, got)
   198  				}
   199  				return
   200  			}
   201  			if err != nil {
   202  				t.Fatalf("parseCurvePreferences(%q) unexpected error: %v", tc.input, err)
   203  			}
   204  			if len(got) != len(tc.want) {
   205  				t.Fatalf("parseCurvePreferences(%q) returned %d curves, want %d", tc.input, len(got), len(tc.want))
   206  			}
   207  			for i := range tc.want {
   208  				if got[i] != tc.want[i] {
   209  					t.Errorf("parseCurvePreferences(%q)[%d] = %d, want %d", tc.input, i, got[i], tc.want[i])
   210  				}
   211  			}
   212  		})
   213  	}
   214  }
   215  
   216  func TestDefaultConfigFromEnv(t *testing.T) {
   217  	t.Run("no env vars returns TLS 1.3 default", func(t *testing.T) {
   218  		cfg, err := DefaultConfigFromEnv("")
   219  		if err != nil {
   220  			t.Fatal("unexpected error:", err)
   221  		}
   222  		if cfg.MinVersion != cryptotls.VersionTLS13 {
   223  			t.Errorf("MinVersion = %d, want %d", cfg.MinVersion, cryptotls.VersionTLS13)
   224  		}
   225  		if cfg.MaxVersion != 0 {
   226  			t.Errorf("MaxVersion = %d, want 0", cfg.MaxVersion)
   227  		}
   228  		if cfg.CipherSuites != nil {
   229  			t.Errorf("CipherSuites = %v, want nil", cfg.CipherSuites)
   230  		}
   231  		if cfg.CurvePreferences != nil {
   232  			t.Errorf("CurvePreferences = %v, want nil", cfg.CurvePreferences)
   233  		}
   234  	})
   235  
   236  	t.Run("min version from env overrides default", func(t *testing.T) {
   237  		t.Setenv(MinVersionEnvKey, "1.2")
   238  		cfg, err := DefaultConfigFromEnv("")
   239  		if err != nil {
   240  			t.Fatal("unexpected error:", err)
   241  		}
   242  		if cfg.MinVersion != cryptotls.VersionTLS12 {
   243  			t.Errorf("MinVersion = %d, want %d", cfg.MinVersion, cryptotls.VersionTLS12)
   244  		}
   245  	})
   246  
   247  	t.Run("max version from env", func(t *testing.T) {
   248  		t.Setenv(MaxVersionEnvKey, "1.3")
   249  		cfg, err := DefaultConfigFromEnv("")
   250  		if err != nil {
   251  			t.Fatal("unexpected error:", err)
   252  		}
   253  		if cfg.MaxVersion != cryptotls.VersionTLS13 {
   254  			t.Errorf("MaxVersion = %d, want %d", cfg.MaxVersion, cryptotls.VersionTLS13)
   255  		}
   256  	})
   257  
   258  	t.Run("cipher suites from env", func(t *testing.T) {
   259  		t.Setenv(CipherSuitesEnvKey, "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256")
   260  		cfg, err := DefaultConfigFromEnv("")
   261  		if err != nil {
   262  			t.Fatal("unexpected error:", err)
   263  		}
   264  		if len(cfg.CipherSuites) != 1 || cfg.CipherSuites[0] != cryptotls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 {
   265  			t.Errorf("CipherSuites = %v, want [%d]", cfg.CipherSuites, cryptotls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256)
   266  		}
   267  	})
   268  
   269  	t.Run("curve preferences from env", func(t *testing.T) {
   270  		t.Setenv(CurvePreferencesEnvKey, "X25519,CurveP256")
   271  		cfg, err := DefaultConfigFromEnv("")
   272  		if err != nil {
   273  			t.Fatal("unexpected error:", err)
   274  		}
   275  		if len(cfg.CurvePreferences) != 2 {
   276  			t.Fatalf("CurvePreferences has %d entries, want 2", len(cfg.CurvePreferences))
   277  		}
   278  		if cfg.CurvePreferences[0] != cryptotls.X25519 {
   279  			t.Errorf("CurvePreferences[0] = %d, want %d", cfg.CurvePreferences[0], cryptotls.X25519)
   280  		}
   281  		if cfg.CurvePreferences[1] != cryptotls.CurveP256 {
   282  			t.Errorf("CurvePreferences[1] = %d, want %d", cfg.CurvePreferences[1], cryptotls.CurveP256)
   283  		}
   284  	})
   285  
   286  	t.Run("prefix is prepended to env key", func(t *testing.T) {
   287  		t.Setenv("WEBHOOK_TLS_MIN_VERSION", "1.2")
   288  		cfg, err := DefaultConfigFromEnv("WEBHOOK_")
   289  		if err != nil {
   290  			t.Fatal("unexpected error:", err)
   291  		}
   292  		if cfg.MinVersion != cryptotls.VersionTLS12 {
   293  			t.Errorf("MinVersion = %d, want %d", cfg.MinVersion, cryptotls.VersionTLS12)
   294  		}
   295  	})
   296  
   297  	t.Run("all env vars set", func(t *testing.T) {
   298  		t.Setenv(MinVersionEnvKey, "1.2")
   299  		t.Setenv(MaxVersionEnvKey, "1.3")
   300  		t.Setenv(CipherSuitesEnvKey, "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384")
   301  		t.Setenv(CurvePreferencesEnvKey, "X25519,P-256")
   302  
   303  		cfg, err := DefaultConfigFromEnv("")
   304  		if err != nil {
   305  			t.Fatal("unexpected error:", err)
   306  		}
   307  		if cfg.MinVersion != cryptotls.VersionTLS12 {
   308  			t.Errorf("MinVersion = %d, want %d", cfg.MinVersion, cryptotls.VersionTLS12)
   309  		}
   310  		if cfg.MaxVersion != cryptotls.VersionTLS13 {
   311  			t.Errorf("MaxVersion = %d, want %d", cfg.MaxVersion, cryptotls.VersionTLS13)
   312  		}
   313  		if len(cfg.CipherSuites) != 2 {
   314  			t.Fatalf("CipherSuites has %d entries, want 2", len(cfg.CipherSuites))
   315  		}
   316  		if len(cfg.CurvePreferences) != 2 {
   317  			t.Fatalf("CurvePreferences has %d entries, want 2", len(cfg.CurvePreferences))
   318  		}
   319  	})
   320  
   321  	t.Run("invalid min version", func(t *testing.T) {
   322  		t.Setenv(MinVersionEnvKey, "1.0")
   323  		_, err := DefaultConfigFromEnv("")
   324  		if err == nil {
   325  			t.Fatal("expected error for invalid min version")
   326  		}
   327  	})
   328  
   329  	t.Run("invalid max version", func(t *testing.T) {
   330  		t.Setenv(MaxVersionEnvKey, "bad")
   331  		_, err := DefaultConfigFromEnv("")
   332  		if err == nil {
   333  			t.Fatal("expected error for invalid max version")
   334  		}
   335  	})
   336  
   337  	t.Run("invalid cipher suite", func(t *testing.T) {
   338  		t.Setenv(CipherSuitesEnvKey, "NOT_A_REAL_CIPHER")
   339  		_, err := DefaultConfigFromEnv("")
   340  		if err == nil {
   341  			t.Fatal("expected error for invalid cipher suite")
   342  		}
   343  	})
   344  
   345  	t.Run("invalid curve", func(t *testing.T) {
   346  		t.Setenv(CurvePreferencesEnvKey, "NotACurve")
   347  		_, err := DefaultConfigFromEnv("")
   348  		if err == nil {
   349  			t.Fatal("expected error for invalid curve")
   350  		}
   351  	})
   352  }