t.Error("Server connection was not closed when the context was canceled")
}
}
- serverHasAESGCM: false,
- expectedCipher: TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305,
+
+ func TestAESCipherReordering(t *testing.T) {
+ currentAESSupport := hasAESGCMHardwareSupport
+ defer func() { hasAESGCMHardwareSupport = currentAESSupport; initDefaultCipherSuites() }()
+
+ tests := []struct {
+ name string
+ clientCiphers []uint16
+ serverHasAESGCM bool
+ preferServerCipherSuites bool
+ serverCiphers []uint16
+ expectedCipher uint16
++ boringExpectedCipher uint16 // If non-zero, used when BoringCrypto is enabled.
+ }{
+ {
+ name: "server has hardware AES, client doesn't (pick ChaCha)",
+ clientCiphers: []uint16{
+ TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305,
+ TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
+ TLS_RSA_WITH_AES_128_CBC_SHA,
+ },
+ serverHasAESGCM: true,
+ preferServerCipherSuites: true,
+ expectedCipher: TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305,
+ },
+ {
+ name: "server strongly prefers AES-GCM, client doesn't (pick AES-GCM)",
+ clientCiphers: []uint16{
+ TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305,
+ TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
+ TLS_RSA_WITH_AES_128_CBC_SHA,
+ },
+ serverHasAESGCM: true,
+ preferServerCipherSuites: true,
+ serverCiphers: []uint16{
+ TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
+ TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305,
+ TLS_RSA_WITH_AES_128_CBC_SHA,
+ },
+ expectedCipher: TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
+ },
+ {
+ name: "client prefers AES-GCM, server doesn't have hardware AES (pick ChaCha)",
+ clientCiphers: []uint16{
+ TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
+ TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305,
+ TLS_RSA_WITH_AES_128_CBC_SHA,
+ },
- serverHasAESGCM: false,
- expectedCipher: TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305,
++ serverHasAESGCM: false,
++ expectedCipher: TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305,
++ boringExpectedCipher: TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, // When BoringCrypto is enabled, AES-GCM is prioritized even without server hardware.
+ },
+ {
+ name: "client prefers AES-GCM, server has hardware AES (pick AES-GCM)",
+ clientCiphers: []uint16{
+ TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
+ TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305,
+ TLS_RSA_WITH_AES_128_CBC_SHA,
+ },
+ serverHasAESGCM: true,
+ expectedCipher: TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
+ },
+ {
+ name: "client prefers AES-GCM and sends GREASE, server has hardware AES (pick AES-GCM)",
+ clientCiphers: []uint16{
+ 0x0A0A, // GREASE value
+ TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
+ TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305,
+ TLS_RSA_WITH_AES_128_CBC_SHA,
+ },
+ serverHasAESGCM: true,
+ expectedCipher: TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
+ },
+ {
+ name: "client prefers AES-GCM and doesn't support ChaCha, server doesn't have hardware AES (pick AES-GCM)",
+ clientCiphers: []uint16{
+ TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
+ TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
+ TLS_RSA_WITH_AES_128_CBC_SHA,
+ },
+ serverHasAESGCM: false,
+ expectedCipher: TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
+ },
+ {
+ name: "client prefers AES-GCM and AES-CBC over ChaCha, server doesn't have hardware AES (pick AES-GCM)",
+ clientCiphers: []uint16{
+ TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
+ TLS_RSA_WITH_AES_128_CBC_SHA,
+ TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305,
+ },
+ serverHasAESGCM: false,
+ expectedCipher: TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
+ },
+ {
+ name: "client prefers AES-GCM over ChaCha and sends GREASE, server doesn't have hardware AES (pick ChaCha)",
+ clientCiphers: []uint16{
+ 0x0A0A, // GREASE value
+ TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
+ TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305,
+ TLS_RSA_WITH_AES_128_CBC_SHA,
+ },
- if tc.expectedCipher != hs.suite.id {
- t.Errorf("unexpected cipher chosen: want %d, got %d", tc.expectedCipher, hs.suite.id)
++ serverHasAESGCM: false,
++ expectedCipher: TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305,
++ boringExpectedCipher: TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, // When BoringCrypto is enabled, AES-GCM is prioritized even without server hardware.
+ },
+ {
+ name: "client supports multiple AES-GCM, server doesn't have hardware AES and doesn't support ChaCha (pick corrent AES-GCM)",
+ clientCiphers: []uint16{
+ TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
+ TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305,
+ TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
+ },
+ serverHasAESGCM: false,
+ serverCiphers: []uint16{
+ TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
+ TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
+ },
+ expectedCipher: TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
+ },
+ }
+
+ for _, tc := range tests {
+ t.Run(tc.name, func(t *testing.T) {
+ hasAESGCMHardwareSupport = tc.serverHasAESGCM
+ initDefaultCipherSuites()
+ hs := &serverHandshakeState{
+ c: &Conn{
+ config: &Config{
+ PreferServerCipherSuites: tc.preferServerCipherSuites,
+ CipherSuites: tc.serverCiphers,
+ },
+ vers: VersionTLS12,
+ },
+ clientHello: &clientHelloMsg{
+ cipherSuites: tc.clientCiphers,
+ vers: VersionTLS12,
+ },
+ ecdheOk: true,
+ rsaSignOk: true,
+ rsaDecryptOk: true,
+ }
+
+ err := hs.pickCipherSuite()
+ if err != nil {
+ t.Errorf("pickCipherSuite failed: %s", err)
+ }
+
- serverHasAESGCM: false,
- expectedCipher: TLS_CHACHA20_POLY1305_SHA256,
++ want := tc.expectedCipher
++ if boringEnabled && tc.boringExpectedCipher != 0 {
++ want = tc.boringExpectedCipher
++ }
++ if want != hs.suite.id {
++ t.Errorf("unexpected cipher chosen: want %d, got %d", want, hs.suite.id)
+ }
+ })
+ }
+ }
+
+ func TestAESCipherReordering13(t *testing.T) {
+ currentAESSupport := hasAESGCMHardwareSupport
+ defer func() { hasAESGCMHardwareSupport = currentAESSupport; initDefaultCipherSuites() }()
+
+ tests := []struct {
+ name string
+ clientCiphers []uint16
+ serverHasAESGCM bool
+ preferServerCipherSuites bool
+ expectedCipher uint16
++ boringExpectedCipher uint16 // If non-zero, used when BoringCrypto is enabled.
+ }{
+ {
+ name: "server has hardware AES, client doesn't (pick ChaCha)",
+ clientCiphers: []uint16{
+ TLS_CHACHA20_POLY1305_SHA256,
+ TLS_AES_128_GCM_SHA256,
+ },
+ serverHasAESGCM: true,
+ preferServerCipherSuites: true,
+ expectedCipher: TLS_CHACHA20_POLY1305_SHA256,
+ },
+ {
+ name: "neither server nor client have hardware AES (pick ChaCha)",
+ clientCiphers: []uint16{
+ TLS_CHACHA20_POLY1305_SHA256,
+ TLS_AES_128_GCM_SHA256,
+ },
+ serverHasAESGCM: false,
+ preferServerCipherSuites: true,
+ expectedCipher: TLS_CHACHA20_POLY1305_SHA256,
+ },
+ {
+ name: "client prefers AES, server doesn't have hardware, prefer server ciphers (pick ChaCha)",
+ clientCiphers: []uint16{
+ TLS_AES_128_GCM_SHA256,
+ TLS_CHACHA20_POLY1305_SHA256,
+ },
+ serverHasAESGCM: false,
+ preferServerCipherSuites: true,
+ expectedCipher: TLS_CHACHA20_POLY1305_SHA256,
++ boringExpectedCipher: TLS_AES_128_GCM_SHA256, // When BoringCrypto is enabled, AES-GCM is prioritized even without server hardware.
+ },
+ {
+ name: "client prefers AES and sends GREASE, server doesn't have hardware, prefer server ciphers (pick ChaCha)",
+ clientCiphers: []uint16{
+ 0x0A0A, // GREASE value
+ TLS_AES_128_GCM_SHA256,
+ TLS_CHACHA20_POLY1305_SHA256,
+ },
+ serverHasAESGCM: false,
+ preferServerCipherSuites: true,
+ expectedCipher: TLS_CHACHA20_POLY1305_SHA256,
++ boringExpectedCipher: TLS_AES_128_GCM_SHA256, // When BoringCrypto is enabled, AES-GCM is prioritized even without server hardware.
+ },
+ {
+ name: "client prefers AES, server doesn't (pick ChaCha)",
+ clientCiphers: []uint16{
+ TLS_AES_128_GCM_SHA256,
+ TLS_CHACHA20_POLY1305_SHA256,
+ },
- if tc.expectedCipher != hs.suite.id {
- t.Errorf("unexpected cipher chosen: want %d, got %d", tc.expectedCipher, hs.suite.id)
++ serverHasAESGCM: false,
++ expectedCipher: TLS_CHACHA20_POLY1305_SHA256,
++ boringExpectedCipher: TLS_AES_128_GCM_SHA256, // When BoringCrypto is enabled, AES-GCM is prioritized even without server hardware.
+ },
+ {
+ name: "client prefers AES, server has hardware AES (pick AES)",
+ clientCiphers: []uint16{
+ TLS_AES_128_GCM_SHA256,
+ TLS_CHACHA20_POLY1305_SHA256,
+ },
+ serverHasAESGCM: true,
+ expectedCipher: TLS_AES_128_GCM_SHA256,
+ },
+ {
+ name: "client prefers AES and sends GREASE, server has hardware AES (pick AES)",
+ clientCiphers: []uint16{
+ 0x0A0A, // GREASE value
+ TLS_AES_128_GCM_SHA256,
+ TLS_CHACHA20_POLY1305_SHA256,
+ },
+ serverHasAESGCM: true,
+ expectedCipher: TLS_AES_128_GCM_SHA256,
+ },
+ }
+
+ for _, tc := range tests {
+ t.Run(tc.name, func(t *testing.T) {
+ hasAESGCMHardwareSupport = tc.serverHasAESGCM
+ initDefaultCipherSuites()
+ hs := &serverHandshakeStateTLS13{
+ c: &Conn{
+ config: &Config{
+ PreferServerCipherSuites: tc.preferServerCipherSuites,
+ },
+ vers: VersionTLS13,
+ },
+ clientHello: &clientHelloMsg{
+ cipherSuites: tc.clientCiphers,
+ supportedVersions: []uint16{VersionTLS13},
+ compressionMethods: []uint8{compressionNone},
+ keyShares: []keyShare{{group: X25519, data: curve25519.Basepoint}},
+ },
+ }
+
+ err := hs.processClientHello()
+ if err != nil {
+ t.Errorf("pickCipherSuite failed: %s", err)
+ }
+
++ want := tc.expectedCipher
++ if boringEnabled && tc.boringExpectedCipher != 0 {
++ want = tc.boringExpectedCipher
++ }
++ if want != hs.suite.id {
++ t.Errorf("unexpected cipher chosen: want %d, got %d", want, hs.suite.id)
+ }
+ })
+ }
+ }