github.com/pion/dtls/v2@v2.2.12/e2e/e2e_openssl_test.go (about)

     1  // SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
     2  // SPDX-License-Identifier: MIT
     3  
     4  //go:build openssl && !js
     5  // +build openssl,!js
     6  
     7  package e2e
     8  
     9  import (
    10  	"crypto/x509"
    11  	"encoding/pem"
    12  	"errors"
    13  	"fmt"
    14  	"io/ioutil"
    15  	"net"
    16  	"os"
    17  	"os/exec"
    18  	"regexp"
    19  	"strings"
    20  	"testing"
    21  	"time"
    22  
    23  	"github.com/pion/dtls/v2"
    24  )
    25  
    26  func serverOpenSSL(c *comm) {
    27  	go func() {
    28  		c.serverMutex.Lock()
    29  		defer c.serverMutex.Unlock()
    30  
    31  		cfg := c.serverConfig
    32  
    33  		// create openssl arguments
    34  		args := []string{
    35  			"s_server",
    36  			"-dtls1_2",
    37  			"-quiet",
    38  			"-verify_quiet",
    39  			"-verify_return_error",
    40  			fmt.Sprintf("-accept=%d", c.serverPort),
    41  		}
    42  		ciphers := ciphersOpenSSL(cfg)
    43  		if ciphers != "" {
    44  			args = append(args, fmt.Sprintf("-cipher=%s", ciphers))
    45  		}
    46  
    47  		// psk arguments
    48  		if cfg.PSK != nil {
    49  			psk, err := cfg.PSK(nil)
    50  			if err != nil {
    51  				c.errChan <- err
    52  				return
    53  			}
    54  			args = append(args, fmt.Sprintf("-psk=%X", psk))
    55  			if len(cfg.PSKIdentityHint) > 0 {
    56  				args = append(args, fmt.Sprintf("-psk_hint=%s", cfg.PSKIdentityHint))
    57  			}
    58  		}
    59  
    60  		// certs arguments
    61  		if len(cfg.Certificates) > 0 {
    62  			// create temporary cert files
    63  			certPEM, keyPEM, err := writeTempPEM(cfg)
    64  			if err != nil {
    65  				c.errChan <- err
    66  				return
    67  			}
    68  			args = append(args,
    69  				fmt.Sprintf("-cert=%s", certPEM),
    70  				fmt.Sprintf("-key=%s", keyPEM))
    71  			defer func() {
    72  				_ = os.Remove(certPEM)
    73  				_ = os.Remove(keyPEM)
    74  			}()
    75  		} else {
    76  			args = append(args, "-nocert")
    77  		}
    78  
    79  		// launch command
    80  		// #nosec G204
    81  		cmd := exec.CommandContext(c.ctx, "openssl", args...)
    82  		var inner net.Conn
    83  		inner, c.serverConn = net.Pipe()
    84  		cmd.Stdin = inner
    85  		cmd.Stdout = inner
    86  		cmd.Stderr = os.Stderr
    87  		if err := cmd.Start(); err != nil {
    88  			c.errChan <- err
    89  			_ = inner.Close()
    90  			return
    91  		}
    92  
    93  		// Ensure that server has started
    94  		time.Sleep(500 * time.Millisecond)
    95  
    96  		c.serverReady <- struct{}{}
    97  		simpleReadWrite(c.errChan, c.serverChan, c.serverConn, c.messageRecvCount)
    98  	}()
    99  }
   100  
   101  func clientOpenSSL(c *comm) {
   102  	select {
   103  	case <-c.serverReady:
   104  		// OK
   105  	case <-time.After(time.Second):
   106  		c.errChan <- errors.New("waiting on serverReady err: timeout")
   107  	}
   108  
   109  	c.clientMutex.Lock()
   110  	defer c.clientMutex.Unlock()
   111  
   112  	cfg := c.clientConfig
   113  
   114  	// create openssl arguments
   115  	args := []string{
   116  		"s_client",
   117  		"-dtls1_2",
   118  		"-quiet",
   119  		"-verify_quiet",
   120  		"-servername=localhost",
   121  		fmt.Sprintf("-connect=127.0.0.1:%d", c.serverPort),
   122  	}
   123  	ciphers := ciphersOpenSSL(cfg)
   124  	if ciphers != "" {
   125  		args = append(args, fmt.Sprintf("-cipher=%s", ciphers))
   126  	}
   127  
   128  	// psk arguments
   129  	if cfg.PSK != nil {
   130  		psk, err := cfg.PSK(nil)
   131  		if err != nil {
   132  			c.errChan <- err
   133  			return
   134  		}
   135  		args = append(args, fmt.Sprintf("-psk=%X", psk))
   136  	}
   137  
   138  	// certificate arguments
   139  	if len(cfg.Certificates) > 0 {
   140  		// create temporary cert files
   141  		certPEM, keyPEM, err := writeTempPEM(cfg)
   142  		if err != nil {
   143  			c.errChan <- err
   144  			return
   145  		}
   146  		args = append(args, fmt.Sprintf("-CAfile=%s", certPEM), fmt.Sprintf("-cert=%s", certPEM), fmt.Sprintf("-key=%s", keyPEM))
   147  		defer func() {
   148  			_ = os.Remove(certPEM)
   149  			_ = os.Remove(keyPEM)
   150  		}()
   151  	}
   152  	if !cfg.InsecureSkipVerify {
   153  		args = append(args, "-verify_return_error")
   154  	}
   155  
   156  	// launch command
   157  	// #nosec G204
   158  	cmd := exec.CommandContext(c.ctx, "openssl", args...)
   159  	var inner net.Conn
   160  	inner, c.clientConn = net.Pipe()
   161  	cmd.Stdin = inner
   162  	cmd.Stdout = inner
   163  	cmd.Stderr = os.Stderr
   164  	if err := cmd.Start(); err != nil {
   165  		c.errChan <- err
   166  		_ = inner.Close()
   167  		return
   168  	}
   169  
   170  	simpleReadWrite(c.errChan, c.clientChan, c.clientConn, c.messageRecvCount)
   171  }
   172  
   173  func ciphersOpenSSL(cfg *dtls.Config) string {
   174  	// See https://tls.mbed.org/supported-ssl-ciphersuites
   175  	translate := map[dtls.CipherSuiteID]string{
   176  		dtls.TLS_ECDHE_ECDSA_WITH_AES_128_CCM:   "ECDHE-ECDSA-AES128-CCM",
   177  		dtls.TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8: "ECDHE-ECDSA-AES128-CCM8",
   178  
   179  		dtls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256: "ECDHE-ECDSA-AES128-GCM-SHA256",
   180  		dtls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384: "ECDHE-ECDSA-AES256-GCM-SHA384",
   181  
   182  		dtls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256: "ECDHE-RSA-AES128-GCM-SHA256",
   183  		dtls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384: "ECDHE-RSA-AES256-GCM-SHA384",
   184  
   185  		dtls.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA: "ECDHE-ECDSA-AES256-SHA",
   186  		dtls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA:   "ECDHE-RSA-AES256-SHA",
   187  
   188  		dtls.TLS_PSK_WITH_AES_128_CCM:   "PSK-AES128-CCM",
   189  		dtls.TLS_PSK_WITH_AES_128_CCM_8: "PSK-AES128-CCM8",
   190  		dtls.TLS_PSK_WITH_AES_256_CCM_8: "PSK-AES256-CCM8",
   191  
   192  		dtls.TLS_PSK_WITH_AES_128_GCM_SHA256: "PSK-AES128-GCM-SHA256",
   193  
   194  		dtls.TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256: "ECDHE-PSK-AES128-CBC-SHA256",
   195  	}
   196  
   197  	var ciphers []string
   198  	for _, c := range cfg.CipherSuites {
   199  		if text, ok := translate[c]; ok {
   200  			ciphers = append(ciphers, text)
   201  		}
   202  	}
   203  	return strings.Join(ciphers, ";")
   204  }
   205  
   206  func writeTempPEM(cfg *dtls.Config) (string, string, error) {
   207  	certOut, err := ioutil.TempFile("", "cert.pem")
   208  	if err != nil {
   209  		return "", "", fmt.Errorf("failed to create temporary file: %w", err)
   210  	}
   211  	keyOut, err := ioutil.TempFile("", "key.pem")
   212  	if err != nil {
   213  		return "", "", fmt.Errorf("failed to create temporary file: %w", err)
   214  	}
   215  
   216  	cert := cfg.Certificates[0]
   217  	derBytes := cert.Certificate[0]
   218  	if err = pem.Encode(certOut, &pem.Block{Type: "CERTIFICATE", Bytes: derBytes}); err != nil {
   219  		return "", "", fmt.Errorf("failed to write data to cert.pem: %w", err)
   220  	}
   221  	if err = certOut.Close(); err != nil {
   222  		return "", "", fmt.Errorf("error closing cert.pem: %w", err)
   223  	}
   224  
   225  	priv := cert.PrivateKey
   226  	var privBytes []byte
   227  	privBytes, err = x509.MarshalPKCS8PrivateKey(priv)
   228  	if err != nil {
   229  		return "", "", fmt.Errorf("unable to marshal private key: %w", err)
   230  	}
   231  	if err = pem.Encode(keyOut, &pem.Block{Type: "PRIVATE KEY", Bytes: privBytes}); err != nil {
   232  		return "", "", fmt.Errorf("failed to write data to key.pem: %w", err)
   233  	}
   234  	if err = keyOut.Close(); err != nil {
   235  		return "", "", fmt.Errorf("error closing key.pem: %w", err)
   236  	}
   237  	return certOut.Name(), keyOut.Name(), nil
   238  }
   239  
   240  func minimumOpenSSLVersion(t *testing.T) bool {
   241  	t.Helper()
   242  
   243  	cmd := exec.Command("openssl", "version")
   244  	allOut, err := cmd.CombinedOutput()
   245  	if err != nil {
   246  		t.Log("Cannot determine OpenSSL version: ", err)
   247  		return false
   248  	}
   249  	verMatch := regexp.MustCompile(`(?i)^OpenSSL\s(?P<version>(\d+\.)?(\d+\.)?(\*|\d+)(\w)?).+$`)
   250  	match := verMatch.FindStringSubmatch(strings.TrimSpace(string(allOut)))
   251  	params := map[string]string{}
   252  	for i, name := range verMatch.SubexpNames() {
   253  		if i > 0 && i <= len(match) {
   254  			params[name] = match[i]
   255  		}
   256  	}
   257  	var ver string
   258  	if val, ok := params["version"]; !ok {
   259  		t.Log("Could not extract OpenSSL version")
   260  		return false
   261  	} else {
   262  		ver = val
   263  	}
   264  
   265  	cmp := strings.Compare(ver, "3.0.0")
   266  	if cmp == -1 {
   267  		return false
   268  	}
   269  	return true
   270  }
   271  
   272  func TestPionOpenSSLE2ESimple(t *testing.T) {
   273  	t.Run("OpenSSLServer", func(t *testing.T) {
   274  		testPionE2ESimple(t, serverOpenSSL, clientPion)
   275  	})
   276  	t.Run("OpenSSLClient", func(t *testing.T) {
   277  		testPionE2ESimple(t, serverPion, clientOpenSSL)
   278  	})
   279  }
   280  
   281  func TestPionOpenSSLE2ESimplePSK(t *testing.T) {
   282  	t.Run("OpenSSLServer", func(t *testing.T) {
   283  		testPionE2ESimplePSK(t, serverOpenSSL, clientPion)
   284  	})
   285  	t.Run("OpenSSLClient", func(t *testing.T) {
   286  		testPionE2ESimplePSK(t, serverPion, clientOpenSSL)
   287  	})
   288  }
   289  
   290  func TestPionOpenSSLE2EMTUs(t *testing.T) {
   291  	t.Run("OpenSSLServer", func(t *testing.T) {
   292  		testPionE2EMTUs(t, serverOpenSSL, clientPion)
   293  	})
   294  	t.Run("OpenSSLClient", func(t *testing.T) {
   295  		testPionE2EMTUs(t, serverPion, clientOpenSSL)
   296  	})
   297  }
   298  
   299  func TestPionOpenSSLE2ESimpleED25519(t *testing.T) {
   300  	t.Run("OpenSSLServer", func(t *testing.T) {
   301  		if !minimumOpenSSLVersion(t) {
   302  			t.Skip("Cannot use OpenSSL < 3.0 as a DTLS server with ED25519 keys")
   303  		}
   304  		testPionE2ESimpleED25519(t, serverOpenSSL, clientPion)
   305  	})
   306  	t.Run("OpenSSLClient", func(t *testing.T) {
   307  		testPionE2ESimpleED25519(t, serverPion, clientOpenSSL)
   308  	})
   309  }
   310  
   311  func TestPionOpenSSLE2ESimpleED25519ClientCert(t *testing.T) {
   312  	t.Run("OpenSSLServer", func(t *testing.T) {
   313  		if !minimumOpenSSLVersion(t) {
   314  			t.Skip("Cannot use OpenSSL < 3.0 as a DTLS server with ED25519 keys")
   315  		}
   316  		testPionE2ESimpleED25519ClientCert(t, serverOpenSSL, clientPion)
   317  	})
   318  	t.Run("OpenSSLClient", func(t *testing.T) {
   319  		testPionE2ESimpleED25519ClientCert(t, serverPion, clientOpenSSL)
   320  	})
   321  }
   322  
   323  func TestPionOpenSSLE2ESimpleECDSAClientCert(t *testing.T) {
   324  	t.Run("OpenSSLServer", func(t *testing.T) {
   325  		testPionE2ESimpleECDSAClientCert(t, serverOpenSSL, clientPion)
   326  	})
   327  	t.Run("OpenSSLClient", func(t *testing.T) {
   328  		testPionE2ESimpleECDSAClientCert(t, serverPion, clientOpenSSL)
   329  	})
   330  }