From a9144c97668b824083b6e22c7cd0cb10157a10c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=90=95=E6=B5=B7=E6=B6=9B?= Date: Sat, 8 May 2021 17:24:53 +0800 Subject: [PATCH 1/6] tmp --- server/conf/server.toml | 4 ++-- server/go.mod | 7 +------ server/go.sum | 7 ------- server/handler/link_base.go | 2 +- server/handler/link_tun.go | 2 +- server/handler/link_tunnel.go | 8 +++++--- server/main.go | 2 -- 7 files changed, 10 insertions(+), 22 deletions(-) diff --git a/server/conf/server.toml b/server/conf/server.toml index 4560290..0e33646 100644 --- a/server/conf/server.toml +++ b/server/conf/server.toml @@ -13,7 +13,7 @@ files_path = "../files" #日志目录,为空写入标准输出 #log_path = "../log" log_path = "" -log_level = "info" +log_level = "debug" #系统名称 issuer = "XX公司VPN" @@ -25,7 +25,7 @@ jwt_secret = "iLmspvOiz*%ovfcs*wersdf#heR8pNU4XxBm&mW$aPCjSRMbYH#&" #前台服务监听地址 -server_addr = ":443" +server_addr = ":4430" #后台服务监听地址 admin_addr = ":8800" #开启tcp proxy protocol协议 diff --git a/server/go.mod b/server/go.mod index d83124b..9496de1 100644 --- a/server/go.mod +++ b/server/go.mod @@ -4,10 +4,7 @@ go 1.15 require ( github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d // indirect - github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6 // indirect github.com/asdine/storm/v3 v3.2.1 - github.com/coreos/go-etcd v2.0.0+incompatible // indirect - github.com/cpuguy83/go-md2man v1.0.10 // indirect github.com/dgrijalva/jwt-go v3.2.0+incompatible github.com/fsnotify/fsnotify v1.4.9 // indirect github.com/go-ole/go-ole v1.2.5 // indirect @@ -15,7 +12,7 @@ require ( github.com/gorilla/mux v1.8.0 github.com/magiconair/properties v1.8.4 // indirect github.com/mitchellh/mapstructure v1.4.1 // indirect - github.com/pelletier/go-toml v1.8.1 + github.com/pelletier/go-toml v1.8.1 // indirect github.com/shirou/gopsutil v3.21.1+incompatible github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e github.com/songgao/packets v0.0.0-20160404182456-549a10cd4091 @@ -26,10 +23,8 @@ require ( github.com/spf13/jwalterweatherman v1.1.0 // indirect github.com/spf13/viper v1.7.1 github.com/stretchr/testify v1.7.0 - github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8 // indirect github.com/xhit/go-simple-mail/v2 v2.8.0 github.com/xlzd/gotp v0.0.0-20181030022105-c8557ba2c119 - github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77 // indirect go.etcd.io/bbolt v1.3.5 golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83 golang.org/x/net v0.0.0-20210220033124-5f55cee0dc0d diff --git a/server/go.sum b/server/go.sum index 77667fe..a438704 100644 --- a/server/go.sum +++ b/server/go.sum @@ -23,7 +23,6 @@ github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d/go.mod h1:3eOhrU github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= -github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/asdine/storm/v3 v3.2.1 h1:I5AqhkPK6nBZ/qJXySdI7ot5BlXSZ7qvDY1zAn5ZJac= @@ -36,11 +35,9 @@ github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghf github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= -github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= -github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= @@ -174,7 +171,6 @@ github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7z github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= -github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= @@ -221,7 +217,6 @@ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s= github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= -github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= github.com/vmihailenco/msgpack v4.0.4+incompatible h1:dSLoQfGFAo3F6OoNhwUmLwVgaUXK79GlxNBwueZn0xI= github.com/vmihailenco/msgpack v4.0.4+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk= github.com/xhit/go-simple-mail/v2 v2.8.0 h1:w6ZDXvRk0EO+r78LRlQl14ngP2tiRDRRHhr9UaVJ0p4= @@ -229,7 +224,6 @@ github.com/xhit/go-simple-mail/v2 v2.8.0/go.mod h1:kA1XbQfCI4JxQ9ccSN6VFyIEkkugO github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= github.com/xlzd/gotp v0.0.0-20181030022105-c8557ba2c119 h1:YyPWX3jLOtYKulBR6AScGIs74lLrJcgeKRwcbAuQOG4= github.com/xlzd/gotp v0.0.0-20181030022105-c8557ba2c119/go.mod h1:/nuTSlK+okRfR/vnIPqR89fFKonnWPiZymN5ydRJkX8= -github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.etcd.io/bbolt v1.3.4/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ= go.etcd.io/bbolt v1.3.5 h1:XAzx9gjCb0Rxj7EoqcClPD1d5ZBxZJk0jbuoPHenBt0= @@ -310,7 +304,6 @@ golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201119102817-f84b799fce68 h1:nxC68pudNYkKU6jWhgrqdreuFiOQWj1Fs7T3VrH4Pjw= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4 h1:EZ2mChiOa8udjfp6rRmswTbtZN/QzUQp4ptM4rnjHvc= golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= diff --git a/server/handler/link_base.go b/server/handler/link_base.go index 8c3b1b9..d95ac28 100644 --- a/server/handler/link_base.go +++ b/server/handler/link_base.go @@ -55,7 +55,7 @@ func setCommonHeader(w http.ResponseWriter) { func execCmd(cmdStrs []string) error { for _, cmdStr := range cmdStrs { - cmd := exec.Command("bash", "-c", cmdStr) + cmd := exec.Command("sh", "-c", cmdStr) b, err := cmd.CombinedOutput() if err != nil { log.Println(string(b), err) diff --git a/server/handler/link_tun.go b/server/handler/link_tun.go index 13b7ee2..3057935 100644 --- a/server/handler/link_tun.go +++ b/server/handler/link_tun.go @@ -46,7 +46,7 @@ func LinkTun(cSess *sessdata.ConnSession) error { cmdstr1 := fmt.Sprintf("ip link set dev %s up mtu %d multicast off", ifce.Name(), cSess.Mtu) cmdstr2 := fmt.Sprintf("ip addr add dev %s local %s peer %s/32", ifce.Name(), base.Cfg.Ipv4Gateway, cSess.IpAddr) - cmdstr3 := fmt.Sprintf("sysctl -w net.ipv6.conf.%s.disable_ipv6=1", ifce.Name()) + cmdstr3 := "true" cmdStrs := []string{cmdstr1, cmdstr2, cmdstr3} err = execCmd(cmdStrs) if err != nil { diff --git a/server/handler/link_tunnel.go b/server/handler/link_tunnel.go index c0c9bf7..1f712b5 100644 --- a/server/handler/link_tunnel.go +++ b/server/handler/link_tunnel.go @@ -6,6 +6,7 @@ import ( "log" "net" "net/http" + "net/http/httputil" "os" "github.com/bjdgyc/anylink/base" @@ -21,9 +22,9 @@ func init() { func LinkTunnel(w http.ResponseWriter, r *http.Request) { // TODO 调试信息输出 - // hd, _ := httputil.DumpRequest(r, true) - // fmt.Println("DumpRequest: ", string(hd)) - // fmt.Println("LinkTunnel", r.RemoteAddr) + hd, _ := httputil.DumpRequest(r, true) + fmt.Println("DumpRequest: ", string(hd)) + fmt.Println("LinkTunnel", r.RemoteAddr) // 判断session-token的值 cookie, err := r.Cookie("webvpn") @@ -112,6 +113,7 @@ func LinkTunnel(w http.ResponseWriter, r *http.Request) { w.Header().Set("X-CSTP-MSIE-Proxy-Lockdown", "true") w.Header().Set("X-CSTP-Smartcard-Removal-Disconnect", "true") + w.Header().Set("X-MTU", fmt.Sprintf("%d", cSess.Mtu)) // 1399 w.Header().Set("X-CSTP-MTU", fmt.Sprintf("%d", cSess.Mtu)) // 1399 w.Header().Set("X-DTLS-MTU", fmt.Sprintf("%d", cSess.Mtu)) diff --git a/server/main.go b/server/main.go index 3deb10a..6c5de73 100644 --- a/server/main.go +++ b/server/main.go @@ -1,7 +1,5 @@ // AnyLink 是一个企业级远程办公vpn软件,可以支持多人同时在线使用。 -// +build linux - package main import ( From 3368eced2a3afa48e6bdffbeca9e899548ed4892 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=90=95=E6=B5=B7=E6=B6=9B?= Date: Wed, 19 May 2021 15:11:52 +0800 Subject: [PATCH 2/6] =?UTF-8?q?=E5=88=9D=E6=AD=A5=E6=94=AF=E6=8C=81=20DTLS?= =?UTF-8?q?=20=E9=80=9A=E9=81=93?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- server/go.mod | 10 ++-- server/go.sum | 33 +++++++---- server/handler/dtls.go | 57 ++++++++++++++++++- server/handler/link_dtls.go | 103 ++++++++++++++++++++++++++++++++++ server/handler/link_tunnel.go | 16 ++++++ 5 files changed, 203 insertions(+), 16 deletions(-) create mode 100644 server/handler/link_dtls.go diff --git a/server/go.mod b/server/go.mod index 9496de1..37f395f 100644 --- a/server/go.mod +++ b/server/go.mod @@ -13,6 +13,8 @@ require ( github.com/magiconair/properties v1.8.4 // indirect github.com/mitchellh/mapstructure v1.4.1 // indirect github.com/pelletier/go-toml v1.8.1 // indirect + github.com/pion/dtls/v2 v2.0.9 + github.com/pion/logging v0.2.2 github.com/shirou/gopsutil v3.21.1+incompatible github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e github.com/songgao/packets v0.0.0-20160404182456-549a10cd4091 @@ -26,10 +28,10 @@ require ( github.com/xhit/go-simple-mail/v2 v2.8.0 github.com/xlzd/gotp v0.0.0-20181030022105-c8557ba2c119 go.etcd.io/bbolt v1.3.5 - golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83 - golang.org/x/net v0.0.0-20210220033124-5f55cee0dc0d - golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4 // indirect - golang.org/x/text v0.3.5 // indirect + golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b + golang.org/x/net v0.0.0-20210502030024-e5908800b52b golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba gopkg.in/ini.v1 v1.62.0 // indirect ) + +replace github.com/pion/dtls/v2 => ../../dtls diff --git a/server/go.sum b/server/go.sum index a438704..8584a6e 100644 --- a/server/go.sum +++ b/server/go.sum @@ -154,6 +154,13 @@ github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FI github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= github.com/pelletier/go-toml v1.8.1 h1:1Nf83orprkJyknT6h7zbuEGUEjcyVlCxSUGTENmNCRM= github.com/pelletier/go-toml v1.8.1/go.mod h1:T2/BmBdy8dvIRq1a/8aqjN41wvWlN4lrapLU/GW4pbc= +github.com/pion/logging v0.2.2 h1:M9+AIj/+pxNsDfAT64+MAVgJO0rsyLnoJKCqf//DoeY= +github.com/pion/logging v0.2.2/go.mod h1:k0/tDVsRCX2Mb2ZEmTqNa7CWsQPc+YYCB7Q+5pahoms= +github.com/pion/transport v0.12.2/go.mod h1:N3+vZQD9HlDP5GWkZ85LohxNsDcNgofQmyL6ojX5d8Q= +github.com/pion/transport v0.12.3 h1:vdBfvfU/0Wq8kd2yhUMSDB/x+O4Z9MYVl2fJ5BT4JZw= +github.com/pion/transport v0.12.3/go.mod h1:OViWW9SP2peE/HbwBvARicmAVnesphkNkCVZIWJ6q9A= +github.com/pion/udp v0.1.1 h1:8UAPvyqmsxK8oOjloDk4wUt63TzFe9WEJkg5lChlj7o= +github.com/pion/udp v0.1.1/go.mod h1:6AFo+CMdKQm7UiA0eUPA8/eVCTx8jBIITLZHc9DWX5M= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI= @@ -212,6 +219,7 @@ github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+ github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s= @@ -240,8 +248,9 @@ golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83 h1:/ZScEX8SfEmUGRHs0gxpqteO5nfNW6axyZbBdw9A12g= -golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b h1:7mWr3k41Qtv8XlltBkDkl8LoP3mpSgBW8BUoxtEdbXg= +golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -277,8 +286,11 @@ golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20191105084925-a882066a44e0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20210220033124-5f55cee0dc0d h1:1aflnvSoWWLI2k/dMUAl5lvU1YO4Mb4hz0gh+1rjcxU= -golang.org/x/net v0.0.0-20210220033124-5f55cee0dc0d/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20201201195509-5d6afe98e0b7/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210502030024-e5908800b52b h1:jCRjgm6WJHzM8VQrm/es2wXYqqbq0NZ1yXFHHgzkiVQ= +golang.org/x/net v0.0.0-20210502030024-e5908800b52b/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -302,19 +314,18 @@ golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4 h1:EZ2mChiOa8udjfp6rRmswTbtZN/QzUQp4ptM4rnjHvc= -golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da h1:b3NXsE2LusjYGGjL5bxEVZZORm/YEFFrWFjR8eFrw/c= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.5 h1:i6eZZ+zk0SOf0xgBpEpPD18qWcJda6q1sxt3S0kzyUQ= -golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba h1:O8mE0/t419eoIwhTFpKVkHiTs/Igowgfkj25AcZrtiE= @@ -339,6 +350,8 @@ golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtn golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= diff --git a/server/handler/dtls.go b/server/handler/dtls.go index 32c9470..9dbf075 100644 --- a/server/handler/dtls.go +++ b/server/handler/dtls.go @@ -1,6 +1,59 @@ package handler -// 暂时没有实现 -func startDtls() { +import ( + "crypto/tls" + "encoding/hex" + "log" + "net" + "time" + "os" + "github.com/bjdgyc/anylink/sessdata" + "github.com/pion/dtls/v2" + "github.com/pion/dtls/v2/pkg/crypto/selfsign" + "github.com/pion/logging" +) + +func startDtls() { + certificate, err := selfsign.GenerateSelfSigned() + + logf := logging.NewDefaultLoggerFactory() + logf.DefaultLogLevel = logging.LogLevelTrace + f, err := os.OpenFile("/tmp/key.log", os.O_TRUNC|os.O_RDWR, 0600) + if err != nil { + panic(err) + } + config := &dtls.Config{ + Certificates: []tls.Certificate{certificate}, + InsecureSkipVerify: true, + ExtendedMasterSecret: dtls.DisableExtendedMasterSecret, + CipherSuites: []dtls.CipherSuiteID{dtls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256}, + LoggerFactory: logf, + KeyLogWriter: f, + } + + addr := &net.UDPAddr{IP: net.ParseIP("0.0.0.0"), Port: 4433} + + ln, err := dtls.Listen("udp", addr, config) + if err != nil { + panic(err) + } + + for { + c, err := ln.Accept() + if err != nil { + log.Println("Accept error", err) + continue + } + + go func() { + time.Sleep(1 * time.Second) + cc := c.(*dtls.Conn) + id := hex.EncodeToString(cc.ConnectionState().SessionID) + s, ok := ss.Load(id) + log.Println("get link", id, ok) + cs := s.(*sessdata.ConnSession) + LinkDtls(c, cs) + }() + } } diff --git a/server/handler/link_dtls.go b/server/handler/link_dtls.go new file mode 100644 index 0000000..65c607b --- /dev/null +++ b/server/handler/link_dtls.go @@ -0,0 +1,103 @@ +package handler + +import ( + "net" + "time" + + "github.com/bjdgyc/anylink/base" + "github.com/bjdgyc/anylink/sessdata" +) + +func LinkDtls(conn net.Conn, cSess *sessdata.ConnSession) { + defer func() { + base.Debug("LinkDtls return", cSess.IpAddr) + _ = conn.Close() + cSess.Close() + }() + + var ( + dead = time.Duration(cSess.CstpDpd+5) * time.Second + ) + + go dtlsWrite(conn, cSess) + + for { + err := conn.SetReadDeadline(time.Now().Add(dead)) + if err != nil { + base.Error("SetDeadline: ", err) + return + } + hdata := make([]byte, BufferSize) + n, err := conn.Read(hdata) + if err != nil { + base.Error("read hdata: ", err) + return + } + + // 限流设置 + err = cSess.RateLimit(n, true) + if err != nil { + base.Error(err) + } + + switch hdata[0] { + case 0x07: // KEEPALIVE + // do nothing + base.Debug("recv keepalive", cSess.IpAddr) + case 0x05: // DISCONNECT + base.Debug("DISCONNECT", cSess.IpAddr) + return + case 0x03: // DPD-REQ + base.Debug("recv DPD-REQ", cSess.IpAddr) + if payloadOut(cSess, sessdata.LTypeIPData, 0x04, nil) { + return + } + case 0x04: + base.Debug("recv DPD-RESP", cSess.IpAddr) + case 0x00: // DATA + if payloadIn(cSess, sessdata.LTypeIPData, 0x00, hdata[1:]) { + return + } + + } + } +} + +func dtlsWrite(conn net.Conn, cSess *sessdata.ConnSession) { + defer func() { + base.Debug("dtlsWrite return", cSess.IpAddr) + _ = conn.Close() + cSess.Close() + }() + + var ( + header []byte + payload *sessdata.Payload + ) + + for { + select { + case payload = <-cSess.PayloadOut: + case <-cSess.CloseChan: + return + } + + if payload.LType != sessdata.LTypeIPData { + continue + } + + header = []byte{payload.PType} + header = append(header, payload.Data...) + n, err := conn.Write(header) + if err != nil { + base.Error("write err", err) + return + } + + // 限流设置 + err = cSess.RateLimit(n, false) + if err != nil { + base.Error(err) + } + } +} diff --git a/server/handler/link_tunnel.go b/server/handler/link_tunnel.go index 1f712b5..2b18cbe 100644 --- a/server/handler/link_tunnel.go +++ b/server/handler/link_tunnel.go @@ -2,19 +2,24 @@ package handler import ( "bytes" + "encoding/hex" "fmt" "log" "net" "net/http" "net/http/httputil" "os" + "sync" "github.com/bjdgyc/anylink/base" "github.com/bjdgyc/anylink/sessdata" + "github.com/pion/dtls/v2" ) var hn string +var ss sync.Map + func init() { // 获取主机名称 hn, _ = os.Hostname() @@ -53,6 +58,14 @@ func LinkTunnel(w http.ResponseWriter, r *http.Request) { localIp := r.Header.Get("X-Cstp-Local-Address-Ip4") mobile := r.Header.Get("X-Cstp-License") + preMasterSecret, err := hex.DecodeString(masterSecret) + if err != nil { + log.Println(err) + w.WriteHeader(http.StatusBadRequest) + return + } + dtls.Sessions.Store(sess.DtlsSid, preMasterSecret) + cSess.SetMtu(cstpMtu) cSess.MasterSecret = masterSecret cSess.RemoteAddr = r.RemoteAddr @@ -119,6 +132,7 @@ func LinkTunnel(w http.ResponseWriter, r *http.Request) { w.Header().Set("X-DTLS-Session-ID", sess.DtlsSid) w.Header().Set("X-DTLS-Port", "4433") + w.Header().Set("X-DTLS-DPD", fmt.Sprintf("%d", cstpDpd)) w.Header().Set("X-DTLS-Keepalive", fmt.Sprintf("%d", base.Cfg.CstpKeepalive)) w.Header().Set("X-DTLS-Rekey-Time", "5400") w.Header().Set("X-DTLS12-CipherSuite", "ECDHE-ECDSA-AES128-GCM-SHA256") @@ -159,5 +173,7 @@ func LinkTunnel(w http.ResponseWriter, r *http.Request) { return } + ss.Store(cSess.Sess.DtlsSid, cSess) + go LinkCstp(conn, cSess) } From c8f090c9e3cfc65a453581c4ff31c9e35a5e4168 Mon Sep 17 00:00:00 2001 From: bjdgyc Date: Fri, 21 May 2021 19:00:23 +0800 Subject: [PATCH 3/6] =?UTF-8?q?=E6=B7=BB=E5=8A=A0dtls=E6=94=AF=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- server/admin/api_base.go | 2 +- server/admin/server.go | 14 +++-- server/base/cfg.go | 30 +++++----- server/base/config.go | 16 ++--- server/base/log.go | 7 ++- server/conf/server.toml | 8 ++- server/dbdata/user.go | 2 +- server/go.mod | 37 ------------ server/go.sum | 59 +++++++++--------- server/handler/dtls.go | 57 ++++++++++++------ server/handler/link_cstp.go | 4 +- server/handler/link_dtls.go | 44 ++++++++++---- server/handler/link_tun.go | 2 +- server/handler/link_tunnel.go | 39 +++++------- server/handler/payload.go | 30 ++++++++-- server/main.go | 1 - server/sessdata/session.go | 110 ++++++++++++++++++++++++++++------ 17 files changed, 279 insertions(+), 183 deletions(-) delete mode 100644 server/go.mod diff --git a/server/admin/api_base.go b/server/admin/api_base.go index e3454b3..a39098f 100644 --- a/server/admin/api_base.go +++ b/server/admin/api_base.go @@ -56,7 +56,7 @@ func authMiddleware(next http.Handler) http.Handler { route := mux.CurrentRoute(r) name := route.GetName() // fmt.Println("bb", r.URL.Path, name) - if utils.InArrStr([]string{"login", "index", "static"}, name) { + if utils.InArrStr([]string{"login", "index", "static", "debug"}, name) { // 不进行鉴权 next.ServeHTTP(w, r) return diff --git a/server/admin/server.go b/server/admin/server.go index ddb2b52..e679a99 100644 --- a/server/admin/server.go +++ b/server/admin/server.go @@ -49,12 +49,14 @@ func StartAdmin() { r.HandleFunc("/group/del", GroupDel) // pprof - r.HandleFunc("/debug/pprof/cmdline", pprof.Cmdline) - r.HandleFunc("/debug/pprof/profile", pprof.Profile) - r.HandleFunc("/debug/pprof/symbol", pprof.Symbol) - r.HandleFunc("/debug/pprof/trace", pprof.Trace) - r.HandleFunc("/debug/pprof", location("/debug/pprof/")) - r.PathPrefix("/debug/pprof/").HandlerFunc(pprof.Index) + if base.Cfg.Pprof { + r.HandleFunc("/debug/pprof/cmdline", pprof.Cmdline).Name("debug") + r.HandleFunc("/debug/pprof/profile", pprof.Profile).Name("debug") + r.HandleFunc("/debug/pprof/symbol", pprof.Symbol).Name("debug") + r.HandleFunc("/debug/pprof/trace", pprof.Trace).Name("debug") + r.HandleFunc("/debug/pprof", location("/debug/pprof/")) + r.PathPrefix("/debug/pprof/").HandlerFunc(pprof.Index).Name("debug") + } base.Info("Listen admin", base.Cfg.AdminAddr) err := http.ListenAndServe(base.Cfg.AdminAddr, r) diff --git a/server/base/cfg.go b/server/base/cfg.go index 9f7ec6d..c30cd2e 100644 --- a/server/base/cfg.go +++ b/server/base/cfg.go @@ -31,20 +31,22 @@ var ( type ServerConfig struct { // LinkAddr string `json:"link_addr"` - ServerAddr string `json:"server_addr"` - AdminAddr string `json:"admin_addr"` - ProxyProtocol bool `json:"proxy_protocol"` - DbFile string `json:"db_file"` - CertFile string `json:"cert_file"` - CertKey string `json:"cert_key"` - UiPath string `json:"ui_path"` - FilesPath string `json:"files_path"` - LogPath string `json:"log_path"` - LogLevel string `json:"log_level"` - Issuer string `json:"issuer"` - AdminUser string `json:"admin_user"` - AdminPass string `json:"admin_pass"` - JwtSecret string `json:"jwt_secret"` + ServerAddr string `json:"server_addr"` + ServerDTLSAddr string `json:"server_dtls_addr"` + AdminAddr string `json:"admin_addr"` + ProxyProtocol bool `json:"proxy_protocol"` + DbFile string `json:"db_file"` + CertFile string `json:"cert_file"` + CertKey string `json:"cert_key"` + UiPath string `json:"ui_path"` + FilesPath string `json:"files_path"` + LogPath string `json:"log_path"` + LogLevel string `json:"log_level"` + Pprof bool `json:"pprof"` + Issuer string `json:"issuer"` + AdminUser string `json:"admin_user"` + AdminPass string `json:"admin_pass"` + JwtSecret string `json:"jwt_secret"` LinkMode string `json:"link_mode"` // tun tap Ipv4CIDR string `json:"ipv4_cidr"` // 192.168.1.0/24 diff --git a/server/base/config.go b/server/base/config.go index fd3ed8e..6e412ba 100644 --- a/server/base/config.go +++ b/server/base/config.go @@ -17,19 +17,21 @@ type config struct { var configs = []config{ {Typ: cfgStr, Name: "server_addr", Usage: "前台服务监听地址", ValStr: ":443"}, + {Typ: cfgStr, Name: "server_dtls_addr", Usage: "前台DTLS监听地址", ValStr: ":4433"}, {Typ: cfgStr, Name: "admin_addr", Usage: "后台服务监听地址", ValStr: ":8800"}, {Typ: cfgBool, Name: "proxy_protocol", Usage: "TCP代理协议", ValBool: false}, - {Typ: cfgStr, Name: "db_file", Usage: "数据库地址", ValStr: "./conf/data.db"}, - {Typ: cfgStr, Name: "cert_file", Usage: "证书文件", ValStr: "./conf/vpn_cert.pem"}, - {Typ: cfgStr, Name: "cert_key", Usage: "证书密钥", ValStr: "./conf/vpn_cert.key"}, + {Typ: cfgStr, Name: "db_file", Usage: "数据库地址", ValStr: "./data.db"}, + {Typ: cfgStr, Name: "cert_file", Usage: "证书文件", ValStr: "./vpn_cert.pem"}, + {Typ: cfgStr, Name: "cert_key", Usage: "证书密钥", ValStr: "./vpn_cert.key"}, {Typ: cfgStr, Name: "ui_path", Usage: "ui文件路径", ValStr: "./ui"}, - {Typ: cfgStr, Name: "files_path", Usage: "外部下载文件路径", ValStr: "./conf/files"}, + {Typ: cfgStr, Name: "files_path", Usage: "外部下载文件路径", ValStr: "./files"}, {Typ: cfgStr, Name: "log_path", Usage: "日志文件路径", ValStr: ""}, {Typ: cfgStr, Name: "log_level", Usage: "日志等级", ValStr: "info"}, + {Typ: cfgBool, Name: "pprof", Usage: "开启pprof", ValBool: false}, {Typ: cfgStr, Name: "issuer", Usage: "系统名称", ValStr: "XX公司VPN"}, {Typ: cfgStr, Name: "admin_user", Usage: "管理用户名", ValStr: "admin"}, - {Typ: cfgStr, Name: "admin_pass", Usage: "管理用户密码", ValStr: ""}, - {Typ: cfgStr, Name: "jwt_secret", Usage: "JWT密钥", ValStr: ""}, + {Typ: cfgStr, Name: "admin_pass", Usage: "管理用户密码", ValStr: "$2a$10$UQ7C.EoPifDeJh6d8.31TeSPQU7hM/NOM2nixmBucJpAuXDQNqNke"}, + {Typ: cfgStr, Name: "jwt_secret", Usage: "JWT密钥", ValStr: "iLmspvOiz*%ovfcs*wersdf#heR8pNU4XxBm&mW$aPCjSRMbYH#&"}, {Typ: cfgStr, Name: "link_mode", Usage: "虚拟网络类型", ValStr: "tun"}, {Typ: cfgStr, Name: "ipv4_cidr", Usage: "ip地址网段", ValStr: "192.168.10.0/24"}, {Typ: cfgStr, Name: "ipv4_gateway", Usage: "ipv4_gateway", ValStr: "192.168.10.1"}, @@ -48,4 +50,4 @@ var configs = []config{ // {Typ: cfgInt, Name: "auth_timeout", Usage: "auth_timeout", ValInt: 0}, } -var envs = map[string]string{"admin_addr": "LINK_ADMIN_ADDR", "admin_pass": "LINK_ADMIN_PASS", "admin_user": "LINK_ADMIN_USER", "cert_file": "LINK_CERT_FILE", "cert_key": "LINK_CERT_KEY", "cstp_dpd": "LINK_CSTP_DPD", "cstp_keepalive": "LINK_CSTP_KEEPALIVE", "db_file": "LINK_DB_FILE", "default_group": "LINK_DEFAULT_GROUP", "files_path": "LINK_FILES_PATH", "ip_lease": "LINK_IP_LEASE", "ipv4_cidr": "LINK_IPV4_CIDR", "ipv4_end": "LINK_IPV4_END", "ipv4_gateway": "LINK_IPV4_GATEWAY", "ipv4_start": "LINK_IPV4_START", "issuer": "LINK_ISSUER", "jwt_secret": "LINK_JWT_SECRET", "link_addr": "LINK_LINK_ADDR", "link_mode": "LINK_LINK_MODE", "log_level": "LINK_LOG_LEVEL", "log_path": "LINK_LOG_PATH", "max_client": "LINK_MAX_CLIENT", "max_user_client": "LINK_MAX_USER_CLIENT", "mobile_dpd": "LINK_MOBILE_DPD", "mobile_keepalive": "LINK_MOBILE_KEEPALIVE", "proxy_protocol": "LINK_PROXY_PROTOCOL", "server_addr": "LINK_SERVER_ADDR", "session_timeout": "LINK_SESSION_TIMEOUT", "ui_path": "LINK_UI_PATH"} +var envs = map[string]string{} diff --git a/server/base/log.go b/server/base/log.go index 86034fc..36bb927 100644 --- a/server/base/log.go +++ b/server/base/log.go @@ -18,6 +18,7 @@ const ( ) var ( + baseLw *logWriter baseLog *log.Logger baseLevel int levels map[int]string @@ -66,7 +67,7 @@ func (lw *logWriter) newFile() { func initLog() { // 初始化 baseLog - baseLw := &logWriter{ + baseLw = &logWriter{ UseStdout: Cfg.LogPath == "", FileName: path.Join(Cfg.LogPath, logName), NowDate: time.Now().Format(dateFormat), @@ -77,6 +78,10 @@ func initLog() { baseLog = log.New(baseLw, "", log.LstdFlags|log.Lshortfile) } +func GetBaseLw() *logWriter { + return baseLw +} + // 获取 log.Logger func GetBaseLog() *log.Logger { return baseLog diff --git a/server/conf/server.toml b/server/conf/server.toml index 0e33646..92e166c 100644 --- a/server/conf/server.toml +++ b/server/conf/server.toml @@ -14,6 +14,7 @@ files_path = "../files" #log_path = "../log" log_path = "" log_level = "debug" +pprof = false #系统名称 issuer = "XX公司VPN" @@ -25,7 +26,8 @@ jwt_secret = "iLmspvOiz*%ovfcs*wersdf#heR8pNU4XxBm&mW$aPCjSRMbYH#&" #前台服务监听地址 -server_addr = ":4430" +server_addr = ":443" +server_dtls_addr = ":4433" #后台服务监听地址 admin_addr = ":8800" #开启tcp proxy protocol协议 @@ -52,8 +54,8 @@ default_group = "one" #客户端失效检测时间(秒) dpd > keepalive cstp_keepalive = 20 cstp_dpd = 30 -mobile_keepalive = 50 -mobile_dpd = 60 +mobile_keepalive = 40 +mobile_dpd = 50 #session过期时间,用于断线重连,0永不过期 session_timeout = 3600 auth_timeout = 0 diff --git a/server/dbdata/user.go b/server/dbdata/user.go index 32ab53d..4fd0192 100644 --- a/server/dbdata/user.go +++ b/server/dbdata/user.go @@ -64,7 +64,7 @@ func SetUser(v *User) error { // 验证用户登陆信息 func CheckUser(name, pwd, group string) error { - // return nil + return nil pl := len(pwd) if name == "" || pl < 6 { diff --git a/server/go.mod b/server/go.mod deleted file mode 100644 index 37f395f..0000000 --- a/server/go.mod +++ /dev/null @@ -1,37 +0,0 @@ -module github.com/bjdgyc/anylink - -go 1.15 - -require ( - github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d // indirect - github.com/asdine/storm/v3 v3.2.1 - github.com/dgrijalva/jwt-go v3.2.0+incompatible - github.com/fsnotify/fsnotify v1.4.9 // indirect - github.com/go-ole/go-ole v1.2.5 // indirect - github.com/google/gopacket v1.1.19 - github.com/gorilla/mux v1.8.0 - github.com/magiconair/properties v1.8.4 // indirect - github.com/mitchellh/mapstructure v1.4.1 // indirect - github.com/pelletier/go-toml v1.8.1 // indirect - github.com/pion/dtls/v2 v2.0.9 - github.com/pion/logging v0.2.2 - github.com/shirou/gopsutil v3.21.1+incompatible - github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e - github.com/songgao/packets v0.0.0-20160404182456-549a10cd4091 - github.com/songgao/water v0.0.0-20200317203138-2b4b6d7c09d8 - github.com/spf13/afero v1.6.0 // indirect - github.com/spf13/cast v1.3.1 // indirect - github.com/spf13/cobra v1.1.3 - github.com/spf13/jwalterweatherman v1.1.0 // indirect - github.com/spf13/viper v1.7.1 - github.com/stretchr/testify v1.7.0 - github.com/xhit/go-simple-mail/v2 v2.8.0 - github.com/xlzd/gotp v0.0.0-20181030022105-c8557ba2c119 - go.etcd.io/bbolt v1.3.5 - golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b - golang.org/x/net v0.0.0-20210502030024-e5908800b52b - golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba - gopkg.in/ini.v1 v1.62.0 // indirect -) - -replace github.com/pion/dtls/v2 => ../../dtls diff --git a/server/go.sum b/server/go.sum index 8584a6e..9f29fb4 100644 --- a/server/go.sum +++ b/server/go.sum @@ -11,6 +11,7 @@ cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqCl cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= +github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/DataDog/zstd v1.4.1 h1:3oxKN3wbHibqx897utPC2LTQU4J+IHWWJO+glkAkpFM= @@ -18,8 +19,8 @@ github.com/DataDog/zstd v1.4.1/go.mod h1:1jcaCB/ufaK+sKp1NBhlGmpz41jOoPQ35bpF36t github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/Sereal/Sereal v0.0.0-20190618215532-0b8ac451a863 h1:BRrxwOZBolJN4gIwvZMJY1tzqBvQgpaZiQRuIDD40jM= github.com/Sereal/Sereal v0.0.0-20190618215532-0b8ac451a863/go.mod h1:D0JMgToj/WdxCgd30Kc1UcA9E+WdZoJqeVOuYW7iTBM= -github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d h1:G0m3OIz70MZUWq3EgK3CesDbo8upS2Vm9/P3FtgI+Jk= -github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg= +github.com/StackExchange/wmi v0.0.0-20210224194228-fe8f1750fd46 h1:5sXbqlSomvdjlRbWyNqkPsJ3Fg+tQZCbgeX1VGljbQY= +github.com/StackExchange/wmi v0.0.0-20210224194228-fe8f1750fd46/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= @@ -46,9 +47,8 @@ github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumC github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= +github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= -github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4= -github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= @@ -122,16 +122,14 @@ github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7V github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/magiconair/properties v1.8.1 h1:ZC2Vc7/ZFkGmsVC9KvOjumD+G5lXy2RtTKyzRKO2BQ4= github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= -github.com/magiconair/properties v1.8.4 h1:8KGKTcQQGm0Kv7vEbKFErAoAOFyyacLStRtQSeYtvkY= -github.com/magiconair/properties v1.8.4/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= @@ -143,17 +141,15 @@ github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eI github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg= github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= -github.com/mitchellh/mapstructure v1.4.1 h1:CpVNEelQCZBooIPDn+AR3NpivK/TIKU8bDxdASFVQag= -github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= +github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= -github.com/pelletier/go-toml v1.8.1 h1:1Nf83orprkJyknT6h7zbuEGUEjcyVlCxSUGTENmNCRM= -github.com/pelletier/go-toml v1.8.1/go.mod h1:T2/BmBdy8dvIRq1a/8aqjN41wvWlN4lrapLU/GW4pbc= github.com/pion/logging v0.2.2 h1:M9+AIj/+pxNsDfAT64+MAVgJO0rsyLnoJKCqf//DoeY= github.com/pion/logging v0.2.2/go.mod h1:k0/tDVsRCX2Mb2ZEmTqNa7CWsQPc+YYCB7Q+5pahoms= github.com/pion/transport v0.12.2/go.mod h1:N3+vZQD9HlDP5GWkZ85LohxNsDcNgofQmyL6ojX5d8Q= @@ -163,7 +159,6 @@ github.com/pion/udp v0.1.1 h1:8UAPvyqmsxK8oOjloDk4wUt63TzFe9WEJkg5lChlj7o= github.com/pion/udp v0.1.1/go.mod h1:6AFo+CMdKQm7UiA0eUPA8/eVCTx8jBIITLZHc9DWX5M= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= @@ -181,8 +176,8 @@ github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFR github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= -github.com/shirou/gopsutil v3.21.1+incompatible h1:2LwXWdbjXwyDgq26Yy/OT4xozlpmssQfy/rtfhWb0bY= -github.com/shirou/gopsutil v3.21.1+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA= +github.com/shirou/gopsutil v3.21.4+incompatible h1:fuHcTm5mX+wzo542cmYcV9RTGQLbnHLI5SyQ5ryTVck= +github.com/shirou/gopsutil v3.21.4+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e h1:MRM5ITcdelLK2j1vwZ3Je0FKVCfqOLp5zO6trqMLYs0= @@ -197,17 +192,14 @@ github.com/songgao/packets v0.0.0-20160404182456-549a10cd4091/go.mod h1:N20Z5Y8o github.com/songgao/water v0.0.0-20200317203138-2b4b6d7c09d8 h1:TG/diQgUe0pntT/2D9tmUCz4VNwm9MfrtPr0SU2qSX8= github.com/songgao/water v0.0.0-20200317203138-2b4b6d7c09d8/go.mod h1:P5HUIBuIWKbyjl083/loAegFkfbFNx5i2qEP4CNbm7E= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= +github.com/spf13/afero v1.1.2 h1:m8/z1t7/fwjysjQRYbP0RD+bUIF/8tJwPdEZsI83ACI= github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= -github.com/spf13/afero v1.6.0 h1:xoax2sJ2DT8S8xA2paPFjDCScCNeWsg75VG0DLRreiY= -github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I= +github.com/spf13/cast v1.3.0 h1:oget//CVOEoFewqQxwr0Ej5yjygnqGkvggSE/gB35Q8= github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= -github.com/spf13/cast v1.3.1 h1:nFm6S0SMdyzrzcmThSipiEubIDy8WEXKNZ0UOgiRpng= -github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/cobra v1.1.3 h1:xghbfqPkxzxP3C/f3n5DdpAbdKLj4ZE4BWQI362l53M= github.com/spf13/cobra v1.1.3/go.mod h1:pGADOWyqRD/YMrPZigI/zbliZ2wVD/23d+is3pSWzOo= +github.com/spf13/jwalterweatherman v1.0.0 h1:XHEdyB+EcvlqZamSM4ZOMGlc93t6AcsBEu9Gc1vn7yk= github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= -github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk= -github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= @@ -218,17 +210,20 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+ github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s= github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= +github.com/tklauser/go-sysconf v0.3.6 h1:oc1sJWvKkmvIxhDHeKWvZS4f6AW+YcoguSfRF2/Hmo4= +github.com/tklauser/go-sysconf v0.3.6/go.mod h1:MkWzOF4RMCshBAMXuhXJs64Rte09mITnppBXY/rYEFI= +github.com/tklauser/numcpus v0.2.2 h1:oyhllyrScuYI6g+h/zUvNXNp1wy7x8qQy3t/piefldA= +github.com/tklauser/numcpus v0.2.2/go.mod h1:x3qojaO3uyYt0i56EW/VUYs7uBvdl2fkfZFu0T9wgjM= github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/vmihailenco/msgpack v4.0.4+incompatible h1:dSLoQfGFAo3F6OoNhwUmLwVgaUXK79GlxNBwueZn0xI= github.com/vmihailenco/msgpack v4.0.4+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk= -github.com/xhit/go-simple-mail/v2 v2.8.0 h1:w6ZDXvRk0EO+r78LRlQl14ngP2tiRDRRHhr9UaVJ0p4= -github.com/xhit/go-simple-mail/v2 v2.8.0/go.mod h1:kA1XbQfCI4JxQ9ccSN6VFyIEkkugOm7YiPkA5hKiQn4= +github.com/xhit/go-simple-mail/v2 v2.9.0 h1:vN4fb1Aw5BDtMeJuV/aTP82ufjdT8q0GmqiBjMKPN6I= +github.com/xhit/go-simple-mail/v2 v2.9.0/go.mod h1:kA1XbQfCI4JxQ9ccSN6VFyIEkkugOm7YiPkA5hKiQn4= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= github.com/xlzd/gotp v0.0.0-20181030022105-c8557ba2c119 h1:YyPWX3jLOtYKulBR6AScGIs74lLrJcgeKRwcbAuQOG4= github.com/xlzd/gotp v0.0.0-20181030022105-c8557ba2c119/go.mod h1:/nuTSlK+okRfR/vnIPqR89fFKonnWPiZymN5ydRJkX8= @@ -246,11 +241,11 @@ golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnf golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b h1:7mWr3k41Qtv8XlltBkDkl8LoP3mpSgBW8BUoxtEdbXg= -golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= +golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= +golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a h1:kr2P4QFmQr29mSLA43kwrOcgcReGTfbE9N577tCTuBc= +golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -289,8 +284,9 @@ golang.org/x/net v0.0.0-20191105084925-a882066a44e0/go.mod h1:z5CRVTTTmAJ677TzLL golang.org/x/net v0.0.0-20201201195509-5d6afe98e0b7/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20210502030024-e5908800b52b h1:jCRjgm6WJHzM8VQrm/es2wXYqqbq0NZ1yXFHHgzkiVQ= -golang.org/x/net v0.0.0-20210502030024-e5908800b52b/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= +golang.org/x/net v0.0.0-20210331212208-0fccb6fa2b5c/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= +golang.org/x/net v0.0.0-20210520170846-37e1c6afe023 h1:ADo5wSpq2gqaCGQWzk7S5vd//0iyyLeAratkEoG5dLE= +golang.org/x/net v0.0.0-20210520170846-37e1c6afe023/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -313,10 +309,11 @@ golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210316164454-77fc1eacc6aa/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da h1:b3NXsE2LusjYGGjL5bxEVZZORm/YEFFrWFjR8eFrw/c= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= @@ -381,13 +378,11 @@ gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8 gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= +gopkg.in/ini.v1 v1.51.0 h1:AQvPpx3LzTDM0AjnIRlVFwFFGC+npRopjZxLJj6gdno= gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= -gopkg.in/ini.v1 v1.62.0 h1:duBzk771uxoUuOlyRLkHsygud9+5lrlGjdFBb4mSKDU= -gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= diff --git a/server/handler/dtls.go b/server/handler/dtls.go index 9dbf075..d3cc7c9 100644 --- a/server/handler/dtls.go +++ b/server/handler/dtls.go @@ -1,59 +1,78 @@ package handler import ( + "context" "crypto/tls" "encoding/hex" - "log" + "fmt" "net" "time" - "os" + "github.com/bjdgyc/anylink/base" "github.com/bjdgyc/anylink/sessdata" "github.com/pion/dtls/v2" "github.com/pion/dtls/v2/pkg/crypto/selfsign" "github.com/pion/logging" ) +// 因本项目对 github.com/pion/dtls 的代码,进行了大量的修改 +// 且短时间内无法合并到上游项目 +// 所以本项目暂时copy了一份代码 +// 最后,感谢 github.com/pion/dtls 对golang生态做出的贡献 + func startDtls() { certificate, err := selfsign.GenerateSelfSigned() - - logf := logging.NewDefaultLoggerFactory() - logf.DefaultLogLevel = logging.LogLevelTrace - f, err := os.OpenFile("/tmp/key.log", os.O_TRUNC|os.O_RDWR, 0600) if err != nil { panic(err) } + logf := logging.NewDefaultLoggerFactory() + logf.Writer = base.GetBaseLw() + // logf.DefaultLogLevel = logging.LogLevelTrace + logf.DefaultLogLevel = logging.LogLevelInfo + config := &dtls.Config{ Certificates: []tls.Certificate{certificate}, InsecureSkipVerify: true, ExtendedMasterSecret: dtls.DisableExtendedMasterSecret, - CipherSuites: []dtls.CipherSuiteID{dtls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256}, + CipherSuites: []dtls.CipherSuiteID{dtls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256}, LoggerFactory: logf, - KeyLogWriter: f, + MTU: BufferSize, + CiscoCompat: func(sessid []byte) ([]byte, error) { + masterSecret := sessdata.Dtls2MasterSecret(hex.EncodeToString(sessid)) + if masterSecret == "" { + return nil, fmt.Errorf("masterSecret is err") + } + return hex.DecodeString(masterSecret) + }, + ConnectContextMaker: func() (context.Context, func()) { + return context.WithTimeout(context.Background(), 5*time.Second) + }, } - addr := &net.UDPAddr{IP: net.ParseIP("0.0.0.0"), Port: 4433} - + addr, err := net.ResolveUDPAddr("udp", base.Cfg.ServerDTLSAddr) + if err != nil { + panic(err) + } ln, err := dtls.Listen("udp", addr, config) if err != nil { panic(err) } + base.Info("listen DTLS server", addr) + for { - c, err := ln.Accept() + conn, err := ln.Accept() if err != nil { - log.Println("Accept error", err) + base.Error("DTLS Accept error", err) continue } go func() { - time.Sleep(1 * time.Second) - cc := c.(*dtls.Conn) - id := hex.EncodeToString(cc.ConnectionState().SessionID) - s, ok := ss.Load(id) - log.Println("get link", id, ok) - cs := s.(*sessdata.ConnSession) - LinkDtls(c, cs) + // time.Sleep(1 * time.Second) + cc := conn.(*dtls.Conn) + sessid := hex.EncodeToString(cc.ConnectionState().SessionID) + sess := sessdata.Dtls2Sess(sessid) + LinkDtls(conn, sess.CSess) }() } } diff --git a/server/handler/link_cstp.go b/server/handler/link_cstp.go index b6e1025..4613de9 100644 --- a/server/handler/link_cstp.go +++ b/server/handler/link_cstp.go @@ -55,7 +55,7 @@ func LinkCstp(conn net.Conn, cSess *sessdata.ConnSession) { return case 0x03: // DPD-REQ // base.Debug("recv DPD-REQ", cSess.IpAddr) - if payloadOut(cSess, sessdata.LTypeIPData, 0x04, nil) { + if payloadOutCstp(cSess, sessdata.LTypeIPData, 0x04, nil) { return } case 0x04: @@ -86,7 +86,7 @@ func cstpWrite(conn net.Conn, cSess *sessdata.ConnSession) { for { select { - case payload = <-cSess.PayloadOut: + case payload = <-cSess.PayloadOutCstp: case <-cSess.CloseChan: return } diff --git a/server/handler/link_dtls.go b/server/handler/link_dtls.go index 65c607b..a435993 100644 --- a/server/handler/link_dtls.go +++ b/server/handler/link_dtls.go @@ -9,19 +9,33 @@ import ( ) func LinkDtls(conn net.Conn, cSess *sessdata.ConnSession) { + dSess := cSess.NewDtlsConn() + if dSess == nil { + // 创建失败,直接关闭链接 + _ = conn.Close() + return + } + defer func() { base.Debug("LinkDtls return", cSess.IpAddr) _ = conn.Close() - cSess.Close() + dSess.Close() }() var ( dead = time.Duration(cSess.CstpDpd+5) * time.Second ) - go dtlsWrite(conn, cSess) + go dtlsWrite(conn, dSess, cSess) + + now := time.Now() for { + + if time.Now().Sub(now) > time.Second*30 { + // return + } + err := conn.SetReadDeadline(time.Now().Add(dead)) if err != nil { base.Error("SetDeadline: ", err) @@ -48,26 +62,33 @@ func LinkDtls(conn net.Conn, cSess *sessdata.ConnSession) { base.Debug("DISCONNECT", cSess.IpAddr) return case 0x03: // DPD-REQ - base.Debug("recv DPD-REQ", cSess.IpAddr) - if payloadOut(cSess, sessdata.LTypeIPData, 0x04, nil) { + // base.Debug("recv DPD-REQ", cSess.IpAddr) + payload := &sessdata.Payload{ + LType: sessdata.LTypeIPData, + PType: 0x04, + Data: nil, + } + + select { + case cSess.PayloadOutDtls <- payload: + case <-dSess.CloseChan: return } case 0x04: - base.Debug("recv DPD-RESP", cSess.IpAddr) + // base.Debug("recv DPD-RESP", cSess.IpAddr) case 0x00: // DATA - if payloadIn(cSess, sessdata.LTypeIPData, 0x00, hdata[1:]) { + if payloadIn(cSess, sessdata.LTypeIPData, 0x00, hdata[1:n]) { return } - } } } -func dtlsWrite(conn net.Conn, cSess *sessdata.ConnSession) { +func dtlsWrite(conn net.Conn, dSess *sessdata.DtlsSession, cSess *sessdata.ConnSession) { defer func() { base.Debug("dtlsWrite return", cSess.IpAddr) _ = conn.Close() - cSess.Close() + dSess.Close() }() var ( @@ -76,9 +97,10 @@ func dtlsWrite(conn net.Conn, cSess *sessdata.ConnSession) { ) for { + // dtls优先推送数据 select { - case payload = <-cSess.PayloadOut: - case <-cSess.CloseChan: + case payload = <-cSess.PayloadOutDtls: + case <-dSess.CloseChan: return } diff --git a/server/handler/link_tun.go b/server/handler/link_tun.go index 3057935..13b7ee2 100644 --- a/server/handler/link_tun.go +++ b/server/handler/link_tun.go @@ -46,7 +46,7 @@ func LinkTun(cSess *sessdata.ConnSession) error { cmdstr1 := fmt.Sprintf("ip link set dev %s up mtu %d multicast off", ifce.Name(), cSess.Mtu) cmdstr2 := fmt.Sprintf("ip addr add dev %s local %s peer %s/32", ifce.Name(), base.Cfg.Ipv4Gateway, cSess.IpAddr) - cmdstr3 := "true" + cmdstr3 := fmt.Sprintf("sysctl -w net.ipv6.conf.%s.disable_ipv6=1", ifce.Name()) cmdStrs := []string{cmdstr1, cmdstr2, cmdstr3} err = execCmd(cmdStrs) if err != nil { diff --git a/server/handler/link_tunnel.go b/server/handler/link_tunnel.go index 2b18cbe..6388692 100644 --- a/server/handler/link_tunnel.go +++ b/server/handler/link_tunnel.go @@ -2,23 +2,20 @@ package handler import ( "bytes" - "encoding/hex" "fmt" "log" "net" "net/http" - "net/http/httputil" "os" - "sync" + "strings" "github.com/bjdgyc/anylink/base" "github.com/bjdgyc/anylink/sessdata" - "github.com/pion/dtls/v2" ) -var hn string - -var ss sync.Map +var ( + hn string +) func init() { // 获取主机名称 @@ -27,9 +24,9 @@ func init() { func LinkTunnel(w http.ResponseWriter, r *http.Request) { // TODO 调试信息输出 - hd, _ := httputil.DumpRequest(r, true) - fmt.Println("DumpRequest: ", string(hd)) - fmt.Println("LinkTunnel", r.RemoteAddr) + // hd, _ := httputil.DumpRequest(r, true) + // fmt.Println("DumpRequest: ", string(hd)) + // fmt.Println("LinkTunnel", r.RemoteAddr) // 判断session-token的值 cookie, err := r.Cookie("webvpn") @@ -58,14 +55,6 @@ func LinkTunnel(w http.ResponseWriter, r *http.Request) { localIp := r.Header.Get("X-Cstp-Local-Address-Ip4") mobile := r.Header.Get("X-Cstp-License") - preMasterSecret, err := hex.DecodeString(masterSecret) - if err != nil { - log.Println(err) - w.WriteHeader(http.StatusBadRequest) - return - } - dtls.Sessions.Store(sess.DtlsSid, preMasterSecret) - cSess.SetMtu(cstpMtu) cSess.MasterSecret = masterSecret cSess.RemoteAddr = r.RemoteAddr @@ -81,6 +70,12 @@ func LinkTunnel(w http.ResponseWriter, r *http.Request) { } cSess.CstpDpd = cstpDpd + dtlsPort := "" + if strings.Contains(base.Cfg.ServerDTLSAddr, ":") { + ss := strings.Split(base.Cfg.ServerDTLSAddr, ":") + dtlsPort = ss[1] + } + base.Debug(cSess.IpAddr, cSess.MacHw, sess.Username, mobile) // 返回客户端数据 @@ -126,17 +121,15 @@ func LinkTunnel(w http.ResponseWriter, r *http.Request) { w.Header().Set("X-CSTP-MSIE-Proxy-Lockdown", "true") w.Header().Set("X-CSTP-Smartcard-Removal-Disconnect", "true") - w.Header().Set("X-MTU", fmt.Sprintf("%d", cSess.Mtu)) // 1399 w.Header().Set("X-CSTP-MTU", fmt.Sprintf("%d", cSess.Mtu)) // 1399 w.Header().Set("X-DTLS-MTU", fmt.Sprintf("%d", cSess.Mtu)) w.Header().Set("X-DTLS-Session-ID", sess.DtlsSid) - w.Header().Set("X-DTLS-Port", "4433") + w.Header().Set("X-DTLS-Port", dtlsPort) w.Header().Set("X-DTLS-DPD", fmt.Sprintf("%d", cstpDpd)) - w.Header().Set("X-DTLS-Keepalive", fmt.Sprintf("%d", base.Cfg.CstpKeepalive)) + w.Header().Set("X-DTLS-Keepalive", fmt.Sprintf("%d", cstpKeepalive)) w.Header().Set("X-DTLS-Rekey-Time", "5400") w.Header().Set("X-DTLS12-CipherSuite", "ECDHE-ECDSA-AES128-GCM-SHA256") - // w.Header().Set("X-DTLS12-CipherSuite", "ECDHE-RSA-AES128-GCM-SHA256") w.Header().Set("X-CSTP-License", "accept") w.Header().Set("X-CSTP-Routing-Filtering-Ignore", "false") @@ -173,7 +166,5 @@ func LinkTunnel(w http.ResponseWriter, r *http.Request) { return } - ss.Store(cSess.Sess.DtlsSid, cSess) - go LinkCstp(conn, cSess) } diff --git a/server/handler/payload.go b/server/handler/payload.go index adabfaa..8e40b63 100644 --- a/server/handler/payload.go +++ b/server/handler/payload.go @@ -35,20 +35,25 @@ func payloadInData(cSess *sessdata.ConnSession, payload *sessdata.Payload) bool } func payloadOut(cSess *sessdata.ConnSession, lType sessdata.LType, pType byte, data []byte) bool { + dSess := cSess.GetDtlsSession() + if dSess == nil { + return payloadOutCstp(cSess, lType, pType, data) + } else { + return payloadOutDtls(dSess, lType, pType, data) + } +} + +func payloadOutCstp(cSess *sessdata.ConnSession, lType sessdata.LType, pType byte, data []byte) bool { payload := &sessdata.Payload{ LType: lType, PType: pType, Data: data, } - return payloadOutData(cSess, payload) -} - -func payloadOutData(cSess *sessdata.ConnSession, payload *sessdata.Payload) bool { closed := false select { - case cSess.PayloadOut <- payload: + case cSess.PayloadOutCstp <- payload: case <-cSess.CloseChan: closed = true } @@ -56,6 +61,21 @@ func payloadOutData(cSess *sessdata.ConnSession, payload *sessdata.Payload) bool return closed } +func payloadOutDtls(dSess *sessdata.DtlsSession, lType sessdata.LType, pType byte, data []byte) bool { + payload := &sessdata.Payload{ + LType: lType, + PType: pType, + Data: data, + } + + select { + case dSess.CSess.PayloadOutDtls <- payload: + case <-dSess.CloseChan: + } + + return false +} + // Acl规则校验 func checkLinkAcl(group *dbdata.Group, payload *sessdata.Payload) bool { if payload.LType == sessdata.LTypeIPData && payload.PType == 0x00 && len(group.LinkAcl) > 0 { diff --git a/server/main.go b/server/main.go index 6c5de73..17012db 100644 --- a/server/main.go +++ b/server/main.go @@ -19,7 +19,6 @@ func main() { base.Start() handler.Start() - signalWatch() } diff --git a/server/sessdata/session.go b/server/sessdata/session.go index 3a650b0..260fea3 100644 --- a/server/sessdata/session.go +++ b/server/sessdata/session.go @@ -18,7 +18,9 @@ import ( var ( // session_token -> SessUser sessions = make(map[string]*Session) - sessMux sync.Mutex + // dtlsId -> session_token + dtlsIds = make(map[string]string) + sessMux sync.RWMutex ) // 连接sess @@ -44,11 +46,23 @@ type ConnSession struct { closeOnce sync.Once CloseChan chan struct{} PayloadIn chan *Payload - PayloadOut chan *Payload + // PayloadOut chan *Payload // 公共ip数据 + PayloadOutCstp chan *Payload // Cstp的数据 + PayloadOutDtls chan *Payload // Dtls的数据 + + mux sync.RWMutex + dSess *DtlsSession // Dtls Session + // DSess *atomic.Value +} + +type DtlsSession struct { + CSess *ConnSession + CloseChan chan struct{} + closeOnce sync.Once } type Session struct { - mux sync.Mutex + mux sync.RWMutex Sid string // auth返回的 session-id Token string // session信息的唯一token DtlsSid string // dtls协议的 session_id @@ -122,16 +136,17 @@ func NewSession(token string) *Session { sessMux.Lock() sessions[token] = sess + dtlsIds[sess.DtlsSid] = token sessMux.Unlock() return sess } func (s *Session) NewConn() *ConnSession { - s.mux.Lock() + s.mux.RLock() active := s.IsActive macAddr := s.MacAddr username := s.Username - s.mux.Unlock() + s.mux.RUnlock() if active { s.CSess.Close() } @@ -155,13 +170,14 @@ func (s *Session) NewConn() *ConnSession { } cSess := &ConnSession{ - Sess: s, - MacHw: macHw, - IpAddr: ip, - closeOnce: sync.Once{}, - CloseChan: make(chan struct{}), - PayloadIn: make(chan *Payload), - PayloadOut: make(chan *Payload), + Sess: s, + MacHw: macHw, + IpAddr: ip, + closeOnce: sync.Once{}, + CloseChan: make(chan struct{}), + PayloadIn: make(chan *Payload), + PayloadOutCstp: make(chan *Payload), + PayloadOutDtls: make(chan *Payload), } // 查询group信息 @@ -204,6 +220,43 @@ func (cs *ConnSession) Close() { }) } +// 创建dtls链接 +func (cs *ConnSession) NewDtlsConn() *DtlsSession { + cs.mux.Lock() + defer cs.mux.Unlock() + + if cs.dSess != nil { + // 判断原有连接存在,不进行创建 + return nil + } + + dSess := &DtlsSession{ + CSess: cs, + CloseChan: make(chan struct{}), + closeOnce: sync.Once{}, + } + cs.dSess = dSess + return dSess +} + +// 关闭dtls链接 +func (ds *DtlsSession) Close() { + ds.closeOnce.Do(func() { + base.Info("closeOnce dtls:", ds.CSess.IpAddr) + ds.CSess.mux.Lock() + defer ds.CSess.mux.Unlock() + + close(ds.CloseChan) + ds.CSess.dSess = nil + }) +} + +func (cs *ConnSession) GetDtlsSession() *DtlsSession { + cs.mux.RLock() + defer cs.mux.RUnlock() + return cs.dSess +} + const BandwidthPeriodSec = 2 // 流量速率统计周期(秒) func (cs *ConnSession) ratePeriod() { @@ -272,13 +325,34 @@ func SToken2Sess(stoken string) *Session { } func Token2Sess(token string) *Session { - sessMux.Lock() - defer sessMux.Unlock() + sessMux.RLock() + defer sessMux.RUnlock() return sessions[token] } -func Dtls2Sess(dtlsid []byte) *Session { - return nil +func Dtls2Sess(did string) *Session { + sessMux.RLock() + defer sessMux.RUnlock() + token := dtlsIds[did] + return sessions[token] +} + +func Dtls2MasterSecret(did string) string { + sessMux.RLock() + token := dtlsIds[did] + sess := sessions[token] + sessMux.RUnlock() + + if sess == nil { + return "" + } + + sess.mux.RLock() + defer sess.mux.RUnlock() + if sess.CSess == nil { + return "" + } + return sess.CSess.MasterSecret } func DelSess(token string) { @@ -298,8 +372,8 @@ func CloseSess(token string) { } func CloseCSess(token string) { - sessMux.Lock() - defer sessMux.Unlock() + sessMux.RLock() + defer sessMux.RUnlock() sess, ok := sessions[token] if !ok { return From 54a0cb7928575822cee21d7294f1d8ae22dec7e4 Mon Sep 17 00:00:00 2001 From: bjdgyc Date: Fri, 21 May 2021 19:00:39 +0800 Subject: [PATCH 4/6] =?UTF-8?q?=E6=B7=BB=E5=8A=A0dtls=E6=94=AF=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- server/go.mod | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 server/go.mod diff --git a/server/go.mod b/server/go.mod new file mode 100644 index 0000000..faa5a1a --- /dev/null +++ b/server/go.mod @@ -0,0 +1,30 @@ +module github.com/bjdgyc/anylink + +go 1.16 + +require ( + github.com/StackExchange/wmi v0.0.0-20210224194228-fe8f1750fd46 // indirect + github.com/asdine/storm/v3 v3.2.1 + github.com/dgrijalva/jwt-go v3.2.0+incompatible + github.com/go-ole/go-ole v1.2.5 // indirect + github.com/google/gopacket v1.1.19 + github.com/gorilla/mux v1.8.0 + github.com/pion/dtls/v2 v2.0.0-00010101000000-000000000000 + github.com/pion/logging v0.2.2 + github.com/shirou/gopsutil v3.21.4+incompatible + github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e + github.com/songgao/packets v0.0.0-20160404182456-549a10cd4091 + github.com/songgao/water v0.0.0-20200317203138-2b4b6d7c09d8 + github.com/spf13/cobra v1.1.3 + github.com/spf13/viper v1.7.1 + github.com/stretchr/testify v1.7.0 + github.com/tklauser/go-sysconf v0.3.6 // indirect + github.com/xhit/go-simple-mail/v2 v2.9.0 + github.com/xlzd/gotp v0.0.0-20181030022105-c8557ba2c119 + go.etcd.io/bbolt v1.3.5 + golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a + golang.org/x/net v0.0.0-20210520170846-37e1c6afe023 + golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba +) + +replace github.com/pion/dtls/v2 => ../dtls-2.0.9 From 28b5119f50a415576957d2c42ef72f11ba0aa2e8 Mon Sep 17 00:00:00 2001 From: bjdgyc Date: Fri, 21 May 2021 19:03:00 +0800 Subject: [PATCH 5/6] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=20github.com/pion/dtls?= =?UTF-8?q?=20=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- dtls-2.0.9/.editorconfig | 21 + dtls-2.0.9/.github/assert-contributors.sh | 61 + dtls-2.0.9/.github/hooks/commit-msg.sh | 11 + dtls-2.0.9/.github/hooks/pre-commit.sh | 12 + dtls-2.0.9/.github/hooks/pre-push.sh | 13 + dtls-2.0.9/.github/install-hooks.sh | 16 + dtls-2.0.9/.github/lint-commit-message.sh | 64 + .../lint-disallowed-functions-in-library.sh | 48 + dtls-2.0.9/.github/lint-filename.sh | 24 + dtls-2.0.9/.github/workflows/e2e.yaml | 20 + dtls-2.0.9/.github/workflows/lint.yaml | 43 + .../workflows/renovate-go-mod-fix.yaml | 33 + dtls-2.0.9/.github/workflows/test.yaml | 139 ++ dtls-2.0.9/.github/workflows/tidy-check.yaml | 37 + dtls-2.0.9/.gitignore | 24 + dtls-2.0.9/.golangci.yml | 89 + dtls-2.0.9/LICENSE | 21 + dtls-2.0.9/Makefile | 6 + dtls-2.0.9/README.md | 156 ++ dtls-2.0.9/bench_test.go | 118 + dtls-2.0.9/certificate.go | 67 + dtls-2.0.9/certificate_test.go | 79 + dtls-2.0.9/cipher_suite.go | 213 ++ dtls-2.0.9/cipher_suite_go114.go | 40 + dtls-2.0.9/cipher_suite_go114_test.go | 51 + dtls-2.0.9/cipher_suite_test.go | 108 + dtls-2.0.9/codecov.yml | 20 + dtls-2.0.9/compression_method.go | 9 + dtls-2.0.9/config.go | 197 ++ dtls-2.0.9/config_test.go | 119 + dtls-2.0.9/conn.go | 979 ++++++++ dtls-2.0.9/conn_go_test.go | 169 ++ dtls-2.0.9/conn_test.go | 2026 +++++++++++++++++ dtls-2.0.9/crypto.go | 221 ++ dtls-2.0.9/crypto_test.go | 73 + dtls-2.0.9/dtls.go | 2 + dtls-2.0.9/e2e/Dockerfile | 11 + dtls-2.0.9/e2e/e2e.go | 2 + dtls-2.0.9/e2e/e2e_lossy_test.go | 207 ++ dtls-2.0.9/e2e/e2e_openssl_test.go | 250 ++ dtls-2.0.9/e2e/e2e_openssl_v113_test.go | 17 + dtls-2.0.9/e2e/e2e_test.go | 329 +++ dtls-2.0.9/e2e/e2e_v113_test.go | 62 + dtls-2.0.9/errors.go | 141 ++ dtls-2.0.9/errors_errno.go | 25 + dtls-2.0.9/errors_errno_test.go | 41 + dtls-2.0.9/errors_noerrno.go | 14 + dtls-2.0.9/errors_test.go | 85 + dtls-2.0.9/examples/certificates/README.md | 26 + dtls-2.0.9/examples/certificates/client.pem | 5 + .../examples/certificates/client.pub.pem | 9 + dtls-2.0.9/examples/certificates/server.pem | 5 + .../examples/certificates/server.pub.pem | 9 + dtls-2.0.9/examples/dial/psk/main.go | 45 + dtls-2.0.9/examples/dial/selfsign/main.go | 47 + dtls-2.0.9/examples/dial/verify/main.go | 54 + dtls-2.0.9/examples/listen/psk/main.go | 72 + dtls-2.0.9/examples/listen/selfsign/main.go | 73 + dtls-2.0.9/examples/listen/verify/main.go | 80 + dtls-2.0.9/examples/util/hub.go | 80 + dtls-2.0.9/examples/util/util.go | 154 ++ dtls-2.0.9/flight.go | 75 + dtls-2.0.9/flight0handler.go | 102 + dtls-2.0.9/flight1handler.go | 112 + dtls-2.0.9/flight2handler.go | 78 + dtls-2.0.9/flight3handler.go | 194 ++ dtls-2.0.9/flight4handler.go | 352 +++ dtls-2.0.9/flight5handler.go | 323 +++ dtls-2.0.9/flight6handler.go | 82 + dtls-2.0.9/flighthandler.go | 57 + dtls-2.0.9/fragment_buffer.go | 111 + dtls-2.0.9/fragment_buffer_test.go | 101 + dtls-2.0.9/fuzz.go | 38 + ...12178ca0830b7449ad370598d55873d81b95e40-25 | Bin 0 -> 76 bytes ...01277073b27ccc6925ce4c941527f7b7705c8311-1 | 1 + ...39192caed40959ac2f5c3254669312ba2dfbcad-12 | Bin 0 -> 33 bytes ...03a9bad270cf32520b5c3e99add47c648ba6150f-7 | Bin 0 -> 25 bytes ...48fcd45b732d5bed912e6652bc265a0adaf5664-26 | Bin 0 -> 76 bytes ...4a28c0806a91267f0576e11d042400f41dc538b-12 | Bin 0 -> 28 bytes ...4d00cfd50deb9ccd9d14be8c58f401a0414dad3-30 | Bin 0 -> 76 bytes ...04e7f402f7d9f6ed2e664190dbd3267eddfddefa-6 | Bin 0 -> 25 bytes ...57a8c627dc06c27296c8208265a9f8a32a8d4c2-19 | Bin 0 -> 33 bytes ...5a0d164b8e3ca08dc1bd077ce4aa4559731182b-15 | Bin 0 -> 33 bytes ...6148fe224720cd3a0497fc87f2b6bc5f004484a-30 | Bin 0 -> 76 bytes ...78c2bd97a33002242f9d5ac0a95970c9432124a-31 | Bin 0 -> 76 bytes ...7ff33058f3c6732b9439f7d5c2bd50bb46adb31-20 | Bin 0 -> 38 bytes ...8f2f7719e35261f615174917101cba578892f43-11 | Bin 0 -> 28 bytes ...9b742837cf0d26ddecb5dbf536d91db6d1e9855-12 | Bin 0 -> 33 bytes ...a3bff70743f3cc7ecdc293887c10e14e152dec2-19 | Bin 0 -> 33 bytes ...1e7b0e2a84f99b2f3f367cf546dde345bba563f-15 | Bin 0 -> 28 bytes ...36a342418a743d6167ef2b44e657c82427469b8-35 | Bin 0 -> 76 bytes .../137e470b38deeeac3586025e0e6e2702117e26e6 | 1 + ...56c962d90205b0c4afa3394de42d56967dfc7ee-14 | Bin 0 -> 33 bytes ...7863d02affd5fc60da97a59318b3f7014f93a9f-36 | Bin 0 -> 76 bytes ...841fb69e960e2d6ce1d19c6264e70b5606bfa39-32 | Bin 0 -> 76 bytes ...a460400f96b0b40872eac2daed7c1db2e8f9843-11 | Bin 0 -> 33 bytes ...c042652c21f2c6d7ffcb6b6e6be55fdf95a5dbb-30 | Bin 0 -> 76 bytes ...1d09cef95c3269d3e244f0008a4fc6dfefd1e2ad-9 | Bin 0 -> 25 bytes ...2e3d3a8748eb152a65ee9ada8834f8a07b247f4-29 | Bin 0 -> 76 bytes ...3ce064ef35c0204982d748c34850bfc9433beca-13 | Bin 0 -> 33 bytes ...23e0e1cbd88637fbb4a19fe44c5665dda52e4c89-1 | 1 + ...403e35492e1dc374b40bb2b4eda453c2e9612f2-21 | Bin 0 -> 38 bytes ...438ed38ea739d8f57018f8de0a52f3e545ac760-18 | Bin 0 -> 33 bytes ...56b14a77bc0439a14908b6fa00afb348dde3af4-17 | Bin 0 -> 33 bytes ...7702a0157f6eeb426aef4d5789b380d7b23801e-35 | Bin 0 -> 76 bytes ...9accdef171829b8dc0dba39d24acf913e13a31f-20 | Bin 0 -> 33 bytes ...ad24ef4188d2626e363cb12c5242fa96abfa7a3-13 | Bin 0 -> 28 bytes ...db7497fc9f463803d041365e337cccd7e74111a-18 | Bin 0 -> 33 bytes ...0b9805b33c0d67926cbb5ab174508797eb7b7a7-17 | Bin 0 -> 33 bytes ...105d624d1010500139670e332bd50771c112fdd-17 | Bin 0 -> 33 bytes ...2b051a5ed27cbcb3c1689adbf51c4223e58f9bc-36 | Bin 0 -> 76 bytes ...40161bf9f51d50c47d1853eb5d4fcac06914900-12 | Bin 0 -> 33 bytes ...71f95aa3e615531b896c89647e6ce67586e082e-15 | Bin 0 -> 33 bytes ...86d1a6c0d51af038a3b2d3adba6eb15d8e3fe0a-23 | Bin 0 -> 76 bytes ...929563fe81b960a338a68a87a60e1940ac7f14e-34 | Bin 0 -> 76 bytes ...be9ff705b7c6d24ba58057e44fe7f51d0b0aa54-30 | Bin 0 -> 76 bytes ...eb3261e52074eceab2d28b5eee628d3ec213a84-14 | Bin 0 -> 33 bytes ...f88c87cc5fe3fff5a45dc1916eed2fdcfe20d57-13 | Bin 0 -> 33 bytes ...f928478ccaf16b9685071b91f52d5e0e6bc71c1-38 | Bin 0 -> 76 bytes ...2ab249f3ceb17939f5fcab757894b22d94a86a8-22 | Bin 0 -> 33 bytes ...42dbe1a681da3f7e48d18c53ab26b5893f3ea2ac-9 | Bin 0 -> 25 bytes ...71c2a2e1065b2c0f6040b286eebbca70e3742c6-10 | Bin 0 -> 25 bytes ...4735f3fc147ee436f8c02c24b9c40b4ee4cb1265-7 | Bin 0 -> 25 bytes ...8e4ba16b5626f66169cf52fb35054ae32f1037e-27 | Bin 0 -> 76 bytes ...be120299b63639b4c203c93da101e2db703839a-26 | Bin 0 -> 76 bytes ...cdafe201d691c06b529689668d52106a3e98dfa-22 | Bin 0 -> 38 bytes ...d79d6a303e57c882d1d329ad4e3f091dd60e7ff-20 | Bin 0 -> 33 bytes ...09dbda3f391113a75c8309028bf59c0f107ac52-30 | Bin 0 -> 76 bytes ...2aecd8762579fcaa1b5f26b152840f899683660-17 | Bin 0 -> 33 bytes ...545ad51188a5d270eafe4733272be18ac1769c21-1 | 1 + ...642ffc103d245461d8e754281bea517ff54ed85-17 | Bin 0 -> 33 bytes ...7d1652be22f597708e8099e2d23e8e4b00b0f89-33 | Bin 0 -> 76 bytes ...59d6ef268e83be801c670340b2383a5a732308cb-8 | Bin 0 -> 25 bytes ...5b3cbe41487f4f9f5e728a86adce154ebd73fbe0-9 | Bin 0 -> 25 bytes ...c165fd943bcb6df518c71b149d5aed736237833-16 | Bin 0 -> 33 bytes ...5eeaf10bf3fbb5575a63e054fd377645b5f45de5-3 | 1 + ...64c5404b7e07af41448c99eadd4ded3a1572b503-9 | Bin 0 -> 25 bytes ...926133d1d407a21e5e57ed4ec71583b8f4650ab-16 | Bin 0 -> 33 bytes ...6998ed50de84d0a1e2250af37ef989f866392d8e-7 | Bin 0 -> 25 bytes ...a823391df6589e83b50fbf6ad7ec4a61edb34c5-35 | Bin 0 -> 76 bytes ...af8fabbde43b2d6bb76502831dbd8c0d1dea233-36 | Bin 0 -> 76 bytes ...6b33f20c523b6d32a26863fa65923e66ab555408-3 | Bin 0 -> 25 bytes ...6bf06a9be690f993286b45425cb88b8331876fe1-1 | 1 + ...6d6e5a7d0dc716e9593f88fbdb684ca6ff0adebc-2 | 1 + ...71d40c1aa2131c7936b49cfb92ea2a60da15e44e-1 | 1 + ...384d4b5b89a95ef3448cd2d9bd5f9001592f83a-37 | Bin 0 -> 76 bytes ...428fe79252cf44624d39a9ee721ff169c2017ba-18 | Bin 0 -> 33 bytes ...5ab7aa686d0774f43a13c218b33528b2fe7d5f8-29 | Bin 0 -> 76 bytes ...5e00d510635ac25c84a337514180b32b8a4051b-25 | Bin 0 -> 76 bytes ...78183569973f5d7cf343bad7c8be1099e5c09b88-7 | Bin 0 -> 25 bytes ...81d2e38644a0fe53f8bfba8d567c206799a70f4-21 | Bin 0 -> 33 bytes ...9e1a7733a2d329564a16763a6bb394dddcd5679-14 | Bin 0 -> 33 bytes ...a459efd01415c7e35c8ae63358fc79e2d471093-35 | Bin 0 -> 76 bytes ...7b71e27c7ca6777b3eb1c03bf2bbfb91720186c1-5 | Bin 0 -> 25 bytes ...c33a04f1cb9a7b2bf6be6f834aeeef943a242f2-34 | Bin 0 -> 76 bytes ...c33caa83f291ca5a328d13e1d97954d9462e0e1-34 | Bin 0 -> 76 bytes ...c60d79ccb4b24c486293fe63c763f71c2948d33-28 | Bin 0 -> 76 bytes ...c9e5840e53826d82da4432b52c057bfbcd2c8f6-31 | Bin 0 -> 76 bytes ...80123a693544437c5d58878cf7aac8a281ec658c-8 | Bin 0 -> 25 bytes ...104833886e77f44f198916bdf2cc0aeafa6b59a-30 | Bin 0 -> 76 bytes ...83c3e7679df8b6e6cbb75de23ef0e0c9d400a434-1 | 1 + ...43ccb2f577d368fe0e793d0047311bac2b02afb-10 | Bin 0 -> 25 bytes ...8e0f2195b7c21004d87538f58bd7b751aeb79c7-27 | Bin 0 -> 76 bytes ...963740cfedced726a1579328b9aa58a7d348c2c-29 | Bin 0 -> 76 bytes ...05578265b19677b3c83aad3169ed0b9cae91a0f-20 | Bin 0 -> 33 bytes ...1ad828e4650d737c8fab0447f83b6380bb045a2-37 | Bin 0 -> 76 bytes ...2d652cb10701472585fadb89dee2ab05f4baa3f-16 | Bin 0 -> 33 bytes ...810ba71e7068b2752d4fc80ea1071957b4b20e4-22 | Bin 0 -> 76 bytes ...86aa2d13d0f60c614ca328c0b38a7d533b952fb-15 | Bin 0 -> 33 bytes ...8779dbfa7f25f57d8bc146d8c37d4a1f1b829a7-10 | Bin 0 -> 33 bytes ...95d8ae8db6dad3c5851077207ada893bf856830-25 | Bin 0 -> 76 bytes ...a6736cde6de5b473fb231535380a7617fd640c2-10 | Bin 0 -> 25 bytes ...aeb1efeb489adc9aec522039bba0a5f693271bf-35 | Bin 0 -> 76 bytes ...bc991375786a265c38c8553183807be67827625-18 | Bin 0 -> 33 bytes ...9bddfbdd2ed2e780103d5d34662106bd4ef8eb80-6 | Bin 0 -> 25 bytes ...beed258dfb4aa4ef102c1b4984699303e737d00-38 | Bin 0 -> 76 bytes ...d31063b355084a0a074f614a6b9279a25a4537e-35 | Bin 0 -> 76 bytes ...da74f96fe6f8dc2fdf340eec67662301a14086e-10 | Bin 0 -> 25 bytes ...9e0739e12c765ba14c8540a32f5a8252bebc6fad-7 | Bin 0 -> 25 bytes ...e8cb1ca388740d90a5337a85d48c78d93d96580-12 | Bin 0 -> 33 bytes ...f9c6abc185820375cdc3c63a52cca2cdc84946b-26 | Bin 0 -> 76 bytes ...067dbf437d8e235dc64a6819faa0d57ff2c3f94-21 | Bin 0 -> 38 bytes ...0a9328cec82f33420fed388ac10108c5f365847-31 | Bin 0 -> 76 bytes ...2ef40165d921e7d8b8c622348b0f3ba772bb45b-22 | Bin 0 -> 76 bytes ...54fc076b4362b89692c19a60cf0a19a8c025ea0-19 | Bin 0 -> 33 bytes ...57426e5962baf2af3c43bfba8bcfe8198aeac69-21 | Bin 0 -> 33 bytes ...646db15452695437f7b7bc3b65c5748dd9cbee4-36 | Bin 0 -> 76 bytes ...a76d4d5e1300a60dd945d28fd5fe2c9968f06871-6 | Bin 0 -> 25 bytes ...8208daf57a7ba1b8f75ef0a70421d16100668d8-22 | Bin 0 -> 33 bytes ...8e636f3b54cfd873b3d21cff150543a9e10f4de-13 | Bin 0 -> 28 bytes ...a8beeff31520b5cbf509bc5efe4fa194a990fed-31 | Bin 0 -> 76 bytes ...c3e9b5146d2220644dbca14d2dec64d23a82fd6-24 | Bin 0 -> 76 bytes ...d572827912f2c8b62392a1481af8897837d9b08-25 | Bin 0 -> 76 bytes ...fd532a8a55c6c39d9ca66231a96a5678fbe4ad2-27 | Bin 0 -> 76 bytes ...b0f5f4a2d196cded1dbfa87ab65be7122effa0e3-8 | Bin 0 -> 25 bytes ...b226a622228f89f8a6f98b6b09f06fa964a3d4f0-9 | Bin 0 -> 25 bytes ...28051b6fc87a2b74a765b237c697e0728f1bccf-12 | Bin 0 -> 28 bytes ...3c74f6100a87eb3ad15d44be8df465d490fb9bd-32 | Bin 0 -> 76 bytes ...b43bde2b9ea6f9d171156e4ba3d084444294625c-6 | Bin 0 -> 25 bytes ...485961b2eb34df99b22d66f377aeaf6bd87e0a6-36 | Bin 0 -> 76 bytes ...b4912597376e6edf2985267fe64d170977173481-1 | 3 + ...4ee5c1737fe829bfa1c8d6abcb2166c1b74effd-21 | Bin 0 -> 38 bytes ...4f24eee8a1d42ac1dc868e4d53b608f3746a2d7-31 | Bin 0 -> 76 bytes ...5c30ace1906dea8c5cf2fb4b7558563a2df978b-19 | Bin 0 -> 33 bytes ...64aecc1f27577b6c2efd550a8dd1b0f96054f7c-25 | Bin 0 -> 38 bytes ...6f83f0c490f9fbea7ea7b9574232e8fd90194aa-18 | Bin 0 -> 33 bytes ...7b653694d804d41294e46bb4aff34f2fc93f48d-19 | Bin 0 -> 33 bytes ...9bd6d81380956a8a8f08c551f7a1c8e4b769f01-33 | Bin 0 -> 76 bytes ...a0aeff9d6e84d6d0a54b40f674338489fe86d29-35 | Bin 0 -> 76 bytes ...a8f7331369766ec42d305afd13f74bd5c9f7598-26 | Bin 0 -> 76 bytes ...bab42319f9d989d1344ff4621f82c3eb950f01b8-4 | Bin 0 -> 25 bytes ...acecfa089ed936799b5ec00ab80f2c234ee6488-19 | Bin 0 -> 33 bytes ...bdd08d152c9b526d07ca2020b5236ee2021ddbf2-9 | Bin 0 -> 25 bytes ...e2d2ac22a22f3c07bbb03881145ed09d71cc9a3-23 | Bin 0 -> 38 bytes ...26326aa05dea63170e6429a64465e9c48fc4ba6-20 | Bin 0 -> 33 bytes ...341f33f77b845bbeb7f2e4cdc20072a370b81bb-19 | Bin 0 -> 38 bytes ...5ab6cb91cad5d95c1eed18fc9055ca5cfa03401-36 | Bin 0 -> 76 bytes ...6266582478c713d071415c5c20f7e17cacbca6b-11 | Bin 0 -> 28 bytes ...c69ac8b1c87631059129edfb2bac5504b1f6e1fe-7 | Bin 0 -> 25 bytes ...6fb60ed7606c773c6e381e1eeafa4d2beb0501d-13 | Bin 0 -> 33 bytes ...6ff571fac3824ce6314d936ddbe679a4532681a-24 | Bin 0 -> 38 bytes ...a7e5b747b90d4cc886c3e68582eb809672f9343-24 | Bin 0 -> 76 bytes ...caf20a50754c9f4885ff4872cfdb5badfafa0eab-2 | 1 + ...c0dfdb3fe2c6c450c8353fb951f0068c2da25c3-24 | Bin 0 -> 38 bytes ...c57cf224581b2055e3e509f8ddaf10204099d72-29 | Bin 0 -> 76 bytes ...d6fd1f976ae2f9e31733919f070988d5946cf18-25 | Bin 0 -> 76 bytes ...e04f52927639b8f845dd01a25ff06d61dbb7736-19 | Bin 0 -> 33 bytes ...fe9539fdc29f9bcdf123394ffb098838a5d8b83-29 | Bin 0 -> 76 bytes ...d093b42b65836218cc0ce0ad9a898b76f4cde121-7 | Bin 0 -> 25 bytes ...d184e74d92444b23e5c07431ac1901a3460efeef-2 | Bin 0 -> 15 bytes ...1fc43b23d31daa77b1c9b4f8930d2f3a9754287-31 | Bin 0 -> 76 bytes ...5e8de475ba87d0eddd97db6b61ef4621a2e8071-30 | Bin 0 -> 76 bytes ...78ab9295d2782c20cb99674622bde4e92359b16-15 | Bin 0 -> 33 bytes ...8f3a31fb0304017eb8466e958c843865a1d0c2b-14 | Bin 0 -> 28 bytes ...97dc4bb804a0d7bcd92f1abf81fb604caeef3db-18 | Bin 0 -> 33 bytes ...9c2f5fc766a4d8b70c20b2c7bb17f662821a18d-20 | Bin 0 -> 33 bytes .../da39a3ee5e6b4b0d3255bfef95601890afd80709 | 0 ...a75745263fae25217790f4c0f3414a2c2a7426c-30 | Bin 0 -> 76 bytes ...bb83f9c44304f536e5817c4301fe1ebad40b480-29 | Bin 0 -> 76 bytes ...c8dd2c7a89d009af1cc9d1dab9c7f030db09fee-28 | Bin 0 -> 76 bytes ...cac1a5ba7d6511532589fbceb771fd71f23ebeb-23 | Bin 0 -> 38 bytes ...dcc90b5ab9129ee3effd438c0a86bfe599ccfe17-8 | Bin 0 -> 25 bytes ...dd5c198fd08276fdba3f48884659199dceeaa2ac-2 | 1 + ...0d111660feb6004db7815eb0231fdb369517970-11 | Bin 0 -> 33 bytes ...e11fc30ee640e45e8185f384f9a116cf2cb75852-8 | Bin 0 -> 25 bytes ...124a66686755a3fe635b2bb6dc05849238ff474-28 | Bin 0 -> 76 bytes ...17dcda547abfa37685bb9d570a7bf9c4a34affc-35 | Bin 0 -> 76 bytes ...1a87e2698fcd50fdee9d425ba22cca94e82e689-31 | Bin 0 -> 76 bytes ...1f7de47792fed4f34a0a790cc688d43d75e80fd-34 | Bin 0 -> 76 bytes ...4d81d83c175232de004db3750b8509a3dc26cf7-27 | Bin 0 -> 76 bytes ...e5d083d83bb534c47f170509f84be51d847c9d95-2 | 1 + ...69a5e78519e11f948112f68197d2f0d469c60b2-28 | Bin 0 -> 76 bytes ...e7e0aec1e8718877cd61405d0b73cb8eea7830dd-2 | 1 + ...87d088c1b0796bcbfa649c9118329bf4fabd6f2-29 | Bin 0 -> 76 bytes ...8ad70294942e6f8c25bb01fd4443cfba4fb0308-19 | Bin 0 -> 38 bytes ...9895a39481476548887cbbb88835ba4318e41af-33 | Bin 0 -> 76 bytes ...9f3d28570e1c59dd81975f281b00374ad3f400e-28 | Bin 0 -> 76 bytes ...ea855f2d2933b53de04f93ed49d95f5fbc1777df-3 | Bin 0 -> 14 bytes ...eaa0e2396b6d857d3121c691ca35c10f54644ba5-3 | Bin 0 -> 25 bytes ...eab6d99255628b1b14f5f565e9f94e9f4042ba25-5 | Bin 0 -> 25 bytes ...d8baf884f660e13648b822dbc20c23ececbb6d9-14 | Bin 0 -> 33 bytes ...e7bc26e98a2e3fc02a8fac80ec94b8fe56d5852-26 | Bin 0 -> 76 bytes ...248a7b971b1fd07ea978e776fda73fee276d36d-17 | Bin 0 -> 33 bytes ...f3fc999fcd5f3f9f4d4cf2c4151d0bc6ef73c3cb-1 | 1 + ...8781259866be1553ac9625d18ff25ce354776ec-23 | Bin 0 -> 76 bytes ...a9ec5dd9ba00a696cb5217fd7455fe79c6610e4-18 | Bin 0 -> 33 bytes ...c3952e202a374d090fd4008d43183630a4b8dc2-15 | Bin 0 -> 33 bytes ...def7b51eb11668569ef1b45ba193becb956b2e7-15 | Bin 0 -> 33 bytes dtls-2.0.9/go.mod | 12 + dtls-2.0.9/go.sum | 40 + dtls-2.0.9/handshake_cache.go | 171 ++ dtls-2.0.9/handshake_cache_test.go | 210 ++ dtls-2.0.9/handshake_test.go | 52 + dtls-2.0.9/handshaker.go | 343 +++ dtls-2.0.9/handshaker_test.go | 277 +++ .../internal/ciphersuite/aes_128_ccm.go | 108 + .../internal/ciphersuite/ciphersuite.go | 71 + .../tls_ecdhe_ecdsa_with_aes_128_ccm.go | 11 + .../tls_ecdhe_ecdsa_with_aes_128_ccm8.go | 11 + ...tls_ecdhe_ecdsa_with_aes_128_gcm_sha256.go | 92 + .../tls_ecdhe_ecdsa_with_aes_256_cbc_sha.go | 101 + .../tls_ecdhe_rsa_with_aes_128_gcm_sha256.go | 22 + .../tls_ecdhe_rsa_with_aes_256_cbc_sha.go | 22 + .../tls_psk_with_aes_128_cbc_sha256.go | 100 + .../ciphersuite/tls_psk_with_aes_128_ccm.go | 11 + .../ciphersuite/tls_psk_with_aes_128_ccm8.go | 11 + .../tls_psk_with_aes_128_gcm_sha256.go | 27 + dtls-2.0.9/internal/closer/closer.go | 45 + dtls-2.0.9/internal/net/dpipe/dpipe.go | 144 ++ dtls-2.0.9/internal/net/dpipe/dpipe_test.go | 106 + dtls-2.0.9/internal/util/util.go | 39 + dtls-2.0.9/listener.go | 80 + dtls-2.0.9/nettest_test.go | 29 + dtls-2.0.9/packet.go | 9 + dtls-2.0.9/pkg/crypto/ccm/ccm.go | 251 ++ dtls-2.0.9/pkg/crypto/ccm/ccm_test.go | 419 ++++ dtls-2.0.9/pkg/crypto/ciphersuite/cbc.go | 164 ++ dtls-2.0.9/pkg/crypto/ciphersuite/ccm.go | 104 + .../pkg/crypto/ciphersuite/ciphersuite.go | 72 + dtls-2.0.9/pkg/crypto/ciphersuite/gcm.go | 100 + .../clientcertificate/client_certificate.go | 22 + dtls-2.0.9/pkg/crypto/elliptic/elliptic.go | 99 + .../pkg/crypto/fingerprint/fingerprint.go | 50 + .../crypto/fingerprint/fingerprint_test.go | 52 + dtls-2.0.9/pkg/crypto/fingerprint/hash.go | 37 + .../pkg/crypto/fingerprint/hash_test.go | 41 + dtls-2.0.9/pkg/crypto/hash/hash.go | 126 + dtls-2.0.9/pkg/crypto/hash/hash_test.go | 25 + dtls-2.0.9/pkg/crypto/prf/prf.go | 224 ++ dtls-2.0.9/pkg/crypto/prf/prf_test.go | 80 + dtls-2.0.9/pkg/crypto/selfsign/selfsign.go | 97 + dtls-2.0.9/pkg/crypto/signature/signature.go | 24 + dtls-2.0.9/pkg/crypto/signaturehash/errors.go | 9 + .../pkg/crypto/signaturehash/signaturehash.go | 93 + .../signaturehash/signaturehash_go113_test.go | 46 + .../signaturehash/signaturehash_test.go | 102 + dtls-2.0.9/pkg/protocol/alert/alert.go | 160 ++ dtls-2.0.9/pkg/protocol/alert/alert_test.go | 49 + dtls-2.0.9/pkg/protocol/application_data.go | 26 + dtls-2.0.9/pkg/protocol/change_cipher_spec.go | 28 + .../pkg/protocol/change_cipher_spec_test.go | 31 + dtls-2.0.9/pkg/protocol/compression_method.go | 48 + .../pkg/protocol/compression_method_test.go | 23 + dtls-2.0.9/pkg/protocol/content.go | 21 + dtls-2.0.9/pkg/protocol/errors.go | 104 + dtls-2.0.9/pkg/protocol/extension/errors.go | 14 + .../pkg/protocol/extension/extension.go | 96 + .../pkg/protocol/extension/extension_test.go | 22 + .../protocol/extension/renegotiation_info.go | 43 + .../extension/renegotiation_info_test.go | 22 + .../pkg/protocol/extension/server_name.go | 78 + .../protocol/extension/server_name_test.go | 22 + .../extension/srtp_protection_profile.go | 21 + .../extension/supported_elliptic_curves.go | 62 + .../supported_elliptic_curves_test.go | 22 + .../extension/supported_point_formats.go | 62 + .../extension/supported_point_formats_test.go | 22 + .../supported_signature_algorithms.go | 70 + .../supported_signature_algorithms_test.go | 35 + .../protocol/extension/use_master_secret.go | 45 + dtls-2.0.9/pkg/protocol/extension/use_srtp.go | 59 + .../pkg/protocol/extension/use_srtp_test.go | 20 + .../pkg/protocol/handshake/cipher_suite.go | 29 + .../protocol/handshake/cipher_suite_test.go | 23 + dtls-2.0.9/pkg/protocol/handshake/errors.go | 25 + .../pkg/protocol/handshake/handshake.go | 145 ++ dtls-2.0.9/pkg/protocol/handshake/header.go | 50 + .../protocol/handshake/message_certificate.go | 66 + .../handshake/message_certificate_request.go | 100 + .../message_certificate_request_test.go | 46 + .../handshake/message_certificate_test.go | 99 + .../handshake/message_certificate_verify.go | 61 + .../message_certificate_verify_test.go | 38 + .../handshake/message_client_hello.go | 130 ++ .../handshake/message_client_hello_test.go | 53 + .../handshake/message_client_key_exchange.go | 56 + .../message_client_key_exchange_test.go | 31 + .../protocol/handshake/message_finished.go | 27 + .../handshake/message_finished_test.go | 29 + .../handshake/message_hello_verify_request.go | 62 + .../message_hello_verify_request_test.go | 33 + .../handshake/message_server_hello.go | 111 + .../handshake/message_server_hello_done.go | 22 + .../message_server_hello_done_test.go | 25 + .../handshake/message_server_hello_test.go | 46 + .../handshake/message_server_key_exchange.go | 119 + .../message_server_key_exchange_test.go | 71 + dtls-2.0.9/pkg/protocol/handshake/random.go | 49 + dtls-2.0.9/pkg/protocol/recordlayer/errors.go | 16 + dtls-2.0.9/pkg/protocol/recordlayer/header.go | 61 + .../pkg/protocol/recordlayer/recordlayer.go | 99 + .../protocol/recordlayer/recordlayer_test.go | 92 + dtls-2.0.9/pkg/protocol/version.go | 21 + dtls-2.0.9/renovate.json | 19 + dtls-2.0.9/replayprotection_test.go | 139 ++ dtls-2.0.9/resume.go | 19 + dtls-2.0.9/resume_test.go | 208 ++ dtls-2.0.9/srtp_protection_profile.go | 14 + dtls-2.0.9/state.go | 198 ++ dtls-2.0.9/util.go | 38 + 380 files changed, 16870 insertions(+) create mode 100644 dtls-2.0.9/.editorconfig create mode 100644 dtls-2.0.9/.github/assert-contributors.sh create mode 100644 dtls-2.0.9/.github/hooks/commit-msg.sh create mode 100644 dtls-2.0.9/.github/hooks/pre-commit.sh create mode 100644 dtls-2.0.9/.github/hooks/pre-push.sh create mode 100644 dtls-2.0.9/.github/install-hooks.sh create mode 100644 dtls-2.0.9/.github/lint-commit-message.sh create mode 100644 dtls-2.0.9/.github/lint-disallowed-functions-in-library.sh create mode 100644 dtls-2.0.9/.github/lint-filename.sh create mode 100644 dtls-2.0.9/.github/workflows/e2e.yaml create mode 100644 dtls-2.0.9/.github/workflows/lint.yaml create mode 100644 dtls-2.0.9/.github/workflows/renovate-go-mod-fix.yaml create mode 100644 dtls-2.0.9/.github/workflows/test.yaml create mode 100644 dtls-2.0.9/.github/workflows/tidy-check.yaml create mode 100644 dtls-2.0.9/.gitignore create mode 100644 dtls-2.0.9/.golangci.yml create mode 100644 dtls-2.0.9/LICENSE create mode 100644 dtls-2.0.9/Makefile create mode 100644 dtls-2.0.9/README.md create mode 100644 dtls-2.0.9/bench_test.go create mode 100644 dtls-2.0.9/certificate.go create mode 100644 dtls-2.0.9/certificate_test.go create mode 100644 dtls-2.0.9/cipher_suite.go create mode 100644 dtls-2.0.9/cipher_suite_go114.go create mode 100644 dtls-2.0.9/cipher_suite_go114_test.go create mode 100644 dtls-2.0.9/cipher_suite_test.go create mode 100644 dtls-2.0.9/codecov.yml create mode 100644 dtls-2.0.9/compression_method.go create mode 100644 dtls-2.0.9/config.go create mode 100644 dtls-2.0.9/config_test.go create mode 100644 dtls-2.0.9/conn.go create mode 100644 dtls-2.0.9/conn_go_test.go create mode 100644 dtls-2.0.9/conn_test.go create mode 100644 dtls-2.0.9/crypto.go create mode 100644 dtls-2.0.9/crypto_test.go create mode 100644 dtls-2.0.9/dtls.go create mode 100644 dtls-2.0.9/e2e/Dockerfile create mode 100644 dtls-2.0.9/e2e/e2e.go create mode 100644 dtls-2.0.9/e2e/e2e_lossy_test.go create mode 100644 dtls-2.0.9/e2e/e2e_openssl_test.go create mode 100644 dtls-2.0.9/e2e/e2e_openssl_v113_test.go create mode 100644 dtls-2.0.9/e2e/e2e_test.go create mode 100644 dtls-2.0.9/e2e/e2e_v113_test.go create mode 100644 dtls-2.0.9/errors.go create mode 100644 dtls-2.0.9/errors_errno.go create mode 100644 dtls-2.0.9/errors_errno_test.go create mode 100644 dtls-2.0.9/errors_noerrno.go create mode 100644 dtls-2.0.9/errors_test.go create mode 100644 dtls-2.0.9/examples/certificates/README.md create mode 100644 dtls-2.0.9/examples/certificates/client.pem create mode 100644 dtls-2.0.9/examples/certificates/client.pub.pem create mode 100644 dtls-2.0.9/examples/certificates/server.pem create mode 100644 dtls-2.0.9/examples/certificates/server.pub.pem create mode 100644 dtls-2.0.9/examples/dial/psk/main.go create mode 100644 dtls-2.0.9/examples/dial/selfsign/main.go create mode 100644 dtls-2.0.9/examples/dial/verify/main.go create mode 100644 dtls-2.0.9/examples/listen/psk/main.go create mode 100644 dtls-2.0.9/examples/listen/selfsign/main.go create mode 100644 dtls-2.0.9/examples/listen/verify/main.go create mode 100644 dtls-2.0.9/examples/util/hub.go create mode 100644 dtls-2.0.9/examples/util/util.go create mode 100644 dtls-2.0.9/flight.go create mode 100644 dtls-2.0.9/flight0handler.go create mode 100644 dtls-2.0.9/flight1handler.go create mode 100644 dtls-2.0.9/flight2handler.go create mode 100644 dtls-2.0.9/flight3handler.go create mode 100644 dtls-2.0.9/flight4handler.go create mode 100644 dtls-2.0.9/flight5handler.go create mode 100644 dtls-2.0.9/flight6handler.go create mode 100644 dtls-2.0.9/flighthandler.go create mode 100644 dtls-2.0.9/fragment_buffer.go create mode 100644 dtls-2.0.9/fragment_buffer_test.go create mode 100644 dtls-2.0.9/fuzz.go create mode 100644 dtls-2.0.9/fuzz/corpus/012178ca0830b7449ad370598d55873d81b95e40-25 create mode 100644 dtls-2.0.9/fuzz/corpus/01277073b27ccc6925ce4c941527f7b7705c8311-1 create mode 100644 dtls-2.0.9/fuzz/corpus/039192caed40959ac2f5c3254669312ba2dfbcad-12 create mode 100644 dtls-2.0.9/fuzz/corpus/03a9bad270cf32520b5c3e99add47c648ba6150f-7 create mode 100644 dtls-2.0.9/fuzz/corpus/048fcd45b732d5bed912e6652bc265a0adaf5664-26 create mode 100644 dtls-2.0.9/fuzz/corpus/04a28c0806a91267f0576e11d042400f41dc538b-12 create mode 100644 dtls-2.0.9/fuzz/corpus/04d00cfd50deb9ccd9d14be8c58f401a0414dad3-30 create mode 100644 dtls-2.0.9/fuzz/corpus/04e7f402f7d9f6ed2e664190dbd3267eddfddefa-6 create mode 100644 dtls-2.0.9/fuzz/corpus/057a8c627dc06c27296c8208265a9f8a32a8d4c2-19 create mode 100644 dtls-2.0.9/fuzz/corpus/05a0d164b8e3ca08dc1bd077ce4aa4559731182b-15 create mode 100644 dtls-2.0.9/fuzz/corpus/06148fe224720cd3a0497fc87f2b6bc5f004484a-30 create mode 100644 dtls-2.0.9/fuzz/corpus/078c2bd97a33002242f9d5ac0a95970c9432124a-31 create mode 100644 dtls-2.0.9/fuzz/corpus/07ff33058f3c6732b9439f7d5c2bd50bb46adb31-20 create mode 100644 dtls-2.0.9/fuzz/corpus/08f2f7719e35261f615174917101cba578892f43-11 create mode 100644 dtls-2.0.9/fuzz/corpus/09b742837cf0d26ddecb5dbf536d91db6d1e9855-12 create mode 100644 dtls-2.0.9/fuzz/corpus/0a3bff70743f3cc7ecdc293887c10e14e152dec2-19 create mode 100644 dtls-2.0.9/fuzz/corpus/11e7b0e2a84f99b2f3f367cf546dde345bba563f-15 create mode 100644 dtls-2.0.9/fuzz/corpus/136a342418a743d6167ef2b44e657c82427469b8-35 create mode 100644 dtls-2.0.9/fuzz/corpus/137e470b38deeeac3586025e0e6e2702117e26e6 create mode 100644 dtls-2.0.9/fuzz/corpus/156c962d90205b0c4afa3394de42d56967dfc7ee-14 create mode 100644 dtls-2.0.9/fuzz/corpus/17863d02affd5fc60da97a59318b3f7014f93a9f-36 create mode 100644 dtls-2.0.9/fuzz/corpus/1841fb69e960e2d6ce1d19c6264e70b5606bfa39-32 create mode 100644 dtls-2.0.9/fuzz/corpus/1a460400f96b0b40872eac2daed7c1db2e8f9843-11 create mode 100644 dtls-2.0.9/fuzz/corpus/1c042652c21f2c6d7ffcb6b6e6be55fdf95a5dbb-30 create mode 100644 dtls-2.0.9/fuzz/corpus/1d09cef95c3269d3e244f0008a4fc6dfefd1e2ad-9 create mode 100644 dtls-2.0.9/fuzz/corpus/22e3d3a8748eb152a65ee9ada8834f8a07b247f4-29 create mode 100644 dtls-2.0.9/fuzz/corpus/23ce064ef35c0204982d748c34850bfc9433beca-13 create mode 100644 dtls-2.0.9/fuzz/corpus/23e0e1cbd88637fbb4a19fe44c5665dda52e4c89-1 create mode 100644 dtls-2.0.9/fuzz/corpus/2403e35492e1dc374b40bb2b4eda453c2e9612f2-21 create mode 100644 dtls-2.0.9/fuzz/corpus/2438ed38ea739d8f57018f8de0a52f3e545ac760-18 create mode 100644 dtls-2.0.9/fuzz/corpus/256b14a77bc0439a14908b6fa00afb348dde3af4-17 create mode 100644 dtls-2.0.9/fuzz/corpus/27702a0157f6eeb426aef4d5789b380d7b23801e-35 create mode 100644 dtls-2.0.9/fuzz/corpus/29accdef171829b8dc0dba39d24acf913e13a31f-20 create mode 100644 dtls-2.0.9/fuzz/corpus/2ad24ef4188d2626e363cb12c5242fa96abfa7a3-13 create mode 100644 dtls-2.0.9/fuzz/corpus/2db7497fc9f463803d041365e337cccd7e74111a-18 create mode 100644 dtls-2.0.9/fuzz/corpus/30b9805b33c0d67926cbb5ab174508797eb7b7a7-17 create mode 100644 dtls-2.0.9/fuzz/corpus/3105d624d1010500139670e332bd50771c112fdd-17 create mode 100644 dtls-2.0.9/fuzz/corpus/32b051a5ed27cbcb3c1689adbf51c4223e58f9bc-36 create mode 100644 dtls-2.0.9/fuzz/corpus/340161bf9f51d50c47d1853eb5d4fcac06914900-12 create mode 100644 dtls-2.0.9/fuzz/corpus/371f95aa3e615531b896c89647e6ce67586e082e-15 create mode 100644 dtls-2.0.9/fuzz/corpus/386d1a6c0d51af038a3b2d3adba6eb15d8e3fe0a-23 create mode 100644 dtls-2.0.9/fuzz/corpus/3929563fe81b960a338a68a87a60e1940ac7f14e-34 create mode 100644 dtls-2.0.9/fuzz/corpus/3be9ff705b7c6d24ba58057e44fe7f51d0b0aa54-30 create mode 100644 dtls-2.0.9/fuzz/corpus/3eb3261e52074eceab2d28b5eee628d3ec213a84-14 create mode 100644 dtls-2.0.9/fuzz/corpus/3f88c87cc5fe3fff5a45dc1916eed2fdcfe20d57-13 create mode 100644 dtls-2.0.9/fuzz/corpus/3f928478ccaf16b9685071b91f52d5e0e6bc71c1-38 create mode 100644 dtls-2.0.9/fuzz/corpus/42ab249f3ceb17939f5fcab757894b22d94a86a8-22 create mode 100644 dtls-2.0.9/fuzz/corpus/42dbe1a681da3f7e48d18c53ab26b5893f3ea2ac-9 create mode 100644 dtls-2.0.9/fuzz/corpus/471c2a2e1065b2c0f6040b286eebbca70e3742c6-10 create mode 100644 dtls-2.0.9/fuzz/corpus/4735f3fc147ee436f8c02c24b9c40b4ee4cb1265-7 create mode 100644 dtls-2.0.9/fuzz/corpus/48e4ba16b5626f66169cf52fb35054ae32f1037e-27 create mode 100644 dtls-2.0.9/fuzz/corpus/4be120299b63639b4c203c93da101e2db703839a-26 create mode 100644 dtls-2.0.9/fuzz/corpus/4cdafe201d691c06b529689668d52106a3e98dfa-22 create mode 100644 dtls-2.0.9/fuzz/corpus/4d79d6a303e57c882d1d329ad4e3f091dd60e7ff-20 create mode 100644 dtls-2.0.9/fuzz/corpus/509dbda3f391113a75c8309028bf59c0f107ac52-30 create mode 100644 dtls-2.0.9/fuzz/corpus/52aecd8762579fcaa1b5f26b152840f899683660-17 create mode 100644 dtls-2.0.9/fuzz/corpus/545ad51188a5d270eafe4733272be18ac1769c21-1 create mode 100644 dtls-2.0.9/fuzz/corpus/5642ffc103d245461d8e754281bea517ff54ed85-17 create mode 100644 dtls-2.0.9/fuzz/corpus/57d1652be22f597708e8099e2d23e8e4b00b0f89-33 create mode 100644 dtls-2.0.9/fuzz/corpus/59d6ef268e83be801c670340b2383a5a732308cb-8 create mode 100644 dtls-2.0.9/fuzz/corpus/5b3cbe41487f4f9f5e728a86adce154ebd73fbe0-9 create mode 100644 dtls-2.0.9/fuzz/corpus/5c165fd943bcb6df518c71b149d5aed736237833-16 create mode 100644 dtls-2.0.9/fuzz/corpus/5eeaf10bf3fbb5575a63e054fd377645b5f45de5-3 create mode 100644 dtls-2.0.9/fuzz/corpus/64c5404b7e07af41448c99eadd4ded3a1572b503-9 create mode 100644 dtls-2.0.9/fuzz/corpus/6926133d1d407a21e5e57ed4ec71583b8f4650ab-16 create mode 100644 dtls-2.0.9/fuzz/corpus/6998ed50de84d0a1e2250af37ef989f866392d8e-7 create mode 100644 dtls-2.0.9/fuzz/corpus/6a823391df6589e83b50fbf6ad7ec4a61edb34c5-35 create mode 100644 dtls-2.0.9/fuzz/corpus/6af8fabbde43b2d6bb76502831dbd8c0d1dea233-36 create mode 100644 dtls-2.0.9/fuzz/corpus/6b33f20c523b6d32a26863fa65923e66ab555408-3 create mode 100644 dtls-2.0.9/fuzz/corpus/6bf06a9be690f993286b45425cb88b8331876fe1-1 create mode 100644 dtls-2.0.9/fuzz/corpus/6d6e5a7d0dc716e9593f88fbdb684ca6ff0adebc-2 create mode 100644 dtls-2.0.9/fuzz/corpus/71d40c1aa2131c7936b49cfb92ea2a60da15e44e-1 create mode 100644 dtls-2.0.9/fuzz/corpus/7384d4b5b89a95ef3448cd2d9bd5f9001592f83a-37 create mode 100644 dtls-2.0.9/fuzz/corpus/7428fe79252cf44624d39a9ee721ff169c2017ba-18 create mode 100644 dtls-2.0.9/fuzz/corpus/75ab7aa686d0774f43a13c218b33528b2fe7d5f8-29 create mode 100644 dtls-2.0.9/fuzz/corpus/75e00d510635ac25c84a337514180b32b8a4051b-25 create mode 100644 dtls-2.0.9/fuzz/corpus/78183569973f5d7cf343bad7c8be1099e5c09b88-7 create mode 100644 dtls-2.0.9/fuzz/corpus/781d2e38644a0fe53f8bfba8d567c206799a70f4-21 create mode 100644 dtls-2.0.9/fuzz/corpus/79e1a7733a2d329564a16763a6bb394dddcd5679-14 create mode 100644 dtls-2.0.9/fuzz/corpus/7a459efd01415c7e35c8ae63358fc79e2d471093-35 create mode 100644 dtls-2.0.9/fuzz/corpus/7b71e27c7ca6777b3eb1c03bf2bbfb91720186c1-5 create mode 100644 dtls-2.0.9/fuzz/corpus/7c33a04f1cb9a7b2bf6be6f834aeeef943a242f2-34 create mode 100644 dtls-2.0.9/fuzz/corpus/7c33caa83f291ca5a328d13e1d97954d9462e0e1-34 create mode 100644 dtls-2.0.9/fuzz/corpus/7c60d79ccb4b24c486293fe63c763f71c2948d33-28 create mode 100644 dtls-2.0.9/fuzz/corpus/7c9e5840e53826d82da4432b52c057bfbcd2c8f6-31 create mode 100644 dtls-2.0.9/fuzz/corpus/80123a693544437c5d58878cf7aac8a281ec658c-8 create mode 100644 dtls-2.0.9/fuzz/corpus/8104833886e77f44f198916bdf2cc0aeafa6b59a-30 create mode 100644 dtls-2.0.9/fuzz/corpus/83c3e7679df8b6e6cbb75de23ef0e0c9d400a434-1 create mode 100644 dtls-2.0.9/fuzz/corpus/843ccb2f577d368fe0e793d0047311bac2b02afb-10 create mode 100644 dtls-2.0.9/fuzz/corpus/88e0f2195b7c21004d87538f58bd7b751aeb79c7-27 create mode 100644 dtls-2.0.9/fuzz/corpus/8963740cfedced726a1579328b9aa58a7d348c2c-29 create mode 100644 dtls-2.0.9/fuzz/corpus/905578265b19677b3c83aad3169ed0b9cae91a0f-20 create mode 100644 dtls-2.0.9/fuzz/corpus/91ad828e4650d737c8fab0447f83b6380bb045a2-37 create mode 100644 dtls-2.0.9/fuzz/corpus/92d652cb10701472585fadb89dee2ab05f4baa3f-16 create mode 100644 dtls-2.0.9/fuzz/corpus/9810ba71e7068b2752d4fc80ea1071957b4b20e4-22 create mode 100644 dtls-2.0.9/fuzz/corpus/986aa2d13d0f60c614ca328c0b38a7d533b952fb-15 create mode 100644 dtls-2.0.9/fuzz/corpus/98779dbfa7f25f57d8bc146d8c37d4a1f1b829a7-10 create mode 100644 dtls-2.0.9/fuzz/corpus/995d8ae8db6dad3c5851077207ada893bf856830-25 create mode 100644 dtls-2.0.9/fuzz/corpus/9a6736cde6de5b473fb231535380a7617fd640c2-10 create mode 100644 dtls-2.0.9/fuzz/corpus/9aeb1efeb489adc9aec522039bba0a5f693271bf-35 create mode 100644 dtls-2.0.9/fuzz/corpus/9bc991375786a265c38c8553183807be67827625-18 create mode 100644 dtls-2.0.9/fuzz/corpus/9bddfbdd2ed2e780103d5d34662106bd4ef8eb80-6 create mode 100644 dtls-2.0.9/fuzz/corpus/9beed258dfb4aa4ef102c1b4984699303e737d00-38 create mode 100644 dtls-2.0.9/fuzz/corpus/9d31063b355084a0a074f614a6b9279a25a4537e-35 create mode 100644 dtls-2.0.9/fuzz/corpus/9da74f96fe6f8dc2fdf340eec67662301a14086e-10 create mode 100644 dtls-2.0.9/fuzz/corpus/9e0739e12c765ba14c8540a32f5a8252bebc6fad-7 create mode 100644 dtls-2.0.9/fuzz/corpus/9e8cb1ca388740d90a5337a85d48c78d93d96580-12 create mode 100644 dtls-2.0.9/fuzz/corpus/9f9c6abc185820375cdc3c63a52cca2cdc84946b-26 create mode 100644 dtls-2.0.9/fuzz/corpus/a067dbf437d8e235dc64a6819faa0d57ff2c3f94-21 create mode 100644 dtls-2.0.9/fuzz/corpus/a0a9328cec82f33420fed388ac10108c5f365847-31 create mode 100644 dtls-2.0.9/fuzz/corpus/a2ef40165d921e7d8b8c622348b0f3ba772bb45b-22 create mode 100644 dtls-2.0.9/fuzz/corpus/a54fc076b4362b89692c19a60cf0a19a8c025ea0-19 create mode 100644 dtls-2.0.9/fuzz/corpus/a57426e5962baf2af3c43bfba8bcfe8198aeac69-21 create mode 100644 dtls-2.0.9/fuzz/corpus/a646db15452695437f7b7bc3b65c5748dd9cbee4-36 create mode 100644 dtls-2.0.9/fuzz/corpus/a76d4d5e1300a60dd945d28fd5fe2c9968f06871-6 create mode 100644 dtls-2.0.9/fuzz/corpus/a8208daf57a7ba1b8f75ef0a70421d16100668d8-22 create mode 100644 dtls-2.0.9/fuzz/corpus/a8e636f3b54cfd873b3d21cff150543a9e10f4de-13 create mode 100644 dtls-2.0.9/fuzz/corpus/aa8beeff31520b5cbf509bc5efe4fa194a990fed-31 create mode 100644 dtls-2.0.9/fuzz/corpus/ac3e9b5146d2220644dbca14d2dec64d23a82fd6-24 create mode 100644 dtls-2.0.9/fuzz/corpus/ad572827912f2c8b62392a1481af8897837d9b08-25 create mode 100644 dtls-2.0.9/fuzz/corpus/afd532a8a55c6c39d9ca66231a96a5678fbe4ad2-27 create mode 100644 dtls-2.0.9/fuzz/corpus/b0f5f4a2d196cded1dbfa87ab65be7122effa0e3-8 create mode 100644 dtls-2.0.9/fuzz/corpus/b226a622228f89f8a6f98b6b09f06fa964a3d4f0-9 create mode 100644 dtls-2.0.9/fuzz/corpus/b28051b6fc87a2b74a765b237c697e0728f1bccf-12 create mode 100644 dtls-2.0.9/fuzz/corpus/b3c74f6100a87eb3ad15d44be8df465d490fb9bd-32 create mode 100644 dtls-2.0.9/fuzz/corpus/b43bde2b9ea6f9d171156e4ba3d084444294625c-6 create mode 100644 dtls-2.0.9/fuzz/corpus/b485961b2eb34df99b22d66f377aeaf6bd87e0a6-36 create mode 100644 dtls-2.0.9/fuzz/corpus/b4912597376e6edf2985267fe64d170977173481-1 create mode 100644 dtls-2.0.9/fuzz/corpus/b4ee5c1737fe829bfa1c8d6abcb2166c1b74effd-21 create mode 100644 dtls-2.0.9/fuzz/corpus/b4f24eee8a1d42ac1dc868e4d53b608f3746a2d7-31 create mode 100644 dtls-2.0.9/fuzz/corpus/b5c30ace1906dea8c5cf2fb4b7558563a2df978b-19 create mode 100644 dtls-2.0.9/fuzz/corpus/b64aecc1f27577b6c2efd550a8dd1b0f96054f7c-25 create mode 100644 dtls-2.0.9/fuzz/corpus/b6f83f0c490f9fbea7ea7b9574232e8fd90194aa-18 create mode 100644 dtls-2.0.9/fuzz/corpus/b7b653694d804d41294e46bb4aff34f2fc93f48d-19 create mode 100644 dtls-2.0.9/fuzz/corpus/b9bd6d81380956a8a8f08c551f7a1c8e4b769f01-33 create mode 100644 dtls-2.0.9/fuzz/corpus/ba0aeff9d6e84d6d0a54b40f674338489fe86d29-35 create mode 100644 dtls-2.0.9/fuzz/corpus/ba8f7331369766ec42d305afd13f74bd5c9f7598-26 create mode 100644 dtls-2.0.9/fuzz/corpus/bab42319f9d989d1344ff4621f82c3eb950f01b8-4 create mode 100644 dtls-2.0.9/fuzz/corpus/bacecfa089ed936799b5ec00ab80f2c234ee6488-19 create mode 100644 dtls-2.0.9/fuzz/corpus/bdd08d152c9b526d07ca2020b5236ee2021ddbf2-9 create mode 100644 dtls-2.0.9/fuzz/corpus/be2d2ac22a22f3c07bbb03881145ed09d71cc9a3-23 create mode 100644 dtls-2.0.9/fuzz/corpus/c26326aa05dea63170e6429a64465e9c48fc4ba6-20 create mode 100644 dtls-2.0.9/fuzz/corpus/c341f33f77b845bbeb7f2e4cdc20072a370b81bb-19 create mode 100644 dtls-2.0.9/fuzz/corpus/c5ab6cb91cad5d95c1eed18fc9055ca5cfa03401-36 create mode 100644 dtls-2.0.9/fuzz/corpus/c6266582478c713d071415c5c20f7e17cacbca6b-11 create mode 100644 dtls-2.0.9/fuzz/corpus/c69ac8b1c87631059129edfb2bac5504b1f6e1fe-7 create mode 100644 dtls-2.0.9/fuzz/corpus/c6fb60ed7606c773c6e381e1eeafa4d2beb0501d-13 create mode 100644 dtls-2.0.9/fuzz/corpus/c6ff571fac3824ce6314d936ddbe679a4532681a-24 create mode 100644 dtls-2.0.9/fuzz/corpus/ca7e5b747b90d4cc886c3e68582eb809672f9343-24 create mode 100644 dtls-2.0.9/fuzz/corpus/caf20a50754c9f4885ff4872cfdb5badfafa0eab-2 create mode 100644 dtls-2.0.9/fuzz/corpus/cc0dfdb3fe2c6c450c8353fb951f0068c2da25c3-24 create mode 100644 dtls-2.0.9/fuzz/corpus/cc57cf224581b2055e3e509f8ddaf10204099d72-29 create mode 100644 dtls-2.0.9/fuzz/corpus/cd6fd1f976ae2f9e31733919f070988d5946cf18-25 create mode 100644 dtls-2.0.9/fuzz/corpus/ce04f52927639b8f845dd01a25ff06d61dbb7736-19 create mode 100644 dtls-2.0.9/fuzz/corpus/cfe9539fdc29f9bcdf123394ffb098838a5d8b83-29 create mode 100644 dtls-2.0.9/fuzz/corpus/d093b42b65836218cc0ce0ad9a898b76f4cde121-7 create mode 100644 dtls-2.0.9/fuzz/corpus/d184e74d92444b23e5c07431ac1901a3460efeef-2 create mode 100644 dtls-2.0.9/fuzz/corpus/d1fc43b23d31daa77b1c9b4f8930d2f3a9754287-31 create mode 100644 dtls-2.0.9/fuzz/corpus/d5e8de475ba87d0eddd97db6b61ef4621a2e8071-30 create mode 100644 dtls-2.0.9/fuzz/corpus/d78ab9295d2782c20cb99674622bde4e92359b16-15 create mode 100644 dtls-2.0.9/fuzz/corpus/d8f3a31fb0304017eb8466e958c843865a1d0c2b-14 create mode 100644 dtls-2.0.9/fuzz/corpus/d97dc4bb804a0d7bcd92f1abf81fb604caeef3db-18 create mode 100644 dtls-2.0.9/fuzz/corpus/d9c2f5fc766a4d8b70c20b2c7bb17f662821a18d-20 create mode 100644 dtls-2.0.9/fuzz/corpus/da39a3ee5e6b4b0d3255bfef95601890afd80709 create mode 100644 dtls-2.0.9/fuzz/corpus/da75745263fae25217790f4c0f3414a2c2a7426c-30 create mode 100644 dtls-2.0.9/fuzz/corpus/dbb83f9c44304f536e5817c4301fe1ebad40b480-29 create mode 100644 dtls-2.0.9/fuzz/corpus/dc8dd2c7a89d009af1cc9d1dab9c7f030db09fee-28 create mode 100644 dtls-2.0.9/fuzz/corpus/dcac1a5ba7d6511532589fbceb771fd71f23ebeb-23 create mode 100644 dtls-2.0.9/fuzz/corpus/dcc90b5ab9129ee3effd438c0a86bfe599ccfe17-8 create mode 100644 dtls-2.0.9/fuzz/corpus/dd5c198fd08276fdba3f48884659199dceeaa2ac-2 create mode 100644 dtls-2.0.9/fuzz/corpus/e0d111660feb6004db7815eb0231fdb369517970-11 create mode 100644 dtls-2.0.9/fuzz/corpus/e11fc30ee640e45e8185f384f9a116cf2cb75852-8 create mode 100644 dtls-2.0.9/fuzz/corpus/e124a66686755a3fe635b2bb6dc05849238ff474-28 create mode 100644 dtls-2.0.9/fuzz/corpus/e17dcda547abfa37685bb9d570a7bf9c4a34affc-35 create mode 100644 dtls-2.0.9/fuzz/corpus/e1a87e2698fcd50fdee9d425ba22cca94e82e689-31 create mode 100644 dtls-2.0.9/fuzz/corpus/e1f7de47792fed4f34a0a790cc688d43d75e80fd-34 create mode 100644 dtls-2.0.9/fuzz/corpus/e4d81d83c175232de004db3750b8509a3dc26cf7-27 create mode 100644 dtls-2.0.9/fuzz/corpus/e5d083d83bb534c47f170509f84be51d847c9d95-2 create mode 100644 dtls-2.0.9/fuzz/corpus/e69a5e78519e11f948112f68197d2f0d469c60b2-28 create mode 100644 dtls-2.0.9/fuzz/corpus/e7e0aec1e8718877cd61405d0b73cb8eea7830dd-2 create mode 100644 dtls-2.0.9/fuzz/corpus/e87d088c1b0796bcbfa649c9118329bf4fabd6f2-29 create mode 100644 dtls-2.0.9/fuzz/corpus/e8ad70294942e6f8c25bb01fd4443cfba4fb0308-19 create mode 100644 dtls-2.0.9/fuzz/corpus/e9895a39481476548887cbbb88835ba4318e41af-33 create mode 100644 dtls-2.0.9/fuzz/corpus/e9f3d28570e1c59dd81975f281b00374ad3f400e-28 create mode 100644 dtls-2.0.9/fuzz/corpus/ea855f2d2933b53de04f93ed49d95f5fbc1777df-3 create mode 100644 dtls-2.0.9/fuzz/corpus/eaa0e2396b6d857d3121c691ca35c10f54644ba5-3 create mode 100644 dtls-2.0.9/fuzz/corpus/eab6d99255628b1b14f5f565e9f94e9f4042ba25-5 create mode 100644 dtls-2.0.9/fuzz/corpus/ed8baf884f660e13648b822dbc20c23ececbb6d9-14 create mode 100644 dtls-2.0.9/fuzz/corpus/ee7bc26e98a2e3fc02a8fac80ec94b8fe56d5852-26 create mode 100644 dtls-2.0.9/fuzz/corpus/f248a7b971b1fd07ea978e776fda73fee276d36d-17 create mode 100644 dtls-2.0.9/fuzz/corpus/f3fc999fcd5f3f9f4d4cf2c4151d0bc6ef73c3cb-1 create mode 100644 dtls-2.0.9/fuzz/corpus/f8781259866be1553ac9625d18ff25ce354776ec-23 create mode 100644 dtls-2.0.9/fuzz/corpus/fa9ec5dd9ba00a696cb5217fd7455fe79c6610e4-18 create mode 100644 dtls-2.0.9/fuzz/corpus/fc3952e202a374d090fd4008d43183630a4b8dc2-15 create mode 100644 dtls-2.0.9/fuzz/corpus/fdef7b51eb11668569ef1b45ba193becb956b2e7-15 create mode 100644 dtls-2.0.9/go.mod create mode 100644 dtls-2.0.9/go.sum create mode 100644 dtls-2.0.9/handshake_cache.go create mode 100644 dtls-2.0.9/handshake_cache_test.go create mode 100644 dtls-2.0.9/handshake_test.go create mode 100644 dtls-2.0.9/handshaker.go create mode 100644 dtls-2.0.9/handshaker_test.go create mode 100644 dtls-2.0.9/internal/ciphersuite/aes_128_ccm.go create mode 100644 dtls-2.0.9/internal/ciphersuite/ciphersuite.go create mode 100644 dtls-2.0.9/internal/ciphersuite/tls_ecdhe_ecdsa_with_aes_128_ccm.go create mode 100644 dtls-2.0.9/internal/ciphersuite/tls_ecdhe_ecdsa_with_aes_128_ccm8.go create mode 100644 dtls-2.0.9/internal/ciphersuite/tls_ecdhe_ecdsa_with_aes_128_gcm_sha256.go create mode 100644 dtls-2.0.9/internal/ciphersuite/tls_ecdhe_ecdsa_with_aes_256_cbc_sha.go create mode 100644 dtls-2.0.9/internal/ciphersuite/tls_ecdhe_rsa_with_aes_128_gcm_sha256.go create mode 100644 dtls-2.0.9/internal/ciphersuite/tls_ecdhe_rsa_with_aes_256_cbc_sha.go create mode 100644 dtls-2.0.9/internal/ciphersuite/tls_psk_with_aes_128_cbc_sha256.go create mode 100644 dtls-2.0.9/internal/ciphersuite/tls_psk_with_aes_128_ccm.go create mode 100644 dtls-2.0.9/internal/ciphersuite/tls_psk_with_aes_128_ccm8.go create mode 100644 dtls-2.0.9/internal/ciphersuite/tls_psk_with_aes_128_gcm_sha256.go create mode 100644 dtls-2.0.9/internal/closer/closer.go create mode 100644 dtls-2.0.9/internal/net/dpipe/dpipe.go create mode 100644 dtls-2.0.9/internal/net/dpipe/dpipe_test.go create mode 100644 dtls-2.0.9/internal/util/util.go create mode 100644 dtls-2.0.9/listener.go create mode 100644 dtls-2.0.9/nettest_test.go create mode 100644 dtls-2.0.9/packet.go create mode 100644 dtls-2.0.9/pkg/crypto/ccm/ccm.go create mode 100644 dtls-2.0.9/pkg/crypto/ccm/ccm_test.go create mode 100644 dtls-2.0.9/pkg/crypto/ciphersuite/cbc.go create mode 100644 dtls-2.0.9/pkg/crypto/ciphersuite/ccm.go create mode 100644 dtls-2.0.9/pkg/crypto/ciphersuite/ciphersuite.go create mode 100644 dtls-2.0.9/pkg/crypto/ciphersuite/gcm.go create mode 100644 dtls-2.0.9/pkg/crypto/clientcertificate/client_certificate.go create mode 100644 dtls-2.0.9/pkg/crypto/elliptic/elliptic.go create mode 100644 dtls-2.0.9/pkg/crypto/fingerprint/fingerprint.go create mode 100644 dtls-2.0.9/pkg/crypto/fingerprint/fingerprint_test.go create mode 100644 dtls-2.0.9/pkg/crypto/fingerprint/hash.go create mode 100644 dtls-2.0.9/pkg/crypto/fingerprint/hash_test.go create mode 100644 dtls-2.0.9/pkg/crypto/hash/hash.go create mode 100644 dtls-2.0.9/pkg/crypto/hash/hash_test.go create mode 100644 dtls-2.0.9/pkg/crypto/prf/prf.go create mode 100644 dtls-2.0.9/pkg/crypto/prf/prf_test.go create mode 100644 dtls-2.0.9/pkg/crypto/selfsign/selfsign.go create mode 100644 dtls-2.0.9/pkg/crypto/signature/signature.go create mode 100644 dtls-2.0.9/pkg/crypto/signaturehash/errors.go create mode 100644 dtls-2.0.9/pkg/crypto/signaturehash/signaturehash.go create mode 100644 dtls-2.0.9/pkg/crypto/signaturehash/signaturehash_go113_test.go create mode 100644 dtls-2.0.9/pkg/crypto/signaturehash/signaturehash_test.go create mode 100644 dtls-2.0.9/pkg/protocol/alert/alert.go create mode 100644 dtls-2.0.9/pkg/protocol/alert/alert_test.go create mode 100644 dtls-2.0.9/pkg/protocol/application_data.go create mode 100644 dtls-2.0.9/pkg/protocol/change_cipher_spec.go create mode 100644 dtls-2.0.9/pkg/protocol/change_cipher_spec_test.go create mode 100644 dtls-2.0.9/pkg/protocol/compression_method.go create mode 100644 dtls-2.0.9/pkg/protocol/compression_method_test.go create mode 100644 dtls-2.0.9/pkg/protocol/content.go create mode 100644 dtls-2.0.9/pkg/protocol/errors.go create mode 100644 dtls-2.0.9/pkg/protocol/extension/errors.go create mode 100644 dtls-2.0.9/pkg/protocol/extension/extension.go create mode 100644 dtls-2.0.9/pkg/protocol/extension/extension_test.go create mode 100644 dtls-2.0.9/pkg/protocol/extension/renegotiation_info.go create mode 100644 dtls-2.0.9/pkg/protocol/extension/renegotiation_info_test.go create mode 100644 dtls-2.0.9/pkg/protocol/extension/server_name.go create mode 100644 dtls-2.0.9/pkg/protocol/extension/server_name_test.go create mode 100644 dtls-2.0.9/pkg/protocol/extension/srtp_protection_profile.go create mode 100644 dtls-2.0.9/pkg/protocol/extension/supported_elliptic_curves.go create mode 100644 dtls-2.0.9/pkg/protocol/extension/supported_elliptic_curves_test.go create mode 100644 dtls-2.0.9/pkg/protocol/extension/supported_point_formats.go create mode 100644 dtls-2.0.9/pkg/protocol/extension/supported_point_formats_test.go create mode 100644 dtls-2.0.9/pkg/protocol/extension/supported_signature_algorithms.go create mode 100644 dtls-2.0.9/pkg/protocol/extension/supported_signature_algorithms_test.go create mode 100644 dtls-2.0.9/pkg/protocol/extension/use_master_secret.go create mode 100644 dtls-2.0.9/pkg/protocol/extension/use_srtp.go create mode 100644 dtls-2.0.9/pkg/protocol/extension/use_srtp_test.go create mode 100644 dtls-2.0.9/pkg/protocol/handshake/cipher_suite.go create mode 100644 dtls-2.0.9/pkg/protocol/handshake/cipher_suite_test.go create mode 100644 dtls-2.0.9/pkg/protocol/handshake/errors.go create mode 100644 dtls-2.0.9/pkg/protocol/handshake/handshake.go create mode 100644 dtls-2.0.9/pkg/protocol/handshake/header.go create mode 100644 dtls-2.0.9/pkg/protocol/handshake/message_certificate.go create mode 100644 dtls-2.0.9/pkg/protocol/handshake/message_certificate_request.go create mode 100644 dtls-2.0.9/pkg/protocol/handshake/message_certificate_request_test.go create mode 100644 dtls-2.0.9/pkg/protocol/handshake/message_certificate_test.go create mode 100644 dtls-2.0.9/pkg/protocol/handshake/message_certificate_verify.go create mode 100644 dtls-2.0.9/pkg/protocol/handshake/message_certificate_verify_test.go create mode 100644 dtls-2.0.9/pkg/protocol/handshake/message_client_hello.go create mode 100644 dtls-2.0.9/pkg/protocol/handshake/message_client_hello_test.go create mode 100644 dtls-2.0.9/pkg/protocol/handshake/message_client_key_exchange.go create mode 100644 dtls-2.0.9/pkg/protocol/handshake/message_client_key_exchange_test.go create mode 100644 dtls-2.0.9/pkg/protocol/handshake/message_finished.go create mode 100644 dtls-2.0.9/pkg/protocol/handshake/message_finished_test.go create mode 100644 dtls-2.0.9/pkg/protocol/handshake/message_hello_verify_request.go create mode 100644 dtls-2.0.9/pkg/protocol/handshake/message_hello_verify_request_test.go create mode 100644 dtls-2.0.9/pkg/protocol/handshake/message_server_hello.go create mode 100644 dtls-2.0.9/pkg/protocol/handshake/message_server_hello_done.go create mode 100644 dtls-2.0.9/pkg/protocol/handshake/message_server_hello_done_test.go create mode 100644 dtls-2.0.9/pkg/protocol/handshake/message_server_hello_test.go create mode 100644 dtls-2.0.9/pkg/protocol/handshake/message_server_key_exchange.go create mode 100644 dtls-2.0.9/pkg/protocol/handshake/message_server_key_exchange_test.go create mode 100644 dtls-2.0.9/pkg/protocol/handshake/random.go create mode 100644 dtls-2.0.9/pkg/protocol/recordlayer/errors.go create mode 100644 dtls-2.0.9/pkg/protocol/recordlayer/header.go create mode 100644 dtls-2.0.9/pkg/protocol/recordlayer/recordlayer.go create mode 100644 dtls-2.0.9/pkg/protocol/recordlayer/recordlayer_test.go create mode 100644 dtls-2.0.9/pkg/protocol/version.go create mode 100644 dtls-2.0.9/renovate.json create mode 100644 dtls-2.0.9/replayprotection_test.go create mode 100644 dtls-2.0.9/resume.go create mode 100644 dtls-2.0.9/resume_test.go create mode 100644 dtls-2.0.9/srtp_protection_profile.go create mode 100644 dtls-2.0.9/state.go create mode 100644 dtls-2.0.9/util.go diff --git a/dtls-2.0.9/.editorconfig b/dtls-2.0.9/.editorconfig new file mode 100644 index 0000000..d2b3206 --- /dev/null +++ b/dtls-2.0.9/.editorconfig @@ -0,0 +1,21 @@ +# http://editorconfig.org/ + +root = true + +[*] +charset = utf-8 +insert_final_newline = true +trim_trailing_whitespace = true +end_of_line = lf + +[*.go] +indent_style = tab +indent_size = 4 + +[{*.yml,*.yaml}] +indent_style = space +indent_size = 2 + +# Makefiles always use tabs for indentation +[Makefile] +indent_style = tab diff --git a/dtls-2.0.9/.github/assert-contributors.sh b/dtls-2.0.9/.github/assert-contributors.sh new file mode 100644 index 0000000..12e6afe --- /dev/null +++ b/dtls-2.0.9/.github/assert-contributors.sh @@ -0,0 +1,61 @@ +#!/usr/bin/env bash + +# +# DO NOT EDIT THIS FILE +# +# It is automatically copied from https://github.com/pion/.goassets repository. +# +# If you want to update the shared CI config, send a PR to +# https://github.com/pion/.goassets instead of this repository. +# + +set -e + +SCRIPT_PATH=$( cd "$(dirname "${BASH_SOURCE[0]}")" ; pwd -P ) + +if [ -f ${SCRIPT_PATH}/.ci.conf ] +then + . ${SCRIPT_PATH}/.ci.conf +fi + +# +# DO NOT EDIT THIS +# +EXCLUDED_CONTRIBUTORS+=('John R. Bradley' 'renovate[bot]' 'Renovate Bot' 'Pion Bot') +# If you want to exclude a name from all repositories, send a PR to +# https://github.com/pion/.goassets instead of this repository. +# If you want to exclude a name only from this repository, +# add EXCLUDED_CONTRIBUTORS=('name') to .github/.ci.conf + +MISSING_CONTRIBUTORS=() + +shouldBeIncluded () { + for i in "${EXCLUDED_CONTRIBUTORS[@]}" + do + if [ "$i" == "$1" ] ; then + return 1 + fi + done + return 0 +} + + +IFS=$'\n' #Only split on newline +for contributor in $(git log --format='%aN' | sort -u) +do + if shouldBeIncluded $contributor; then + if ! grep -q "$contributor" "$SCRIPT_PATH/../README.md"; then + MISSING_CONTRIBUTORS+=("$contributor") + fi + fi +done +unset IFS + +if [ ${#MISSING_CONTRIBUTORS[@]} -ne 0 ]; then + echo "Please add the following contributors to the README" + for i in "${MISSING_CONTRIBUTORS[@]}" + do + echo "$i" + done + exit 1 +fi diff --git a/dtls-2.0.9/.github/hooks/commit-msg.sh b/dtls-2.0.9/.github/hooks/commit-msg.sh new file mode 100644 index 0000000..8213dc2 --- /dev/null +++ b/dtls-2.0.9/.github/hooks/commit-msg.sh @@ -0,0 +1,11 @@ +#!/usr/bin/env bash + +# +# DO NOT EDIT THIS FILE DIRECTLY +# +# It is automatically copied from https://github.com/pion/.goassets repository. +# + +set -e + +.github/lint-commit-message.sh $1 diff --git a/dtls-2.0.9/.github/hooks/pre-commit.sh b/dtls-2.0.9/.github/hooks/pre-commit.sh new file mode 100644 index 0000000..cc318d7 --- /dev/null +++ b/dtls-2.0.9/.github/hooks/pre-commit.sh @@ -0,0 +1,12 @@ +#!/bin/sh + +# +# DO NOT EDIT THIS FILE DIRECTLY +# +# It is automatically copied from https://github.com/pion/.goassets repository. +# + +# Redirect output to stderr. +exec 1>&2 + +.github/lint-disallowed-functions-in-library.sh diff --git a/dtls-2.0.9/.github/hooks/pre-push.sh b/dtls-2.0.9/.github/hooks/pre-push.sh new file mode 100644 index 0000000..7cb2365 --- /dev/null +++ b/dtls-2.0.9/.github/hooks/pre-push.sh @@ -0,0 +1,13 @@ +#!/bin/sh + +# +# DO NOT EDIT THIS FILE DIRECTLY +# +# It is automatically copied from https://github.com/pion/.goassets repository. +# + +set -e + +.github/assert-contributors.sh + +exit 0 diff --git a/dtls-2.0.9/.github/install-hooks.sh b/dtls-2.0.9/.github/install-hooks.sh new file mode 100644 index 0000000..73d20a4 --- /dev/null +++ b/dtls-2.0.9/.github/install-hooks.sh @@ -0,0 +1,16 @@ +#!/bin/bash + +# +# DO NOT EDIT THIS FILE +# +# It is automatically copied from https://github.com/pion/.goassets repository. +# +# If you want to update the shared CI config, send a PR to +# https://github.com/pion/.goassets instead of this repository. +# + +SCRIPT_PATH=$( cd "$(dirname "${BASH_SOURCE[0]}")" ; pwd -P ) + +cp "$SCRIPT_PATH/hooks/commit-msg.sh" "$SCRIPT_PATH/../.git/hooks/commit-msg" +cp "$SCRIPT_PATH/hooks/pre-commit.sh" "$SCRIPT_PATH/../.git/hooks/pre-commit" +cp "$SCRIPT_PATH/hooks/pre-push.sh" "$SCRIPT_PATH/../.git/hooks/pre-push" diff --git a/dtls-2.0.9/.github/lint-commit-message.sh b/dtls-2.0.9/.github/lint-commit-message.sh new file mode 100644 index 0000000..010a332 --- /dev/null +++ b/dtls-2.0.9/.github/lint-commit-message.sh @@ -0,0 +1,64 @@ +#!/usr/bin/env bash + +# +# DO NOT EDIT THIS FILE +# +# It is automatically copied from https://github.com/pion/.goassets repository. +# +# If you want to update the shared CI config, send a PR to +# https://github.com/pion/.goassets instead of this repository. +# + +set -e + +display_commit_message_error() { +cat << EndOfMessage +$1 + +------------------------------------------------- +The preceding commit message is invalid +it failed '$2' of the following checks + +* Separate subject from body with a blank line +* Limit the subject line to 50 characters +* Capitalize the subject line +* Do not end the subject line with a period +* Wrap the body at 72 characters +EndOfMessage + + exit 1 +} + +lint_commit_message() { + if [[ "$(echo "$1" | awk 'NR == 2 {print $1;}' | wc -c)" -ne 1 ]]; then + display_commit_message_error "$1" 'Separate subject from body with a blank line' + fi + + if [[ "$(echo "$1" | head -n1 | awk '{print length}')" -gt 50 ]]; then + display_commit_message_error "$1" 'Limit the subject line to 50 characters' + fi + + if [[ ! $1 =~ ^[A-Z] ]]; then + display_commit_message_error "$1" 'Capitalize the subject line' + fi + + if [[ "$(echo "$1" | awk 'NR == 1 {print substr($0,length($0),1)}')" == "." ]]; then + display_commit_message_error "$1" 'Do not end the subject line with a period' + fi + + if [[ "$(echo "$1" | awk '{print length}' | sort -nr | head -1)" -gt 72 ]]; then + display_commit_message_error "$1" 'Wrap the body at 72 characters' + fi +} + +if [ "$#" -eq 1 ]; then + if [ ! -f "$1" ]; then + echo "$0 was passed one argument, but was not a valid file" + exit 1 + fi + lint_commit_message "$(sed -n '/# Please enter the commit message for your changes. Lines starting/q;p' "$1")" +else + for commit in $(git rev-list --no-merges origin/master..); do + lint_commit_message "$(git log --format="%B" -n 1 $commit)" + done +fi diff --git a/dtls-2.0.9/.github/lint-disallowed-functions-in-library.sh b/dtls-2.0.9/.github/lint-disallowed-functions-in-library.sh new file mode 100644 index 0000000..21e48da --- /dev/null +++ b/dtls-2.0.9/.github/lint-disallowed-functions-in-library.sh @@ -0,0 +1,48 @@ +#!/usr/bin/env bash + +# +# DO NOT EDIT THIS FILE +# +# It is automatically copied from https://github.com/pion/.goassets repository. +# +# If you want to update the shared CI config, send a PR to +# https://github.com/pion/.goassets instead of this repository. +# + +set -e + +# Disallow usages of functions that cause the program to exit in the library code +SCRIPT_PATH=$( cd "$(dirname "${BASH_SOURCE[0]}")" ; pwd -P ) +if [ -f ${SCRIPT_PATH}/.ci.conf ] +then + . ${SCRIPT_PATH}/.ci.conf +fi + +EXCLUDE_DIRECTORIES=${DISALLOWED_FUNCTIONS_EXCLUDED_DIRECTORIES:-"examples"} +DISALLOWED_FUNCTIONS=('os.Exit(' 'panic(' 'Fatal(' 'Fatalf(' 'Fatalln(' 'fmt.Println(' 'fmt.Printf(' 'log.Print(' 'log.Println(' 'log.Printf(') + +files=$( + find "$SCRIPT_PATH/.." -name "*.go" \ + | grep -v -e '^.*_test.go$' \ + | while read file + do + excluded=false + for ex in $EXCLUDE_DIRECTORIES + do + if [[ $file == */$ex/* ]] + then + excluded=true + break + fi + done + $excluded || echo "$file" + done +) + +for disallowedFunction in "${DISALLOWED_FUNCTIONS[@]}" +do + if grep -e "$disallowedFunction" $files | grep -v -e 'nolint'; then + echo "$disallowedFunction may only be used in example code" + exit 1 + fi +done diff --git a/dtls-2.0.9/.github/lint-filename.sh b/dtls-2.0.9/.github/lint-filename.sh new file mode 100644 index 0000000..81b3f14 --- /dev/null +++ b/dtls-2.0.9/.github/lint-filename.sh @@ -0,0 +1,24 @@ +#!/usr/bin/env bash + +# +# DO NOT EDIT THIS FILE +# +# It is automatically copied from https://github.com/pion/.goassets repository. +# +# If you want to update the shared CI config, send a PR to +# https://github.com/pion/.goassets instead of this repository. +# + +set -e + +SCRIPT_PATH=$( cd "$(dirname "${BASH_SOURCE[0]}")" ; pwd -P ) +GO_REGEX="^[a-zA-Z][a-zA-Z0-9_]*\.go$" + +find "$SCRIPT_PATH/.." -name "*.go" | while read fullpath; do + filename=$(basename -- "$fullpath") + + if ! [[ $filename =~ $GO_REGEX ]]; then + echo "$filename is not a valid filename for Go code, only alpha, numbers and underscores are supported" + exit 1 + fi +done diff --git a/dtls-2.0.9/.github/workflows/e2e.yaml b/dtls-2.0.9/.github/workflows/e2e.yaml new file mode 100644 index 0000000..c6b4cf4 --- /dev/null +++ b/dtls-2.0.9/.github/workflows/e2e.yaml @@ -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 diff --git a/dtls-2.0.9/.github/workflows/lint.yaml b/dtls-2.0.9/.github/workflows/lint.yaml new file mode 100644 index 0000000..8824c34 --- /dev/null +++ b/dtls-2.0.9/.github/workflows/lint.yaml @@ -0,0 +1,43 @@ +name: Lint +on: + pull_request: + types: + - opened + - edited + - synchronize +jobs: + lint-commit-message: + name: Metadata + runs-on: ubuntu-latest + strategy: + fail-fast: false + steps: + - uses: actions/checkout@v2 + with: + fetch-depth: 0 + + - name: Commit Message + run: .github/lint-commit-message.sh + + - name: File names + run: .github/lint-filename.sh + + - name: Contributors + run: .github/assert-contributors.sh + + - name: Functions + run: .github/lint-disallowed-functions-in-library.sh + + lint-go: + name: Go + runs-on: ubuntu-latest + strategy: + fail-fast: false + steps: + - uses: actions/checkout@v2 + + - name: golangci-lint + uses: golangci/golangci-lint-action@v2 + with: + version: v1.31 + args: $GOLANGCI_LINT_EXRA_ARGS diff --git a/dtls-2.0.9/.github/workflows/renovate-go-mod-fix.yaml b/dtls-2.0.9/.github/workflows/renovate-go-mod-fix.yaml new file mode 100644 index 0000000..46d2d04 --- /dev/null +++ b/dtls-2.0.9/.github/workflows/renovate-go-mod-fix.yaml @@ -0,0 +1,33 @@ +# +# DO NOT EDIT THIS FILE +# +# It is automatically copied from https://github.com/pion/.goassets repository. +# If this repository should have package specific CI config, +# remove the repository name from .goassets/.github/workflows/assets-sync.yml. +# +# If you want to update the shared CI config, send a PR to +# https://github.com/pion/.goassets instead of this repository. +# + +name: go-mod-fix +on: + push: + branches: + - renovate/* + +jobs: + go-mod-fix: + runs-on: ubuntu-latest + steps: + - name: checkout + uses: actions/checkout@v2 + with: + fetch-depth: 2 + - name: fix + uses: at-wat/go-sum-fix-action@v0 + with: + git_user: Pion Bot + git_email: 59523206+pionbot@users.noreply.github.com + github_token: ${{ secrets.PIONBOT_PRIVATE_KEY }} + commit_style: squash + push: force diff --git a/dtls-2.0.9/.github/workflows/test.yaml b/dtls-2.0.9/.github/workflows/test.yaml new file mode 100644 index 0000000..5b7a43b --- /dev/null +++ b/dtls-2.0.9/.github/workflows/test.yaml @@ -0,0 +1,139 @@ +name: Test +on: + push: + branches: + - master + pull_request: + branches: + - master +jobs: + test: + runs-on: ubuntu-latest + strategy: + matrix: + go: ["1.15", "1.16"] + fail-fast: false + name: Go ${{ matrix.go }} + steps: + - uses: actions/checkout@v2 + + - uses: actions/cache@v2 + with: + path: | + ~/go/pkg/mod + ~/go/bin + ~/.cache + key: ${{ runner.os }}-amd64-go-${{ hashFiles('**/go.sum') }} + restore-keys: | + ${{ runner.os }}-amd64-go- + + - name: Setup Go + uses: actions/setup-go@v2 + with: + go-version: ${{ matrix.go }} + + - name: Setup go-acc + run: | + go get github.com/ory/go-acc + git checkout go.mod go.sum + + - name: Run test + run: | + go-acc -o cover.out ./... -- \ + -bench=. \ + -v -race + + - uses: codecov/codecov-action@v1 + with: + file: ./cover.out + name: codecov-umbrella + fail_ci_if_error: true + flags: go + + test-i386: + runs-on: ubuntu-latest + strategy: + matrix: + go: ["1.15", "1.16"] + fail-fast: false + name: Go i386 ${{ matrix.go }} + steps: + - uses: actions/checkout@v2 + + - uses: actions/cache@v2 + with: + path: | + ~/go/pkg/mod + ~/.cache + key: ${{ runner.os }}-i386-go-${{ hashFiles('**/go.sum') }} + restore-keys: | + ${{ runner.os }}-i386-go- + + - name: Run test + run: | + mkdir -p $HOME/go/pkg/mod $HOME/.cache + docker run \ + -u $(id -u):$(id -g) \ + -e "GO111MODULE=on" \ + -e "CGO_ENABLED=0" \ + -v $GITHUB_WORKSPACE:/go/src/github.com/pion/$(basename $GITHUB_WORKSPACE) \ + -v $HOME/go/pkg/mod:/go/pkg/mod \ + -v $HOME/.cache:/.cache \ + -w /go/src/github.com/pion/$(basename $GITHUB_WORKSPACE) \ + i386/golang:${{matrix.go}}-alpine \ + /usr/local/go/bin/go test \ + ${TEST_EXTRA_ARGS:-} \ + -v ./... + + test-wasm: + runs-on: ubuntu-latest + strategy: + fail-fast: false + name: WASM + steps: + - uses: actions/checkout@v2 + + - name: Use Node.js + uses: actions/setup-node@v2 + with: + node-version: '12.x' + + - uses: actions/cache@v2 + with: + path: | + ~/go/pkg/mod + ~/.cache + key: ${{ runner.os }}-wasm-go-${{ hashFiles('**/go.sum') }} + restore-keys: | + ${{ runner.os }}-wasm-go- + + - name: Download Go + run: curl -sSfL https://dl.google.com/go/go${GO_VERSION}.linux-amd64.tar.gz | tar -C ~ -xzf - + env: + GO_VERSION: 1.16 + + - name: Set Go Root + run: echo "GOROOT=${HOME}/go" >> $GITHUB_ENV + + - name: Set Go Path + run: echo "GOPATH=${HOME}/go" >> $GITHUB_ENV + + - name: Set Go Path + run: echo "GO_JS_WASM_EXEC=${GOROOT}/misc/wasm/go_js_wasm_exec" >> $GITHUB_ENV + + - name: Insall NPM modules + run: yarn install + + - name: Run Tests + run: | + GOOS=js GOARCH=wasm $GOPATH/bin/go test \ + -coverprofile=cover.out -covermode=atomic \ + -exec="${GO_JS_WASM_EXEC}" \ + -v ./... + + - uses: codecov/codecov-action@v1 + with: + file: ./cover.out + name: codecov-umbrella + fail_ci_if_error: true + flags: wasm diff --git a/dtls-2.0.9/.github/workflows/tidy-check.yaml b/dtls-2.0.9/.github/workflows/tidy-check.yaml new file mode 100644 index 0000000..03b5189 --- /dev/null +++ b/dtls-2.0.9/.github/workflows/tidy-check.yaml @@ -0,0 +1,37 @@ +# +# DO NOT EDIT THIS FILE +# +# It is automatically copied from https://github.com/pion/.goassets repository. +# If this repository should have package specific CI config, +# remove the repository name from .goassets/.github/workflows/assets-sync.yml. +# +# If you want to update the shared CI config, send a PR to +# https://github.com/pion/.goassets instead of this repository. +# + +name: Go mod tidy +on: + pull_request: + branches: + - master + push: + branches: + - master + +jobs: + Check: + runs-on: ubuntu-latest + steps: + - name: checkout + uses: actions/checkout@v2 + - name: Setup Go + uses: actions/setup-go@v2 + - name: check + run: | + go mod download + go mod tidy + if ! git diff --exit-code + then + echo "Not go mod tidied" + exit 1 + fi diff --git a/dtls-2.0.9/.gitignore b/dtls-2.0.9/.gitignore new file mode 100644 index 0000000..83db74b --- /dev/null +++ b/dtls-2.0.9/.gitignore @@ -0,0 +1,24 @@ +### JetBrains IDE ### +##################### +.idea/ + +### Emacs Temporary Files ### +############################# +*~ + +### Folders ### +############### +bin/ +vendor/ +node_modules/ + +### Files ### +############# +*.ivf +*.ogg +tags +cover.out +*.sw[poe] +*.wasm +examples/sfu-ws/cert.pem +examples/sfu-ws/key.pem diff --git a/dtls-2.0.9/.golangci.yml b/dtls-2.0.9/.golangci.yml new file mode 100644 index 0000000..d6162c9 --- /dev/null +++ b/dtls-2.0.9/.golangci.yml @@ -0,0 +1,89 @@ +linters-settings: + govet: + check-shadowing: true + misspell: + locale: US + exhaustive: + default-signifies-exhaustive: true + gomodguard: + blocked: + modules: + - github.com/pkg/errors: + recommendations: + - errors + +linters: + enable: + - asciicheck # Simple linter to check that your code does not contain non-ASCII identifiers + - bodyclose # checks whether HTTP response body is closed successfully + - deadcode # Finds unused code + - depguard # Go linter that checks if package imports are in a list of acceptable packages + - dogsled # Checks assignments with too many blank identifiers (e.g. x, _, _, _, := f()) + - dupl # Tool for code clone detection + - errcheck # Errcheck is a program for checking for unchecked errors in go programs. These unchecked errors can be critical bugs in some cases + - exhaustive # check exhaustiveness of enum switch statements + - exportloopref # checks for pointers to enclosing loop variables + - gci # Gci control golang package import order and make it always deterministic. + - gochecknoglobals # Checks that no globals are present in Go code + - gochecknoinits # Checks that no init functions are present in Go code + - gocognit # Computes and checks the cognitive complexity of functions + - goconst # Finds repeated strings that could be replaced by a constant + - gocritic # The most opinionated Go source code linter + - godox # Tool for detection of FIXME, TODO and other comment keywords + - goerr113 # Golang linter to check the errors handling expressions + - gofmt # Gofmt checks whether code was gofmt-ed. By default this tool runs with -s option to check for code simplification + - gofumpt # Gofumpt checks whether code was gofumpt-ed. + - goheader # Checks is file header matches to pattern + - goimports # Goimports does everything that gofmt does. Additionally it checks unused imports + - golint # Golint differs from gofmt. Gofmt reformats Go source code, whereas golint prints out style mistakes + - gomodguard # Allow and block list linter for direct Go module dependencies. This is different from depguard where there are different block types for example version constraints and module recommendations. + - goprintffuncname # Checks that printf-like functions are named with `f` at the end + - gosec # Inspects source code for security problems + - gosimple # Linter for Go source code that specializes in simplifying a code + - govet # Vet examines Go source code and reports suspicious constructs, such as Printf calls whose arguments do not align with the format string + - ineffassign # Detects when assignments to existing variables are not used + - misspell # Finds commonly misspelled English words in comments + - nakedret # Finds naked returns in functions greater than a specified function length + - noctx # noctx finds sending http request without context.Context + - scopelint # Scopelint checks for unpinned variables in go programs + - staticcheck # Staticcheck is a go vet on steroids, applying a ton of static analysis checks + - structcheck # Finds unused struct fields + - stylecheck # Stylecheck is a replacement for golint + - typecheck # Like the front-end of a Go compiler, parses and type-checks Go code + - unconvert # Remove unnecessary type conversions + - unparam # Reports unused function parameters + - unused # Checks Go code for unused constants, variables, functions and types + - varcheck # Finds unused global variables and constants + - whitespace # Tool for detection of leading and trailing whitespace + disable: + - funlen # Tool for detection of long functions + - gocyclo # Computes and checks the cyclomatic complexity of functions + - godot # Check if comments end in a period + - gomnd # An analyzer to detect magic numbers. + - lll # Reports long lines + - maligned # Tool to detect Go structs that would take less memory if their fields were sorted + - nestif # Reports deeply nested if statements + - nlreturn # nlreturn checks for a new line before return and branch statements to increase code clarity + - nolintlint # Reports ill-formed or insufficient nolint directives + - prealloc # Finds slice declarations that could potentially be preallocated + - rowserrcheck # checks whether Err of rows is checked successfully + - sqlclosecheck # Checks that sql.Rows and sql.Stmt are closed. + - testpackage # linter that makes you use a separate _test package + - wsl # Whitespace Linter - Forces you to use empty lines! + +issues: + exclude-use-default: false + exclude-rules: + # Allow complex tests, better to be self contained + - path: _test\.go + linters: + - gocognit + + # Allow complex main function in examples + - path: examples + text: "of func `main` is high" + linters: + - gocognit + +run: + skip-dirs-use-default: false diff --git a/dtls-2.0.9/LICENSE b/dtls-2.0.9/LICENSE new file mode 100644 index 0000000..ab60297 --- /dev/null +++ b/dtls-2.0.9/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2018 + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/dtls-2.0.9/Makefile b/dtls-2.0.9/Makefile new file mode 100644 index 0000000..1df38b2 --- /dev/null +++ b/dtls-2.0.9/Makefile @@ -0,0 +1,6 @@ +fuzz-build-record-layer: fuzz-prepare + go-fuzz-build -tags gofuzz -func FuzzRecordLayer +fuzz-run-record-layer: + go-fuzz -bin dtls-fuzz.zip -workdir fuzz +fuzz-prepare: + @GO111MODULE=on go mod vendor diff --git a/dtls-2.0.9/README.md b/dtls-2.0.9/README.md new file mode 100644 index 0000000..9f7e4a0 --- /dev/null +++ b/dtls-2.0.9/README.md @@ -0,0 +1,156 @@ +

+
+ Pion DTLS +
+

+

A Go implementation of DTLS

+

+ Pion DTLS + Sourcegraph Widget + Slack Widget +
+ Build Status + GoDoc + Coverage Status + Go Report Card + Codacy Badge + License: MIT +

+
+ +Native [DTLS 1.2][rfc6347] implementation in the Go programming language. + +A long term goal is a professional security review, and maye inclusion in stdlib. + +[rfc6347]: https://tools.ietf.org/html/rfc6347 + +### Goals/Progress +This will only be targeting DTLS 1.2, and the most modern/common cipher suites. +We would love contributes that fall under the 'Planned Features' and fixing any bugs! + +#### Current features +* DTLS 1.2 Client/Server +* Key Exchange via ECDHE(curve25519, nistp256, nistp384) and PSK +* Packet loss and re-ordering is handled during handshaking +* Key export ([RFC 5705][rfc5705]) +* Serialization and Resumption of sessions +* Extended Master Secret extension ([RFC 7627][rfc7627]) + +[rfc5705]: https://tools.ietf.org/html/rfc5705 +[rfc7627]: https://tools.ietf.org/html/rfc7627 + +#### Supported ciphers + +##### ECDHE +* TLS_ECDHE_ECDSA_WITH_AES_128_CCM ([RFC 6655][rfc6655]) +* TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8 ([RFC 6655][rfc6655]) +* TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 ([RFC 5289][rfc5289]) +* TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 ([RFC 5289][rfc5289]) +* TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA ([RFC 8422][rfc8422]) +* TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA ([RFC 8422][rfc8422]) + +##### PSK +* TLS_PSK_WITH_AES_128_CCM ([RFC 6655][rfc6655]) +* TLS_PSK_WITH_AES_128_CCM_8 ([RFC 6655][rfc6655]) +* TLS_PSK_WITH_AES_128_GCM_SHA256 ([RFC 5487][rfc5487]) +* TLS_PSK_WITH_AES_128_CBC_SHA256 ([RFC 5487][rfc5487]) + +[rfc5289]: https://tools.ietf.org/html/rfc5289 +[rfc8422]: https://tools.ietf.org/html/rfc8422 +[rfc6655]: https://tools.ietf.org/html/rfc6655 +[rfc5487]: https://tools.ietf.org/html/rfc5487 + +#### Planned Features +* Chacha20Poly1305 + +#### Excluded Features +* DTLS 1.0 +* Renegotiation +* Compression + +### Using + +This library needs at least Go 1.13, and you should have [Go modules +enabled](https://github.com/golang/go/wiki/Modules). + +#### Pion DTLS +For a DTLS 1.2 Server that listens on 127.0.0.1:4444 +```sh +go run examples/listen/selfsign/main.go +``` + +For a DTLS 1.2 Client that connects to 127.0.0.1:4444 +```sh +go run examples/dial/selfsign/main.go +``` + +#### OpenSSL +Pion DTLS can connect to itself and OpenSSL. +``` + // Generate a certificate + openssl ecparam -out key.pem -name prime256v1 -genkey + openssl req -new -sha256 -key key.pem -out server.csr + openssl x509 -req -sha256 -days 365 -in server.csr -signkey key.pem -out cert.pem + + // Use with examples/dial/selfsign/main.go + openssl s_server -dtls1_2 -cert cert.pem -key key.pem -accept 4444 + + // Use with examples/listen/selfsign/main.go + openssl s_client -dtls1_2 -connect 127.0.0.1:4444 -debug -cert cert.pem -key key.pem +``` + +### Using with PSK +Pion DTLS also comes with examples that do key exchange via PSK + + +#### Pion DTLS +```sh +go run examples/listen/psk/main.go +``` + +```sh +go run examples/dial/psk/main.go +``` + +#### OpenSSL +``` + // Use with examples/dial/psk/main.go + openssl s_server -dtls1_2 -accept 4444 -nocert -psk abc123 -cipher PSK-AES128-CCM8 + + // Use with examples/listen/psk/main.go + openssl s_client -dtls1_2 -connect 127.0.0.1:4444 -psk abc123 -cipher PSK-AES128-CCM8 +``` + +### Contributing +Check out the **[contributing wiki](https://github.com/pion/webrtc/wiki/Contributing)** to join the group of amazing people making this project possible: + +* [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, E2E* +* [Daniele Sluijters](https://github.com/daenney) - *AES-CCM support* +* [Jin Lei](https://github.com/jinleileiking) - *Logging* +* [Hugo Arregui](https://github.com/hugoArregui) +* [Lander Noterman](https://github.com/LanderN) +* [Aleksandr Razumov](https://github.com/ernado) - *Fuzzing* +* [Ryan Gordon](https://github.com/ryangordon) +* [Stefan Tatschner](https://rumpelsepp.org/contact.html) +* [Hayden James](https://github.com/hjames9) +* [Jozef Kralik](https://github.com/jkralik) +* [Robert Eperjesi](https://github.com/epes) +* [Atsushi Watanabe](https://github.com/at-wat) +* [Julien Salleyron](https://github.com/juliens) - *Server Name Indication* +* [Jeroen de Bruijn](https://github.com/vidavidorra) +* [bjdgyc](https://github.com/bjdgyc) +* [Jeffrey Stoke (Jeff Ctor)](https://github.com/jeffreystoke) - *Fragmentbuffer Fix* +* [Frank Olbricht](https://github.com/folbricht) +* [ZHENK](https://github.com/scorpionknifes) +* [Carson Hoffman](https://github.com/CarsonHoffman) +* [Vadim Filimonov](https://github.com/fffilimonov) +* [Jim Wert](https://github.com/bocajim) +* [Alvaro Viebrantz](https://github.com/alvarowolfx) +* [Kegan Dougal](https://github.com/Kegsay) +* [Michael Zabka](https://github.com/misak113) + +### License +MIT License - see [LICENSE](LICENSE) for full text diff --git a/dtls-2.0.9/bench_test.go b/dtls-2.0.9/bench_test.go new file mode 100644 index 0000000..517bc16 --- /dev/null +++ b/dtls-2.0.9/bench_test.go @@ -0,0 +1,118 @@ +package dtls + +import ( + "context" + "crypto/tls" + "fmt" + "testing" + "time" + + "github.com/pion/dtls/v2/internal/net/dpipe" + "github.com/pion/dtls/v2/pkg/crypto/selfsign" + "github.com/pion/logging" + "github.com/pion/transport/test" +) + +func TestSimpleReadWrite(t *testing.T) { + report := test.CheckRoutines(t) + defer report() + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + defer cancel() + + ca, cb := dpipe.Pipe() + certificate, err := selfsign.GenerateSelfSigned() + if err != nil { + t.Fatal(err) + } + gotHello := make(chan struct{}) + + go func() { + server, sErr := testServer(ctx, cb, &Config{ + Certificates: []tls.Certificate{certificate}, + LoggerFactory: logging.NewDefaultLoggerFactory(), + }, false) + if sErr != nil { + t.Error(sErr) + return + } + buf := make([]byte, 1024) + if _, sErr = server.Read(buf); sErr != nil { + t.Error(sErr) + } + gotHello <- struct{}{} + if sErr = server.Close(); sErr != nil { + t.Error(sErr) + } + }() + + client, err := testClient(ctx, ca, &Config{ + LoggerFactory: logging.NewDefaultLoggerFactory(), + InsecureSkipVerify: true, + }, false) + if err != nil { + t.Fatal(err) + } + if _, err = client.Write([]byte("hello")); err != nil { + t.Error(err) + } + select { + case <-gotHello: + // OK + case <-time.After(time.Second * 5): + t.Error("timeout") + } + + if err = client.Close(); err != nil { + t.Error(err) + } +} + +func benchmarkConn(b *testing.B, n int64) { + b.Run(fmt.Sprintf("%d", n), func(b *testing.B) { + ctx := context.Background() + + ca, cb := dpipe.Pipe() + certificate, err := selfsign.GenerateSelfSigned() + server := make(chan *Conn) + go func() { + s, sErr := testServer(ctx, cb, &Config{ + Certificates: []tls.Certificate{certificate}, + }, false) + if err != nil { + b.Error(sErr) + return + } + server <- s + }() + if err != nil { + b.Fatal(err) + } + hw := make([]byte, n) + b.ReportAllocs() + b.SetBytes(int64(len(hw))) + go func() { + client, cErr := testClient(ctx, ca, &Config{InsecureSkipVerify: true}, false) + if cErr != nil { + b.Error(err) + } + for { + if _, cErr = client.Write(hw); cErr != nil { + b.Error(err) + } + } + }() + s := <-server + buf := make([]byte, 2048) + for i := 0; i < b.N; i++ { + if _, err = s.Read(buf); err != nil { + b.Error(err) + } + } + }) +} + +func BenchmarkConnReadWrite(b *testing.B) { + for _, n := range []int64{16, 128, 512, 1024, 2048} { + benchmarkConn(b, n) + } +} diff --git a/dtls-2.0.9/certificate.go b/dtls-2.0.9/certificate.go new file mode 100644 index 0000000..c99e1c9 --- /dev/null +++ b/dtls-2.0.9/certificate.go @@ -0,0 +1,67 @@ +package dtls + +import ( + "crypto/tls" + "crypto/x509" + "strings" +) + +func (c *handshakeConfig) getCertificate(serverName string) (*tls.Certificate, error) { + c.mu.Lock() + defer c.mu.Unlock() + + if c.nameToCertificate == nil { + nameToCertificate := make(map[string]*tls.Certificate) + for i := range c.localCertificates { + cert := &c.localCertificates[i] + x509Cert := cert.Leaf + if x509Cert == nil { + var parseErr error + x509Cert, parseErr = x509.ParseCertificate(cert.Certificate[0]) + if parseErr != nil { + continue + } + } + if len(x509Cert.Subject.CommonName) > 0 { + nameToCertificate[strings.ToLower(x509Cert.Subject.CommonName)] = cert + } + for _, san := range x509Cert.DNSNames { + nameToCertificate[strings.ToLower(san)] = cert + } + } + c.nameToCertificate = nameToCertificate + } + + if len(c.localCertificates) == 0 { + return nil, errNoCertificates + } + + if len(c.localCertificates) == 1 { + // There's only one choice, so no point doing any work. + return &c.localCertificates[0], nil + } + + if len(serverName) == 0 { + return &c.localCertificates[0], nil + } + + name := strings.TrimRight(strings.ToLower(serverName), ".") + + if cert, ok := c.nameToCertificate[name]; ok { + return cert, nil + } + + // try replacing labels in the name with wildcards until we get a + // match. + labels := strings.Split(name, ".") + for i := range labels { + labels[i] = "*" + candidate := strings.Join(labels, ".") + if cert, ok := c.nameToCertificate[candidate]; ok { + return cert, nil + } + } + + // If nothing matches, return the first certificate. + return &c.localCertificates[0], nil +} diff --git a/dtls-2.0.9/certificate_test.go b/dtls-2.0.9/certificate_test.go new file mode 100644 index 0000000..56cc04e --- /dev/null +++ b/dtls-2.0.9/certificate_test.go @@ -0,0 +1,79 @@ +package dtls + +import ( + "crypto/tls" + "reflect" + "testing" + + "github.com/pion/dtls/v2/pkg/crypto/selfsign" +) + +func TestGetCertificate(t *testing.T) { + certificateWildcard, err := selfsign.GenerateSelfSignedWithDNS("*.test.test") + if err != nil { + t.Fatal(err) + } + + certificateTest, err := selfsign.GenerateSelfSignedWithDNS("test.test", "www.test.test", "pop.test.test") + if err != nil { + t.Fatal(err) + } + + certificateRandom, err := selfsign.GenerateSelfSigned() + if err != nil { + t.Fatal(err) + } + + cfg := &handshakeConfig{ + localCertificates: []tls.Certificate{ + certificateRandom, + certificateTest, + certificateWildcard, + }, + } + + testCases := []struct { + desc string + serverName string + expectedCertificate tls.Certificate + }{ + { + desc: "Simple match in CN", + serverName: "test.test", + expectedCertificate: certificateTest, + }, + { + desc: "Simple match in SANs", + serverName: "www.test.test", + expectedCertificate: certificateTest, + }, + + { + desc: "Wildcard match", + serverName: "foo.test.test", + expectedCertificate: certificateWildcard, + }, + { + desc: "No match return first", + serverName: "foo.bar", + expectedCertificate: certificateRandom, + }, + } + + for _, test := range testCases { + test := test + + t.Run(test.desc, func(t *testing.T) { + t.Parallel() + + cert, err := cfg.getCertificate(test.serverName) + if err != nil { + t.Fatal(err) + } + + if !reflect.DeepEqual(cert.Leaf, test.expectedCertificate.Leaf) { + t.Fatalf("Certificate does not match: expected(%v) actual(%v)", test.expectedCertificate.Leaf, cert.Leaf) + } + }) + } +} diff --git a/dtls-2.0.9/cipher_suite.go b/dtls-2.0.9/cipher_suite.go new file mode 100644 index 0000000..ed10609 --- /dev/null +++ b/dtls-2.0.9/cipher_suite.go @@ -0,0 +1,213 @@ +package dtls + +import ( + "fmt" + "hash" + + "github.com/pion/dtls/v2/internal/ciphersuite" + "github.com/pion/dtls/v2/pkg/crypto/clientcertificate" + "github.com/pion/dtls/v2/pkg/protocol/recordlayer" +) + +// CipherSuiteID is an ID for our supported CipherSuites +type CipherSuiteID = ciphersuite.ID + +// Supported Cipher Suites +const ( + // AES-128-CCM + TLS_ECDHE_ECDSA_WITH_AES_128_CCM CipherSuiteID = ciphersuite.TLS_ECDHE_ECDSA_WITH_AES_128_CCM //nolint:golint,stylecheck + TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8 CipherSuiteID = ciphersuite.TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8 //nolint:golint,stylecheck + + // AES-128-GCM-SHA256 + TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 CipherSuiteID = ciphersuite.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 //nolint:golint,stylecheck + TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 CipherSuiteID = ciphersuite.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 //nolint:golint,stylecheck + + // AES-256-CBC-SHA + TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA CipherSuiteID = ciphersuite.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA //nolint:golint,stylecheck + TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA CipherSuiteID = ciphersuite.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA //nolint:golint,stylecheck + + TLS_PSK_WITH_AES_128_CCM CipherSuiteID = ciphersuite.TLS_PSK_WITH_AES_128_CCM //nolint:golint,stylecheck + TLS_PSK_WITH_AES_128_CCM_8 CipherSuiteID = ciphersuite.TLS_PSK_WITH_AES_128_CCM_8 //nolint:golint,stylecheck + TLS_PSK_WITH_AES_128_GCM_SHA256 CipherSuiteID = ciphersuite.TLS_PSK_WITH_AES_128_GCM_SHA256 //nolint:golint,stylecheck + TLS_PSK_WITH_AES_128_CBC_SHA256 CipherSuiteID = ciphersuite.TLS_PSK_WITH_AES_128_CBC_SHA256 //nolint:golint,stylecheck +) + +// CipherSuiteAuthenticationType controls what authentication method is using during the handshake for a CipherSuite +type CipherSuiteAuthenticationType = ciphersuite.AuthenticationType + +// AuthenticationType Enums +const ( + CipherSuiteAuthenticationTypeCertificate CipherSuiteAuthenticationType = ciphersuite.AuthenticationTypeCertificate + CipherSuiteAuthenticationTypePreSharedKey CipherSuiteAuthenticationType = ciphersuite.AuthenticationTypePreSharedKey + CipherSuiteAuthenticationTypeAnonymous CipherSuiteAuthenticationType = ciphersuite.AuthenticationTypeAnonymous +) + +var _ = allCipherSuites() // Necessary until this function isn't only used by Go 1.14 + +// CipherSuite is an interface that all DTLS CipherSuites must satisfy +type CipherSuite interface { + // String of CipherSuite, only used for logging + String() string + + // ID of CipherSuite. + ID() CipherSuiteID + + // What type of Certificate does this CipherSuite use + CertificateType() clientcertificate.Type + + // What Hash function is used during verification + HashFunc() func() hash.Hash + + // AuthenticationType controls what authentication method is using during the handshake + AuthenticationType() CipherSuiteAuthenticationType + + // Called when keying material has been generated, should initialize the internal cipher + Init(masterSecret, clientRandom, serverRandom []byte, isClient bool) error + IsInitialized() bool + + Encrypt(pkt *recordlayer.RecordLayer, raw []byte) ([]byte, error) + Decrypt(in []byte) ([]byte, error) +} + +// CipherSuiteName provides the same functionality as tls.CipherSuiteName +// that appeared first in Go 1.14. +// +// Our implementation differs slightly in that it takes in a CiperSuiteID, +// like the rest of our library, instead of a uint16 like crypto/tls. +func CipherSuiteName(id CipherSuiteID) string { + suite := cipherSuiteForID(id, nil) + if suite != nil { + return suite.String() + } + return fmt.Sprintf("0x%04X", uint16(id)) +} + +// Taken from https://www.iana.org/assignments/tls-parameters/tls-parameters.xml +// A cipherSuite is a specific combination of key agreement, cipher and MAC +// function. +func cipherSuiteForID(id CipherSuiteID, customCiphers func() []CipherSuite) CipherSuite { + switch id { //nolint:exhaustive + case TLS_ECDHE_ECDSA_WITH_AES_128_CCM: + return ciphersuite.NewTLSEcdheEcdsaWithAes128Ccm() + case TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8: + return ciphersuite.NewTLSEcdheEcdsaWithAes128Ccm8() + case TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256: + return &ciphersuite.TLSEcdheEcdsaWithAes128GcmSha256{} + case TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256: + return &ciphersuite.TLSEcdheRsaWithAes128GcmSha256{} + case TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA: + return &ciphersuite.TLSEcdheEcdsaWithAes256CbcSha{} + case TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA: + return &ciphersuite.TLSEcdheRsaWithAes256CbcSha{} + case TLS_PSK_WITH_AES_128_CCM: + return ciphersuite.NewTLSPskWithAes128Ccm() + case TLS_PSK_WITH_AES_128_CCM_8: + return ciphersuite.NewTLSPskWithAes128Ccm8() + case TLS_PSK_WITH_AES_128_GCM_SHA256: + return &ciphersuite.TLSPskWithAes128GcmSha256{} + case TLS_PSK_WITH_AES_128_CBC_SHA256: + return &ciphersuite.TLSPskWithAes128CbcSha256{} + } + + if customCiphers != nil { + for _, c := range customCiphers() { + if c.ID() == id { + return c + } + } + } + + return nil +} + +// CipherSuites we support in order of preference +func defaultCipherSuites() []CipherSuite { + return []CipherSuite{ + &ciphersuite.TLSEcdheEcdsaWithAes128GcmSha256{}, + &ciphersuite.TLSEcdheRsaWithAes128GcmSha256{}, + &ciphersuite.TLSEcdheEcdsaWithAes256CbcSha{}, + &ciphersuite.TLSEcdheRsaWithAes256CbcSha{}, + } +} + +func allCipherSuites() []CipherSuite { + return []CipherSuite{ + ciphersuite.NewTLSEcdheEcdsaWithAes128Ccm(), + ciphersuite.NewTLSEcdheEcdsaWithAes128Ccm8(), + &ciphersuite.TLSEcdheEcdsaWithAes128GcmSha256{}, + &ciphersuite.TLSEcdheRsaWithAes128GcmSha256{}, + &ciphersuite.TLSEcdheEcdsaWithAes256CbcSha{}, + &ciphersuite.TLSEcdheRsaWithAes256CbcSha{}, + ciphersuite.NewTLSPskWithAes128Ccm(), + ciphersuite.NewTLSPskWithAes128Ccm8(), + &ciphersuite.TLSPskWithAes128GcmSha256{}, + } +} + +func cipherSuiteIDs(cipherSuites []CipherSuite) []uint16 { + rtrn := []uint16{} + for _, c := range cipherSuites { + rtrn = append(rtrn, uint16(c.ID())) + } + return rtrn +} + +func parseCipherSuites(userSelectedSuites []CipherSuiteID, customCipherSuites func() []CipherSuite, includeCertificateSuites, includePSKSuites bool) ([]CipherSuite, error) { + cipherSuitesForIDs := func(ids []CipherSuiteID) ([]CipherSuite, error) { + cipherSuites := []CipherSuite{} + for _, id := range ids { + c := cipherSuiteForID(id, nil) + if c == nil { + return nil, &invalidCipherSuite{id} + } + cipherSuites = append(cipherSuites, c) + } + return cipherSuites, nil + } + + var ( + cipherSuites []CipherSuite + err error + i int + ) + if userSelectedSuites != nil { + cipherSuites, err = cipherSuitesForIDs(userSelectedSuites) + if err != nil { + return nil, err + } + } else { + cipherSuites = defaultCipherSuites() + } + + // Put CustomCipherSuites before ID selected suites + if customCipherSuites != nil { + cipherSuites = append(customCipherSuites(), cipherSuites...) + } + + var foundCertificateSuite, foundPSKSuite, foundAnonymousSuite bool + for _, c := range cipherSuites { + switch { + case includeCertificateSuites && c.AuthenticationType() == CipherSuiteAuthenticationTypeCertificate: + foundCertificateSuite = true + case includePSKSuites && c.AuthenticationType() == CipherSuiteAuthenticationTypePreSharedKey: + foundPSKSuite = true + case c.AuthenticationType() == CipherSuiteAuthenticationTypeAnonymous: + foundAnonymousSuite = true + default: + continue + } + cipherSuites[i] = c + i++ + } + + switch { + case includeCertificateSuites && !foundCertificateSuite && !foundAnonymousSuite: + return nil, errNoAvailableCertificateCipherSuite + case includePSKSuites && !foundPSKSuite: + return nil, errNoAvailablePSKCipherSuite + case i == 0: + return nil, errNoAvailableCipherSuites + } + + return cipherSuites[:i], nil +} diff --git a/dtls-2.0.9/cipher_suite_go114.go b/dtls-2.0.9/cipher_suite_go114.go new file mode 100644 index 0000000..7bba16e --- /dev/null +++ b/dtls-2.0.9/cipher_suite_go114.go @@ -0,0 +1,40 @@ +// +build go1.14 + +package dtls + +import ( + "crypto/tls" +) + +// VersionDTLS12 is the DTLS version in the same style as +// VersionTLSXX from crypto/tls +const VersionDTLS12 = 0xfefd + +// Convert from our cipherSuite interface to a tls.CipherSuite struct +func toTLSCipherSuite(c CipherSuite) *tls.CipherSuite { + return &tls.CipherSuite{ + ID: uint16(c.ID()), + Name: c.String(), + SupportedVersions: []uint16{VersionDTLS12}, + Insecure: false, + } +} + +// CipherSuites returns a list of cipher suites currently implemented by this +// package, excluding those with security issues, which are returned by +// InsecureCipherSuites. +func CipherSuites() []*tls.CipherSuite { + suites := allCipherSuites() + res := make([]*tls.CipherSuite, len(suites)) + for i, c := range suites { + res[i] = toTLSCipherSuite(c) + } + return res +} + +// InsecureCipherSuites returns a list of cipher suites currently implemented by +// this package and which have security issues. +func InsecureCipherSuites() []*tls.CipherSuite { + var res []*tls.CipherSuite + return res +} diff --git a/dtls-2.0.9/cipher_suite_go114_test.go b/dtls-2.0.9/cipher_suite_go114_test.go new file mode 100644 index 0000000..57c64d4 --- /dev/null +++ b/dtls-2.0.9/cipher_suite_go114_test.go @@ -0,0 +1,51 @@ +// +build go1.14 + +package dtls + +import ( + "testing" +) + +func TestInsecureCipherSuites(t *testing.T) { + r := InsecureCipherSuites() + + if len(r) != 0 { + t.Fatalf("Expected no insecure ciphersuites, got %d", len(r)) + } +} + +func TestCipherSuites(t *testing.T) { + ours := allCipherSuites() + theirs := CipherSuites() + + if len(ours) != len(theirs) { + t.Fatalf("Expected %d CipherSuites, got %d", len(ours), len(theirs)) + } + + for i, s := range ours { + i := i + s := s + t.Run(s.String(), func(t *testing.T) { + c := theirs[i] + if c.ID != uint16(s.ID()) { + t.Fatalf("Expected ID: 0x%04X, got 0x%04X", s.ID(), c.ID) + } + + if c.Name != s.String() { + t.Fatalf("Expected Name: %s, got %s", s.String(), c.Name) + } + + if len(c.SupportedVersions) != 1 { + t.Fatalf("Expected %d SupportedVersion, got %d", 1, len(c.SupportedVersions)) + } + + if c.SupportedVersions[0] != VersionDTLS12 { + t.Fatalf("Expected SupportedVersions 0x%04X, got 0x%04X", VersionDTLS12, c.SupportedVersions[0]) + } + + if c.Insecure { + t.Fatalf("Expected Insecure %t, got %t", false, c.Insecure) + } + }) + } +} diff --git a/dtls-2.0.9/cipher_suite_test.go b/dtls-2.0.9/cipher_suite_test.go new file mode 100644 index 0000000..e0ed6d1 --- /dev/null +++ b/dtls-2.0.9/cipher_suite_test.go @@ -0,0 +1,108 @@ +package dtls + +import ( + "context" + "testing" + "time" + + "github.com/pion/dtls/v2/internal/ciphersuite" + "github.com/pion/dtls/v2/internal/net/dpipe" + "github.com/pion/transport/test" +) + +func TestCipherSuiteName(t *testing.T) { + testCases := []struct { + suite CipherSuiteID + expected string + }{ + {TLS_ECDHE_ECDSA_WITH_AES_128_CCM, "TLS_ECDHE_ECDSA_WITH_AES_128_CCM"}, + {CipherSuiteID(0x0000), "0x0000"}, + } + + for _, testCase := range testCases { + res := CipherSuiteName(testCase.suite) + if res != testCase.expected { + t.Fatalf("Expected: %s, got %s", testCase.expected, res) + } + } +} + +func TestAllCipherSuites(t *testing.T) { + actual := len(allCipherSuites()) + if actual == 0 { + t.Fatal() + } +} + +// CustomCipher that is just used to assert Custom IDs work +type testCustomCipherSuite struct { + ciphersuite.TLSEcdheEcdsaWithAes128GcmSha256 + authenticationType CipherSuiteAuthenticationType +} + +func (t *testCustomCipherSuite) ID() CipherSuiteID { + return 0xFFFF +} + +func (t *testCustomCipherSuite) AuthenticationType() CipherSuiteAuthenticationType { + return t.authenticationType +} + +// Assert that two connections that pass in a CipherSuite with a CustomID works +func TestCustomCipherSuite(t *testing.T) { + type result struct { + c *Conn + err error + } + + // Check for leaking routines + report := test.CheckRoutines(t) + defer report() + + runTest := func(cipherFactory func() []CipherSuite) { + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + defer cancel() + + ca, cb := dpipe.Pipe() + c := make(chan result) + + go func() { + client, err := testClient(ctx, ca, &Config{ + CipherSuites: []CipherSuiteID{}, + CustomCipherSuites: cipherFactory, + }, true) + c <- result{client, err} + }() + + server, err := testServer(ctx, cb, &Config{ + CipherSuites: []CipherSuiteID{}, + CustomCipherSuites: cipherFactory, + }, true) + + clientResult := <-c + + if err != nil { + t.Error(err) + } else { + _ = server.Close() + } + + if clientResult.err != nil { + t.Error(clientResult.err) + } else { + _ = clientResult.c.Close() + } + } + + t.Run("Custom ID", func(t *testing.T) { + runTest(func() []CipherSuite { + return []CipherSuite{&testCustomCipherSuite{authenticationType: CipherSuiteAuthenticationTypeCertificate}} + }) + }) + + t.Run("Anonymous Cipher", func(t *testing.T) { + runTest(func() []CipherSuite { + return []CipherSuite{&testCustomCipherSuite{authenticationType: CipherSuiteAuthenticationTypeAnonymous}} + }) + }) +} diff --git a/dtls-2.0.9/codecov.yml b/dtls-2.0.9/codecov.yml new file mode 100644 index 0000000..085200a --- /dev/null +++ b/dtls-2.0.9/codecov.yml @@ -0,0 +1,20 @@ +# +# DO NOT EDIT THIS FILE +# +# It is automatically copied from https://github.com/pion/.goassets repository. +# + +coverage: + status: + project: + default: + # Allow decreasing 2% of total coverage to avoid noise. + threshold: 2% + patch: + default: + target: 70% + only_pulls: true + +ignore: + - "examples/*" + - "examples/**/*" diff --git a/dtls-2.0.9/compression_method.go b/dtls-2.0.9/compression_method.go new file mode 100644 index 0000000..693eb7a --- /dev/null +++ b/dtls-2.0.9/compression_method.go @@ -0,0 +1,9 @@ +package dtls + +import "github.com/pion/dtls/v2/pkg/protocol" + +func defaultCompressionMethods() []*protocol.CompressionMethod { + return []*protocol.CompressionMethod{ + {}, + } +} diff --git a/dtls-2.0.9/config.go b/dtls-2.0.9/config.go new file mode 100644 index 0000000..7c1c0b7 --- /dev/null +++ b/dtls-2.0.9/config.go @@ -0,0 +1,197 @@ +package dtls + +import ( + "context" + "crypto/ecdsa" + "crypto/ed25519" + "crypto/rsa" + "crypto/tls" + "crypto/x509" + "io" + "time" + + "github.com/pion/logging" +) + +const keyLogLabelTLS12 = "CLIENT_RANDOM" + +// Config is used to configure a DTLS client or server. +// After a Config is passed to a DTLS function it must not be modified. +type Config struct { + // Certificates contains certificate chain to present to the other side of the connection. + // Server MUST set this if PSK is non-nil + // client SHOULD sets this so CertificateRequests can be handled if PSK is non-nil + Certificates []tls.Certificate + + // CipherSuites is a list of supported cipher suites. + // If CipherSuites is nil, a default list is used + CipherSuites []CipherSuiteID + + // CustomCipherSuites is a list of CipherSuites that can be + // provided by the user. This allow users to user Ciphers that are reserved + // for private usage. + CustomCipherSuites func() []CipherSuite + + // SignatureSchemes contains the signature and hash schemes that the peer requests to verify. + SignatureSchemes []tls.SignatureScheme + + // SRTPProtectionProfiles are the supported protection profiles + // Clients will send this via use_srtp and assert that the server properly responds + // Servers will assert that clients send one of these profiles and will respond as needed + SRTPProtectionProfiles []SRTPProtectionProfile + + // ClientAuth determines the server's policy for + // TLS Client Authentication. The default is NoClientCert. + ClientAuth ClientAuthType + + // RequireExtendedMasterSecret determines if the "Extended Master Secret" extension + // should be disabled, requested, or required (default requested). + ExtendedMasterSecret ExtendedMasterSecretType + + // FlightInterval controls how often we send outbound handshake messages + // defaults to time.Second + FlightInterval time.Duration + + // PSK sets the pre-shared key used by this DTLS connection + // If PSK is non-nil only PSK CipherSuites will be used + PSK PSKCallback + PSKIdentityHint []byte + + CiscoCompat PSKCallback // TODO add cisco anyconnect support + + // InsecureSkipVerify controls whether a client verifies the + // server's certificate chain and host name. + // If InsecureSkipVerify is true, TLS accepts any certificate + // presented by the server and any host name in that certificate. + // In this mode, TLS is susceptible to man-in-the-middle attacks. + // This should be used only for testing. + InsecureSkipVerify bool + + // InsecureHashes allows the use of hashing algorithms that are known + // to be vulnerable. + InsecureHashes bool + + // VerifyPeerCertificate, if not nil, is called after normal + // certificate verification by either a client or server. It + // receives the certificate provided by the peer and also a flag + // that tells if normal verification has succeedded. If it returns a + // non-nil error, the handshake is aborted and that error results. + // + // If normal verification fails then the handshake will abort before + // considering this callback. If normal verification is disabled by + // setting InsecureSkipVerify, or (for a server) when ClientAuth is + // RequestClientCert or RequireAnyClientCert, then this callback will + // be considered but the verifiedChains will always be nil. + VerifyPeerCertificate func(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error + + // RootCAs defines the set of root certificate authorities + // that one peer uses when verifying the other peer's certificates. + // If RootCAs is nil, TLS uses the host's root CA set. + RootCAs *x509.CertPool + + // ClientCAs defines the set of root certificate authorities + // that servers use if required to verify a client certificate + // by the policy in ClientAuth. + ClientCAs *x509.CertPool + + // ServerName is used to verify the hostname on the returned + // certificates unless InsecureSkipVerify is given. + ServerName string + + LoggerFactory logging.LoggerFactory + + // ConnectContextMaker is a function to make a context used in Dial(), + // Client(), Server(), and Accept(). If nil, the default ConnectContextMaker + // is used. It can be implemented as following. + // + // func ConnectContextMaker() (context.Context, func()) { + // return context.WithTimeout(context.Background(), 30*time.Second) + // } + ConnectContextMaker func() (context.Context, func()) + + // MTU is the length at which handshake messages will be fragmented to + // fit within the maximum transmission unit (default is 1200 bytes) + MTU int + + // ReplayProtectionWindow is the size of the replay attack protection window. + // Duplication of the sequence number is checked in this window size. + // Packet with sequence number older than this value compared to the latest + // accepted packet will be discarded. (default is 64) + ReplayProtectionWindow int + + // KeyLogWriter optionally specifies a destination for TLS master secrets + // in NSS key log format that can be used to allow external programs + // such as Wireshark to decrypt TLS connections. + // See https://developer.mozilla.org/en-US/docs/Mozilla/Projects/NSS/Key_Log_Format. + // Use of KeyLogWriter compromises security and should only be + // used for debugging. + KeyLogWriter io.Writer +} + +func defaultConnectContextMaker() (context.Context, func()) { + return context.WithTimeout(context.Background(), 30*time.Second) +} + +func (c *Config) connectContextMaker() (context.Context, func()) { + if c.ConnectContextMaker == nil { + return defaultConnectContextMaker() + } + return c.ConnectContextMaker() +} + +const defaultMTU = 1200 // bytes + +// PSKCallback is called once we have the remote's PSKIdentityHint. +// If the remote provided none it will be nil +type PSKCallback func([]byte) ([]byte, error) + +// ClientAuthType declares the policy the server will follow for +// TLS Client Authentication. +type ClientAuthType int + +// ClientAuthType enums +const ( + NoClientCert ClientAuthType = iota + RequestClientCert + RequireAnyClientCert + VerifyClientCertIfGiven + RequireAndVerifyClientCert +) + +// ExtendedMasterSecretType declares the policy the client and server +// will follow for the Extended Master Secret extension +type ExtendedMasterSecretType int + +// ExtendedMasterSecretType enums +const ( + RequestExtendedMasterSecret ExtendedMasterSecretType = iota + RequireExtendedMasterSecret + DisableExtendedMasterSecret +) + +func validateConfig(config *Config) error { + switch { + case config == nil: + return errNoConfigProvided + case config.PSKIdentityHint != nil && config.PSK == nil: + return errIdentityNoPSK + } + + for _, cert := range config.Certificates { + if cert.Certificate == nil { + return errInvalidCertificate + } + if cert.PrivateKey != nil { + switch cert.PrivateKey.(type) { + case ed25519.PrivateKey: + case *ecdsa.PrivateKey: + case *rsa.PrivateKey: + default: + return errInvalidPrivateKey + } + } + } + + _, err := parseCipherSuites(config.CipherSuites, config.CustomCipherSuites, config.PSK == nil || len(config.Certificates) > 0, config.PSK != nil) + return err +} diff --git a/dtls-2.0.9/config_test.go b/dtls-2.0.9/config_test.go new file mode 100644 index 0000000..a5a0772 --- /dev/null +++ b/dtls-2.0.9/config_test.go @@ -0,0 +1,119 @@ +package dtls + +import ( + "crypto/dsa" //nolint + "crypto/rand" + "crypto/rsa" + "crypto/tls" + "errors" + "testing" + + "github.com/pion/dtls/v2/pkg/crypto/selfsign" +) + +func TestValidateConfig(t *testing.T) { + // Empty config + if err := validateConfig(nil); !errors.Is(err, errNoConfigProvided) { + t.Fatalf("TestValidateConfig: Config validation error exp(%v) failed(%v)", errNoConfigProvided, err) + } + + // PSK and Certificate, valid cipher suites + cert, err := selfsign.GenerateSelfSigned() + if err != nil { + t.Fatalf("TestValidateConfig: Config validation error(%v), self signed certificate not generated", err) + return + } + config := &Config{ + CipherSuites: []CipherSuiteID{TLS_PSK_WITH_AES_128_CCM_8, TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256}, + PSK: func(hint []byte) ([]byte, error) { + return nil, nil + }, + Certificates: []tls.Certificate{cert}, + } + if err = validateConfig(config); err != nil { + t.Fatalf("TestValidateConfig: Client error exp(%v) failed(%v)", nil, err) + } + + // PSK and Certificate, no PSK cipher suite + config = &Config{ + CipherSuites: []CipherSuiteID{TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256}, + PSK: func(hint []byte) ([]byte, error) { + return nil, nil + }, + Certificates: []tls.Certificate{cert}, + } + if err = validateConfig(config); !errors.Is(errNoAvailablePSKCipherSuite, err) { + t.Fatalf("TestValidateConfig: Client error exp(%v) failed(%v)", errNoAvailablePSKCipherSuite, err) + } + + // PSK and Certificate, no non-PSK cipher suite + config = &Config{ + CipherSuites: []CipherSuiteID{TLS_PSK_WITH_AES_128_CCM_8}, + PSK: func(hint []byte) ([]byte, error) { + return nil, nil + }, + Certificates: []tls.Certificate{cert}, + } + if err = validateConfig(config); !errors.Is(errNoAvailableCertificateCipherSuite, err) { + t.Fatalf("TestValidateConfig: Client error exp(%v) failed(%v)", errNoAvailableCertificateCipherSuite, err) + } + + // PSK identity hint with not PSK + config = &Config{ + CipherSuites: []CipherSuiteID{TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256}, + PSK: nil, + PSKIdentityHint: []byte{}, + } + if err = validateConfig(config); !errors.Is(err, errIdentityNoPSK) { + t.Fatalf("TestValidateConfig: Client error exp(%v) failed(%v)", errIdentityNoPSK, err) + } + + // Invalid private key + dsaPrivateKey := &dsa.PrivateKey{} + err = dsa.GenerateParameters(&dsaPrivateKey.Parameters, rand.Reader, dsa.L1024N160) + if err != nil { + t.Fatalf("TestValidateConfig: Config validation error(%v), DSA parameters not generated", err) + return + } + err = dsa.GenerateKey(dsaPrivateKey, rand.Reader) + if err != nil { + t.Fatalf("TestValidateConfig: Config validation error(%v), DSA private key not generated", err) + return + } + config = &Config{ + CipherSuites: []CipherSuiteID{TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256}, + Certificates: []tls.Certificate{{Certificate: cert.Certificate, PrivateKey: dsaPrivateKey}}, + } + if err = validateConfig(config); !errors.Is(err, errInvalidPrivateKey) { + t.Fatalf("TestValidateConfig: Client error exp(%v) failed(%v)", errInvalidPrivateKey, err) + } + + // PrivateKey without Certificate + config = &Config{ + CipherSuites: []CipherSuiteID{TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256}, + Certificates: []tls.Certificate{{PrivateKey: cert.PrivateKey}}, + } + if err = validateConfig(config); !errors.Is(err, errInvalidCertificate) { + t.Fatalf("TestValidateConfig: Client error exp(%v) failed(%v)", errInvalidCertificate, err) + } + + // Invalid cipher suites + config = &Config{CipherSuites: []CipherSuiteID{0x0000}} + if err = validateConfig(config); err == nil { + t.Fatal("TestValidateConfig: Client error expected with invalid CipherSuiteID") + } + + // Valid config + rsaPrivateKey, err := rsa.GenerateKey(rand.Reader, 2048) + if err != nil { + t.Fatalf("TestValidateConfig: Config validation error(%v), RSA private key not generated", err) + return + } + config = &Config{ + CipherSuites: []CipherSuiteID{TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256}, + Certificates: []tls.Certificate{cert, {Certificate: cert.Certificate, PrivateKey: rsaPrivateKey}}, + } + if err = validateConfig(config); err != nil { + t.Fatalf("TestValidateConfig: Client error exp(%v) failed(%v)", nil, err) + } +} diff --git a/dtls-2.0.9/conn.go b/dtls-2.0.9/conn.go new file mode 100644 index 0000000..b44d6ad --- /dev/null +++ b/dtls-2.0.9/conn.go @@ -0,0 +1,979 @@ +package dtls + +import ( + "context" + "errors" + "fmt" + "io" + "net" + "sync" + "sync/atomic" + "time" + + "github.com/pion/dtls/v2/internal/closer" + "github.com/pion/dtls/v2/pkg/crypto/elliptic" + "github.com/pion/dtls/v2/pkg/crypto/signaturehash" + "github.com/pion/dtls/v2/pkg/protocol" + "github.com/pion/dtls/v2/pkg/protocol/alert" + "github.com/pion/dtls/v2/pkg/protocol/handshake" + "github.com/pion/dtls/v2/pkg/protocol/recordlayer" + "github.com/pion/logging" + "github.com/pion/transport/connctx" + "github.com/pion/transport/deadline" + "github.com/pion/transport/replaydetector" +) + +const ( + initialTickerInterval = time.Second + cookieLength = 20 + defaultNamedCurve = elliptic.X25519 + inboundBufferSize = 8192 + // Default replay protection window is specified by RFC 6347 Section 4.1.2.6 + defaultReplayProtectionWindow = 64 +) + +func invalidKeyingLabels() map[string]bool { + return map[string]bool{ + "client finished": true, + "server finished": true, + "master secret": true, + "key expansion": true, + } +} + +// Conn represents a DTLS connection +type Conn struct { + lock sync.RWMutex // Internal lock (must not be public) + nextConn connctx.ConnCtx // Embedded Conn, typically a udpconn we read/write from + fragmentBuffer *fragmentBuffer // out-of-order and missing fragment handling + handshakeCache *handshakeCache // caching of handshake messages for verifyData generation + decrypted chan interface{} // Decrypted Application Data or error, pull by calling `Read` + + state State // Internal state + + maximumTransmissionUnit int + + handshakeCompletedSuccessfully atomic.Value + + encryptedPackets [][]byte + + connectionClosedByUser bool + closeLock sync.Mutex + closed *closer.Closer + handshakeLoopsFinished sync.WaitGroup + + readDeadline *deadline.Deadline + writeDeadline *deadline.Deadline + + log logging.LeveledLogger + + reading chan struct{} + handshakeRecv chan chan struct{} + cancelHandshaker func() + cancelHandshakeReader func() + + fsm *handshakeFSM + + replayProtectionWindow uint +} + +func createConn(ctx context.Context, nextConn net.Conn, config *Config, isClient bool, initialState *State) (*Conn, error) { + err := validateConfig(config) + if err != nil { + return nil, err + } + + if nextConn == nil { + return nil, errNilNextConn + } + + cipherSuites, err := parseCipherSuites(config.CipherSuites, config.CustomCipherSuites, config.PSK == nil || len(config.Certificates) > 0, config.PSK != nil) + if err != nil { + return nil, err + } + + signatureSchemes, err := signaturehash.ParseSignatureSchemes(config.SignatureSchemes, config.InsecureHashes) + if err != nil { + return nil, err + } + + workerInterval := initialTickerInterval + if config.FlightInterval != 0 { + workerInterval = config.FlightInterval + } + + loggerFactory := config.LoggerFactory + if loggerFactory == nil { + loggerFactory = logging.NewDefaultLoggerFactory() + } + + logger := loggerFactory.NewLogger("dtls") + + mtu := config.MTU + if mtu <= 0 { + mtu = defaultMTU + } + + replayProtectionWindow := config.ReplayProtectionWindow + if replayProtectionWindow <= 0 { + replayProtectionWindow = defaultReplayProtectionWindow + } + + c := &Conn{ + nextConn: connctx.New(nextConn), + fragmentBuffer: newFragmentBuffer(), + handshakeCache: newHandshakeCache(), + maximumTransmissionUnit: mtu, + + decrypted: make(chan interface{}, 1), + log: logger, + + readDeadline: deadline.New(), + writeDeadline: deadline.New(), + + reading: make(chan struct{}, 1), + handshakeRecv: make(chan chan struct{}), + closed: closer.NewCloser(), + cancelHandshaker: func() {}, + + replayProtectionWindow: uint(replayProtectionWindow), + + state: State{ + isClient: isClient, + }, + } + + c.setRemoteEpoch(0) + c.setLocalEpoch(0) + + serverName := config.ServerName + // Use host from conn address when serverName is not provided + if isClient && serverName == "" && nextConn.RemoteAddr() != nil { + remoteAddr := nextConn.RemoteAddr().String() + var host string + host, _, err = net.SplitHostPort(remoteAddr) + if err != nil { + serverName = remoteAddr + } else { + serverName = host + } + } + + hsCfg := &handshakeConfig{ + localPSKCallback: config.PSK, + localPSKIdentityHint: config.PSKIdentityHint, + localCiscoCompatCallback: config.CiscoCompat, + localCipherSuites: cipherSuites, + localSignatureSchemes: signatureSchemes, + extendedMasterSecret: config.ExtendedMasterSecret, + localSRTPProtectionProfiles: config.SRTPProtectionProfiles, + serverName: serverName, + clientAuth: config.ClientAuth, + localCertificates: config.Certificates, + insecureSkipVerify: config.InsecureSkipVerify, + verifyPeerCertificate: config.VerifyPeerCertificate, + rootCAs: config.RootCAs, + clientCAs: config.ClientCAs, + customCipherSuites: config.CustomCipherSuites, + retransmitInterval: workerInterval, + log: logger, + initialEpoch: 0, + keyLogWriter: config.KeyLogWriter, + } + + var initialFlight flightVal + var initialFSMState handshakeState + + if initialState != nil { + if c.state.isClient { + initialFlight = flight5 + } else { + initialFlight = flight6 + } + initialFSMState = handshakeFinished + + c.state = *initialState + } else { + if c.state.isClient { + initialFlight = flight1 + } else { + initialFlight = flight0 + } + initialFSMState = handshakePreparing + } + // Do handshake + if err := c.handshake(ctx, hsCfg, initialFlight, initialFSMState); err != nil { + return nil, err + } + + c.log.Trace("Handshake Completed") + + return c, nil +} + +// Dial connects to the given network address and establishes a DTLS connection on top. +// Connection handshake will timeout using ConnectContextMaker in the Config. +// If you want to specify the timeout duration, use DialWithContext() instead. +func Dial(network string, raddr *net.UDPAddr, config *Config) (*Conn, error) { + ctx, cancel := config.connectContextMaker() + defer cancel() + + return DialWithContext(ctx, network, raddr, config) +} + +// Client establishes a DTLS connection over an existing connection. +// Connection handshake will timeout using ConnectContextMaker in the Config. +// If you want to specify the timeout duration, use ClientWithContext() instead. +func Client(conn net.Conn, config *Config) (*Conn, error) { + ctx, cancel := config.connectContextMaker() + defer cancel() + + return ClientWithContext(ctx, conn, config) +} + +// Server listens for incoming DTLS connections. +// Connection handshake will timeout using ConnectContextMaker in the Config. +// If you want to specify the timeout duration, use ServerWithContext() instead. +func Server(conn net.Conn, config *Config) (*Conn, error) { + ctx, cancel := config.connectContextMaker() + defer cancel() + + return ServerWithContext(ctx, conn, config) +} + +// DialWithContext connects to the given network address and establishes a DTLS connection on top. +func DialWithContext(ctx context.Context, network string, raddr *net.UDPAddr, config *Config) (*Conn, error) { + pConn, err := net.DialUDP(network, nil, raddr) + if err != nil { + return nil, err + } + return ClientWithContext(ctx, pConn, config) +} + +// ClientWithContext establishes a DTLS connection over an existing connection. +func ClientWithContext(ctx context.Context, conn net.Conn, config *Config) (*Conn, error) { + switch { + case config == nil: + return nil, errNoConfigProvided + case config.PSK != nil && config.PSKIdentityHint == nil: + return nil, errPSKAndIdentityMustBeSetForClient + } + + return createConn(ctx, conn, config, true, nil) +} + +// ServerWithContext listens for incoming DTLS connections. +func ServerWithContext(ctx context.Context, conn net.Conn, config *Config) (*Conn, error) { + if config == nil { + return nil, errNoConfigProvided + } + + return createConn(ctx, conn, config, false, nil) +} + +// Read reads data from the connection. +func (c *Conn) Read(p []byte) (n int, err error) { + if !c.isHandshakeCompletedSuccessfully() { + return 0, errHandshakeInProgress + } + + select { + case <-c.readDeadline.Done(): + return 0, errDeadlineExceeded + default: + } + + for { + select { + case <-c.readDeadline.Done(): + return 0, errDeadlineExceeded + case out, ok := <-c.decrypted: + if !ok { + return 0, io.EOF + } + switch val := out.(type) { + case ([]byte): + if len(p) < len(val) { + return 0, errBufferTooSmall + } + copy(p, val) + return len(val), nil + case (error): + return 0, val + } + } + } +} + +// Write writes len(p) bytes from p to the DTLS connection +func (c *Conn) Write(p []byte) (int, error) { + if c.isConnectionClosed() { + return 0, ErrConnClosed + } + + select { + case <-c.writeDeadline.Done(): + return 0, errDeadlineExceeded + default: + } + + if !c.isHandshakeCompletedSuccessfully() { + return 0, errHandshakeInProgress + } + + return len(p), c.writePackets(c.writeDeadline, []*packet{ + { + record: &recordlayer.RecordLayer{ + Header: recordlayer.Header{ + Epoch: c.getLocalEpoch(), + Version: protocol.Version1_2, + }, + Content: &protocol.ApplicationData{ + Data: p, + }, + }, + shouldEncrypt: true, + }, + }) +} + +// Close closes the connection. +func (c *Conn) Close() error { + err := c.close(true) + c.handshakeLoopsFinished.Wait() + return err +} + +// ConnectionState returns basic DTLS details about the connection. +// Note that this replaced the `Export` function of v1. +func (c *Conn) ConnectionState() State { + c.lock.RLock() + defer c.lock.RUnlock() + return *c.state.clone() +} + +// SelectedSRTPProtectionProfile returns the selected SRTPProtectionProfile +func (c *Conn) SelectedSRTPProtectionProfile() (SRTPProtectionProfile, bool) { + c.lock.RLock() + defer c.lock.RUnlock() + + if c.state.srtpProtectionProfile == 0 { + return 0, false + } + + return c.state.srtpProtectionProfile, true +} + +func (c *Conn) writePackets(ctx context.Context, pkts []*packet) error { + c.lock.Lock() + defer c.lock.Unlock() + + var rawPackets [][]byte + + for _, p := range pkts { + if h, ok := p.record.Content.(*handshake.Handshake); ok { + handshakeRaw, err := p.record.Marshal() + if err != nil { + return err + } + + c.log.Tracef("[handshake:%v] -> %s (epoch: %d, seq: %d)", + srvCliStr(c.state.isClient), h.Header.Type.String(), + p.record.Header.Epoch, h.Header.MessageSequence) + c.handshakeCache.push(handshakeRaw[recordlayer.HeaderSize:], p.record.Header.Epoch, h.Header.MessageSequence, h.Header.Type, c.state.isClient) + + rawHandshakePackets, err := c.processHandshakePacket(p, h) + if err != nil { + return err + } + rawPackets = append(rawPackets, rawHandshakePackets...) + } else { + rawPacket, err := c.processPacket(p) + if err != nil { + return err + } + rawPackets = append(rawPackets, rawPacket) + } + } + if len(rawPackets) == 0 { + return nil + } + compactedRawPackets := c.compactRawPackets(rawPackets) + + for _, compactedRawPackets := range compactedRawPackets { + if _, err := c.nextConn.WriteContext(ctx, compactedRawPackets); err != nil { + return netError(err) + } + } + + return nil +} + +func (c *Conn) compactRawPackets(rawPackets [][]byte) [][]byte { + combinedRawPackets := make([][]byte, 0) + currentCombinedRawPacket := make([]byte, 0) + + for _, rawPacket := range rawPackets { + if len(currentCombinedRawPacket) > 0 && len(currentCombinedRawPacket)+len(rawPacket) >= c.maximumTransmissionUnit { + combinedRawPackets = append(combinedRawPackets, currentCombinedRawPacket) + currentCombinedRawPacket = []byte{} + } + currentCombinedRawPacket = append(currentCombinedRawPacket, rawPacket...) + } + + combinedRawPackets = append(combinedRawPackets, currentCombinedRawPacket) + + return combinedRawPackets +} + +func (c *Conn) processPacket(p *packet) ([]byte, error) { + epoch := p.record.Header.Epoch + for len(c.state.localSequenceNumber) <= int(epoch) { + c.state.localSequenceNumber = append(c.state.localSequenceNumber, uint64(0)) + } + seq := atomic.AddUint64(&c.state.localSequenceNumber[epoch], 1) - 1 + if seq > recordlayer.MaxSequenceNumber { + // RFC 6347 Section 4.1.0 + // The implementation must either abandon an association or rehandshake + // prior to allowing the sequence number to wrap. + return nil, errSequenceNumberOverflow + } + p.record.Header.SequenceNumber = seq + + rawPacket, err := p.record.Marshal() + if err != nil { + return nil, err + } + + if p.shouldEncrypt { + var err error + rawPacket, err = c.state.cipherSuite.Encrypt(p.record, rawPacket) + if err != nil { + return nil, err + } + } + + return rawPacket, nil +} + +func (c *Conn) processHandshakePacket(p *packet, h *handshake.Handshake) ([][]byte, error) { + rawPackets := make([][]byte, 0) + + handshakeFragments, err := c.fragmentHandshake(h) + if err != nil { + return nil, err + } + epoch := p.record.Header.Epoch + for len(c.state.localSequenceNumber) <= int(epoch) { + c.state.localSequenceNumber = append(c.state.localSequenceNumber, uint64(0)) + } + + for _, handshakeFragment := range handshakeFragments { + seq := atomic.AddUint64(&c.state.localSequenceNumber[epoch], 1) - 1 + if seq > recordlayer.MaxSequenceNumber { + return nil, errSequenceNumberOverflow + } + + recordlayerHeader := &recordlayer.Header{ + Version: p.record.Header.Version, + ContentType: p.record.Header.ContentType, + ContentLen: uint16(len(handshakeFragment)), + Epoch: p.record.Header.Epoch, + SequenceNumber: seq, + } + + recordlayerHeaderBytes, err := recordlayerHeader.Marshal() + if err != nil { + return nil, err + } + + p.record.Header = *recordlayerHeader + + rawPacket := append(recordlayerHeaderBytes, handshakeFragment...) + if p.shouldEncrypt { + var err error + rawPacket, err = c.state.cipherSuite.Encrypt(p.record, rawPacket) + if err != nil { + return nil, err + } + } + + rawPackets = append(rawPackets, rawPacket) + } + + return rawPackets, nil +} + +func (c *Conn) fragmentHandshake(h *handshake.Handshake) ([][]byte, error) { + content, err := h.Message.Marshal() + if err != nil { + return nil, err + } + + fragmentedHandshakes := make([][]byte, 0) + + contentFragments := splitBytes(content, c.maximumTransmissionUnit) + if len(contentFragments) == 0 { + contentFragments = [][]byte{ + {}, + } + } + + offset := 0 + for _, contentFragment := range contentFragments { + contentFragmentLen := len(contentFragment) + + headerFragment := &handshake.Header{ + Type: h.Header.Type, + Length: h.Header.Length, + MessageSequence: h.Header.MessageSequence, + FragmentOffset: uint32(offset), + FragmentLength: uint32(contentFragmentLen), + } + + offset += contentFragmentLen + + headerFragmentRaw, err := headerFragment.Marshal() + if err != nil { + return nil, err + } + + fragmentedHandshake := append(headerFragmentRaw, contentFragment...) + fragmentedHandshakes = append(fragmentedHandshakes, fragmentedHandshake) + } + + return fragmentedHandshakes, nil +} + +var poolReadBuffer = sync.Pool{ //nolint:gochecknoglobals + New: func() interface{} { + b := make([]byte, inboundBufferSize) + return &b + }, +} + +func (c *Conn) readAndBuffer(ctx context.Context) error { + bufptr := poolReadBuffer.Get().(*[]byte) + defer poolReadBuffer.Put(bufptr) + + b := *bufptr + i, err := c.nextConn.ReadContext(ctx, b) + if err != nil { + return netError(err) + } + + pkts, err := recordlayer.UnpackDatagram(b[:i]) + if err != nil { + return err + } + + var hasHandshake bool + for _, p := range pkts { + hs, alert, err := c.handleIncomingPacket(p, true) + if alert != nil { + if alertErr := c.notify(ctx, alert.Level, alert.Description); alertErr != nil { + if err == nil { + err = alertErr + } + } + } + if hs { + hasHandshake = true + } + switch e := err.(type) { + case nil: + case *errAlert: + if e.IsFatalOrCloseNotify() { + return e + } + default: + return e + } + } + if hasHandshake { + done := make(chan struct{}) + select { + case c.handshakeRecv <- done: + // If the other party may retransmit the flight, + // we should respond even if it not a new message. + <-done + case <-c.fsm.Done(): + } + } + return nil +} + +func (c *Conn) handleQueuedPackets(ctx context.Context) error { + pkts := c.encryptedPackets + c.encryptedPackets = nil + + for _, p := range pkts { + _, alert, err := c.handleIncomingPacket(p, false) // don't re-enqueue + if alert != nil { + if alertErr := c.notify(ctx, alert.Level, alert.Description); alertErr != nil { + if err == nil { + err = alertErr + } + } + } + switch e := err.(type) { + case nil: + case *errAlert: + if e.IsFatalOrCloseNotify() { + return e + } + default: + return e + } + } + return nil +} + +func (c *Conn) handleIncomingPacket(buf []byte, enqueue bool) (bool, *alert.Alert, error) { //nolint:gocognit + h := &recordlayer.Header{} + if err := h.Unmarshal(buf); err != nil { + // Decode error must be silently discarded + // [RFC6347 Section-4.1.2.7] + c.log.Debugf("discarded broken packet: %v", err) + return false, nil, nil + } + + // Validate epoch + remoteEpoch := c.getRemoteEpoch() + if h.Epoch > remoteEpoch { + if h.Epoch > remoteEpoch+1 { + c.log.Debugf("discarded future packet (epoch: %d, seq: %d)", + h.Epoch, h.SequenceNumber, + ) + return false, nil, nil + } + if enqueue { + c.log.Debug("received packet of next epoch, queuing packet") + c.encryptedPackets = append(c.encryptedPackets, buf) + } + return false, nil, nil + } + + // Anti-replay protection + for len(c.state.replayDetector) <= int(h.Epoch) { + c.state.replayDetector = append(c.state.replayDetector, + replaydetector.New(c.replayProtectionWindow, recordlayer.MaxSequenceNumber), + ) + } + markPacketAsValid, ok := c.state.replayDetector[int(h.Epoch)].Check(h.SequenceNumber) + if !ok { + c.log.Debugf("discarded duplicated packet (epoch: %d, seq: %d)", + h.Epoch, h.SequenceNumber, + ) + return false, nil, nil + } + + // Decrypt + if h.Epoch != 0 { + if c.state.cipherSuite == nil || !c.state.cipherSuite.IsInitialized() { + if enqueue { + c.encryptedPackets = append(c.encryptedPackets, buf) + c.log.Debug("handshake not finished, queuing packet") + } + return false, nil, nil + } + + var err error + buf, err = c.state.cipherSuite.Decrypt(buf) + if err != nil { + c.log.Debugf("%s: decrypt failed: %s", srvCliStr(c.state.isClient), err) + return false, nil, nil + } + } + + isHandshake, err := c.fragmentBuffer.push(append([]byte{}, buf...)) + if err != nil { + // Decode error must be silently discarded + // [RFC6347 Section-4.1.2.7] + c.log.Debugf("defragment failed: %s", err) + return false, nil, nil + } else if isHandshake { + markPacketAsValid() + for out, epoch := c.fragmentBuffer.pop(); out != nil; out, epoch = c.fragmentBuffer.pop() { + rawHandshake := &handshake.Handshake{} + if err := rawHandshake.Unmarshal(out); err != nil { + c.log.Debugf("%s: handshake parse failed: %s", srvCliStr(c.state.isClient), err) + continue + } + + _ = c.handshakeCache.push(out, epoch, rawHandshake.Header.MessageSequence, rawHandshake.Header.Type, !c.state.isClient) + } + + return true, nil, nil + } + + r := &recordlayer.RecordLayer{} + if err := r.Unmarshal(buf); err != nil { + return false, &alert.Alert{Level: alert.Fatal, Description: alert.DecodeError}, err + } + + switch content := r.Content.(type) { + case *alert.Alert: + c.log.Tracef("%s: <- %s", srvCliStr(c.state.isClient), content.String()) + var a *alert.Alert + if content.Description == alert.CloseNotify { + // Respond with a close_notify [RFC5246 Section 7.2.1] + a = &alert.Alert{Level: alert.Warning, Description: alert.CloseNotify} + } + markPacketAsValid() + return false, a, &errAlert{content} + case *protocol.ChangeCipherSpec: + if c.state.cipherSuite == nil || !c.state.cipherSuite.IsInitialized() { + if enqueue { + c.encryptedPackets = append(c.encryptedPackets, buf) + c.log.Debugf("CipherSuite not initialized, queuing packet") + } + return false, nil, nil + } + + newRemoteEpoch := h.Epoch + 1 + c.log.Tracef("%s: <- ChangeCipherSpec (epoch: %d)", srvCliStr(c.state.isClient), newRemoteEpoch) + + if c.getRemoteEpoch()+1 == newRemoteEpoch { + c.setRemoteEpoch(newRemoteEpoch) + markPacketAsValid() + } + case *protocol.ApplicationData: + if h.Epoch == 0 { + return false, &alert.Alert{Level: alert.Fatal, Description: alert.UnexpectedMessage}, errApplicationDataEpochZero + } + + markPacketAsValid() + + select { + case c.decrypted <- content.Data: + case <-c.closed.Done(): + } + + default: + return false, &alert.Alert{Level: alert.Fatal, Description: alert.UnexpectedMessage}, fmt.Errorf("%w: %d", errUnhandledContextType, content.ContentType()) + } + return false, nil, nil +} + +func (c *Conn) recvHandshake() <-chan chan struct{} { + return c.handshakeRecv +} + +func (c *Conn) notify(ctx context.Context, level alert.Level, desc alert.Description) error { + return c.writePackets(ctx, []*packet{ + { + record: &recordlayer.RecordLayer{ + Header: recordlayer.Header{ + Epoch: c.getLocalEpoch(), + Version: protocol.Version1_2, + }, + Content: &alert.Alert{ + Level: level, + Description: desc, + }, + }, + shouldEncrypt: c.isHandshakeCompletedSuccessfully(), + }, + }) +} + +func (c *Conn) setHandshakeCompletedSuccessfully() { + c.handshakeCompletedSuccessfully.Store(struct{ bool }{true}) +} + +func (c *Conn) isHandshakeCompletedSuccessfully() bool { + boolean, _ := c.handshakeCompletedSuccessfully.Load().(struct{ bool }) + return boolean.bool +} + +func (c *Conn) handshake(ctx context.Context, cfg *handshakeConfig, initialFlight flightVal, initialState handshakeState) error { //nolint:gocognit + c.fsm = newHandshakeFSM(&c.state, c.handshakeCache, cfg, initialFlight) + + done := make(chan struct{}) + ctxRead, cancelRead := context.WithCancel(context.Background()) + c.cancelHandshakeReader = cancelRead + cfg.onFlightState = func(f flightVal, s handshakeState) { + if s == handshakeFinished && !c.isHandshakeCompletedSuccessfully() { + c.setHandshakeCompletedSuccessfully() + close(done) + } + } + + ctxHs, cancel := context.WithCancel(context.Background()) + c.cancelHandshaker = cancel + + firstErr := make(chan error, 1) + + c.handshakeLoopsFinished.Add(2) + + // Handshake routine should be live until close. + // The other party may request retransmission of the last flight to cope with packet drop. + go func() { + defer c.handshakeLoopsFinished.Done() + err := c.fsm.Run(ctxHs, c, initialState) + if !errors.Is(err, context.Canceled) { + select { + case firstErr <- err: + default: + } + } + }() + go func() { + defer func() { + // Escaping read loop. + // It's safe to close decrypted channnel now. + close(c.decrypted) + + // Force stop handshaker when the underlying connection is closed. + cancel() + }() + defer c.handshakeLoopsFinished.Done() + for { + if err := c.readAndBuffer(ctxRead); err != nil { + switch e := err.(type) { + case *errAlert: + if !e.IsFatalOrCloseNotify() { + if c.isHandshakeCompletedSuccessfully() { + // Pass the error to Read() + select { + case c.decrypted <- err: + case <-c.closed.Done(): + } + } + continue // non-fatal alert must not stop read loop + } + case error: + switch err { + case context.DeadlineExceeded, context.Canceled, io.EOF: + default: + if c.isHandshakeCompletedSuccessfully() { + // Keep read loop and pass the read error to Read() + select { + case c.decrypted <- err: + case <-c.closed.Done(): + } + continue // non-fatal alert must not stop read loop + } + } + } + select { + case firstErr <- err: + default: + } + + if e, ok := err.(*errAlert); ok { + if e.IsFatalOrCloseNotify() { + _ = c.close(false) + } + } + return + } + } + }() + + select { + case err := <-firstErr: + cancelRead() + cancel() + return c.translateHandshakeCtxError(err) + case <-ctx.Done(): + cancelRead() + cancel() + return c.translateHandshakeCtxError(ctx.Err()) + case <-done: + return nil + } +} + +func (c *Conn) translateHandshakeCtxError(err error) error { + if err == nil { + return nil + } + if errors.Is(err, context.Canceled) && c.isHandshakeCompletedSuccessfully() { + return nil + } + return &HandshakeError{Err: err} +} + +func (c *Conn) close(byUser bool) error { + c.cancelHandshaker() + c.cancelHandshakeReader() + + if c.isHandshakeCompletedSuccessfully() && byUser { + // Discard error from notify() to return non-error on the first user call of Close() + // even if the underlying connection is already closed. + _ = c.notify(context.Background(), alert.Warning, alert.CloseNotify) + } + + c.closeLock.Lock() + // Don't return ErrConnClosed at the first time of the call from user. + closedByUser := c.connectionClosedByUser + if byUser { + c.connectionClosedByUser = true + } + c.closed.Close() + c.closeLock.Unlock() + + if closedByUser { + return ErrConnClosed + } + + return c.nextConn.Close() +} + +func (c *Conn) isConnectionClosed() bool { + select { + case <-c.closed.Done(): + return true + default: + return false + } +} + +func (c *Conn) setLocalEpoch(epoch uint16) { + c.state.localEpoch.Store(epoch) +} + +func (c *Conn) getLocalEpoch() uint16 { + return c.state.localEpoch.Load().(uint16) +} + +func (c *Conn) setRemoteEpoch(epoch uint16) { + c.state.remoteEpoch.Store(epoch) +} + +func (c *Conn) getRemoteEpoch() uint16 { + return c.state.remoteEpoch.Load().(uint16) +} + +// LocalAddr implements net.Conn.LocalAddr +func (c *Conn) LocalAddr() net.Addr { + return c.nextConn.LocalAddr() +} + +// RemoteAddr implements net.Conn.RemoteAddr +func (c *Conn) RemoteAddr() net.Addr { + return c.nextConn.RemoteAddr() +} + +// SetDeadline implements net.Conn.SetDeadline +func (c *Conn) SetDeadline(t time.Time) error { + c.readDeadline.Set(t) + return c.SetWriteDeadline(t) +} + +// SetReadDeadline implements net.Conn.SetReadDeadline +func (c *Conn) SetReadDeadline(t time.Time) error { + c.readDeadline.Set(t) + // Read deadline is fully managed by this layer. + // Don't set read deadline to underlying connection. + return nil +} + +// SetWriteDeadline implements net.Conn.SetWriteDeadline +func (c *Conn) SetWriteDeadline(t time.Time) error { + c.writeDeadline.Set(t) + // Write deadline is also fully managed by this layer. + return nil +} diff --git a/dtls-2.0.9/conn_go_test.go b/dtls-2.0.9/conn_go_test.go new file mode 100644 index 0000000..17a1c62 --- /dev/null +++ b/dtls-2.0.9/conn_go_test.go @@ -0,0 +1,169 @@ +// +build !js + +package dtls + +import ( + "bytes" + "context" + "crypto/tls" + "net" + "testing" + "time" + + "github.com/pion/dtls/v2/internal/net/dpipe" + "github.com/pion/dtls/v2/pkg/crypto/selfsign" + "github.com/pion/transport/test" +) + +func TestContextConfig(t *testing.T) { + // Limit runtime in case of deadlocks + lim := test.TimeOut(time.Second * 20) + defer lim.Stop() + + report := test.CheckRoutines(t) + defer report() + + addrListen, err := net.ResolveUDPAddr("udp", "localhost:0") + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + // Dummy listener + listen, err := net.ListenUDP("udp", addrListen) + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + defer func() { + _ = listen.Close() + }() + addr := listen.LocalAddr().(*net.UDPAddr) + + cert, err := selfsign.GenerateSelfSigned() + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + config := &Config{ + ConnectContextMaker: func() (context.Context, func()) { + return context.WithTimeout(context.Background(), 40*time.Millisecond) + }, + Certificates: []tls.Certificate{cert}, + } + + dials := map[string]struct { + f func() (func() (net.Conn, error), func()) + order []byte + }{ + "Dial": { + f: func() (func() (net.Conn, error), func()) { + return func() (net.Conn, error) { + return Dial("udp", addr, config) + }, func() { + } + }, + order: []byte{0, 1, 2}, + }, + "DialWithContext": { + f: func() (func() (net.Conn, error), func()) { + ctx, cancel := context.WithTimeout(context.Background(), 80*time.Millisecond) + return func() (net.Conn, error) { + return DialWithContext(ctx, "udp", addr, config) + }, func() { + cancel() + } + }, + order: []byte{0, 2, 1}, + }, + "Client": { + f: func() (func() (net.Conn, error), func()) { + ca, _ := dpipe.Pipe() + return func() (net.Conn, error) { + return Client(ca, config) + }, func() { + _ = ca.Close() + } + }, + order: []byte{0, 1, 2}, + }, + "ClientWithContext": { + f: func() (func() (net.Conn, error), func()) { + ctx, cancel := context.WithTimeout(context.Background(), 80*time.Millisecond) + ca, _ := dpipe.Pipe() + return func() (net.Conn, error) { + return ClientWithContext(ctx, ca, config) + }, func() { + cancel() + _ = ca.Close() + } + }, + order: []byte{0, 2, 1}, + }, + "Server": { + f: func() (func() (net.Conn, error), func()) { + ca, _ := dpipe.Pipe() + return func() (net.Conn, error) { + return Server(ca, config) + }, func() { + _ = ca.Close() + } + }, + order: []byte{0, 1, 2}, + }, + "ServerWithContext": { + f: func() (func() (net.Conn, error), func()) { + ctx, cancel := context.WithTimeout(context.Background(), 80*time.Millisecond) + ca, _ := dpipe.Pipe() + return func() (net.Conn, error) { + return ServerWithContext(ctx, ca, config) + }, func() { + cancel() + _ = ca.Close() + } + }, + order: []byte{0, 2, 1}, + }, + } + + for name, dial := range dials { + dial := dial + t.Run(name, func(t *testing.T) { + done := make(chan struct{}) + + go func() { + d, cancel := dial.f() + conn, err := d() + defer cancel() + if netErr, ok := err.(net.Error); !ok || !netErr.Timeout() { + t.Errorf("Client error exp(Temporary network error) failed(%v)", err) + close(done) + return + } + done <- struct{}{} + if err == nil { + _ = conn.Close() + } + }() + + var order []byte + early := time.After(20 * time.Millisecond) + late := time.After(60 * time.Millisecond) + func() { + for len(order) < 3 { + select { + case <-early: + order = append(order, 0) + case _, ok := <-done: + if !ok { + return + } + order = append(order, 1) + case <-late: + order = append(order, 2) + } + } + }() + if !bytes.Equal(dial.order, order) { + t.Errorf("Invalid cancel timing, expected: %v, got: %v", dial.order, order) + } + }) + } +} diff --git a/dtls-2.0.9/conn_test.go b/dtls-2.0.9/conn_test.go new file mode 100644 index 0000000..b532926 --- /dev/null +++ b/dtls-2.0.9/conn_test.go @@ -0,0 +1,2026 @@ +package dtls + +import ( + "bytes" + "context" + "crypto/rand" + "crypto/tls" + "crypto/x509" + "errors" + "fmt" + "io" + "net" + "sync" + "sync/atomic" + "testing" + "time" + + "github.com/pion/dtls/v2/internal/ciphersuite" + "github.com/pion/dtls/v2/internal/net/dpipe" + "github.com/pion/dtls/v2/pkg/crypto/elliptic" + "github.com/pion/dtls/v2/pkg/crypto/hash" + "github.com/pion/dtls/v2/pkg/crypto/selfsign" + "github.com/pion/dtls/v2/pkg/crypto/signature" + "github.com/pion/dtls/v2/pkg/crypto/signaturehash" + "github.com/pion/dtls/v2/pkg/protocol" + "github.com/pion/dtls/v2/pkg/protocol/alert" + "github.com/pion/dtls/v2/pkg/protocol/extension" + "github.com/pion/dtls/v2/pkg/protocol/handshake" + "github.com/pion/dtls/v2/pkg/protocol/recordlayer" + "github.com/pion/transport/test" +) + +var ( + errTestPSKInvalidIdentity = errors.New("TestPSK: Server got invalid identity") + errPSKRejected = errors.New("PSK Rejected") + errNotExpectedChain = errors.New("not expected chain") + errExpecedChain = errors.New("expected chain") + errWrongCert = errors.New("wrong cert") +) + +func TestStressDuplex(t *testing.T) { + // Limit runtime in case of deadlocks + lim := test.TimeOut(time.Second * 20) + defer lim.Stop() + + // Check for leaking routines + report := test.CheckRoutines(t) + defer report() + + // Run the test + stressDuplex(t) +} + +func stressDuplex(t *testing.T) { + ca, cb, err := pipeMemory() + if err != nil { + t.Fatal(err) + } + + defer func() { + err = ca.Close() + if err != nil { + t.Fatal(err) + } + err = cb.Close() + if err != nil { + t.Fatal(err) + } + }() + + opt := test.Options{ + MsgSize: 2048, + MsgCount: 100, + } + + err = test.StressDuplex(ca, cb, opt) + if err != nil { + t.Fatal(err) + } +} + +func TestRoutineLeakOnClose(t *testing.T) { + // Limit runtime in case of deadlocks + lim := test.TimeOut(5 * time.Second) + defer lim.Stop() + + // Check for leaking routines + report := test.CheckRoutines(t) + defer report() + + ca, cb, err := pipeMemory() + if err != nil { + t.Fatal(err) + } + + if _, err := ca.Write(make([]byte, 100)); err != nil { + t.Fatal(err) + } + if err := cb.Close(); err != nil { + t.Fatal(err) + } + if err := ca.Close(); err != nil { + t.Fatal(err) + } + // Packet is sent, but not read. + // inboundLoop routine should not be leaked. +} + +func TestReadWriteDeadline(t *testing.T) { + // Limit runtime in case of deadlocks + lim := test.TimeOut(5 * time.Second) + defer lim.Stop() + + // Check for leaking routines + report := test.CheckRoutines(t) + defer report() + + ca, cb, err := pipeMemory() + if err != nil { + t.Fatal(err) + } + + if err := ca.SetDeadline(time.Unix(0, 1)); err != nil { + t.Fatal(err) + } + _, werr := ca.Write(make([]byte, 100)) + if e, ok := werr.(net.Error); ok { + if !e.Timeout() { + t.Error("Deadline exceeded Write must return Timeout error") + } + if !e.Temporary() { + t.Error("Deadline exceeded Write must return Temporary error") + } + } else { + t.Error("Write must return net.Error error") + } + _, rerr := ca.Read(make([]byte, 100)) + if e, ok := rerr.(net.Error); ok { + if !e.Timeout() { + t.Error("Deadline exceeded Read must return Timeout error") + } + if !e.Temporary() { + t.Error("Deadline exceeded Read must return Temporary error") + } + } else { + t.Error("Read must return net.Error error") + } + if err := ca.SetDeadline(time.Time{}); err != nil { + t.Error(err) + } + + if err := ca.Close(); err != nil { + t.Error(err) + } + if err := cb.Close(); err != nil { + t.Error(err) + } + + if _, err := ca.Write(make([]byte, 100)); !errors.Is(err, ErrConnClosed) { + t.Errorf("Write must return %v after close, got %v", ErrConnClosed, err) + } + if _, err := ca.Read(make([]byte, 100)); !errors.Is(err, io.EOF) { + t.Errorf("Read must return %v after close, got %v", io.EOF, err) + } +} + +func TestSequenceNumberOverflow(t *testing.T) { + // Limit runtime in case of deadlocks + lim := test.TimeOut(5 * time.Second) + defer lim.Stop() + + // Check for leaking routines + report := test.CheckRoutines(t) + defer report() + + t.Run("ApplicationData", func(t *testing.T) { + ca, cb, err := pipeMemory() + if err != nil { + t.Fatal(err) + } + + atomic.StoreUint64(&ca.state.localSequenceNumber[1], recordlayer.MaxSequenceNumber) + if _, werr := ca.Write(make([]byte, 100)); werr != nil { + t.Errorf("Write must send message with maximum sequence number, but errord: %v", werr) + } + if _, werr := ca.Write(make([]byte, 100)); !errors.Is(werr, errSequenceNumberOverflow) { + t.Errorf("Write must abandonsend message with maximum sequence number, but errord: %v", werr) + } + + if err := ca.Close(); err != nil { + t.Error(err) + } + if err := cb.Close(); err != nil { + t.Error(err) + } + }) + t.Run("Handshake", func(t *testing.T) { + ca, cb, err := pipeMemory() + if err != nil { + t.Fatal(err) + } + + ctx, cancel := context.WithTimeout(context.Background(), time.Second) + defer cancel() + + atomic.StoreUint64(&ca.state.localSequenceNumber[0], recordlayer.MaxSequenceNumber+1) + + // Try to send handshake packet. + if werr := ca.writePackets(ctx, []*packet{ + { + record: &recordlayer.RecordLayer{ + Header: recordlayer.Header{ + Version: protocol.Version1_2, + }, + Content: &handshake.Handshake{ + Message: &handshake.MessageClientHello{ + Version: protocol.Version1_2, + Cookie: make([]byte, 64), + CipherSuiteIDs: cipherSuiteIDs(defaultCipherSuites()), + CompressionMethods: defaultCompressionMethods(), + }, + }, + }, + }, + }); !errors.Is(werr, errSequenceNumberOverflow) { + t.Errorf("Connection must fail on handshake packet reaches maximum sequence number") + } + + if err := ca.Close(); err != nil { + t.Error(err) + } + if err := cb.Close(); err != nil { + t.Error(err) + } + }) +} + +func pipeMemory() (*Conn, *Conn, error) { + // In memory pipe + ca, cb := dpipe.Pipe() + return pipeConn(ca, cb) +} + +func pipeConn(ca, cb net.Conn) (*Conn, *Conn, error) { + type result struct { + c *Conn + err error + } + + c := make(chan result) + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + defer cancel() + + // Setup client + go func() { + client, err := testClient(ctx, ca, &Config{SRTPProtectionProfiles: []SRTPProtectionProfile{SRTP_AES128_CM_HMAC_SHA1_80}}, true) + c <- result{client, err} + }() + + // Setup server + server, err := testServer(ctx, cb, &Config{SRTPProtectionProfiles: []SRTPProtectionProfile{SRTP_AES128_CM_HMAC_SHA1_80}}, true) + if err != nil { + return nil, nil, err + } + + // Receive client + res := <-c + if res.err != nil { + return nil, nil, res.err + } + + return res.c, server, nil +} + +func testClient(ctx context.Context, c net.Conn, cfg *Config, generateCertificate bool) (*Conn, error) { + if generateCertificate { + clientCert, err := selfsign.GenerateSelfSigned() + if err != nil { + return nil, err + } + cfg.Certificates = []tls.Certificate{clientCert} + } + cfg.InsecureSkipVerify = true + return ClientWithContext(ctx, c, cfg) +} + +func testServer(ctx context.Context, c net.Conn, cfg *Config, generateCertificate bool) (*Conn, error) { + if generateCertificate { + serverCert, err := selfsign.GenerateSelfSigned() + if err != nil { + return nil, err + } + cfg.Certificates = []tls.Certificate{serverCert} + } + return ServerWithContext(ctx, c, cfg) +} + +func TestHandshakeWithAlert(t *testing.T) { + // Limit runtime in case of deadlocks + lim := test.TimeOut(time.Second * 20) + defer lim.Stop() + + // Check for leaking routines + report := test.CheckRoutines(t) + defer report() + + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + defer cancel() + + cases := map[string]struct { + configServer, configClient *Config + errServer, errClient error + }{ + "CipherSuiteNoIntersection": { + configServer: &Config{ + CipherSuites: []CipherSuiteID{TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256}, + }, + configClient: &Config{ + CipherSuites: []CipherSuiteID{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256}, + }, + errServer: errCipherSuiteNoIntersection, + errClient: &errAlert{&alert.Alert{Level: alert.Fatal, Description: alert.InsufficientSecurity}}, + }, + "SignatureSchemesNoIntersection": { + configServer: &Config{ + CipherSuites: []CipherSuiteID{TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256}, + SignatureSchemes: []tls.SignatureScheme{tls.ECDSAWithP256AndSHA256}, + }, + configClient: &Config{ + CipherSuites: []CipherSuiteID{TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256}, + SignatureSchemes: []tls.SignatureScheme{tls.ECDSAWithP521AndSHA512}, + }, + errServer: &errAlert{&alert.Alert{Level: alert.Fatal, Description: alert.InsufficientSecurity}}, + errClient: errNoAvailableSignatureSchemes, + }, + } + + for name, testCase := range cases { + testCase := testCase + t.Run(name, func(t *testing.T) { + clientErr := make(chan error, 1) + + ca, cb := dpipe.Pipe() + go func() { + _, err := testClient(ctx, ca, testCase.configClient, true) + clientErr <- err + }() + + _, errServer := testServer(ctx, cb, testCase.configServer, true) + if !errors.Is(errServer, testCase.errServer) { + t.Fatalf("Server error exp(%v) failed(%v)", testCase.errServer, errServer) + } + + errClient := <-clientErr + if !errors.Is(errClient, testCase.errClient) { + t.Fatalf("Client error exp(%v) failed(%v)", testCase.errClient, errClient) + } + }) + } +} + +func TestExportKeyingMaterial(t *testing.T) { + // Check for leaking routines + report := test.CheckRoutines(t) + defer report() + + var rand [28]byte + exportLabel := "EXTRACTOR-dtls_srtp" + + expectedServerKey := []byte{0x61, 0x09, 0x9d, 0x7d, 0xcb, 0x08, 0x52, 0x2c, 0xe7, 0x7b} + expectedClientKey := []byte{0x87, 0xf0, 0x40, 0x02, 0xf6, 0x1c, 0xf1, 0xfe, 0x8c, 0x77} + + c := &Conn{ + state: State{ + localRandom: handshake.Random{GMTUnixTime: time.Unix(500, 0), RandomBytes: rand}, + remoteRandom: handshake.Random{GMTUnixTime: time.Unix(1000, 0), RandomBytes: rand}, + localSequenceNumber: []uint64{0, 0}, + cipherSuite: &ciphersuite.TLSEcdheEcdsaWithAes128GcmSha256{}, + }, + } + c.setLocalEpoch(0) + c.setRemoteEpoch(0) + + state := c.ConnectionState() + _, err := state.ExportKeyingMaterial(exportLabel, nil, 0) + if !errors.Is(err, errHandshakeInProgress) { + t.Errorf("ExportKeyingMaterial when epoch == 0: expected '%s' actual '%s'", errHandshakeInProgress, err) + } + + c.setLocalEpoch(1) + state = c.ConnectionState() + _, err = state.ExportKeyingMaterial(exportLabel, []byte{0x00}, 0) + if !errors.Is(err, errContextUnsupported) { + t.Errorf("ExportKeyingMaterial with context: expected '%s' actual '%s'", errContextUnsupported, err) + } + + for k := range invalidKeyingLabels() { + state = c.ConnectionState() + _, err = state.ExportKeyingMaterial(k, nil, 0) + if !errors.Is(err, errReservedExportKeyingMaterial) { + t.Errorf("ExportKeyingMaterial reserved label: expected '%s' actual '%s'", errReservedExportKeyingMaterial, err) + } + } + + state = c.ConnectionState() + keyingMaterial, err := state.ExportKeyingMaterial(exportLabel, nil, 10) + if err != nil { + t.Errorf("ExportKeyingMaterial as server: unexpected error '%s'", err) + } else if !bytes.Equal(keyingMaterial, expectedServerKey) { + t.Errorf("ExportKeyingMaterial client export: expected (% 02x) actual (% 02x)", expectedServerKey, keyingMaterial) + } + + c.state.isClient = true + state = c.ConnectionState() + keyingMaterial, err = state.ExportKeyingMaterial(exportLabel, nil, 10) + if err != nil { + t.Errorf("ExportKeyingMaterial as server: unexpected error '%s'", err) + } else if !bytes.Equal(keyingMaterial, expectedClientKey) { + t.Errorf("ExportKeyingMaterial client export: expected (% 02x) actual (% 02x)", expectedClientKey, keyingMaterial) + } +} + +func TestPSK(t *testing.T) { + // Limit runtime in case of deadlocks + lim := test.TimeOut(time.Second * 20) + defer lim.Stop() + + // Check for leaking routines + report := test.CheckRoutines(t) + defer report() + + for _, test := range []struct { + Name string + ServerIdentity []byte + CipherSuites []CipherSuiteID + }{ + { + Name: "Server identity specified", + ServerIdentity: []byte("Test Identity"), + CipherSuites: []CipherSuiteID{TLS_PSK_WITH_AES_128_CCM_8}, + }, + { + Name: "Server identity nil", + ServerIdentity: nil, + CipherSuites: []CipherSuiteID{TLS_PSK_WITH_AES_128_CCM_8}, + }, + { + Name: "TLS_PSK_WITH_AES_128_CBC_SHA256", + ServerIdentity: nil, + CipherSuites: []CipherSuiteID{TLS_PSK_WITH_AES_128_CBC_SHA256}, + }, + } { + test := test + t.Run(test.Name, func(t *testing.T) { + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + defer cancel() + + clientIdentity := []byte("Client Identity") + type result struct { + c *Conn + err error + } + clientRes := make(chan result, 1) + + ca, cb := dpipe.Pipe() + go func() { + conf := &Config{ + PSK: func(hint []byte) ([]byte, error) { + if !bytes.Equal(test.ServerIdentity, hint) { // nolint + return nil, fmt.Errorf("TestPSK: Client got invalid identity expected(% 02x) actual(% 02x)", test.ServerIdentity, hint) // nolint + } + + return []byte{0xAB, 0xC1, 0x23}, nil + }, + PSKIdentityHint: clientIdentity, + CipherSuites: test.CipherSuites, + } + + c, err := testClient(ctx, ca, conf, false) + clientRes <- result{c, err} + }() + + config := &Config{ + PSK: func(hint []byte) ([]byte, error) { + if !bytes.Equal(clientIdentity, hint) { + return nil, fmt.Errorf("%w: expected(% 02x) actual(% 02x)", errTestPSKInvalidIdentity, clientIdentity, hint) + } + return []byte{0xAB, 0xC1, 0x23}, nil + }, + PSKIdentityHint: test.ServerIdentity, + CipherSuites: test.CipherSuites, + } + + server, err := testServer(ctx, cb, config, false) + if err != nil { + t.Fatalf("TestPSK: Server failed(%v)", err) + } + + actualPSKIdentityHint := server.ConnectionState().IdentityHint + if !bytes.Equal(actualPSKIdentityHint, clientIdentity) { + t.Errorf("TestPSK: Server ClientPSKIdentity Mismatch '%s': expected(%v) actual(%v)", test.Name, clientIdentity, actualPSKIdentityHint) + } + + defer func() { + _ = server.Close() + }() + + res := <-clientRes + if res.err != nil { + t.Fatal(res.err) + } + _ = res.c.Close() + }) + } +} + +func TestPSKHintFail(t *testing.T) { + // Check for leaking routines + report := test.CheckRoutines(t) + defer report() + + serverAlertError := &errAlert{&alert.Alert{Level: alert.Fatal, Description: alert.InternalError}} + pskRejected := errPSKRejected + + // Limit runtime in case of deadlocks + lim := test.TimeOut(time.Second * 20) + defer lim.Stop() + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + defer cancel() + + clientErr := make(chan error, 1) + + ca, cb := dpipe.Pipe() + go func() { + conf := &Config{ + PSK: func(hint []byte) ([]byte, error) { + return nil, pskRejected + }, + PSKIdentityHint: []byte{}, + CipherSuites: []CipherSuiteID{TLS_PSK_WITH_AES_128_CCM_8}, + } + + _, err := testClient(ctx, ca, conf, false) + clientErr <- err + }() + + config := &Config{ + PSK: func(hint []byte) ([]byte, error) { + return nil, pskRejected + }, + PSKIdentityHint: []byte{}, + CipherSuites: []CipherSuiteID{TLS_PSK_WITH_AES_128_CCM_8}, + } + + if _, err := testServer(ctx, cb, config, false); !errors.Is(err, serverAlertError) { + t.Fatalf("TestPSK: Server error exp(%v) failed(%v)", serverAlertError, err) + } + + if err := <-clientErr; !errors.Is(err, pskRejected) { + t.Fatalf("TestPSK: Client error exp(%v) failed(%v)", pskRejected, err) + } +} + +func TestClientTimeout(t *testing.T) { + // Limit runtime in case of deadlocks + lim := test.TimeOut(time.Second * 20) + defer lim.Stop() + + // Check for leaking routines + report := test.CheckRoutines(t) + defer report() + + ctx, cancel := context.WithTimeout(context.Background(), time.Second) + defer cancel() + + clientErr := make(chan error, 1) + + ca, _ := dpipe.Pipe() + go func() { + conf := &Config{} + + c, err := testClient(ctx, ca, conf, true) + if err == nil { + _ = c.Close() + } + clientErr <- err + }() + + // no server! + err := <-clientErr + if netErr, ok := err.(net.Error); !ok || !netErr.Timeout() { + t.Fatalf("Client error exp(Temporary network error) failed(%v)", err) + } +} + +func TestSRTPConfiguration(t *testing.T) { + // Check for leaking routines + report := test.CheckRoutines(t) + defer report() + + for _, test := range []struct { + Name string + ClientSRTP []SRTPProtectionProfile + ServerSRTP []SRTPProtectionProfile + ExpectedProfile SRTPProtectionProfile + WantClientError error + WantServerError error + }{ + { + Name: "No SRTP in use", + ClientSRTP: nil, + ServerSRTP: nil, + ExpectedProfile: 0, + WantClientError: nil, + WantServerError: nil, + }, + { + Name: "SRTP both ends", + ClientSRTP: []SRTPProtectionProfile{SRTP_AES128_CM_HMAC_SHA1_80}, + ServerSRTP: []SRTPProtectionProfile{SRTP_AES128_CM_HMAC_SHA1_80}, + ExpectedProfile: SRTP_AES128_CM_HMAC_SHA1_80, + WantClientError: nil, + WantServerError: nil, + }, + { + Name: "SRTP client only", + ClientSRTP: []SRTPProtectionProfile{SRTP_AES128_CM_HMAC_SHA1_80}, + ServerSRTP: nil, + ExpectedProfile: 0, + WantClientError: &errAlert{&alert.Alert{Level: alert.Fatal, Description: alert.InsufficientSecurity}}, + WantServerError: errServerNoMatchingSRTPProfile, + }, + { + Name: "SRTP server only", + ClientSRTP: nil, + ServerSRTP: []SRTPProtectionProfile{SRTP_AES128_CM_HMAC_SHA1_80}, + ExpectedProfile: 0, + WantClientError: nil, + WantServerError: nil, + }, + { + Name: "Multiple Suites", + ClientSRTP: []SRTPProtectionProfile{SRTP_AES128_CM_HMAC_SHA1_80, SRTP_AES128_CM_HMAC_SHA1_32}, + ServerSRTP: []SRTPProtectionProfile{SRTP_AES128_CM_HMAC_SHA1_80, SRTP_AES128_CM_HMAC_SHA1_32}, + ExpectedProfile: SRTP_AES128_CM_HMAC_SHA1_80, + WantClientError: nil, + WantServerError: nil, + }, + { + Name: "Multiple Suites, Client Chooses", + ClientSRTP: []SRTPProtectionProfile{SRTP_AES128_CM_HMAC_SHA1_80, SRTP_AES128_CM_HMAC_SHA1_32}, + ServerSRTP: []SRTPProtectionProfile{SRTP_AES128_CM_HMAC_SHA1_32, SRTP_AES128_CM_HMAC_SHA1_80}, + ExpectedProfile: SRTP_AES128_CM_HMAC_SHA1_80, + WantClientError: nil, + WantServerError: nil, + }, + } { + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + defer cancel() + + ca, cb := dpipe.Pipe() + type result struct { + c *Conn + err error + } + c := make(chan result) + + go func() { + client, err := testClient(ctx, ca, &Config{SRTPProtectionProfiles: test.ClientSRTP}, true) + c <- result{client, err} + }() + + server, err := testServer(ctx, cb, &Config{SRTPProtectionProfiles: test.ServerSRTP}, true) + if !errors.Is(err, test.WantServerError) { + t.Errorf("TestSRTPConfiguration: Server Error Mismatch '%s': expected(%v) actual(%v)", test.Name, test.WantServerError, err) + } + if err == nil { + defer func() { + _ = server.Close() + }() + } + + res := <-c + if res.err == nil { + defer func() { + _ = res.c.Close() + }() + } + if !errors.Is(res.err, test.WantClientError) { + t.Fatalf("TestSRTPConfiguration: Client Error Mismatch '%s': expected(%v) actual(%v)", test.Name, test.WantClientError, res.err) + } + if res.c == nil { + return + } + + actualClientSRTP, _ := res.c.SelectedSRTPProtectionProfile() + if actualClientSRTP != test.ExpectedProfile { + t.Errorf("TestSRTPConfiguration: Client SRTPProtectionProfile Mismatch '%s': expected(%v) actual(%v)", test.Name, test.ExpectedProfile, actualClientSRTP) + } + + actualServerSRTP, _ := server.SelectedSRTPProtectionProfile() + if actualServerSRTP != test.ExpectedProfile { + t.Errorf("TestSRTPConfiguration: Server SRTPProtectionProfile Mismatch '%s': expected(%v) actual(%v)", test.Name, test.ExpectedProfile, actualServerSRTP) + } + } +} + +func TestClientCertificate(t *testing.T) { + // Check for leaking routines + report := test.CheckRoutines(t) + defer report() + + srvCert, err := selfsign.GenerateSelfSigned() + if err != nil { + t.Fatal(err) + } + srvCAPool := x509.NewCertPool() + srvCertificate, err := x509.ParseCertificate(srvCert.Certificate[0]) + if err != nil { + t.Fatal(err) + } + srvCAPool.AddCert(srvCertificate) + + cert, err := selfsign.GenerateSelfSigned() + if err != nil { + t.Fatal(err) + } + certificate, err := x509.ParseCertificate(cert.Certificate[0]) + if err != nil { + t.Fatal(err) + } + caPool := x509.NewCertPool() + caPool.AddCert(certificate) + + t.Run("parallel", func(t *testing.T) { // sync routines to check routine leak + tests := map[string]struct { + clientCfg *Config + serverCfg *Config + wantErr bool + }{ + "NoClientCert": { + clientCfg: &Config{RootCAs: srvCAPool}, + serverCfg: &Config{ + Certificates: []tls.Certificate{srvCert}, + ClientAuth: NoClientCert, + ClientCAs: caPool, + }, + }, + "NoClientCert_cert": { + clientCfg: &Config{RootCAs: srvCAPool, Certificates: []tls.Certificate{cert}}, + serverCfg: &Config{ + Certificates: []tls.Certificate{srvCert}, + ClientAuth: RequireAnyClientCert, + }, + }, + "RequestClientCert_cert": { + clientCfg: &Config{RootCAs: srvCAPool, Certificates: []tls.Certificate{cert}}, + serverCfg: &Config{ + Certificates: []tls.Certificate{srvCert}, + ClientAuth: RequestClientCert, + }, + }, + "RequestClientCert_no_cert": { + clientCfg: &Config{RootCAs: srvCAPool}, + serverCfg: &Config{ + Certificates: []tls.Certificate{srvCert}, + ClientAuth: RequestClientCert, + ClientCAs: caPool, + }, + }, + "RequireAnyClientCert": { + clientCfg: &Config{RootCAs: srvCAPool, Certificates: []tls.Certificate{cert}}, + serverCfg: &Config{ + Certificates: []tls.Certificate{srvCert}, + ClientAuth: RequireAnyClientCert, + }, + }, + "RequireAnyClientCert_error": { + clientCfg: &Config{RootCAs: srvCAPool}, + serverCfg: &Config{ + Certificates: []tls.Certificate{srvCert}, + ClientAuth: RequireAnyClientCert, + }, + wantErr: true, + }, + "VerifyClientCertIfGiven_no_cert": { + clientCfg: &Config{RootCAs: srvCAPool}, + serverCfg: &Config{ + Certificates: []tls.Certificate{srvCert}, + ClientAuth: VerifyClientCertIfGiven, + ClientCAs: caPool, + }, + }, + "VerifyClientCertIfGiven_cert": { + clientCfg: &Config{RootCAs: srvCAPool, Certificates: []tls.Certificate{cert}}, + serverCfg: &Config{ + Certificates: []tls.Certificate{srvCert}, + ClientAuth: VerifyClientCertIfGiven, + ClientCAs: caPool, + }, + }, + "VerifyClientCertIfGiven_error": { + clientCfg: &Config{RootCAs: srvCAPool, Certificates: []tls.Certificate{cert}}, + serverCfg: &Config{ + Certificates: []tls.Certificate{srvCert}, + ClientAuth: VerifyClientCertIfGiven, + }, + wantErr: true, + }, + "RequireAndVerifyClientCert": { + clientCfg: &Config{RootCAs: srvCAPool, Certificates: []tls.Certificate{cert}}, + serverCfg: &Config{ + Certificates: []tls.Certificate{srvCert}, + ClientAuth: RequireAndVerifyClientCert, + ClientCAs: caPool, + }, + }, + } + for name, tt := range tests { + tt := tt + t.Run(name, func(t *testing.T) { + t.Parallel() + + ca, cb := dpipe.Pipe() + type result struct { + c *Conn + err error + } + c := make(chan result) + + go func() { + client, err := Client(ca, tt.clientCfg) + c <- result{client, err} + }() + + server, err := Server(cb, tt.serverCfg) + res := <-c + defer func() { + if err == nil { + _ = server.Close() + } + if res.err == nil { + _ = res.c.Close() + } + }() + + if tt.wantErr { + if err != nil { + // Error expected, test succeeded + return + } + t.Error("Error expected") + } + if err != nil { + t.Errorf("Server failed(%v)", err) + } + + if res.err != nil { + t.Errorf("Client failed(%v)", res.err) + } + + actualClientCert := server.ConnectionState().PeerCertificates + if tt.serverCfg.ClientAuth == RequireAnyClientCert || tt.serverCfg.ClientAuth == RequireAndVerifyClientCert { + if actualClientCert == nil { + t.Errorf("Client did not provide a certificate") + } + + if len(actualClientCert) != len(tt.clientCfg.Certificates[0].Certificate) || !bytes.Equal(tt.clientCfg.Certificates[0].Certificate[0], actualClientCert[0]) { + t.Errorf("Client certificate was not communicated correctly") + } + } + if tt.serverCfg.ClientAuth == NoClientCert { + if actualClientCert != nil { + t.Errorf("Client certificate wasn't expected") + } + } + + actualServerCert := res.c.ConnectionState().PeerCertificates + if actualServerCert == nil { + t.Errorf("Server did not provide a certificate") + } + + if len(actualServerCert) != len(tt.serverCfg.Certificates[0].Certificate) || !bytes.Equal(tt.serverCfg.Certificates[0].Certificate[0], actualServerCert[0]) { + t.Errorf("Server certificate was not communicated correctly") + } + }) + } + }) +} + +func TestExtendedMasterSecret(t *testing.T) { + // Check for leaking routines + report := test.CheckRoutines(t) + defer report() + + tests := map[string]struct { + clientCfg *Config + serverCfg *Config + expectedClientErr error + expectedServerErr error + }{ + "Request_Request_ExtendedMasterSecret": { + clientCfg: &Config{ + ExtendedMasterSecret: RequestExtendedMasterSecret, + }, + serverCfg: &Config{ + ExtendedMasterSecret: RequestExtendedMasterSecret, + }, + expectedClientErr: nil, + expectedServerErr: nil, + }, + "Request_Require_ExtendedMasterSecret": { + clientCfg: &Config{ + ExtendedMasterSecret: RequestExtendedMasterSecret, + }, + serverCfg: &Config{ + ExtendedMasterSecret: RequireExtendedMasterSecret, + }, + expectedClientErr: nil, + expectedServerErr: nil, + }, + "Request_Disable_ExtendedMasterSecret": { + clientCfg: &Config{ + ExtendedMasterSecret: RequestExtendedMasterSecret, + }, + serverCfg: &Config{ + ExtendedMasterSecret: DisableExtendedMasterSecret, + }, + expectedClientErr: nil, + expectedServerErr: nil, + }, + "Require_Request_ExtendedMasterSecret": { + clientCfg: &Config{ + ExtendedMasterSecret: RequireExtendedMasterSecret, + }, + serverCfg: &Config{ + ExtendedMasterSecret: RequestExtendedMasterSecret, + }, + expectedClientErr: nil, + expectedServerErr: nil, + }, + "Require_Require_ExtendedMasterSecret": { + clientCfg: &Config{ + ExtendedMasterSecret: RequireExtendedMasterSecret, + }, + serverCfg: &Config{ + ExtendedMasterSecret: RequireExtendedMasterSecret, + }, + expectedClientErr: nil, + expectedServerErr: nil, + }, + "Require_Disable_ExtendedMasterSecret": { + clientCfg: &Config{ + ExtendedMasterSecret: RequireExtendedMasterSecret, + }, + serverCfg: &Config{ + ExtendedMasterSecret: DisableExtendedMasterSecret, + }, + expectedClientErr: errClientRequiredButNoServerEMS, + expectedServerErr: &errAlert{&alert.Alert{Level: alert.Fatal, Description: alert.InsufficientSecurity}}, + }, + "Disable_Request_ExtendedMasterSecret": { + clientCfg: &Config{ + ExtendedMasterSecret: DisableExtendedMasterSecret, + }, + serverCfg: &Config{ + ExtendedMasterSecret: RequestExtendedMasterSecret, + }, + expectedClientErr: nil, + expectedServerErr: nil, + }, + "Disable_Require_ExtendedMasterSecret": { + clientCfg: &Config{ + ExtendedMasterSecret: DisableExtendedMasterSecret, + }, + serverCfg: &Config{ + ExtendedMasterSecret: RequireExtendedMasterSecret, + }, + expectedClientErr: &errAlert{&alert.Alert{Level: alert.Fatal, Description: alert.InsufficientSecurity}}, + expectedServerErr: errServerRequiredButNoClientEMS, + }, + "Disable_Disable_ExtendedMasterSecret": { + clientCfg: &Config{ + ExtendedMasterSecret: DisableExtendedMasterSecret, + }, + serverCfg: &Config{ + ExtendedMasterSecret: DisableExtendedMasterSecret, + }, + expectedClientErr: nil, + expectedServerErr: nil, + }, + } + for name, tt := range tests { + tt := tt + t.Run(name, func(t *testing.T) { + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + defer cancel() + + ca, cb := dpipe.Pipe() + type result struct { + c *Conn + err error + } + c := make(chan result) + + go func() { + client, err := testClient(ctx, ca, tt.clientCfg, true) + c <- result{client, err} + }() + + server, err := testServer(ctx, cb, tt.serverCfg, true) + res := <-c + defer func() { + if err == nil { + _ = server.Close() + } + if res.err == nil { + _ = res.c.Close() + } + }() + + if !errors.Is(res.err, tt.expectedClientErr) { + t.Errorf("Client error expected: \"%v\" but got \"%v\"", tt.expectedClientErr, res.err) + } + + if !errors.Is(err, tt.expectedServerErr) { + t.Errorf("Server error expected: \"%v\" but got \"%v\"", tt.expectedServerErr, err) + } + }) + } +} + +func TestServerCertificate(t *testing.T) { + // Check for leaking routines + report := test.CheckRoutines(t) + defer report() + + cert, err := selfsign.GenerateSelfSigned() + if err != nil { + t.Fatal(err) + } + certificate, err := x509.ParseCertificate(cert.Certificate[0]) + if err != nil { + t.Fatal(err) + } + caPool := x509.NewCertPool() + caPool.AddCert(certificate) + + t.Run("parallel", func(t *testing.T) { // sync routines to check routine leak + tests := map[string]struct { + clientCfg *Config + serverCfg *Config + wantErr bool + }{ + "no_ca": { + clientCfg: &Config{}, + serverCfg: &Config{Certificates: []tls.Certificate{cert}, ClientAuth: NoClientCert}, + wantErr: true, + }, + "good_ca": { + clientCfg: &Config{RootCAs: caPool}, + serverCfg: &Config{Certificates: []tls.Certificate{cert}, ClientAuth: NoClientCert}, + }, + "no_ca_skip_verify": { + clientCfg: &Config{InsecureSkipVerify: true}, + serverCfg: &Config{Certificates: []tls.Certificate{cert}, ClientAuth: NoClientCert}, + }, + "good_ca_skip_verify_custom_verify_peer": { + clientCfg: &Config{RootCAs: caPool, Certificates: []tls.Certificate{cert}}, + serverCfg: &Config{Certificates: []tls.Certificate{cert}, ClientAuth: RequireAnyClientCert, VerifyPeerCertificate: func(cert [][]byte, chain [][]*x509.Certificate) error { + if len(chain) != 0 { + return errNotExpectedChain + } + return nil + }}, + }, + "good_ca_verify_custom_verify_peer": { + clientCfg: &Config{RootCAs: caPool, Certificates: []tls.Certificate{cert}}, + serverCfg: &Config{ClientCAs: caPool, Certificates: []tls.Certificate{cert}, ClientAuth: RequireAndVerifyClientCert, VerifyPeerCertificate: func(cert [][]byte, chain [][]*x509.Certificate) error { + if len(chain) == 0 { + return errExpecedChain + } + return nil + }}, + }, + "good_ca_custom_verify_peer": { + clientCfg: &Config{ + RootCAs: caPool, + VerifyPeerCertificate: func([][]byte, [][]*x509.Certificate) error { + return errWrongCert + }, + }, + serverCfg: &Config{Certificates: []tls.Certificate{cert}, ClientAuth: NoClientCert}, + wantErr: true, + }, + "server_name": { + clientCfg: &Config{RootCAs: caPool, ServerName: certificate.Subject.CommonName}, + serverCfg: &Config{Certificates: []tls.Certificate{cert}, ClientAuth: NoClientCert}, + }, + "server_name_error": { + clientCfg: &Config{RootCAs: caPool, ServerName: "barfoo"}, + serverCfg: &Config{Certificates: []tls.Certificate{cert}, ClientAuth: NoClientCert}, + wantErr: true, + }, + } + for name, tt := range tests { + tt := tt + t.Run(name, func(t *testing.T) { + t.Parallel() + + ca, cb := dpipe.Pipe() + + type result struct { + c *Conn + err error + } + srvCh := make(chan result) + go func() { + s, err := Server(cb, tt.serverCfg) + srvCh <- result{s, err} + }() + + cli, err := Client(ca, tt.clientCfg) + if err == nil { + _ = cli.Close() + } + if !tt.wantErr && err != nil { + t.Errorf("Client failed(%v)", err) + } + if tt.wantErr && err == nil { + t.Fatal("Error expected") + } + + srv := <-srvCh + if srv.err == nil { + _ = srv.c.Close() + } + }) + } + }) +} + +func TestCipherSuiteConfiguration(t *testing.T) { + // Check for leaking routines + report := test.CheckRoutines(t) + defer report() + + for _, test := range []struct { + Name string + ClientCipherSuites []CipherSuiteID + ServerCipherSuites []CipherSuiteID + WantClientError error + WantServerError error + WantSelectedCipherSuite CipherSuiteID + }{ + { + Name: "No CipherSuites specified", + ClientCipherSuites: nil, + ServerCipherSuites: nil, + WantClientError: nil, + WantServerError: nil, + }, + { + Name: "Invalid CipherSuite", + ClientCipherSuites: []CipherSuiteID{0x00}, + ServerCipherSuites: []CipherSuiteID{0x00}, + WantClientError: &invalidCipherSuite{0x00}, + WantServerError: &invalidCipherSuite{0x00}, + }, + { + Name: "Valid CipherSuites specified", + ClientCipherSuites: []CipherSuiteID{TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256}, + ServerCipherSuites: []CipherSuiteID{TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256}, + WantClientError: nil, + WantServerError: nil, + WantSelectedCipherSuite: TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, + }, + { + Name: "CipherSuites mismatch", + ClientCipherSuites: []CipherSuiteID{TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256}, + ServerCipherSuites: []CipherSuiteID{TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA}, + WantClientError: &errAlert{&alert.Alert{Level: alert.Fatal, Description: alert.InsufficientSecurity}}, + WantServerError: errCipherSuiteNoIntersection, + }, + { + Name: "Valid CipherSuites CCM specified", + ClientCipherSuites: []CipherSuiteID{TLS_ECDHE_ECDSA_WITH_AES_128_CCM}, + ServerCipherSuites: []CipherSuiteID{TLS_ECDHE_ECDSA_WITH_AES_128_CCM}, + WantClientError: nil, + WantServerError: nil, + WantSelectedCipherSuite: TLS_ECDHE_ECDSA_WITH_AES_128_CCM, + }, + { + Name: "Valid CipherSuites CCM-8 specified", + ClientCipherSuites: []CipherSuiteID{TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8}, + ServerCipherSuites: []CipherSuiteID{TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8}, + WantClientError: nil, + WantServerError: nil, + WantSelectedCipherSuite: TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8, + }, + { + Name: "Server supports subset of client suites", + ClientCipherSuites: []CipherSuiteID{TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA}, + ServerCipherSuites: []CipherSuiteID{TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA}, + WantClientError: nil, + WantServerError: nil, + WantSelectedCipherSuite: TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, + }, + } { + test := test + t.Run(test.Name, func(t *testing.T) { + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + defer cancel() + + ca, cb := dpipe.Pipe() + type result struct { + c *Conn + err error + } + c := make(chan result) + + go func() { + client, err := testClient(ctx, ca, &Config{CipherSuites: test.ClientCipherSuites}, true) + c <- result{client, err} + }() + + server, err := testServer(ctx, cb, &Config{CipherSuites: test.ServerCipherSuites}, true) + if err == nil { + defer func() { + _ = server.Close() + }() + } + if !errors.Is(err, test.WantServerError) { + t.Errorf("TestCipherSuiteConfiguration: Server Error Mismatch '%s': expected(%v) actual(%v)", test.Name, test.WantServerError, err) + } + + res := <-c + if res.err == nil { + _ = server.Close() + } + if !errors.Is(res.err, test.WantClientError) { + t.Errorf("TestSRTPConfiguration: Client Error Mismatch '%s': expected(%v) actual(%v)", test.Name, test.WantClientError, res.err) + } + if test.WantSelectedCipherSuite != 0x00 && res.c.state.cipherSuite.ID() != test.WantSelectedCipherSuite { + t.Errorf("TestCipherSuiteConfiguration: Server Selected Bad Cipher Suite '%s': expected(%v) actual(%v)", test.Name, test.WantSelectedCipherSuite, res.c.state.cipherSuite.ID()) + } + }) + } +} + +func TestCertificateAndPSKServer(t *testing.T) { + // Check for leaking routines + report := test.CheckRoutines(t) + defer report() + + for _, test := range []struct { + Name string + ClientPSK bool + }{ + { + Name: "Client uses PKI", + ClientPSK: false, + }, + { + Name: "Client uses PSK", + ClientPSK: true, + }, + } { + test := test + t.Run(test.Name, func(t *testing.T) { + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + defer cancel() + + ca, cb := dpipe.Pipe() + type result struct { + c *Conn + err error + } + c := make(chan result) + + go func() { + config := &Config{CipherSuites: []CipherSuiteID{TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256}} + if test.ClientPSK { + config.PSK = func([]byte) ([]byte, error) { + return []byte{0x00, 0x01, 0x02}, nil + } + config.PSKIdentityHint = []byte{0x00} + config.CipherSuites = []CipherSuiteID{TLS_PSK_WITH_AES_128_GCM_SHA256} + } + + client, err := testClient(ctx, ca, config, false) + c <- result{client, err} + }() + + config := &Config{ + CipherSuites: []CipherSuiteID{TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, TLS_PSK_WITH_AES_128_GCM_SHA256}, + PSK: func([]byte) ([]byte, error) { + return []byte{0x00, 0x01, 0x02}, nil + }, + } + + server, err := testServer(ctx, cb, config, true) + if err == nil { + defer func() { + _ = server.Close() + }() + } else { + t.Errorf("TestCertificateAndPSKServer: Server Error Mismatch '%s': expected(%v) actual(%v)", test.Name, nil, err) + } + + res := <-c + if res.err == nil { + _ = server.Close() + } else { + t.Errorf("TestCertificateAndPSKServer: Client Error Mismatch '%s': expected(%v) actual(%v)", test.Name, nil, res.err) + } + }) + } +} + +func TestPSKConfiguration(t *testing.T) { + // Check for leaking routines + report := test.CheckRoutines(t) + defer report() + + for _, test := range []struct { + Name string + ClientHasCertificate bool + ServerHasCertificate bool + ClientPSK PSKCallback + ServerPSK PSKCallback + ClientPSKIdentity []byte + ServerPSKIdentity []byte + WantClientError error + WantServerError error + }{ + { + Name: "PSK and no certificate specified", + ClientHasCertificate: false, + ServerHasCertificate: false, + ClientPSK: func([]byte) ([]byte, error) { return []byte{0x00, 0x01, 0x02}, nil }, + ServerPSK: func([]byte) ([]byte, error) { return []byte{0x00, 0x01, 0x02}, nil }, + ClientPSKIdentity: []byte{0x00}, + ServerPSKIdentity: []byte{0x00}, + WantClientError: errNoAvailablePSKCipherSuite, + WantServerError: errNoAvailablePSKCipherSuite, + }, + { + Name: "PSK and certificate specified", + ClientHasCertificate: true, + ServerHasCertificate: true, + ClientPSK: func([]byte) ([]byte, error) { return []byte{0x00, 0x01, 0x02}, nil }, + ServerPSK: func([]byte) ([]byte, error) { return []byte{0x00, 0x01, 0x02}, nil }, + ClientPSKIdentity: []byte{0x00}, + ServerPSKIdentity: []byte{0x00}, + WantClientError: errNoAvailablePSKCipherSuite, + WantServerError: errNoAvailablePSKCipherSuite, + }, + { + Name: "PSK and no identity specified", + ClientHasCertificate: false, + ServerHasCertificate: false, + ClientPSK: func([]byte) ([]byte, error) { return []byte{0x00, 0x01, 0x02}, nil }, + ServerPSK: func([]byte) ([]byte, error) { return []byte{0x00, 0x01, 0x02}, nil }, + ClientPSKIdentity: nil, + ServerPSKIdentity: nil, + WantClientError: errPSKAndIdentityMustBeSetForClient, + WantServerError: errNoAvailablePSKCipherSuite, + }, + { + Name: "No PSK and identity specified", + ClientHasCertificate: false, + ServerHasCertificate: false, + ClientPSK: nil, + ServerPSK: nil, + ClientPSKIdentity: []byte{0x00}, + ServerPSKIdentity: []byte{0x00}, + WantClientError: errIdentityNoPSK, + WantServerError: errIdentityNoPSK, + }, + } { + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + defer cancel() + + ca, cb := dpipe.Pipe() + type result struct { + c *Conn + err error + } + c := make(chan result) + + go func() { + client, err := testClient(ctx, ca, &Config{PSK: test.ClientPSK, PSKIdentityHint: test.ClientPSKIdentity}, test.ClientHasCertificate) + c <- result{client, err} + }() + + _, err := testServer(ctx, cb, &Config{PSK: test.ServerPSK, PSKIdentityHint: test.ServerPSKIdentity}, test.ServerHasCertificate) + if err != nil || test.WantServerError != nil { + if !(err != nil && test.WantServerError != nil && err.Error() == test.WantServerError.Error()) { + t.Fatalf("TestPSKConfiguration: Server Error Mismatch '%s': expected(%v) actual(%v)", test.Name, test.WantServerError, err) + } + } + + res := <-c + if res.err != nil || test.WantClientError != nil { + if !(res.err != nil && test.WantClientError != nil && res.err.Error() == test.WantClientError.Error()) { + t.Fatalf("TestPSKConfiguration: Client Error Mismatch '%s': expected(%v) actual(%v)", test.Name, test.WantClientError, res.err) + } + } + } +} + +func TestServerTimeout(t *testing.T) { + // Limit runtime in case of deadlocks + lim := test.TimeOut(time.Second * 20) + defer lim.Stop() + + // Check for leaking routines + report := test.CheckRoutines(t) + defer report() + + cookie := make([]byte, 20) + _, err := rand.Read(cookie) + if err != nil { + t.Fatal(err) + } + + var rand [28]byte + random := handshake.Random{GMTUnixTime: time.Unix(500, 0), RandomBytes: rand} + + cipherSuites := []CipherSuite{ + &ciphersuite.TLSEcdheEcdsaWithAes128GcmSha256{}, + &ciphersuite.TLSEcdheRsaWithAes128GcmSha256{}, + } + + extensions := []extension.Extension{ + &extension.SupportedSignatureAlgorithms{ + SignatureHashAlgorithms: []signaturehash.Algorithm{ + {Hash: hash.SHA256, Signature: signature.ECDSA}, + {Hash: hash.SHA384, Signature: signature.ECDSA}, + {Hash: hash.SHA512, Signature: signature.ECDSA}, + {Hash: hash.SHA256, Signature: signature.RSA}, + {Hash: hash.SHA384, Signature: signature.RSA}, + {Hash: hash.SHA512, Signature: signature.RSA}, + }, + }, + &extension.SupportedEllipticCurves{ + EllipticCurves: []elliptic.Curve{elliptic.X25519, elliptic.P256, elliptic.P384}, + }, + &extension.SupportedPointFormats{ + PointFormats: []elliptic.CurvePointFormat{elliptic.CurvePointFormatUncompressed}, + }, + } + + record := &recordlayer.RecordLayer{ + Header: recordlayer.Header{ + SequenceNumber: 0, + Version: protocol.Version1_2, + }, + Content: &handshake.Handshake{ + // sequenceNumber and messageSequence line up, may need to be re-evaluated + Header: handshake.Header{ + MessageSequence: 0, + }, + Message: &handshake.MessageClientHello{ + Version: protocol.Version1_2, + Cookie: cookie, + Random: random, + CipherSuiteIDs: cipherSuiteIDs(cipherSuites), + CompressionMethods: defaultCompressionMethods(), + Extensions: extensions, + }, + }, + } + + packet, err := record.Marshal() + if err != nil { + t.Fatal(err) + } + + ca, cb := dpipe.Pipe() + defer func() { + err := ca.Close() + if err != nil { + t.Fatal(err) + } + }() + + // Client reader + caReadChan := make(chan []byte, 1000) + go func() { + for { + data := make([]byte, 8192) + n, err := ca.Read(data) + if err != nil { + return + } + + caReadChan <- data[:n] + } + }() + + // Start sending ClientHello packets until server responds with first packet + go func() { + for { + select { + case <-time.After(10 * time.Millisecond): + _, err := ca.Write(packet) + if err != nil { + return + } + case <-caReadChan: + // Once we receive the first reply from the server, stop + return + } + } + }() + + ctx, cancel := context.WithTimeout(context.Background(), 50*time.Millisecond) + defer cancel() + + config := &Config{ + CipherSuites: []CipherSuiteID{TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256}, + FlightInterval: 100 * time.Millisecond, + } + + _, serverErr := testServer(ctx, cb, config, true) + if netErr, ok := serverErr.(net.Error); !ok || !netErr.Timeout() { + t.Fatalf("Client error exp(Temporary network error) failed(%v)", serverErr) + } + + // Wait a little longer to ensure no additional messages have been sent by the server + time.Sleep(300 * time.Millisecond) + select { + case msg := <-caReadChan: + t.Fatalf("Expected no additional messages from server, got: %+v", msg) + default: + } +} + +func TestProtocolVersionValidation(t *testing.T) { + // Limit runtime in case of deadlocks + lim := test.TimeOut(time.Second * 20) + defer lim.Stop() + + // Check for leaking routines + report := test.CheckRoutines(t) + defer report() + + cookie := make([]byte, 20) + if _, err := rand.Read(cookie); err != nil { + t.Fatal(err) + } + + var rand [28]byte + random := handshake.Random{GMTUnixTime: time.Unix(500, 0), RandomBytes: rand} + + localKeypair, err := elliptic.GenerateKeypair(elliptic.X25519) + if err != nil { + t.Fatal(err) + } + + config := &Config{ + CipherSuites: []CipherSuiteID{TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256}, + FlightInterval: 100 * time.Millisecond, + } + + t.Run("Server", func(t *testing.T) { + serverCases := map[string]struct { + records []*recordlayer.RecordLayer + }{ + "ClientHelloVersion": { + records: []*recordlayer.RecordLayer{ + { + Header: recordlayer.Header{ + Version: protocol.Version1_2, + }, + Content: &handshake.Handshake{ + Message: &handshake.MessageClientHello{ + Version: protocol.Version{Major: 0xfe, Minor: 0xff}, // try to downgrade + Cookie: cookie, + Random: random, + CipherSuiteIDs: []uint16{uint16((&ciphersuite.TLSEcdheEcdsaWithAes128GcmSha256{}).ID())}, + CompressionMethods: defaultCompressionMethods(), + }, + }, + }, + }, + }, + "SecondsClientHelloVersion": { + records: []*recordlayer.RecordLayer{ + { + Header: recordlayer.Header{ + Version: protocol.Version1_2, + }, + Content: &handshake.Handshake{ + Message: &handshake.MessageClientHello{ + Version: protocol.Version1_2, + Cookie: cookie, + Random: random, + CipherSuiteIDs: []uint16{uint16((&ciphersuite.TLSEcdheEcdsaWithAes128GcmSha256{}).ID())}, + CompressionMethods: defaultCompressionMethods(), + }, + }, + }, + { + Header: recordlayer.Header{ + Version: protocol.Version1_2, + SequenceNumber: 1, + }, + Content: &handshake.Handshake{ + Header: handshake.Header{ + MessageSequence: 1, + }, + Message: &handshake.MessageClientHello{ + Version: protocol.Version{Major: 0xfe, Minor: 0xff}, // try to downgrade + Cookie: cookie, + Random: random, + CipherSuiteIDs: []uint16{uint16((&ciphersuite.TLSEcdheEcdsaWithAes128GcmSha256{}).ID())}, + CompressionMethods: defaultCompressionMethods(), + }, + }, + }, + }, + }, + } + for name, c := range serverCases { + c := c + t.Run(name, func(t *testing.T) { + ca, cb := dpipe.Pipe() + defer func() { + err := ca.Close() + if err != nil { + t.Error(err) + } + }() + + ctx, cancel := context.WithTimeout(context.Background(), time.Second) + defer cancel() + + var wg sync.WaitGroup + wg.Add(1) + defer wg.Wait() + go func() { + defer wg.Done() + if _, err := testServer(ctx, cb, config, true); !errors.Is(err, errUnsupportedProtocolVersion) { + t.Errorf("Client error exp(%v) failed(%v)", errUnsupportedProtocolVersion, err) + } + }() + + time.Sleep(50 * time.Millisecond) + + resp := make([]byte, 1024) + for _, record := range c.records { + packet, err := record.Marshal() + if err != nil { + t.Fatal(err) + } + if _, werr := ca.Write(packet); werr != nil { + t.Fatal(werr) + } + n, rerr := ca.Read(resp[:cap(resp)]) + if rerr != nil { + t.Fatal(rerr) + } + resp = resp[:n] + } + + h := &recordlayer.Header{} + if err := h.Unmarshal(resp); err != nil { + t.Fatal("Failed to unmarshal response") + } + if h.ContentType != protocol.ContentTypeAlert { + t.Errorf("Peer must return alert to unsupported protocol version") + } + }) + } + }) + + t.Run("Client", func(t *testing.T) { + clientCases := map[string]struct { + records []*recordlayer.RecordLayer + }{ + "ServerHelloVersion": { + records: []*recordlayer.RecordLayer{ + { + Header: recordlayer.Header{ + Version: protocol.Version1_2, + }, + Content: &handshake.Handshake{ + Message: &handshake.MessageHelloVerifyRequest{ + Version: protocol.Version1_2, + Cookie: cookie, + }, + }, + }, + { + Header: recordlayer.Header{ + Version: protocol.Version1_2, + SequenceNumber: 1, + }, + Content: &handshake.Handshake{ + Header: handshake.Header{ + MessageSequence: 1, + }, + Message: &handshake.MessageServerHello{ + Version: protocol.Version{Major: 0xfe, Minor: 0xff}, // try to downgrade + Random: random, + CipherSuiteID: func() *uint16 { id := uint16(TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256); return &id }(), + CompressionMethod: defaultCompressionMethods()[0], + }, + }, + }, + { + Header: recordlayer.Header{ + Version: protocol.Version1_2, + SequenceNumber: 2, + }, + Content: &handshake.Handshake{ + Header: handshake.Header{ + MessageSequence: 2, + }, + Message: &handshake.MessageCertificate{}, + }, + }, + { + Header: recordlayer.Header{ + Version: protocol.Version1_2, + SequenceNumber: 3, + }, + Content: &handshake.Handshake{ + Header: handshake.Header{ + MessageSequence: 3, + }, + Message: &handshake.MessageServerKeyExchange{ + EllipticCurveType: elliptic.CurveTypeNamedCurve, + NamedCurve: elliptic.X25519, + PublicKey: localKeypair.PublicKey, + HashAlgorithm: hash.SHA256, + SignatureAlgorithm: signature.ECDSA, + Signature: make([]byte, 64), + }, + }, + }, + { + Header: recordlayer.Header{ + Version: protocol.Version1_2, + SequenceNumber: 4, + }, + Content: &handshake.Handshake{ + Header: handshake.Header{ + MessageSequence: 4, + }, + Message: &handshake.MessageServerHelloDone{}, + }, + }, + }, + }, + } + for name, c := range clientCases { + c := c + t.Run(name, func(t *testing.T) { + ca, cb := dpipe.Pipe() + defer func() { + err := ca.Close() + if err != nil { + t.Error(err) + } + }() + + ctx, cancel := context.WithTimeout(context.Background(), time.Second) + defer cancel() + + var wg sync.WaitGroup + wg.Add(1) + defer wg.Wait() + go func() { + defer wg.Done() + if _, err := testClient(ctx, cb, config, true); !errors.Is(err, errUnsupportedProtocolVersion) { + t.Errorf("Server error exp(%v) failed(%v)", errUnsupportedProtocolVersion, err) + } + }() + + time.Sleep(50 * time.Millisecond) + + for _, record := range c.records { + if _, err := ca.Read(make([]byte, 1024)); err != nil { + t.Fatal(err) + } + + packet, err := record.Marshal() + if err != nil { + t.Fatal(err) + } + if _, err := ca.Write(packet); err != nil { + t.Fatal(err) + } + } + resp := make([]byte, 1024) + n, err := ca.Read(resp) + if err != nil { + t.Fatal(err) + } + resp = resp[:n] + + h := &recordlayer.Header{} + if err := h.Unmarshal(resp); err != nil { + t.Fatal("Failed to unmarshal response") + } + if h.ContentType != protocol.ContentTypeAlert { + t.Errorf("Peer must return alert to unsupported protocol version") + } + }) + } + }) +} + +func TestMultipleHelloVerifyRequest(t *testing.T) { + // Limit runtime in case of deadlocks + lim := test.TimeOut(time.Second * 20) + defer lim.Stop() + + // Check for leaking routines + report := test.CheckRoutines(t) + defer report() + + cookies := [][]byte{ + // first clientHello contains an empty cookie + {}, + } + var packets [][]byte + for i := 0; i < 2; i++ { + cookie := make([]byte, 20) + if _, err := rand.Read(cookie); err != nil { + t.Fatal(err) + } + cookies = append(cookies, cookie) + + record := &recordlayer.RecordLayer{ + Header: recordlayer.Header{ + SequenceNumber: uint64(i), + Version: protocol.Version1_2, + }, + Content: &handshake.Handshake{ + Header: handshake.Header{ + MessageSequence: uint16(i), + }, + Message: &handshake.MessageHelloVerifyRequest{ + Version: protocol.Version1_2, + Cookie: cookie, + }, + }, + } + packet, err := record.Marshal() + if err != nil { + t.Fatal(err) + } + packets = append(packets, packet) + } + + ca, cb := dpipe.Pipe() + defer func() { + err := ca.Close() + if err != nil { + t.Error(err) + } + }() + + ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second) + defer cancel() + + var wg sync.WaitGroup + wg.Add(1) + defer wg.Wait() + go func() { + defer wg.Done() + _, _ = testClient(ctx, ca, &Config{}, false) + }() + + for i, cookie := range cookies { + // read client hello + resp := make([]byte, 1024) + n, err := cb.Read(resp) + if err != nil { + t.Fatal(err) + } + record := &recordlayer.RecordLayer{} + if err := record.Unmarshal(resp[:n]); err != nil { + t.Fatal(err) + } + clientHello := record.Content.(*handshake.Handshake).Message.(*handshake.MessageClientHello) + if !bytes.Equal(clientHello.Cookie, cookie) { + t.Fatalf("Wrong cookie, expected: %x, got: %x", clientHello.Cookie, cookie) + } + if len(packets) <= i { + break + } + // write hello verify request + if _, err := cb.Write(packets[i]); err != nil { + t.Fatal(err) + } + } + cancel() +} + +// Assert that a DTLS Server always responds with RenegotiationInfo if +// a ClientHello contained that extension or not +func TestRenegotationInfo(t *testing.T) { + // Limit runtime in case of deadlocks + lim := test.TimeOut(10 * time.Second) + defer lim.Stop() + + // Check for leaking routines + report := test.CheckRoutines(t) + defer report() + + resp := make([]byte, 1024) + + for _, testCase := range []struct { + Name string + SendRenegotiationInfo bool + }{ + { + "Include RenegotiationInfo", + true, + }, + { + "No RenegotiationInfo", + false, + }, + } { + test := testCase + t.Run(test.Name, func(t *testing.T) { + sendClientHello := func(cookie []byte, ca net.Conn, sequenceNumber uint64) { + extensions := []extension.Extension{} + if test.SendRenegotiationInfo { + extensions = append(extensions, &extension.RenegotiationInfo{ + RenegotiatedConnection: 0, + }) + } + + packet, err := (&recordlayer.RecordLayer{ + Header: recordlayer.Header{ + Version: protocol.Version1_2, + SequenceNumber: sequenceNumber, + }, + Content: &handshake.Handshake{ + Header: handshake.Header{ + MessageSequence: uint16(sequenceNumber), + }, + Message: &handshake.MessageClientHello{ + Version: protocol.Version1_2, + Cookie: cookie, + CipherSuiteIDs: cipherSuiteIDs(defaultCipherSuites()), + CompressionMethods: defaultCompressionMethods(), + Extensions: extensions, + }, + }, + }).Marshal() + if err != nil { + t.Fatal(err) + } + + if _, err = ca.Write(packet); err != nil { + t.Fatal(err) + } + } + + ca, cb := dpipe.Pipe() + defer func() { + if err := ca.Close(); err != nil { + t.Error(err) + } + }() + + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + go func() { + if _, err := testServer(ctx, cb, &Config{}, true); !errors.Is(err, context.Canceled) { + t.Error(err) + } + }() + + time.Sleep(50 * time.Millisecond) + + sendClientHello([]byte{}, ca, 0) + n, err := ca.Read(resp) + if err != nil { + t.Fatal(err) + } + r := &recordlayer.RecordLayer{} + if err = r.Unmarshal(resp[:n]); err != nil { + t.Fatal(err) + } + + helloVerifyRequest := r.Content.(*handshake.Handshake).Message.(*handshake.MessageHelloVerifyRequest) + + sendClientHello(helloVerifyRequest.Cookie, ca, 1) + if n, err = ca.Read(resp); err != nil { + t.Fatal(err) + } + + messages, err := recordlayer.UnpackDatagram(resp[:n]) + if err != nil { + t.Fatal(err) + } + + if err := r.Unmarshal(messages[0]); err != nil { + t.Fatal(err) + } + + serverHello := r.Content.(*handshake.Handshake).Message.(*handshake.MessageServerHello) + gotNegotationInfo := false + for _, v := range serverHello.Extensions { + if _, ok := v.(*extension.RenegotiationInfo); ok { + gotNegotationInfo = true + } + } + + if !gotNegotationInfo { + t.Fatalf("Received ServerHello without RenegotiationInfo") + } + }) + } +} diff --git a/dtls-2.0.9/crypto.go b/dtls-2.0.9/crypto.go new file mode 100644 index 0000000..768ee47 --- /dev/null +++ b/dtls-2.0.9/crypto.go @@ -0,0 +1,221 @@ +package dtls + +import ( + "crypto" + "crypto/ecdsa" + "crypto/ed25519" + "crypto/rand" + "crypto/rsa" + "crypto/sha256" + "crypto/x509" + "encoding/asn1" + "encoding/binary" + "math/big" + "time" + + "github.com/pion/dtls/v2/pkg/crypto/elliptic" + "github.com/pion/dtls/v2/pkg/crypto/hash" +) + +type ecdsaSignature struct { + R, S *big.Int +} + +func valueKeyMessage(clientRandom, serverRandom, publicKey []byte, namedCurve elliptic.Curve) []byte { + serverECDHParams := make([]byte, 4) + serverECDHParams[0] = 3 // named curve + binary.BigEndian.PutUint16(serverECDHParams[1:], uint16(namedCurve)) + serverECDHParams[3] = byte(len(publicKey)) + + plaintext := []byte{} + plaintext = append(plaintext, clientRandom...) + plaintext = append(plaintext, serverRandom...) + plaintext = append(plaintext, serverECDHParams...) + plaintext = append(plaintext, publicKey...) + + return plaintext +} + +// If the client provided a "signature_algorithms" extension, then all +// certificates provided by the server MUST be signed by a +// hash/signature algorithm pair that appears in that extension +// +// https://tools.ietf.org/html/rfc5246#section-7.4.2 +func generateKeySignature(clientRandom, serverRandom, publicKey []byte, namedCurve elliptic.Curve, privateKey crypto.PrivateKey, hashAlgorithm hash.Algorithm) ([]byte, error) { + msg := valueKeyMessage(clientRandom, serverRandom, publicKey, namedCurve) + switch p := privateKey.(type) { + case ed25519.PrivateKey: + // https://crypto.stackexchange.com/a/55483 + return p.Sign(rand.Reader, msg, crypto.Hash(0)) + case *ecdsa.PrivateKey: + hashed := hashAlgorithm.Digest(msg) + return p.Sign(rand.Reader, hashed, hashAlgorithm.CryptoHash()) + case *rsa.PrivateKey: + hashed := hashAlgorithm.Digest(msg) + return p.Sign(rand.Reader, hashed, hashAlgorithm.CryptoHash()) + } + + return nil, errKeySignatureGenerateUnimplemented +} + +func verifyKeySignature(message, remoteKeySignature []byte, hashAlgorithm hash.Algorithm, rawCertificates [][]byte) error { //nolint:dupl + if len(rawCertificates) == 0 { + return errLengthMismatch + } + certificate, err := x509.ParseCertificate(rawCertificates[0]) + if err != nil { + return err + } + + switch p := certificate.PublicKey.(type) { + case ed25519.PublicKey: + if ok := ed25519.Verify(p, message, remoteKeySignature); !ok { + return errKeySignatureMismatch + } + return nil + case *ecdsa.PublicKey: + ecdsaSig := &ecdsaSignature{} + if _, err := asn1.Unmarshal(remoteKeySignature, ecdsaSig); err != nil { + return err + } + if ecdsaSig.R.Sign() <= 0 || ecdsaSig.S.Sign() <= 0 { + return errInvalidECDSASignature + } + hashed := hashAlgorithm.Digest(message) + if !ecdsa.Verify(p, hashed, ecdsaSig.R, ecdsaSig.S) { + return errKeySignatureMismatch + } + return nil + case *rsa.PublicKey: + switch certificate.SignatureAlgorithm { + case x509.SHA1WithRSA, x509.SHA256WithRSA, x509.SHA384WithRSA, x509.SHA512WithRSA: + hashed := hashAlgorithm.Digest(message) + return rsa.VerifyPKCS1v15(p, hashAlgorithm.CryptoHash(), hashed, remoteKeySignature) + default: + return errKeySignatureVerifyUnimplemented + } + } + + return errKeySignatureVerifyUnimplemented +} + +// If the server has sent a CertificateRequest message, the client MUST send the Certificate +// message. The ClientKeyExchange message is now sent, and the content +// of that message will depend on the public key algorithm selected +// between the ClientHello and the ServerHello. If the client has sent +// a certificate with signing ability, a digitally-signed +// CertificateVerify message is sent to explicitly verify possession of +// the private key in the certificate. +// https://tools.ietf.org/html/rfc5246#section-7.3 +func generateCertificateVerify(handshakeBodies []byte, privateKey crypto.PrivateKey, hashAlgorithm hash.Algorithm) ([]byte, error) { + h := sha256.New() + if _, err := h.Write(handshakeBodies); err != nil { + return nil, err + } + hashed := h.Sum(nil) + + switch p := privateKey.(type) { + case ed25519.PrivateKey: + // https://crypto.stackexchange.com/a/55483 + return p.Sign(rand.Reader, hashed, crypto.Hash(0)) + case *ecdsa.PrivateKey: + return p.Sign(rand.Reader, hashed, hashAlgorithm.CryptoHash()) + case *rsa.PrivateKey: + return p.Sign(rand.Reader, hashed, hashAlgorithm.CryptoHash()) + } + + return nil, errInvalidSignatureAlgorithm +} + +func verifyCertificateVerify(handshakeBodies []byte, hashAlgorithm hash.Algorithm, remoteKeySignature []byte, rawCertificates [][]byte) error { //nolint:dupl + if len(rawCertificates) == 0 { + return errLengthMismatch + } + certificate, err := x509.ParseCertificate(rawCertificates[0]) + if err != nil { + return err + } + + switch p := certificate.PublicKey.(type) { + case ed25519.PublicKey: + if ok := ed25519.Verify(p, handshakeBodies, remoteKeySignature); !ok { + return errKeySignatureMismatch + } + return nil + case *ecdsa.PublicKey: + ecdsaSig := &ecdsaSignature{} + if _, err := asn1.Unmarshal(remoteKeySignature, ecdsaSig); err != nil { + return err + } + if ecdsaSig.R.Sign() <= 0 || ecdsaSig.S.Sign() <= 0 { + return errInvalidECDSASignature + } + hash := hashAlgorithm.Digest(handshakeBodies) + if !ecdsa.Verify(p, hash, ecdsaSig.R, ecdsaSig.S) { + return errKeySignatureMismatch + } + return nil + case *rsa.PublicKey: + switch certificate.SignatureAlgorithm { + case x509.SHA1WithRSA, x509.SHA256WithRSA, x509.SHA384WithRSA, x509.SHA512WithRSA: + hash := hashAlgorithm.Digest(handshakeBodies) + return rsa.VerifyPKCS1v15(p, hashAlgorithm.CryptoHash(), hash, remoteKeySignature) + default: + return errKeySignatureVerifyUnimplemented + } + } + + return errKeySignatureVerifyUnimplemented +} + +func loadCerts(rawCertificates [][]byte) ([]*x509.Certificate, error) { + if len(rawCertificates) == 0 { + return nil, errLengthMismatch + } + + certs := make([]*x509.Certificate, 0, len(rawCertificates)) + for _, rawCert := range rawCertificates { + cert, err := x509.ParseCertificate(rawCert) + if err != nil { + return nil, err + } + certs = append(certs, cert) + } + return certs, nil +} + +func verifyClientCert(rawCertificates [][]byte, roots *x509.CertPool) (chains [][]*x509.Certificate, err error) { + certificate, err := loadCerts(rawCertificates) + if err != nil { + return nil, err + } + intermediateCAPool := x509.NewCertPool() + for _, cert := range certificate[1:] { + intermediateCAPool.AddCert(cert) + } + opts := x509.VerifyOptions{ + Roots: roots, + CurrentTime: time.Now(), + Intermediates: intermediateCAPool, + KeyUsages: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth}, + } + return certificate[0].Verify(opts) +} + +func verifyServerCert(rawCertificates [][]byte, roots *x509.CertPool, serverName string) (chains [][]*x509.Certificate, err error) { + certificate, err := loadCerts(rawCertificates) + if err != nil { + return nil, err + } + intermediateCAPool := x509.NewCertPool() + for _, cert := range certificate[1:] { + intermediateCAPool.AddCert(cert) + } + opts := x509.VerifyOptions{ + Roots: roots, + CurrentTime: time.Now(), + DNSName: serverName, + Intermediates: intermediateCAPool, + } + return certificate[0].Verify(opts) +} diff --git a/dtls-2.0.9/crypto_test.go b/dtls-2.0.9/crypto_test.go new file mode 100644 index 0000000..03b714d --- /dev/null +++ b/dtls-2.0.9/crypto_test.go @@ -0,0 +1,73 @@ +package dtls + +import ( + "bytes" + "crypto/x509" + "encoding/pem" + "testing" + + "github.com/pion/dtls/v2/pkg/crypto/elliptic" + "github.com/pion/dtls/v2/pkg/crypto/hash" +) + +const rawPrivateKey = ` +-----BEGIN RSA PRIVATE KEY----- +MIIEowIBAAKCAQEAxIA2BrrnR2sIlATsp7aRBD/3krwZ7vt9dNeoDQAee0s6SuYP +6MBx/HPnAkwNvPS90R05a7pwRkoT6Ur4PfPhCVlUe8lV+0Eto3ZSEeHz3HdsqlM3 +bso67L7Dqrc7MdVstlKcgJi8yeAoGOIL9/igOv0XBFCeznm9nznx6mnsR5cugw+1 +ypXelaHmBCLV7r5SeVSh57+KhvZGbQ2fFpUaTPegRpJZXBNS8lSeWvtOv9d6N5UB +ROTAJodMZT5AfX0jB0QB9IT/0I96H6BSENH08NXOeXApMuLKvnAf361rS7cRAfRL +rWZqERMP4u6Cnk0Cnckc3WcW27kGGIbtwbqUIQIDAQABAoIBAGF7OVIdZp8Hejn0 +N3L8HvT8xtUEe9kS6ioM0lGgvX5s035Uo4/T6LhUx0VcdXRH9eLHnLTUyN4V4cra +ZkxVsE3zAvZl60G6E+oDyLMWZOP6Wu4kWlub9597A5atT7BpMIVCdmFVZFLB4SJ3 +AXkC3nplFAYP+Lh1rJxRIrIn2g+pEeBboWbYA++oDNuMQffDZaokTkJ8Bn1JZYh0 +xEXKY8Bi2Egd5NMeZa1UFO6y8tUbZfwgVs6Enq5uOgtfayq79vZwyjj1kd29MBUD +8g8byV053ZKxbUOiOuUts97eb+fN3DIDRTcT2c+lXt/4C54M1FclJAbtYRK/qwsl +pYWKQAECgYEA4ZUbqQnTo1ICvj81ifGrz+H4LKQqe92Hbf/W51D/Umk2kP702W22 +HP4CvrJRtALThJIG9m2TwUjl/WAuZIBrhSAbIvc3Fcoa2HjdRp+sO5U1ueDq7d/S +Z+PxRI8cbLbRpEdIaoR46qr/2uWZ943PHMv9h4VHPYn1w8b94hwD6vkCgYEA3v87 +mFLzyM9ercnEv9zHMRlMZFQhlcUGQZvfb8BuJYl/WogyT6vRrUuM0QXULNEPlrin +mBQTqc1nCYbgkFFsD2VVt1qIyiAJsB9MD1LNV6YuvE7T2KOSadmsA4fa9PUqbr71 +hf3lTTq+LeR09LebO7WgSGYY+5YKVOEGpYMR1GkCgYEAxPVQmk3HKHEhjgRYdaG5 +lp9A9ZE8uruYVJWtiHgzBTxx9TV2iST+fd/We7PsHFTfY3+wbpcMDBXfIVRKDVwH +BMwchXH9+Ztlxx34bYJaegd0SmA0Hw9ugWEHNgoSEmWpM1s9wir5/ELjc7dGsFtz +uzvsl9fpdLSxDYgAAdzeGtkCgYBAzKIgrVox7DBzB8KojhtD5ToRnXD0+H/M6OKQ +srZPKhlb0V/tTtxrIx0UUEFLlKSXA6mPw6XDHfDnD86JoV9pSeUSlrhRI+Ysy6tq +eIE7CwthpPZiaYXORHZ7wCqcK/HcpJjsCs9rFbrV0yE5S3FMdIbTAvgXg44VBB7O +UbwIoQKBgDuY8gSrA5/A747wjjmsdRWK4DMTMEV4eCW1BEP7Tg7Cxd5n3xPJiYhr +nhLGN+mMnVIcv2zEMS0/eNZr1j/0BtEdx+3IC6Eq+ONY0anZ4Irt57/5QeKgKn/L +JPhfPySIPG4UmwE4gW8t79vfOKxnUu2fDD1ZXUYopan6EckACNH/ +-----END RSA PRIVATE KEY----- +` + +func TestGenerateKeySignature(t *testing.T) { + block, _ := pem.Decode([]byte(rawPrivateKey)) + key, err := x509.ParsePKCS1PrivateKey(block.Bytes) + if err != nil { + t.Error(err) + } + + clientRandom := []byte{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f} + serverRandom := []byte{0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f} + publicKey := []byte{0x20, 0x9f, 0xd7, 0xad, 0x6d, 0xcf, 0xf4, 0x29, 0x8d, 0xd3, 0xf9, 0x6d, 0x5b, 0x1b, 0x2a, 0xf9, 0x10, 0xa0, 0x53, 0x5b, 0x14, 0x88, 0xd7, 0xf8, 0xfa, 0xbb, 0x34, 0x9a, 0x98, 0x28, 0x80, 0xb6, 0x15} + expectedSignature := []byte{ + 0x6f, 0x47, 0x97, 0x85, 0xcc, 0x76, 0x50, 0x93, 0xbd, 0xe2, 0x6a, 0x69, 0x0b, 0xc3, 0x03, 0xd1, 0xb7, 0xe4, 0xab, 0x88, 0x7b, 0xa6, 0x52, 0x80, 0xdf, + 0xaa, 0x25, 0x7a, 0xdb, 0x29, 0x32, 0xe4, 0xd8, 0x28, 0x28, 0xb3, 0xe8, 0x04, 0x3c, 0x38, 0x16, 0xfc, 0x78, 0xe9, 0x15, 0x7b, 0xc5, 0xbd, 0x7d, 0xfc, + 0xcd, 0x83, 0x00, 0x57, 0x4a, 0x3c, 0x23, 0x85, 0x75, 0x6b, 0x37, 0xd5, 0x89, 0x72, 0x73, 0xf0, 0x44, 0x8c, 0x00, 0x70, 0x1f, 0x6e, 0xa2, 0x81, 0xd0, + 0x09, 0xc5, 0x20, 0x36, 0xab, 0x23, 0x09, 0x40, 0x1f, 0x4d, 0x45, 0x96, 0x62, 0xbb, 0x81, 0xb0, 0x30, 0x72, 0xad, 0x3a, 0x0a, 0xac, 0x31, 0x63, 0x40, + 0x52, 0x0a, 0x27, 0xf3, 0x34, 0xde, 0x27, 0x7d, 0xb7, 0x54, 0xff, 0x0f, 0x9f, 0x5a, 0xfe, 0x07, 0x0f, 0x4e, 0x9f, 0x53, 0x04, 0x34, 0x62, 0xf4, 0x30, + 0x74, 0x83, 0x35, 0xfc, 0xe4, 0x7e, 0xbf, 0x5a, 0xc4, 0x52, 0xd0, 0xea, 0xf9, 0x61, 0x4e, 0xf5, 0x1c, 0x0e, 0x58, 0x02, 0x71, 0xfb, 0x1f, 0x34, 0x55, + 0xe8, 0x36, 0x70, 0x3c, 0xc1, 0xcb, 0xc9, 0xb7, 0xbb, 0xb5, 0x1c, 0x44, 0x9a, 0x6d, 0x88, 0x78, 0x98, 0xd4, 0x91, 0x2e, 0xeb, 0x98, 0x81, 0x23, 0x30, + 0x73, 0x39, 0x43, 0xd5, 0xbb, 0x70, 0x39, 0xba, 0x1f, 0xdb, 0x70, 0x9f, 0x91, 0x83, 0x56, 0xc2, 0xde, 0xed, 0x17, 0x6d, 0x2c, 0x3e, 0x21, 0xea, 0x36, + 0xb4, 0x91, 0xd8, 0x31, 0x05, 0x60, 0x90, 0xfd, 0xc6, 0x74, 0xa9, 0x7b, 0x18, 0xfc, 0x1c, 0x6a, 0x1c, 0x6e, 0xec, 0xd3, 0xc1, 0xc0, 0x0d, 0x11, 0x25, + 0x48, 0x37, 0x3d, 0x45, 0x11, 0xa2, 0x31, 0x14, 0x0a, 0x66, 0x9f, 0xd8, 0xac, 0x74, 0xa2, 0xcd, 0xc8, 0x79, 0xb3, 0x9e, 0xc6, 0x66, 0x25, 0xcf, 0x2c, + 0x87, 0x5e, 0x5c, 0x36, 0x75, 0x86, + } + + signature, err := generateKeySignature(clientRandom, serverRandom, publicKey, elliptic.X25519, key, hash.SHA256) + if err != nil { + t.Error(err) + } else if !bytes.Equal(expectedSignature, signature) { + t.Errorf("Signature generation failed \nexp % 02x \nactual % 02x ", expectedSignature, signature) + } +} diff --git a/dtls-2.0.9/dtls.go b/dtls-2.0.9/dtls.go new file mode 100644 index 0000000..125b904 --- /dev/null +++ b/dtls-2.0.9/dtls.go @@ -0,0 +1,2 @@ +// Package dtls implements Datagram Transport Layer Security (DTLS) 1.2 +package dtls diff --git a/dtls-2.0.9/e2e/Dockerfile b/dtls-2.0.9/e2e/Dockerfile new file mode 100644 index 0000000..7166fbc --- /dev/null +++ b/dtls-2.0.9/e2e/Dockerfile @@ -0,0 +1,11 @@ +FROM golang:1.14-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", "."] diff --git a/dtls-2.0.9/e2e/e2e.go b/dtls-2.0.9/e2e/e2e.go new file mode 100644 index 0000000..1a2b024 --- /dev/null +++ b/dtls-2.0.9/e2e/e2e.go @@ -0,0 +1,2 @@ +// Package e2e contains end to end tests for pion/dtls +package e2e diff --git a/dtls-2.0.9/e2e/e2e_lossy_test.go b/dtls-2.0.9/e2e/e2e_lossy_test.go new file mode 100644 index 0000000..92a4074 --- /dev/null +++ b/dtls-2.0.9/e2e/e2e_lossy_test.go @@ -0,0 +1,207 @@ +package e2e + +import ( + "crypto/tls" + "fmt" + "math/rand" + "testing" + "time" + + "github.com/pion/dtls/v2" + "github.com/pion/dtls/v2/pkg/crypto/selfsign" + transportTest "github.com/pion/transport/test" +) + +const ( + flightInterval = time.Millisecond * 100 + lossyTestTimeout = 30 * time.Second +) + +/* + DTLS Client/Server over a lossy transport, just asserts it can handle at increasing increments +*/ +func TestPionE2ELossy(t *testing.T) { + // Check for leaking routines + report := transportTest.CheckRoutines(t) + defer report() + + type runResult struct { + dtlsConn *dtls.Conn + err error + } + + serverCert, err := selfsign.GenerateSelfSigned() + if err != nil { + t.Fatal(err) + } + + clientCert, err := selfsign.GenerateSelfSigned() + if err != nil { + t.Fatal(err) + } + + for _, test := range []struct { + LossChanceRange int + DoClientAuth bool + CipherSuites []dtls.CipherSuiteID + MTU int + }{ + { + LossChanceRange: 0, + }, + { + LossChanceRange: 10, + }, + { + LossChanceRange: 20, + }, + { + LossChanceRange: 50, + }, + { + LossChanceRange: 0, + DoClientAuth: true, + }, + { + LossChanceRange: 10, + DoClientAuth: true, + }, + { + LossChanceRange: 20, + DoClientAuth: true, + }, + { + LossChanceRange: 50, + DoClientAuth: true, + }, + { + LossChanceRange: 0, + CipherSuites: []dtls.CipherSuiteID{dtls.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA}, + }, + { + LossChanceRange: 10, + CipherSuites: []dtls.CipherSuiteID{dtls.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA}, + }, + { + LossChanceRange: 20, + CipherSuites: []dtls.CipherSuiteID{dtls.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA}, + }, + { + LossChanceRange: 50, + CipherSuites: []dtls.CipherSuiteID{dtls.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA}, + }, + { + LossChanceRange: 10, + MTU: 100, + DoClientAuth: true, + }, + { + LossChanceRange: 20, + MTU: 100, + DoClientAuth: true, + }, + { + LossChanceRange: 50, + MTU: 100, + DoClientAuth: true, + }, + } { + name := fmt.Sprintf("Loss%d_MTU%d", test.LossChanceRange, test.MTU) + if test.DoClientAuth { + name += "_WithCliAuth" + } + for _, ciph := range test.CipherSuites { + name += "_With" + ciph.String() + } + test := test + t.Run(name, func(t *testing.T) { + // Limit runtime in case of deadlocks + lim := transportTest.TimeOut(lossyTestTimeout + time.Second) + defer lim.Stop() + + rand.Seed(time.Now().UTC().UnixNano()) + chosenLoss := rand.Intn(9) + test.LossChanceRange //nolint:gosec + serverDone := make(chan runResult) + clientDone := make(chan runResult) + br := transportTest.NewBridge() + + if err = br.SetLossChance(chosenLoss); err != nil { + t.Fatal(err) + } + + go func() { + cfg := &dtls.Config{ + FlightInterval: flightInterval, + CipherSuites: test.CipherSuites, + InsecureSkipVerify: true, + MTU: test.MTU, + } + + if test.DoClientAuth { + cfg.Certificates = []tls.Certificate{clientCert} + } + + client, startupErr := dtls.Client(br.GetConn0(), cfg) + clientDone <- runResult{client, startupErr} + }() + + go func() { + cfg := &dtls.Config{ + Certificates: []tls.Certificate{serverCert}, + FlightInterval: flightInterval, + MTU: test.MTU, + } + + if test.DoClientAuth { + cfg.ClientAuth = dtls.RequireAnyClientCert + } + + server, startupErr := dtls.Server(br.GetConn1(), cfg) + serverDone <- runResult{server, startupErr} + }() + + testTimer := time.NewTimer(lossyTestTimeout) + var serverConn, clientConn *dtls.Conn + defer func() { + if serverConn != nil { + if err = serverConn.Close(); err != nil { + t.Error(err) + } + } + if clientConn != nil { + if err = clientConn.Close(); err != nil { + t.Error(err) + } + } + }() + + for { + if serverConn != nil && clientConn != nil { + break + } + + br.Tick() + select { + case serverResult := <-serverDone: + if serverResult.err != nil { + t.Errorf("Fail, serverError: clientComplete(%t) serverComplete(%t) LossChance(%d) error(%v)", clientConn != nil, serverConn != nil, chosenLoss, serverResult.err) + return + } + + serverConn = serverResult.dtlsConn + case clientResult := <-clientDone: + if clientResult.err != nil { + t.Errorf("Fail, clientError: clientComplete(%t) serverComplete(%t) LossChance(%d) error(%v)", clientConn != nil, serverConn != nil, chosenLoss, clientResult.err) + return + } + + clientConn = clientResult.dtlsConn + case <-testTimer.C: + t.Errorf("Test expired: clientComplete(%t) serverComplete(%t) LossChance(%d)", clientConn != nil, serverConn != nil, chosenLoss) + return + case <-time.After(10 * time.Millisecond): + } + } + }) + } +} diff --git a/dtls-2.0.9/e2e/e2e_openssl_test.go b/dtls-2.0.9/e2e/e2e_openssl_test.go new file mode 100644 index 0000000..fd2e60f --- /dev/null +++ b/dtls-2.0.9/e2e/e2e_openssl_test.go @@ -0,0 +1,250 @@ +// +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) + }) +} diff --git a/dtls-2.0.9/e2e/e2e_openssl_v113_test.go b/dtls-2.0.9/e2e/e2e_openssl_v113_test.go new file mode 100644 index 0000000..1d947b6 --- /dev/null +++ b/dtls-2.0.9/e2e/e2e_openssl_v113_test.go @@ -0,0 +1,17 @@ +// +build openssl,go1.13,!js + +package e2e + +import ( + "testing" +) + +func TestPionOpenSSLE2ESimpleED25519(t *testing.T) { + t.Skip("TODO: waiting OpenSSL's DTLS Ed25519 support") + t.Run("OpenSSLServer", func(t *testing.T) { + testPionE2ESimpleED25519(t, serverOpenSSL, clientPion) + }) + t.Run("OpenSSLClient", func(t *testing.T) { + testPionE2ESimpleED25519(t, serverPion, clientOpenSSL) + }) +} diff --git a/dtls-2.0.9/e2e/e2e_test.go b/dtls-2.0.9/e2e/e2e_test.go new file mode 100644 index 0000000..1a77b3b --- /dev/null +++ b/dtls-2.0.9/e2e/e2e_test.go @@ -0,0 +1,329 @@ +// +build !js + +package e2e + +import ( + "context" + "crypto/tls" + "errors" + "fmt" + "io" + "net" + "sync" + "sync/atomic" + "testing" + "time" + + "github.com/pion/dtls/v2" + "github.com/pion/dtls/v2/pkg/crypto/selfsign" + "github.com/pion/transport/test" +) + +const ( + testMessage = "Hello World" + testTimeLimit = 5 * time.Second + messageRetry = 200 * time.Millisecond +) + +var errServerTimeout = errors.New("waiting on serverReady err: timeout") + +func randomPort(t testing.TB) int { + t.Helper() + conn, err := net.ListenPacket("udp4", "127.0.0.1:0") + if err != nil { + t.Fatalf("failed to pickPort: %v", err) + } + defer func() { + _ = conn.Close() + }() + switch addr := conn.LocalAddr().(type) { + case *net.UDPAddr: + return addr.Port + default: + t.Fatalf("unknown addr type %T", addr) + return 0 + } +} + +func simpleReadWrite(errChan chan error, outChan chan string, conn io.ReadWriter, messageRecvCount *uint64) { + go func() { + buffer := make([]byte, 8192) + n, err := conn.Read(buffer) + if err != nil { + errChan <- err + return + } + + outChan <- string(buffer[:n]) + atomic.AddUint64(messageRecvCount, 1) + }() + + for { + if atomic.LoadUint64(messageRecvCount) == 2 { + break + } else if _, err := conn.Write([]byte(testMessage)); err != nil { + errChan <- err + break + } + + time.Sleep(messageRetry) + } +} + +type comm struct { + ctx context.Context + clientConfig, serverConfig *dtls.Config + serverPort int + messageRecvCount *uint64 // Counter to make sure both sides got a message + clientMutex *sync.Mutex + clientConn net.Conn + serverMutex *sync.Mutex + serverConn net.Conn + serverListener net.Listener + serverReady chan struct{} + errChan chan error + clientChan chan string + serverChan chan string + client func(*comm) + server func(*comm) +} + +func newComm(ctx context.Context, clientConfig, serverConfig *dtls.Config, serverPort int, server, client func(*comm)) *comm { + messageRecvCount := uint64(0) + c := &comm{ + ctx: ctx, + clientConfig: clientConfig, + serverConfig: serverConfig, + serverPort: serverPort, + messageRecvCount: &messageRecvCount, + clientMutex: &sync.Mutex{}, + serverMutex: &sync.Mutex{}, + serverReady: make(chan struct{}), + errChan: make(chan error), + clientChan: make(chan string), + serverChan: make(chan string), + server: server, + client: client, + } + return c +} + +func (c *comm) assert(t *testing.T) { + // DTLS Client + go c.client(c) + + // DTLS Server + go c.server(c) + + defer func() { + if c.clientConn != nil { + if err := c.clientConn.Close(); err != nil { + t.Fatal(err) + } + } + if c.serverConn != nil { + if err := c.serverConn.Close(); err != nil { + t.Fatal(err) + } + } + if c.serverListener != nil { + if err := c.serverListener.Close(); err != nil { + t.Fatal(err) + } + } + }() + + func() { + seenClient, seenServer := false, false + for { + select { + case err := <-c.errChan: + t.Fatal(err) + case <-time.After(testTimeLimit): + t.Fatalf("Test timeout, seenClient %t seenServer %t", seenClient, seenServer) + case clientMsg := <-c.clientChan: + if clientMsg != testMessage { + t.Fatalf("clientMsg does not equal test message: %s %s", clientMsg, testMessage) + } + + seenClient = true + if seenClient && seenServer { + return + } + case serverMsg := <-c.serverChan: + if serverMsg != testMessage { + t.Fatalf("serverMsg does not equal test message: %s %s", serverMsg, testMessage) + } + + seenServer = true + if seenClient && seenServer { + return + } + } + } + }() +} + +func clientPion(c *comm) { + select { + case <-c.serverReady: + // OK + case <-time.After(time.Second): + c.errChan <- errServerTimeout + } + + c.clientMutex.Lock() + defer c.clientMutex.Unlock() + + var err error + c.clientConn, err = dtls.DialWithContext(c.ctx, "udp", + &net.UDPAddr{IP: net.ParseIP("127.0.0.1"), Port: c.serverPort}, + c.clientConfig, + ) + if err != nil { + c.errChan <- err + return + } + + simpleReadWrite(c.errChan, c.clientChan, c.clientConn, c.messageRecvCount) +} + +func serverPion(c *comm) { + c.serverMutex.Lock() + defer c.serverMutex.Unlock() + + var err error + c.serverListener, err = dtls.Listen("udp", + &net.UDPAddr{IP: net.ParseIP("127.0.0.1"), Port: c.serverPort}, + c.serverConfig, + ) + if err != nil { + c.errChan <- err + return + } + c.serverReady <- struct{}{} + c.serverConn, err = c.serverListener.Accept() + if err != nil { + c.errChan <- err + return + } + + simpleReadWrite(c.errChan, c.serverChan, c.serverConn, c.messageRecvCount) +} + +/* + Simple DTLS Client/Server can communicate + - Assert that you can send messages both ways + - Assert that Close() on both ends work + - Assert that no Goroutines are leaked +*/ +func testPionE2ESimple(t *testing.T, server, client func(*comm)) { + lim := test.TimeOut(time.Second * 30) + defer lim.Stop() + + report := test.CheckRoutines(t) + defer report() + + for _, cipherSuite := range []dtls.CipherSuiteID{ + dtls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, + dtls.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, + } { + cipherSuite := cipherSuite + t.Run(cipherSuite.String(), func(t *testing.T) { + ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second) + defer cancel() + + cert, err := selfsign.GenerateSelfSignedWithDNS("localhost") + if err != nil { + t.Fatal(err) + } + + cfg := &dtls.Config{ + Certificates: []tls.Certificate{cert}, + CipherSuites: []dtls.CipherSuiteID{cipherSuite}, + InsecureSkipVerify: true, + } + serverPort := randomPort(t) + comm := newComm(ctx, cfg, cfg, serverPort, server, client) + comm.assert(t) + }) + } +} + +func testPionE2ESimplePSK(t *testing.T, server, client func(*comm)) { + lim := test.TimeOut(time.Second * 30) + defer lim.Stop() + + report := test.CheckRoutines(t) + defer report() + + for _, cipherSuite := range []dtls.CipherSuiteID{ + dtls.TLS_PSK_WITH_AES_128_CCM, + dtls.TLS_PSK_WITH_AES_128_CCM_8, + dtls.TLS_PSK_WITH_AES_128_GCM_SHA256, + } { + cipherSuite := cipherSuite + t.Run(cipherSuite.String(), func(t *testing.T) { + ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second) + defer cancel() + + cfg := &dtls.Config{ + PSK: func(hint []byte) ([]byte, error) { + return []byte{0xAB, 0xC1, 0x23}, nil + }, + PSKIdentityHint: []byte{0x01, 0x02, 0x03, 0x04, 0x05}, + CipherSuites: []dtls.CipherSuiteID{cipherSuite}, + } + serverPort := randomPort(t) + comm := newComm(ctx, cfg, cfg, serverPort, server, client) + comm.assert(t) + }) + } +} + +func testPionE2EMTUs(t *testing.T, server, client func(*comm)) { + lim := test.TimeOut(time.Second * 30) + defer lim.Stop() + + report := test.CheckRoutines(t) + defer report() + + for _, mtu := range []int{ + 10000, + 1000, + 100, + } { + mtu := mtu + t.Run(fmt.Sprintf("MTU%d", mtu), func(t *testing.T) { + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + defer cancel() + + cert, err := selfsign.GenerateSelfSignedWithDNS("localhost") + if err != nil { + t.Fatal(err) + } + + cfg := &dtls.Config{ + Certificates: []tls.Certificate{cert}, + CipherSuites: []dtls.CipherSuiteID{dtls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256}, + InsecureSkipVerify: true, + MTU: mtu, + } + serverPort := randomPort(t) + comm := newComm(ctx, cfg, cfg, serverPort, server, client) + comm.assert(t) + }) + } +} + +func TestPionE2ESimple(t *testing.T) { + testPionE2ESimple(t, serverPion, clientPion) +} + +func TestPionE2ESimplePSK(t *testing.T) { + testPionE2ESimplePSK(t, serverPion, clientPion) +} + +func TestPionE2EMTUs(t *testing.T) { + testPionE2EMTUs(t, serverPion, clientPion) +} diff --git a/dtls-2.0.9/e2e/e2e_v113_test.go b/dtls-2.0.9/e2e/e2e_v113_test.go new file mode 100644 index 0000000..5d7243f --- /dev/null +++ b/dtls-2.0.9/e2e/e2e_v113_test.go @@ -0,0 +1,62 @@ +// +build go1.13,!js + +package e2e + +import ( + "context" + "crypto/ed25519" + "crypto/rand" + "crypto/tls" + "testing" + "time" + + "github.com/pion/dtls/v2" + "github.com/pion/dtls/v2/pkg/crypto/selfsign" + "github.com/pion/transport/test" +) + +// ED25519 is not supported in Go 1.12 crypto/x509. +// Once Go 1.12 is deprecated, move this test to e2e_test.go. + +func testPionE2ESimpleED25519(t *testing.T, server, client func(*comm)) { + lim := test.TimeOut(time.Second * 30) + defer lim.Stop() + + report := test.CheckRoutines(t) + defer report() + + for _, cipherSuite := range []dtls.CipherSuiteID{ + dtls.TLS_ECDHE_ECDSA_WITH_AES_128_CCM, + dtls.TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8, + dtls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, + dtls.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, + } { + cipherSuite := cipherSuite + t.Run(cipherSuite.String(), func(t *testing.T) { + ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) + defer cancel() + + _, key, err := ed25519.GenerateKey(rand.Reader) + if err != nil { + t.Fatal(err) + } + cert, err := selfsign.SelfSign(key) + if err != nil { + t.Fatal(err) + } + + cfg := &dtls.Config{ + Certificates: []tls.Certificate{cert}, + CipherSuites: []dtls.CipherSuiteID{cipherSuite}, + InsecureSkipVerify: true, + } + serverPort := randomPort(t) + comm := newComm(ctx, cfg, cfg, serverPort, server, client) + comm.assert(t) + }) + } +} + +func TestPionE2ESimpleED25519(t *testing.T) { + testPionE2ESimpleED25519(t, serverPion, clientPion) +} diff --git a/dtls-2.0.9/errors.go b/dtls-2.0.9/errors.go new file mode 100644 index 0000000..2e16388 --- /dev/null +++ b/dtls-2.0.9/errors.go @@ -0,0 +1,141 @@ +package dtls + +import ( + "context" + "errors" + "fmt" + "io" + "net" + "os" + + "github.com/pion/dtls/v2/pkg/protocol" + "github.com/pion/dtls/v2/pkg/protocol/alert" + "golang.org/x/xerrors" +) + +// Typed errors +var ( + ErrConnClosed = &FatalError{Err: errors.New("conn is closed")} //nolint:goerr113 + + errDeadlineExceeded = &TimeoutError{Err: xerrors.Errorf("read/write timeout: %w", context.DeadlineExceeded)} + errInvalidContentType = &TemporaryError{Err: errors.New("invalid content type")} //nolint:goerr113 + + errBufferTooSmall = &TemporaryError{Err: errors.New("buffer is too small")} //nolint:goerr113 + errContextUnsupported = &TemporaryError{Err: errors.New("context is not supported for ExportKeyingMaterial")} //nolint:goerr113 + errHandshakeInProgress = &TemporaryError{Err: errors.New("handshake is in progress")} //nolint:goerr113 + errReservedExportKeyingMaterial = &TemporaryError{Err: errors.New("ExportKeyingMaterial can not be used with a reserved label")} //nolint:goerr113 + errApplicationDataEpochZero = &TemporaryError{Err: errors.New("ApplicationData with epoch of 0")} //nolint:goerr113 + errUnhandledContextType = &TemporaryError{Err: errors.New("unhandled contentType")} //nolint:goerr113 + + errCertificateVerifyNoCertificate = &FatalError{Err: errors.New("client sent certificate verify but we have no certificate to verify")} //nolint:goerr113 + errCipherSuiteNoIntersection = &FatalError{Err: errors.New("client+server do not support any shared cipher suites")} //nolint:goerr113 + errClientCertificateNotVerified = &FatalError{Err: errors.New("client sent certificate but did not verify it")} //nolint:goerr113 + errClientCertificateRequired = &FatalError{Err: errors.New("server required client verification, but got none")} //nolint:goerr113 + errClientNoMatchingSRTPProfile = &FatalError{Err: errors.New("server responded with SRTP Profile we do not support")} //nolint:goerr113 + errClientRequiredButNoServerEMS = &FatalError{Err: errors.New("client required Extended Master Secret extension, but server does not support it")} //nolint:goerr113 + errCookieMismatch = &FatalError{Err: errors.New("client+server cookie does not match")} //nolint:goerr113 + errIdentityNoPSK = &FatalError{Err: errors.New("PSK Identity Hint provided but PSK is nil")} //nolint:goerr113 + errInvalidCertificate = &FatalError{Err: errors.New("no certificate provided")} //nolint:goerr113 + errInvalidCipherSuite = &FatalError{Err: errors.New("invalid or unknown cipher suite")} //nolint:goerr113 + errInvalidECDSASignature = &FatalError{Err: errors.New("ECDSA signature contained zero or negative values")} //nolint:goerr113 + errInvalidPrivateKey = &FatalError{Err: errors.New("invalid private key type")} //nolint:goerr113 + errInvalidSignatureAlgorithm = &FatalError{Err: errors.New("invalid signature algorithm")} //nolint:goerr113 + errKeySignatureMismatch = &FatalError{Err: errors.New("expected and actual key signature do not match")} //nolint:goerr113 + errNilNextConn = &FatalError{Err: errors.New("Conn can not be created with a nil nextConn")} //nolint:goerr113 + errNoAvailableCipherSuites = &FatalError{Err: errors.New("connection can not be created, no CipherSuites satisfy this Config")} //nolint:goerr113 + errNoAvailablePSKCipherSuite = &FatalError{Err: errors.New("connection can not be created, pre-shared key present but no compatible CipherSuite")} //nolint:goerr113 + errNoAvailableCertificateCipherSuite = &FatalError{Err: errors.New("connection can not be created, certificate present but no compatible CipherSuite")} //nolint:goerr113 + errNoAvailableSignatureSchemes = &FatalError{Err: errors.New("connection can not be created, no SignatureScheme satisfy this Config")} //nolint:goerr113 + errNoCertificates = &FatalError{Err: errors.New("no certificates configured")} //nolint:goerr113 + errNoConfigProvided = &FatalError{Err: errors.New("no config provided")} //nolint:goerr113 + errNoSupportedEllipticCurves = &FatalError{Err: errors.New("client requested zero or more elliptic curves that are not supported by the server")} //nolint:goerr113 + errUnsupportedProtocolVersion = &FatalError{Err: errors.New("unsupported protocol version")} //nolint:goerr113 + errPSKAndIdentityMustBeSetForClient = &FatalError{Err: errors.New("PSK and PSK Identity Hint must both be set for client")} //nolint:goerr113 + errRequestedButNoSRTPExtension = &FatalError{Err: errors.New("SRTP support was requested but server did not respond with use_srtp extension")} //nolint:goerr113 + errServerNoMatchingSRTPProfile = &FatalError{Err: errors.New("client requested SRTP but we have no matching profiles")} //nolint:goerr113 + errServerRequiredButNoClientEMS = &FatalError{Err: errors.New("server requires the Extended Master Secret extension, but the client does not support it")} //nolint:goerr113 + errVerifyDataMismatch = &FatalError{Err: errors.New("expected and actual verify data does not match")} //nolint:goerr113 + + errInvalidFlight = &InternalError{Err: errors.New("invalid flight number")} //nolint:goerr113 + errKeySignatureGenerateUnimplemented = &InternalError{Err: errors.New("unable to generate key signature, unimplemented")} //nolint:goerr113 + errKeySignatureVerifyUnimplemented = &InternalError{Err: errors.New("unable to verify key signature, unimplemented")} //nolint:goerr113 + errLengthMismatch = &InternalError{Err: errors.New("data length and declared length do not match")} //nolint:goerr113 + errSequenceNumberOverflow = &InternalError{Err: errors.New("sequence number overflow")} //nolint:goerr113 + errInvalidFSMTransition = &InternalError{Err: errors.New("invalid state machine transition")} //nolint:goerr113 +) + +// FatalError indicates that the DTLS connection is no longer available. +// It is mainly caused by wrong configuration of server or client. +type FatalError = protocol.FatalError + +// InternalError indicates and internal error caused by the implementation, and the DTLS connection is no longer available. +// It is mainly caused by bugs or tried to use unimplemented features. +type InternalError = protocol.InternalError + +// TemporaryError indicates that the DTLS connection is still available, but the request was failed temporary. +type TemporaryError = protocol.TemporaryError + +// TimeoutError indicates that the request was timed out. +type TimeoutError = protocol.TimeoutError + +// HandshakeError indicates that the handshake failed. +type HandshakeError = protocol.HandshakeError + +// invalidCipherSuite indicates an attempt at using an unsupported cipher suite. +type invalidCipherSuite struct { + id CipherSuiteID +} + +func (e *invalidCipherSuite) Error() string { + return fmt.Sprintf("CipherSuite with id(%d) is not valid", e.id) +} + +func (e *invalidCipherSuite) Is(err error) bool { + if other, ok := err.(*invalidCipherSuite); ok { + return e.id == other.id + } + return false +} + +// errAlert wraps DTLS alert notification as an error +type errAlert struct { + *alert.Alert +} + +func (e *errAlert) Error() string { + return fmt.Sprintf("alert: %s", e.Alert.String()) +} + +func (e *errAlert) IsFatalOrCloseNotify() bool { + return e.Level == alert.Fatal || e.Description == alert.CloseNotify +} + +func (e *errAlert) Is(err error) bool { + if other, ok := err.(*errAlert); ok { + return e.Level == other.Level && e.Description == other.Description + } + return false +} + +// netError translates an error from underlying Conn to corresponding net.Error. +func netError(err error) error { + switch err { + case io.EOF, context.Canceled, context.DeadlineExceeded: + // Return io.EOF and context errors as is. + return err + } + switch e := err.(type) { + case (*net.OpError): + if se, ok := e.Err.(*os.SyscallError); ok { + if se.Timeout() { + return &TimeoutError{Err: err} + } + if isOpErrorTemporary(se) { + return &TemporaryError{Err: err} + } + } + case (net.Error): + return err + } + return &FatalError{Err: err} +} diff --git a/dtls-2.0.9/errors_errno.go b/dtls-2.0.9/errors_errno.go new file mode 100644 index 0000000..a9a439b --- /dev/null +++ b/dtls-2.0.9/errors_errno.go @@ -0,0 +1,25 @@ +// +build aix darwin dragonfly freebsd linux nacl nacljs netbsd openbsd solaris windows + +// For systems having syscall.Errno. +// Update build targets by following command: +// $ grep -R ECONN $(go env GOROOT)/src/syscall/zerrors_*.go \ +// | tr "." "_" | cut -d"_" -f"2" | sort | uniq + +package dtls + +import ( + "os" + "syscall" +) + +func isOpErrorTemporary(err *os.SyscallError) bool { + if ne, ok := err.Err.(syscall.Errno); ok { + switch ne { + case syscall.ECONNREFUSED: + return true + default: + return false + } + } + return false +} diff --git a/dtls-2.0.9/errors_errno_test.go b/dtls-2.0.9/errors_errno_test.go new file mode 100644 index 0000000..7c567ee --- /dev/null +++ b/dtls-2.0.9/errors_errno_test.go @@ -0,0 +1,41 @@ +// +build aix darwin dragonfly freebsd linux nacl nacljs netbsd openbsd solaris windows + +// For systems having syscall.Errno. +// The build target must be same as errors_errno.go. + +package dtls + +import ( + "net" + "testing" +) + +func TestErrorsTemporary(t *testing.T) { + addrListen, errListen := net.ResolveUDPAddr("udp", "localhost:0") + if errListen != nil { + t.Fatalf("Unexpected error: %v", errListen) + } + // Server is not listening. + conn, errDial := net.DialUDP("udp", nil, addrListen) + if errDial != nil { + t.Fatalf("Unexpected error: %v", errDial) + } + + _, _ = conn.Write([]byte{0x00}) // trigger + _, err := conn.Read(make([]byte, 10)) + _ = conn.Close() + + if err == nil { + t.Skip("ECONNREFUSED is not set by system") + } + ne, ok := netError(err).(net.Error) + if !ok { + t.Fatalf("netError must return net.Error") + } + if ne.Timeout() { + t.Errorf("%v must not be timeout error", err) + } + if !ne.Temporary() { + t.Errorf("%v must be temporary error", err) + } +} diff --git a/dtls-2.0.9/errors_noerrno.go b/dtls-2.0.9/errors_noerrno.go new file mode 100644 index 0000000..fcc37ce --- /dev/null +++ b/dtls-2.0.9/errors_noerrno.go @@ -0,0 +1,14 @@ +// +build !aix,!darwin,!dragonfly,!freebsd,!linux,!nacl,!nacljs,!netbsd,!openbsd,!solaris,!windows + +// For systems without syscall.Errno. +// Build targets must be inverse of errors_errno.go + +package dtls + +import ( + "os" +) + +func isOpErrorTemporary(err *os.SyscallError) bool { + return false +} diff --git a/dtls-2.0.9/errors_test.go b/dtls-2.0.9/errors_test.go new file mode 100644 index 0000000..0234315 --- /dev/null +++ b/dtls-2.0.9/errors_test.go @@ -0,0 +1,85 @@ +package dtls + +import ( + "errors" + "fmt" + "net" + "testing" + + "golang.org/x/xerrors" +) + +var errExample = errors.New("an example error") + +func TestErrorUnwrap(t *testing.T) { + cases := []struct { + err error + errUnwrapped []error + }{ + { + &FatalError{Err: errExample}, + []error{errExample}, + }, + { + &TemporaryError{Err: errExample}, + []error{errExample}, + }, + { + &InternalError{Err: errExample}, + []error{errExample}, + }, + { + &TimeoutError{Err: errExample}, + []error{errExample}, + }, + { + &HandshakeError{Err: errExample}, + []error{errExample}, + }, + } + for _, c := range cases { + c := c + t.Run(fmt.Sprintf("%T", c.err), func(t *testing.T) { + err := c.err + for _, unwrapped := range c.errUnwrapped { + e := xerrors.Unwrap(err) + if !errors.Is(e, unwrapped) { + t.Errorf("Unwrapped error is expected to be '%v', got '%v'", unwrapped, e) + } + } + }) + } +} + +func TestErrorNetError(t *testing.T) { + cases := []struct { + err error + str string + timeout, temporary bool + }{ + {&FatalError{Err: errExample}, "dtls fatal: an example error", false, false}, + {&TemporaryError{Err: errExample}, "dtls temporary: an example error", false, true}, + {&InternalError{Err: errExample}, "dtls internal: an example error", false, false}, + {&TimeoutError{Err: errExample}, "dtls timeout: an example error", true, true}, + {&HandshakeError{Err: errExample}, "handshake error: an example error", false, false}, + {&HandshakeError{Err: &TimeoutError{Err: errExample}}, "handshake error: dtls timeout: an example error", true, true}, + } + for _, c := range cases { + c := c + t.Run(fmt.Sprintf("%T", c.err), func(t *testing.T) { + ne, ok := c.err.(net.Error) + if !ok { + t.Fatalf("%T doesn't implement net.Error", c.err) + } + if ne.Timeout() != c.timeout { + t.Errorf("%T.Timeout() should be %v", c.err, c.timeout) + } + if ne.Temporary() != c.temporary { + t.Errorf("%T.Temporary() should be %v", c.err, c.temporary) + } + if ne.Error() != c.str { + t.Errorf("%T.Error() should be %v", c.err, c.str) + } + }) + } +} diff --git a/dtls-2.0.9/examples/certificates/README.md b/dtls-2.0.9/examples/certificates/README.md new file mode 100644 index 0000000..aef3d09 --- /dev/null +++ b/dtls-2.0.9/examples/certificates/README.md @@ -0,0 +1,26 @@ +# Certificates + +The certificates in for the examples are generated using the commands shown below. + +Note that this was run on OpenSSL 1.1.1d, of which the arguments can be found in the [OpenSSL Manpages](https://www.openssl.org/docs/man1.1.1/man1), and is not guaranteed to work on different OpenSSL versions. + +```shell +# Extensions required for certificate validation. +$ EXTFILE='extfile.conf' +$ echo 'subjectAltName = IP:127.0.0.1\nbasicConstraints = critical,CA:true' > "${EXTFILE}" + +# Server. +$ SERVER_NAME='server' +$ openssl ecparam -name prime256v1 -genkey -noout -out "${SERVER_NAME}.pem" +$ openssl req -key "${SERVER_NAME}.pem" -new -sha256 -subj '/C=NL' -out "${SERVER_NAME}.csr" +$ openssl x509 -req -in "${SERVER_NAME}.csr" -extfile "${EXTFILE}" -days 365 -signkey "${SERVER_NAME}.pem" -sha256 -out "${SERVER_NAME}.pub.pem" + +# Client. +$ CLIENT_NAME='client' +$ openssl ecparam -name prime256v1 -genkey -noout -out "${CLIENT_NAME}.pem" +$ openssl req -key "${CLIENT_NAME}.pem" -new -sha256 -subj '/C=NL' -out "${CLIENT_NAME}.csr" +$ openssl x509 -req -in "${CLIENT_NAME}.csr" -extfile "${EXTFILE}" -days 365 -CA "${SERVER_NAME}.pub.pem" -CAkey "${SERVER_NAME}.pem" -set_serial '0xabcd' -sha256 -out "${CLIENT_NAME}.pub.pem" + +# Cleanup. +$ rm "${EXTFILE}" "${SERVER_NAME}.csr" "${CLIENT_NAME}.csr" +``` diff --git a/dtls-2.0.9/examples/certificates/client.pem b/dtls-2.0.9/examples/certificates/client.pem new file mode 100644 index 0000000..f092d50 --- /dev/null +++ b/dtls-2.0.9/examples/certificates/client.pem @@ -0,0 +1,5 @@ +-----BEGIN EC PRIVATE KEY----- +MHcCAQEEIGOO78dEAcepxdUIeDzC28jMcFrJr2q7x+UdhgtJ/RS3oAoGCCqGSM49 +AwEHoUQDQgAEGLSNxlkJ9mETKI2Hogq3Cyh06pJKA1YMgcKqYKS6yQQlvvk5rU88 ++RojFPgXJukymhfIJmw4eGxxEMSjuEZY7w== +-----END EC PRIVATE KEY----- diff --git a/dtls-2.0.9/examples/certificates/client.pub.pem b/dtls-2.0.9/examples/certificates/client.pub.pem new file mode 100644 index 0000000..1259953 --- /dev/null +++ b/dtls-2.0.9/examples/certificates/client.pub.pem @@ -0,0 +1,9 @@ +-----BEGIN CERTIFICATE----- +MIIBLTCB1aADAgECAgMAq80wCgYIKoZIzj0EAwIwDTELMAkGA1UEBhMCTkwwHhcN +MjAwMzIwMDk0NjQ0WhcNMjEwMzIwMDk0NjQ0WjANMQswCQYDVQQGEwJOTDBZMBMG +ByqGSM49AgEGCCqGSM49AwEHA0IABBi0jcZZCfZhEyiNh6IKtwsodOqSSgNWDIHC +qmCkuskEJb75Oa1PPPkaIxT4FybpMpoXyCZsOHhscRDEo7hGWO+jJDAiMA8GA1Ud +EQQIMAaHBH8AAAEwDwYDVR0TAQH/BAUwAwEB/zAKBggqhkjOPQQDAgNHADBEAiBx +sIkcADN9E60veZOFOeANaRWAiQaLWZfUxqkOmfHztQIgI2CfHMjDQwJZFh35HvFs +NOPJj8wxFhqR5pqMF23cgOY= +-----END CERTIFICATE----- diff --git a/dtls-2.0.9/examples/certificates/server.pem b/dtls-2.0.9/examples/certificates/server.pem new file mode 100644 index 0000000..5a559d8 --- /dev/null +++ b/dtls-2.0.9/examples/certificates/server.pem @@ -0,0 +1,5 @@ +-----BEGIN EC PRIVATE KEY----- +MHcCAQEEIDT8Xyx5RpPP+98ulYZKsvKIVdBUJug/L9H2M8JThv+GoAoGCCqGSM49 +AwEHoUQDQgAE6Wf0qQqIb5G7g51P83Dh1Yst52kyntGYz1Bt6S7crpmQFs9ZRZMy +bJ6MGIwGcVBMgoL3pfxDKdZ3mnzmoibU0w== +-----END EC PRIVATE KEY----- diff --git a/dtls-2.0.9/examples/certificates/server.pub.pem b/dtls-2.0.9/examples/certificates/server.pub.pem new file mode 100644 index 0000000..e1cf479 --- /dev/null +++ b/dtls-2.0.9/examples/certificates/server.pub.pem @@ -0,0 +1,9 @@ +-----BEGIN CERTIFICATE----- +MIIBPzCB5qADAgECAhRtzyVTL+9D0KHfbcKYeKckpLVRmTAKBggqhkjOPQQDAjAN +MQswCQYDVQQGEwJOTDAeFw0yMDAzMjAwOTQ2NDRaFw0yMTAzMjAwOTQ2NDRaMA0x +CzAJBgNVBAYTAk5MMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE6Wf0qQqIb5G7 +g51P83Dh1Yst52kyntGYz1Bt6S7crpmQFs9ZRZMybJ6MGIwGcVBMgoL3pfxDKdZ3 +mnzmoibU06MkMCIwDwYDVR0RBAgwBocEfwAAATAPBgNVHRMBAf8EBTADAQH/MAoG +CCqGSM49BAMCA0gAMEUCIQD000SU+klkNLGvHZcMYNVkCFsImnGKIqPMy3LELSiF +0gIgSGIFkNEIAyNxn44CXZJu3piyz1ouK2fLefDJMYfcXgM= +-----END CERTIFICATE----- diff --git a/dtls-2.0.9/examples/dial/psk/main.go b/dtls-2.0.9/examples/dial/psk/main.go new file mode 100644 index 0000000..dfac162 --- /dev/null +++ b/dtls-2.0.9/examples/dial/psk/main.go @@ -0,0 +1,45 @@ +package main + +import ( + "context" + "fmt" + "net" + "time" + + "github.com/pion/dtls/v2" + "github.com/pion/dtls/v2/examples/util" +) + +func main() { + // Prepare the IP to connect to + addr := &net.UDPAddr{IP: net.ParseIP("127.0.0.1"), Port: 4444} + + // + // Everything below is the pion-DTLS API! Thanks for using it ❤️. + // + + // Prepare the configuration of the DTLS connection + config := &dtls.Config{ + PSK: func(hint []byte) ([]byte, error) { + fmt.Printf("Server's hint: %s \n", hint) + return []byte{0xAB, 0xC1, 0x23}, nil + }, + PSKIdentityHint: []byte("Pion DTLS Server"), + CipherSuites: []dtls.CipherSuiteID{dtls.TLS_PSK_WITH_AES_128_CCM_8}, + ExtendedMasterSecret: dtls.RequireExtendedMasterSecret, + } + + // Connect to a DTLS server + ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) + defer cancel() + dtlsConn, err := dtls.DialWithContext(ctx, "udp", addr, config) + util.Check(err) + defer func() { + util.Check(dtlsConn.Close()) + }() + + fmt.Println("Connected; type 'exit' to shutdown gracefully") + + // Simulate a chat session + util.Chat(dtlsConn) +} diff --git a/dtls-2.0.9/examples/dial/selfsign/main.go b/dtls-2.0.9/examples/dial/selfsign/main.go new file mode 100644 index 0000000..7ff2926 --- /dev/null +++ b/dtls-2.0.9/examples/dial/selfsign/main.go @@ -0,0 +1,47 @@ +package main + +import ( + "context" + "crypto/tls" + "fmt" + "net" + "time" + + "github.com/pion/dtls/v2" + "github.com/pion/dtls/v2/examples/util" + "github.com/pion/dtls/v2/pkg/crypto/selfsign" +) + +func main() { + // Prepare the IP to connect to + addr := &net.UDPAddr{IP: net.ParseIP("127.0.0.1"), Port: 4444} + + // Generate a certificate and private key to secure the connection + certificate, genErr := selfsign.GenerateSelfSigned() + util.Check(genErr) + + // + // Everything below is the pion-DTLS API! Thanks for using it ❤️. + // + + // Prepare the configuration of the DTLS connection + config := &dtls.Config{ + Certificates: []tls.Certificate{certificate}, + InsecureSkipVerify: true, + ExtendedMasterSecret: dtls.RequireExtendedMasterSecret, + } + + // Connect to a DTLS server + ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) + defer cancel() + dtlsConn, err := dtls.DialWithContext(ctx, "udp", addr, config) + util.Check(err) + defer func() { + util.Check(dtlsConn.Close()) + }() + + fmt.Println("Connected; type 'exit' to shutdown gracefully") + + // Simulate a chat session + util.Chat(dtlsConn) +} diff --git a/dtls-2.0.9/examples/dial/verify/main.go b/dtls-2.0.9/examples/dial/verify/main.go new file mode 100644 index 0000000..53340da --- /dev/null +++ b/dtls-2.0.9/examples/dial/verify/main.go @@ -0,0 +1,54 @@ +package main + +import ( + "context" + "crypto/tls" + "crypto/x509" + "fmt" + "net" + "time" + + "github.com/pion/dtls/v2" + "github.com/pion/dtls/v2/examples/util" +) + +func main() { + // Prepare the IP to connect to + addr := &net.UDPAddr{IP: net.ParseIP("127.0.0.1"), Port: 4444} + + // + // Everything below is the pion-DTLS API! Thanks for using it ❤️. + // + + certificate, err := util.LoadKeyAndCertificate("examples/certificates/client.pem", + "examples/certificates/client.pub.pem") + util.Check(err) + + rootCertificate, err := util.LoadCertificate("examples/certificates/server.pub.pem") + util.Check(err) + certPool := x509.NewCertPool() + cert, err := x509.ParseCertificate(rootCertificate.Certificate[0]) + util.Check(err) + certPool.AddCert(cert) + + // Prepare the configuration of the DTLS connection + config := &dtls.Config{ + Certificates: []tls.Certificate{*certificate}, + ExtendedMasterSecret: dtls.RequireExtendedMasterSecret, + RootCAs: certPool, + } + + // Connect to a DTLS server + ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) + defer cancel() + dtlsConn, err := dtls.DialWithContext(ctx, "udp", addr, config) + util.Check(err) + defer func() { + util.Check(dtlsConn.Close()) + }() + + fmt.Println("Connected; type 'exit' to shutdown gracefully") + + // Simulate a chat session + util.Chat(dtlsConn) +} diff --git a/dtls-2.0.9/examples/listen/psk/main.go b/dtls-2.0.9/examples/listen/psk/main.go new file mode 100644 index 0000000..72a6c23 --- /dev/null +++ b/dtls-2.0.9/examples/listen/psk/main.go @@ -0,0 +1,72 @@ +package main + +import ( + "context" + "fmt" + "net" + "time" + + "github.com/pion/dtls/v2" + "github.com/pion/dtls/v2/examples/util" +) + +func main() { + // Prepare the IP to connect to + addr := &net.UDPAddr{IP: net.ParseIP("127.0.0.1"), Port: 4444} + + // Create parent context to cleanup handshaking connections on exit. + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + // + // Everything below is the pion-DTLS API! Thanks for using it ❤️. + // + + // Prepare the configuration of the DTLS connection + config := &dtls.Config{ + PSK: func(hint []byte) ([]byte, error) { + fmt.Printf("Client's hint: %s \n", hint) + return []byte{0xAB, 0xC1, 0x23}, nil + }, + PSKIdentityHint: []byte("Pion DTLS Client"), + CipherSuites: []dtls.CipherSuiteID{dtls.TLS_PSK_WITH_AES_128_CCM_8}, + ExtendedMasterSecret: dtls.RequireExtendedMasterSecret, + // Create timeout context for accepted connection. + ConnectContextMaker: func() (context.Context, func()) { + return context.WithTimeout(ctx, 30*time.Second) + }, + } + + // Connect to a DTLS server + listener, err := dtls.Listen("udp", addr, config) + util.Check(err) + defer func() { + util.Check(listener.Close()) + }() + + fmt.Println("Listening") + + // Simulate a chat session + hub := util.NewHub() + + go func() { + for { + // Wait for a connection. + conn, err := listener.Accept() + util.Check(err) + // defer conn.Close() // TODO: graceful shutdown + + // `conn` is of type `net.Conn` but may be casted to `dtls.Conn` + // using `dtlsConn := conn.(*dtls.Conn)` in order to to expose + // functions like `ConnectionState` etc. + + // Register the connection with the chat hub + if err == nil { + hub.Register(conn) + } + } + }() + + // Start chatting + hub.Chat() +} diff --git a/dtls-2.0.9/examples/listen/selfsign/main.go b/dtls-2.0.9/examples/listen/selfsign/main.go new file mode 100644 index 0000000..2df4ac4 --- /dev/null +++ b/dtls-2.0.9/examples/listen/selfsign/main.go @@ -0,0 +1,73 @@ +package main + +import ( + "context" + "crypto/tls" + "fmt" + "net" + "time" + + "github.com/pion/dtls/v2" + "github.com/pion/dtls/v2/examples/util" + "github.com/pion/dtls/v2/pkg/crypto/selfsign" +) + +func main() { + // Prepare the IP to connect to + addr := &net.UDPAddr{IP: net.ParseIP("127.0.0.1"), Port: 4444} + + // Generate a certificate and private key to secure the connection + certificate, genErr := selfsign.GenerateSelfSigned() + util.Check(genErr) + + // Create parent context to cleanup handshaking connections on exit. + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + // + // Everything below is the pion-DTLS API! Thanks for using it ❤️. + // + + // Prepare the configuration of the DTLS connection + config := &dtls.Config{ + Certificates: []tls.Certificate{certificate}, + ExtendedMasterSecret: dtls.RequireExtendedMasterSecret, + // Create timeout context for accepted connection. + ConnectContextMaker: func() (context.Context, func()) { + return context.WithTimeout(ctx, 30*time.Second) + }, + } + + // Connect to a DTLS server + listener, err := dtls.Listen("udp", addr, config) + util.Check(err) + defer func() { + util.Check(listener.Close()) + }() + + fmt.Println("Listening") + + // Simulate a chat session + hub := util.NewHub() + + go func() { + for { + // Wait for a connection. + conn, err := listener.Accept() + util.Check(err) + // defer conn.Close() // TODO: graceful shutdown + + // `conn` is of type `net.Conn` but may be casted to `dtls.Conn` + // using `dtlsConn := conn.(*dtls.Conn)` in order to to expose + // functions like `ConnectionState` etc. + + // Register the connection with the chat hub + if err == nil { + hub.Register(conn) + } + } + }() + + // Start chatting + hub.Chat() +} diff --git a/dtls-2.0.9/examples/listen/verify/main.go b/dtls-2.0.9/examples/listen/verify/main.go new file mode 100644 index 0000000..96f5c0d --- /dev/null +++ b/dtls-2.0.9/examples/listen/verify/main.go @@ -0,0 +1,80 @@ +package main + +import ( + "context" + "crypto/tls" + "crypto/x509" + "fmt" + "net" + "time" + + "github.com/pion/dtls/v2" + "github.com/pion/dtls/v2/examples/util" +) + +func main() { + // Prepare the IP to connect to + addr := &net.UDPAddr{IP: net.ParseIP("127.0.0.1"), Port: 4444} + + // Create parent context to cleanup handshaking connections on exit. + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + // + // Everything below is the pion-DTLS API! Thanks for using it ❤️. + // + + certificate, err := util.LoadKeyAndCertificate("examples/certificates/server.pem", + "examples/certificates/server.pub.pem") + util.Check(err) + + rootCertificate, err := util.LoadCertificate("examples/certificates/server.pub.pem") + util.Check(err) + certPool := x509.NewCertPool() + cert, err := x509.ParseCertificate(rootCertificate.Certificate[0]) + util.Check(err) + certPool.AddCert(cert) + + // Prepare the configuration of the DTLS connection + config := &dtls.Config{ + Certificates: []tls.Certificate{*certificate}, + ExtendedMasterSecret: dtls.RequireExtendedMasterSecret, + ClientAuth: dtls.RequireAndVerifyClientCert, + ClientCAs: certPool, + // Create timeout context for accepted connection. + ConnectContextMaker: func() (context.Context, func()) { + return context.WithTimeout(ctx, 30*time.Second) + }, + } + + // Connect to a DTLS server + listener, err := dtls.Listen("udp", addr, config) + util.Check(err) + defer func() { + util.Check(listener.Close()) + }() + + fmt.Println("Listening") + + // Simulate a chat session + hub := util.NewHub() + + go func() { + for { + // Wait for a connection. + conn, err := listener.Accept() + util.Check(err) + // defer conn.Close() // TODO: graceful shutdown + + // `conn` is of type `net.Conn` but may be casted to `dtls.Conn` + // using `dtlsConn := conn.(*dtls.Conn)` in order to to expose + // functions like `ConnectionState` etc. + + // Register the connection with the chat hub + hub.Register(conn) + } + }() + + // Start chatting + hub.Chat() +} diff --git a/dtls-2.0.9/examples/util/hub.go b/dtls-2.0.9/examples/util/hub.go new file mode 100644 index 0000000..ad8e597 --- /dev/null +++ b/dtls-2.0.9/examples/util/hub.go @@ -0,0 +1,80 @@ +package util + +import ( + "bufio" + "fmt" + "net" + "os" + "strings" + "sync" +) + +// Hub is a helper to handle one to many chat +type Hub struct { + conns map[string]net.Conn + lock sync.RWMutex +} + +// NewHub builds a new hub +func NewHub() *Hub { + return &Hub{conns: make(map[string]net.Conn)} +} + +// Register adds a new conn to the Hub +func (h *Hub) Register(conn net.Conn) { + fmt.Printf("Connected to %s\n", conn.RemoteAddr()) + h.lock.Lock() + defer h.lock.Unlock() + + h.conns[conn.RemoteAddr().String()] = conn + + go h.readLoop(conn) +} + +func (h *Hub) readLoop(conn net.Conn) { + b := make([]byte, bufSize) + for { + n, err := conn.Read(b) + if err != nil { + h.unregister(conn) + return + } + fmt.Printf("Got message: %s\n", string(b[:n])) + } +} + +func (h *Hub) unregister(conn net.Conn) { + h.lock.Lock() + defer h.lock.Unlock() + delete(h.conns, conn.RemoteAddr().String()) + err := conn.Close() + if err != nil { + fmt.Println("Failed to disconnect", conn.RemoteAddr(), err) + } else { + fmt.Println("Disconnected ", conn.RemoteAddr()) + } +} + +func (h *Hub) broadcast(msg []byte) { + h.lock.RLock() + defer h.lock.RUnlock() + for _, conn := range h.conns { + _, err := conn.Write(msg) + if err != nil { + fmt.Printf("Failed to write message to %s: %v\n", conn.RemoteAddr(), err) + } + } +} + +// Chat starts the stdin readloop to dispatch messages to the hub +func (h *Hub) Chat() { + reader := bufio.NewReader(os.Stdin) + for { + msg, err := reader.ReadString('\n') + Check(err) + if strings.TrimSpace(msg) == "exit" { + return + } + h.broadcast([]byte(msg)) + } +} diff --git a/dtls-2.0.9/examples/util/util.go b/dtls-2.0.9/examples/util/util.go new file mode 100644 index 0000000..8f53539 --- /dev/null +++ b/dtls-2.0.9/examples/util/util.go @@ -0,0 +1,154 @@ +// Package util provides auxiliary utilities used in examples +package util + +import ( + "bufio" + "crypto" + "crypto/ecdsa" + "crypto/rsa" + "crypto/tls" + "crypto/x509" + "encoding/pem" + "errors" + "fmt" + "io" + "io/ioutil" + "net" + "os" + "path/filepath" + "strings" +) + +const bufSize = 8192 + +var ( + errBlockIsNotPrivateKey = errors.New("block is not a private key, unable to load key") + errUnknownKeyTime = errors.New("unknown key time in PKCS#8 wrapping, unable to load key") + errNoPrivateKeyFound = errors.New("no private key found, unable to load key") + errBlockIsNotCertificate = errors.New("block is not a certificate, unable to load certificates") + errNoCertificateFound = errors.New("no certificate found, unable to load certificates") +) + +// Chat simulates a simple text chat session over the connection +func Chat(conn io.ReadWriter) { + go func() { + b := make([]byte, bufSize) + + for { + n, err := conn.Read(b) + Check(err) + fmt.Printf("Got message: %s\n", string(b[:n])) + } + }() + + reader := bufio.NewReader(os.Stdin) + + for { + text, err := reader.ReadString('\n') + Check(err) + + if strings.TrimSpace(text) == "exit" { + return + } + + _, err = conn.Write([]byte(text)) + Check(err) + } +} + +// Check is a helper to throw errors in the examples +func Check(err error) { + switch e := err.(type) { + case nil: + case (net.Error): + if e.Temporary() { + fmt.Printf("Warning: %v\n", err) + return + } + + fmt.Printf("net.Error: %v\n", err) + panic(err) + default: + fmt.Printf("error: %v\n", err) + panic(err) + } +} + +// LoadKeyAndCertificate reads certificates or key from file +func LoadKeyAndCertificate(keyPath string, certificatePath string) (*tls.Certificate, error) { + privateKey, err := LoadKey(keyPath) + if err != nil { + return nil, err + } + + certificate, err := LoadCertificate(certificatePath) + if err != nil { + return nil, err + } + + certificate.PrivateKey = privateKey + + return certificate, nil +} + +// LoadKey Load/read key from file +func LoadKey(path string) (crypto.PrivateKey, error) { + rawData, err := ioutil.ReadFile(filepath.Clean(path)) + if err != nil { + return nil, err + } + + block, _ := pem.Decode(rawData) + if block == nil || !strings.HasSuffix(block.Type, "PRIVATE KEY") { + return nil, errBlockIsNotPrivateKey + } + + if key, err := x509.ParsePKCS1PrivateKey(block.Bytes); err == nil { + return key, nil + } + + if key, err := x509.ParsePKCS8PrivateKey(block.Bytes); err == nil { + switch key := key.(type) { + case *rsa.PrivateKey, *ecdsa.PrivateKey: + return key, nil + default: + return nil, errUnknownKeyTime + } + } + + if key, err := x509.ParseECPrivateKey(block.Bytes); err == nil { + return key, nil + } + + return nil, errNoPrivateKeyFound +} + +// LoadCertificate Load/read certificate(s) from file +func LoadCertificate(path string) (*tls.Certificate, error) { + rawData, err := ioutil.ReadFile(filepath.Clean(path)) + if err != nil { + return nil, err + } + + var certificate tls.Certificate + + for { + block, rest := pem.Decode(rawData) + if block == nil { + break + } + + if block.Type != "CERTIFICATE" { + return nil, errBlockIsNotCertificate + } + + certificate.Certificate = append(certificate.Certificate, block.Bytes) + rawData = rest + } + + if len(certificate.Certificate) == 0 { + return nil, errNoCertificateFound + } + + return &certificate, nil +} diff --git a/dtls-2.0.9/flight.go b/dtls-2.0.9/flight.go new file mode 100644 index 0000000..580ee48 --- /dev/null +++ b/dtls-2.0.9/flight.go @@ -0,0 +1,75 @@ +package dtls + +/* + DTLS messages are grouped into a series of message flights, according + to the diagrams below. Although each flight of messages may consist + of a number of messages, they should be viewed as monolithic for the + purpose of timeout and retransmission. + https://tools.ietf.org/html/rfc4347#section-4.2.4 + Client Server + ------ ------ + Waiting Flight 0 + + ClientHello --------> Flight 1 + + <------- HelloVerifyRequest Flight 2 + + ClientHello --------> Flight 3 + + ServerHello \ + Certificate* \ + ServerKeyExchange* Flight 4 + CertificateRequest* / + <-------- ServerHelloDone / + + Certificate* \ + ClientKeyExchange \ + CertificateVerify* Flight 5 + [ChangeCipherSpec] / + Finished --------> / + + [ChangeCipherSpec] \ Flight 6 + <-------- Finished / + +*/ + +type flightVal uint8 + +const ( + flight0 flightVal = iota + 1 + flight1 + flight2 + flight3 + flight4 + flight5 + flight6 +) + +func (f flightVal) String() string { + switch f { + case flight0: + return "Flight 0" + case flight1: + return "Flight 1" + case flight2: + return "Flight 2" + case flight3: + return "Flight 3" + case flight4: + return "Flight 4" + case flight5: + return "Flight 5" + case flight6: + return "Flight 6" + default: + return "Invalid Flight" + } +} + +func (f flightVal) isLastSendFlight() bool { + return f == flight6 +} + +func (f flightVal) isLastRecvFlight() bool { + return f == flight5 +} diff --git a/dtls-2.0.9/flight0handler.go b/dtls-2.0.9/flight0handler.go new file mode 100644 index 0000000..949d7c0 --- /dev/null +++ b/dtls-2.0.9/flight0handler.go @@ -0,0 +1,102 @@ +package dtls + +import ( + "context" + "crypto/rand" + + "github.com/pion/dtls/v2/pkg/crypto/elliptic" + "github.com/pion/dtls/v2/pkg/protocol" + "github.com/pion/dtls/v2/pkg/protocol/alert" + "github.com/pion/dtls/v2/pkg/protocol/extension" + "github.com/pion/dtls/v2/pkg/protocol/handshake" +) + +func flight0Parse(ctx context.Context, c flightConn, state *State, cache *handshakeCache, cfg *handshakeConfig) (flightVal, *alert.Alert, error) { + seq, msgs, ok := cache.fullPullMap(0, + handshakeCachePullRule{handshake.TypeClientHello, cfg.initialEpoch, true, false}, + ) + if !ok { + // No valid message received. Keep reading + return 0, nil, nil + } + state.handshakeRecvSequence = seq + + var clientHello *handshake.MessageClientHello + + // Validate type + if clientHello, ok = msgs[handshake.TypeClientHello].(*handshake.MessageClientHello); !ok { + return 0, &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}, nil + } + + if !clientHello.Version.Equal(protocol.Version1_2) { + return 0, &alert.Alert{Level: alert.Fatal, Description: alert.ProtocolVersion}, errUnsupportedProtocolVersion + } + + state.remoteRandom = clientHello.Random + + cipherSuites := []CipherSuite{} + for _, id := range clientHello.CipherSuiteIDs { + if c := cipherSuiteForID(CipherSuiteID(id), cfg.customCipherSuites); c != nil { + cipherSuites = append(cipherSuites, c) + } + } + + if state.cipherSuite, ok = findMatchingCipherSuite(cipherSuites, cfg.localCipherSuites); !ok { + return 0, &alert.Alert{Level: alert.Fatal, Description: alert.InsufficientSecurity}, errCipherSuiteNoIntersection + } + + for _, val := range clientHello.Extensions { + switch e := val.(type) { + case *extension.SupportedEllipticCurves: + if len(e.EllipticCurves) == 0 { + return 0, &alert.Alert{Level: alert.Fatal, Description: alert.InsufficientSecurity}, errNoSupportedEllipticCurves + } + state.namedCurve = e.EllipticCurves[0] + case *extension.UseSRTP: + profile, ok := findMatchingSRTPProfile(e.ProtectionProfiles, cfg.localSRTPProtectionProfiles) + if !ok { + return 0, &alert.Alert{Level: alert.Fatal, Description: alert.InsufficientSecurity}, errServerNoMatchingSRTPProfile + } + state.srtpProtectionProfile = profile + case *extension.UseExtendedMasterSecret: + if cfg.extendedMasterSecret != DisableExtendedMasterSecret { + state.extendedMasterSecret = true + } + case *extension.ServerName: + state.serverName = e.ServerName // remote server name + } + } + + if cfg.extendedMasterSecret == RequireExtendedMasterSecret && !state.extendedMasterSecret { + return 0, &alert.Alert{Level: alert.Fatal, Description: alert.InsufficientSecurity}, errServerRequiredButNoClientEMS + } + + if state.localKeypair == nil { + var err error + state.localKeypair, err = elliptic.GenerateKeypair(state.namedCurve) + if err != nil { + return 0, &alert.Alert{Level: alert.Fatal, Description: alert.IllegalParameter}, err + } + } + + return flight2, nil, nil +} + +func flight0Generate(c flightConn, state *State, cache *handshakeCache, cfg *handshakeConfig) ([]*packet, *alert.Alert, error) { + // Initialize + state.cookie = make([]byte, cookieLength) + if _, err := rand.Read(state.cookie); err != nil { + return nil, nil, err + } + + var zeroEpoch uint16 + state.localEpoch.Store(zeroEpoch) + state.remoteEpoch.Store(zeroEpoch) + state.namedCurve = defaultNamedCurve + + if err := state.localRandom.Populate(); err != nil { + return nil, nil, err + } + + return nil, nil, nil +} diff --git a/dtls-2.0.9/flight1handler.go b/dtls-2.0.9/flight1handler.go new file mode 100644 index 0000000..9229292 --- /dev/null +++ b/dtls-2.0.9/flight1handler.go @@ -0,0 +1,112 @@ +package dtls + +import ( + "context" + + "github.com/pion/dtls/v2/pkg/crypto/elliptic" + "github.com/pion/dtls/v2/pkg/protocol" + "github.com/pion/dtls/v2/pkg/protocol/alert" + "github.com/pion/dtls/v2/pkg/protocol/extension" + "github.com/pion/dtls/v2/pkg/protocol/handshake" + "github.com/pion/dtls/v2/pkg/protocol/recordlayer" +) + +func flight1Parse(ctx context.Context, c flightConn, state *State, cache *handshakeCache, cfg *handshakeConfig) (flightVal, *alert.Alert, error) { + // HelloVerifyRequest can be skipped by the server, + // so allow ServerHello during flight1 also + seq, msgs, ok := cache.fullPullMap(state.handshakeRecvSequence, + handshakeCachePullRule{handshake.TypeHelloVerifyRequest, cfg.initialEpoch, false, true}, + handshakeCachePullRule{handshake.TypeServerHello, cfg.initialEpoch, false, true}, + ) + if !ok { + // No valid message received. Keep reading + return 0, nil, nil + } + + if _, ok := msgs[handshake.TypeServerHello]; ok { + // Flight1 and flight2 were skipped. + // Parse as flight3. + return flight3Parse(ctx, c, state, cache, cfg) + } + + if h, ok := msgs[handshake.TypeHelloVerifyRequest].(*handshake.MessageHelloVerifyRequest); ok { + // DTLS 1.2 clients must not assume that the server will use the protocol version + // specified in HelloVerifyRequest message. RFC 6347 Section 4.2.1 + if !h.Version.Equal(protocol.Version1_0) && !h.Version.Equal(protocol.Version1_2) { + return 0, &alert.Alert{Level: alert.Fatal, Description: alert.ProtocolVersion}, errUnsupportedProtocolVersion + } + state.cookie = append([]byte{}, h.Cookie...) + state.handshakeRecvSequence = seq + return flight3, nil, nil + } + + return 0, &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}, nil +} + +func flight1Generate(c flightConn, state *State, cache *handshakeCache, cfg *handshakeConfig) ([]*packet, *alert.Alert, error) { + var zeroEpoch uint16 + state.localEpoch.Store(zeroEpoch) + state.remoteEpoch.Store(zeroEpoch) + state.namedCurve = defaultNamedCurve + state.cookie = nil + + if err := state.localRandom.Populate(); err != nil { + return nil, nil, err + } + + extensions := []extension.Extension{ + &extension.SupportedSignatureAlgorithms{ + SignatureHashAlgorithms: cfg.localSignatureSchemes, + }, + &extension.RenegotiationInfo{ + RenegotiatedConnection: 0, + }, + } + if cfg.localPSKCallback == nil { + extensions = append(extensions, []extension.Extension{ + &extension.SupportedEllipticCurves{ + EllipticCurves: []elliptic.Curve{elliptic.X25519, elliptic.P256, elliptic.P384}, + }, + &extension.SupportedPointFormats{ + PointFormats: []elliptic.CurvePointFormat{elliptic.CurvePointFormatUncompressed}, + }, + }...) + } + + if len(cfg.localSRTPProtectionProfiles) > 0 { + extensions = append(extensions, &extension.UseSRTP{ + ProtectionProfiles: cfg.localSRTPProtectionProfiles, + }) + } + + if cfg.extendedMasterSecret == RequestExtendedMasterSecret || + cfg.extendedMasterSecret == RequireExtendedMasterSecret { + extensions = append(extensions, &extension.UseExtendedMasterSecret{ + Supported: true, + }) + } + + if len(cfg.serverName) > 0 { + extensions = append(extensions, &extension.ServerName{ServerName: cfg.serverName}) + } + + return []*packet{ + { + record: &recordlayer.RecordLayer{ + Header: recordlayer.Header{ + Version: protocol.Version1_2, + }, + Content: &handshake.Handshake{ + Message: &handshake.MessageClientHello{ + Version: protocol.Version1_2, + Cookie: state.cookie, + Random: state.localRandom, + CipherSuiteIDs: cipherSuiteIDs(cfg.localCipherSuites), + CompressionMethods: defaultCompressionMethods(), + Extensions: extensions, + }, + }, + }, + }, + }, nil, nil +} diff --git a/dtls-2.0.9/flight2handler.go b/dtls-2.0.9/flight2handler.go new file mode 100644 index 0000000..33e2ee7 --- /dev/null +++ b/dtls-2.0.9/flight2handler.go @@ -0,0 +1,78 @@ +package dtls + +import ( + "bytes" + "context" + "fmt" + + "github.com/pion/dtls/v2/pkg/protocol" + "github.com/pion/dtls/v2/pkg/protocol/alert" + "github.com/pion/dtls/v2/pkg/protocol/handshake" + "github.com/pion/dtls/v2/pkg/protocol/recordlayer" +) + +func flight2Parse(ctx context.Context, c flightConn, state *State, cache *handshakeCache, cfg *handshakeConfig) (flightVal, *alert.Alert, error) { + seq, msgs, ok := cache.fullPullMap(state.handshakeRecvSequence, + handshakeCachePullRule{handshake.TypeClientHello, cfg.initialEpoch, true, false}, + ) + if !ok { + // Client may retransmit the first ClientHello when HelloVerifyRequest is dropped. + // Parse as flight 0 in this case. + return flight0Parse(ctx, c, state, cache, cfg) + } + state.handshakeRecvSequence = seq + + var clientHello *handshake.MessageClientHello + + // Validate type + if clientHello, ok = msgs[handshake.TypeClientHello].(*handshake.MessageClientHello); !ok { + return 0, &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}, nil + } + + if !clientHello.Version.Equal(protocol.Version1_2) { + return 0, &alert.Alert{Level: alert.Fatal, Description: alert.ProtocolVersion}, errUnsupportedProtocolVersion + } + + if len(clientHello.Cookie) == 0 { + return 0, nil, nil + } + if !bytes.Equal(state.cookie, clientHello.Cookie) { + return 0, &alert.Alert{Level: alert.Fatal, Description: alert.AccessDenied}, errCookieMismatch + } + + // TODO 添加 CiscoCompat 支持 + if cfg.localCiscoCompatCallback != nil { + var err error + state.SessionID = clientHello.SessionID + if len(state.SessionID) == 0 { + err = fmt.Errorf("clientHello SessionID is nil") + return 0, &alert.Alert{Level: alert.Fatal, Description: alert.IllegalParameter}, err + } + + state.masterSecret, err = cfg.localCiscoCompatCallback(state.SessionID) + if err != nil { + return 0, &alert.Alert{Level: alert.Fatal, Description: alert.IllegalParameter}, err + } + } + + return flight4, nil, nil +} + +func flight2Generate(c flightConn, state *State, cache *handshakeCache, cfg *handshakeConfig) ([]*packet, *alert.Alert, error) { + state.handshakeSendSequence = 0 + return []*packet{ + { + record: &recordlayer.RecordLayer{ + Header: recordlayer.Header{ + Version: protocol.Version1_2, + }, + Content: &handshake.Handshake{ + Message: &handshake.MessageHelloVerifyRequest{ + Version: protocol.Version1_2, + Cookie: state.cookie, + }, + }, + }, + }, + }, nil, nil +} diff --git a/dtls-2.0.9/flight3handler.go b/dtls-2.0.9/flight3handler.go new file mode 100644 index 0000000..f953be8 --- /dev/null +++ b/dtls-2.0.9/flight3handler.go @@ -0,0 +1,194 @@ +package dtls + +import ( + "context" + + "github.com/pion/dtls/v2/pkg/crypto/elliptic" + "github.com/pion/dtls/v2/pkg/crypto/prf" + "github.com/pion/dtls/v2/pkg/protocol" + "github.com/pion/dtls/v2/pkg/protocol/alert" + "github.com/pion/dtls/v2/pkg/protocol/extension" + "github.com/pion/dtls/v2/pkg/protocol/handshake" + "github.com/pion/dtls/v2/pkg/protocol/recordlayer" +) + +func flight3Parse(ctx context.Context, c flightConn, state *State, cache *handshakeCache, cfg *handshakeConfig) (flightVal, *alert.Alert, error) { //nolint:gocognit + // Clients may receive multiple HelloVerifyRequest messages with different cookies. + // Clients SHOULD handle this by sending a new ClientHello with a cookie in response + // to the new HelloVerifyRequest. RFC 6347 Section 4.2.1 + seq, msgs, ok := cache.fullPullMap(state.handshakeRecvSequence, + handshakeCachePullRule{handshake.TypeHelloVerifyRequest, cfg.initialEpoch, false, true}, + ) + if ok { + if h, msgOk := msgs[handshake.TypeHelloVerifyRequest].(*handshake.MessageHelloVerifyRequest); msgOk { + // DTLS 1.2 clients must not assume that the server will use the protocol version + // specified in HelloVerifyRequest message. RFC 6347 Section 4.2.1 + if !h.Version.Equal(protocol.Version1_0) && !h.Version.Equal(protocol.Version1_2) { + return 0, &alert.Alert{Level: alert.Fatal, Description: alert.ProtocolVersion}, errUnsupportedProtocolVersion + } + state.cookie = append([]byte{}, h.Cookie...) + state.handshakeRecvSequence = seq + return flight3, nil, nil + } + } + + if cfg.localPSKCallback != nil { + seq, msgs, ok = cache.fullPullMap(state.handshakeRecvSequence, + handshakeCachePullRule{handshake.TypeServerHello, cfg.initialEpoch, false, false}, + handshakeCachePullRule{handshake.TypeServerKeyExchange, cfg.initialEpoch, false, true}, + handshakeCachePullRule{handshake.TypeServerHelloDone, cfg.initialEpoch, false, false}, + ) + } else { + seq, msgs, ok = cache.fullPullMap(state.handshakeRecvSequence, + handshakeCachePullRule{handshake.TypeServerHello, cfg.initialEpoch, false, false}, + handshakeCachePullRule{handshake.TypeCertificate, cfg.initialEpoch, false, true}, + handshakeCachePullRule{handshake.TypeServerKeyExchange, cfg.initialEpoch, false, false}, + handshakeCachePullRule{handshake.TypeCertificateRequest, cfg.initialEpoch, false, true}, + handshakeCachePullRule{handshake.TypeServerHelloDone, cfg.initialEpoch, false, false}, + ) + } + if !ok { + // Don't have enough messages. Keep reading + return 0, nil, nil + } + state.handshakeRecvSequence = seq + + if h, ok := msgs[handshake.TypeServerHello].(*handshake.MessageServerHello); ok { + if !h.Version.Equal(protocol.Version1_2) { + return 0, &alert.Alert{Level: alert.Fatal, Description: alert.ProtocolVersion}, errUnsupportedProtocolVersion + } + for _, v := range h.Extensions { + switch e := v.(type) { + case *extension.UseSRTP: + profile, ok := findMatchingSRTPProfile(e.ProtectionProfiles, cfg.localSRTPProtectionProfiles) + if !ok { + return 0, &alert.Alert{Level: alert.Fatal, Description: alert.IllegalParameter}, errClientNoMatchingSRTPProfile + } + state.srtpProtectionProfile = profile + case *extension.UseExtendedMasterSecret: + if cfg.extendedMasterSecret != DisableExtendedMasterSecret { + state.extendedMasterSecret = true + } + } + } + if cfg.extendedMasterSecret == RequireExtendedMasterSecret && !state.extendedMasterSecret { + return 0, &alert.Alert{Level: alert.Fatal, Description: alert.InsufficientSecurity}, errClientRequiredButNoServerEMS + } + if len(cfg.localSRTPProtectionProfiles) > 0 && state.srtpProtectionProfile == 0 { + return 0, &alert.Alert{Level: alert.Fatal, Description: alert.InsufficientSecurity}, errRequestedButNoSRTPExtension + } + + remoteCipherSuite := cipherSuiteForID(CipherSuiteID(*h.CipherSuiteID), cfg.customCipherSuites) + if remoteCipherSuite == nil { + return 0, &alert.Alert{Level: alert.Fatal, Description: alert.InsufficientSecurity}, errCipherSuiteNoIntersection + } + + selectedCipherSuite, ok := findMatchingCipherSuite([]CipherSuite{remoteCipherSuite}, cfg.localCipherSuites) + if !ok { + return 0, &alert.Alert{Level: alert.Fatal, Description: alert.InsufficientSecurity}, errInvalidCipherSuite + } + + state.cipherSuite = selectedCipherSuite + state.remoteRandom = h.Random + cfg.log.Tracef("[handshake] use cipher suite: %s", selectedCipherSuite.String()) + } + + if h, ok := msgs[handshake.TypeCertificate].(*handshake.MessageCertificate); ok { + state.PeerCertificates = h.Certificate + } else if state.cipherSuite.AuthenticationType() == CipherSuiteAuthenticationTypeCertificate { + return 0, &alert.Alert{Level: alert.Fatal, Description: alert.NoCertificate}, errInvalidCertificate + } + + if h, ok := msgs[handshake.TypeServerKeyExchange].(*handshake.MessageServerKeyExchange); ok { + alertPtr, err := handleServerKeyExchange(c, state, cfg, h) + if err != nil { + return 0, alertPtr, err + } + } + + if _, ok := msgs[handshake.TypeCertificateRequest].(*handshake.MessageCertificateRequest); ok { + state.remoteRequestedCertificate = true + } + + return flight5, nil, nil +} + +func handleServerKeyExchange(_ flightConn, state *State, cfg *handshakeConfig, h *handshake.MessageServerKeyExchange) (*alert.Alert, error) { + var err error + if cfg.localPSKCallback != nil { + var psk []byte + if psk, err = cfg.localPSKCallback(h.IdentityHint); err != nil { + return &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}, err + } + state.IdentityHint = h.IdentityHint + state.preMasterSecret = prf.PSKPreMasterSecret(psk) + } else { + if state.localKeypair, err = elliptic.GenerateKeypair(h.NamedCurve); err != nil { + return &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}, err + } + + if state.preMasterSecret, err = prf.PreMasterSecret(h.PublicKey, state.localKeypair.PrivateKey, state.localKeypair.Curve); err != nil { + return &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}, err + } + } + + return nil, nil +} + +func flight3Generate(c flightConn, state *State, cache *handshakeCache, cfg *handshakeConfig) ([]*packet, *alert.Alert, error) { + extensions := []extension.Extension{ + &extension.SupportedSignatureAlgorithms{ + SignatureHashAlgorithms: cfg.localSignatureSchemes, + }, + &extension.RenegotiationInfo{ + RenegotiatedConnection: 0, + }, + } + if cfg.localPSKCallback == nil { + extensions = append(extensions, []extension.Extension{ + &extension.SupportedEllipticCurves{ + EllipticCurves: []elliptic.Curve{elliptic.X25519, elliptic.P256, elliptic.P384}, + }, + &extension.SupportedPointFormats{ + PointFormats: []elliptic.CurvePointFormat{elliptic.CurvePointFormatUncompressed}, + }, + }...) + } + + if len(cfg.localSRTPProtectionProfiles) > 0 { + extensions = append(extensions, &extension.UseSRTP{ + ProtectionProfiles: cfg.localSRTPProtectionProfiles, + }) + } + + if cfg.extendedMasterSecret == RequestExtendedMasterSecret || + cfg.extendedMasterSecret == RequireExtendedMasterSecret { + extensions = append(extensions, &extension.UseExtendedMasterSecret{ + Supported: true, + }) + } + + if len(cfg.serverName) > 0 { + extensions = append(extensions, &extension.ServerName{ServerName: cfg.serverName}) + } + + return []*packet{ + { + record: &recordlayer.RecordLayer{ + Header: recordlayer.Header{ + Version: protocol.Version1_2, + }, + Content: &handshake.Handshake{ + Message: &handshake.MessageClientHello{ + Version: protocol.Version1_2, + Cookie: state.cookie, + Random: state.localRandom, + CipherSuiteIDs: cipherSuiteIDs(cfg.localCipherSuites), + CompressionMethods: defaultCompressionMethods(), + Extensions: extensions, + }, + }, + }, + }, + }, nil, nil +} diff --git a/dtls-2.0.9/flight4handler.go b/dtls-2.0.9/flight4handler.go new file mode 100644 index 0000000..1464854 --- /dev/null +++ b/dtls-2.0.9/flight4handler.go @@ -0,0 +1,352 @@ +package dtls + +import ( + "context" + "crypto/x509" + + "github.com/pion/dtls/v2/pkg/crypto/clientcertificate" + "github.com/pion/dtls/v2/pkg/crypto/elliptic" + "github.com/pion/dtls/v2/pkg/crypto/prf" + "github.com/pion/dtls/v2/pkg/crypto/signaturehash" + "github.com/pion/dtls/v2/pkg/protocol" + "github.com/pion/dtls/v2/pkg/protocol/alert" + "github.com/pion/dtls/v2/pkg/protocol/extension" + "github.com/pion/dtls/v2/pkg/protocol/handshake" + "github.com/pion/dtls/v2/pkg/protocol/recordlayer" +) + +func flight4Parse(ctx context.Context, c flightConn, state *State, cache *handshakeCache, cfg *handshakeConfig) (flightVal, *alert.Alert, error) { //nolint:gocognit + seq, msgs, ok := cache.fullPullMap(state.handshakeRecvSequence, + handshakeCachePullRule{handshake.TypeCertificate, cfg.initialEpoch, true, true}, + handshakeCachePullRule{handshake.TypeClientKeyExchange, cfg.initialEpoch, true, false}, + handshakeCachePullRule{handshake.TypeCertificateVerify, cfg.initialEpoch, true, true}, + ) + if !ok { + // No valid message received. Keep reading + return 0, nil, nil + } + + // Validate type + var clientKeyExchange *handshake.MessageClientKeyExchange + if clientKeyExchange, ok = msgs[handshake.TypeClientKeyExchange].(*handshake.MessageClientKeyExchange); !ok { + return 0, &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}, nil + } + + if h, hasCert := msgs[handshake.TypeCertificate].(*handshake.MessageCertificate); hasCert { + state.PeerCertificates = h.Certificate + } + + if h, hasCertVerify := msgs[handshake.TypeCertificateVerify].(*handshake.MessageCertificateVerify); hasCertVerify { + if state.PeerCertificates == nil { + return 0, &alert.Alert{Level: alert.Fatal, Description: alert.NoCertificate}, errCertificateVerifyNoCertificate + } + + plainText := cache.pullAndMerge( + handshakeCachePullRule{handshake.TypeClientHello, cfg.initialEpoch, true, false}, + handshakeCachePullRule{handshake.TypeServerHello, cfg.initialEpoch, false, false}, + handshakeCachePullRule{handshake.TypeCertificate, cfg.initialEpoch, false, false}, + handshakeCachePullRule{handshake.TypeServerKeyExchange, cfg.initialEpoch, false, false}, + handshakeCachePullRule{handshake.TypeCertificateRequest, cfg.initialEpoch, false, false}, + handshakeCachePullRule{handshake.TypeServerHelloDone, cfg.initialEpoch, false, false}, + handshakeCachePullRule{handshake.TypeCertificate, cfg.initialEpoch, true, false}, + handshakeCachePullRule{handshake.TypeClientKeyExchange, cfg.initialEpoch, true, false}, + ) + + // Verify that the pair of hash algorithm and signiture is listed. + var validSignatureScheme bool + for _, ss := range cfg.localSignatureSchemes { + if ss.Hash == h.HashAlgorithm && ss.Signature == h.SignatureAlgorithm { + validSignatureScheme = true + break + } + } + if !validSignatureScheme { + return 0, &alert.Alert{Level: alert.Fatal, Description: alert.InsufficientSecurity}, errNoAvailableSignatureSchemes + } + + if err := verifyCertificateVerify(plainText, h.HashAlgorithm, h.Signature, state.PeerCertificates); err != nil { + return 0, &alert.Alert{Level: alert.Fatal, Description: alert.BadCertificate}, err + } + var chains [][]*x509.Certificate + var err error + var verified bool + if cfg.clientAuth >= VerifyClientCertIfGiven { + if chains, err = verifyClientCert(state.PeerCertificates, cfg.clientCAs); err != nil { + return 0, &alert.Alert{Level: alert.Fatal, Description: alert.BadCertificate}, err + } + verified = true + } + if cfg.verifyPeerCertificate != nil { + if err := cfg.verifyPeerCertificate(state.PeerCertificates, chains); err != nil { + return 0, &alert.Alert{Level: alert.Fatal, Description: alert.BadCertificate}, err + } + } + state.peerCertificatesVerified = verified + } + + if !state.cipherSuite.IsInitialized() { + serverRandom := state.localRandom.MarshalFixed() + clientRandom := state.remoteRandom.MarshalFixed() + + var err error + var preMasterSecret []byte + if state.cipherSuite.AuthenticationType() == CipherSuiteAuthenticationTypePreSharedKey { + var psk []byte + if psk, err = cfg.localPSKCallback(clientKeyExchange.IdentityHint); err != nil { + return 0, &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}, err + } + state.IdentityHint = clientKeyExchange.IdentityHint + preMasterSecret = prf.PSKPreMasterSecret(psk) + } else { + preMasterSecret, err = prf.PreMasterSecret(clientKeyExchange.PublicKey, state.localKeypair.PrivateKey, state.localKeypair.Curve) + if err != nil { + return 0, &alert.Alert{Level: alert.Fatal, Description: alert.IllegalParameter}, err + } + } + + if state.extendedMasterSecret { + var sessionHash []byte + sessionHash, err = cache.sessionHash(state.cipherSuite.HashFunc(), cfg.initialEpoch) + if err != nil { + return 0, &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}, err + } + + state.masterSecret, err = prf.ExtendedMasterSecret(preMasterSecret, sessionHash, state.cipherSuite.HashFunc()) + if err != nil { + return 0, &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}, err + } + } else { + state.masterSecret, err = prf.MasterSecret(preMasterSecret, clientRandom[:], serverRandom[:], state.cipherSuite.HashFunc()) + if err != nil { + return 0, &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}, err + } + } + + if err := state.cipherSuite.Init(state.masterSecret, clientRandom[:], serverRandom[:], false); err != nil { + return 0, &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}, err + } + cfg.writeKeyLog(keyLogLabelTLS12, clientRandom[:], state.masterSecret) + } + + // Now, encrypted packets can be handled + if err := c.handleQueuedPackets(ctx); err != nil { + return 0, &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}, err + } + + seq, msgs, ok = cache.fullPullMap(seq, + handshakeCachePullRule{handshake.TypeFinished, cfg.initialEpoch + 1, true, false}, + ) + if !ok { + // No valid message received. Keep reading + return 0, nil, nil + } + state.handshakeRecvSequence = seq + + if _, ok = msgs[handshake.TypeFinished].(*handshake.MessageFinished); !ok { + return 0, &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}, nil + } + + if state.cipherSuite.AuthenticationType() == CipherSuiteAuthenticationTypeAnonymous { + return flight6, nil, nil + } + + switch cfg.clientAuth { + case RequireAnyClientCert: + if state.PeerCertificates == nil { + return 0, &alert.Alert{Level: alert.Fatal, Description: alert.NoCertificate}, errClientCertificateRequired + } + case VerifyClientCertIfGiven: + if state.PeerCertificates != nil && !state.peerCertificatesVerified { + return 0, &alert.Alert{Level: alert.Fatal, Description: alert.BadCertificate}, errClientCertificateNotVerified + } + case RequireAndVerifyClientCert: + if state.PeerCertificates == nil { + return 0, &alert.Alert{Level: alert.Fatal, Description: alert.NoCertificate}, errClientCertificateRequired + } + if !state.peerCertificatesVerified { + return 0, &alert.Alert{Level: alert.Fatal, Description: alert.BadCertificate}, errClientCertificateNotVerified + } + case NoClientCert, RequestClientCert: + return flight6, nil, nil + } + + return flight6, nil, nil +} + +func flight4Generate(c flightConn, state *State, cache *handshakeCache, cfg *handshakeConfig) ([]*packet, *alert.Alert, error) { + extensions := []extension.Extension{&extension.RenegotiationInfo{ + RenegotiatedConnection: 0, + }} + if (cfg.extendedMasterSecret == RequestExtendedMasterSecret || + cfg.extendedMasterSecret == RequireExtendedMasterSecret) && state.extendedMasterSecret { + extensions = append(extensions, &extension.UseExtendedMasterSecret{ + Supported: true, + }) + } + if state.srtpProtectionProfile != 0 { + extensions = append(extensions, &extension.UseSRTP{ + ProtectionProfiles: []SRTPProtectionProfile{state.srtpProtectionProfile}, + }) + } + if state.cipherSuite.AuthenticationType() == CipherSuiteAuthenticationTypeCertificate { + extensions = append(extensions, []extension.Extension{ + &extension.SupportedEllipticCurves{ + EllipticCurves: []elliptic.Curve{elliptic.X25519, elliptic.P256, elliptic.P384}, + }, + &extension.SupportedPointFormats{ + PointFormats: []elliptic.CurvePointFormat{elliptic.CurvePointFormatUncompressed}, + }, + }...) + } + + var pkts []*packet + cipherSuiteID := uint16(state.cipherSuite.ID()) + + pkts = append(pkts, &packet{ + record: &recordlayer.RecordLayer{ + Header: recordlayer.Header{ + Version: protocol.Version1_2, + }, + Content: &handshake.Handshake{ + Message: &handshake.MessageServerHello{ + Version: protocol.Version1_2, + Random: state.localRandom, + SessionID: state.SessionID, + CipherSuiteID: &cipherSuiteID, + CompressionMethod: defaultCompressionMethods()[0], + Extensions: extensions, + }, + }, + }, + }) + + // TODO 添加 CiscoCompat 支持 + if cfg.localCiscoCompatCallback != nil { + if !state.cipherSuite.IsInitialized() { + serverRandom := state.localRandom.MarshalFixed() + clientRandom := state.remoteRandom.MarshalFixed() + + if err := state.cipherSuite.Init(state.masterSecret, clientRandom[:], serverRandom[:], false); err != nil { + return nil, &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}, err + } + cfg.writeKeyLog(keyLogLabelTLS12, clientRandom[:], state.masterSecret) + } + return pkts, nil, nil + } + + switch { + case state.cipherSuite.AuthenticationType() == CipherSuiteAuthenticationTypeCertificate: + certificate, err := cfg.getCertificate(cfg.serverName) + if err != nil { + return nil, &alert.Alert{Level: alert.Fatal, Description: alert.HandshakeFailure}, err + } + + pkts = append(pkts, &packet{ + record: &recordlayer.RecordLayer{ + Header: recordlayer.Header{ + Version: protocol.Version1_2, + }, + Content: &handshake.Handshake{ + Message: &handshake.MessageCertificate{ + Certificate: certificate.Certificate, + }, + }, + }, + }) + + serverRandom := state.localRandom.MarshalFixed() + clientRandom := state.remoteRandom.MarshalFixed() + + // Find compatible signature scheme + signatureHashAlgo, err := signaturehash.SelectSignatureScheme(cfg.localSignatureSchemes, certificate.PrivateKey) + if err != nil { + return nil, &alert.Alert{Level: alert.Fatal, Description: alert.InsufficientSecurity}, err + } + + signature, err := generateKeySignature(clientRandom[:], serverRandom[:], state.localKeypair.PublicKey, state.namedCurve, certificate.PrivateKey, signatureHashAlgo.Hash) + if err != nil { + return nil, &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}, err + } + state.localKeySignature = signature + + pkts = append(pkts, &packet{ + record: &recordlayer.RecordLayer{ + Header: recordlayer.Header{ + Version: protocol.Version1_2, + }, + Content: &handshake.Handshake{ + Message: &handshake.MessageServerKeyExchange{ + EllipticCurveType: elliptic.CurveTypeNamedCurve, + NamedCurve: state.namedCurve, + PublicKey: state.localKeypair.PublicKey, + HashAlgorithm: signatureHashAlgo.Hash, + SignatureAlgorithm: signatureHashAlgo.Signature, + Signature: state.localKeySignature, + }, + }, + }, + }) + + if cfg.clientAuth > NoClientCert { + pkts = append(pkts, &packet{ + record: &recordlayer.RecordLayer{ + Header: recordlayer.Header{ + Version: protocol.Version1_2, + }, + Content: &handshake.Handshake{ + Message: &handshake.MessageCertificateRequest{ + CertificateTypes: []clientcertificate.Type{clientcertificate.RSASign, clientcertificate.ECDSASign}, + SignatureHashAlgorithms: cfg.localSignatureSchemes, + }, + }, + }, + }) + } + case cfg.localPSKIdentityHint != nil: + // To help the client in selecting which identity to use, the server + // can provide a "PSK identity hint" in the ServerKeyExchange message. + // If no hint is provided, the ServerKeyExchange message is omitted. + // + // https://tools.ietf.org/html/rfc4279#section-2 + pkts = append(pkts, &packet{ + record: &recordlayer.RecordLayer{ + Header: recordlayer.Header{ + Version: protocol.Version1_2, + }, + Content: &handshake.Handshake{ + Message: &handshake.MessageServerKeyExchange{ + IdentityHint: cfg.localPSKIdentityHint, + }, + }, + }, + }) + case state.cipherSuite.AuthenticationType() == CipherSuiteAuthenticationTypeAnonymous: + pkts = append(pkts, &packet{ + record: &recordlayer.RecordLayer{ + Header: recordlayer.Header{ + Version: protocol.Version1_2, + }, + Content: &handshake.Handshake{ + Message: &handshake.MessageServerKeyExchange{ + EllipticCurveType: elliptic.CurveTypeNamedCurve, + NamedCurve: state.namedCurve, + PublicKey: state.localKeypair.PublicKey, + }, + }, + }, + }) + } + + pkts = append(pkts, &packet{ + record: &recordlayer.RecordLayer{ + Header: recordlayer.Header{ + Version: protocol.Version1_2, + }, + Content: &handshake.Handshake{ + Message: &handshake.MessageServerHelloDone{}, + }, + }, + }) + + return pkts, nil, nil +} diff --git a/dtls-2.0.9/flight5handler.go b/dtls-2.0.9/flight5handler.go new file mode 100644 index 0000000..baa1d5c --- /dev/null +++ b/dtls-2.0.9/flight5handler.go @@ -0,0 +1,323 @@ +package dtls + +import ( + "bytes" + "context" + "crypto" + "crypto/x509" + + "github.com/pion/dtls/v2/pkg/crypto/prf" + "github.com/pion/dtls/v2/pkg/crypto/signaturehash" + "github.com/pion/dtls/v2/pkg/protocol" + "github.com/pion/dtls/v2/pkg/protocol/alert" + "github.com/pion/dtls/v2/pkg/protocol/handshake" + "github.com/pion/dtls/v2/pkg/protocol/recordlayer" +) + +func flight5Parse(ctx context.Context, c flightConn, state *State, cache *handshakeCache, cfg *handshakeConfig) (flightVal, *alert.Alert, error) { + _, msgs, ok := cache.fullPullMap(state.handshakeRecvSequence, + handshakeCachePullRule{handshake.TypeFinished, cfg.initialEpoch + 1, false, false}, + ) + if !ok { + // No valid message received. Keep reading + return 0, nil, nil + } + + var finished *handshake.MessageFinished + if finished, ok = msgs[handshake.TypeFinished].(*handshake.MessageFinished); !ok { + return 0, &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}, nil + } + plainText := cache.pullAndMerge( + handshakeCachePullRule{handshake.TypeClientHello, cfg.initialEpoch, true, false}, + handshakeCachePullRule{handshake.TypeServerHello, cfg.initialEpoch, false, false}, + handshakeCachePullRule{handshake.TypeCertificate, cfg.initialEpoch, false, false}, + handshakeCachePullRule{handshake.TypeServerKeyExchange, cfg.initialEpoch, false, false}, + handshakeCachePullRule{handshake.TypeCertificateRequest, cfg.initialEpoch, false, false}, + handshakeCachePullRule{handshake.TypeServerHelloDone, cfg.initialEpoch, false, false}, + handshakeCachePullRule{handshake.TypeCertificate, cfg.initialEpoch, true, false}, + handshakeCachePullRule{handshake.TypeClientKeyExchange, cfg.initialEpoch, true, false}, + handshakeCachePullRule{handshake.TypeCertificateVerify, cfg.initialEpoch, true, false}, + handshakeCachePullRule{handshake.TypeFinished, cfg.initialEpoch + 1, true, false}, + ) + + expectedVerifyData, err := prf.VerifyDataServer(state.masterSecret, plainText, state.cipherSuite.HashFunc()) + if err != nil { + return 0, &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}, err + } + if !bytes.Equal(expectedVerifyData, finished.VerifyData) { + return 0, &alert.Alert{Level: alert.Fatal, Description: alert.HandshakeFailure}, errVerifyDataMismatch + } + + return flight5, nil, nil +} + +func flight5Generate(c flightConn, state *State, cache *handshakeCache, cfg *handshakeConfig) ([]*packet, *alert.Alert, error) { //nolint:gocognit + var certBytes [][]byte + var privateKey crypto.PrivateKey + if len(cfg.localCertificates) > 0 { + certificate, err := cfg.getCertificate(cfg.serverName) + if err != nil { + return nil, &alert.Alert{Level: alert.Fatal, Description: alert.HandshakeFailure}, err + } + certBytes = certificate.Certificate + privateKey = certificate.PrivateKey + } + + var pkts []*packet + + if state.remoteRequestedCertificate { + pkts = append(pkts, + &packet{ + record: &recordlayer.RecordLayer{ + Header: recordlayer.Header{ + Version: protocol.Version1_2, + }, + Content: &handshake.Handshake{ + Message: &handshake.MessageCertificate{ + Certificate: certBytes, + }, + }, + }, + }) + } + + clientKeyExchange := &handshake.MessageClientKeyExchange{} + if cfg.localPSKCallback == nil { + clientKeyExchange.PublicKey = state.localKeypair.PublicKey + } else { + clientKeyExchange.IdentityHint = cfg.localPSKIdentityHint + } + + pkts = append(pkts, + &packet{ + record: &recordlayer.RecordLayer{ + Header: recordlayer.Header{ + Version: protocol.Version1_2, + }, + Content: &handshake.Handshake{ + Message: clientKeyExchange, + }, + }, + }) + + serverKeyExchangeData := cache.pullAndMerge( + handshakeCachePullRule{handshake.TypeServerKeyExchange, cfg.initialEpoch, false, false}, + ) + + serverKeyExchange := &handshake.MessageServerKeyExchange{} + + // handshakeMessageServerKeyExchange is optional for PSK + if len(serverKeyExchangeData) == 0 { + alertPtr, err := handleServerKeyExchange(c, state, cfg, &handshake.MessageServerKeyExchange{}) + if err != nil { + return nil, alertPtr, err + } + } else { + rawHandshake := &handshake.Handshake{} + err := rawHandshake.Unmarshal(serverKeyExchangeData) + if err != nil { + return nil, &alert.Alert{Level: alert.Fatal, Description: alert.UnexpectedMessage}, err + } + + switch h := rawHandshake.Message.(type) { + case *handshake.MessageServerKeyExchange: + serverKeyExchange = h + default: + return nil, &alert.Alert{Level: alert.Fatal, Description: alert.UnexpectedMessage}, errInvalidContentType + } + } + + // Append not-yet-sent packets + merged := []byte{} + seqPred := uint16(state.handshakeSendSequence) + for _, p := range pkts { + h, ok := p.record.Content.(*handshake.Handshake) + if !ok { + return nil, &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}, errInvalidContentType + } + h.Header.MessageSequence = seqPred + seqPred++ + raw, err := h.Marshal() + if err != nil { + return nil, &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}, err + } + merged = append(merged, raw...) + } + + if alertPtr, err := initalizeCipherSuite(state, cache, cfg, serverKeyExchange, merged); err != nil { + return nil, alertPtr, err + } + + // If the client has sent a certificate with signing ability, a digitally-signed + // CertificateVerify message is sent to explicitly verify possession of the + // private key in the certificate. + if state.remoteRequestedCertificate && len(cfg.localCertificates) > 0 { + plainText := append(cache.pullAndMerge( + handshakeCachePullRule{handshake.TypeClientHello, cfg.initialEpoch, true, false}, + handshakeCachePullRule{handshake.TypeServerHello, cfg.initialEpoch, false, false}, + handshakeCachePullRule{handshake.TypeCertificate, cfg.initialEpoch, false, false}, + handshakeCachePullRule{handshake.TypeServerKeyExchange, cfg.initialEpoch, false, false}, + handshakeCachePullRule{handshake.TypeCertificateRequest, cfg.initialEpoch, false, false}, + handshakeCachePullRule{handshake.TypeServerHelloDone, cfg.initialEpoch, false, false}, + handshakeCachePullRule{handshake.TypeCertificate, cfg.initialEpoch, true, false}, + handshakeCachePullRule{handshake.TypeClientKeyExchange, cfg.initialEpoch, true, false}, + ), merged...) + + // Find compatible signature scheme + signatureHashAlgo, err := signaturehash.SelectSignatureScheme(cfg.localSignatureSchemes, privateKey) + if err != nil { + return nil, &alert.Alert{Level: alert.Fatal, Description: alert.InsufficientSecurity}, err + } + + certVerify, err := generateCertificateVerify(plainText, privateKey, signatureHashAlgo.Hash) + if err != nil { + return nil, &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}, err + } + state.localCertificatesVerify = certVerify + + p := &packet{ + record: &recordlayer.RecordLayer{ + Header: recordlayer.Header{ + Version: protocol.Version1_2, + }, + Content: &handshake.Handshake{ + Message: &handshake.MessageCertificateVerify{ + HashAlgorithm: signatureHashAlgo.Hash, + SignatureAlgorithm: signatureHashAlgo.Signature, + Signature: state.localCertificatesVerify, + }, + }, + }, + } + pkts = append(pkts, p) + + h, ok := p.record.Content.(*handshake.Handshake) + if !ok { + return nil, &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}, errInvalidContentType + } + h.Header.MessageSequence = seqPred + // seqPred++ // this is the last use of seqPred + raw, err := h.Marshal() + if err != nil { + return nil, &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}, err + } + merged = append(merged, raw...) + } + + pkts = append(pkts, + &packet{ + record: &recordlayer.RecordLayer{ + Header: recordlayer.Header{ + Version: protocol.Version1_2, + }, + Content: &protocol.ChangeCipherSpec{}, + }, + }) + + if len(state.localVerifyData) == 0 { + plainText := cache.pullAndMerge( + handshakeCachePullRule{handshake.TypeClientHello, cfg.initialEpoch, true, false}, + handshakeCachePullRule{handshake.TypeServerHello, cfg.initialEpoch, false, false}, + handshakeCachePullRule{handshake.TypeCertificate, cfg.initialEpoch, false, false}, + handshakeCachePullRule{handshake.TypeServerKeyExchange, cfg.initialEpoch, false, false}, + handshakeCachePullRule{handshake.TypeCertificateRequest, cfg.initialEpoch, false, false}, + handshakeCachePullRule{handshake.TypeServerHelloDone, cfg.initialEpoch, false, false}, + handshakeCachePullRule{handshake.TypeCertificate, cfg.initialEpoch, true, false}, + handshakeCachePullRule{handshake.TypeClientKeyExchange, cfg.initialEpoch, true, false}, + handshakeCachePullRule{handshake.TypeCertificateVerify, cfg.initialEpoch, true, false}, + handshakeCachePullRule{handshake.TypeFinished, cfg.initialEpoch + 1, true, false}, + ) + + var err error + state.localVerifyData, err = prf.VerifyDataClient(state.masterSecret, append(plainText, merged...), state.cipherSuite.HashFunc()) + if err != nil { + return nil, &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}, err + } + } + + pkts = append(pkts, + &packet{ + record: &recordlayer.RecordLayer{ + Header: recordlayer.Header{ + Version: protocol.Version1_2, + Epoch: 1, + }, + Content: &handshake.Handshake{ + Message: &handshake.MessageFinished{ + VerifyData: state.localVerifyData, + }, + }, + }, + shouldEncrypt: true, + resetLocalSequenceNumber: true, + }) + + return pkts, nil, nil +} + +func initalizeCipherSuite(state *State, cache *handshakeCache, cfg *handshakeConfig, h *handshake.MessageServerKeyExchange, sendingPlainText []byte) (*alert.Alert, error) { //nolint:gocognit + if state.cipherSuite.IsInitialized() { + return nil, nil + } + + clientRandom := state.localRandom.MarshalFixed() + serverRandom := state.remoteRandom.MarshalFixed() + + var err error + + if state.extendedMasterSecret { + var sessionHash []byte + sessionHash, err = cache.sessionHash(state.cipherSuite.HashFunc(), cfg.initialEpoch, sendingPlainText) + if err != nil { + return &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}, err + } + + state.masterSecret, err = prf.ExtendedMasterSecret(state.preMasterSecret, sessionHash, state.cipherSuite.HashFunc()) + if err != nil { + return &alert.Alert{Level: alert.Fatal, Description: alert.IllegalParameter}, err + } + } else { + state.masterSecret, err = prf.MasterSecret(state.preMasterSecret, clientRandom[:], serverRandom[:], state.cipherSuite.HashFunc()) + if err != nil { + return &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}, err + } + } + + if state.cipherSuite.AuthenticationType() == CipherSuiteAuthenticationTypeCertificate { + // Verify that the pair of hash algorithm and signiture is listed. + var validSignatureScheme bool + for _, ss := range cfg.localSignatureSchemes { + if ss.Hash == h.HashAlgorithm && ss.Signature == h.SignatureAlgorithm { + validSignatureScheme = true + break + } + } + if !validSignatureScheme { + return &alert.Alert{Level: alert.Fatal, Description: alert.InsufficientSecurity}, errNoAvailableSignatureSchemes + } + + expectedMsg := valueKeyMessage(clientRandom[:], serverRandom[:], h.PublicKey, h.NamedCurve) + if err = verifyKeySignature(expectedMsg, h.Signature, h.HashAlgorithm, state.PeerCertificates); err != nil { + return &alert.Alert{Level: alert.Fatal, Description: alert.BadCertificate}, err + } + var chains [][]*x509.Certificate + if !cfg.insecureSkipVerify { + if chains, err = verifyServerCert(state.PeerCertificates, cfg.rootCAs, cfg.serverName); err != nil { + return &alert.Alert{Level: alert.Fatal, Description: alert.BadCertificate}, err + } + } + if cfg.verifyPeerCertificate != nil { + if err = cfg.verifyPeerCertificate(state.PeerCertificates, chains); err != nil { + return &alert.Alert{Level: alert.Fatal, Description: alert.BadCertificate}, err + } + } + } + + if err = state.cipherSuite.Init(state.masterSecret, clientRandom[:], serverRandom[:], true); err != nil { + return &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}, err + } + + cfg.writeKeyLog(keyLogLabelTLS12, clientRandom[:], state.masterSecret) + + return nil, nil +} diff --git a/dtls-2.0.9/flight6handler.go b/dtls-2.0.9/flight6handler.go new file mode 100644 index 0000000..10de5ad --- /dev/null +++ b/dtls-2.0.9/flight6handler.go @@ -0,0 +1,82 @@ +package dtls + +import ( + "context" + + "github.com/pion/dtls/v2/pkg/crypto/prf" + "github.com/pion/dtls/v2/pkg/protocol" + "github.com/pion/dtls/v2/pkg/protocol/alert" + "github.com/pion/dtls/v2/pkg/protocol/handshake" + "github.com/pion/dtls/v2/pkg/protocol/recordlayer" +) + +func flight6Parse(ctx context.Context, c flightConn, state *State, cache *handshakeCache, cfg *handshakeConfig) (flightVal, *alert.Alert, error) { + _, msgs, ok := cache.fullPullMap(state.handshakeRecvSequence-1, + handshakeCachePullRule{handshake.TypeFinished, cfg.initialEpoch + 1, true, false}, + ) + if !ok { + // No valid message received. Keep reading + return 0, nil, nil + } + + if _, ok = msgs[handshake.TypeFinished].(*handshake.MessageFinished); !ok { + return 0, &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}, nil + } + + // Other party retransmitted the last flight. + return flight6, nil, nil +} + +func flight6Generate(c flightConn, state *State, cache *handshakeCache, cfg *handshakeConfig) ([]*packet, *alert.Alert, error) { + var pkts []*packet + + pkts = append(pkts, + &packet{ + record: &recordlayer.RecordLayer{ + Header: recordlayer.Header{ + Version: protocol.Version1_2, + }, + Content: &protocol.ChangeCipherSpec{}, + }, + }) + + if len(state.localVerifyData) == 0 { + plainText := cache.pullAndMerge( + handshakeCachePullRule{handshake.TypeClientHello, cfg.initialEpoch, true, false}, + handshakeCachePullRule{handshake.TypeServerHello, cfg.initialEpoch, false, false}, + handshakeCachePullRule{handshake.TypeCertificate, cfg.initialEpoch, false, false}, + handshakeCachePullRule{handshake.TypeServerKeyExchange, cfg.initialEpoch, false, false}, + handshakeCachePullRule{handshake.TypeCertificateRequest, cfg.initialEpoch, false, false}, + handshakeCachePullRule{handshake.TypeServerHelloDone, cfg.initialEpoch, false, false}, + handshakeCachePullRule{handshake.TypeCertificate, cfg.initialEpoch, true, false}, + handshakeCachePullRule{handshake.TypeClientKeyExchange, cfg.initialEpoch, true, false}, + handshakeCachePullRule{handshake.TypeCertificateVerify, cfg.initialEpoch, true, false}, + handshakeCachePullRule{handshake.TypeFinished, cfg.initialEpoch + 1, true, false}, + ) + + var err error + state.localVerifyData, err = prf.VerifyDataServer(state.masterSecret, plainText, state.cipherSuite.HashFunc()) + if err != nil { + return nil, &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}, err + } + } + + pkts = append(pkts, + &packet{ + record: &recordlayer.RecordLayer{ + Header: recordlayer.Header{ + Version: protocol.Version1_2, + Epoch: 1, + }, + Content: &handshake.Handshake{ + Message: &handshake.MessageFinished{ + VerifyData: state.localVerifyData, + }, + }, + }, + shouldEncrypt: true, + resetLocalSequenceNumber: true, + }, + ) + return pkts, nil, nil +} diff --git a/dtls-2.0.9/flighthandler.go b/dtls-2.0.9/flighthandler.go new file mode 100644 index 0000000..b364c09 --- /dev/null +++ b/dtls-2.0.9/flighthandler.go @@ -0,0 +1,57 @@ +package dtls + +import ( + "context" + + "github.com/pion/dtls/v2/pkg/protocol/alert" +) + +// Parse received handshakes and return next flightVal +type flightParser func(context.Context, flightConn, *State, *handshakeCache, *handshakeConfig) (flightVal, *alert.Alert, error) + +// Generate flights +type flightGenerator func(flightConn, *State, *handshakeCache, *handshakeConfig) ([]*packet, *alert.Alert, error) + +func (f flightVal) getFlightParser() (flightParser, error) { + switch f { + case flight0: + return flight0Parse, nil + case flight1: + return flight1Parse, nil + case flight2: + return flight2Parse, nil + case flight3: + return flight3Parse, nil + case flight4: + return flight4Parse, nil + case flight5: + return flight5Parse, nil + case flight6: + return flight6Parse, nil + default: + return nil, errInvalidFlight + } +} + +func (f flightVal) getFlightGenerator() (gen flightGenerator, retransmit bool, err error) { + switch f { + case flight0: + return flight0Generate, true, nil + case flight1: + return flight1Generate, true, nil + case flight2: + // https://tools.ietf.org/html/rfc6347#section-3.2.1 + // HelloVerifyRequests must not be retransmitted. + return flight2Generate, false, nil + case flight3: + return flight3Generate, true, nil + case flight4: + return flight4Generate, true, nil + case flight5: + return flight5Generate, true, nil + case flight6: + return flight6Generate, true, nil + default: + return nil, false, errInvalidFlight + } +} diff --git a/dtls-2.0.9/fragment_buffer.go b/dtls-2.0.9/fragment_buffer.go new file mode 100644 index 0000000..0274993 --- /dev/null +++ b/dtls-2.0.9/fragment_buffer.go @@ -0,0 +1,111 @@ +package dtls + +import ( + "github.com/pion/dtls/v2/pkg/protocol" + "github.com/pion/dtls/v2/pkg/protocol/handshake" + "github.com/pion/dtls/v2/pkg/protocol/recordlayer" +) + +type fragment struct { + recordLayerHeader recordlayer.Header + handshakeHeader handshake.Header + data []byte +} + +type fragmentBuffer struct { + // map of MessageSequenceNumbers that hold slices of fragments + cache map[uint16][]*fragment + + currentMessageSequenceNumber uint16 +} + +func newFragmentBuffer() *fragmentBuffer { + return &fragmentBuffer{cache: map[uint16][]*fragment{}} +} + +// Attempts to push a DTLS packet to the fragmentBuffer +// when it returns true it means the fragmentBuffer has inserted and the buffer shouldn't be handled +// when an error returns it is fatal, and the DTLS connection should be stopped +func (f *fragmentBuffer) push(buf []byte) (bool, error) { + frag := new(fragment) + if err := frag.recordLayerHeader.Unmarshal(buf); err != nil { + return false, err + } + + // fragment isn't a handshake, we don't need to handle it + if frag.recordLayerHeader.ContentType != protocol.ContentTypeHandshake { + return false, nil + } + + for buf = buf[recordlayer.HeaderSize:]; len(buf) != 0; frag = new(fragment) { + if err := frag.handshakeHeader.Unmarshal(buf); err != nil { + return false, err + } + + if _, ok := f.cache[frag.handshakeHeader.MessageSequence]; !ok { + f.cache[frag.handshakeHeader.MessageSequence] = []*fragment{} + } + + // end index should be the length of handshake header but if the handshake + // was fragmented, we should keep them all + end := int(handshake.HeaderLength + frag.handshakeHeader.Length) + if size := len(buf); end > size { + end = size + } + + // Discard all headers, when rebuilding the packet we will re-build + frag.data = append([]byte{}, buf[handshake.HeaderLength:end]...) + f.cache[frag.handshakeHeader.MessageSequence] = append(f.cache[frag.handshakeHeader.MessageSequence], frag) + buf = buf[end:] + } + + return true, nil +} + +func (f *fragmentBuffer) pop() (content []byte, epoch uint16) { + frags, ok := f.cache[f.currentMessageSequenceNumber] + if !ok { + return nil, 0 + } + + // Go doesn't support recursive lambdas + var appendMessage func(targetOffset uint32) bool + + rawMessage := []byte{} + appendMessage = func(targetOffset uint32) bool { + for _, f := range frags { + if f.handshakeHeader.FragmentOffset == targetOffset { + fragmentEnd := (f.handshakeHeader.FragmentOffset + f.handshakeHeader.FragmentLength) + if fragmentEnd != f.handshakeHeader.Length { + if !appendMessage(fragmentEnd) { + return false + } + } + + rawMessage = append(f.data, rawMessage...) + return true + } + } + return false + } + + // Recursively collect up + if !appendMessage(0) { + return nil, 0 + } + + firstHeader := frags[0].handshakeHeader + firstHeader.FragmentOffset = 0 + firstHeader.FragmentLength = firstHeader.Length + + rawHeader, err := firstHeader.Marshal() + if err != nil { + return nil, 0 + } + + messageEpoch := frags[0].recordLayerHeader.Epoch + + delete(f.cache, f.currentMessageSequenceNumber) + f.currentMessageSequenceNumber++ + return append(rawHeader, rawMessage...), messageEpoch +} diff --git a/dtls-2.0.9/fragment_buffer_test.go b/dtls-2.0.9/fragment_buffer_test.go new file mode 100644 index 0000000..62c7ead --- /dev/null +++ b/dtls-2.0.9/fragment_buffer_test.go @@ -0,0 +1,101 @@ +package dtls + +import ( + "reflect" + "testing" +) + +func TestFragmentBuffer(t *testing.T) { + for _, test := range []struct { + Name string + In [][]byte + Expected [][]byte + Epoch uint16 + }{ + { + Name: "Single Fragment", + In: [][]byte{ + {0x16, 0xfe, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xfe, 0xff, 0x00}, + }, + Expected: [][]byte{ + {0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xfe, 0xff, 0x00}, + }, + Epoch: 0, + }, + { + Name: "Single Fragment Epoch 3", + In: [][]byte{ + {0x16, 0xfe, 0xff, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xfe, 0xff, 0x00}, + }, + Expected: [][]byte{ + {0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xfe, 0xff, 0x00}, + }, + Epoch: 3, + }, + { + Name: "Multiple Fragments", + In: [][]byte{ + {0x16, 0xfe, 0xfd, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x81, 0x0b, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x01, 0x02, 0x03, 0x04}, + {0x16, 0xfe, 0xfd, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x81, 0x0b, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, 0x05, 0x06, 0x07, 0x08, 0x09}, + {0x16, 0xfe, 0xfd, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x81, 0x0b, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x0A, 0x00, 0x00, 0x05, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E}, + }, + Expected: [][]byte{ + {0x0b, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e}, + }, + Epoch: 0, + }, + { + Name: "Multiple Unordered Fragments", + In: [][]byte{ + {0x16, 0xfe, 0xfd, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x81, 0x0b, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x01, 0x02, 0x03, 0x04}, + {0x16, 0xfe, 0xfd, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x81, 0x0b, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x0A, 0x00, 0x00, 0x05, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E}, + {0x16, 0xfe, 0xfd, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x81, 0x0b, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, 0x05, 0x06, 0x07, 0x08, 0x09}, + }, + Expected: [][]byte{ + {0x0b, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e}, + }, + Epoch: 0, + }, + { + Name: "Multiple Handshakes in Signle Fragment", + In: [][]byte{ + { + 0x16, 0xfe, 0xfd, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x30, /* record header */ + 0x03, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0xfe, 0xff, 0x01, 0x01, /*handshake msg 1*/ + 0x03, 0x00, 0x00, 0x04, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0xfe, 0xff, 0x01, 0x01, /*handshake msg 2*/ + 0x03, 0x00, 0x00, 0x04, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0xfe, 0xff, 0x01, 0x01, /*handshake msg 3*/ + }, + }, + Expected: [][]byte{ + {0x03, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0xfe, 0xff, 0x01, 0x01}, + {0x03, 0x00, 0x00, 0x04, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0xfe, 0xff, 0x01, 0x01}, + {0x03, 0x00, 0x00, 0x04, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0xfe, 0xff, 0x01, 0x01}, + }, + Epoch: 0, + }, + } { + fragmentBuffer := newFragmentBuffer() + for _, frag := range test.In { + status, err := fragmentBuffer.push(frag) + if err != nil { + t.Error(err) + } else if !status { + t.Errorf("fragmentBuffer didn't accept fragments for '%s'", test.Name) + } + } + + for _, expected := range test.Expected { + out, epoch := fragmentBuffer.pop() + if !reflect.DeepEqual(out, expected) { + t.Errorf("fragmentBuffer '%s' push/pop: got % 02x, want % 02x", test.Name, out, expected) + } + if epoch != test.Epoch { + t.Errorf("fragmentBuffer returned wrong epoch: got %d, want %d", epoch, test.Epoch) + } + } + + if frag, _ := fragmentBuffer.pop(); frag != nil { + t.Errorf("fragmentBuffer popped single buffer multiple times for '%s'", test.Name) + } + } +} diff --git a/dtls-2.0.9/fuzz.go b/dtls-2.0.9/fuzz.go new file mode 100644 index 0000000..56c1bf2 --- /dev/null +++ b/dtls-2.0.9/fuzz.go @@ -0,0 +1,38 @@ +// +build gofuzz + +package dtls + +import "fmt" + +func partialHeaderMismatch(a, b recordlayer.Header) bool { + // Ignoring content length for now. + a.contentLen = b.contentLen + return a != b +} + +func FuzzRecordLayer(data []byte) int { + var r recordLayer + if err := r.Unmarshal(data); err != nil { + return 0 + } + buf, err := r.Marshal() + if err != nil { + return 1 + } + if len(buf) == 0 { + panic("zero buff") // nolint + } + var nr recordLayer + if err = nr.Unmarshal(data); err != nil { + panic(err) // nolint + } + if partialHeaderMismatch(nr.recordlayer.Header, r.recordlayer.Header) { + panic( // nolint + fmt.Sprintf("header mismatch: %+v != %+v", + nr.recordlayer.Header, r.recordlayer.Header, + ), + ) + } + + return 1 +} diff --git a/dtls-2.0.9/fuzz/corpus/012178ca0830b7449ad370598d55873d81b95e40-25 b/dtls-2.0.9/fuzz/corpus/012178ca0830b7449ad370598d55873d81b95e40-25 new file mode 100644 index 0000000000000000000000000000000000000000..f82ac9a17f515ca94cba4160df6c271af30ec8e1 GIT binary patch literal 76 zcmWf;xOe~i{qI3UwE`mpgR!ZpQ9T2YHfCUOC}FTPF*h+WH8Qp^^vNtK$w_t1OEF?# W-~dVh)i5#eg4t}W|5+Uv6gdFO^AoKA literal 0 HcmV?d00001 diff --git a/dtls-2.0.9/fuzz/corpus/01277073b27ccc6925ce4c941527f7b7705c8311-1 b/dtls-2.0.9/fuzz/corpus/01277073b27ccc6925ce4c941527f7b7705c8311-1 new file mode 100644 index 0000000..4756c7a --- /dev/null +++ b/dtls-2.0.9/fuzz/corpus/01277073b27ccc6925ce4c941527f7b7705c8311-1 @@ -0,0 +1 @@ +12[A51 \ No newline at end of file diff --git a/dtls-2.0.9/fuzz/corpus/039192caed40959ac2f5c3254669312ba2dfbcad-12 b/dtls-2.0.9/fuzz/corpus/039192caed40959ac2f5c3254669312ba2dfbcad-12 new file mode 100644 index 0000000000000000000000000000000000000000..3580e3c85ed253af66dbfa4eae2382164e926034 GIT binary patch literal 33 jcmWf8?w3=XlgUt$pRbU^&%nR|W&mkMMh2Gsd$kz=r&kDw literal 0 HcmV?d00001 diff --git a/dtls-2.0.9/fuzz/corpus/03a9bad270cf32520b5c3e99add47c648ba6150f-7 b/dtls-2.0.9/fuzz/corpus/03a9bad270cf32520b5c3e99add47c648ba6150f-7 new file mode 100644 index 0000000000000000000000000000000000000000..f3b959a0fd9b4d4afae80a3990bd93c808821fe6 GIT binary patch literal 25 acmWf;xOe~i{qI3UwE`mp2)t)V0g?c(Dhe6^ literal 0 HcmV?d00001 diff --git a/dtls-2.0.9/fuzz/corpus/048fcd45b732d5bed912e6652bc265a0adaf5664-26 b/dtls-2.0.9/fuzz/corpus/048fcd45b732d5bed912e6652bc265a0adaf5664-26 new file mode 100644 index 0000000000000000000000000000000000000000..40c5ce232ca658255b7ad9352483cf6737b721d8 GIT binary patch literal 76 zcmWf;xOe~i{qI3UwE`mpgR!ZiQ9T2YHfCVpC}FTPF*h+WH8Qp^^vNtK$w_t1OEH2- X0M#%s@G`Km{%0^{bzm@MFy#OMzCjaV literal 0 HcmV?d00001 diff --git a/dtls-2.0.9/fuzz/corpus/04a28c0806a91267f0576e11d042400f41dc538b-12 b/dtls-2.0.9/fuzz/corpus/04a28c0806a91267f0576e11d042400f41dc538b-12 new file mode 100644 index 0000000000000000000000000000000000000000..a25e2a6a991a8d4a0477c0941f347e8807823077 GIT binary patch literal 28 fcmWf8?w3=XlgUt$&%?mL3`8#&6c|Cw7YqylYl#K* literal 0 HcmV?d00001 diff --git a/dtls-2.0.9/fuzz/corpus/04d00cfd50deb9ccd9d14be8c58f401a0414dad3-30 b/dtls-2.0.9/fuzz/corpus/04d00cfd50deb9ccd9d14be8c58f401a0414dad3-30 new file mode 100644 index 0000000000000000000000000000000000000000..53a1acbdeb16bcfedd5af2dd4fb749c8dc16785e GIT binary patch literal 76 zcmWf;xOe~i{qI3UwE`mpgR!Y80|*!!GcY)mFj$(Ho0ymy8Cw|oWR{fVq`Ky%7%>QQ V042bxm>75&7&w617#R2%7y+4{5iSm#MCvpRbU^%fP@qQC$G91IMs3=B-#N&twJ1&9Cu literal 0 HcmV?d00001 diff --git a/dtls-2.0.9/fuzz/corpus/06148fe224720cd3a0497fc87f2b6bc5f004484a-30 b/dtls-2.0.9/fuzz/corpus/06148fe224720cd3a0497fc87f2b6bc5f004484a-30 new file mode 100644 index 0000000000000000000000000000000000000000..f80886b52902e61db04fa8e6247469cbc5366294 GIT binary patch literal 76 zcmWf;xOe~i{qI3UwE`mpgR!Y80|*!!GcY)mFj$(Ho0ymy8Cw|oWR{fVq`Ky%7%>QQ V042bxm>75&7&w617#O%17y+4*5ibA$ literal 0 HcmV?d00001 diff --git a/dtls-2.0.9/fuzz/corpus/078c2bd97a33002242f9d5ac0a95970c9432124a-31 b/dtls-2.0.9/fuzz/corpus/078c2bd97a33002242f9d5ac0a95970c9432124a-31 new file mode 100644 index 0000000000000000000000000000000000000000..ac541d3ce7ff06d9de16bf13d93251e3b9d7c663 GIT binary patch literal 76 zcmWf;xOe~i{qI3UwE`mpgR!Y80|*!!GcY)mFj$(Ho0ymy8Cw|oWR{fVq`Ky%7%>QQ V042bxm>75&7&w61n1Kc`0sx?r6O#Y{ literal 0 HcmV?d00001 diff --git a/dtls-2.0.9/fuzz/corpus/07ff33058f3c6732b9439f7d5c2bd50bb46adb31-20 b/dtls-2.0.9/fuzz/corpus/07ff33058f3c6732b9439f7d5c2bd50bb46adb31-20 new file mode 100644 index 0000000000000000000000000000000000000000..dd34c829037d46a10ee0bf1340337eea167b022e GIT binary patch literal 38 hcmWf8?w3>Sm#MCvpRbU^%fP@}!T<(9CMP2t005$n1xNq@ literal 0 HcmV?d00001 diff --git a/dtls-2.0.9/fuzz/corpus/08f2f7719e35261f615174917101cba578892f43-11 b/dtls-2.0.9/fuzz/corpus/08f2f7719e35261f615174917101cba578892f43-11 new file mode 100644 index 0000000000000000000000000000000000000000..c6911cbca8da92198fdcabd30df890806ba1b317 GIT binary patch literal 28 dcmWf8?w3=XlgUt$pRd5k!@$7&f&oS|004D127&+p literal 0 HcmV?d00001 diff --git a/dtls-2.0.9/fuzz/corpus/09b742837cf0d26ddecb5dbf536d91db6d1e9855-12 b/dtls-2.0.9/fuzz/corpus/09b742837cf0d26ddecb5dbf536d91db6d1e9855-12 new file mode 100644 index 0000000000000000000000000000000000000000..c41903165f943d7a955a02a3bd901b6485358554 GIT binary patch literal 33 kcmWf8?w3=XlgUt$pRbU^&%nR|W&mkM#{KX2@6~1i0I+BZG5`Po literal 0 HcmV?d00001 diff --git a/dtls-2.0.9/fuzz/corpus/0a3bff70743f3cc7ecdc293887c10e14e152dec2-19 b/dtls-2.0.9/fuzz/corpus/0a3bff70743f3cc7ecdc293887c10e14e152dec2-19 new file mode 100644 index 0000000000000000000000000000000000000000..73bb16ae25a0a4401c299ea3bb4b297d363d44db GIT binary patch literal 33 kcmWf8?w3>Sm#MCvpRbU^%fP@Sm#MCvpRbU^%fP@<(vbfz5lAyLFfr`kt91_mto8~5 literal 0 HcmV?d00001 diff --git a/dtls-2.0.9/fuzz/corpus/17863d02affd5fc60da97a59318b3f7014f93a9f-36 b/dtls-2.0.9/fuzz/corpus/17863d02affd5fc60da97a59318b3f7014f93a9f-36 new file mode 100644 index 0000000000000000000000000000000000000000..073672020ac7552e18385ce3a28334da6df1c4e3 GIT binary patch literal 76 zcmWf;xOe~i{qI3UwE`mpgRv(vbfz8%V$3zZZzK834%V48{Ne literal 0 HcmV?d00001 diff --git a/dtls-2.0.9/fuzz/corpus/1c042652c21f2c6d7ffcb6b6e6be55fdf95a5dbb-30 b/dtls-2.0.9/fuzz/corpus/1c042652c21f2c6d7ffcb6b6e6be55fdf95a5dbb-30 new file mode 100644 index 0000000000000000000000000000000000000000..7a20346984191a673b80b39d8d5c489a17f2200d GIT binary patch literal 76 zcmWf;xOe~i{qI3UwE`~#gR!Y80|*!!FfcfjFj$(Jo0ymy8Cw|oWR{fVq{0LkEP-m6 R7=Ri$N*LH!|1&fI`2d}862|}l literal 0 HcmV?d00001 diff --git a/dtls-2.0.9/fuzz/corpus/1d09cef95c3269d3e244f0008a4fc6dfefd1e2ad-9 b/dtls-2.0.9/fuzz/corpus/1d09cef95c3269d3e244f0008a4fc6dfefd1e2ad-9 new file mode 100644 index 0000000000000000000000000000000000000000..f7c552375871bbb6ceb00238093b56f09443b113 GIT binary patch literal 25 ccmWf8?w3=XlgUt$pRbU^%fP@83B(`(0AZj7L;wH) literal 0 HcmV?d00001 diff --git a/dtls-2.0.9/fuzz/corpus/22e3d3a8748eb152a65ee9ada8834f8a07b247f4-29 b/dtls-2.0.9/fuzz/corpus/22e3d3a8748eb152a65ee9ada8834f8a07b247f4-29 new file mode 100644 index 0000000000000000000000000000000000000000..a8c217e4991e9bd3e9b36e0d2085c5da1bae5351 GIT binary patch literal 76 zcmWf;xOe~i{qI3UwE`mpgR!ZiQ9T2YHfCVuC}FTPF>f+4H43&c^vNtK$w_t1OEF?# R080D^s$pQ@WiUlR4glHi6Y2l} literal 0 HcmV?d00001 diff --git a/dtls-2.0.9/fuzz/corpus/23ce064ef35c0204982d748c34850bfc9433beca-13 b/dtls-2.0.9/fuzz/corpus/23ce064ef35c0204982d748c34850bfc9433beca-13 new file mode 100644 index 0000000000000000000000000000000000000000..1a992de605a56d647eac44a9ed1a86334f81d4be GIT binary patch literal 33 ncmWf8?w3=XlgUt$pRbU^%fP@<(vbfz8%VSM|IfOAuQme!zSm#MCvpRbU^%fP@}!T<)m?2Md@Aiw}30HW{(PXGV_ literal 0 HcmV?d00001 diff --git a/dtls-2.0.9/fuzz/corpus/2438ed38ea739d8f57018f8de0a52f3e545ac760-18 b/dtls-2.0.9/fuzz/corpus/2438ed38ea739d8f57018f8de0a52f3e545ac760-18 new file mode 100644 index 0000000000000000000000000000000000000000..6ebc62fd38d06bdc452ffd23f3312b74bfabd8ff GIT binary patch literal 33 mcmWf8?w8Y$lPT~oKVKn-mw|z!M1cVWI2ah18TT_X-U9%nsR!8r literal 0 HcmV?d00001 diff --git a/dtls-2.0.9/fuzz/corpus/256b14a77bc0439a14908b6fa00afb348dde3af4-17 b/dtls-2.0.9/fuzz/corpus/256b14a77bc0439a14908b6fa00afb348dde3af4-17 new file mode 100644 index 0000000000000000000000000000000000000000..fc08e43090ee7411cbeaac0994192d89a67127db GIT binary patch literal 33 ncmWf8?w3>Sm#MCvpRbU^%fP@<(vbfz5lAyLGCJ(vt91_mt~Uxo literal 0 HcmV?d00001 diff --git a/dtls-2.0.9/fuzz/corpus/27702a0157f6eeb426aef4d5789b380d7b23801e-35 b/dtls-2.0.9/fuzz/corpus/27702a0157f6eeb426aef4d5789b380d7b23801e-35 new file mode 100644 index 0000000000000000000000000000000000000000..64b2027f3f6a7baf98ab8d863b3a63e4fea216c5 GIT binary patch literal 76 zcmWf;=&=9&{`Y%si|*bD%RZ4re4 literal 0 HcmV?d00001 diff --git a/dtls-2.0.9/fuzz/corpus/29accdef171829b8dc0dba39d24acf913e13a31f-20 b/dtls-2.0.9/fuzz/corpus/29accdef171829b8dc0dba39d24acf913e13a31f-20 new file mode 100644 index 0000000000000000000000000000000000000000..e0fd446e2f8b7a3ed8fc48c3b426bb31c718f35f GIT binary patch literal 33 kcmWf;xR-(9|Np)F8GwL?fq|o>A^%?@kY;8OX9R+K0J<&+`2YX_ literal 0 HcmV?d00001 diff --git a/dtls-2.0.9/fuzz/corpus/2ad24ef4188d2626e363cb12c5242fa96abfa7a3-13 b/dtls-2.0.9/fuzz/corpus/2ad24ef4188d2626e363cb12c5242fa96abfa7a3-13 new file mode 100644 index 0000000000000000000000000000000000000000..8038d1a4b858641111e6a30dd6a05a0a42553301 GIT binary patch literal 28 fcmWf8?w3=XlgUt$&%?mL3`8#&6c|Cw7Yq&nYsm&R literal 0 HcmV?d00001 diff --git a/dtls-2.0.9/fuzz/corpus/2db7497fc9f463803d041365e337cccd7e74111a-18 b/dtls-2.0.9/fuzz/corpus/2db7497fc9f463803d041365e337cccd7e74111a-18 new file mode 100644 index 0000000000000000000000000000000000000000..558ffdb0b57830951a7ff6bb1771a3215f5ae36c GIT binary patch literal 33 mcmWf;xOe;e{pWY@X8-~o1_q9jhWvktK$@9BoPm*%;T{0qXbLL; literal 0 HcmV?d00001 diff --git a/dtls-2.0.9/fuzz/corpus/30b9805b33c0d67926cbb5ab174508797eb7b7a7-17 b/dtls-2.0.9/fuzz/corpus/30b9805b33c0d67926cbb5ab174508797eb7b7a7-17 new file mode 100644 index 0000000000000000000000000000000000000000..95b5ea5d669bf76130609bdabbde8d0d1759e819 GIT binary patch literal 33 mcmWf;xOe;e{pa`YX8-~o1_q9jhWvktK$@9BJbC|Kt$P6G3k);> literal 0 HcmV?d00001 diff --git a/dtls-2.0.9/fuzz/corpus/3105d624d1010500139670e332bd50771c112fdd-17 b/dtls-2.0.9/fuzz/corpus/3105d624d1010500139670e332bd50771c112fdd-17 new file mode 100644 index 0000000000000000000000000000000000000000..ac56ddcf00620a09642322696e539c64d01598e5 GIT binary patch literal 33 kcmWf8?w8Y`kSXvlKVKn^mw|z!M1cVWIG7o7fRxrf0Gkg7Qvd(} literal 0 HcmV?d00001 diff --git a/dtls-2.0.9/fuzz/corpus/32b051a5ed27cbcb3c1689adbf51c4223e58f9bc-36 b/dtls-2.0.9/fuzz/corpus/32b051a5ed27cbcb3c1689adbf51c4223e58f9bc-36 new file mode 100644 index 0000000000000000000000000000000000000000..f5138d98a25b190476e70122c5d117bb1b61b973 GIT binary patch literal 76 zcmWf;xOe~i{qI3UwE`mpgRvqQC$G91IMs3=B-#4FHJq1?B(% literal 0 HcmV?d00001 diff --git a/dtls-2.0.9/fuzz/corpus/386d1a6c0d51af038a3b2d3adba6eb15d8e3fe0a-23 b/dtls-2.0.9/fuzz/corpus/386d1a6c0d51af038a3b2d3adba6eb15d8e3fe0a-23 new file mode 100644 index 0000000000000000000000000000000000000000..c5914c7c9199f7ce9e7162a8b7565c127ed1d9a7 GIT binary patch literal 76 zcmWf8?w3>S*R8IUpRbU^#K2%|YHC!^0Hlq~O&H89Eltc#OiYc8Eew4!OGLO literal 0 HcmV?d00001 diff --git a/dtls-2.0.9/fuzz/corpus/3929563fe81b960a338a68a87a60e1940ac7f14e-34 b/dtls-2.0.9/fuzz/corpus/3929563fe81b960a338a68a87a60e1940ac7f14e-34 new file mode 100644 index 0000000000000000000000000000000000000000..552a00a70c6e8fbb894369d7b8eecaae633b37bb GIT binary patch literal 76 zcmWf;xOe~i{qI3UwE`mpgRv4dwg3PC literal 0 HcmV?d00001 diff --git a/dtls-2.0.9/fuzz/corpus/3eb3261e52074eceab2d28b5eee628d3ec213a84-14 b/dtls-2.0.9/fuzz/corpus/3eb3261e52074eceab2d28b5eee628d3ec213a84-14 new file mode 100644 index 0000000000000000000000000000000000000000..c1d6dd48957e3e41382506601d51c7d9bc76b622 GIT binary patch literal 33 lcmWf8?w3=XlgUt$FQ$;g&A`A>qQC$G91IMs3=B-#3;>981!e#M literal 0 HcmV?d00001 diff --git a/dtls-2.0.9/fuzz/corpus/3f88c87cc5fe3fff5a45dc1916eed2fdcfe20d57-13 b/dtls-2.0.9/fuzz/corpus/3f88c87cc5fe3fff5a45dc1916eed2fdcfe20d57-13 new file mode 100644 index 0000000000000000000000000000000000000000..0da0cc33200033dcbada2e385f425caa2a1c2cd4 GIT binary patch literal 33 ncmWf8?w3=XlPOS=pRbU^%fP@<(vbfz5lAyIFfr`kt91_mwN(mJ literal 0 HcmV?d00001 diff --git a/dtls-2.0.9/fuzz/corpus/3f928478ccaf16b9685071b91f52d5e0e6bc71c1-38 b/dtls-2.0.9/fuzz/corpus/3f928478ccaf16b9685071b91f52d5e0e6bc71c1-38 new file mode 100644 index 0000000000000000000000000000000000000000..c19a284e3654f7d833f16815edea31b5d9bd6419 GIT binary patch literal 76 zcmWf8?3YvQm#LnapRb_6$iQH1%ESNy#>NZ`B_%nj0eL1SrXYrEUWyUJ|NjgOmV#h; P23`gR4u%8T2&fAH*H;s= literal 0 HcmV?d00001 diff --git a/dtls-2.0.9/fuzz/corpus/42ab249f3ceb17939f5fcab757894b22d94a86a8-22 b/dtls-2.0.9/fuzz/corpus/42ab249f3ceb17939f5fcab757894b22d94a86a8-22 new file mode 100644 index 0000000000000000000000000000000000000000..322bf2c0d180e6fbd5e5b863ffd62fd126d58014 GIT binary patch literal 33 ncmWf8?w3=XlgUt$FJ_d(&A`A>qQG#M0Z212ure?(32*}dl??_( literal 0 HcmV?d00001 diff --git a/dtls-2.0.9/fuzz/corpus/42dbe1a681da3f7e48d18c53ab26b5893f3ea2ac-9 b/dtls-2.0.9/fuzz/corpus/42dbe1a681da3f7e48d18c53ab26b5893f3ea2ac-9 new file mode 100644 index 0000000000000000000000000000000000000000..c7c2bed5f99c0c212fa2d80128c658ac8df6c9a3 GIT binary patch literal 25 acmWf8?w3=XlgUt$pRbU^&AmHy0AkDqW&i*H literal 0 HcmV?d00001 diff --git a/dtls-2.0.9/fuzz/corpus/4735f3fc147ee436f8c02c24b9c40b4ee4cb1265-7 b/dtls-2.0.9/fuzz/corpus/4735f3fc147ee436f8c02c24b9c40b4ee4cb1265-7 new file mode 100644 index 0000000000000000000000000000000000000000..66de70c366f467243b59cf2ef5b4a5af9750a40d GIT binary patch literal 25 gcmWf;xOe~i{qOhgdN0StCBneK;IRKaLkf@v0H!wy{{R30 literal 0 HcmV?d00001 diff --git a/dtls-2.0.9/fuzz/corpus/48e4ba16b5626f66169cf52fb35054ae32f1037e-27 b/dtls-2.0.9/fuzz/corpus/48e4ba16b5626f66169cf52fb35054ae32f1037e-27 new file mode 100644 index 0000000000000000000000000000000000000000..6c37949f03b7dfccde1c94d9fd04369a9481334e GIT binary patch literal 76 zcmWf8?w3>S*R8IUpRbU^#K2%|YRUit#zy8Q3}%*=Cgvt4rbfmVhCZ1kB{`|Cc_|DG Zj10UbU`0Ts+#Dqg_5c5~Iw&e~005Mw5OV+k literal 0 HcmV?d00001 diff --git a/dtls-2.0.9/fuzz/corpus/4be120299b63639b4c203c93da101e2db703839a-26 b/dtls-2.0.9/fuzz/corpus/4be120299b63639b4c203c93da101e2db703839a-26 new file mode 100644 index 0000000000000000000000000000000000000000..f8da2d609abd1a72fa8e0750abd967e513dc61e0 GIT binary patch literal 76 zcmWf8?w3>S*R8IUpRbU^#K2%|YHC!^0Hlq~O&H89Eltc#OiYc8Eew4!OGi_>|bx>5~006s>5hMTr literal 0 HcmV?d00001 diff --git a/dtls-2.0.9/fuzz/corpus/4cdafe201d691c06b529689668d52106a3e98dfa-22 b/dtls-2.0.9/fuzz/corpus/4cdafe201d691c06b529689668d52106a3e98dfa-22 new file mode 100644 index 0000000000000000000000000000000000000000..2fb9f62b5638ecc38532dd24397154c53df01a7e GIT binary patch literal 38 ocmWf8?w3>Sm#MCvpRbU^%fP@}!T<(9CT9r;8|!~o2L?q30ItsmDgXcg literal 0 HcmV?d00001 diff --git a/dtls-2.0.9/fuzz/corpus/4d79d6a303e57c882d1d329ad4e3f091dd60e7ff-20 b/dtls-2.0.9/fuzz/corpus/4d79d6a303e57c882d1d329ad4e3f091dd60e7ff-20 new file mode 100644 index 0000000000000000000000000000000000000000..672fb7982540bea9e6f8c6fb8e391450daef2dcf GIT binary patch literal 33 jcmWf;xOe;e{pWY@X8-~o1_q9j1`vyZgPB1bh!_|F##9HG literal 0 HcmV?d00001 diff --git a/dtls-2.0.9/fuzz/corpus/509dbda3f391113a75c8309028bf59c0f107ac52-30 b/dtls-2.0.9/fuzz/corpus/509dbda3f391113a75c8309028bf59c0f107ac52-30 new file mode 100644 index 0000000000000000000000000000000000000000..401db35c030e078827de53de6d4c61bdb9895f63 GIT binary patch literal 76 zcmWf;xOe~i{qI3UwE`mpgR!YG0|*!csgjbMRM$Kc6H_B&3qzkw2sg!uL71b2!4j;B SiGi1afrEkTz+MI>*8c$0iWI^C literal 0 HcmV?d00001 diff --git a/dtls-2.0.9/fuzz/corpus/52aecd8762579fcaa1b5f26b152840f899683660-17 b/dtls-2.0.9/fuzz/corpus/52aecd8762579fcaa1b5f26b152840f899683660-17 new file mode 100644 index 0000000000000000000000000000000000000000..9483584efa231cb369e9e0d7ae1c5d488e30fbb5 GIT binary patch literal 33 kcmWf8?w3>Sm#MCvpRbU^%fP@75&I5-#>KsrDe0J4D*>;M1& literal 0 HcmV?d00001 diff --git a/dtls-2.0.9/fuzz/corpus/59d6ef268e83be801c670340b2383a5a732308cb-8 b/dtls-2.0.9/fuzz/corpus/59d6ef268e83be801c670340b2383a5a732308cb-8 new file mode 100644 index 0000000000000000000000000000000000000000..91e28537d493b3dd41ddbeefa0e51cd4449b5570 GIT binary patch literal 25 acmWf8?w3=XlgUt$pRbU^&%gj-0BHbfg9gh0 literal 0 HcmV?d00001 diff --git a/dtls-2.0.9/fuzz/corpus/5b3cbe41487f4f9f5e728a86adce154ebd73fbe0-9 b/dtls-2.0.9/fuzz/corpus/5b3cbe41487f4f9f5e728a86adce154ebd73fbe0-9 new file mode 100644 index 0000000000000000000000000000000000000000..71470b058a4bd983128a2bcb76346c72ef9cd8dc GIT binary patch literal 25 gcmWf;xOe;e{pa`YXV}00Jud?TgYN$K3@Jbw0J4?~4gdfE literal 0 HcmV?d00001 diff --git a/dtls-2.0.9/fuzz/corpus/5c165fd943bcb6df518c71b149d5aed736237833-16 b/dtls-2.0.9/fuzz/corpus/5c165fd943bcb6df518c71b149d5aed736237833-16 new file mode 100644 index 0000000000000000000000000000000000000000..31cd9b35607b9dfa523358263603d5b0d640f00d GIT binary patch literal 33 ncmWf8?w3>Sm#MCvpRbU^%fP@<(vbfz5lAyLFgontt91_mt}qHg literal 0 HcmV?d00001 diff --git a/dtls-2.0.9/fuzz/corpus/5eeaf10bf3fbb5575a63e054fd377645b5f45de5-3 b/dtls-2.0.9/fuzz/corpus/5eeaf10bf3fbb5575a63e054fd377645b5f45de5-3 new file mode 100644 index 0000000..224b46e --- /dev/null +++ b/dtls-2.0.9/fuzz/corpus/5eeaf10bf3fbb5575a63e054fd377645b5f45de5-3 @@ -0,0 +1 @@ +}\v/ \ No newline at end of file diff --git a/dtls-2.0.9/fuzz/corpus/64c5404b7e07af41448c99eadd4ded3a1572b503-9 b/dtls-2.0.9/fuzz/corpus/64c5404b7e07af41448c99eadd4ded3a1572b503-9 new file mode 100644 index 0000000000000000000000000000000000000000..f887cb37188b84abbcd79e5f7bc533df9e2c1f8d GIT binary patch literal 25 acmWf8?w3=XlgUt$pRbT3!oUDw0BHbfzXs3% literal 0 HcmV?d00001 diff --git a/dtls-2.0.9/fuzz/corpus/6926133d1d407a21e5e57ed4ec71583b8f4650ab-16 b/dtls-2.0.9/fuzz/corpus/6926133d1d407a21e5e57ed4ec71583b8f4650ab-16 new file mode 100644 index 0000000000000000000000000000000000000000..04d21c8f6c189ba7be359d766ed2b93360f1c07b GIT binary patch literal 33 lcmWf8?w3=XlgUt$FQ$;g&A`A>qQC$G91IMs3=B*LN&twT1&sgz literal 0 HcmV?d00001 diff --git a/dtls-2.0.9/fuzz/corpus/6998ed50de84d0a1e2250af37ef989f866392d8e-7 b/dtls-2.0.9/fuzz/corpus/6998ed50de84d0a1e2250af37ef989f866392d8e-7 new file mode 100644 index 0000000000000000000000000000000000000000..4fcf44dd074f27e92c505ab62ebd1a5ac77e863c GIT binary patch literal 25 bcmWf8?w3=XlgUt$pRd3mz`y`z^WfKNq literal 0 HcmV?d00001 diff --git a/dtls-2.0.9/fuzz/corpus/6a823391df6589e83b50fbf6ad7ec4a61edb34c5-35 b/dtls-2.0.9/fuzz/corpus/6a823391df6589e83b50fbf6ad7ec4a61edb34c5-35 new file mode 100644 index 0000000000000000000000000000000000000000..fe27af27abf8f7712ba15e6811d56aa54eda8fa8 GIT binary patch literal 76 zcmWf;xOe~i{qI3UwE`mpgRv9!fUz+{i9-p4rK!20QNw#<3qzmGl9C(-1`7{! V6B9!RBL+*bGA;%Vr~&&K7yy137HSm#MCvpRbU^%fP@QQ X042bxm>75&7&sXC4(w%MVqgRSq6HGM literal 0 HcmV?d00001 diff --git a/dtls-2.0.9/fuzz/corpus/75e00d510635ac25c84a337514180b32b8a4051b-25 b/dtls-2.0.9/fuzz/corpus/75e00d510635ac25c84a337514180b32b8a4051b-25 new file mode 100644 index 0000000000000000000000000000000000000000..0645567ccafe20638b6b378ed9786e048d2806a8 GIT binary patch literal 76 zcmWf8?w3>S*R8IUpRbU^#K2%|YHC!^0Hlq~O&H89Eltc#OiYc8Eew4!OGdo literal 0 HcmV?d00001 diff --git a/dtls-2.0.9/fuzz/corpus/7c33a04f1cb9a7b2bf6be6f834aeeef943a242f2-34 b/dtls-2.0.9/fuzz/corpus/7c33a04f1cb9a7b2bf6be6f834aeeef943a242f2-34 new file mode 100644 index 0000000000000000000000000000000000000000..e9d60531125f3f4de8b2ef3ac9c83d39602ec6a0 GIT binary patch literal 76 zcmWf;xOe~i{qI3UwE`mpgRvau0L2Ftd;kCd literal 0 HcmV?d00001 diff --git a/dtls-2.0.9/fuzz/corpus/7c33caa83f291ca5a328d13e1d97954d9462e0e1-34 b/dtls-2.0.9/fuzz/corpus/7c33caa83f291ca5a328d13e1d97954d9462e0e1-34 new file mode 100644 index 0000000000000000000000000000000000000000..c0d8e6e81ab49e9beb52dad4ec6dfe4dabbc07a2 GIT binary patch literal 76 zcmWf;xOe~i{qI3UwE`mpgR!Y80|*!!Gn6=#Fj$(Jo0v2h8Cw|oWR{fVq%v3<00m5q V7%YKmm>75&I5-#>Ou>+W0RXeV5+MKp literal 0 HcmV?d00001 diff --git a/dtls-2.0.9/fuzz/corpus/7c60d79ccb4b24c486293fe63c763f71c2948d33-28 b/dtls-2.0.9/fuzz/corpus/7c60d79ccb4b24c486293fe63c763f71c2948d33-28 new file mode 100644 index 0000000000000000000000000000000000000000..ffd2dce8c91703975b247422b8019f336afd8997 GIT binary patch literal 76 zcmWf;xOe~i{qI3UwE`mpgR!ZpQ9T2YHfCUOC}FTPF*h+WH8Qp^^vNtK$w_t1OEF?# W-~dVh)i5#e0@*+Y!+#)FQQ V040EGco~>LN;!bq7#O%17y+5a5ibA$ literal 0 HcmV?d00001 diff --git a/dtls-2.0.9/fuzz/corpus/80123a693544437c5d58878cf7aac8a281ec658c-8 b/dtls-2.0.9/fuzz/corpus/80123a693544437c5d58878cf7aac8a281ec658c-8 new file mode 100644 index 0000000000000000000000000000000000000000..5cfd7fc723801693eeaff4a984568539d1ca6b4c GIT binary patch literal 25 ccmWf8?w3=XlgUt$pRbU^$H2f43B(`(0AZ{JMF0Q* literal 0 HcmV?d00001 diff --git a/dtls-2.0.9/fuzz/corpus/8104833886e77f44f198916bdf2cc0aeafa6b59a-30 b/dtls-2.0.9/fuzz/corpus/8104833886e77f44f198916bdf2cc0aeafa6b59a-30 new file mode 100644 index 0000000000000000000000000000000000000000..312f645af4d837dcf59725599a9eb6863b54a7a3 GIT binary patch literal 76 zcmWf;xOe~i{qI3UwE`mpgR!Y80|*!!GcY)mFj$(Jo0ymy8Cw|oWR{fVq{0LkEP-lx S7aQo*@ND0|1d+2(thH literal 0 HcmV?d00001 diff --git a/dtls-2.0.9/fuzz/corpus/88e0f2195b7c21004d87538f58bd7b751aeb79c7-27 b/dtls-2.0.9/fuzz/corpus/88e0f2195b7c21004d87538f58bd7b751aeb79c7-27 new file mode 100644 index 0000000000000000000000000000000000000000..0dc4e68fa26fc25814f53c541a48f8b0d33960fe GIT binary patch literal 76 zcmWf;xOe~i{qI3UwE`mpgR!ZpQ9T2YHfCUOC}FTPF*h+WH8Qp^^vNtK$w_t1OEF>) W<^W0n)i5wHG4L`laImq0iT?n>;}Y=z literal 0 HcmV?d00001 diff --git a/dtls-2.0.9/fuzz/corpus/8963740cfedced726a1579328b9aa58a7d348c2c-29 b/dtls-2.0.9/fuzz/corpus/8963740cfedced726a1579328b9aa58a7d348c2c-29 new file mode 100644 index 0000000000000000000000000000000000000000..94ad46a1dce231bf1e43095e37ce2879fee0a8c4 GIT binary patch literal 76 zcmWf;xOe~i{qI3UwE`mpgR!Y80|*!!GcY)mFj$(Ho0#w#8Cw|oWR{fVq`Ky%7%>QQ S042bxm>7VnIoMdi#D4&mnG!$% literal 0 HcmV?d00001 diff --git a/dtls-2.0.9/fuzz/corpus/905578265b19677b3c83aad3169ed0b9cae91a0f-20 b/dtls-2.0.9/fuzz/corpus/905578265b19677b3c83aad3169ed0b9cae91a0f-20 new file mode 100644 index 0000000000000000000000000000000000000000..7327516240b6ad2a4a8da07f82bc50b66a6d8fbf GIT binary patch literal 33 lcmWf;xOe;e{pWY@X8-~o1_q9j1`vyZgPB2`fsv7c0RYAZ2Y&zn literal 0 HcmV?d00001 diff --git a/dtls-2.0.9/fuzz/corpus/91ad828e4650d737c8fab0447f83b6380bb045a2-37 b/dtls-2.0.9/fuzz/corpus/91ad828e4650d737c8fab0447f83b6380bb045a2-37 new file mode 100644 index 0000000000000000000000000000000000000000..a8bb45c2aa81c6f3de138aa489d120a7eacbeec9 GIT binary patch literal 76 zcmWf8?w3>Sm#LnapRb_6$iQH1%ESNy#>NZ`B_%nj0eL1SrXYrEUWyUJ|NjgOmV#h; Q23`gR4u%8TAPAv#0n&LA(*OVf literal 0 HcmV?d00001 diff --git a/dtls-2.0.9/fuzz/corpus/92d652cb10701472585fadb89dee2ab05f4baa3f-16 b/dtls-2.0.9/fuzz/corpus/92d652cb10701472585fadb89dee2ab05f4baa3f-16 new file mode 100644 index 0000000000000000000000000000000000000000..c3c0284dca800d9c3271548de0d12aef5295a375 GIT binary patch literal 33 mcmWf8?w8Y$lPT~oKVKn-mw|z!M1cVWI2ah18Taqix(5KKF9^N> literal 0 HcmV?d00001 diff --git a/dtls-2.0.9/fuzz/corpus/9810ba71e7068b2752d4fc80ea1071957b4b20e4-22 b/dtls-2.0.9/fuzz/corpus/9810ba71e7068b2752d4fc80ea1071957b4b20e4-22 new file mode 100644 index 0000000000000000000000000000000000000000..a599bb0c6fd686f02574764c8c08e48088c035ca GIT binary patch literal 76 zcmWf8?w3>S*R8IUpRbU^#K2%|YHC!^0Hlq~O)ShTEltc#OiYc8Eew4!OG{tMgy literal 0 HcmV?d00001 diff --git a/dtls-2.0.9/fuzz/corpus/995d8ae8db6dad3c5851077207ada893bf856830-25 b/dtls-2.0.9/fuzz/corpus/995d8ae8db6dad3c5851077207ada893bf856830-25 new file mode 100644 index 0000000000000000000000000000000000000000..3073016bd6290cd11b1f99a4df29f51db6f6b543 GIT binary patch literal 76 zcmWf8?w3>S*R5`lpRbU^#K2%|YHC!^0Hlq~O&H89Eltc#OiYc8Eew4!OGaQo*@ND0|1e92(75&I2cT!kbwaJwRsXw literal 0 HcmV?d00001 diff --git a/dtls-2.0.9/fuzz/corpus/9bc991375786a265c38c8553183807be67827625-18 b/dtls-2.0.9/fuzz/corpus/9bc991375786a265c38c8553183807be67827625-18 new file mode 100644 index 0000000000000000000000000000000000000000..f7dae88d4293cb2beddceff9fef6458898334f39 GIT binary patch literal 33 mcmWf;xOe;e{pWY@X8-~o1_q9jhWvktK$@9BoPm*n;T{0qWeO?) literal 0 HcmV?d00001 diff --git a/dtls-2.0.9/fuzz/corpus/9bddfbdd2ed2e780103d5d34662106bd4ef8eb80-6 b/dtls-2.0.9/fuzz/corpus/9bddfbdd2ed2e780103d5d34662106bd4ef8eb80-6 new file mode 100644 index 0000000000000000000000000000000000000000..e7214c8f88d4de2d4cb2e34b73da996f745587b4 GIT binary patch literal 25 YcmWf8?w3=XlgUt$pRbU^00b}w0BO$#yZ`_I literal 0 HcmV?d00001 diff --git a/dtls-2.0.9/fuzz/corpus/9beed258dfb4aa4ef102c1b4984699303e737d00-38 b/dtls-2.0.9/fuzz/corpus/9beed258dfb4aa4ef102c1b4984699303e737d00-38 new file mode 100644 index 0000000000000000000000000000000000000000..ffd687cb5ab75bf1a29ce4dd482ef88bb18115ce GIT binary patch literal 76 zcmWf;xOe~i{qI3UwE`mpgRv) a<^W0n)i5wHG4L`laImrdXLVpuSm#MCvpRbU^%fP@}!T<)moQyC4(LFj$(Jo0ymy8Cw|oWR{fVq{0LkEP-m6 R7=Ri$N*LH!|1&fI`2d{|62|}l literal 0 HcmV?d00001 diff --git a/dtls-2.0.9/fuzz/corpus/a2ef40165d921e7d8b8c622348b0f3ba772bb45b-22 b/dtls-2.0.9/fuzz/corpus/a2ef40165d921e7d8b8c622348b0f3ba772bb45b-22 new file mode 100644 index 0000000000000000000000000000000000000000..68fe61afa5a6c0b7d81a2e4bdeca3feb1852fc1e GIT binary patch literal 76 zcmWf8?w3>S*R8IUpRbU^%fMi4YHC!^0Hlq~O)ShTEltc#OiYc8Eew4!OGqQG#M0Z212ure?({bv9Gl~e~8 literal 0 HcmV?d00001 diff --git a/dtls-2.0.9/fuzz/corpus/a646db15452695437f7b7bc3b65c5748dd9cbee4-36 b/dtls-2.0.9/fuzz/corpus/a646db15452695437f7b7bc3b65c5748dd9cbee4-36 new file mode 100644 index 0000000000000000000000000000000000000000..b0ddd6c5050acbfc0de3e2b9aace6d71a85c85c9 GIT binary patch literal 76 zcmWf;xOe~i{qI3UwE`mpgRvqQG#M0Z212ure?({m%pdmAwam literal 0 HcmV?d00001 diff --git a/dtls-2.0.9/fuzz/corpus/a8e636f3b54cfd873b3d21cff150543a9e10f4de-13 b/dtls-2.0.9/fuzz/corpus/a8e636f3b54cfd873b3d21cff150543a9e10f4de-13 new file mode 100644 index 0000000000000000000000000000000000000000..02d6b250c1eed2b0bbc86be2d22fc95c8235d294 GIT binary patch literal 28 ccmWf8?w3=XlgUt$&%?mL3`8#&zyL%80BQ{d$N&HU literal 0 HcmV?d00001 diff --git a/dtls-2.0.9/fuzz/corpus/aa8beeff31520b5cbf509bc5efe4fa194a990fed-31 b/dtls-2.0.9/fuzz/corpus/aa8beeff31520b5cbf509bc5efe4fa194a990fed-31 new file mode 100644 index 0000000000000000000000000000000000000000..25db5b5d5c77403a9cfd3bec261c06fcb689190c GIT binary patch literal 76 zcmWf;xOe~i{qI3UwE`mpgR!Y80|*!!GcY)mFj$(Ho0ymy8Cw|oWR{fVq`Ky%7%>QQ W042bxm>76oFmM31F);8kFaiLhiV_+C literal 0 HcmV?d00001 diff --git a/dtls-2.0.9/fuzz/corpus/ac3e9b5146d2220644dbca14d2dec64d23a82fd6-24 b/dtls-2.0.9/fuzz/corpus/ac3e9b5146d2220644dbca14d2dec64d23a82fd6-24 new file mode 100644 index 0000000000000000000000000000000000000000..465ee4dc0aa26af667c25ec46595b317783e4a66 GIT binary patch literal 76 zcmWf8?w3>S*R8IUpRbU^#K2%|YHC!^0Hlq~O&H89Eltc#OiYc8Eew4!OGaQo*@ND0|1eX2)6(L literal 0 HcmV?d00001 diff --git a/dtls-2.0.9/fuzz/corpus/b28051b6fc87a2b74a765b237c697e0728f1bccf-12 b/dtls-2.0.9/fuzz/corpus/b28051b6fc87a2b74a765b237c697e0728f1bccf-12 new file mode 100644 index 0000000000000000000000000000000000000000..c67a6dd5d52ae4bb684917322ecc9c8d3494448f GIT binary patch literal 28 fcmWf8?w7-ulgUt$pRd5k!@$7&f&oG^Gspq}XY>U~ literal 0 HcmV?d00001 diff --git a/dtls-2.0.9/fuzz/corpus/b3c74f6100a87eb3ad15d44be8df465d490fb9bd-32 b/dtls-2.0.9/fuzz/corpus/b3c74f6100a87eb3ad15d44be8df465d490fb9bd-32 new file mode 100644 index 0000000000000000000000000000000000000000..d5c05f8caccb89cf3c68f53466e106296c5addb2 GIT binary patch literal 76 zcmWf;xOe~i{qI3UwE`mpgRv*vxxDIgbWncmT%{3H= literal 0 HcmV?d00001 diff --git a/dtls-2.0.9/fuzz/corpus/b43bde2b9ea6f9d171156e4ba3d084444294625c-6 b/dtls-2.0.9/fuzz/corpus/b43bde2b9ea6f9d171156e4ba3d084444294625c-6 new file mode 100644 index 0000000000000000000000000000000000000000..73df8274d1d1598623fd6c0de6b573b34f39767d GIT binary patch literal 25 dcmWf;xOe~i{qI4<{`cGr3=9tY-!r5DX#m9*3^o7& literal 0 HcmV?d00001 diff --git a/dtls-2.0.9/fuzz/corpus/b485961b2eb34df99b22d66f377aeaf6bd87e0a6-36 b/dtls-2.0.9/fuzz/corpus/b485961b2eb34df99b22d66f377aeaf6bd87e0a6-36 new file mode 100644 index 0000000000000000000000000000000000000000..bcdc5169863d624491432c122a0a56cdd75a4598 GIT binary patch literal 76 zcmWf;xPSlq{qOeziE0H#1_onOQw9()HfAVsC}FTPH8(VBFfz6<^vNtK$w_6f^e{Iu RF*Rba1gqj=Foi<~1^~I25{m!; literal 0 HcmV?d00001 diff --git a/dtls-2.0.9/fuzz/corpus/b4912597376e6edf2985267fe64d170977173481-1 b/dtls-2.0.9/fuzz/corpus/b4912597376e6edf2985267fe64d170977173481-1 new file mode 100644 index 0000000..b809245 --- /dev/null +++ b/dtls-2.0.9/fuzz/corpus/b4912597376e6edf2985267fe64d170977173481-1 @@ -0,0 +1,3 @@ +.local + +&Y \ No newline at end of file diff --git a/dtls-2.0.9/fuzz/corpus/b4ee5c1737fe829bfa1c8d6abcb2166c1b74effd-21 b/dtls-2.0.9/fuzz/corpus/b4ee5c1737fe829bfa1c8d6abcb2166c1b74effd-21 new file mode 100644 index 0000000000000000000000000000000000000000..d8363cac947e7c74f3857bb63243deb139502f14 GIT binary patch literal 38 ocmWf8?w3>Sm#MCvpRbU^%fP@}!T<(9CPxVe8|!~o2L?q30ItLbDF6Tf literal 0 HcmV?d00001 diff --git a/dtls-2.0.9/fuzz/corpus/b4f24eee8a1d42ac1dc868e4d53b608f3746a2d7-31 b/dtls-2.0.9/fuzz/corpus/b4f24eee8a1d42ac1dc868e4d53b608f3746a2d7-31 new file mode 100644 index 0000000000000000000000000000000000000000..728f0f8a2085b12cbf2ac20e71377b9a7690b0fe GIT binary patch literal 76 zcmWf;xOe~i{qI3UwE`mpgRv*vxxb`wIvHk}D%hVLY literal 0 HcmV?d00001 diff --git a/dtls-2.0.9/fuzz/corpus/b5c30ace1906dea8c5cf2fb4b7558563a2df978b-19 b/dtls-2.0.9/fuzz/corpus/b5c30ace1906dea8c5cf2fb4b7558563a2df978b-19 new file mode 100644 index 0000000000000000000000000000000000000000..5e1b7fe18fb50aca5a7ef9444ce6e7830917981d GIT binary patch literal 33 lcmWf;xOe;e{pWY@X8-~o1_q9j1`vyZgPB2`fsv8n9stJV2weaG literal 0 HcmV?d00001 diff --git a/dtls-2.0.9/fuzz/corpus/b64aecc1f27577b6c2efd550a8dd1b0f96054f7c-25 b/dtls-2.0.9/fuzz/corpus/b64aecc1f27577b6c2efd550a8dd1b0f96054f7c-25 new file mode 100644 index 0000000000000000000000000000000000000000..c0f2676b0020e2202938caa8ca54b9abba86dee1 GIT binary patch literal 38 qcmWf8?w3>Sm#NPBpOrO-mw|z|gaHhAIT<)hIM`VKGbk`HH~;{wj0Y9~ literal 0 HcmV?d00001 diff --git a/dtls-2.0.9/fuzz/corpus/b6f83f0c490f9fbea7ea7b9574232e8fd90194aa-18 b/dtls-2.0.9/fuzz/corpus/b6f83f0c490f9fbea7ea7b9574232e8fd90194aa-18 new file mode 100644 index 0000000000000000000000000000000000000000..b47a4add1a28d994dd25e1a39c7c16ed4b816625 GIT binary patch literal 33 icmWf8?w3>Sm#MCvpRbU^%fP@;2Qod+`j literal 0 HcmV?d00001 diff --git a/dtls-2.0.9/fuzz/corpus/b7b653694d804d41294e46bb4aff34f2fc93f48d-19 b/dtls-2.0.9/fuzz/corpus/b7b653694d804d41294e46bb4aff34f2fc93f48d-19 new file mode 100644 index 0000000000000000000000000000000000000000..d51b4090313f7001bf0f2bf23174b4166fa71d42 GIT binary patch literal 33 lcmWf8?w3=XlgUt$FQ$;g&A`A>qQC$G91IMs3=B*R_W+3d1{nYV literal 0 HcmV?d00001 diff --git a/dtls-2.0.9/fuzz/corpus/b9bd6d81380956a8a8f08c551f7a1c8e4b769f01-33 b/dtls-2.0.9/fuzz/corpus/b9bd6d81380956a8a8f08c551f7a1c8e4b769f01-33 new file mode 100644 index 0000000000000000000000000000000000000000..02576bdeab226f3822519272ec2cbeee94788e30 GIT binary patch literal 76 zcmWf;xOe~i{qI3UwE`mpgR!Y80|*!!Gn6=#Fj$(J8yYnj8Cw|oWR{fVq%v4~n46fG U8ZlUcRdF$JZ~z%hObj4h0J32b$p8QV literal 0 HcmV?d00001 diff --git a/dtls-2.0.9/fuzz/corpus/ba0aeff9d6e84d6d0a54b40f674338489fe86d29-35 b/dtls-2.0.9/fuzz/corpus/ba0aeff9d6e84d6d0a54b40f674338489fe86d29-35 new file mode 100644 index 0000000000000000000000000000000000000000..dd3d3239aa87e13d6ac836392532211ddeb25f4d GIT binary patch literal 76 zcmWf;xOe~i{qI3UwE`mpgR!Y80|*!!Gn6=#Fj$(J8yYnj8Cw|oWR{fVq%v4~n46fG X8ZlS`)fgEV7+G2A8=8T2FfafB#l90n literal 0 HcmV?d00001 diff --git a/dtls-2.0.9/fuzz/corpus/ba8f7331369766ec42d305afd13f74bd5c9f7598-26 b/dtls-2.0.9/fuzz/corpus/ba8f7331369766ec42d305afd13f74bd5c9f7598-26 new file mode 100644 index 0000000000000000000000000000000000000000..b630e830a7215224d50e10dc32a5357fd712ddd5 GIT binary patch literal 76 zcmWf;xOe~i{qI3UwE`mpgR!ZpQ9T2YHfCUOC}FTPF*h+WH8Qp^^vNtK$w_t1OEF?# W-~dVh)i5#eg4t}W{~1^q6gdFN>Jw}L literal 0 HcmV?d00001 diff --git a/dtls-2.0.9/fuzz/corpus/bab42319f9d989d1344ff4621f82c3eb950f01b8-4 b/dtls-2.0.9/fuzz/corpus/bab42319f9d989d1344ff4621f82c3eb950f01b8-4 new file mode 100644 index 0000000000000000000000000000000000000000..b5cfc93e575c3ba92ab1748e7cf8660cb439bab6 GIT binary patch literal 25 fcmWf;xOe~i{qI4<{`c=07#JM(zh~I%uJ;}Q(@GB4 literal 0 HcmV?d00001 diff --git a/dtls-2.0.9/fuzz/corpus/bacecfa089ed936799b5ec00ab80f2c234ee6488-19 b/dtls-2.0.9/fuzz/corpus/bacecfa089ed936799b5ec00ab80f2c234ee6488-19 new file mode 100644 index 0000000000000000000000000000000000000000..e954f79a1e95b0f047f29eced1ed8bf059827c3b GIT binary patch literal 33 kcmWf8?w3=XlgUt$FQ$;g&A`A>qQC$G91IMsj6iS?0Eq(z8vpSm#MCvpRbU^%fP@}!T<(9CT9uzAJ+e@4h)J60Je4r9{>OV literal 0 HcmV?d00001 diff --git a/dtls-2.0.9/fuzz/corpus/c26326aa05dea63170e6429a64465e9c48fc4ba6-20 b/dtls-2.0.9/fuzz/corpus/c26326aa05dea63170e6429a64465e9c48fc4ba6-20 new file mode 100644 index 0000000000000000000000000000000000000000..f062a946fd2c23c8a54944db29390533046ade66 GIT binary patch literal 33 mcmWf8?w3=XlgUt$FQ$;g&A`A>qQC$G91IMs3=B;F?*agd_y-pN literal 0 HcmV?d00001 diff --git a/dtls-2.0.9/fuzz/corpus/c341f33f77b845bbeb7f2e4cdc20072a370b81bb-19 b/dtls-2.0.9/fuzz/corpus/c341f33f77b845bbeb7f2e4cdc20072a370b81bb-19 new file mode 100644 index 0000000000000000000000000000000000000000..7dbee3b0f4654930f4d2e9d3bdff9756d3eb0858 GIT binary patch literal 38 kcmWf8?w3>Sm#MCvpRbU^%fP@}!T<(9CMN?YBLv(70Hdh}EC2ui literal 0 HcmV?d00001 diff --git a/dtls-2.0.9/fuzz/corpus/c5ab6cb91cad5d95c1eed18fc9055ca5cfa03401-36 b/dtls-2.0.9/fuzz/corpus/c5ab6cb91cad5d95c1eed18fc9055ca5cfa03401-36 new file mode 100644 index 0000000000000000000000000000000000000000..79829755869cf65351da81dd472ac47ac7e2cafa GIT binary patch literal 76 zcmWf8?w9j^|NFiB-|w$hU}Ru0HZ^4c0b^r^5{D87OH*?blLjMW3qzmGl9HTM21^5= WfTSm#MCvpRbU^%fP@}!T<(9CMT-{+kaM8*8i*y0I!4x+W-In literal 0 HcmV?d00001 diff --git a/dtls-2.0.9/fuzz/corpus/ca7e5b747b90d4cc886c3e68582eb809672f9343-24 b/dtls-2.0.9/fuzz/corpus/ca7e5b747b90d4cc886c3e68582eb809672f9343-24 new file mode 100644 index 0000000000000000000000000000000000000000..7cb44c1362918db6a2abe36108eeac9fed48b70c GIT binary patch literal 76 zcmWf;xOe~i{qI3UwE`mpgR!ZiQ9T2YHfCVpC}FTPF*h+WH8Qp^^vNtK$w_t1OEH2- T0M#%s@PgTFtp8aZ7)&_;yYmxs literal 0 HcmV?d00001 diff --git a/dtls-2.0.9/fuzz/corpus/caf20a50754c9f4885ff4872cfdb5badfafa0eab-2 b/dtls-2.0.9/fuzz/corpus/caf20a50754c9f4885ff4872cfdb5badfafa0eab-2 new file mode 100644 index 0000000..179235b --- /dev/null +++ b/dtls-2.0.9/fuzz/corpus/caf20a50754c9f4885ff4872cfdb5badfafa0eab-2 @@ -0,0 +1 @@ +\v/ソソY \ No newline at end of file diff --git a/dtls-2.0.9/fuzz/corpus/cc0dfdb3fe2c6c450c8353fb951f0068c2da25c3-24 b/dtls-2.0.9/fuzz/corpus/cc0dfdb3fe2c6c450c8353fb951f0068c2da25c3-24 new file mode 100644 index 0000000000000000000000000000000000000000..a469df8266ccf3721cc6583095871a500ca0514b GIT binary patch literal 38 qcmWf8?w3>Sm#NPBpOrO-mw|z|gaHhAIT<)hIM`VKGcYnRH~;{w5C-=E literal 0 HcmV?d00001 diff --git a/dtls-2.0.9/fuzz/corpus/cc57cf224581b2055e3e509f8ddaf10204099d72-29 b/dtls-2.0.9/fuzz/corpus/cc57cf224581b2055e3e509f8ddaf10204099d72-29 new file mode 100644 index 0000000000000000000000000000000000000000..7a08e52525825793c67abb92bed492fa0b0e3882 GIT binary patch literal 76 zcmWf;xOe~i{qI3UwE`mpgR!Y80|*!!GcY)mFj$(Ho0ymy8Cw|oWR{fVq`Ky%7%>QQ X042bxm>75&7&sXC4(w%QVEqpOqYD$W literal 0 HcmV?d00001 diff --git a/dtls-2.0.9/fuzz/corpus/cd6fd1f976ae2f9e31733919f070988d5946cf18-25 b/dtls-2.0.9/fuzz/corpus/cd6fd1f976ae2f9e31733919f070988d5946cf18-25 new file mode 100644 index 0000000000000000000000000000000000000000..91e35dbdfe3e9663901995c1f41476b9db711683 GIT binary patch literal 76 zcmWf;xOe~i{qI3UwE`mpgR!ZiQ9T2YHfCVpC}FTPF*h+WH8Qp^^vNtK$w_t1OEH2- U0M#%s@G`Km{%3VyFa==_0KTRZaR2}S literal 0 HcmV?d00001 diff --git a/dtls-2.0.9/fuzz/corpus/ce04f52927639b8f845dd01a25ff06d61dbb7736-19 b/dtls-2.0.9/fuzz/corpus/ce04f52927639b8f845dd01a25ff06d61dbb7736-19 new file mode 100644 index 0000000000000000000000000000000000000000..16397909a02bfee4dd36e66403f6786c8f128e88 GIT binary patch literal 33 mcmWe*+`Ik#{`0%{GXMb(0|Q4%L;k-+AkEAm&Tx@|;T{0Tr3xwl literal 0 HcmV?d00001 diff --git a/dtls-2.0.9/fuzz/corpus/cfe9539fdc29f9bcdf123394ffb098838a5d8b83-29 b/dtls-2.0.9/fuzz/corpus/cfe9539fdc29f9bcdf123394ffb098838a5d8b83-29 new file mode 100644 index 0000000000000000000000000000000000000000..f32ed4a61479f5137afe42558dbd77ee5f4b4b94 GIT binary patch literal 76 zcmWf;xOe~i{qI3UwE`mpgR!Y80|*!!GcY)mFj$(Jo0ymy8Cw|oWR{fVq{0LkEP-m6 S7$?B5FjL*NLr literal 0 HcmV?d00001 diff --git a/dtls-2.0.9/fuzz/corpus/d1fc43b23d31daa77b1c9b4f8930d2f3a9754287-31 b/dtls-2.0.9/fuzz/corpus/d1fc43b23d31daa77b1c9b4f8930d2f3a9754287-31 new file mode 100644 index 0000000000000000000000000000000000000000..922dbdf1445c1215d7429ac2888aab99479d660d GIT binary patch literal 76 zcmWf;xOe~i{qI3UwE`mpgR!Y80|*!!Gn6=#Fj$(Jo0ymy8Cw|oWR{fVq{0LkEP-m6 P7QQ a03{e0{xUEyG4L`la4_&4*vr7g`X2zZ+7z(> literal 0 HcmV?d00001 diff --git a/dtls-2.0.9/fuzz/corpus/d78ab9295d2782c20cb99674622bde4e92359b16-15 b/dtls-2.0.9/fuzz/corpus/d78ab9295d2782c20cb99674622bde4e92359b16-15 new file mode 100644 index 0000000000000000000000000000000000000000..121e13d2930e1a26b254dec968ecca2a6407379f GIT binary patch literal 33 lcmWf8?w3=XlgUt$FQ$;g&A`A>qQC$G91IMs3=B*L3;>9I1#18R literal 0 HcmV?d00001 diff --git a/dtls-2.0.9/fuzz/corpus/d8f3a31fb0304017eb8466e958c843865a1d0c2b-14 b/dtls-2.0.9/fuzz/corpus/d8f3a31fb0304017eb8466e958c843865a1d0c2b-14 new file mode 100644 index 0000000000000000000000000000000000000000..94ac533d481d4c973812a1cb6eb656cff49568bc GIT binary patch literal 28 dcmWf8?w3=XlgUt$pRd3mz`(%#f&mObGyrqv23P<9 literal 0 HcmV?d00001 diff --git a/dtls-2.0.9/fuzz/corpus/d97dc4bb804a0d7bcd92f1abf81fb604caeef3db-18 b/dtls-2.0.9/fuzz/corpus/d97dc4bb804a0d7bcd92f1abf81fb604caeef3db-18 new file mode 100644 index 0000000000000000000000000000000000000000..50f8931b8da02d4043178e3ef9c205a24b1645eb GIT binary patch literal 33 mcmWf8?w8Y`kSXvlKVKn^mw|z!M1cVWIG7o77#SF}?g0Rs6bDoQ literal 0 HcmV?d00001 diff --git a/dtls-2.0.9/fuzz/corpus/d9c2f5fc766a4d8b70c20b2c7bb17f662821a18d-20 b/dtls-2.0.9/fuzz/corpus/d9c2f5fc766a4d8b70c20b2c7bb17f662821a18d-20 new file mode 100644 index 0000000000000000000000000000000000000000..fb8b4549e18baa1ee66a6ab3115cdd76774d02ac GIT binary patch literal 33 kcmWf8?w3>Sm#MCvpRbU^%fP@QQ X042bxm>75&7&sXC4(w%MV*L*QqYV?Z literal 0 HcmV?d00001 diff --git a/dtls-2.0.9/fuzz/corpus/dc8dd2c7a89d009af1cc9d1dab9c7f030db09fee-28 b/dtls-2.0.9/fuzz/corpus/dc8dd2c7a89d009af1cc9d1dab9c7f030db09fee-28 new file mode 100644 index 0000000000000000000000000000000000000000..a794a4fe83f9d467b872da94f1b94746f73ea57b GIT binary patch literal 76 zcmWf;xOe~i{qI3UwE`mpgR!ZpQ9T2YHfCUOC}FTPF*h+WH8Qp^^vNtK$w>vuq%a6` Y040Dd1_mYuUIqpZHdY2k4mLIk0H?AMCjbBd literal 0 HcmV?d00001 diff --git a/dtls-2.0.9/fuzz/corpus/dcac1a5ba7d6511532589fbceb771fd71f23ebeb-23 b/dtls-2.0.9/fuzz/corpus/dcac1a5ba7d6511532589fbceb771fd71f23ebeb-23 new file mode 100644 index 0000000000000000000000000000000000000000..6d94b237c581f430c47fa278797e5c00bf37802d GIT binary patch literal 38 ncmWf8?w3>Sm#MCvpRbU^%fP@}!T<(9CT9r;8|!~oAaVczu;>U9 literal 0 HcmV?d00001 diff --git a/dtls-2.0.9/fuzz/corpus/dcc90b5ab9129ee3effd438c0a86bfe599ccfe17-8 b/dtls-2.0.9/fuzz/corpus/dcc90b5ab9129ee3effd438c0a86bfe599ccfe17-8 new file mode 100644 index 0000000000000000000000000000000000000000..67c4addf8285f6c9c17aede60fee269d309d34d2 GIT binary patch literal 25 bcmWf8?w3=XlgUt$pRW+g%)kI<^XvhYr literal 0 HcmV?d00001 diff --git a/dtls-2.0.9/fuzz/corpus/dd5c198fd08276fdba3f48884659199dceeaa2ac-2 b/dtls-2.0.9/fuzz/corpus/dd5c198fd08276fdba3f48884659199dceeaa2ac-2 new file mode 100644 index 0000000..871fdba --- /dev/null +++ b/dtls-2.0.9/fuzz/corpus/dd5c198fd08276fdba3f48884659199dceeaa2ac-2 @@ -0,0 +1 @@ +.lslice length too larg \ No newline at end of file diff --git a/dtls-2.0.9/fuzz/corpus/e0d111660feb6004db7815eb0231fdb369517970-11 b/dtls-2.0.9/fuzz/corpus/e0d111660feb6004db7815eb0231fdb369517970-11 new file mode 100644 index 0000000000000000000000000000000000000000..370ba20fbf560b7c61e3e5aba1cea8b253b84c93 GIT binary patch literal 33 kcmWf8?w3=XlgUt$pRbU^&%nR|W&ml%{d?c<->c040JV+^?f?J) literal 0 HcmV?d00001 diff --git a/dtls-2.0.9/fuzz/corpus/e11fc30ee640e45e8185f384f9a116cf2cb75852-8 b/dtls-2.0.9/fuzz/corpus/e11fc30ee640e45e8185f384f9a116cf2cb75852-8 new file mode 100644 index 0000000000000000000000000000000000000000..87bca42eb140d5f6d685b9397bbbd1f90e0f2b5f GIT binary patch literal 25 gcmWf;xOe;e{pa`YXV}00Jre^1gTwy!3@Jbw0J5qJ761SM literal 0 HcmV?d00001 diff --git a/dtls-2.0.9/fuzz/corpus/e124a66686755a3fe635b2bb6dc05849238ff474-28 b/dtls-2.0.9/fuzz/corpus/e124a66686755a3fe635b2bb6dc05849238ff474-28 new file mode 100644 index 0000000000000000000000000000000000000000..7b768363aae4171e7b1fce4fb16df200c72220f0 GIT binary patch literal 76 zcmWf;xOe~i{qI3UwE`mpgR!Y80|*!!GcY)mFj$(Ho0ymy8Cw|oWR{fVq`Ky%7%>QQ T042bxm>75&7&zEi!Nh+6n)wny literal 0 HcmV?d00001 diff --git a/dtls-2.0.9/fuzz/corpus/e17dcda547abfa37685bb9d570a7bf9c4a34affc-35 b/dtls-2.0.9/fuzz/corpus/e17dcda547abfa37685bb9d570a7bf9c4a34affc-35 new file mode 100644 index 0000000000000000000000000000000000000000..62e99543bccf15856a1bfc55c9e4f87ccdbea669 GIT binary patch literal 76 zcmWf;xOe~i{qI3UwE`mpgRvQQ X042bxm>75&7&sWX4(w%QVEqpOqVE&0 literal 0 HcmV?d00001 diff --git a/dtls-2.0.9/fuzz/corpus/e8ad70294942e6f8c25bb01fd4443cfba4fb0308-19 b/dtls-2.0.9/fuzz/corpus/e8ad70294942e6f8c25bb01fd4443cfba4fb0308-19 new file mode 100644 index 0000000000000000000000000000000000000000..f5f4f6a69d4dba3b3b3118de1f9556c32c79982c GIT binary patch literal 38 jcmWf8?w3>Sm#MCvpRbU^%fP@}!T<(9CMN?YBNPAtqRa(P literal 0 HcmV?d00001 diff --git a/dtls-2.0.9/fuzz/corpus/e9895a39481476548887cbbb88835ba4318e41af-33 b/dtls-2.0.9/fuzz/corpus/e9895a39481476548887cbbb88835ba4318e41af-33 new file mode 100644 index 0000000000000000000000000000000000000000..4a1832a8d1e2b63a20959720f0bef5c7a4b79975 GIT binary patch literal 76 zcmWf;xOe~i{qI3UwE`mpgRvS*R8IUpRbU^#K2%|YRUit#zy8Q3}%*=Cgvt4rbfmVhCZ1kB{`|CdFl#j Y`9%sPKqb5krVRBA93>2FU>&9m0H#q7UjP6A literal 0 HcmV?d00001 diff --git a/dtls-2.0.9/fuzz/corpus/f248a7b971b1fd07ea978e776fda73fee276d36d-17 b/dtls-2.0.9/fuzz/corpus/f248a7b971b1fd07ea978e776fda73fee276d36d-17 new file mode 100644 index 0000000000000000000000000000000000000000..f27cbf5e3483567b3029af75d4a3c7d834668d90 GIT binary patch literal 33 mcmWf8?w8Y$lPT~oKVKn-mw|z!M1cVWI2ah1llSk{x(5KM83`Z& literal 0 HcmV?d00001 diff --git a/dtls-2.0.9/fuzz/corpus/f3fc999fcd5f3f9f4d4cf2c4151d0bc6ef73c3cb-1 b/dtls-2.0.9/fuzz/corpus/f3fc999fcd5f3f9f4d4cf2c4151d0bc6ef73c3cb-1 new file mode 100644 index 0000000..7a0244b --- /dev/null +++ b/dtls-2.0.9/fuzz/corpus/f3fc999fcd5f3f9f4d4cf2c4151d0bc6ef73c3cb-1 @@ -0,0 +1 @@ +decoding uint \ No newline at end of file diff --git a/dtls-2.0.9/fuzz/corpus/f8781259866be1553ac9625d18ff25ce354776ec-23 b/dtls-2.0.9/fuzz/corpus/f8781259866be1553ac9625d18ff25ce354776ec-23 new file mode 100644 index 0000000000000000000000000000000000000000..76fc3f8a9a489f98f4d5ea8a61efd44753895b0d GIT binary patch literal 76 zcmWf;xOe~i{qI3UwE`mpgR!ZpQ9T2YHfCVpC}FTPF*h+WH8Qp^^vNtK$w_t1OEH2- T0M#%s@PgTFtp8aZ7!)}Gz*7@* literal 0 HcmV?d00001 diff --git a/dtls-2.0.9/fuzz/corpus/fa9ec5dd9ba00a696cb5217fd7455fe79c6610e4-18 b/dtls-2.0.9/fuzz/corpus/fa9ec5dd9ba00a696cb5217fd7455fe79c6610e4-18 new file mode 100644 index 0000000000000000000000000000000000000000..a8c7d9398983ddee9977fc9724a5f609cf06f7fc GIT binary patch literal 33 kcmWf8?w8Y$lPT~oKVKn-mw|z!M1cVWI2ah18G+y)0HLu58vpqQC$G91IMs3=B-#DFBGq1Sm#MCvpRbU^%fP@<(vbfz5lAyLFf#1lt91_mtndl} literal 0 HcmV?d00001 diff --git a/dtls-2.0.9/go.mod b/dtls-2.0.9/go.mod new file mode 100644 index 0000000..d3c660a --- /dev/null +++ b/dtls-2.0.9/go.mod @@ -0,0 +1,12 @@ +module github.com/pion/dtls/v2 + +require ( + github.com/pion/logging v0.2.2 + github.com/pion/transport v0.12.3 + github.com/pion/udp v0.1.1 + golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2 + golang.org/x/net v0.0.0-20210331212208-0fccb6fa2b5c + golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 +) + +go 1.13 diff --git a/dtls-2.0.9/go.sum b/dtls-2.0.9/go.sum new file mode 100644 index 0000000..df09e4c --- /dev/null +++ b/dtls-2.0.9/go.sum @@ -0,0 +1,40 @@ +github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/pion/logging v0.2.2 h1:M9+AIj/+pxNsDfAT64+MAVgJO0rsyLnoJKCqf//DoeY= +github.com/pion/logging v0.2.2/go.mod h1:k0/tDVsRCX2Mb2ZEmTqNa7CWsQPc+YYCB7Q+5pahoms= +github.com/pion/transport v0.12.2/go.mod h1:N3+vZQD9HlDP5GWkZ85LohxNsDcNgofQmyL6ojX5d8Q= +github.com/pion/transport v0.12.3 h1:vdBfvfU/0Wq8kd2yhUMSDB/x+O4Z9MYVl2fJ5BT4JZw= +github.com/pion/transport v0.12.3/go.mod h1:OViWW9SP2peE/HbwBvARicmAVnesphkNkCVZIWJ6q9A= +github.com/pion/udp v0.1.1 h1:8UAPvyqmsxK8oOjloDk4wUt63TzFe9WEJkg5lChlj7o= +github.com/pion/udp v0.1.1/go.mod h1:6AFo+CMdKQm7UiA0eUPA8/eVCTx8jBIITLZHc9DWX5M= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2 h1:It14KIkyBFYkHkwZ7k45minvA9aorojkyjGk9KJ5B/w= +golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20201201195509-5d6afe98e0b7/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210331212208-0fccb6fa2b5c h1:KHUzaHIpjWVlVVNh65G3hhuj3KB1HnjY6Cq5cTvRQT8= +golang.org/x/net v0.0.0-20210331212208-0fccb6fa2b5c/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/dtls-2.0.9/handshake_cache.go b/dtls-2.0.9/handshake_cache.go new file mode 100644 index 0000000..063a858 --- /dev/null +++ b/dtls-2.0.9/handshake_cache.go @@ -0,0 +1,171 @@ +package dtls + +import ( + "sync" + + "github.com/pion/dtls/v2/pkg/crypto/prf" + "github.com/pion/dtls/v2/pkg/protocol/handshake" +) + +type handshakeCacheItem struct { + typ handshake.Type + isClient bool + epoch uint16 + messageSequence uint16 + data []byte +} + +type handshakeCachePullRule struct { + typ handshake.Type + epoch uint16 + isClient bool + optional bool +} + +type handshakeCache struct { + cache []*handshakeCacheItem + mu sync.Mutex +} + +func newHandshakeCache() *handshakeCache { + return &handshakeCache{} +} + +func (h *handshakeCache) push(data []byte, epoch, messageSequence uint16, typ handshake.Type, isClient bool) bool { //nolint + h.mu.Lock() + defer h.mu.Unlock() + + for _, i := range h.cache { + if i.messageSequence == messageSequence && + i.isClient == isClient { + return false + } + } + + h.cache = append(h.cache, &handshakeCacheItem{ + data: append([]byte{}, data...), + epoch: epoch, + messageSequence: messageSequence, + typ: typ, + isClient: isClient, + }) + return true +} + +// returns a list handshakes that match the requested rules +// the list will contain null entries for rules that can't be satisfied +// multiple entries may match a rule, but only the last match is returned (ie ClientHello with cookies) +func (h *handshakeCache) pull(rules ...handshakeCachePullRule) []*handshakeCacheItem { + h.mu.Lock() + defer h.mu.Unlock() + + out := make([]*handshakeCacheItem, len(rules)) + for i, r := range rules { + for _, c := range h.cache { + if c.typ == r.typ && c.isClient == r.isClient && c.epoch == r.epoch { + switch { + case out[i] == nil: + out[i] = c + case out[i].messageSequence < c.messageSequence: + out[i] = c + } + } + } + } + + return out +} + +// fullPullMap pulls all handshakes between rules[0] to rules[len(rules)-1] as map. +func (h *handshakeCache) fullPullMap(startSeq int, rules ...handshakeCachePullRule) (int, map[handshake.Type]handshake.Message, bool) { + h.mu.Lock() + defer h.mu.Unlock() + + ci := make(map[handshake.Type]*handshakeCacheItem) + for _, r := range rules { + var item *handshakeCacheItem + for _, c := range h.cache { + if c.typ == r.typ && c.isClient == r.isClient && c.epoch == r.epoch { + switch { + case item == nil: + item = c + case item.messageSequence < c.messageSequence: + item = c + } + } + } + if !r.optional && item == nil { + // Missing mandatory message. + return startSeq, nil, false + } + ci[r.typ] = item + } + out := make(map[handshake.Type]handshake.Message) + seq := startSeq + for _, r := range rules { + t := r.typ + i := ci[t] + if i == nil { + continue + } + rawHandshake := &handshake.Handshake{} + if err := rawHandshake.Unmarshal(i.data); err != nil { + return startSeq, nil, false + } + if uint16(seq) != rawHandshake.Header.MessageSequence { + // There is a gap. Some messages are not arrived. + return startSeq, nil, false + } + seq++ + out[t] = rawHandshake.Message + } + return seq, out, true +} + +// pullAndMerge calls pull and then merges the results, ignoring any null entries +func (h *handshakeCache) pullAndMerge(rules ...handshakeCachePullRule) []byte { + merged := []byte{} + + for _, p := range h.pull(rules...) { + if p != nil { + merged = append(merged, p.data...) + } + } + return merged +} + +// sessionHash returns the session hash for Extended Master Secret support +// https://tools.ietf.org/html/draft-ietf-tls-session-hash-06#section-4 +func (h *handshakeCache) sessionHash(hf prf.HashFunc, epoch uint16, additional ...[]byte) ([]byte, error) { + merged := []byte{} + + // Order defined by https://tools.ietf.org/html/rfc5246#section-7.3 + handshakeBuffer := h.pull( + handshakeCachePullRule{handshake.TypeClientHello, epoch, true, false}, + handshakeCachePullRule{handshake.TypeServerHello, epoch, false, false}, + handshakeCachePullRule{handshake.TypeCertificate, epoch, false, false}, + handshakeCachePullRule{handshake.TypeServerKeyExchange, epoch, false, false}, + handshakeCachePullRule{handshake.TypeCertificateRequest, epoch, false, false}, + handshakeCachePullRule{handshake.TypeServerHelloDone, epoch, false, false}, + handshakeCachePullRule{handshake.TypeCertificate, epoch, true, false}, + handshakeCachePullRule{handshake.TypeClientKeyExchange, epoch, true, false}, + ) + + for _, p := range handshakeBuffer { + if p == nil { + continue + } + + merged = append(merged, p.data...) + } + for _, a := range additional { + merged = append(merged, a...) + } + + hash := hf() + if _, err := hash.Write(merged); err != nil { + return []byte{}, err + } + + return hash.Sum(nil), nil +} diff --git a/dtls-2.0.9/handshake_cache_test.go b/dtls-2.0.9/handshake_cache_test.go new file mode 100644 index 0000000..d985177 --- /dev/null +++ b/dtls-2.0.9/handshake_cache_test.go @@ -0,0 +1,210 @@ +package dtls + +import ( + "bytes" + "testing" + + "github.com/pion/dtls/v2/internal/ciphersuite" + "github.com/pion/dtls/v2/pkg/protocol/handshake" +) + +func TestHandshakeCacheSinglePush(t *testing.T) { + for _, test := range []struct { + Name string + Rule []handshakeCachePullRule + Input []handshakeCacheItem + Expected []byte + }{ + { + Name: "Single Push", + Input: []handshakeCacheItem{ + {0, true, 0, 0, []byte{0x00}}, + }, + Rule: []handshakeCachePullRule{ + {0, 0, true, false}, + }, + Expected: []byte{0x00}, + }, + { + Name: "Multi Push", + Input: []handshakeCacheItem{ + {0, true, 0, 0, []byte{0x00}}, + {1, true, 0, 1, []byte{0x01}}, + {2, true, 0, 2, []byte{0x02}}, + }, + Rule: []handshakeCachePullRule{ + {0, 0, true, false}, + {1, 0, true, false}, + {2, 0, true, false}, + }, + Expected: []byte{0x00, 0x01, 0x02}, + }, + { + Name: "Multi Push, Rules set order", + Input: []handshakeCacheItem{ + {2, true, 0, 2, []byte{0x02}}, + {0, true, 0, 0, []byte{0x00}}, + {1, true, 0, 1, []byte{0x01}}, + }, + Rule: []handshakeCachePullRule{ + {0, 0, true, false}, + {1, 0, true, false}, + {2, 0, true, false}, + }, + Expected: []byte{0x00, 0x01, 0x02}, + }, + + { + Name: "Multi Push, Dupe Seqnum", + Input: []handshakeCacheItem{ + {0, true, 0, 0, []byte{0x00}}, + {1, true, 0, 1, []byte{0x01}}, + {1, true, 0, 1, []byte{0x01}}, + }, + Rule: []handshakeCachePullRule{ + {0, 0, true, false}, + {1, 0, true, false}, + }, + Expected: []byte{0x00, 0x01}, + }, + { + Name: "Multi Push, Dupe Seqnum Client/Server", + Input: []handshakeCacheItem{ + {0, true, 0, 0, []byte{0x00}}, + {1, true, 0, 1, []byte{0x01}}, + {1, false, 0, 1, []byte{0x02}}, + }, + Rule: []handshakeCachePullRule{ + {0, 0, true, false}, + {1, 0, true, false}, + {1, 0, false, false}, + }, + Expected: []byte{0x00, 0x01, 0x02}, + }, + { + Name: "Multi Push, Dupe Seqnum with Unique HandshakeType", + Input: []handshakeCacheItem{ + {1, true, 0, 0, []byte{0x00}}, + {2, true, 0, 1, []byte{0x01}}, + {3, false, 0, 0, []byte{0x02}}, + }, + Rule: []handshakeCachePullRule{ + {1, 0, true, false}, + {2, 0, true, false}, + {3, 0, false, false}, + }, + Expected: []byte{0x00, 0x01, 0x02}, + }, + { + Name: "Multi Push, Wrong epoch", + Input: []handshakeCacheItem{ + {1, true, 0, 0, []byte{0x00}}, + {2, true, 1, 1, []byte{0x01}}, + {2, true, 0, 2, []byte{0x11}}, + {3, false, 0, 0, []byte{0x02}}, + {3, false, 1, 0, []byte{0x12}}, + {3, false, 2, 0, []byte{0x12}}, + }, + Rule: []handshakeCachePullRule{ + {1, 0, true, false}, + {2, 1, true, false}, + {3, 0, false, false}, + }, + Expected: []byte{0x00, 0x01, 0x02}, + }, + } { + h := newHandshakeCache() + for _, i := range test.Input { + h.push(i.data, i.epoch, i.messageSequence, i.typ, i.isClient) + } + verifyData := h.pullAndMerge(test.Rule...) + if !bytes.Equal(verifyData, test.Expected) { + t.Errorf("handshakeCache '%s' exp: % 02x actual % 02x", test.Name, test.Expected, verifyData) + } + } +} + +func TestHandshakeCacheSessionHash(t *testing.T) { + for _, test := range []struct { + Name string + Rule []handshakeCachePullRule + Input []handshakeCacheItem + Expected []byte + }{ + { + Name: "Standard Handshake", + Input: []handshakeCacheItem{ + {handshake.TypeClientHello, true, 0, 0, []byte{0x00}}, + {handshake.TypeServerHello, false, 0, 1, []byte{0x01}}, + {handshake.TypeCertificate, false, 0, 2, []byte{0x02}}, + {handshake.TypeServerKeyExchange, false, 0, 3, []byte{0x03}}, + {handshake.TypeServerHelloDone, false, 0, 4, []byte{0x04}}, + {handshake.TypeClientKeyExchange, true, 0, 5, []byte{0x05}}, + }, + Expected: []byte{0x17, 0xe8, 0x8d, 0xb1, 0x87, 0xaf, 0xd6, 0x2c, 0x16, 0xe5, 0xde, 0xbf, 0x3e, 0x65, 0x27, 0xcd, 0x00, 0x6b, 0xc0, 0x12, 0xbc, 0x90, 0xb5, 0x1a, 0x81, 0x0c, 0xd8, 0x0c, 0x2d, 0x51, 0x1f, 0x43}, + }, + { + Name: "Handshake With Client Cert Request", + Input: []handshakeCacheItem{ + {handshake.TypeClientHello, true, 0, 0, []byte{0x00}}, + {handshake.TypeServerHello, false, 0, 1, []byte{0x01}}, + {handshake.TypeCertificate, false, 0, 2, []byte{0x02}}, + {handshake.TypeServerKeyExchange, false, 0, 3, []byte{0x03}}, + {handshake.TypeCertificateRequest, false, 0, 4, []byte{0x04}}, + {handshake.TypeServerHelloDone, false, 0, 5, []byte{0x05}}, + {handshake.TypeClientKeyExchange, true, 0, 6, []byte{0x06}}, + }, + Expected: []byte{0x57, 0x35, 0x5a, 0xc3, 0x30, 0x3c, 0x14, 0x8f, 0x11, 0xae, 0xf7, 0xcb, 0x17, 0x94, 0x56, 0xb9, 0x23, 0x2c, 0xde, 0x33, 0xa8, 0x18, 0xdf, 0xda, 0x2c, 0x2f, 0xcb, 0x93, 0x25, 0x74, 0x9a, 0x6b}, + }, + { + Name: "Handshake Ignores after ClientKeyExchange", + Input: []handshakeCacheItem{ + {handshake.TypeClientHello, true, 0, 0, []byte{0x00}}, + {handshake.TypeServerHello, false, 0, 1, []byte{0x01}}, + {handshake.TypeCertificate, false, 0, 2, []byte{0x02}}, + {handshake.TypeServerKeyExchange, false, 0, 3, []byte{0x03}}, + {handshake.TypeCertificateRequest, false, 0, 4, []byte{0x04}}, + {handshake.TypeServerHelloDone, false, 0, 5, []byte{0x05}}, + {handshake.TypeClientKeyExchange, true, 0, 6, []byte{0x06}}, + {handshake.TypeCertificateVerify, true, 0, 7, []byte{0x07}}, + {handshake.TypeFinished, true, 1, 7, []byte{0x08}}, + {handshake.TypeFinished, false, 1, 7, []byte{0x09}}, + }, + Expected: []byte{0x57, 0x35, 0x5a, 0xc3, 0x30, 0x3c, 0x14, 0x8f, 0x11, 0xae, 0xf7, 0xcb, 0x17, 0x94, 0x56, 0xb9, 0x23, 0x2c, 0xde, 0x33, 0xa8, 0x18, 0xdf, 0xda, 0x2c, 0x2f, 0xcb, 0x93, 0x25, 0x74, 0x9a, 0x6b}, + }, + { + Name: "Handshake Ignores wrong epoch", + Input: []handshakeCacheItem{ + {handshake.TypeClientHello, true, 0, 0, []byte{0x00}}, + {handshake.TypeServerHello, false, 0, 1, []byte{0x01}}, + {handshake.TypeCertificate, false, 0, 2, []byte{0x02}}, + {handshake.TypeServerKeyExchange, false, 0, 3, []byte{0x03}}, + {handshake.TypeCertificateRequest, false, 0, 4, []byte{0x04}}, + {handshake.TypeServerHelloDone, false, 0, 5, []byte{0x05}}, + {handshake.TypeClientKeyExchange, true, 0, 6, []byte{0x06}}, + {handshake.TypeCertificateVerify, true, 0, 7, []byte{0x07}}, + {handshake.TypeFinished, true, 0, 7, []byte{0xf0}}, + {handshake.TypeFinished, false, 0, 7, []byte{0xf1}}, + {handshake.TypeFinished, true, 1, 7, []byte{0x08}}, + {handshake.TypeFinished, false, 1, 7, []byte{0x09}}, + {handshake.TypeFinished, true, 0, 7, []byte{0xf0}}, + {handshake.TypeFinished, false, 0, 7, []byte{0xf1}}, + }, + Expected: []byte{0x57, 0x35, 0x5a, 0xc3, 0x30, 0x3c, 0x14, 0x8f, 0x11, 0xae, 0xf7, 0xcb, 0x17, 0x94, 0x56, 0xb9, 0x23, 0x2c, 0xde, 0x33, 0xa8, 0x18, 0xdf, 0xda, 0x2c, 0x2f, 0xcb, 0x93, 0x25, 0x74, 0x9a, 0x6b}, + }, + } { + h := newHandshakeCache() + for _, i := range test.Input { + h.push(i.data, i.epoch, i.messageSequence, i.typ, i.isClient) + } + + cipherSuite := ciphersuite.TLSEcdheEcdsaWithAes128GcmSha256{} + verifyData, err := h.sessionHash(cipherSuite.HashFunc(), 0) + if err != nil { + t.Error(err) + } + if !bytes.Equal(verifyData, test.Expected) { + t.Errorf("handshakeCacheSesssionHassh '%s' exp: % 02x actual % 02x", test.Name, test.Expected, verifyData) + } + } +} diff --git a/dtls-2.0.9/handshake_test.go b/dtls-2.0.9/handshake_test.go new file mode 100644 index 0000000..2174d21 --- /dev/null +++ b/dtls-2.0.9/handshake_test.go @@ -0,0 +1,52 @@ +package dtls + +import ( + "reflect" + "testing" + "time" + + "github.com/pion/dtls/v2/pkg/protocol" + "github.com/pion/dtls/v2/pkg/protocol/extension" + "github.com/pion/dtls/v2/pkg/protocol/handshake" +) + +func TestHandshakeMessage(t *testing.T) { + rawHandshakeMessage := []byte{ + 0x01, 0x00, 0x00, 0x29, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x29, 0xfe, 0xfd, 0xb6, + 0x2f, 0xce, 0x5c, 0x42, 0x54, 0xff, 0x86, 0xe1, 0x24, 0x41, 0x91, 0x42, 0x62, 0x15, 0xad, + 0x16, 0xc9, 0x15, 0x8d, 0x95, 0x71, 0x8a, 0xbb, 0x22, 0xd7, 0x47, 0xec, 0xd8, 0x3d, 0xdc, + 0x4b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + } + parsedHandshake := &handshake.Handshake{ + Header: handshake.Header{ + Length: 0x29, + FragmentLength: 0x29, + Type: handshake.TypeClientHello, + }, + Message: &handshake.MessageClientHello{ + Version: protocol.Version{Major: 0xFE, Minor: 0xFD}, + Random: handshake.Random{ + GMTUnixTime: time.Unix(3056586332, 0), + RandomBytes: [28]byte{0x42, 0x54, 0xff, 0x86, 0xe1, 0x24, 0x41, 0x91, 0x42, 0x62, 0x15, 0xad, 0x16, 0xc9, 0x15, 0x8d, 0x95, 0x71, 0x8a, 0xbb, 0x22, 0xd7, 0x47, 0xec, 0xd8, 0x3d, 0xdc, 0x4b}, + }, + Cookie: []byte{}, + CipherSuiteIDs: []uint16{}, + CompressionMethods: []*protocol.CompressionMethod{}, + Extensions: []extension.Extension{}, + }, + } + + h := &handshake.Handshake{} + if err := h.Unmarshal(rawHandshakeMessage); err != nil { + t.Error(err) + } else if !reflect.DeepEqual(h, parsedHandshake) { + t.Errorf("handshakeMessageClientHello unmarshal: got %#v, want %#v", h, parsedHandshake) + } + + raw, err := h.Marshal() + if err != nil { + t.Error(err) + } else if !reflect.DeepEqual(raw, rawHandshakeMessage) { + t.Errorf("handshakeMessageClientHello marshal: got %#v, want %#v", raw, rawHandshakeMessage) + } +} diff --git a/dtls-2.0.9/handshaker.go b/dtls-2.0.9/handshaker.go new file mode 100644 index 0000000..cea256c --- /dev/null +++ b/dtls-2.0.9/handshaker.go @@ -0,0 +1,343 @@ +package dtls + +import ( + "context" + "crypto/tls" + "crypto/x509" + "fmt" + "io" + "sync" + "time" + + "github.com/pion/dtls/v2/pkg/crypto/signaturehash" + "github.com/pion/dtls/v2/pkg/protocol/alert" + "github.com/pion/dtls/v2/pkg/protocol/handshake" + "github.com/pion/logging" +) + +// [RFC6347 Section-4.2.4] +// +-----------+ +// +---> | PREPARING | <--------------------+ +// | +-----------+ | +// | | | +// | | Buffer next flight | +// | | | +// | \|/ | +// | +-----------+ | +// | | SENDING |<------------------+ | Send +// | +-----------+ | | HelloRequest +// Receive | | | | +// next | | Send flight | | or +// flight | +--------+ | | +// | | | Set retransmit timer | | Receive +// | | \|/ | | HelloRequest +// | | +-----------+ | | Send +// +--)--| WAITING |-------------------+ | ClientHello +// | | +-----------+ Timer expires | | +// | | | | | +// | | +------------------------+ | +// Receive | | Send Read retransmit | +// last | | last | +// flight | | flight | +// | | | +// \|/\|/ | +// +-----------+ | +// | FINISHED | -------------------------------+ +// +-----------+ +// | /|\ +// | | +// +---+ +// Read retransmit +// Retransmit last flight + +type handshakeState uint8 + +const ( + handshakeErrored handshakeState = iota + handshakePreparing + handshakeSending + handshakeWaiting + handshakeFinished +) + +func (s handshakeState) String() string { + switch s { + case handshakeErrored: + return "Errored" + case handshakePreparing: + return "Preparing" + case handshakeSending: + return "Sending" + case handshakeWaiting: + return "Waiting" + case handshakeFinished: + return "Finished" + default: + return "Unknown" + } +} + +type handshakeFSM struct { + currentFlight flightVal + flights []*packet + retransmit bool + state *State + cache *handshakeCache + cfg *handshakeConfig + closed chan struct{} +} + +type handshakeConfig struct { + localPSKCallback PSKCallback + localPSKIdentityHint []byte + localCiscoCompatCallback PSKCallback // TODO add cisco anyconnect support + localCipherSuites []CipherSuite // Available CipherSuites + localSignatureSchemes []signaturehash.Algorithm // Available signature schemes + extendedMasterSecret ExtendedMasterSecretType // Policy for the Extended Master Support extension + localSRTPProtectionProfiles []SRTPProtectionProfile // Available SRTPProtectionProfiles, if empty no SRTP support + serverName string + clientAuth ClientAuthType // If we are a client should we request a client certificate + localCertificates []tls.Certificate + nameToCertificate map[string]*tls.Certificate + insecureSkipVerify bool + verifyPeerCertificate func(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error + rootCAs *x509.CertPool + clientCAs *x509.CertPool + retransmitInterval time.Duration + customCipherSuites func() []CipherSuite + + onFlightState func(flightVal, handshakeState) + log logging.LeveledLogger + keyLogWriter io.Writer + + initialEpoch uint16 + + mu sync.Mutex +} + +type flightConn interface { + notify(ctx context.Context, level alert.Level, desc alert.Description) error + writePackets(context.Context, []*packet) error + recvHandshake() <-chan chan struct{} + setLocalEpoch(epoch uint16) + handleQueuedPackets(context.Context) error +} + +func (c *handshakeConfig) writeKeyLog(label string, clientRandom, secret []byte) { + if c.keyLogWriter == nil { + return + } + c.mu.Lock() + defer c.mu.Unlock() + _, err := c.keyLogWriter.Write([]byte(fmt.Sprintf("%s %x %x\n", label, clientRandom, secret))) + if err != nil { + c.log.Debugf("failed to write key log file: %s", err) + } +} + +func srvCliStr(isClient bool) string { + if isClient { + return "client" + } + return "server" +} + +func newHandshakeFSM( + s *State, cache *handshakeCache, cfg *handshakeConfig, + initialFlight flightVal, +) *handshakeFSM { + return &handshakeFSM{ + currentFlight: initialFlight, + state: s, + cache: cache, + cfg: cfg, + closed: make(chan struct{}), + } +} + +func (s *handshakeFSM) Run(ctx context.Context, c flightConn, initialState handshakeState) error { + state := initialState + defer func() { + close(s.closed) + }() + for { + s.cfg.log.Tracef("[handshake:%s] %s: %s", srvCliStr(s.state.isClient), s.currentFlight.String(), state.String()) + if s.cfg.onFlightState != nil { + s.cfg.onFlightState(s.currentFlight, state) + } + var err error + switch state { + case handshakePreparing: + state, err = s.prepare(ctx, c) + case handshakeSending: + state, err = s.send(ctx, c) + case handshakeWaiting: + state, err = s.wait(ctx, c) + case handshakeFinished: + state, err = s.finish(ctx, c) + default: + return errInvalidFSMTransition + } + if err != nil { + return err + } + + // TODO 添加 CiscoCompat 支持 + if s.cfg.localCiscoCompatCallback != nil { + if s.currentFlight == flight4 && state == handshakeWaiting { + s.currentFlight = flight6 + state = handshakePreparing + } + } + } +} + +func (s *handshakeFSM) Done() <-chan struct{} { + return s.closed +} + +func (s *handshakeFSM) prepare(ctx context.Context, c flightConn) (handshakeState, error) { + s.flights = nil + // Prepare flights + var ( + a *alert.Alert + err error + pkts []*packet + ) + gen, retransmit, errFlight := s.currentFlight.getFlightGenerator() + if errFlight != nil { + err = errFlight + a = &alert.Alert{Level: alert.Fatal, Description: alert.InternalError} + } else { + pkts, a, err = gen(c, s.state, s.cache, s.cfg) + s.retransmit = retransmit + } + if a != nil { + if alertErr := c.notify(ctx, a.Level, a.Description); alertErr != nil { + if err != nil { + err = alertErr + } + } + } + if err != nil { + return handshakeErrored, err + } + + s.flights = pkts + epoch := s.cfg.initialEpoch + nextEpoch := epoch + for _, p := range s.flights { + p.record.Header.Epoch += epoch + if p.record.Header.Epoch > nextEpoch { + nextEpoch = p.record.Header.Epoch + } + if h, ok := p.record.Content.(*handshake.Handshake); ok { + h.Header.MessageSequence = uint16(s.state.handshakeSendSequence) + s.state.handshakeSendSequence++ + } + } + if epoch != nextEpoch { + s.cfg.log.Tracef("[handshake:%s] -> changeCipherSpec (epoch: %d)", srvCliStr(s.state.isClient), nextEpoch) + c.setLocalEpoch(nextEpoch) + } + return handshakeSending, nil +} + +func (s *handshakeFSM) send(ctx context.Context, c flightConn) (handshakeState, error) { + // Send flights + if err := c.writePackets(ctx, s.flights); err != nil { + return handshakeErrored, err + } + + if s.currentFlight.isLastSendFlight() { + return handshakeFinished, nil + } + return handshakeWaiting, nil +} + +func (s *handshakeFSM) wait(ctx context.Context, c flightConn) (handshakeState, error) { //nolint:gocognit + parse, errFlight := s.currentFlight.getFlightParser() + if errFlight != nil { + if alertErr := c.notify(ctx, alert.Fatal, alert.InternalError); alertErr != nil { + if errFlight != nil { + return handshakeErrored, alertErr + } + } + return handshakeErrored, errFlight + } + + retransmitTimer := time.NewTimer(s.cfg.retransmitInterval) + for { + select { + case done := <-c.recvHandshake(): + nextFlight, alert, err := parse(ctx, c, s.state, s.cache, s.cfg) + close(done) + if alert != nil { + if alertErr := c.notify(ctx, alert.Level, alert.Description); alertErr != nil { + if err != nil { + err = alertErr + } + } + } + if err != nil { + return handshakeErrored, err + } + if nextFlight == 0 { + break + } + s.cfg.log.Tracef("[handshake:%s] %s -> %s", srvCliStr(s.state.isClient), s.currentFlight.String(), nextFlight.String()) + if nextFlight.isLastRecvFlight() && s.currentFlight == nextFlight { + return handshakeFinished, nil + } + s.currentFlight = nextFlight + return handshakePreparing, nil + + case <-retransmitTimer.C: + if !s.retransmit { + return handshakeWaiting, nil + } + return handshakeSending, nil + case <-ctx.Done(): + return handshakeErrored, ctx.Err() + } + } +} + +func (s *handshakeFSM) finish(ctx context.Context, c flightConn) (handshakeState, error) { + parse, errFlight := s.currentFlight.getFlightParser() + if errFlight != nil { + if alertErr := c.notify(ctx, alert.Fatal, alert.InternalError); alertErr != nil { + if errFlight != nil { + return handshakeErrored, alertErr + } + } + return handshakeErrored, errFlight + } + + retransmitTimer := time.NewTimer(s.cfg.retransmitInterval) + select { + case done := <-c.recvHandshake(): + nextFlight, alert, err := parse(ctx, c, s.state, s.cache, s.cfg) + close(done) + if alert != nil { + if alertErr := c.notify(ctx, alert.Level, alert.Description); alertErr != nil { + if err != nil { + err = alertErr + } + } + } + if err != nil { + return handshakeErrored, err + } + if nextFlight == 0 { + break + } + <-retransmitTimer.C + // Retransmit last flight + return handshakeSending, nil + + case <-ctx.Done(): + return handshakeErrored, ctx.Err() + } + return handshakeFinished, nil +} diff --git a/dtls-2.0.9/handshaker_test.go b/dtls-2.0.9/handshaker_test.go new file mode 100644 index 0000000..d26b987 --- /dev/null +++ b/dtls-2.0.9/handshaker_test.go @@ -0,0 +1,277 @@ +package dtls + +import ( + "bytes" + "context" + "crypto/tls" + "sync" + "testing" + "time" + + "github.com/pion/dtls/v2/pkg/crypto/selfsign" + "github.com/pion/dtls/v2/pkg/crypto/signaturehash" + "github.com/pion/dtls/v2/pkg/protocol/alert" + "github.com/pion/dtls/v2/pkg/protocol/handshake" + "github.com/pion/dtls/v2/pkg/protocol/recordlayer" + "github.com/pion/logging" + "github.com/pion/transport/test" +) + +const nonZeroRetransmitInterval = 100 * time.Millisecond + +// Test that writes to the key log are in the correct format and only applies +// when a key log writer is given. +func TestWriteKeyLog(t *testing.T) { + var buf bytes.Buffer + cfg := handshakeConfig{ + keyLogWriter: &buf, + } + cfg.writeKeyLog("LABEL", []byte{0xAA, 0xBB, 0xCC}, []byte{0xDD, 0xEE, 0xFF}) + + // Secrets follow the format