Skip to content

Commit

Permalink
Add end to end tests using OpenSSL
Browse files Browse the repository at this point in the history
End to end tests are now executed in the folowing modes:

 - `default`: pion client, pion server
 - `openssl_client`: openssl client, pion server
 - `openssl_server`: pion client, openssl server

OpenSSL is launched using `exec.Command`
  • Loading branch information
igolaizola committed Feb 17, 2020
1 parent 34ebfdb commit 10db9a6
Show file tree
Hide file tree
Showing 4 changed files with 435 additions and 158 deletions.
5 changes: 5 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,11 @@ test_job: &test_job
env: CACHE_NAME=test
before_install:
- go mod download
addons:
apt:
update: true
packages:
- openssl
install:
- go build ./...
script:
Expand Down
211 changes: 211 additions & 0 deletions e2e/e2e_openssl_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,211 @@
package e2e

import (
"crypto/x509"
"encoding/pem"
"errors"
"fmt"
"io/ioutil"
"net"
"os"
"os/exec"
"strings"
"time"

"github.com/pion/dtls/v2"
)

func (c *comm) serverOpenSSL() {
go func() {
c.serverMutex.Lock()
defer c.serverMutex.Unlock()

cfg := c.serverConfig

// create openssl arguments
args := []string{"s_server",
"-dtls1_2",
"-quiet",
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
}

c.serverReady <- struct{}{}
simpleReadWrite(c.errChan, c.serverChan, c.serverConn, c.messageRecvCount)
}()
}

func (c *comm) clientOpenSSL() {
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",
fmt.Sprintf("-connect=localhost:%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
}
fmt.Println(certPEM, keyPEM)
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
}
Loading

0 comments on commit 10db9a6

Please sign in to comment.