Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add end to end tests using OpenSSL #193

Merged
merged 1 commit into from
Feb 24, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions .github/workflows/e2e.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
name: E2E
on:
pull_request:
branches:
- master
push:
branches:
- master

jobs:
e2e-test:
name: Test
runs-on: ubuntu-latest
steps:
- name: checkout
uses: actions/checkout@v2
- name: test
run: |
docker build -t pion-dtls-e2e -f e2e/Dockerfile .
docker run -i --rm pion-dtls-e2e
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ Check out the **[contributing wiki](https://github.com/pion/webrtc/wiki/Contribu
* [Sean DuBois](https://github.com/Sean-Der) - *Original Author*
* [Michiel De Backker](https://github.com/backkem) - *Public API*
* [Chris Hiszpanski](https://github.com/thinkski) - *Support Signature Algorithms Extension*
* [Iñigo Garcia Olaizola](https://github.com/igolaizola) - *Serialization & resumption, cert verification*
* [Iñigo Garcia Olaizola](https://github.com/igolaizola) - *Serialization & resumption, cert verification, E2E*
* [Daniele Sluijters](https://github.com/daenney) - *AES-CCM support*
* [Jin Lei](https://github.com/jinleileiking) - *Logging*
* [Hugo Arregui](https://github.com/hugoArregui)
Expand Down
11 changes: 11 additions & 0 deletions e2e/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
FROM golang:1.13-alpine3.11

RUN apk add --no-cache \
openssl

ENV CGO_ENABLED=0

COPY . /go/src/github.com/pion/dtls
WORKDIR /go/src/github.com/pion/dtls/e2e

CMD ["go", "test", "-tags=openssl", "-v", "."]
246 changes: 246 additions & 0 deletions e2e/e2e_openssl_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,246 @@
// +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",
igolaizola marked this conversation as resolved.
Show resolved Hide resolved
"-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)
})
}
17 changes: 17 additions & 0 deletions e2e/e2e_openssl_v113_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// +build openssl,go1.13,!js

package e2e

import (
"testing"
)

func TestPionOpenSSLE2ESimpleED25519(t *testing.T) {
t.Skip("TODO: make ED25519 test work with openssl")
t.Run("OpenSSLServer", func(t *testing.T) {
testPionE2ESimpleED25519(t, serverOpenSSL, clientPion)
})
t.Run("OpenSSLClient", func(t *testing.T) {
testPionE2ESimpleED25519(t, serverPion, clientOpenSSL)
})
}
Loading