dockerfiles/anylink/dtls-2.0.9/e2e/e2e_openssl_test.go

251 lines
6.0 KiB
Go

// +build openssl,!js
package e2e
import (
"crypto/x509"
"encoding/pem"
"errors"
"fmt"
"io/ioutil"
"net"
"os"
"os/exec"
"strings"
"testing"
"time"
"github.com/pion/dtls/v2"
)
func serverOpenSSL(c *comm) {
go func() {
c.serverMutex.Lock()
defer c.serverMutex.Unlock()
cfg := c.serverConfig
// create openssl arguments
args := []string{
"s_server",
"-dtls1_2",
"-quiet",
"-verify_quiet",
"-verify_return_error",
fmt.Sprintf("-accept=%d", c.serverPort),
}
ciphers := ciphersOpenSSL(cfg)
if ciphers != "" {
args = append(args, fmt.Sprintf("-cipher=%s", ciphers))
}
// psk arguments
if cfg.PSK != nil {
psk, err := cfg.PSK(nil)
if err != nil {
c.errChan <- err
return
}
args = append(args, fmt.Sprintf("-psk=%X", psk))
if len(cfg.PSKIdentityHint) > 0 {
args = append(args, fmt.Sprintf("-psk_hint=%s", cfg.PSKIdentityHint))
}
}
// certs arguments
if len(cfg.Certificates) > 0 {
// create temporary cert files
certPEM, keyPEM, err := writeTempPEM(cfg)
if err != nil {
c.errChan <- err
return
}
args = append(args,
fmt.Sprintf("-cert=%s", certPEM),
fmt.Sprintf("-key=%s", keyPEM))
defer func() {
_ = os.Remove(certPEM)
_ = os.Remove(keyPEM)
}()
} else {
args = append(args, "-nocert")
}
// launch command
// #nosec G204
cmd := exec.CommandContext(c.ctx, "openssl", args...)
var inner net.Conn
inner, c.serverConn = net.Pipe()
cmd.Stdin = inner
cmd.Stdout = inner
cmd.Stderr = os.Stderr
if err := cmd.Start(); err != nil {
c.errChan <- err
_ = inner.Close()
return
}
// Ensure that server has started
time.Sleep(500 * time.Millisecond)
c.serverReady <- struct{}{}
simpleReadWrite(c.errChan, c.serverChan, c.serverConn, c.messageRecvCount)
}()
}
func clientOpenSSL(c *comm) {
select {
case <-c.serverReady:
// OK
case <-time.After(time.Second):
c.errChan <- errors.New("waiting on serverReady err: timeout")
}
c.clientMutex.Lock()
defer c.clientMutex.Unlock()
cfg := c.clientConfig
// create openssl arguments
args := []string{
"s_client",
"-dtls1_2",
"-quiet",
"-verify_quiet",
"-verify_return_error",
"-servername=localhost",
fmt.Sprintf("-connect=127.0.0.1:%d", c.serverPort),
}
ciphers := ciphersOpenSSL(cfg)
if ciphers != "" {
args = append(args, fmt.Sprintf("-cipher=%s", ciphers))
}
// psk arguments
if cfg.PSK != nil {
psk, err := cfg.PSK(nil)
if err != nil {
c.errChan <- err
return
}
args = append(args, fmt.Sprintf("-psk=%X", psk))
}
// certificate arguments
if len(cfg.Certificates) > 0 {
// create temporary cert files
certPEM, keyPEM, err := writeTempPEM(cfg)
if err != nil {
c.errChan <- err
return
}
args = append(args, fmt.Sprintf("-CAfile=%s", certPEM))
defer func() {
_ = os.Remove(certPEM)
_ = os.Remove(keyPEM)
}()
}
// launch command
// #nosec G204
cmd := exec.CommandContext(c.ctx, "openssl", args...)
var inner net.Conn
inner, c.clientConn = net.Pipe()
cmd.Stdin = inner
cmd.Stdout = inner
cmd.Stderr = os.Stderr
if err := cmd.Start(); err != nil {
c.errChan <- err
_ = inner.Close()
return
}
simpleReadWrite(c.errChan, c.clientChan, c.clientConn, c.messageRecvCount)
}
func ciphersOpenSSL(cfg *dtls.Config) string {
// See https://tls.mbed.org/supported-ssl-ciphersuites
translate := map[dtls.CipherSuiteID]string{
dtls.TLS_ECDHE_ECDSA_WITH_AES_128_CCM: "ECDHE-ECDSA-AES128-CCM",
dtls.TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8: "ECDHE-ECDSA-AES128-CCM8",
dtls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256: "ECDHE-ECDSA-AES128-GCM-SHA256",
dtls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256: "ECDHE-RSA-AES128-GCM-SHA256",
dtls.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA: "ECDHE-ECDSA-AES256-SHA",
dtls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA: "ECDHE-RSA-AES128-SHA",
dtls.TLS_PSK_WITH_AES_128_CCM: "PSK-AES128-CCM",
dtls.TLS_PSK_WITH_AES_128_CCM_8: "PSK-AES128-CCM8",
dtls.TLS_PSK_WITH_AES_128_GCM_SHA256: "PSK-AES128-GCM-SHA256",
}
var ciphers []string
for _, c := range cfg.CipherSuites {
if text, ok := translate[c]; ok {
ciphers = append(ciphers, text)
}
}
return strings.Join(ciphers, ";")
}
func writeTempPEM(cfg *dtls.Config) (string, string, error) {
certOut, err := ioutil.TempFile("", "cert.pem")
if err != nil {
return "", "", fmt.Errorf("failed to create temporary file: %w", err)
}
keyOut, err := ioutil.TempFile("", "key.pem")
if err != nil {
return "", "", fmt.Errorf("failed to create temporary file: %w", err)
}
cert := cfg.Certificates[0]
derBytes := cert.Certificate[0]
if err = pem.Encode(certOut, &pem.Block{Type: "CERTIFICATE", Bytes: derBytes}); err != nil {
return "", "", fmt.Errorf("failed to write data to cert.pem: %w", err)
}
if err = certOut.Close(); err != nil {
return "", "", fmt.Errorf("error closing cert.pem: %w", err)
}
priv := cert.PrivateKey
var privBytes []byte
privBytes, err = x509.MarshalPKCS8PrivateKey(priv)
if err != nil {
return "", "", fmt.Errorf("unable to marshal private key: %w", err)
}
if err = pem.Encode(keyOut, &pem.Block{Type: "PRIVATE KEY", Bytes: privBytes}); err != nil {
return "", "", fmt.Errorf("failed to write data to key.pem: %w", err)
}
if err = keyOut.Close(); err != nil {
return "", "", fmt.Errorf("error closing key.pem: %w", err)
}
return certOut.Name(), keyOut.Name(), nil
}
func TestPionOpenSSLE2ESimple(t *testing.T) {
t.Run("OpenSSLServer", func(t *testing.T) {
testPionE2ESimple(t, serverOpenSSL, clientPion)
})
t.Run("OpenSSLClient", func(t *testing.T) {
testPionE2ESimple(t, serverPion, clientOpenSSL)
})
}
func TestPionOpenSSLE2ESimplePSK(t *testing.T) {
t.Run("OpenSSLServer", func(t *testing.T) {
testPionE2ESimplePSK(t, serverOpenSSL, clientPion)
})
t.Run("OpenSSLClient", func(t *testing.T) {
testPionE2ESimplePSK(t, serverPion, clientOpenSSL)
})
}
func TestPionOpenSSLE2EMTUs(t *testing.T) {
t.Run("OpenSSLServer", func(t *testing.T) {
testPionE2EMTUs(t, serverOpenSSL, clientPion)
})
t.Run("OpenSSLClient", func(t *testing.T) {
testPionE2EMTUs(t, serverPion, clientOpenSSL)
})
}