e2e_openssl_test.go 8.4 KB

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