e2e_openssl_test.go 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250
  1. // +build openssl,!js
  2. package e2e
  3. import (
  4. "crypto/x509"
  5. "encoding/pem"
  6. "errors"
  7. "fmt"
  8. "io/ioutil"
  9. "net"
  10. "os"
  11. "os/exec"
  12. "strings"
  13. "testing"
  14. "time"
  15. "github.com/pion/dtls/v2"
  16. )
  17. func serverOpenSSL(c *comm) {
  18. go func() {
  19. c.serverMutex.Lock()
  20. defer c.serverMutex.Unlock()
  21. cfg := c.serverConfig
  22. // create openssl arguments
  23. args := []string{
  24. "s_server",
  25. "-dtls1_2",
  26. "-quiet",
  27. "-verify_quiet",
  28. "-verify_return_error",
  29. fmt.Sprintf("-accept=%d", c.serverPort),
  30. }
  31. ciphers := ciphersOpenSSL(cfg)
  32. if ciphers != "" {
  33. args = append(args, fmt.Sprintf("-cipher=%s", ciphers))
  34. }
  35. // psk arguments
  36. if cfg.PSK != nil {
  37. psk, err := cfg.PSK(nil)
  38. if err != nil {
  39. c.errChan <- err
  40. return
  41. }
  42. args = append(args, fmt.Sprintf("-psk=%X", psk))
  43. if len(cfg.PSKIdentityHint) > 0 {
  44. args = append(args, fmt.Sprintf("-psk_hint=%s", cfg.PSKIdentityHint))
  45. }
  46. }
  47. // certs arguments
  48. if len(cfg.Certificates) > 0 {
  49. // create temporary cert files
  50. certPEM, keyPEM, err := writeTempPEM(cfg)
  51. if err != nil {
  52. c.errChan <- err
  53. return
  54. }
  55. args = append(args,
  56. fmt.Sprintf("-cert=%s", certPEM),
  57. fmt.Sprintf("-key=%s", keyPEM))
  58. defer func() {
  59. _ = os.Remove(certPEM)
  60. _ = os.Remove(keyPEM)
  61. }()
  62. } else {
  63. args = append(args, "-nocert")
  64. }
  65. // launch command
  66. // #nosec G204
  67. cmd := exec.CommandContext(c.ctx, "openssl", args...)
  68. var inner net.Conn
  69. inner, c.serverConn = net.Pipe()
  70. cmd.Stdin = inner
  71. cmd.Stdout = inner
  72. cmd.Stderr = os.Stderr
  73. if err := cmd.Start(); err != nil {
  74. c.errChan <- err
  75. _ = inner.Close()
  76. return
  77. }
  78. // Ensure that server has started
  79. time.Sleep(500 * time.Millisecond)
  80. c.serverReady <- struct{}{}
  81. simpleReadWrite(c.errChan, c.serverChan, c.serverConn, c.messageRecvCount)
  82. }()
  83. }
  84. func clientOpenSSL(c *comm) {
  85. select {
  86. case <-c.serverReady:
  87. // OK
  88. case <-time.After(time.Second):
  89. c.errChan <- errors.New("waiting on serverReady err: timeout")
  90. }
  91. c.clientMutex.Lock()
  92. defer c.clientMutex.Unlock()
  93. cfg := c.clientConfig
  94. // create openssl arguments
  95. args := []string{
  96. "s_client",
  97. "-dtls1_2",
  98. "-quiet",
  99. "-verify_quiet",
  100. "-verify_return_error",
  101. "-servername=localhost",
  102. fmt.Sprintf("-connect=127.0.0.1:%d", c.serverPort),
  103. }
  104. ciphers := ciphersOpenSSL(cfg)
  105. if ciphers != "" {
  106. args = append(args, fmt.Sprintf("-cipher=%s", ciphers))
  107. }
  108. // psk arguments
  109. if cfg.PSK != nil {
  110. psk, err := cfg.PSK(nil)
  111. if err != nil {
  112. c.errChan <- err
  113. return
  114. }
  115. args = append(args, fmt.Sprintf("-psk=%X", psk))
  116. }
  117. // certificate arguments
  118. if len(cfg.Certificates) > 0 {
  119. // create temporary cert files
  120. certPEM, keyPEM, err := writeTempPEM(cfg)
  121. if err != nil {
  122. c.errChan <- err
  123. return
  124. }
  125. args = append(args, fmt.Sprintf("-CAfile=%s", certPEM))
  126. defer func() {
  127. _ = os.Remove(certPEM)
  128. _ = os.Remove(keyPEM)
  129. }()
  130. }
  131. // launch command
  132. // #nosec G204
  133. cmd := exec.CommandContext(c.ctx, "openssl", args...)
  134. var inner net.Conn
  135. inner, c.clientConn = net.Pipe()
  136. cmd.Stdin = inner
  137. cmd.Stdout = inner
  138. cmd.Stderr = os.Stderr
  139. if err := cmd.Start(); err != nil {
  140. c.errChan <- err
  141. _ = inner.Close()
  142. return
  143. }
  144. simpleReadWrite(c.errChan, c.clientChan, c.clientConn, c.messageRecvCount)
  145. }
  146. func ciphersOpenSSL(cfg *dtls.Config) string {
  147. // See https://tls.mbed.org/supported-ssl-ciphersuites
  148. translate := map[dtls.CipherSuiteID]string{
  149. dtls.TLS_ECDHE_ECDSA_WITH_AES_128_CCM: "ECDHE-ECDSA-AES128-CCM",
  150. dtls.TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8: "ECDHE-ECDSA-AES128-CCM8",
  151. dtls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256: "ECDHE-ECDSA-AES128-GCM-SHA256",
  152. dtls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256: "ECDHE-RSA-AES128-GCM-SHA256",
  153. dtls.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA: "ECDHE-ECDSA-AES256-SHA",
  154. dtls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA: "ECDHE-RSA-AES128-SHA",
  155. dtls.TLS_PSK_WITH_AES_128_CCM: "PSK-AES128-CCM",
  156. dtls.TLS_PSK_WITH_AES_128_CCM_8: "PSK-AES128-CCM8",
  157. dtls.TLS_PSK_WITH_AES_128_GCM_SHA256: "PSK-AES128-GCM-SHA256",
  158. }
  159. var ciphers []string
  160. for _, c := range cfg.CipherSuites {
  161. if text, ok := translate[c]; ok {
  162. ciphers = append(ciphers, text)
  163. }
  164. }
  165. return strings.Join(ciphers, ";")
  166. }
  167. func writeTempPEM(cfg *dtls.Config) (string, string, error) {
  168. certOut, err := ioutil.TempFile("", "cert.pem")
  169. if err != nil {
  170. return "", "", fmt.Errorf("failed to create temporary file: %w", err)
  171. }
  172. keyOut, err := ioutil.TempFile("", "key.pem")
  173. if err != nil {
  174. return "", "", fmt.Errorf("failed to create temporary file: %w", err)
  175. }
  176. cert := cfg.Certificates[0]
  177. derBytes := cert.Certificate[0]
  178. if err = pem.Encode(certOut, &pem.Block{Type: "CERTIFICATE", Bytes: derBytes}); err != nil {
  179. return "", "", fmt.Errorf("failed to write data to cert.pem: %w", err)
  180. }
  181. if err = certOut.Close(); err != nil {
  182. return "", "", fmt.Errorf("error closing cert.pem: %w", err)
  183. }
  184. priv := cert.PrivateKey
  185. var privBytes []byte
  186. privBytes, err = x509.MarshalPKCS8PrivateKey(priv)
  187. if err != nil {
  188. return "", "", fmt.Errorf("unable to marshal private key: %w", err)
  189. }
  190. if err = pem.Encode(keyOut, &pem.Block{Type: "PRIVATE KEY", Bytes: privBytes}); err != nil {
  191. return "", "", fmt.Errorf("failed to write data to key.pem: %w", err)
  192. }
  193. if err = keyOut.Close(); err != nil {
  194. return "", "", fmt.Errorf("error closing key.pem: %w", err)
  195. }
  196. return certOut.Name(), keyOut.Name(), nil
  197. }
  198. func TestPionOpenSSLE2ESimple(t *testing.T) {
  199. t.Run("OpenSSLServer", func(t *testing.T) {
  200. testPionE2ESimple(t, serverOpenSSL, clientPion)
  201. })
  202. t.Run("OpenSSLClient", func(t *testing.T) {
  203. testPionE2ESimple(t, serverPion, clientOpenSSL)
  204. })
  205. }
  206. func TestPionOpenSSLE2ESimplePSK(t *testing.T) {
  207. t.Run("OpenSSLServer", func(t *testing.T) {
  208. testPionE2ESimplePSK(t, serverOpenSSL, clientPion)
  209. })
  210. t.Run("OpenSSLClient", func(t *testing.T) {
  211. testPionE2ESimplePSK(t, serverPion, clientOpenSSL)
  212. })
  213. }
  214. func TestPionOpenSSLE2EMTUs(t *testing.T) {
  215. t.Run("OpenSSLServer", func(t *testing.T) {
  216. testPionE2EMTUs(t, serverOpenSSL, clientPion)
  217. })
  218. t.Run("OpenSSLClient", func(t *testing.T) {
  219. testPionE2EMTUs(t, serverPion, clientOpenSSL)
  220. })
  221. }