feat(webrtc): add ICE config helper (Configuration + SettingEngine)
Vendors github.com/pion/webrtc/v4 v4.2.11 and its transitive dependencies (datachannel, dtls/v3, ice/v4, interceptor, logging, mdns/v2, sctp, sdp/v3, srtp/v3, stun/v3, transport/v4, turn/v4).
This commit is contained in:
parent
1fdc29ace1
commit
917c353e03
698 changed files with 109266 additions and 0 deletions
47
core/webrtc/ice.go
Normal file
47
core/webrtc/ice.go
Normal file
|
|
@ -0,0 +1,47 @@
|
||||||
|
package webrtc
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/pion/webrtc/v4"
|
||||||
|
)
|
||||||
|
|
||||||
|
// BuildICEConfig translates a Config into the two Pion config pieces every
|
||||||
|
// PeerConnection needs: a webrtc.Configuration (with ICE servers) and a
|
||||||
|
// SettingEngine (with NAT1To1 and port range tuning).
|
||||||
|
//
|
||||||
|
// The returned *SettingEngine may be nil if no engine-level tuning is
|
||||||
|
// required (i.e. PublicIP unset and UDPPortRange at defaults). Callers
|
||||||
|
// should only pass it to webrtc.NewAPI when non-nil.
|
||||||
|
func BuildICEConfig(c Config) (webrtc.Configuration, *webrtc.SettingEngine, error) {
|
||||||
|
if err := c.Validate(); err != nil {
|
||||||
|
return webrtc.Configuration{}, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
rtcConfig := webrtc.Configuration{
|
||||||
|
ICEServers: make([]webrtc.ICEServer, 0, len(c.ICEServers)),
|
||||||
|
}
|
||||||
|
for _, uri := range c.ICEServers {
|
||||||
|
rtcConfig.ICEServers = append(rtcConfig.ICEServers, webrtc.ICEServer{
|
||||||
|
URLs: []string{uri},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
var se *webrtc.SettingEngine
|
||||||
|
if c.PublicIP != "" || c.UDPPortRange.Low > 0 {
|
||||||
|
engine := webrtc.SettingEngine{}
|
||||||
|
if c.PublicIP != "" {
|
||||||
|
engine.SetNAT1To1IPs([]string{c.PublicIP}, webrtc.ICECandidateTypeHost)
|
||||||
|
}
|
||||||
|
// Constrain the ephemeral UDP range Pion allocates for ICE candidates.
|
||||||
|
// Note: this is a separate concern from our FFmpeg→Source UDP ports;
|
||||||
|
// Pion uses its own port pool for the WebRTC media path.
|
||||||
|
if c.UDPPortRange.Low > 0 && c.UDPPortRange.High >= c.UDPPortRange.Low {
|
||||||
|
if err := engine.SetEphemeralUDPPortRange(
|
||||||
|
uint16(c.UDPPortRange.Low), uint16(c.UDPPortRange.High)); err != nil {
|
||||||
|
return webrtc.Configuration{}, nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
se = &engine
|
||||||
|
}
|
||||||
|
|
||||||
|
return rtcConfig, se, nil
|
||||||
|
}
|
||||||
50
core/webrtc/ice_test.go
Normal file
50
core/webrtc/ice_test.go
Normal file
|
|
@ -0,0 +1,50 @@
|
||||||
|
package webrtc
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/pion/webrtc/v4"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestBuildICEConfig_Defaults(t *testing.T) {
|
||||||
|
c := DefaultConfig()
|
||||||
|
rtcConfig, _, err := BuildICEConfig(c)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("BuildICEConfig: %v", err)
|
||||||
|
}
|
||||||
|
if len(rtcConfig.ICEServers) == 0 {
|
||||||
|
t.Error("ICEServers should not be empty")
|
||||||
|
}
|
||||||
|
// First default is Cloudflare STUN.
|
||||||
|
if rtcConfig.ICEServers[0].URLs[0] != "stun:stun.cloudflare.com:3478" {
|
||||||
|
t.Errorf("first ICE server = %q, want stun:stun.cloudflare.com:3478",
|
||||||
|
rtcConfig.ICEServers[0].URLs[0])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestBuildICEConfig_PublicIP(t *testing.T) {
|
||||||
|
c := DefaultConfig()
|
||||||
|
c.PublicIP = "203.0.113.10"
|
||||||
|
_, se, err := BuildICEConfig(c)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("BuildICEConfig: %v", err)
|
||||||
|
}
|
||||||
|
if se == nil {
|
||||||
|
t.Fatal("SettingEngine should not be nil when PublicIP is set")
|
||||||
|
}
|
||||||
|
// We can't introspect NAT1To1IPs directly from Pion's public API; the
|
||||||
|
// smoke test is that building an API from this engine works.
|
||||||
|
api := webrtc.NewAPI(webrtc.WithSettingEngine(*se))
|
||||||
|
if api == nil {
|
||||||
|
t.Fatal("NewAPI returned nil")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestBuildICEConfig_InvalidConfig(t *testing.T) {
|
||||||
|
c := DefaultConfig()
|
||||||
|
c.WHEPListen = ""
|
||||||
|
_, _, err := BuildICEConfig(c)
|
||||||
|
if err == nil {
|
||||||
|
t.Error("BuildICEConfig should reject invalid config")
|
||||||
|
}
|
||||||
|
}
|
||||||
15
go.mod
15
go.mod
|
|
@ -21,6 +21,7 @@ require (
|
||||||
github.com/mattn/go-isatty v0.0.20
|
github.com/mattn/go-isatty v0.0.20
|
||||||
github.com/minio/minio-go/v7 v7.0.70
|
github.com/minio/minio-go/v7 v7.0.70
|
||||||
github.com/pion/rtp v1.10.1
|
github.com/pion/rtp v1.10.1
|
||||||
|
github.com/pion/webrtc/v4 v4.2.11
|
||||||
github.com/prep/average v0.0.0-20200506183628-d26c465f48c3
|
github.com/prep/average v0.0.0-20200506183628-d26c465f48c3
|
||||||
github.com/prometheus/client_golang v1.19.1
|
github.com/prometheus/client_golang v1.19.1
|
||||||
github.com/puzpuzpuz/xsync/v3 v3.1.0
|
github.com/puzpuzpuz/xsync/v3 v3.1.0
|
||||||
|
|
@ -72,7 +73,20 @@ require (
|
||||||
github.com/miekg/dns v1.1.59 // indirect
|
github.com/miekg/dns v1.1.59 // indirect
|
||||||
github.com/minio/md5-simd v1.1.2 // indirect
|
github.com/minio/md5-simd v1.1.2 // indirect
|
||||||
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
||||||
|
github.com/pion/datachannel v1.6.0 // indirect
|
||||||
|
github.com/pion/dtls/v3 v3.1.2 // indirect
|
||||||
|
github.com/pion/ice/v4 v4.2.2 // indirect
|
||||||
|
github.com/pion/interceptor v0.1.44 // indirect
|
||||||
|
github.com/pion/logging v0.2.4 // indirect
|
||||||
|
github.com/pion/mdns/v2 v2.1.0 // indirect
|
||||||
github.com/pion/randutil v0.1.0 // indirect
|
github.com/pion/randutil v0.1.0 // indirect
|
||||||
|
github.com/pion/rtcp v1.2.16 // indirect
|
||||||
|
github.com/pion/sctp v1.9.4 // indirect
|
||||||
|
github.com/pion/sdp/v3 v3.0.18 // indirect
|
||||||
|
github.com/pion/srtp/v3 v3.0.10 // indirect
|
||||||
|
github.com/pion/stun/v3 v3.1.1 // indirect
|
||||||
|
github.com/pion/transport/v4 v4.0.1 // indirect
|
||||||
|
github.com/pion/turn/v4 v4.1.4 // indirect
|
||||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
|
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
|
||||||
github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect
|
github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect
|
||||||
github.com/prometheus/client_model v0.6.1 // indirect
|
github.com/prometheus/client_model v0.6.1 // indirect
|
||||||
|
|
@ -88,6 +102,7 @@ require (
|
||||||
github.com/urfave/cli/v2 v2.27.2 // indirect
|
github.com/urfave/cli/v2 v2.27.2 // indirect
|
||||||
github.com/valyala/bytebufferpool v1.0.0 // indirect
|
github.com/valyala/bytebufferpool v1.0.0 // indirect
|
||||||
github.com/valyala/fasttemplate v1.2.2 // indirect
|
github.com/valyala/fasttemplate v1.2.2 // indirect
|
||||||
|
github.com/wlynxg/anet v0.0.5 // indirect
|
||||||
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect
|
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect
|
||||||
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect
|
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect
|
||||||
github.com/xrash/smetrics v0.0.0-20240312152122-5f08fbb34913 // indirect
|
github.com/xrash/smetrics v0.0.0-20240312152122-5f08fbb34913 // indirect
|
||||||
|
|
|
||||||
32
go.sum
32
go.sum
|
|
@ -134,10 +134,40 @@ github.com/minio/minio-go/v7 v7.0.70 h1:1u9NtMgfK1U42kUxcsl5v0yj6TEOPR497OAQxpJn
|
||||||
github.com/minio/minio-go/v7 v7.0.70/go.mod h1:4yBA8v80xGA30cfM3fz0DKYMXunWl/AV/6tWEs9ryzo=
|
github.com/minio/minio-go/v7 v7.0.70/go.mod h1:4yBA8v80xGA30cfM3fz0DKYMXunWl/AV/6tWEs9ryzo=
|
||||||
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
|
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
|
||||||
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||||
|
github.com/pion/datachannel v1.6.0 h1:XecBlj+cvsxhAMZWFfFcPyUaDZtd7IJvrXqlXD/53i0=
|
||||||
|
github.com/pion/datachannel v1.6.0/go.mod h1:ur+wzYF8mWdC+Mkis5Thosk+u/VOL287apDNEbFpsIk=
|
||||||
|
github.com/pion/dtls/v3 v3.1.2 h1:gqEdOUXLtCGW+afsBLO0LtDD8GnuBBjEy6HRtyofZTc=
|
||||||
|
github.com/pion/dtls/v3 v3.1.2/go.mod h1:Hw/igcX4pdY69z1Hgv5x7wJFrUkdgHwAn/Q/uo7YHRo=
|
||||||
|
github.com/pion/ice/v4 v4.2.2 h1:dQJzzcgTFHDYyV3BoCfjPeX+JEtr58BWPi4PGyo6Vjg=
|
||||||
|
github.com/pion/ice/v4 v4.2.2/go.mod h1:2quLV1S5v1tAx3VvAJaH//KGitRXvo4RKlX6D3tnN+c=
|
||||||
|
github.com/pion/interceptor v0.1.44 h1:sNlZwM8dWXU9JQAkJh8xrarC0Etn8Oolcniukmuy0/I=
|
||||||
|
github.com/pion/interceptor v0.1.44/go.mod h1:4atVlBkcgXuUP+ykQF0qOCGU2j7pQzX2ofvPRFsY5RY=
|
||||||
|
github.com/pion/logging v0.2.4 h1:tTew+7cmQ+Mc1pTBLKH2puKsOvhm32dROumOZ655zB8=
|
||||||
|
github.com/pion/logging v0.2.4/go.mod h1:DffhXTKYdNZU+KtJ5pyQDjvOAh/GsNSyv1lbkFbe3so=
|
||||||
|
github.com/pion/mdns/v2 v2.1.0 h1:3IJ9+Xio6tWYjhN6WwuY142P/1jA0D5ERaIqawg/fOY=
|
||||||
|
github.com/pion/mdns/v2 v2.1.0/go.mod h1:pcez23GdynwcfRU1977qKU0mDxSeucttSHbCSfFOd9A=
|
||||||
github.com/pion/randutil v0.1.0 h1:CFG1UdESneORglEsnimhUjf33Rwjubwj6xfiOXBa3mA=
|
github.com/pion/randutil v0.1.0 h1:CFG1UdESneORglEsnimhUjf33Rwjubwj6xfiOXBa3mA=
|
||||||
github.com/pion/randutil v0.1.0/go.mod h1:XcJrSMMbbMRhASFVOlj/5hQial/Y8oH/HVo7TBZq+j8=
|
github.com/pion/randutil v0.1.0/go.mod h1:XcJrSMMbbMRhASFVOlj/5hQial/Y8oH/HVo7TBZq+j8=
|
||||||
|
github.com/pion/rtcp v1.2.16 h1:fk1B1dNW4hsI78XUCljZJlC4kZOPk67mNRuQ0fcEkSo=
|
||||||
|
github.com/pion/rtcp v1.2.16/go.mod h1:/as7VKfYbs5NIb4h6muQ35kQF/J0ZVNz2Z3xKoCBYOo=
|
||||||
github.com/pion/rtp v1.10.1 h1:xP1prZcCTUuhO2c83XtxyOHJteISg6o8iPsE2acaMtA=
|
github.com/pion/rtp v1.10.1 h1:xP1prZcCTUuhO2c83XtxyOHJteISg6o8iPsE2acaMtA=
|
||||||
github.com/pion/rtp v1.10.1/go.mod h1:rF5nS1GqbR7H/TCpKwylzeq6yDM+MM6k+On5EgeThEM=
|
github.com/pion/rtp v1.10.1/go.mod h1:rF5nS1GqbR7H/TCpKwylzeq6yDM+MM6k+On5EgeThEM=
|
||||||
|
github.com/pion/sctp v1.9.4 h1:cMxEu0F5tbP4qH07bKf1Zjf4rUih9LIo0qQt424e258=
|
||||||
|
github.com/pion/sctp v1.9.4/go.mod h1:N20Dq6LY+JvJDAh9VVh1JELngb2rQ8dPgds5yBWiPgw=
|
||||||
|
github.com/pion/sdp/v3 v3.0.18 h1:l0bAXazKHpepazVdp+tPYnrsy9dfh7ZbT8DxesH5ZnI=
|
||||||
|
github.com/pion/sdp/v3 v3.0.18/go.mod h1:ZREGo6A9ZygQ9XkqAj5xYCQtQpif0i6Pa81HOiAdqQ8=
|
||||||
|
github.com/pion/srtp/v3 v3.0.10 h1:tFirkpBb3XccP5VEXLi50GqXhv5SKPxqrdlhDCJlZrQ=
|
||||||
|
github.com/pion/srtp/v3 v3.0.10/go.mod h1:3mOTIB0cq9qlbn59V4ozvv9ClW/BSEbRp4cY0VtaR7M=
|
||||||
|
github.com/pion/stun/v3 v3.1.1 h1:CkQxveJ4xGQjulGSROXbXq94TAWu8gIX2dT+ePhUkqw=
|
||||||
|
github.com/pion/stun/v3 v3.1.1/go.mod h1:qC1DfmcCTQjl9PBaMa5wSn3x9IPmKxSdcCsxBcDBndM=
|
||||||
|
github.com/pion/transport/v3 v3.1.1 h1:Tr684+fnnKlhPceU+ICdrw6KKkTms+5qHMgw6bIkYOM=
|
||||||
|
github.com/pion/transport/v3 v3.1.1/go.mod h1:+c2eewC5WJQHiAA46fkMMzoYZSuGzA/7E2FPrOYHctQ=
|
||||||
|
github.com/pion/transport/v4 v4.0.1 h1:sdROELU6BZ63Ab7FrOLn13M6YdJLY20wldXW2Cu2k8o=
|
||||||
|
github.com/pion/transport/v4 v4.0.1/go.mod h1:nEuEA4AD5lPdcIegQDpVLgNoDGreqM/YqmEx3ovP4jM=
|
||||||
|
github.com/pion/turn/v4 v4.1.4 h1:EU11yMXKIsK43FhcUnjLlrhE4nboHZq+TXBIi3QpcxQ=
|
||||||
|
github.com/pion/turn/v4 v4.1.4/go.mod h1:ES1DXVFKnOhuDkqn9hn5VJlSWmZPaRJLyBXoOeO/BmQ=
|
||||||
|
github.com/pion/webrtc/v4 v4.2.11 h1:QUX1QZKlNIn4O7U5JxLPGP0sV5RTncZkzu9SPR3jVNU=
|
||||||
|
github.com/pion/webrtc/v4 v4.2.11/go.mod h1:s/rAiyy77GyRFrZMx+Ls6aua26dIBPudH8/ZHYbIRWY=
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
|
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
|
||||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
|
|
@ -204,6 +234,8 @@ github.com/valyala/fasttemplate v1.2.2 h1:lxLXG0uE3Qnshl9QyaK6XJxMXlQZELvChBOCmQ
|
||||||
github.com/valyala/fasttemplate v1.2.2/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ=
|
github.com/valyala/fasttemplate v1.2.2/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ=
|
||||||
github.com/vektah/gqlparser/v2 v2.5.12 h1:COMhVVnql6RoaF7+aTBWiTADdpLGyZWU3K/NwW0ph98=
|
github.com/vektah/gqlparser/v2 v2.5.12 h1:COMhVVnql6RoaF7+aTBWiTADdpLGyZWU3K/NwW0ph98=
|
||||||
github.com/vektah/gqlparser/v2 v2.5.12/go.mod h1:WQQjFc+I1YIzoPvZBhUQX7waZgg3pMLi0r8KymvAE2w=
|
github.com/vektah/gqlparser/v2 v2.5.12/go.mod h1:WQQjFc+I1YIzoPvZBhUQX7waZgg3pMLi0r8KymvAE2w=
|
||||||
|
github.com/wlynxg/anet v0.0.5 h1:J3VJGi1gvo0JwZ/P1/Yc/8p63SoW98B5dHkYDmpgvvU=
|
||||||
|
github.com/wlynxg/anet v0.0.5/go.mod h1:eay5PRQr7fIVAMbTbchTnO9gG65Hg/uYGdc7mguHxoA=
|
||||||
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
|
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
|
||||||
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb h1:zGWFAtiMcyryUHoUjUJX0/lt1H2+i2Ka2n+D3DImSNo=
|
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb h1:zGWFAtiMcyryUHoUjUJX0/lt1H2+i2Ka2n+D3DImSNo=
|
||||||
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
|
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
|
||||||
|
|
|
||||||
28
vendor/github.com/pion/datachannel/.gitignore
generated
vendored
Normal file
28
vendor/github.com/pion/datachannel/.gitignore
generated
vendored
Normal file
|
|
@ -0,0 +1,28 @@
|
||||||
|
# SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
|
||||||
|
# SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
### 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
|
||||||
|
wasm_exec.js
|
||||||
147
vendor/github.com/pion/datachannel/.golangci.yml
generated
vendored
Normal file
147
vendor/github.com/pion/datachannel/.golangci.yml
generated
vendored
Normal file
|
|
@ -0,0 +1,147 @@
|
||||||
|
# SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
|
||||||
|
# SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
version: "2"
|
||||||
|
linters:
|
||||||
|
enable:
|
||||||
|
- asciicheck # Simple linter to check that your code does not contain non-ASCII identifiers
|
||||||
|
- bidichk # Checks for dangerous unicode character sequences
|
||||||
|
- bodyclose # checks whether HTTP response body is closed successfully
|
||||||
|
- containedctx # containedctx is a linter that detects struct contained context.Context field
|
||||||
|
- contextcheck # check the function whether use a non-inherited context
|
||||||
|
- cyclop # checks function and package cyclomatic complexity
|
||||||
|
- decorder # check declaration order and count of types, constants, variables and functions
|
||||||
|
- dogsled # Checks assignments with too many blank identifiers (e.g. x, _, _, _, := f())
|
||||||
|
- dupl # Tool for code clone detection
|
||||||
|
- durationcheck # check for two durations multiplied together
|
||||||
|
- err113 # Golang linter to check the errors handling expressions
|
||||||
|
- errcheck # Errcheck is a program for checking for unchecked errors in go programs. These unchecked errors can be critical bugs in some cases
|
||||||
|
- errchkjson # Checks types passed to the json encoding functions. Reports unsupported types and optionally reports occations, where the check for the returned error can be omitted.
|
||||||
|
- errname # Checks that sentinel errors are prefixed with the `Err` and error types are suffixed with the `Error`.
|
||||||
|
- errorlint # errorlint is a linter for that can be used to find code that will cause problems with the error wrapping scheme introduced in Go 1.13.
|
||||||
|
- exhaustive # check exhaustiveness of enum switch statements
|
||||||
|
- forbidigo # Forbids identifiers
|
||||||
|
- forcetypeassert # finds forced type assertions
|
||||||
|
- gochecknoglobals # Checks that no globals 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
|
||||||
|
- gocyclo # Computes and checks the cyclomatic complexity of functions
|
||||||
|
- godot # Check if comments end in a period
|
||||||
|
- godox # Tool for detection of FIXME, TODO and other comment keywords
|
||||||
|
- goheader # Checks is file header matches to pattern
|
||||||
|
- gomoddirectives # Manage the use of 'replace', 'retract', and 'excludes' directives in go.mod.
|
||||||
|
- goprintffuncname # Checks that printf-like functions are named with `f` at the end
|
||||||
|
- gosec # Inspects source code for security problems
|
||||||
|
- govet # Vet examines Go source code and reports suspicious constructs, such as Printf calls whose arguments do not align with the format string
|
||||||
|
- grouper # An analyzer to analyze expression groups.
|
||||||
|
- importas # Enforces consistent import aliases
|
||||||
|
- ineffassign # Detects when assignments to existing variables are not used
|
||||||
|
- lll # Reports long lines
|
||||||
|
- maintidx # maintidx measures the maintainability index of each function.
|
||||||
|
- makezero # Finds slice declarations with non-zero initial length
|
||||||
|
- misspell # Finds commonly misspelled English words in comments
|
||||||
|
- nakedret # Finds naked returns in functions greater than a specified function length
|
||||||
|
- nestif # Reports deeply nested if statements
|
||||||
|
- nilerr # Finds the code that returns nil even if it checks that the error is not nil.
|
||||||
|
- nilnil # Checks that there is no simultaneous return of `nil` error and an invalid value.
|
||||||
|
- nlreturn # nlreturn checks for a new line before return and branch statements to increase code clarity
|
||||||
|
- noctx # noctx finds sending http request without context.Context
|
||||||
|
- predeclared # find code that shadows one of Go's predeclared identifiers
|
||||||
|
- revive # golint replacement, finds style mistakes
|
||||||
|
- staticcheck # Staticcheck is a go vet on steroids, applying a ton of static analysis checks
|
||||||
|
- tagliatelle # Checks the struct tags.
|
||||||
|
- thelper # thelper detects golang test helpers without t.Helper() call and checks the consistency of test helpers
|
||||||
|
- unconvert # Remove unnecessary type conversions
|
||||||
|
- unparam # Reports unused function parameters
|
||||||
|
- unused # Checks Go code for unused constants, variables, functions and types
|
||||||
|
- varnamelen # checks that the length of a variable's name matches its scope
|
||||||
|
- wastedassign # wastedassign finds wasted assignment statements
|
||||||
|
- whitespace # Tool for detection of leading and trailing whitespace
|
||||||
|
disable:
|
||||||
|
- depguard # Go linter that checks if package imports are in a list of acceptable packages
|
||||||
|
- funlen # Tool for detection of long functions
|
||||||
|
- gochecknoinits # Checks that no init functions are present in Go code
|
||||||
|
- 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.
|
||||||
|
- interfacebloat # A linter that checks length of interface.
|
||||||
|
- ireturn # Accept Interfaces, Return Concrete Types
|
||||||
|
- mnd # An analyzer to detect magic numbers
|
||||||
|
- nolintlint # Reports ill-formed or insufficient nolint directives
|
||||||
|
- paralleltest # paralleltest detects missing usage of t.Parallel() method in your Go test
|
||||||
|
- prealloc # Finds slice declarations that could potentially be preallocated
|
||||||
|
- promlinter # Check Prometheus metrics naming via promlint
|
||||||
|
- 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
|
||||||
|
- tparallel # tparallel detects inappropriate usage of t.Parallel() method in your Go test codes
|
||||||
|
- wrapcheck # Checks that errors returned from external packages are wrapped
|
||||||
|
- wsl # Whitespace Linter - Forces you to use empty lines!
|
||||||
|
settings:
|
||||||
|
staticcheck:
|
||||||
|
checks:
|
||||||
|
- all
|
||||||
|
- -QF1008 # "could remove embedded field", to keep it explicit!
|
||||||
|
- -QF1003 # "could use tagged switch on enum", Cases conflicts with exhaustive!
|
||||||
|
exhaustive:
|
||||||
|
default-signifies-exhaustive: true
|
||||||
|
forbidigo:
|
||||||
|
forbid:
|
||||||
|
- pattern: ^fmt.Print(f|ln)?$
|
||||||
|
- pattern: ^log.(Panic|Fatal|Print)(f|ln)?$
|
||||||
|
- pattern: ^os.Exit$
|
||||||
|
- pattern: ^panic$
|
||||||
|
- pattern: ^print(ln)?$
|
||||||
|
- pattern: ^testing.T.(Error|Errorf|Fatal|Fatalf|Fail|FailNow)$
|
||||||
|
pkg: ^testing$
|
||||||
|
msg: use testify/assert instead
|
||||||
|
analyze-types: true
|
||||||
|
gomodguard:
|
||||||
|
blocked:
|
||||||
|
modules:
|
||||||
|
- github.com/pkg/errors:
|
||||||
|
recommendations:
|
||||||
|
- errors
|
||||||
|
govet:
|
||||||
|
enable:
|
||||||
|
- shadow
|
||||||
|
revive:
|
||||||
|
rules:
|
||||||
|
# Prefer 'any' type alias over 'interface{}' for Go 1.18+ compatibility
|
||||||
|
- name: use-any
|
||||||
|
severity: warning
|
||||||
|
disabled: false
|
||||||
|
misspell:
|
||||||
|
locale: US
|
||||||
|
varnamelen:
|
||||||
|
max-distance: 12
|
||||||
|
min-name-length: 2
|
||||||
|
ignore-type-assert-ok: true
|
||||||
|
ignore-map-index-ok: true
|
||||||
|
ignore-chan-recv-ok: true
|
||||||
|
ignore-decls:
|
||||||
|
- i int
|
||||||
|
- n int
|
||||||
|
- w io.Writer
|
||||||
|
- r io.Reader
|
||||||
|
- b []byte
|
||||||
|
exclusions:
|
||||||
|
generated: lax
|
||||||
|
rules:
|
||||||
|
- linters:
|
||||||
|
- forbidigo
|
||||||
|
- gocognit
|
||||||
|
path: (examples|main\.go)
|
||||||
|
- linters:
|
||||||
|
- gocognit
|
||||||
|
path: _test\.go
|
||||||
|
- linters:
|
||||||
|
- forbidigo
|
||||||
|
path: cmd
|
||||||
|
formatters:
|
||||||
|
enable:
|
||||||
|
- gci # Gci control golang package import order and make it always deterministic.
|
||||||
|
- 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.
|
||||||
|
- goimports # Goimports does everything that gofmt does. Additionally it checks unused imports
|
||||||
|
exclusions:
|
||||||
|
generated: lax
|
||||||
5
vendor/github.com/pion/datachannel/.goreleaser.yml
generated
vendored
Normal file
5
vendor/github.com/pion/datachannel/.goreleaser.yml
generated
vendored
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
# SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
|
||||||
|
# SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
builds:
|
||||||
|
- skip: true
|
||||||
9
vendor/github.com/pion/datachannel/LICENSE
generated
vendored
Normal file
9
vendor/github.com/pion/datachannel/LICENSE
generated
vendored
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2023 The Pion community <https://pion.ly>
|
||||||
|
|
||||||
|
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.
|
||||||
34
vendor/github.com/pion/datachannel/README.md
generated
vendored
Normal file
34
vendor/github.com/pion/datachannel/README.md
generated
vendored
Normal file
|
|
@ -0,0 +1,34 @@
|
||||||
|
<h1 align="center">
|
||||||
|
<br>
|
||||||
|
Pion Data Channels
|
||||||
|
<br>
|
||||||
|
</h1>
|
||||||
|
<h4 align="center">A Go implementation of WebRTC Data Channels</h4>
|
||||||
|
<p align="center">
|
||||||
|
<a href="https://pion.ly"><img src="https://img.shields.io/badge/pion-datachannel-gray.svg?longCache=true&colorB=brightgreen" alt="Pion Data Channels"></a>
|
||||||
|
<a href="https://discord.gg/PngbdqpFbt"><img src="https://img.shields.io/badge/join-us%20on%20discord-gray.svg?longCache=true&logo=discord&colorB=brightblue" alt="join us on Discord"></a> <a href="https://bsky.app/profile/pion.ly"><img src="https://img.shields.io/badge/follow-us%20on%20bluesky-gray.svg?longCache=true&logo=bluesky&colorB=brightblue" alt="Follow us on Bluesky"></a>
|
||||||
|
<br>
|
||||||
|
<img alt="GitHub Workflow Status" src="https://img.shields.io/github/actions/workflow/status/pion/datachannel/test.yaml">
|
||||||
|
<a href="https://pkg.go.dev/github.com/pion/datachannel"><img src="https://pkg.go.dev/badge/github.com/pion/datachannel.svg" alt="Go Reference"></a>
|
||||||
|
<a href="https://codecov.io/gh/pion/datachannel"><img src="https://codecov.io/gh/pion/datachannel/branch/master/graph/badge.svg" alt="Coverage Status"></a>
|
||||||
|
<a href="https://goreportcard.com/report/github.com/pion/datachannel"><img src="https://goreportcard.com/badge/github.com/pion/datachannel" alt="Go Report Card"></a>
|
||||||
|
<a href="LICENSE"><img src="https://img.shields.io/badge/License-MIT-yellow.svg" alt="License: MIT"></a>
|
||||||
|
</p>
|
||||||
|
<br>
|
||||||
|
|
||||||
|
### Roadmap
|
||||||
|
The library is used as a part of our WebRTC implementation. Please refer to that [roadmap](https://github.com/pion/webrtc/issues/9) to track our major milestones.
|
||||||
|
|
||||||
|
### Community
|
||||||
|
Pion has an active community on the [Discord](https://discord.gg/PngbdqpFbt).
|
||||||
|
|
||||||
|
Follow the [Pion Bluesky](https://bsky.app/profile/pion.ly) or [Pion Twitter](https://twitter.com/_pion) for project updates and important WebRTC news.
|
||||||
|
|
||||||
|
We are always looking to support **your projects**. Please reach out if you have something to build!
|
||||||
|
If you need commercial support or don't want to use public methods you can contact us at [team@pion.ly](mailto:team@pion.ly)
|
||||||
|
|
||||||
|
### Contributing
|
||||||
|
Check out the [contributing wiki](https://github.com/pion/webrtc/wiki/Contributing) to join the group of amazing people making this project possible
|
||||||
|
|
||||||
|
### License
|
||||||
|
MIT License - see [LICENSE](LICENSE) for full text
|
||||||
22
vendor/github.com/pion/datachannel/codecov.yml
generated
vendored
Normal file
22
vendor/github.com/pion/datachannel/codecov.yml
generated
vendored
Normal file
|
|
@ -0,0 +1,22 @@
|
||||||
|
#
|
||||||
|
# DO NOT EDIT THIS FILE
|
||||||
|
#
|
||||||
|
# It is automatically copied from https://github.com/pion/.goassets repository.
|
||||||
|
#
|
||||||
|
# SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
|
||||||
|
# SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
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/**/*"
|
||||||
445
vendor/github.com/pion/datachannel/datachannel.go
generated
vendored
Normal file
445
vendor/github.com/pion/datachannel/datachannel.go
generated
vendored
Normal file
|
|
@ -0,0 +1,445 @@
|
||||||
|
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
// Package datachannel implements WebRTC Data Channels
|
||||||
|
package datachannel
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"sync"
|
||||||
|
"sync/atomic"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/pion/logging"
|
||||||
|
"github.com/pion/sctp"
|
||||||
|
)
|
||||||
|
|
||||||
|
const receiveMTU = 8192
|
||||||
|
|
||||||
|
// Reader is an extended io.Reader
|
||||||
|
// that also returns if the message is text.
|
||||||
|
type Reader interface {
|
||||||
|
ReadDataChannel([]byte) (int, bool, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReadDeadliner extends an io.Reader to expose setting a read deadline.
|
||||||
|
type ReadDeadliner interface {
|
||||||
|
SetReadDeadline(time.Time) error
|
||||||
|
}
|
||||||
|
|
||||||
|
// Writer is an extended io.Writer
|
||||||
|
// that also allows indicating if a message is text.
|
||||||
|
type Writer interface {
|
||||||
|
WriteDataChannel([]byte, bool) (int, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// WriteDeadliner extends an io.Writer to expose setting a write deadline.
|
||||||
|
type WriteDeadliner interface {
|
||||||
|
SetWriteDeadline(time.Time) error
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReadWriteCloser is an extended io.ReadWriteCloser
|
||||||
|
// that also implements our Reader and Writer.
|
||||||
|
type ReadWriteCloser interface {
|
||||||
|
io.Reader
|
||||||
|
io.Writer
|
||||||
|
Reader
|
||||||
|
Writer
|
||||||
|
io.Closer
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReadWriteCloserDeadliner is an extended ReadWriteCloser
|
||||||
|
// that also implements r/w deadline.
|
||||||
|
type ReadWriteCloserDeadliner interface {
|
||||||
|
ReadWriteCloser
|
||||||
|
ReadDeadliner
|
||||||
|
WriteDeadliner
|
||||||
|
}
|
||||||
|
|
||||||
|
// DataChannel represents a data channel.
|
||||||
|
type DataChannel struct {
|
||||||
|
Config
|
||||||
|
|
||||||
|
// stats
|
||||||
|
messagesSent uint32
|
||||||
|
messagesReceived uint32
|
||||||
|
bytesSent uint64
|
||||||
|
bytesReceived uint64
|
||||||
|
|
||||||
|
mu sync.Mutex
|
||||||
|
onOpenCompleteHandler func()
|
||||||
|
openCompleteHandlerOnce sync.Once
|
||||||
|
|
||||||
|
stream *sctp.Stream
|
||||||
|
log logging.LeveledLogger
|
||||||
|
}
|
||||||
|
|
||||||
|
// Config is used to configure the data channel.
|
||||||
|
type Config struct {
|
||||||
|
ChannelType ChannelType
|
||||||
|
Negotiated bool
|
||||||
|
Priority uint16
|
||||||
|
ReliabilityParameter uint32
|
||||||
|
Label string
|
||||||
|
Protocol string
|
||||||
|
LoggerFactory logging.LoggerFactory
|
||||||
|
}
|
||||||
|
|
||||||
|
func newDataChannel(stream *sctp.Stream, config *Config) *DataChannel {
|
||||||
|
return &DataChannel{
|
||||||
|
Config: *config,
|
||||||
|
stream: stream,
|
||||||
|
log: config.LoggerFactory.NewLogger("datachannel"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Dial opens a data channels over SCTP.
|
||||||
|
func Dial(a *sctp.Association, id uint16, config *Config) (*DataChannel, error) {
|
||||||
|
stream, err := a.OpenStream(id, sctp.PayloadTypeWebRTCBinary)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
dc, err := Client(stream, config)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
isReliable := dc.ChannelType == ChannelTypeReliable || dc.ChannelType == ChannelTypeReliableUnordered
|
||||||
|
if isReliable && dc.ReliabilityParameter != 0 {
|
||||||
|
dc.log.Warnf("DataChannel opened with channel type %s, but has a non-zero reliability parameter: %d (expected 0)",
|
||||||
|
dc.ChannelType,
|
||||||
|
dc.ReliabilityParameter)
|
||||||
|
}
|
||||||
|
|
||||||
|
return dc, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Client opens a data channel over an SCTP stream.
|
||||||
|
func Client(stream *sctp.Stream, config *Config) (*DataChannel, error) {
|
||||||
|
msg := &channelOpen{
|
||||||
|
ChannelType: config.ChannelType,
|
||||||
|
Priority: config.Priority,
|
||||||
|
ReliabilityParameter: config.ReliabilityParameter,
|
||||||
|
|
||||||
|
Label: []byte(config.Label),
|
||||||
|
Protocol: []byte(config.Protocol),
|
||||||
|
}
|
||||||
|
|
||||||
|
if !config.Negotiated {
|
||||||
|
rawMsg, err := msg.Marshal()
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to marshal ChannelOpen %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err = stream.WriteSCTP(rawMsg, sctp.PayloadTypeWebRTCDCEP); err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to send ChannelOpen %w", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return newDataChannel(stream, config), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Accept is used to accept incoming data channels over SCTP.
|
||||||
|
func Accept(a *sctp.Association, config *Config, existingChannels ...*DataChannel) (*DataChannel, error) {
|
||||||
|
stream, err := a.AcceptStream()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
for _, ch := range existingChannels {
|
||||||
|
if ch.StreamIdentifier() == stream.StreamIdentifier() {
|
||||||
|
ch.stream.SetDefaultPayloadType(sctp.PayloadTypeWebRTCBinary)
|
||||||
|
|
||||||
|
return ch, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
stream.SetDefaultPayloadType(sctp.PayloadTypeWebRTCBinary)
|
||||||
|
|
||||||
|
dc, err := Server(stream, config)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return dc, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Server accepts a data channel over an SCTP stream.
|
||||||
|
func Server(stream *sctp.Stream, config *Config) (*DataChannel, error) {
|
||||||
|
buffer := make([]byte, receiveMTU)
|
||||||
|
n, ppi, err := stream.ReadSCTP(buffer)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if ppi != sctp.PayloadTypeWebRTCDCEP {
|
||||||
|
return nil, fmt.Errorf("%w %s", ErrInvalidPayloadProtocolIdentifier, ppi)
|
||||||
|
}
|
||||||
|
|
||||||
|
openMsg, err := parseExpectDataChannelOpen(buffer[:n])
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to parse DataChannelOpen packet %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
config.ChannelType = openMsg.ChannelType
|
||||||
|
config.Priority = openMsg.Priority
|
||||||
|
config.ReliabilityParameter = openMsg.ReliabilityParameter
|
||||||
|
config.Label = string(openMsg.Label)
|
||||||
|
config.Protocol = string(openMsg.Protocol)
|
||||||
|
|
||||||
|
dataChannel := newDataChannel(stream, config)
|
||||||
|
|
||||||
|
err = dataChannel.writeDataChannelAck()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = dataChannel.commitReliabilityParams()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return dataChannel, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read reads a packet of len(pkt) bytes as binary data.
|
||||||
|
func (c *DataChannel) Read(pkt []byte) (int, error) {
|
||||||
|
n, _, err := c.ReadDataChannel(pkt)
|
||||||
|
|
||||||
|
return n, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReadDataChannel reads a packet of len(pkt) bytes.
|
||||||
|
func (c *DataChannel) ReadDataChannel(pkt []byte) (int, bool, error) {
|
||||||
|
for {
|
||||||
|
n, ppi, err := c.stream.ReadSCTP(pkt)
|
||||||
|
if errors.Is(err, io.EOF) {
|
||||||
|
// When the peer sees that an incoming stream was
|
||||||
|
// reset, it also resets its corresponding outgoing stream.
|
||||||
|
if closeErr := c.stream.Close(); closeErr != nil {
|
||||||
|
return 0, false, closeErr
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return 0, false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if ppi == sctp.PayloadTypeWebRTCDCEP {
|
||||||
|
if err = c.handleDCEP(pkt[:n]); err != nil {
|
||||||
|
c.log.Errorf("Failed to handle DCEP: %s", err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
continue
|
||||||
|
} else if ppi == sctp.PayloadTypeWebRTCBinaryEmpty || ppi == sctp.PayloadTypeWebRTCStringEmpty {
|
||||||
|
n = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
atomic.AddUint32(&c.messagesReceived, 1)
|
||||||
|
atomic.AddUint64(&c.bytesReceived, uint64(n)) //nolint:gosec //G115
|
||||||
|
|
||||||
|
isString := ppi == sctp.PayloadTypeWebRTCString || ppi == sctp.PayloadTypeWebRTCStringEmpty
|
||||||
|
|
||||||
|
return n, isString, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetReadDeadline sets a deadline for reads to return.
|
||||||
|
func (c *DataChannel) SetReadDeadline(t time.Time) error {
|
||||||
|
return c.stream.SetReadDeadline(t)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetWriteDeadline sets a deadline for writes to return,
|
||||||
|
// only available if the BlockWrite is enabled for sctp.
|
||||||
|
func (c *DataChannel) SetWriteDeadline(t time.Time) error {
|
||||||
|
return c.stream.SetWriteDeadline(t)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MessagesSent returns the number of messages sent.
|
||||||
|
func (c *DataChannel) MessagesSent() uint32 {
|
||||||
|
return atomic.LoadUint32(&c.messagesSent)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MessagesReceived returns the number of messages received.
|
||||||
|
func (c *DataChannel) MessagesReceived() uint32 {
|
||||||
|
return atomic.LoadUint32(&c.messagesReceived)
|
||||||
|
}
|
||||||
|
|
||||||
|
// OnOpen sets an event handler which is invoked when
|
||||||
|
// a DATA_CHANNEL_ACK message is received.
|
||||||
|
// The handler is called only on thefor the channel opened
|
||||||
|
// https://datatracker.ietf.org/doc/html/draft-ietf-rtcweb-data-protocol-09#section-5.2
|
||||||
|
func (c *DataChannel) OnOpen(f func()) {
|
||||||
|
c.mu.Lock()
|
||||||
|
c.openCompleteHandlerOnce = sync.Once{}
|
||||||
|
c.onOpenCompleteHandler = f
|
||||||
|
c.mu.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *DataChannel) onOpenComplete() {
|
||||||
|
c.mu.Lock()
|
||||||
|
hdlr := c.onOpenCompleteHandler
|
||||||
|
c.mu.Unlock()
|
||||||
|
|
||||||
|
if hdlr != nil {
|
||||||
|
go c.openCompleteHandlerOnce.Do(func() {
|
||||||
|
hdlr()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// BytesSent returns the number of bytes sent.
|
||||||
|
func (c *DataChannel) BytesSent() uint64 {
|
||||||
|
return atomic.LoadUint64(&c.bytesSent)
|
||||||
|
}
|
||||||
|
|
||||||
|
// BytesReceived returns the number of bytes received.
|
||||||
|
func (c *DataChannel) BytesReceived() uint64 {
|
||||||
|
return atomic.LoadUint64(&c.bytesReceived)
|
||||||
|
}
|
||||||
|
|
||||||
|
// StreamIdentifier returns the Stream identifier associated to the stream.
|
||||||
|
func (c *DataChannel) StreamIdentifier() uint16 {
|
||||||
|
return c.stream.StreamIdentifier()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *DataChannel) handleDCEP(data []byte) error {
|
||||||
|
msg, err := parse(data)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to parse DataChannel packet %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
switch msg := msg.(type) {
|
||||||
|
case *channelAck:
|
||||||
|
if err := c.commitReliabilityParams(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
c.onOpenComplete()
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("%w, wanted ACK got %v", ErrUnexpectedDataChannelType, msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write writes len(pkt) bytes from pkt as binary data.
|
||||||
|
func (c *DataChannel) Write(pkt []byte) (n int, err error) {
|
||||||
|
return c.WriteDataChannel(pkt, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
// WriteDataChannel writes len(pkt) bytes from pkt.
|
||||||
|
func (c *DataChannel) WriteDataChannel(pkt []byte, isString bool) (n int, err error) {
|
||||||
|
// https://tools.ietf.org/html/draft-ietf-rtcweb-data-channel-12#section-6.6
|
||||||
|
// SCTP does not support the sending of empty user messages. Therefore,
|
||||||
|
// if an empty message has to be sent, the appropriate PPID (WebRTC
|
||||||
|
// String Empty or WebRTC Binary Empty) is used and the SCTP user
|
||||||
|
// message of one zero byte is sent. When receiving an SCTP user
|
||||||
|
// message with one of these PPIDs, the receiver MUST ignore the SCTP
|
||||||
|
// user message and process it as an empty message.
|
||||||
|
var ppi sctp.PayloadProtocolIdentifier
|
||||||
|
switch {
|
||||||
|
case !isString && len(pkt) > 0:
|
||||||
|
ppi = sctp.PayloadTypeWebRTCBinary
|
||||||
|
case !isString && len(pkt) == 0:
|
||||||
|
ppi = sctp.PayloadTypeWebRTCBinaryEmpty
|
||||||
|
case isString && len(pkt) > 0:
|
||||||
|
ppi = sctp.PayloadTypeWebRTCString
|
||||||
|
case isString && len(pkt) == 0:
|
||||||
|
ppi = sctp.PayloadTypeWebRTCStringEmpty
|
||||||
|
}
|
||||||
|
|
||||||
|
atomic.AddUint32(&c.messagesSent, 1)
|
||||||
|
atomic.AddUint64(&c.bytesSent, uint64(len(pkt)))
|
||||||
|
|
||||||
|
if len(pkt) == 0 {
|
||||||
|
_, err := c.stream.WriteSCTP([]byte{0}, ppi)
|
||||||
|
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.stream.WriteSCTP(pkt, ppi)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *DataChannel) writeDataChannelAck() error {
|
||||||
|
ack := channelAck{}
|
||||||
|
ackMsg, err := ack.Marshal()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to marshal ChannelOpen ACK: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err = c.stream.WriteSCTP(ackMsg, sctp.PayloadTypeWebRTCDCEP); err != nil {
|
||||||
|
return fmt.Errorf("failed to send ChannelOpen ACK: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close closes the DataChannel and the underlying SCTP stream.
|
||||||
|
func (c *DataChannel) Close() error {
|
||||||
|
// https://tools.ietf.org/html/draft-ietf-rtcweb-data-channel-13#section-6.7
|
||||||
|
// Closing of a data channel MUST be signaled by resetting the
|
||||||
|
// corresponding outgoing streams [RFC6525]. This means that if one
|
||||||
|
// side decides to close the data channel, it resets the corresponding
|
||||||
|
// outgoing stream. When the peer sees that an incoming stream was
|
||||||
|
// reset, it also resets its corresponding outgoing stream. Once this
|
||||||
|
// is completed, the data channel is closed. Resetting a stream sets
|
||||||
|
// the Stream Sequence Numbers (SSNs) of the stream back to 'zero' with
|
||||||
|
// a corresponding notification to the application layer that the reset
|
||||||
|
// has been performed. Streams are available for reuse after a reset
|
||||||
|
// has been performed.
|
||||||
|
return c.stream.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
// BufferedAmount returns the number of bytes of data currently queued to be
|
||||||
|
// sent over this stream.
|
||||||
|
func (c *DataChannel) BufferedAmount() uint64 {
|
||||||
|
return c.stream.BufferedAmount()
|
||||||
|
}
|
||||||
|
|
||||||
|
// BufferedAmountLowThreshold returns the number of bytes of buffered outgoing
|
||||||
|
// data that is considered "low." Defaults to 0.
|
||||||
|
func (c *DataChannel) BufferedAmountLowThreshold() uint64 {
|
||||||
|
return c.stream.BufferedAmountLowThreshold()
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetBufferedAmountLowThreshold is used to update the threshold.
|
||||||
|
// See BufferedAmountLowThreshold().
|
||||||
|
func (c *DataChannel) SetBufferedAmountLowThreshold(th uint64) {
|
||||||
|
c.stream.SetBufferedAmountLowThreshold(th)
|
||||||
|
}
|
||||||
|
|
||||||
|
// OnBufferedAmountLow sets the callback handler which would be called when the
|
||||||
|
// number of bytes of outgoing data buffered is lower than the threshold.
|
||||||
|
func (c *DataChannel) OnBufferedAmountLow(f func()) {
|
||||||
|
c.stream.OnBufferedAmountLow(f)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *DataChannel) commitReliabilityParams() error {
|
||||||
|
switch c.Config.ChannelType {
|
||||||
|
case ChannelTypeReliable:
|
||||||
|
c.stream.SetReliabilityParams(false, sctp.ReliabilityTypeReliable, c.Config.ReliabilityParameter) // RFC 8832 sec 5.1
|
||||||
|
if c.Config.ReliabilityParameter != 0 {
|
||||||
|
c.log.Warnf("Channel type is Reliable but has a non-zero reliability parameter: %d (expected 0)",
|
||||||
|
c.Config.ReliabilityParameter)
|
||||||
|
}
|
||||||
|
case ChannelTypeReliableUnordered:
|
||||||
|
c.stream.SetReliabilityParams(true, sctp.ReliabilityTypeReliable, c.Config.ReliabilityParameter) // RFC 8832 sec 5.1
|
||||||
|
if c.Config.ReliabilityParameter != 0 {
|
||||||
|
c.log.Warnf("Channel type is ReliableUnordered but has a non-zero reliability parameter: %d (expected 0)",
|
||||||
|
c.Config.ReliabilityParameter)
|
||||||
|
}
|
||||||
|
case ChannelTypePartialReliableRexmit:
|
||||||
|
c.stream.SetReliabilityParams(false, sctp.ReliabilityTypeRexmit, c.Config.ReliabilityParameter)
|
||||||
|
case ChannelTypePartialReliableRexmitUnordered:
|
||||||
|
c.stream.SetReliabilityParams(true, sctp.ReliabilityTypeRexmit, c.Config.ReliabilityParameter)
|
||||||
|
case ChannelTypePartialReliableTimed:
|
||||||
|
c.stream.SetReliabilityParams(false, sctp.ReliabilityTypeTimed, c.Config.ReliabilityParameter)
|
||||||
|
case ChannelTypePartialReliableTimedUnordered:
|
||||||
|
c.stream.SetReliabilityParams(true, sctp.ReliabilityTypeTimed, c.Config.ReliabilityParameter)
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("%w %v", ErrInvalidChannelType, c.Config.ChannelType)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
29
vendor/github.com/pion/datachannel/errors.go
generated
vendored
Normal file
29
vendor/github.com/pion/datachannel/errors.go
generated
vendored
Normal file
|
|
@ -0,0 +1,29 @@
|
||||||
|
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package datachannel
|
||||||
|
|
||||||
|
import "errors"
|
||||||
|
|
||||||
|
var (
|
||||||
|
// ErrDataChannelMessageTooShort means that the data isn't long enough to be a valid DataChannel message.
|
||||||
|
ErrDataChannelMessageTooShort = errors.New("DataChannel message is not long enough to determine type")
|
||||||
|
|
||||||
|
// ErrInvalidPayloadProtocolIdentifier means that we got a DataChannel messages with a Payload Protocol Identifier
|
||||||
|
// we don't know how to handle.
|
||||||
|
ErrInvalidPayloadProtocolIdentifier = errors.New(
|
||||||
|
"DataChannel message Payload Protocol Identifier is value we can't handle",
|
||||||
|
)
|
||||||
|
|
||||||
|
// ErrInvalidChannelType means that the remote requested a channel type that we don't support.
|
||||||
|
ErrInvalidChannelType = errors.New("invalid Channel Type")
|
||||||
|
|
||||||
|
// ErrInvalidMessageType is returned when a DataChannel Message has a type we don't support.
|
||||||
|
ErrInvalidMessageType = errors.New("invalid Message Type")
|
||||||
|
|
||||||
|
// ErrExpectedAndActualLengthMismatch is when the declared length and actual length don't match.
|
||||||
|
ErrExpectedAndActualLengthMismatch = errors.New("expected and actual length do not match")
|
||||||
|
|
||||||
|
// ErrUnexpectedDataChannelType is when a message type does not match the expected type.
|
||||||
|
ErrUnexpectedDataChannelType = errors.New("expected and actual message type does not match")
|
||||||
|
)
|
||||||
92
vendor/github.com/pion/datachannel/message.go
generated
vendored
Normal file
92
vendor/github.com/pion/datachannel/message.go
generated
vendored
Normal file
|
|
@ -0,0 +1,92 @@
|
||||||
|
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package datachannel
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
// message is a parsed DataChannel message.
|
||||||
|
type message interface {
|
||||||
|
Marshal() ([]byte, error)
|
||||||
|
Unmarshal([]byte) error
|
||||||
|
String() string
|
||||||
|
}
|
||||||
|
|
||||||
|
// messageType is the first byte in a DataChannel message that specifies type.
|
||||||
|
type messageType byte
|
||||||
|
|
||||||
|
// DataChannel Message Types.
|
||||||
|
const (
|
||||||
|
dataChannelAck messageType = 0x02
|
||||||
|
dataChannelOpen messageType = 0x03
|
||||||
|
)
|
||||||
|
|
||||||
|
func (t messageType) String() string {
|
||||||
|
switch t {
|
||||||
|
case dataChannelAck:
|
||||||
|
return "DataChannelAck"
|
||||||
|
case dataChannelOpen:
|
||||||
|
return "DataChannelOpen"
|
||||||
|
default:
|
||||||
|
return fmt.Sprintf("Unknown MessageType: %d", t)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// parse accepts raw input and returns a DataChannel message.
|
||||||
|
func parse(raw []byte) (message, error) {
|
||||||
|
if len(raw) == 0 {
|
||||||
|
return nil, ErrDataChannelMessageTooShort
|
||||||
|
}
|
||||||
|
|
||||||
|
var msg message
|
||||||
|
switch messageType(raw[0]) {
|
||||||
|
case dataChannelOpen:
|
||||||
|
msg = &channelOpen{}
|
||||||
|
case dataChannelAck:
|
||||||
|
msg = &channelAck{}
|
||||||
|
default:
|
||||||
|
return nil, fmt.Errorf("%w %v", ErrInvalidMessageType, messageType(raw[0]))
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := msg.Unmarshal(raw); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return msg, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// parseExpectDataChannelOpen parses a DataChannelOpen message
|
||||||
|
// or throws an error.
|
||||||
|
func parseExpectDataChannelOpen(raw []byte) (*channelOpen, error) {
|
||||||
|
if len(raw) == 0 {
|
||||||
|
return nil, ErrDataChannelMessageTooShort
|
||||||
|
}
|
||||||
|
|
||||||
|
if actualTyp := messageType(raw[0]); actualTyp != dataChannelOpen {
|
||||||
|
return nil, fmt.Errorf("%w expected(%s) actual(%s)", ErrUnexpectedDataChannelType, actualTyp, dataChannelOpen)
|
||||||
|
}
|
||||||
|
|
||||||
|
msg := &channelOpen{}
|
||||||
|
if err := msg.Unmarshal(raw); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return msg, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// TryMarshalUnmarshal attempts to marshal and unmarshal a message. Added for fuzzing.
|
||||||
|
func TryMarshalUnmarshal(msg []byte) int {
|
||||||
|
message, err := parse(msg)
|
||||||
|
if err != nil {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = message.Marshal()
|
||||||
|
if err != nil {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1
|
||||||
|
}
|
||||||
29
vendor/github.com/pion/datachannel/message_channel_ack.go
generated
vendored
Normal file
29
vendor/github.com/pion/datachannel/message_channel_ack.go
generated
vendored
Normal file
|
|
@ -0,0 +1,29 @@
|
||||||
|
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package datachannel
|
||||||
|
|
||||||
|
// channelAck is used to ACK a DataChannel open.
|
||||||
|
type channelAck struct{}
|
||||||
|
|
||||||
|
const (
|
||||||
|
channelOpenAckLength = 4
|
||||||
|
)
|
||||||
|
|
||||||
|
// Marshal returns raw bytes for the given message.
|
||||||
|
func (c *channelAck) Marshal() ([]byte, error) {
|
||||||
|
raw := make([]byte, channelOpenAckLength)
|
||||||
|
raw[0] = uint8(dataChannelAck)
|
||||||
|
|
||||||
|
return raw, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unmarshal populates the struct with the given raw data.
|
||||||
|
func (c *channelAck) Unmarshal(_ []byte) error {
|
||||||
|
// Message type already checked in Parse and there is no further data
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c channelAck) String() string {
|
||||||
|
return "ACK"
|
||||||
|
}
|
||||||
155
vendor/github.com/pion/datachannel/message_channel_open.go
generated
vendored
Normal file
155
vendor/github.com/pion/datachannel/message_channel_open.go
generated
vendored
Normal file
|
|
@ -0,0 +1,155 @@
|
||||||
|
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package datachannel
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/binary"
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
/*
|
||||||
|
channelOpen represents a DATA_CHANNEL_OPEN Message
|
||||||
|
|
||||||
|
0 1 2 3
|
||||||
|
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
|
||||||
|
|
||||||
|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||||
|
| Message Type | Channel Type | Priority |
|
||||||
|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||||
|
| Reliability Parameter |
|
||||||
|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||||
|
| Label Length | Protocol Length |
|
||||||
|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||||
|
| |
|
||||||
|
| Label |
|
||||||
|
| |
|
||||||
|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||||
|
| |
|
||||||
|
| Protocol |
|
||||||
|
| |
|
||||||
|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||||
|
.
|
||||||
|
*/
|
||||||
|
type channelOpen struct {
|
||||||
|
ChannelType ChannelType
|
||||||
|
Priority uint16
|
||||||
|
ReliabilityParameter uint32
|
||||||
|
|
||||||
|
Label []byte
|
||||||
|
Protocol []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
channelOpenHeaderLength = 12
|
||||||
|
)
|
||||||
|
|
||||||
|
// ChannelType determines the reliability of the WebRTC DataChannel.
|
||||||
|
type ChannelType byte
|
||||||
|
|
||||||
|
// ChannelType enums.
|
||||||
|
const (
|
||||||
|
// ChannelTypeReliable determines the Data Channel provides a
|
||||||
|
// reliable in-order bi-directional communication.
|
||||||
|
ChannelTypeReliable ChannelType = 0x00
|
||||||
|
// ChannelTypeReliableUnordered determines the Data Channel
|
||||||
|
// provides a reliable unordered bi-directional communication.
|
||||||
|
ChannelTypeReliableUnordered ChannelType = 0x80
|
||||||
|
// ChannelTypePartialReliableRexmit determines the Data Channel
|
||||||
|
// provides a partially-reliable in-order bi-directional communication.
|
||||||
|
// User messages will not be retransmitted more times than specified in the Reliability Parameter.
|
||||||
|
ChannelTypePartialReliableRexmit ChannelType = 0x01
|
||||||
|
// ChannelTypePartialReliableRexmitUnordered determines
|
||||||
|
// the Data Channel provides a partial reliable unordered bi-directional communication.
|
||||||
|
// User messages will not be retransmitted more times than specified in the Reliability Parameter.
|
||||||
|
ChannelTypePartialReliableRexmitUnordered ChannelType = 0x81
|
||||||
|
// ChannelTypePartialReliableTimed determines the Data Channel
|
||||||
|
// provides a partial reliable in-order bi-directional communication.
|
||||||
|
// User messages might not be transmitted or retransmitted after
|
||||||
|
// a specified life-time given in milli- seconds in the Reliability Parameter.
|
||||||
|
// This life-time starts when providing the user message to the protocol stack.
|
||||||
|
ChannelTypePartialReliableTimed ChannelType = 0x02
|
||||||
|
// The Data Channel provides a partial reliable unordered bi-directional
|
||||||
|
// communication. User messages might not be transmitted or retransmitted
|
||||||
|
// after a specified life-time given in milli- seconds in the Reliability Parameter.
|
||||||
|
// This life-time starts when providing the user message to the protocol stack.
|
||||||
|
ChannelTypePartialReliableTimedUnordered ChannelType = 0x82
|
||||||
|
)
|
||||||
|
|
||||||
|
func (c ChannelType) String() string {
|
||||||
|
switch c {
|
||||||
|
case ChannelTypeReliable:
|
||||||
|
return "ReliableOrdered"
|
||||||
|
case ChannelTypeReliableUnordered:
|
||||||
|
return "ReliableUnordered"
|
||||||
|
case ChannelTypePartialReliableRexmit:
|
||||||
|
return "PartialReliableRexmit"
|
||||||
|
case ChannelTypePartialReliableRexmitUnordered:
|
||||||
|
return "PartialReliableRexmitUnordered"
|
||||||
|
case ChannelTypePartialReliableTimed:
|
||||||
|
return "PartialReliableTimed"
|
||||||
|
case ChannelTypePartialReliableTimedUnordered:
|
||||||
|
return "PartialReliableTimedUnordered"
|
||||||
|
}
|
||||||
|
|
||||||
|
return "Unknown"
|
||||||
|
}
|
||||||
|
|
||||||
|
// ChannelPriority enums.
|
||||||
|
const (
|
||||||
|
ChannelPriorityBelowNormal uint16 = 128
|
||||||
|
ChannelPriorityNormal uint16 = 256
|
||||||
|
ChannelPriorityHigh uint16 = 512
|
||||||
|
ChannelPriorityExtraHigh uint16 = 1024
|
||||||
|
)
|
||||||
|
|
||||||
|
// Marshal returns raw bytes for the given message.
|
||||||
|
func (c *channelOpen) Marshal() ([]byte, error) {
|
||||||
|
labelLength := len(c.Label)
|
||||||
|
protocolLength := len(c.Protocol)
|
||||||
|
|
||||||
|
totalLen := channelOpenHeaderLength + labelLength + protocolLength
|
||||||
|
raw := make([]byte, totalLen)
|
||||||
|
|
||||||
|
raw[0] = uint8(dataChannelOpen)
|
||||||
|
raw[1] = byte(c.ChannelType)
|
||||||
|
|
||||||
|
binary.BigEndian.PutUint16(raw[2:], c.Priority)
|
||||||
|
binary.BigEndian.PutUint32(raw[4:], c.ReliabilityParameter)
|
||||||
|
binary.BigEndian.PutUint16(raw[8:], uint16(labelLength)) //nolint:gosec //G115
|
||||||
|
binary.BigEndian.PutUint16(raw[10:], uint16(protocolLength)) //nolint:gosec //G115
|
||||||
|
endLabel := channelOpenHeaderLength + labelLength
|
||||||
|
copy(raw[channelOpenHeaderLength:endLabel], c.Label)
|
||||||
|
copy(raw[endLabel:endLabel+protocolLength], c.Protocol)
|
||||||
|
|
||||||
|
return raw, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unmarshal populates the struct with the given raw data.
|
||||||
|
func (c *channelOpen) Unmarshal(raw []byte) error {
|
||||||
|
if len(raw) < channelOpenHeaderLength {
|
||||||
|
return fmt.Errorf("%w expected(%d) actual(%d)", ErrExpectedAndActualLengthMismatch, channelOpenHeaderLength, len(raw))
|
||||||
|
}
|
||||||
|
c.ChannelType = ChannelType(raw[1])
|
||||||
|
c.Priority = binary.BigEndian.Uint16(raw[2:])
|
||||||
|
c.ReliabilityParameter = binary.BigEndian.Uint32(raw[4:])
|
||||||
|
|
||||||
|
labelLength := binary.BigEndian.Uint16(raw[8:])
|
||||||
|
protocolLength := binary.BigEndian.Uint16(raw[10:])
|
||||||
|
|
||||||
|
if expectedLen := channelOpenHeaderLength + int(labelLength) + int(protocolLength); len(raw) != expectedLen {
|
||||||
|
return fmt.Errorf("%w expected(%d) actual(%d)", ErrExpectedAndActualLengthMismatch, expectedLen, len(raw))
|
||||||
|
}
|
||||||
|
|
||||||
|
c.Label = raw[channelOpenHeaderLength : channelOpenHeaderLength+labelLength]
|
||||||
|
c.Protocol = raw[channelOpenHeaderLength+labelLength : channelOpenHeaderLength+labelLength+protocolLength]
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c channelOpen) String() string {
|
||||||
|
return fmt.Sprintf(
|
||||||
|
"Open ChannelType(%s) Priority(%v) ReliabilityParameter(%d) Label(%s) Protocol(%s)",
|
||||||
|
c.ChannelType, c.Priority, c.ReliabilityParameter, string(c.Label), string(c.Protocol),
|
||||||
|
)
|
||||||
|
}
|
||||||
6
vendor/github.com/pion/datachannel/renovate.json
generated
vendored
Normal file
6
vendor/github.com/pion/datachannel/renovate.json
generated
vendored
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
{
|
||||||
|
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
|
||||||
|
"extends": [
|
||||||
|
"github>pion/renovate-config"
|
||||||
|
]
|
||||||
|
}
|
||||||
23
vendor/github.com/pion/dtls/v3/.editorconfig
generated
vendored
Normal file
23
vendor/github.com/pion/dtls/v3/.editorconfig
generated
vendored
Normal file
|
|
@ -0,0 +1,23 @@
|
||||||
|
# http://editorconfig.org/
|
||||||
|
# SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>
|
||||||
|
# SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
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
|
||||||
28
vendor/github.com/pion/dtls/v3/.gitignore
generated
vendored
Normal file
28
vendor/github.com/pion/dtls/v3/.gitignore
generated
vendored
Normal file
|
|
@ -0,0 +1,28 @@
|
||||||
|
# SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>
|
||||||
|
# SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
### 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
|
||||||
|
wasm_exec.js
|
||||||
147
vendor/github.com/pion/dtls/v3/.golangci.yml
generated
vendored
Normal file
147
vendor/github.com/pion/dtls/v3/.golangci.yml
generated
vendored
Normal file
|
|
@ -0,0 +1,147 @@
|
||||||
|
# SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>
|
||||||
|
# SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
version: "2"
|
||||||
|
linters:
|
||||||
|
enable:
|
||||||
|
- asciicheck # Simple linter to check that your code does not contain non-ASCII identifiers
|
||||||
|
- bidichk # Checks for dangerous unicode character sequences
|
||||||
|
- bodyclose # checks whether HTTP response body is closed successfully
|
||||||
|
- containedctx # containedctx is a linter that detects struct contained context.Context field
|
||||||
|
- contextcheck # check the function whether use a non-inherited context
|
||||||
|
- cyclop # checks function and package cyclomatic complexity
|
||||||
|
- decorder # check declaration order and count of types, constants, variables and functions
|
||||||
|
- dogsled # Checks assignments with too many blank identifiers (e.g. x, _, _, _, := f())
|
||||||
|
- dupl # Tool for code clone detection
|
||||||
|
- durationcheck # check for two durations multiplied together
|
||||||
|
- err113 # Golang linter to check the errors handling expressions
|
||||||
|
- errcheck # Errcheck is a program for checking for unchecked errors in go programs. These unchecked errors can be critical bugs in some cases
|
||||||
|
- errchkjson # Checks types passed to the json encoding functions. Reports unsupported types and optionally reports occations, where the check for the returned error can be omitted.
|
||||||
|
- errname # Checks that sentinel errors are prefixed with the `Err` and error types are suffixed with the `Error`.
|
||||||
|
- errorlint # errorlint is a linter for that can be used to find code that will cause problems with the error wrapping scheme introduced in Go 1.13.
|
||||||
|
- exhaustive # check exhaustiveness of enum switch statements
|
||||||
|
- forbidigo # Forbids identifiers
|
||||||
|
- forcetypeassert # finds forced type assertions
|
||||||
|
- gochecknoglobals # Checks that no globals 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
|
||||||
|
- gocyclo # Computes and checks the cyclomatic complexity of functions
|
||||||
|
- godot # Check if comments end in a period
|
||||||
|
- godox # Tool for detection of FIXME, TODO and other comment keywords
|
||||||
|
- goheader # Checks is file header matches to pattern
|
||||||
|
- gomoddirectives # Manage the use of 'replace', 'retract', and 'excludes' directives in go.mod.
|
||||||
|
- goprintffuncname # Checks that printf-like functions are named with `f` at the end
|
||||||
|
- gosec # Inspects source code for security problems
|
||||||
|
- govet # Vet examines Go source code and reports suspicious constructs, such as Printf calls whose arguments do not align with the format string
|
||||||
|
- grouper # An analyzer to analyze expression groups.
|
||||||
|
- importas # Enforces consistent import aliases
|
||||||
|
- ineffassign # Detects when assignments to existing variables are not used
|
||||||
|
- lll # Reports long lines
|
||||||
|
- maintidx # maintidx measures the maintainability index of each function.
|
||||||
|
- makezero # Finds slice declarations with non-zero initial length
|
||||||
|
- misspell # Finds commonly misspelled English words in comments
|
||||||
|
- nakedret # Finds naked returns in functions greater than a specified function length
|
||||||
|
- nestif # Reports deeply nested if statements
|
||||||
|
- nilerr # Finds the code that returns nil even if it checks that the error is not nil.
|
||||||
|
- nilnil # Checks that there is no simultaneous return of `nil` error and an invalid value.
|
||||||
|
- nlreturn # nlreturn checks for a new line before return and branch statements to increase code clarity
|
||||||
|
- noctx # noctx finds sending http request without context.Context
|
||||||
|
- predeclared # find code that shadows one of Go's predeclared identifiers
|
||||||
|
- revive # golint replacement, finds style mistakes
|
||||||
|
- staticcheck # Staticcheck is a go vet on steroids, applying a ton of static analysis checks
|
||||||
|
- tagliatelle # Checks the struct tags.
|
||||||
|
- thelper # thelper detects golang test helpers without t.Helper() call and checks the consistency of test helpers
|
||||||
|
- unconvert # Remove unnecessary type conversions
|
||||||
|
- unparam # Reports unused function parameters
|
||||||
|
- unused # Checks Go code for unused constants, variables, functions and types
|
||||||
|
- varnamelen # checks that the length of a variable's name matches its scope
|
||||||
|
- wastedassign # wastedassign finds wasted assignment statements
|
||||||
|
- whitespace # Tool for detection of leading and trailing whitespace
|
||||||
|
disable:
|
||||||
|
- depguard # Go linter that checks if package imports are in a list of acceptable packages
|
||||||
|
- funlen # Tool for detection of long functions
|
||||||
|
- gochecknoinits # Checks that no init functions are present in Go code
|
||||||
|
- 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.
|
||||||
|
- interfacebloat # A linter that checks length of interface.
|
||||||
|
- ireturn # Accept Interfaces, Return Concrete Types
|
||||||
|
- mnd # An analyzer to detect magic numbers
|
||||||
|
- nolintlint # Reports ill-formed or insufficient nolint directives
|
||||||
|
- paralleltest # paralleltest detects missing usage of t.Parallel() method in your Go test
|
||||||
|
- prealloc # Finds slice declarations that could potentially be preallocated
|
||||||
|
- promlinter # Check Prometheus metrics naming via promlint
|
||||||
|
- 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
|
||||||
|
- tparallel # tparallel detects inappropriate usage of t.Parallel() method in your Go test codes
|
||||||
|
- wrapcheck # Checks that errors returned from external packages are wrapped
|
||||||
|
- wsl # Whitespace Linter - Forces you to use empty lines!
|
||||||
|
settings:
|
||||||
|
staticcheck:
|
||||||
|
checks:
|
||||||
|
- all
|
||||||
|
- -QF1008 # "could remove embedded field", to keep it explicit!
|
||||||
|
- -QF1003 # "could use tagged switch on enum", Cases conflicts with exhaustive!
|
||||||
|
exhaustive:
|
||||||
|
default-signifies-exhaustive: true
|
||||||
|
forbidigo:
|
||||||
|
forbid:
|
||||||
|
- pattern: ^fmt.Print(f|ln)?$
|
||||||
|
- pattern: ^log.(Panic|Fatal|Print)(f|ln)?$
|
||||||
|
- pattern: ^os.Exit$
|
||||||
|
- pattern: ^panic$
|
||||||
|
- pattern: ^print(ln)?$
|
||||||
|
- pattern: ^testing.T.(Error|Errorf|Fatal|Fatalf|Fail|FailNow)$
|
||||||
|
pkg: ^testing$
|
||||||
|
msg: use testify/assert instead
|
||||||
|
analyze-types: true
|
||||||
|
gomodguard:
|
||||||
|
blocked:
|
||||||
|
modules:
|
||||||
|
- github.com/pkg/errors:
|
||||||
|
recommendations:
|
||||||
|
- errors
|
||||||
|
govet:
|
||||||
|
enable:
|
||||||
|
- shadow
|
||||||
|
revive:
|
||||||
|
rules:
|
||||||
|
# Prefer 'any' type alias over 'interface{}' for Go 1.18+ compatibility
|
||||||
|
- name: use-any
|
||||||
|
severity: warning
|
||||||
|
disabled: false
|
||||||
|
misspell:
|
||||||
|
locale: US
|
||||||
|
varnamelen:
|
||||||
|
max-distance: 12
|
||||||
|
min-name-length: 2
|
||||||
|
ignore-type-assert-ok: true
|
||||||
|
ignore-map-index-ok: true
|
||||||
|
ignore-chan-recv-ok: true
|
||||||
|
ignore-decls:
|
||||||
|
- i int
|
||||||
|
- n int
|
||||||
|
- w io.Writer
|
||||||
|
- r io.Reader
|
||||||
|
- b []byte
|
||||||
|
exclusions:
|
||||||
|
generated: lax
|
||||||
|
rules:
|
||||||
|
- linters:
|
||||||
|
- forbidigo
|
||||||
|
- gocognit
|
||||||
|
path: (examples|main\.go)
|
||||||
|
- linters:
|
||||||
|
- gocognit
|
||||||
|
path: _test\.go
|
||||||
|
- linters:
|
||||||
|
- forbidigo
|
||||||
|
path: cmd
|
||||||
|
formatters:
|
||||||
|
enable:
|
||||||
|
- gci # Gci control golang package import order and make it always deterministic.
|
||||||
|
- 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.
|
||||||
|
- goimports # Goimports does everything that gofmt does. Additionally it checks unused imports
|
||||||
|
exclusions:
|
||||||
|
generated: lax
|
||||||
5
vendor/github.com/pion/dtls/v3/.goreleaser.yml
generated
vendored
Normal file
5
vendor/github.com/pion/dtls/v3/.goreleaser.yml
generated
vendored
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
# SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>
|
||||||
|
# SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
builds:
|
||||||
|
- skip: true
|
||||||
9
vendor/github.com/pion/dtls/v3/LICENSE
generated
vendored
Normal file
9
vendor/github.com/pion/dtls/v3/LICENSE
generated
vendored
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2026 The Pion community <https://pion.ly>
|
||||||
|
|
||||||
|
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.
|
||||||
159
vendor/github.com/pion/dtls/v3/README.md
generated
vendored
Normal file
159
vendor/github.com/pion/dtls/v3/README.md
generated
vendored
Normal file
|
|
@ -0,0 +1,159 @@
|
||||||
|
<h1 align="center">
|
||||||
|
<br>
|
||||||
|
Pion DTLS
|
||||||
|
<br>
|
||||||
|
</h1>
|
||||||
|
<h4 align="center">A Go implementation of DTLS</h4>
|
||||||
|
<p align="center">
|
||||||
|
<a href="https://pion.ly"><img src="https://img.shields.io/badge/pion-dtls-gray.svg?longCache=true&colorB=brightgreen" alt="Pion DTLS"></a>
|
||||||
|
<a href="https://sourcegraph.com/github.com/pion/dtls"><img src="https://sourcegraph.com/github.com/pion/dtls/-/badge.svg" alt="Sourcegraph Widget"></a>
|
||||||
|
<a href="https://discord.gg/PngbdqpFbt"><img src="https://img.shields.io/badge/join-us%20on%20discord-gray.svg?longCache=true&logo=discord&colorB=brightblue" alt="join us on Discord"></a> <a href="https://bsky.app/profile/pion.ly"><img src="https://img.shields.io/badge/follow-us%20on%20bluesky-gray.svg?longCache=true&logo=bluesky&colorB=brightblue" alt="Follow us on Bluesky"></a>
|
||||||
|
<br>
|
||||||
|
<img alt="GitHub Workflow Status" src="https://img.shields.io/github/actions/workflow/status/pion/dtls/test.yaml">
|
||||||
|
<a href="https://pkg.go.dev/github.com/pion/dtls/v3"><img src="https://pkg.go.dev/badge/github.com/pion/dtls/v3.svg" alt="Go Reference"></a>
|
||||||
|
<a href="https://codecov.io/gh/pion/dtls"><img src="https://codecov.io/gh/pion/dtls/branch/master/graph/badge.svg" alt="Coverage Status"></a>
|
||||||
|
<a href="https://goreportcard.com/report/github.com/pion/dtls/v3"><img src="https://goreportcard.com/badge/github.com/pion/dtls/v3" alt="Go Report Card"></a>
|
||||||
|
<a href="LICENSE"><img src="https://img.shields.io/badge/License-MIT-yellow.svg" alt="License: MIT"></a>
|
||||||
|
</p>
|
||||||
|
<br>
|
||||||
|
|
||||||
|
Native [DTLS 1.2][rfc6347] implementation in the Go programming language.
|
||||||
|
|
||||||
|
A long term goal is a professional security review, and maybe an inclusion in stdlib.
|
||||||
|
|
||||||
|
### RFCs
|
||||||
|
#### Implemented
|
||||||
|
- **RFC 6347**: [Datagram Transport Layer Security Version 1.2][rfc6347]
|
||||||
|
- **RFC 5705**: [Keying Material Exporters for Transport Layer Security (TLS)][rfc5705]
|
||||||
|
- **RFC 7627**: [Transport Layer Security (TLS) - Session Hash and Extended Master Secret Extension][rfc7627]
|
||||||
|
- **RFC 7301**: [Transport Layer Security (TLS) - Application-Layer Protocol Negotiation Extension][rfc7301]
|
||||||
|
|
||||||
|
[rfc5289]: https://tools.ietf.org/html/rfc5289
|
||||||
|
[rfc5487]: https://tools.ietf.org/html/rfc5487
|
||||||
|
[rfc5489]: https://tools.ietf.org/html/rfc5489
|
||||||
|
[rfc5705]: https://tools.ietf.org/html/rfc5705
|
||||||
|
[rfc6347]: https://tools.ietf.org/html/rfc6347
|
||||||
|
[rfc6655]: https://tools.ietf.org/html/rfc6655
|
||||||
|
[rfc7301]: https://tools.ietf.org/html/rfc7301
|
||||||
|
[rfc7627]: https://tools.ietf.org/html/rfc7627
|
||||||
|
[rfc8422]: https://tools.ietf.org/html/rfc8422
|
||||||
|
[rfc9147]: https://tools.ietf.org/html/rfc9147
|
||||||
|
|
||||||
|
### Goals/Progress
|
||||||
|
This will only be targeting DTLS 1.2, and the most modern/common cipher suites.
|
||||||
|
We would love contributions that fall under the 'Planned Features' and any bug fixes!
|
||||||
|
|
||||||
|
#### 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])
|
||||||
|
* ALPN extension ([RFC 7301][rfc7301])
|
||||||
|
|
||||||
|
#### 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_GCM_SHA384 ([RFC 5289][rfc5289])
|
||||||
|
* TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 ([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_256_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])
|
||||||
|
|
||||||
|
##### ECDHE & PSK
|
||||||
|
|
||||||
|
* TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256 ([RFC 5489][rfc5489])
|
||||||
|
|
||||||
|
#### Planned Features
|
||||||
|
* DTLS 1.3 ([RFC 9147][rfc9147])
|
||||||
|
* Chacha20Poly1305
|
||||||
|
|
||||||
|
#### Excluded Features
|
||||||
|
* DTLS 1.0
|
||||||
|
* Renegotiation
|
||||||
|
* Compression
|
||||||
|
|
||||||
|
### Using
|
||||||
|
|
||||||
|
This library needs at least Go 1.21, 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
|
||||||
|
```
|
||||||
|
|
||||||
|
### Community
|
||||||
|
Pion has an active community on the [Discord](https://discord.gg/PngbdqpFbt).
|
||||||
|
|
||||||
|
Follow the [Pion Bluesky](https://bsky.app/profile/pion.ly) or [Pion Twitter](https://twitter.com/_pion) for project updates and important WebRTC news.
|
||||||
|
|
||||||
|
We are always looking to support **your projects**. Please reach out if you have something to build!
|
||||||
|
If you need commercial support or don't want to use public methods you can contact us at [team@pion.ly](mailto:team@pion.ly)
|
||||||
|
|
||||||
|
### Contributing
|
||||||
|
Check out the [contributing wiki](https://github.com/pion/webrtc/wiki/Contributing) to join the group of amazing people making this project possible
|
||||||
|
|
||||||
|
### Funding
|
||||||
|
<a href="https://nlnet.nl/"><img src="https://nlnet.nl/logo/banner.svg" alt="NLnet foundation logo" width="200"></a>
|
||||||
|
<a href="https://nlnet.nl/commonsfund/"><img src="https://nlnet.nl/image/logos/NGI0Core_tag.svg" alt="NLnet foundation logo" width="200"></a>
|
||||||
|
|
||||||
|
The DTLS 1.3 implementation in this project is funded through the [NGI0 Commons Fund](https://nlnet.nl/commonsfund), a fund established by [NLnet](https://nlnet.nl/) with financial support from the European Commission's [Next Generation Internet](https://ngi.eu/) programme, under the aegis of [DG Communications Networks, Content and Technology](https://commission.europa.eu/about-european-commission/departments-and-executive-agencies/communications-networks-content-and-technology_en) under grant agreement No [101135429](https://cordis.europa.eu/project/id/101135429). Additional funding is made available by the [Swiss State Secretariat for Education, Research and Innovation](https://www.sbfi.admin.ch/sbfi/en/home.html) (SERI). Learn more on the [NLnet project page](https://nlnet.nl/project/PION-DTLS1.3/).
|
||||||
|
|
||||||
|
### License
|
||||||
|
MIT License - see [LICENSE](LICENSE) for full text
|
||||||
167
vendor/github.com/pion/dtls/v3/certificate.go
generated
vendored
Normal file
167
vendor/github.com/pion/dtls/v3/certificate.go
generated
vendored
Normal file
|
|
@ -0,0 +1,167 @@
|
||||||
|
// SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package dtls
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"crypto/tls"
|
||||||
|
"crypto/x509"
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/pion/dtls/v3/pkg/protocol/handshake"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ClientHelloInfo contains information from a ClientHello message in order to
|
||||||
|
// guide application logic in the GetCertificate.
|
||||||
|
type ClientHelloInfo struct {
|
||||||
|
// ServerName indicates the name of the server requested by the client
|
||||||
|
// in order to support virtual hosting. ServerName is only set if the
|
||||||
|
// client is using SNI (see RFC 4366, Section 3.1).
|
||||||
|
ServerName string
|
||||||
|
|
||||||
|
// CipherSuites lists the CipherSuites supported by the client (e.g.
|
||||||
|
// TLS_AES_128_GCM_SHA256, TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256).
|
||||||
|
CipherSuites []CipherSuiteID
|
||||||
|
|
||||||
|
// RandomBytes stores the client hello random bytes
|
||||||
|
RandomBytes [handshake.RandomBytesLength]byte
|
||||||
|
}
|
||||||
|
|
||||||
|
// CertificateRequestInfo contains information from a server's
|
||||||
|
// CertificateRequest message, which is used to demand a certificate and proof
|
||||||
|
// of control from a client.
|
||||||
|
type CertificateRequestInfo struct {
|
||||||
|
// AcceptableCAs contains zero or more, DER-encoded, X.501
|
||||||
|
// Distinguished Names. These are the names of root or intermediate CAs
|
||||||
|
// that the server wishes the returned certificate to be signed by. An
|
||||||
|
// empty slice indicates that the server has no preference.
|
||||||
|
AcceptableCAs [][]byte
|
||||||
|
}
|
||||||
|
|
||||||
|
// SupportsCertificate returns nil if the provided certificate is supported by
|
||||||
|
// the server that sent the CertificateRequest. Otherwise, it returns an error
|
||||||
|
// describing the reason for the incompatibility.
|
||||||
|
// NOTE: original src:
|
||||||
|
// https://github.com/golang/go/blob/29b9a328d268d53833d2cc063d1d8b4bf6852675/src/crypto/tls/common.go#L1273
|
||||||
|
func (cri *CertificateRequestInfo) SupportsCertificate(c *tls.Certificate) error {
|
||||||
|
if len(cri.AcceptableCAs) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
for j, cert := range c.Certificate {
|
||||||
|
x509Cert := c.Leaf
|
||||||
|
// Parse the certificate if this isn't the leaf node, or if
|
||||||
|
// chain.Leaf was nil.
|
||||||
|
if j != 0 || x509Cert == nil {
|
||||||
|
var err error
|
||||||
|
if x509Cert, err = x509.ParseCertificate(cert); err != nil {
|
||||||
|
return fmt.Errorf("failed to parse certificate #%d in the chain: %w", j, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, ca := range cri.AcceptableCAs {
|
||||||
|
if bytes.Equal(x509Cert.RawIssuer, ca) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return errNotAcceptableCertificateChain
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *handshakeConfig) setNameToCertificateLocked() {
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
//nolint:cyclop
|
||||||
|
func (c *handshakeConfig) getCertificate(clientHelloInfo *ClientHelloInfo) (*tls.Certificate, error) {
|
||||||
|
c.mu.Lock()
|
||||||
|
defer c.mu.Unlock()
|
||||||
|
|
||||||
|
if c.localGetCertificate != nil &&
|
||||||
|
(len(c.localCertificates) == 0 || len(clientHelloInfo.ServerName) > 0) {
|
||||||
|
cert, err := c.localGetCertificate(clientHelloInfo)
|
||||||
|
if cert != nil || err != nil {
|
||||||
|
return cert, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.nameToCertificate == nil {
|
||||||
|
c.setNameToCertificateLocked()
|
||||||
|
}
|
||||||
|
|
||||||
|
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(clientHelloInfo.ServerName) == 0 {
|
||||||
|
return &c.localCertificates[0], nil
|
||||||
|
}
|
||||||
|
|
||||||
|
name := strings.TrimRight(strings.ToLower(clientHelloInfo.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
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOTE: original src:
|
||||||
|
// https://github.com/golang/go/blob/29b9a328d268d53833d2cc063d1d8b4bf6852675/src/crypto/tls/handshake_client.go#L974
|
||||||
|
func (c *handshakeConfig) getClientCertificate(cri *CertificateRequestInfo) (*tls.Certificate, error) {
|
||||||
|
c.mu.Lock()
|
||||||
|
defer c.mu.Unlock()
|
||||||
|
if c.localGetClientCertificate != nil {
|
||||||
|
return c.localGetClientCertificate(cri)
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := range c.localCertificates {
|
||||||
|
chain := c.localCertificates[i]
|
||||||
|
if err := cri.SupportsCertificate(&chain); err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
return &chain, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// No acceptable certificate found. Don't send a certificate.
|
||||||
|
return new(tls.Certificate), nil
|
||||||
|
}
|
||||||
295
vendor/github.com/pion/dtls/v3/cipher_suite.go
generated
vendored
Normal file
295
vendor/github.com/pion/dtls/v3/cipher_suite.go
generated
vendored
Normal file
|
|
@ -0,0 +1,295 @@
|
||||||
|
// SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package dtls
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto"
|
||||||
|
"crypto/ecdsa"
|
||||||
|
"crypto/ed25519"
|
||||||
|
"crypto/rsa"
|
||||||
|
"crypto/tls"
|
||||||
|
"fmt"
|
||||||
|
"hash"
|
||||||
|
|
||||||
|
"github.com/pion/dtls/v3/internal/ciphersuite"
|
||||||
|
"github.com/pion/dtls/v3/pkg/crypto/clientcertificate"
|
||||||
|
"github.com/pion/dtls/v3/pkg/protocol/recordlayer"
|
||||||
|
)
|
||||||
|
|
||||||
|
// CipherSuiteID is an ID for our supported CipherSuites.
|
||||||
|
type CipherSuiteID = ciphersuite.ID
|
||||||
|
|
||||||
|
// Supported Cipher Suites.
|
||||||
|
const (
|
||||||
|
|
||||||
|
// nolint: godot
|
||||||
|
// AES-128-CCM
|
||||||
|
TLS_ECDHE_ECDSA_WITH_AES_128_CCM CipherSuiteID = ciphersuite.TLS_ECDHE_ECDSA_WITH_AES_128_CCM // nolint: revive,staticcheck,lll
|
||||||
|
TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8 CipherSuiteID = ciphersuite.TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8 // nolint: revive,staticcheck,lll
|
||||||
|
|
||||||
|
// nolint: godot
|
||||||
|
// AES-128-GCM-SHA256
|
||||||
|
TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 CipherSuiteID = ciphersuite.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 // nolint: revive,staticcheck,lll
|
||||||
|
TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 CipherSuiteID = ciphersuite.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 // nolint: revive,staticcheck,lll
|
||||||
|
|
||||||
|
TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 CipherSuiteID = ciphersuite.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 // nolint: revive,staticcheck,lll
|
||||||
|
TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 CipherSuiteID = ciphersuite.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 // nolint: revive,staticcheck,lll
|
||||||
|
|
||||||
|
// nolint: godot
|
||||||
|
// AES-256-CBC-SHA
|
||||||
|
TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA CipherSuiteID = ciphersuite.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA // nolint: revive,staticcheck,lll
|
||||||
|
TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA CipherSuiteID = ciphersuite.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA // nolint: revive,staticcheck,lll
|
||||||
|
|
||||||
|
TLS_PSK_WITH_AES_128_CCM CipherSuiteID = ciphersuite.TLS_PSK_WITH_AES_128_CCM // nolint: revive,staticcheck,lll
|
||||||
|
TLS_PSK_WITH_AES_128_CCM_8 CipherSuiteID = ciphersuite.TLS_PSK_WITH_AES_128_CCM_8 // nolint: revive,staticcheck,lll
|
||||||
|
TLS_PSK_WITH_AES_256_CCM_8 CipherSuiteID = ciphersuite.TLS_PSK_WITH_AES_256_CCM_8 // nolint: revive,staticcheck,lll
|
||||||
|
TLS_PSK_WITH_AES_128_GCM_SHA256 CipherSuiteID = ciphersuite.TLS_PSK_WITH_AES_128_GCM_SHA256 // nolint: revive,staticcheck,lll
|
||||||
|
TLS_PSK_WITH_AES_128_CBC_SHA256 CipherSuiteID = ciphersuite.TLS_PSK_WITH_AES_128_CBC_SHA256 // nolint: revive,staticcheck,lll
|
||||||
|
|
||||||
|
TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256 CipherSuiteID = ciphersuite.TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256 // nolint: revive,staticcheck,lll
|
||||||
|
)
|
||||||
|
|
||||||
|
// 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
|
||||||
|
)
|
||||||
|
|
||||||
|
// CipherSuiteKeyExchangeAlgorithm controls what exchange algorithm is using during the handshake for a CipherSuite.
|
||||||
|
type CipherSuiteKeyExchangeAlgorithm = ciphersuite.KeyExchangeAlgorithm
|
||||||
|
|
||||||
|
// CipherSuiteKeyExchangeAlgorithm Bitmask.
|
||||||
|
const (
|
||||||
|
CipherSuiteKeyExchangeAlgorithmNone CipherSuiteKeyExchangeAlgorithm = ciphersuite.KeyExchangeAlgorithmNone
|
||||||
|
CipherSuiteKeyExchangeAlgorithmPsk CipherSuiteKeyExchangeAlgorithm = ciphersuite.KeyExchangeAlgorithmPsk
|
||||||
|
CipherSuiteKeyExchangeAlgorithmEcdhe CipherSuiteKeyExchangeAlgorithm = ciphersuite.KeyExchangeAlgorithmEcdhe
|
||||||
|
)
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
// KeyExchangeAlgorithm controls what exchange algorithm is using during the handshake
|
||||||
|
KeyExchangeAlgorithm() CipherSuiteKeyExchangeAlgorithm
|
||||||
|
|
||||||
|
// ECC (Elliptic Curve Cryptography) determines whether ECC extesions will be send during handshake.
|
||||||
|
// https://datatracker.ietf.org/doc/html/rfc4492#page-10
|
||||||
|
ECC() bool
|
||||||
|
|
||||||
|
// 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(h recordlayer.Header, 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 { //nolint:cyclop
|
||||||
|
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_256_CCM_8:
|
||||||
|
return ciphersuite.NewTLSPskWithAes256Ccm8()
|
||||||
|
case TLS_PSK_WITH_AES_128_GCM_SHA256:
|
||||||
|
return &ciphersuite.TLSPskWithAes128GcmSha256{}
|
||||||
|
case TLS_PSK_WITH_AES_128_CBC_SHA256:
|
||||||
|
return &ciphersuite.TLSPskWithAes128CbcSha256{}
|
||||||
|
case TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384:
|
||||||
|
return &ciphersuite.TLSEcdheEcdsaWithAes256GcmSha384{}
|
||||||
|
case TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384:
|
||||||
|
return &ciphersuite.TLSEcdheRsaWithAes256GcmSha384{}
|
||||||
|
case TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256:
|
||||||
|
return ciphersuite.NewTLSEcdhePskWithAes128CbcSha256()
|
||||||
|
}
|
||||||
|
|
||||||
|
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{},
|
||||||
|
&ciphersuite.TLSEcdheEcdsaWithAes256GcmSha384{},
|
||||||
|
&ciphersuite.TLSEcdheRsaWithAes256GcmSha384{},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func allCipherSuites() []CipherSuite {
|
||||||
|
return []CipherSuite{
|
||||||
|
ciphersuite.NewTLSEcdheEcdsaWithAes128Ccm(),
|
||||||
|
ciphersuite.NewTLSEcdheEcdsaWithAes128Ccm8(),
|
||||||
|
&ciphersuite.TLSEcdheEcdsaWithAes128GcmSha256{},
|
||||||
|
&ciphersuite.TLSEcdheRsaWithAes128GcmSha256{},
|
||||||
|
&ciphersuite.TLSEcdheEcdsaWithAes256CbcSha{},
|
||||||
|
&ciphersuite.TLSEcdheRsaWithAes256CbcSha{},
|
||||||
|
ciphersuite.NewTLSPskWithAes128Ccm(),
|
||||||
|
ciphersuite.NewTLSPskWithAes128Ccm8(),
|
||||||
|
ciphersuite.NewTLSPskWithAes256Ccm8(),
|
||||||
|
&ciphersuite.TLSPskWithAes128GcmSha256{},
|
||||||
|
&ciphersuite.TLSEcdheEcdsaWithAes256GcmSha384{},
|
||||||
|
&ciphersuite.TLSEcdheRsaWithAes256GcmSha384{},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func cipherSuiteIDs(cipherSuites []CipherSuite) []uint16 {
|
||||||
|
rtrn := []uint16{}
|
||||||
|
for _, c := range cipherSuites {
|
||||||
|
rtrn = append(rtrn, uint16(c.ID()))
|
||||||
|
}
|
||||||
|
|
||||||
|
return rtrn
|
||||||
|
}
|
||||||
|
|
||||||
|
//nolint:cyclop
|
||||||
|
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, &invalidCipherSuiteError{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
|
||||||
|
}
|
||||||
|
|
||||||
|
func filterCipherSuitesForCertificate(cert *tls.Certificate, cipherSuites []CipherSuite) []CipherSuite {
|
||||||
|
if cert == nil || cert.PrivateKey == nil {
|
||||||
|
return cipherSuites
|
||||||
|
}
|
||||||
|
signer, ok := cert.PrivateKey.(crypto.Signer)
|
||||||
|
if !ok {
|
||||||
|
return cipherSuites
|
||||||
|
}
|
||||||
|
|
||||||
|
var certType clientcertificate.Type
|
||||||
|
switch signer.Public().(type) {
|
||||||
|
case ed25519.PublicKey, *ecdsa.PublicKey:
|
||||||
|
certType = clientcertificate.ECDSASign
|
||||||
|
case *rsa.PublicKey:
|
||||||
|
certType = clientcertificate.RSASign
|
||||||
|
}
|
||||||
|
|
||||||
|
filtered := []CipherSuite{}
|
||||||
|
for _, c := range cipherSuites {
|
||||||
|
if c.AuthenticationType() != CipherSuiteAuthenticationTypeCertificate || certType == c.CertificateType() {
|
||||||
|
filtered = append(filtered, c)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return filtered
|
||||||
|
}
|
||||||
46
vendor/github.com/pion/dtls/v3/cipher_suite_go114.go
generated
vendored
Normal file
46
vendor/github.com/pion/dtls/v3/cipher_suite_go114.go
generated
vendored
Normal file
|
|
@ -0,0 +1,46 @@
|
||||||
|
// SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
//go:build go1.14
|
||||||
|
// +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
|
||||||
|
}
|
||||||
22
vendor/github.com/pion/dtls/v3/codecov.yml
generated
vendored
Normal file
22
vendor/github.com/pion/dtls/v3/codecov.yml
generated
vendored
Normal file
|
|
@ -0,0 +1,22 @@
|
||||||
|
#
|
||||||
|
# DO NOT EDIT THIS FILE
|
||||||
|
#
|
||||||
|
# It is automatically copied from https://github.com/pion/.goassets repository.
|
||||||
|
#
|
||||||
|
# SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>
|
||||||
|
# SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
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/**/*"
|
||||||
12
vendor/github.com/pion/dtls/v3/compression_method.go
generated
vendored
Normal file
12
vendor/github.com/pion/dtls/v3/compression_method.go
generated
vendored
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
// SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package dtls
|
||||||
|
|
||||||
|
import "github.com/pion/dtls/v3/pkg/protocol"
|
||||||
|
|
||||||
|
func defaultCompressionMethods() []*protocol.CompressionMethod {
|
||||||
|
return []*protocol.CompressionMethod{
|
||||||
|
{},
|
||||||
|
}
|
||||||
|
}
|
||||||
306
vendor/github.com/pion/dtls/v3/config.go
generated
vendored
Normal file
306
vendor/github.com/pion/dtls/v3/config.go
generated
vendored
Normal file
|
|
@ -0,0 +1,306 @@
|
||||||
|
// SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package dtls
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto"
|
||||||
|
"crypto/ecdsa"
|
||||||
|
"crypto/ed25519"
|
||||||
|
"crypto/rsa"
|
||||||
|
"crypto/tls"
|
||||||
|
"crypto/x509"
|
||||||
|
"io"
|
||||||
|
"net"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/pion/dtls/v3/pkg/crypto/elliptic"
|
||||||
|
"github.com/pion/dtls/v3/pkg/protocol/handshake"
|
||||||
|
"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.
|
||||||
|
//
|
||||||
|
// Deprecated: prefer the options-based APIs (`*WithOptions`) to construct immutable configs,
|
||||||
|
// This will be removed in the next major version.
|
||||||
|
type Config struct { //nolint:dupl
|
||||||
|
// 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
|
||||||
|
|
||||||
|
// CertificateSignatureSchemes contains the signature and hash schemes that may be used
|
||||||
|
// in digital signatures for X.509 certificates. If not set, the signature_algorithms_cert
|
||||||
|
// extension is not sent, and SignatureSchemes is used for both handshake signatures and
|
||||||
|
// certificate chain validation, as specified in RFC 8446 Section 4.2.3.
|
||||||
|
CertificateSignatureSchemes []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
|
||||||
|
|
||||||
|
// SRTPMasterKeyIdentifier value (if any) is sent via the use_srtp
|
||||||
|
// extension for Clients and Servers
|
||||||
|
SRTPMasterKeyIdentifier []byte
|
||||||
|
|
||||||
|
// 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
|
||||||
|
|
||||||
|
// DisableRetransmitBackoff can be used to the disable the backoff feature
|
||||||
|
// when sending outbound messages as specified in RFC 4347 4.2.4.1
|
||||||
|
DisableRetransmitBackoff bool
|
||||||
|
|
||||||
|
// 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
|
||||||
|
|
||||||
|
// 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
|
||||||
|
|
||||||
|
// VerifyConnection, if not nil, is called after normal certificate
|
||||||
|
// verification/PSK and after VerifyPeerCertificate by either a TLS client
|
||||||
|
// or server. 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. This callback will run for all connections
|
||||||
|
// regardless of InsecureSkipVerify or ClientAuth settings.
|
||||||
|
VerifyConnection func(*State) 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
|
||||||
|
|
||||||
|
// 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
|
||||||
|
|
||||||
|
// SessionStore is the container to store session for resumption.
|
||||||
|
SessionStore SessionStore
|
||||||
|
|
||||||
|
// List of application protocols the peer supports, for ALPN
|
||||||
|
SupportedProtocols []string
|
||||||
|
|
||||||
|
// List of Elliptic Curves to use
|
||||||
|
//
|
||||||
|
// If an ECC ciphersuite is configured and EllipticCurves is empty
|
||||||
|
// it will default to X25519, P-256, P-384 in this specific order.
|
||||||
|
EllipticCurves []elliptic.Curve
|
||||||
|
|
||||||
|
// GetCertificate returns a Certificate based on the given
|
||||||
|
// ClientHelloInfo. It will only be called if the client supplies SNI
|
||||||
|
// information or if Certificates is empty.
|
||||||
|
//
|
||||||
|
// If GetCertificate is nil or returns nil, then the certificate is
|
||||||
|
// retrieved from NameToCertificate. If NameToCertificate is nil, the
|
||||||
|
// best element of Certificates will be used.
|
||||||
|
GetCertificate func(*ClientHelloInfo) (*tls.Certificate, error)
|
||||||
|
|
||||||
|
// GetClientCertificate, if not nil, is called when a server requests a
|
||||||
|
// certificate from a client. If set, the contents of Certificates will
|
||||||
|
// be ignored.
|
||||||
|
//
|
||||||
|
// If GetClientCertificate returns an error, the handshake will be
|
||||||
|
// aborted and that error will be returned. Otherwise
|
||||||
|
// GetClientCertificate must return a non-nil Certificate. If
|
||||||
|
// Certificate.Certificate is empty then no certificate will be sent to
|
||||||
|
// the server. If this is unacceptable to the server then it may abort
|
||||||
|
// the handshake.
|
||||||
|
GetClientCertificate func(*CertificateRequestInfo) (*tls.Certificate, error)
|
||||||
|
|
||||||
|
// InsecureSkipVerifyHello, if true and when acting as server, allow client to
|
||||||
|
// skip hello verify phase and receive ServerHello after initial ClientHello.
|
||||||
|
// This have implication on DoS attack resistance.
|
||||||
|
InsecureSkipVerifyHello bool
|
||||||
|
|
||||||
|
// ConnectionIDGenerator generates connection identifiers that should be
|
||||||
|
// sent by the remote party if it supports the DTLS Connection Identifier
|
||||||
|
// extension, as determined during the handshake. Generated connection
|
||||||
|
// identifiers must always have the same length. Returning a zero-length
|
||||||
|
// connection identifier indicates that the local party supports sending
|
||||||
|
// connection identifiers but does not require the remote party to send
|
||||||
|
// them. A nil ConnectionIDGenerator indicates that connection identifiers
|
||||||
|
// are not supported.
|
||||||
|
// https://datatracker.ietf.org/doc/html/rfc9146
|
||||||
|
ConnectionIDGenerator func() []byte
|
||||||
|
|
||||||
|
// PaddingLengthGenerator generates the number of padding bytes used to
|
||||||
|
// inflate ciphertext size in order to obscure content size from observers.
|
||||||
|
// The length of the content is passed to the generator such that both
|
||||||
|
// deterministic and random padding schemes can be applied while not
|
||||||
|
// exceeding maximum record size.
|
||||||
|
// If no PaddingLengthGenerator is specified, padding will not be applied.
|
||||||
|
// https://datatracker.ietf.org/doc/html/rfc9146#section-4
|
||||||
|
PaddingLengthGenerator func(uint) uint
|
||||||
|
|
||||||
|
// HelloRandomBytesGenerator generates custom client hello random bytes.
|
||||||
|
HelloRandomBytesGenerator func() [handshake.RandomBytesLength]byte
|
||||||
|
|
||||||
|
// Handshake hooks: hooks can be used for testing invalid messages,
|
||||||
|
// mimicking other implementations or randomizing fields, which is valuable
|
||||||
|
// for applications that need censorship-resistance by making
|
||||||
|
// fingerprinting more difficult.
|
||||||
|
|
||||||
|
// ClientHelloMessageHook, if not nil, is called when a Client Hello message is sent
|
||||||
|
// from a client. The returned handshake message replaces the original message.
|
||||||
|
ClientHelloMessageHook func(handshake.MessageClientHello) handshake.Message
|
||||||
|
|
||||||
|
// ServerHelloMessageHook, if not nil, is called when a Server Hello message is sent
|
||||||
|
// from a server. The returned handshake message replaces the original message.
|
||||||
|
ServerHelloMessageHook func(handshake.MessageServerHello) handshake.Message
|
||||||
|
|
||||||
|
// CertificateRequestMessageHook, if not nil, is called when a Certificate Request
|
||||||
|
// message is sent from a server. The returned handshake message replaces the original message.
|
||||||
|
CertificateRequestMessageHook func(handshake.MessageCertificateRequest) handshake.Message
|
||||||
|
|
||||||
|
// OnConnectionAttempt is fired Whenever a connection attempt is made,
|
||||||
|
// the server or application can call this callback function.
|
||||||
|
// The callback function can then implement logic to handle the connection attempt, such as logging the attempt,
|
||||||
|
// checking against a list of blocked IPs, or counting the attempts to prevent brute force attacks.
|
||||||
|
// If the callback function returns an error, the connection attempt will be aborted.
|
||||||
|
OnConnectionAttempt func(net.Addr) error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Config) includeCertificateSuites() bool {
|
||||||
|
return c.PSK == nil || len(c.Certificates) > 0 || c.GetCertificate != nil || c.GetClientCertificate != nil
|
||||||
|
}
|
||||||
|
|
||||||
|
const defaultMTU = 1200 // bytes
|
||||||
|
|
||||||
|
var defaultCurves = []elliptic.Curve{elliptic.X25519, elliptic.P256, elliptic.P384} //nolint:gochecknoglobals
|
||||||
|
|
||||||
|
// 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 { //nolint:cyclop
|
||||||
|
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 {
|
||||||
|
signer, ok := cert.PrivateKey.(crypto.Signer)
|
||||||
|
if !ok {
|
||||||
|
return errInvalidPrivateKey
|
||||||
|
}
|
||||||
|
switch signer.Public().(type) {
|
||||||
|
case ed25519.PublicKey:
|
||||||
|
case *ecdsa.PublicKey:
|
||||||
|
case *rsa.PublicKey:
|
||||||
|
default:
|
||||||
|
return errInvalidPrivateKey
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := parseCipherSuites(
|
||||||
|
config.CipherSuites, config.CustomCipherSuites, config.includeCertificateSuites(), config.PSK != nil,
|
||||||
|
)
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
1397
vendor/github.com/pion/dtls/v3/conn.go
generated
vendored
Normal file
1397
vendor/github.com/pion/dtls/v3/conn.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load diff
105
vendor/github.com/pion/dtls/v3/connection_id.go
generated
vendored
Normal file
105
vendor/github.com/pion/dtls/v3/connection_id.go
generated
vendored
Normal file
|
|
@ -0,0 +1,105 @@
|
||||||
|
// SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package dtls
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/rand"
|
||||||
|
|
||||||
|
"github.com/pion/dtls/v3/pkg/protocol"
|
||||||
|
"github.com/pion/dtls/v3/pkg/protocol/extension"
|
||||||
|
"github.com/pion/dtls/v3/pkg/protocol/handshake"
|
||||||
|
"github.com/pion/dtls/v3/pkg/protocol/recordlayer"
|
||||||
|
)
|
||||||
|
|
||||||
|
// RandomCIDGenerator is a random Connection ID generator where CID is the
|
||||||
|
// specified size. Specifying a size of 0 will indicate to peers that sending a
|
||||||
|
// Connection ID is not necessary.
|
||||||
|
func RandomCIDGenerator(size int) func() []byte {
|
||||||
|
return func() []byte {
|
||||||
|
cid := make([]byte, size)
|
||||||
|
if _, err := rand.Read(cid); err != nil {
|
||||||
|
panic(err) //nolint -- nonrecoverable
|
||||||
|
}
|
||||||
|
|
||||||
|
return cid
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// OnlySendCIDGenerator enables sending Connection IDs negotiated with a peer,
|
||||||
|
// but indicates to the peer that sending Connection IDs in return is not
|
||||||
|
// necessary.
|
||||||
|
func OnlySendCIDGenerator() func() []byte {
|
||||||
|
return func() []byte {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// cidDatagramRouter extracts connection IDs from incoming datagram payloads and
|
||||||
|
// uses them to route to the proper connection.
|
||||||
|
// NOTE: properly routing datagrams based on connection IDs requires using
|
||||||
|
// constant size connection IDs.
|
||||||
|
func cidDatagramRouter(size int) func([]byte) (string, bool) {
|
||||||
|
return func(packet []byte) (string, bool) {
|
||||||
|
pkts, err := recordlayer.ContentAwareUnpackDatagram(packet, size)
|
||||||
|
if err != nil || len(pkts) < 1 {
|
||||||
|
return "", false
|
||||||
|
}
|
||||||
|
for _, pkt := range pkts {
|
||||||
|
h := &recordlayer.Header{
|
||||||
|
ConnectionID: make([]byte, size),
|
||||||
|
}
|
||||||
|
if err := h.Unmarshal(pkt); err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if h.ContentType != protocol.ContentTypeConnectionID {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
return string(h.ConnectionID), true
|
||||||
|
}
|
||||||
|
|
||||||
|
return "", false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// cidConnIdentifier extracts connection IDs from outgoing ServerHello records
|
||||||
|
// and associates them with the associated connection.
|
||||||
|
// NOTE: a ServerHello should always be the first record in a datagram if
|
||||||
|
// multiple are present, so we avoid iterating through all packets if the first
|
||||||
|
// is not a ServerHello.
|
||||||
|
func cidConnIdentifier() func([]byte) (string, bool) { //nolint:cyclop
|
||||||
|
return func(packet []byte) (string, bool) {
|
||||||
|
pkts, err := recordlayer.UnpackDatagram(packet)
|
||||||
|
if err != nil || len(pkts) < 1 {
|
||||||
|
return "", false
|
||||||
|
}
|
||||||
|
var h recordlayer.Header
|
||||||
|
if hErr := h.Unmarshal(pkts[0]); hErr != nil {
|
||||||
|
return "", false
|
||||||
|
}
|
||||||
|
if h.ContentType != protocol.ContentTypeHandshake {
|
||||||
|
return "", false
|
||||||
|
}
|
||||||
|
var hh handshake.Header
|
||||||
|
var sh handshake.MessageServerHello
|
||||||
|
for _, pkt := range pkts {
|
||||||
|
if hhErr := hh.Unmarshal(pkt[recordlayer.FixedHeaderSize:]); hhErr != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if err = sh.Unmarshal(pkt[recordlayer.FixedHeaderSize+handshake.HeaderLength:]); err == nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return "", false
|
||||||
|
}
|
||||||
|
for _, ext := range sh.Extensions {
|
||||||
|
if e, ok := ext.(*extension.ConnectionID); ok {
|
||||||
|
return string(e.CID), true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return "", false
|
||||||
|
}
|
||||||
|
}
|
||||||
457
vendor/github.com/pion/dtls/v3/crypto.go
generated
vendored
Normal file
457
vendor/github.com/pion/dtls/v3/crypto.go
generated
vendored
Normal file
|
|
@ -0,0 +1,457 @@
|
||||||
|
// SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package dtls
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto"
|
||||||
|
"crypto/ecdsa"
|
||||||
|
"crypto/ed25519"
|
||||||
|
"crypto/rand"
|
||||||
|
"crypto/rsa"
|
||||||
|
"crypto/x509"
|
||||||
|
"crypto/x509/pkix"
|
||||||
|
"encoding/asn1"
|
||||||
|
"encoding/binary"
|
||||||
|
"math/big"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/pion/dtls/v3/pkg/crypto/elliptic"
|
||||||
|
"github.com/pion/dtls/v3/pkg/crypto/hash"
|
||||||
|
"github.com/pion/dtls/v3/pkg/crypto/signature"
|
||||||
|
"github.com/pion/dtls/v3/pkg/crypto/signaturehash"
|
||||||
|
)
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
// validateSignatureAlgOID validates that the signature scheme matches the
|
||||||
|
// certificate's public key algorithm OID. This is required by RFC 8446 Section 4.2.3:
|
||||||
|
// - RSA_PSS_RSAE requires rsaEncryption OID
|
||||||
|
// - RSA_PSS_PSS requires id-RSASSA-PSS OID
|
||||||
|
//
|
||||||
|
// Note: returns nil if the given signature.Algorithm is not PSS based.
|
||||||
|
//
|
||||||
|
// https://www.rfc-editor.org/rfc/rfc8446#section-4.2.3
|
||||||
|
func validateSignatureAlgOID(cert *x509.Certificate, sigAlg signature.Algorithm) error {
|
||||||
|
if !sigAlg.IsPSS() {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the certificate's public key algorithm OID from the raw certificate
|
||||||
|
// We need to parse the SubjectPublicKeyInfo to get the algorithm OID
|
||||||
|
var spki struct {
|
||||||
|
Algorithm pkix.AlgorithmIdentifier
|
||||||
|
PublicKey asn1.BitString
|
||||||
|
}
|
||||||
|
if _, err := asn1.Unmarshal(cert.RawSubjectPublicKeyInfo, &spki); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
certOID := spki.Algorithm.Algorithm
|
||||||
|
|
||||||
|
switch sigAlg {
|
||||||
|
// Check RSAE variants (0x0804-0x0806) require rsaEncryption OID
|
||||||
|
case signature.RSA_PSS_RSAE_SHA256, signature.RSA_PSS_RSAE_SHA384, signature.RSA_PSS_RSAE_SHA512:
|
||||||
|
oidPublicKeyRSA := asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 1} // OID: rsaEncryption
|
||||||
|
if !certOID.Equal(oidPublicKeyRSA) {
|
||||||
|
return errInvalidCertificateOID
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
|
||||||
|
// Check PSS variants (0x0809-0x080b) require id-RSASSA-PSS OID
|
||||||
|
case signature.RSA_PSS_PSS_SHA256, signature.RSA_PSS_PSS_SHA384, signature.RSA_PSS_PSS_SHA512:
|
||||||
|
oidPublicKeyRSAPSS := asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 10} // OID: id-RSASSA-PSS
|
||||||
|
if !certOID.Equal(oidPublicKeyRSAPSS) {
|
||||||
|
return errInvalidCertificateOID
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
|
||||||
|
default:
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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,
|
||||||
|
signer crypto.Signer,
|
||||||
|
hashAlgorithm hash.Algorithm,
|
||||||
|
signatureAlgorithm signature.Algorithm,
|
||||||
|
) ([]byte, error) {
|
||||||
|
msg := valueKeyMessage(clientRandom, serverRandom, publicKey, namedCurve)
|
||||||
|
switch signer.Public().(type) {
|
||||||
|
case ed25519.PublicKey:
|
||||||
|
// https://crypto.stackexchange.com/a/55483
|
||||||
|
return signer.Sign(rand.Reader, msg, crypto.Hash(0))
|
||||||
|
case *ecdsa.PublicKey:
|
||||||
|
hashed := hashAlgorithm.Digest(msg)
|
||||||
|
|
||||||
|
return signer.Sign(rand.Reader, hashed, hashAlgorithm.CryptoHash())
|
||||||
|
case *rsa.PublicKey:
|
||||||
|
hashed := hashAlgorithm.Digest(msg)
|
||||||
|
|
||||||
|
// Use RSA-PSS if the signature algorithm is PSS
|
||||||
|
if signatureAlgorithm.IsPSS() {
|
||||||
|
pssOpts := &rsa.PSSOptions{
|
||||||
|
SaltLength: rsa.PSSSaltLengthEqualsHash,
|
||||||
|
Hash: hashAlgorithm.CryptoHash(),
|
||||||
|
}
|
||||||
|
|
||||||
|
return signer.Sign(rand.Reader, hashed, pssOpts)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Otherwise use PKCS#1 v1.5
|
||||||
|
return signer.Sign(rand.Reader, hashed, hashAlgorithm.CryptoHash())
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, errKeySignatureGenerateUnimplemented
|
||||||
|
}
|
||||||
|
|
||||||
|
//nolint:dupl,cyclop
|
||||||
|
func verifyKeySignature(
|
||||||
|
message, remoteKeySignature []byte,
|
||||||
|
hashAlgorithm hash.Algorithm,
|
||||||
|
signatureAlgorithm signature.Algorithm,
|
||||||
|
rawCertificates [][]byte,
|
||||||
|
) error {
|
||||||
|
if len(rawCertificates) == 0 {
|
||||||
|
return errLengthMismatch
|
||||||
|
}
|
||||||
|
certificate, err := x509.ParseCertificate(rawCertificates[0])
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate that the signature algorithm matches the certificate's OID
|
||||||
|
if err := validateSignatureAlgOID(certificate, signatureAlgorithm); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
switch pubKey := certificate.PublicKey.(type) {
|
||||||
|
case ed25519.PublicKey:
|
||||||
|
if ok := ed25519.Verify(pubKey, 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(pubKey, hashed, ecdsaSig.R, ecdsaSig.S) {
|
||||||
|
return errKeySignatureMismatch
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
case *rsa.PublicKey:
|
||||||
|
hashed := hashAlgorithm.Digest(message)
|
||||||
|
|
||||||
|
// Use RSA-PSS verification if the signature algorithm is PSS
|
||||||
|
if signatureAlgorithm.IsPSS() {
|
||||||
|
pssOpts := &rsa.PSSOptions{
|
||||||
|
SaltLength: rsa.PSSSaltLengthEqualsHash,
|
||||||
|
Hash: hashAlgorithm.CryptoHash(),
|
||||||
|
}
|
||||||
|
if err := rsa.VerifyPSS(pubKey, hashAlgorithm.CryptoHash(), hashed, remoteKeySignature, pssOpts); err != nil {
|
||||||
|
return errKeySignatureMismatch
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Otherwise use PKCS#1 v1.5
|
||||||
|
if rsa.VerifyPKCS1v15(pubKey, hashAlgorithm.CryptoHash(), hashed, remoteKeySignature) != nil {
|
||||||
|
return errKeySignatureMismatch
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
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,
|
||||||
|
signer crypto.Signer,
|
||||||
|
hashAlgorithm hash.Algorithm,
|
||||||
|
signatureAlgorithm signature.Algorithm,
|
||||||
|
) ([]byte, error) {
|
||||||
|
if _, ok := signer.Public().(ed25519.PublicKey); ok {
|
||||||
|
// https://pkg.go.dev/crypto/ed25519#PrivateKey.Sign
|
||||||
|
// Sign signs the given message with priv. Ed25519 performs two passes over
|
||||||
|
// messages to be signed and therefore cannot handle pre-hashed messages.
|
||||||
|
return signer.Sign(rand.Reader, handshakeBodies, crypto.Hash(0))
|
||||||
|
}
|
||||||
|
|
||||||
|
hashed := hashAlgorithm.Digest(handshakeBodies)
|
||||||
|
|
||||||
|
switch signer.Public().(type) {
|
||||||
|
case *ecdsa.PublicKey:
|
||||||
|
return signer.Sign(rand.Reader, hashed, hashAlgorithm.CryptoHash())
|
||||||
|
case *rsa.PublicKey:
|
||||||
|
// Use RSA-PSS if the signature algorithm is PSS
|
||||||
|
if signatureAlgorithm.IsPSS() {
|
||||||
|
pssOpts := &rsa.PSSOptions{
|
||||||
|
SaltLength: rsa.PSSSaltLengthEqualsHash,
|
||||||
|
Hash: hashAlgorithm.CryptoHash(),
|
||||||
|
}
|
||||||
|
|
||||||
|
return signer.Sign(rand.Reader, hashed, pssOpts)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Otherwise use PKCS#1 v1.5
|
||||||
|
return signer.Sign(rand.Reader, hashed, hashAlgorithm.CryptoHash())
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, errInvalidSignatureAlgorithm
|
||||||
|
}
|
||||||
|
|
||||||
|
//nolint:dupl,cyclop
|
||||||
|
func verifyCertificateVerify(
|
||||||
|
handshakeBodies []byte,
|
||||||
|
hashAlgorithm hash.Algorithm,
|
||||||
|
signatureAlgorithm signature.Algorithm,
|
||||||
|
remoteKeySignature []byte,
|
||||||
|
rawCertificates [][]byte,
|
||||||
|
) error {
|
||||||
|
if len(rawCertificates) == 0 {
|
||||||
|
return errLengthMismatch
|
||||||
|
}
|
||||||
|
certificate, err := x509.ParseCertificate(rawCertificates[0])
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate that the signature algorithm matches the certificate's OID
|
||||||
|
if err := validateSignatureAlgOID(certificate, signatureAlgorithm); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
switch pubKey := certificate.PublicKey.(type) {
|
||||||
|
case ed25519.PublicKey:
|
||||||
|
if ok := ed25519.Verify(pubKey, 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(pubKey, hash, ecdsaSig.R, ecdsaSig.S) {
|
||||||
|
return errKeySignatureMismatch
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
case *rsa.PublicKey:
|
||||||
|
hash := hashAlgorithm.Digest(handshakeBodies)
|
||||||
|
|
||||||
|
// Use RSA-PSS verification if the signature algorithm is PSS
|
||||||
|
if signatureAlgorithm.IsPSS() {
|
||||||
|
pssOpts := &rsa.PSSOptions{
|
||||||
|
SaltLength: rsa.PSSSaltLengthEqualsHash,
|
||||||
|
Hash: hashAlgorithm.CryptoHash(),
|
||||||
|
}
|
||||||
|
if err := rsa.VerifyPSS(pubKey, hashAlgorithm.CryptoHash(), hash, remoteKeySignature, pssOpts); err != nil {
|
||||||
|
return errKeySignatureMismatch
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Otherwise use PKCS#1 v1.5
|
||||||
|
if rsa.VerifyPKCS1v15(pubKey, hashAlgorithm.CryptoHash(), hash, remoteKeySignature) != nil {
|
||||||
|
return errKeySignatureMismatch
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
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,
|
||||||
|
certSignatureSchemes []signaturehash.Algorithm,
|
||||||
|
) (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},
|
||||||
|
}
|
||||||
|
|
||||||
|
chains, err = certificate[0].Verify(opts)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate certificate signature algorithms if specified.
|
||||||
|
// At least one chain must use only allowed signature algorithms.
|
||||||
|
if len(certSignatureSchemes) > 0 && len(chains) > 0 {
|
||||||
|
var validChainFound bool
|
||||||
|
for _, chain := range chains {
|
||||||
|
if err := validateCertificateSignatureAlgorithms(chain, certSignatureSchemes); err == nil {
|
||||||
|
validChainFound = true
|
||||||
|
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !validChainFound {
|
||||||
|
return nil, errInvalidCertificateSignatureAlgorithm
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return chains, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func verifyServerCert(
|
||||||
|
rawCertificates [][]byte,
|
||||||
|
roots *x509.CertPool,
|
||||||
|
serverName string,
|
||||||
|
certSignatureSchemes []signaturehash.Algorithm,
|
||||||
|
) (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,
|
||||||
|
}
|
||||||
|
|
||||||
|
chains, err = certificate[0].Verify(opts)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate certificate signature algorithms if specified.
|
||||||
|
// At least one chain must use only allowed signature algorithms.
|
||||||
|
if len(certSignatureSchemes) > 0 && len(chains) > 0 {
|
||||||
|
var validChainFound bool
|
||||||
|
for _, chain := range chains {
|
||||||
|
if err := validateCertificateSignatureAlgorithms(chain, certSignatureSchemes); err == nil {
|
||||||
|
validChainFound = true
|
||||||
|
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !validChainFound {
|
||||||
|
return nil, errInvalidCertificateSignatureAlgorithm
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return chains, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// validateCertificateSignatureAlgorithms validates that all certificates in the chain
|
||||||
|
// use signature algorithms that are in the allowed list. This implements the
|
||||||
|
// signature_algorithms_cert extension validation per RFC 8446 Section 4.2.3.
|
||||||
|
func validateCertificateSignatureAlgorithms(
|
||||||
|
certs []*x509.Certificate,
|
||||||
|
allowedAlgorithms []signaturehash.Algorithm,
|
||||||
|
) error {
|
||||||
|
if len(allowedAlgorithms) == 0 {
|
||||||
|
// No restrictions specified
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate each certificate's signature algorithm (except the root, which we trust)
|
||||||
|
for i := 0; i < len(certs)-1; i++ {
|
||||||
|
cert := certs[i]
|
||||||
|
certAlg, err := signaturehash.FromCertificate(cert)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if this algorithm is in the allowed list
|
||||||
|
found := false
|
||||||
|
for _, allowed := range allowedAlgorithms {
|
||||||
|
if certAlg.Hash == allowed.Hash && certAlg.Signature == allowed.Signature {
|
||||||
|
found = true
|
||||||
|
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !found {
|
||||||
|
return errInvalidCertificateSignatureAlgorithm
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
5
vendor/github.com/pion/dtls/v3/dtls.go
generated
vendored
Normal file
5
vendor/github.com/pion/dtls/v3/dtls.go
generated
vendored
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
// SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
// Package dtls implements Datagram Transport Layer Security (DTLS) 1.2
|
||||||
|
package dtls
|
||||||
308
vendor/github.com/pion/dtls/v3/errors.go
generated
vendored
Normal file
308
vendor/github.com/pion/dtls/v3/errors.go
generated
vendored
Normal file
|
|
@ -0,0 +1,308 @@
|
||||||
|
// SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package dtls
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"net"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/pion/dtls/v3/pkg/protocol"
|
||||||
|
"github.com/pion/dtls/v3/pkg/protocol/alert"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Typed errors.
|
||||||
|
var (
|
||||||
|
ErrConnClosed = &FatalError{Err: errors.New("conn is closed")} //nolint:err113
|
||||||
|
|
||||||
|
errDeadlineExceeded = &TimeoutError{Err: fmt.Errorf("read/write timeout: %w", context.DeadlineExceeded)}
|
||||||
|
errInvalidContentType = &TemporaryError{Err: errors.New("invalid content type")} //nolint:err113
|
||||||
|
|
||||||
|
//nolint:err113
|
||||||
|
errBufferTooSmall = &TemporaryError{Err: errors.New("buffer is too small")}
|
||||||
|
//nolint:err113
|
||||||
|
errContextUnsupported = &TemporaryError{Err: errors.New("context is not supported for ExportKeyingMaterial")}
|
||||||
|
//nolint:err113
|
||||||
|
errHandshakeInProgress = &TemporaryError{Err: errors.New("handshake is in progress")}
|
||||||
|
//nolint:err113
|
||||||
|
errReservedExportKeyingMaterial = &TemporaryError{
|
||||||
|
Err: errors.New("ExportKeyingMaterial can not be used with a reserved label"),
|
||||||
|
}
|
||||||
|
//nolint:err113
|
||||||
|
errApplicationDataEpochZero = &TemporaryError{Err: errors.New("ApplicationData with epoch of 0")}
|
||||||
|
//nolint:err113
|
||||||
|
errUnhandledContextType = &TemporaryError{Err: errors.New("unhandled contentType")}
|
||||||
|
|
||||||
|
//nolint:err113
|
||||||
|
errCertificateVerifyNoCertificate = &FatalError{
|
||||||
|
Err: errors.New("client sent certificate verify but we have no certificate to verify"),
|
||||||
|
}
|
||||||
|
//nolint:err113
|
||||||
|
errCipherSuiteNoIntersection = &FatalError{Err: errors.New("client+server do not support any shared cipher suites")}
|
||||||
|
//nolint:err113
|
||||||
|
errClientCertificateNotVerified = &FatalError{Err: errors.New("client sent certificate but did not verify it")}
|
||||||
|
//nolint:err113
|
||||||
|
errClientCertificateRequired = &FatalError{Err: errors.New("server required client verification, but got none")}
|
||||||
|
//nolint:err113
|
||||||
|
errClientNoMatchingSRTPProfile = &FatalError{Err: errors.New("server responded with SRTP Profile we do not support")}
|
||||||
|
//nolint:err113
|
||||||
|
errClientRequiredButNoServerEMS = &FatalError{
|
||||||
|
Err: errors.New("client required Extended Master Secret extension, but server does not support it"),
|
||||||
|
}
|
||||||
|
//nolint:err113
|
||||||
|
errCookieMismatch = &FatalError{Err: errors.New("client+server cookie does not match")}
|
||||||
|
//nolint:err113
|
||||||
|
errIdentityNoPSK = &FatalError{Err: errors.New("PSK Identity Hint provided but PSK is nil")}
|
||||||
|
//nolint:err113
|
||||||
|
errInvalidCertificate = &FatalError{Err: errors.New("no certificate provided")}
|
||||||
|
//nolint:err113
|
||||||
|
errInvalidCipherSuite = &FatalError{Err: errors.New("invalid or unknown cipher suite")}
|
||||||
|
//nolint:err113
|
||||||
|
errInvalidClientAuthType = &FatalError{Err: errors.New("invalid client auth type")}
|
||||||
|
//nolint:err113
|
||||||
|
errInvalidECDSASignature = &FatalError{Err: errors.New("ECDSA signature contained zero or negative values")}
|
||||||
|
//nolint:err113
|
||||||
|
errInvalidPrivateKey = &FatalError{Err: errors.New("invalid private key type")}
|
||||||
|
//nolint:err113
|
||||||
|
errInvalidSignatureAlgorithm = &FatalError{Err: errors.New("invalid signature algorithm")}
|
||||||
|
//nolint:err113
|
||||||
|
errInvalidExtendedMasterSecretType = &FatalError{Err: errors.New("invalid extended master secret type")}
|
||||||
|
//nolint:err113
|
||||||
|
errInvalidCertificateSignatureAlgorithm = &FatalError{
|
||||||
|
Err: errors.New("certificate uses a signature algorithm that is not allowed"),
|
||||||
|
}
|
||||||
|
//nolint:err113
|
||||||
|
errKeySignatureMismatch = &FatalError{Err: errors.New("expected and actual key signature do not match")}
|
||||||
|
//nolint:err113
|
||||||
|
errInvalidCertificateOID = &FatalError{Err: errors.New("certificate OID does not match signature algorithm")}
|
||||||
|
//nolint:err113
|
||||||
|
errNilNextConn = &FatalError{Err: errors.New("Conn can not be created with a nil nextConn")}
|
||||||
|
//nolint:err113
|
||||||
|
errNoAvailableCipherSuites = &FatalError{
|
||||||
|
Err: errors.New("connection can not be created, no CipherSuites satisfy this Config"),
|
||||||
|
}
|
||||||
|
//nolint:err113
|
||||||
|
errNoAvailablePSKCipherSuite = &FatalError{
|
||||||
|
Err: errors.New("connection can not be created, pre-shared key present but no compatible CipherSuite"),
|
||||||
|
}
|
||||||
|
//nolint:err113
|
||||||
|
errNoAvailableCertificateCipherSuite = &FatalError{
|
||||||
|
Err: errors.New("connection can not be created, certificate present but no compatible CipherSuite"),
|
||||||
|
}
|
||||||
|
//nolint:err113
|
||||||
|
errNoAvailableSignatureSchemes = &FatalError{
|
||||||
|
Err: errors.New("connection can not be created, no SignatureScheme satisfy this Config"),
|
||||||
|
}
|
||||||
|
//nolint:err113
|
||||||
|
errNoCertificates = &FatalError{Err: errors.New("no certificates configured")}
|
||||||
|
//nolint:err113
|
||||||
|
errNoConfigProvided = &FatalError{Err: errors.New("no config provided")}
|
||||||
|
//nolint:err113
|
||||||
|
errNoSupportedEllipticCurves = &FatalError{
|
||||||
|
Err: errors.New("client requested zero or more elliptic curves that are not supported by the server"),
|
||||||
|
}
|
||||||
|
//nolint:err113
|
||||||
|
errUnsupportedProtocolVersion = &FatalError{Err: errors.New("unsupported protocol version")}
|
||||||
|
//nolint:err113
|
||||||
|
errPSKAndIdentityMustBeSetForClient = &FatalError{
|
||||||
|
Err: errors.New("PSK and PSK Identity Hint must both be set for client"),
|
||||||
|
}
|
||||||
|
//nolint:err113
|
||||||
|
errRequestedButNoSRTPExtension = &FatalError{
|
||||||
|
Err: errors.New("SRTP support was requested but server did not respond with use_srtp extension"),
|
||||||
|
}
|
||||||
|
//nolint:err113
|
||||||
|
errServerNoMatchingSRTPProfile = &FatalError{Err: errors.New("client requested SRTP but we have no matching profiles")}
|
||||||
|
//nolint:err113
|
||||||
|
errServerRequiredButNoClientEMS = &FatalError{
|
||||||
|
Err: errors.New("server requires the Extended Master Secret extension, but the client does not support it"),
|
||||||
|
}
|
||||||
|
//nolint:err113
|
||||||
|
errVerifyDataMismatch = &FatalError{Err: errors.New("expected and actual verify data does not match")}
|
||||||
|
//nolint:err113
|
||||||
|
errNotAcceptableCertificateChain = &FatalError{Err: errors.New("certificate chain is not signed by an acceptable CA")}
|
||||||
|
|
||||||
|
//nolint:err113
|
||||||
|
errInvalidFlight = &InternalError{Err: errors.New("invalid flight number")}
|
||||||
|
//nolint:err113
|
||||||
|
errKeySignatureGenerateUnimplemented = &InternalError{
|
||||||
|
Err: errors.New("unable to generate key signature, unimplemented"),
|
||||||
|
}
|
||||||
|
//nolint:err113
|
||||||
|
errKeySignatureVerifyUnimplemented = &InternalError{Err: errors.New("unable to verify key signature, unimplemented")}
|
||||||
|
//nolint:err113
|
||||||
|
errLengthMismatch = &InternalError{Err: errors.New("data length and declared length do not match")}
|
||||||
|
//nolint:err113
|
||||||
|
errSequenceNumberOverflow = &InternalError{Err: errors.New("sequence number overflow")}
|
||||||
|
//nolint:err113
|
||||||
|
errInvalidFSMTransition = &InternalError{Err: errors.New("invalid state machine transition")}
|
||||||
|
//nolint:err113
|
||||||
|
errFailedToAccessPoolReadBuffer = &InternalError{Err: errors.New("failed to access pool read buffer")}
|
||||||
|
//nolint:err113
|
||||||
|
errFragmentBufferOverflow = &InternalError{Err: errors.New("fragment buffer overflow")}
|
||||||
|
|
||||||
|
//nolint:err113
|
||||||
|
errEmptyCertificates = &FatalError{Err: errors.New("certificates option requires at least one certificate")}
|
||||||
|
//nolint:err113
|
||||||
|
errEmptyCipherSuites = &FatalError{Err: errors.New("cipher suites option requires at least one cipher suite")}
|
||||||
|
//nolint:err113
|
||||||
|
errNilCustomCipherSuites = &FatalError{Err: errors.New("custom cipher suites option requires a non-nil function")}
|
||||||
|
//nolint:err113
|
||||||
|
errEmptySignatureSchemes = &FatalError{Err: errors.New("signature schemes option requires at least one scheme")}
|
||||||
|
//nolint:err113
|
||||||
|
errEmptyCertificateSignatureSchemes = &FatalError{
|
||||||
|
Err: errors.New("certificate signature schemes option requires at least one scheme"),
|
||||||
|
}
|
||||||
|
//nolint:err113
|
||||||
|
errEmptySRTPProtectionProfiles = &FatalError{
|
||||||
|
Err: errors.New("SRTP protection profiles option requires at least one profile"),
|
||||||
|
}
|
||||||
|
//nolint:err113
|
||||||
|
errInvalidFlightInterval = &FatalError{Err: errors.New("flight interval must be positive")}
|
||||||
|
//nolint:err113
|
||||||
|
errNilPSKCallback = &FatalError{Err: errors.New("PSK option requires a non-nil callback")}
|
||||||
|
//nolint:err113
|
||||||
|
errNilVerifyPeerCertificate = &FatalError{
|
||||||
|
Err: errors.New("verify peer certificate option requires a non-nil callback"),
|
||||||
|
}
|
||||||
|
//nolint:err113
|
||||||
|
errNilVerifyConnection = &FatalError{Err: errors.New("verify connection option requires a non-nil callback")}
|
||||||
|
//nolint:err113
|
||||||
|
errInvalidMTU = &FatalError{Err: errors.New("MTU must be positive")}
|
||||||
|
//nolint:err113
|
||||||
|
errInvalidReplayProtectionWindow = &FatalError{Err: errors.New("replay protection window must be non-negative")}
|
||||||
|
//nolint:err113
|
||||||
|
errEmptySupportedProtocols = &FatalError{
|
||||||
|
Err: errors.New("supported protocols option requires at least one protocol"),
|
||||||
|
}
|
||||||
|
//nolint:err113
|
||||||
|
errEmptyEllipticCurves = &FatalError{Err: errors.New("elliptic curves option requires at least one curve")}
|
||||||
|
//nolint:err113
|
||||||
|
errNilGetClientCertificate = &FatalError{
|
||||||
|
Err: errors.New("get client certificate option requires a non-nil callback"),
|
||||||
|
}
|
||||||
|
//nolint:err113
|
||||||
|
errNilConnectionIDGenerator = &FatalError{
|
||||||
|
Err: errors.New("connection ID generator option requires a non-nil function"),
|
||||||
|
}
|
||||||
|
//nolint:err113
|
||||||
|
errNilPaddingLengthGenerator = &FatalError{
|
||||||
|
Err: errors.New("padding length generator option requires a non-nil function"),
|
||||||
|
}
|
||||||
|
//nolint:err113
|
||||||
|
errNilHelloRandomBytesGenerator = &FatalError{
|
||||||
|
Err: errors.New("hello random bytes generator option requires a non-nil function"),
|
||||||
|
}
|
||||||
|
//nolint:err113
|
||||||
|
errNilClientHelloMessageHook = &FatalError{
|
||||||
|
Err: errors.New("client hello message hook option requires a non-nil function"),
|
||||||
|
}
|
||||||
|
//nolint:err113
|
||||||
|
errNilGetCertificate = &FatalError{Err: errors.New("get certificate option requires a non-nil callback")}
|
||||||
|
//nolint:err113
|
||||||
|
errNilServerHelloMessageHook = &FatalError{
|
||||||
|
Err: errors.New("server hello message hook option requires a non-nil function"),
|
||||||
|
}
|
||||||
|
//nolint:err113
|
||||||
|
errNilCertificateRequestMessageHook = &FatalError{
|
||||||
|
Err: errors.New("certificate request message hook option requires a non-nil function"),
|
||||||
|
}
|
||||||
|
//nolint:err113
|
||||||
|
errNilOnConnectionAttempt = &FatalError{
|
||||||
|
Err: errors.New("on connection attempt option requires a non-nil callback"),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
// 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
|
||||||
|
|
||||||
|
// errInvalidCipherSuite indicates an attempt at using an unsupported cipher suite.
|
||||||
|
type invalidCipherSuiteError struct {
|
||||||
|
id CipherSuiteID
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *invalidCipherSuiteError) Error() string {
|
||||||
|
return fmt.Sprintf("CipherSuite with id(%d) is not valid", e.id)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *invalidCipherSuiteError) Is(err error) bool {
|
||||||
|
var other *invalidCipherSuiteError
|
||||||
|
if errors.As(err, &other) {
|
||||||
|
return e.id == other.id
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// errAlert wraps DTLS alert notification as an error.
|
||||||
|
type alertError struct {
|
||||||
|
*alert.Alert
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *alertError) Error() string {
|
||||||
|
return fmt.Sprintf("alert: %s", e.Alert.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *alertError) IsFatalOrCloseNotify() bool {
|
||||||
|
return e.Level == alert.Fatal || e.Description == alert.CloseNotify
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *alertError) Is(err error) bool {
|
||||||
|
var other *alertError
|
||||||
|
if errors.As(err, &other) {
|
||||||
|
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 {
|
||||||
|
case errors.Is(err, io.EOF), errors.Is(err, context.Canceled), errors.Is(err, context.DeadlineExceeded):
|
||||||
|
// Return io.EOF and context errors as is.
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
ne net.Error
|
||||||
|
opError *net.OpError
|
||||||
|
se *os.SyscallError
|
||||||
|
)
|
||||||
|
|
||||||
|
if errors.As(err, &opError) { //nolint:nestif
|
||||||
|
if errors.As(opError, &se) {
|
||||||
|
if se.Timeout() {
|
||||||
|
return &TimeoutError{Err: err}
|
||||||
|
}
|
||||||
|
if isOpErrorTemporary(se) {
|
||||||
|
return &TemporaryError{Err: err}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if errors.As(err, &ne) {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &FatalError{Err: err}
|
||||||
|
}
|
||||||
22
vendor/github.com/pion/dtls/v3/errors_errno.go
generated
vendored
Normal file
22
vendor/github.com/pion/dtls/v3/errors_errno.go
generated
vendored
Normal file
|
|
@ -0,0 +1,22 @@
|
||||||
|
// SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
//go:build aix || darwin || dragonfly || freebsd || linux || nacl || nacljs || netbsd || openbsd || solaris || windows
|
||||||
|
// +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 (
|
||||||
|
"errors"
|
||||||
|
"os"
|
||||||
|
"syscall"
|
||||||
|
)
|
||||||
|
|
||||||
|
func isOpErrorTemporary(err *os.SyscallError) bool {
|
||||||
|
return errors.Is(err.Err, syscall.ECONNREFUSED)
|
||||||
|
}
|
||||||
18
vendor/github.com/pion/dtls/v3/errors_noerrno.go
generated
vendored
Normal file
18
vendor/github.com/pion/dtls/v3/errors_noerrno.go
generated
vendored
Normal file
|
|
@ -0,0 +1,18 @@
|
||||||
|
// SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
//go:build !aix && !darwin && !dragonfly && !freebsd && !linux && !nacl && !nacljs && !netbsd && !openbsd && !solaris && !windows
|
||||||
|
// +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
|
||||||
|
}
|
||||||
104
vendor/github.com/pion/dtls/v3/flight.go
generated
vendored
Normal file
104
vendor/github.com/pion/dtls/v3/flight.go
generated
vendored
Normal file
|
|
@ -0,0 +1,104 @@
|
||||||
|
// SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
Message flights for full handshake:
|
||||||
|
|
||||||
|
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 /
|
||||||
|
|
||||||
|
Message flights for session-resuming handshake (no cookie exchange):
|
||||||
|
|
||||||
|
Client Server
|
||||||
|
------ ------
|
||||||
|
Waiting Flight 0
|
||||||
|
|
||||||
|
ClientHello --------> Flight 1
|
||||||
|
|
||||||
|
ServerHello \
|
||||||
|
[ChangeCipherSpec] Flight 4b
|
||||||
|
<-------- Finished /
|
||||||
|
|
||||||
|
[ChangeCipherSpec] \ Flight 5b
|
||||||
|
Finished --------> /
|
||||||
|
|
||||||
|
[ChangeCipherSpec] \ Flight 6
|
||||||
|
<-------- Finished /
|
||||||
|
*/
|
||||||
|
|
||||||
|
type flightVal uint8
|
||||||
|
|
||||||
|
const (
|
||||||
|
flight0 flightVal = iota + 1
|
||||||
|
flight1
|
||||||
|
flight2
|
||||||
|
flight3
|
||||||
|
flight4
|
||||||
|
flight4b
|
||||||
|
flight5
|
||||||
|
flight5b
|
||||||
|
flight6
|
||||||
|
)
|
||||||
|
|
||||||
|
func (f flightVal) String() string { //nolint:cyclop
|
||||||
|
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 flight4b:
|
||||||
|
return "Flight 4b"
|
||||||
|
case flight5:
|
||||||
|
return "Flight 5"
|
||||||
|
case flight5b:
|
||||||
|
return "Flight 5b"
|
||||||
|
case flight6:
|
||||||
|
return "Flight 6"
|
||||||
|
default:
|
||||||
|
return "Invalid Flight"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f flightVal) isLastSendFlight() bool {
|
||||||
|
return f == flight6 || f == flight5b
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f flightVal) isLastRecvFlight() bool {
|
||||||
|
return f == flight5 || f == flight4b
|
||||||
|
}
|
||||||
189
vendor/github.com/pion/dtls/v3/flight0handler.go
generated
vendored
Normal file
189
vendor/github.com/pion/dtls/v3/flight0handler.go
generated
vendored
Normal file
|
|
@ -0,0 +1,189 @@
|
||||||
|
// SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package dtls
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"crypto/rand"
|
||||||
|
|
||||||
|
"github.com/pion/dtls/v3/pkg/crypto/elliptic"
|
||||||
|
"github.com/pion/dtls/v3/pkg/protocol"
|
||||||
|
"github.com/pion/dtls/v3/pkg/protocol/alert"
|
||||||
|
"github.com/pion/dtls/v3/pkg/protocol/extension"
|
||||||
|
"github.com/pion/dtls/v3/pkg/protocol/handshake"
|
||||||
|
)
|
||||||
|
|
||||||
|
// renegotiationInfoSCSV is TLS_EMPTY_RENEGOTIATION_INFO_SCSV defined in RFC 5746.
|
||||||
|
// https://datatracker.ietf.org/doc/html/rfc5746#section-3.3.
|
||||||
|
const renegotiationInfoSCSV uint16 = 0x00ff
|
||||||
|
|
||||||
|
//nolint:cyclop,gocognit
|
||||||
|
func flight0Parse(
|
||||||
|
_ context.Context,
|
||||||
|
_ flightConn,
|
||||||
|
state *State,
|
||||||
|
cache *handshakeCache,
|
||||||
|
cfg *handshakeConfig,
|
||||||
|
) (flightVal, *alert.Alert, error) {
|
||||||
|
seq, msgs, ok := cache.fullPullMap(0, state.cipherSuite,
|
||||||
|
handshakeCachePullRule{handshake.TypeClientHello, cfg.initialEpoch, true, false},
|
||||||
|
)
|
||||||
|
if !ok {
|
||||||
|
// No valid message received. Keep reading
|
||||||
|
return 0, nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Connection Identifiers must be negotiated afresh on session resumption.
|
||||||
|
// https://datatracker.ietf.org/doc/html/rfc9146#name-the-connection_id-extension
|
||||||
|
state.setLocalConnectionID(nil)
|
||||||
|
state.remoteConnectionID = 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 id == renegotiationInfoSCSV {
|
||||||
|
state.remoteSupportsRenegotiation = true
|
||||||
|
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
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 ext := val.(type) {
|
||||||
|
case *extension.SupportedEllipticCurves:
|
||||||
|
if len(ext.EllipticCurves) == 0 {
|
||||||
|
return 0, &alert.Alert{Level: alert.Fatal, Description: alert.InsufficientSecurity}, errNoSupportedEllipticCurves
|
||||||
|
}
|
||||||
|
state.namedCurve = ext.EllipticCurves[0]
|
||||||
|
case *extension.UseSRTP:
|
||||||
|
profile, ok := findMatchingSRTPProfile(cfg.localSRTPProtectionProfiles, ext.ProtectionProfiles)
|
||||||
|
if !ok {
|
||||||
|
return 0, &alert.Alert{Level: alert.Fatal, Description: alert.InsufficientSecurity}, errServerNoMatchingSRTPProfile
|
||||||
|
}
|
||||||
|
state.setSRTPProtectionProfile(profile)
|
||||||
|
state.remoteSRTPMasterKeyIdentifier = ext.MasterKeyIdentifier
|
||||||
|
case *extension.UseExtendedMasterSecret:
|
||||||
|
if cfg.extendedMasterSecret != DisableExtendedMasterSecret {
|
||||||
|
state.extendedMasterSecret = true
|
||||||
|
}
|
||||||
|
case *extension.ServerName:
|
||||||
|
state.serverName = ext.ServerName // remote server name
|
||||||
|
case *extension.RenegotiationInfo:
|
||||||
|
state.remoteSupportsRenegotiation = true
|
||||||
|
case *extension.ALPN:
|
||||||
|
state.peerSupportedProtocols = ext.ProtocolNameList
|
||||||
|
case *extension.ConnectionID:
|
||||||
|
// Only set connection ID to be sent if server supports connection
|
||||||
|
// IDs.
|
||||||
|
if cfg.connectionIDGenerator != nil {
|
||||||
|
state.remoteConnectionID = ext.CID
|
||||||
|
}
|
||||||
|
case *extension.SignatureAlgorithmsCert:
|
||||||
|
// Store the client's certificate signature schemes for later validation
|
||||||
|
state.remoteCertSignatureSchemes = ext.SignatureHashAlgorithms
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the client doesn't support connection IDs, the server should not
|
||||||
|
// expect one to be sent.
|
||||||
|
if state.remoteConnectionID == nil {
|
||||||
|
state.setLocalConnectionID(nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
nextFlight := flight2
|
||||||
|
|
||||||
|
if cfg.insecureSkipHelloVerify {
|
||||||
|
nextFlight = flight4
|
||||||
|
}
|
||||||
|
|
||||||
|
return handleHelloResume(clientHello.SessionID, state, cfg, nextFlight)
|
||||||
|
}
|
||||||
|
|
||||||
|
func handleHelloResume(
|
||||||
|
sessionID []byte,
|
||||||
|
state *State,
|
||||||
|
cfg *handshakeConfig,
|
||||||
|
next flightVal,
|
||||||
|
) (flightVal, *alert.Alert, error) {
|
||||||
|
if len(sessionID) > 0 && cfg.sessionStore != nil {
|
||||||
|
if s, err := cfg.sessionStore.Get(sessionID); err != nil {
|
||||||
|
return 0, &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}, err
|
||||||
|
} else if s.ID != nil {
|
||||||
|
cfg.log.Tracef("[handshake] resume session: %x", sessionID)
|
||||||
|
|
||||||
|
state.SessionID = sessionID
|
||||||
|
state.masterSecret = s.Secret
|
||||||
|
|
||||||
|
if err := state.initCipherSuite(); err != nil {
|
||||||
|
return 0, &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
clientRandom := state.localRandom.MarshalFixed()
|
||||||
|
cfg.writeKeyLog(keyLogLabelTLS12, clientRandom[:], state.masterSecret)
|
||||||
|
|
||||||
|
return flight4b, nil, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return next, nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func flight0Generate(
|
||||||
|
_ flightConn,
|
||||||
|
state *State,
|
||||||
|
_ *handshakeCache,
|
||||||
|
cfg *handshakeConfig,
|
||||||
|
) ([]*packet, *alert.Alert, error) {
|
||||||
|
// Initialize
|
||||||
|
if !cfg.insecureSkipHelloVerify {
|
||||||
|
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
|
||||||
|
}
|
||||||
189
vendor/github.com/pion/dtls/v3/flight1handler.go
generated
vendored
Normal file
189
vendor/github.com/pion/dtls/v3/flight1handler.go
generated
vendored
Normal file
|
|
@ -0,0 +1,189 @@
|
||||||
|
// SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package dtls
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"github.com/pion/dtls/v3/pkg/crypto/elliptic"
|
||||||
|
"github.com/pion/dtls/v3/pkg/protocol"
|
||||||
|
"github.com/pion/dtls/v3/pkg/protocol/alert"
|
||||||
|
"github.com/pion/dtls/v3/pkg/protocol/extension"
|
||||||
|
"github.com/pion/dtls/v3/pkg/protocol/handshake"
|
||||||
|
"github.com/pion/dtls/v3/pkg/protocol/recordlayer"
|
||||||
|
)
|
||||||
|
|
||||||
|
func flight1Parse(
|
||||||
|
ctx context.Context,
|
||||||
|
conn 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, state.cipherSuite,
|
||||||
|
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, conn, 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
|
||||||
|
}
|
||||||
|
|
||||||
|
//nolint:cyclop
|
||||||
|
func flight1Generate(
|
||||||
|
conn flightConn,
|
||||||
|
state *State,
|
||||||
|
_ *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
|
||||||
|
}
|
||||||
|
|
||||||
|
if cfg.helloRandomBytesGenerator != nil {
|
||||||
|
state.localRandom.RandomBytes = cfg.helloRandomBytesGenerator()
|
||||||
|
}
|
||||||
|
|
||||||
|
extensions := []extension.Extension{
|
||||||
|
&extension.SupportedSignatureAlgorithms{
|
||||||
|
SignatureHashAlgorithms: cfg.localSignatureSchemes,
|
||||||
|
},
|
||||||
|
&extension.RenegotiationInfo{
|
||||||
|
RenegotiatedConnection: 0,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(cfg.localCertSignatureSchemes) > 0 {
|
||||||
|
extensions = append(extensions, &extension.SignatureAlgorithmsCert{
|
||||||
|
SignatureHashAlgorithms: cfg.localCertSignatureSchemes,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
var setEllipticCurveCryptographyClientHelloExtensions bool
|
||||||
|
for _, c := range cfg.localCipherSuites {
|
||||||
|
if c.ECC() {
|
||||||
|
setEllipticCurveCryptographyClientHelloExtensions = true
|
||||||
|
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if setEllipticCurveCryptographyClientHelloExtensions {
|
||||||
|
extensions = append(extensions, []extension.Extension{
|
||||||
|
&extension.SupportedEllipticCurves{
|
||||||
|
EllipticCurves: cfg.ellipticCurves,
|
||||||
|
},
|
||||||
|
&extension.SupportedPointFormats{
|
||||||
|
PointFormats: []elliptic.CurvePointFormat{elliptic.CurvePointFormatUncompressed},
|
||||||
|
},
|
||||||
|
}...)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(cfg.localSRTPProtectionProfiles) > 0 {
|
||||||
|
extensions = append(extensions, &extension.UseSRTP{
|
||||||
|
ProtectionProfiles: cfg.localSRTPProtectionProfiles,
|
||||||
|
MasterKeyIdentifier: cfg.localSRTPMasterKeyIdentifier,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
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})
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(cfg.supportedProtocols) > 0 {
|
||||||
|
extensions = append(extensions, &extension.ALPN{ProtocolNameList: cfg.supportedProtocols})
|
||||||
|
}
|
||||||
|
|
||||||
|
if cfg.sessionStore != nil {
|
||||||
|
cfg.log.Tracef("[handshake] try to resume session")
|
||||||
|
if s, err := cfg.sessionStore.Get(conn.sessionKey()); err != nil {
|
||||||
|
return nil, &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}, err
|
||||||
|
} else if s.ID != nil {
|
||||||
|
cfg.log.Tracef("[handshake] get saved session: %x", s.ID)
|
||||||
|
|
||||||
|
state.SessionID = s.ID
|
||||||
|
state.masterSecret = s.Secret
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we have a connection ID generator, use it. The CID may be zero length,
|
||||||
|
// in which case we are just requesting that the server send us a CID to
|
||||||
|
// use.
|
||||||
|
if cfg.connectionIDGenerator != nil {
|
||||||
|
state.setLocalConnectionID(cfg.connectionIDGenerator())
|
||||||
|
// The presence of a generator indicates support for connection IDs. We
|
||||||
|
// use the presence of a non-nil local CID in flight 3 to determine
|
||||||
|
// whether we send a CID in the second ClientHello, so we convert any
|
||||||
|
// nil CID returned by a generator to []byte{}.
|
||||||
|
if state.getLocalConnectionID() == nil {
|
||||||
|
state.setLocalConnectionID([]byte{})
|
||||||
|
}
|
||||||
|
extensions = append(extensions, &extension.ConnectionID{CID: state.getLocalConnectionID()})
|
||||||
|
}
|
||||||
|
|
||||||
|
clientHello := &handshake.MessageClientHello{
|
||||||
|
Version: protocol.Version1_2,
|
||||||
|
SessionID: state.SessionID,
|
||||||
|
Cookie: state.cookie,
|
||||||
|
Random: state.localRandom,
|
||||||
|
CipherSuiteIDs: cipherSuiteIDs(cfg.localCipherSuites),
|
||||||
|
CompressionMethods: defaultCompressionMethods(),
|
||||||
|
Extensions: extensions,
|
||||||
|
}
|
||||||
|
|
||||||
|
var content handshake.Handshake
|
||||||
|
|
||||||
|
if cfg.clientHelloMessageHook != nil {
|
||||||
|
content = handshake.Handshake{Message: cfg.clientHelloMessageHook(*clientHello)}
|
||||||
|
} else {
|
||||||
|
content = handshake.Handshake{Message: clientHello}
|
||||||
|
}
|
||||||
|
|
||||||
|
return []*packet{
|
||||||
|
{
|
||||||
|
record: &recordlayer.RecordLayer{
|
||||||
|
Header: recordlayer.Header{
|
||||||
|
Version: protocol.Version1_2,
|
||||||
|
},
|
||||||
|
Content: &content,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}, nil, nil
|
||||||
|
}
|
||||||
77
vendor/github.com/pion/dtls/v3/flight2handler.go
generated
vendored
Normal file
77
vendor/github.com/pion/dtls/v3/flight2handler.go
generated
vendored
Normal file
|
|
@ -0,0 +1,77 @@
|
||||||
|
// SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package dtls
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"github.com/pion/dtls/v3/pkg/protocol"
|
||||||
|
"github.com/pion/dtls/v3/pkg/protocol/alert"
|
||||||
|
"github.com/pion/dtls/v3/pkg/protocol/handshake"
|
||||||
|
"github.com/pion/dtls/v3/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, state.cipherSuite,
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
return flight4, nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func flight2Generate(
|
||||||
|
_ flightConn,
|
||||||
|
state *State,
|
||||||
|
_ *handshakeCache,
|
||||||
|
_ *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
|
||||||
|
}
|
||||||
363
vendor/github.com/pion/dtls/v3/flight3handler.go
generated
vendored
Normal file
363
vendor/github.com/pion/dtls/v3/flight3handler.go
generated
vendored
Normal file
|
|
@ -0,0 +1,363 @@
|
||||||
|
// SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package dtls
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"github.com/pion/dtls/v3/internal/ciphersuite/types"
|
||||||
|
"github.com/pion/dtls/v3/pkg/crypto/elliptic"
|
||||||
|
"github.com/pion/dtls/v3/pkg/crypto/prf"
|
||||||
|
"github.com/pion/dtls/v3/pkg/protocol"
|
||||||
|
"github.com/pion/dtls/v3/pkg/protocol/alert"
|
||||||
|
"github.com/pion/dtls/v3/pkg/protocol/extension"
|
||||||
|
"github.com/pion/dtls/v3/pkg/protocol/handshake"
|
||||||
|
"github.com/pion/dtls/v3/pkg/protocol/recordlayer"
|
||||||
|
)
|
||||||
|
|
||||||
|
//nolint:gocognit,gocyclo,maintidx,cyclop
|
||||||
|
func flight3Parse(
|
||||||
|
ctx context.Context,
|
||||||
|
conn flightConn,
|
||||||
|
state *State,
|
||||||
|
cache *handshakeCache,
|
||||||
|
cfg *handshakeConfig,
|
||||||
|
) (flightVal, *alert.Alert, error) {
|
||||||
|
// 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, state.cipherSuite,
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_, msgs, ok = cache.fullPullMap(state.handshakeRecvSequence, state.cipherSuite,
|
||||||
|
handshakeCachePullRule{handshake.TypeServerHello, cfg.initialEpoch, false, false},
|
||||||
|
)
|
||||||
|
if !ok {
|
||||||
|
// Don't have enough messages. Keep reading
|
||||||
|
return 0, nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if serverHelloMsg, msgOk := msgs[handshake.TypeServerHello].(*handshake.MessageServerHello); msgOk { //nolint:nestif
|
||||||
|
if !serverHelloMsg.Version.Equal(protocol.Version1_2) {
|
||||||
|
return 0, &alert.Alert{Level: alert.Fatal, Description: alert.ProtocolVersion}, errUnsupportedProtocolVersion
|
||||||
|
}
|
||||||
|
for _, v := range serverHelloMsg.Extensions {
|
||||||
|
switch ext := v.(type) {
|
||||||
|
case *extension.UseSRTP:
|
||||||
|
profile, found := findMatchingSRTPProfile(ext.ProtectionProfiles, cfg.localSRTPProtectionProfiles)
|
||||||
|
if !found {
|
||||||
|
return 0, &alert.Alert{Level: alert.Fatal, Description: alert.IllegalParameter}, errClientNoMatchingSRTPProfile
|
||||||
|
}
|
||||||
|
state.setSRTPProtectionProfile(profile)
|
||||||
|
state.remoteSRTPMasterKeyIdentifier = ext.MasterKeyIdentifier
|
||||||
|
case *extension.UseExtendedMasterSecret:
|
||||||
|
if cfg.extendedMasterSecret != DisableExtendedMasterSecret {
|
||||||
|
state.extendedMasterSecret = true
|
||||||
|
}
|
||||||
|
case *extension.ALPN:
|
||||||
|
if len(ext.ProtocolNameList) > 1 { // This should be exactly 1, the zero case is handle when unmarshalling
|
||||||
|
return 0, &alert.Alert{
|
||||||
|
Level: alert.Fatal,
|
||||||
|
Description: alert.InternalError,
|
||||||
|
}, extension.ErrALPNInvalidFormat // Meh, internal error?
|
||||||
|
}
|
||||||
|
state.NegotiatedProtocol = ext.ProtocolNameList[0]
|
||||||
|
case *extension.ConnectionID:
|
||||||
|
// Only set connection ID to be sent if client supports connection
|
||||||
|
// IDs.
|
||||||
|
if cfg.connectionIDGenerator != nil {
|
||||||
|
state.remoteConnectionID = ext.CID
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// If the server doesn't support connection IDs, the client should not
|
||||||
|
// expect one to be sent.
|
||||||
|
if state.remoteConnectionID == nil {
|
||||||
|
state.setLocalConnectionID(nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
if cfg.extendedMasterSecret == RequireExtendedMasterSecret && !state.extendedMasterSecret {
|
||||||
|
return 0, &alert.Alert{Level: alert.Fatal, Description: alert.InsufficientSecurity}, errClientRequiredButNoServerEMS
|
||||||
|
}
|
||||||
|
if len(cfg.localSRTPProtectionProfiles) > 0 && state.getSRTPProtectionProfile() == 0 {
|
||||||
|
return 0, &alert.Alert{Level: alert.Fatal, Description: alert.InsufficientSecurity}, errRequestedButNoSRTPExtension
|
||||||
|
}
|
||||||
|
|
||||||
|
remoteCipherSuite := cipherSuiteForID(CipherSuiteID(*serverHelloMsg.CipherSuiteID), cfg.customCipherSuites)
|
||||||
|
if remoteCipherSuite == nil {
|
||||||
|
return 0, &alert.Alert{Level: alert.Fatal, Description: alert.InsufficientSecurity}, errCipherSuiteNoIntersection
|
||||||
|
}
|
||||||
|
|
||||||
|
selectedCipherSuite, found := findMatchingCipherSuite([]CipherSuite{remoteCipherSuite}, cfg.localCipherSuites)
|
||||||
|
if !found {
|
||||||
|
return 0, &alert.Alert{Level: alert.Fatal, Description: alert.InsufficientSecurity}, errInvalidCipherSuite
|
||||||
|
}
|
||||||
|
|
||||||
|
state.cipherSuite = selectedCipherSuite
|
||||||
|
state.remoteRandom = serverHelloMsg.Random
|
||||||
|
cfg.log.Tracef("[handshake] use cipher suite: %s", selectedCipherSuite.String())
|
||||||
|
|
||||||
|
if len(serverHelloMsg.SessionID) > 0 && bytes.Equal(state.SessionID, serverHelloMsg.SessionID) {
|
||||||
|
return handleResumption(ctx, conn, state, cache, cfg)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(state.SessionID) > 0 {
|
||||||
|
cfg.log.Tracef("[handshake] clean old session : %s", state.SessionID)
|
||||||
|
if err := cfg.sessionStore.Del(state.SessionID); err != nil {
|
||||||
|
return 0, &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if cfg.sessionStore == nil {
|
||||||
|
state.SessionID = []byte{}
|
||||||
|
} else {
|
||||||
|
state.SessionID = serverHelloMsg.SessionID
|
||||||
|
}
|
||||||
|
|
||||||
|
state.masterSecret = []byte{}
|
||||||
|
}
|
||||||
|
|
||||||
|
if cfg.localPSKCallback != nil {
|
||||||
|
seq, msgs, ok = cache.fullPullMap(state.handshakeRecvSequence+1, state.cipherSuite,
|
||||||
|
handshakeCachePullRule{handshake.TypeServerKeyExchange, cfg.initialEpoch, false, true},
|
||||||
|
handshakeCachePullRule{handshake.TypeServerHelloDone, cfg.initialEpoch, false, false},
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
seq, msgs, ok = cache.fullPullMap(state.handshakeRecvSequence+1, state.cipherSuite,
|
||||||
|
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.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(conn, state, cfg, h)
|
||||||
|
if err != nil {
|
||||||
|
return 0, alertPtr, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if creq, ok := msgs[handshake.TypeCertificateRequest].(*handshake.MessageCertificateRequest); ok {
|
||||||
|
state.remoteCertRequestAlgs = creq.SignatureHashAlgorithms
|
||||||
|
state.remoteRequestedCertificate = true
|
||||||
|
}
|
||||||
|
|
||||||
|
return flight5, nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func handleResumption(
|
||||||
|
ctx context.Context,
|
||||||
|
c flightConn,
|
||||||
|
state *State,
|
||||||
|
cache *handshakeCache,
|
||||||
|
cfg *handshakeConfig,
|
||||||
|
) (flightVal, *alert.Alert, error) {
|
||||||
|
if err := state.initCipherSuite(); err != nil {
|
||||||
|
return 0, &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now, encrypted packets can be handled
|
||||||
|
if err := c.handleQueuedPackets(ctx); err != nil {
|
||||||
|
return 0, &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
_, msgs, ok := cache.fullPullMap(state.handshakeRecvSequence+1, state.cipherSuite,
|
||||||
|
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},
|
||||||
|
)
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
clientRandom := state.localRandom.MarshalFixed()
|
||||||
|
cfg.writeKeyLog(keyLogLabelTLS12, clientRandom[:], state.masterSecret)
|
||||||
|
|
||||||
|
return flight5b, nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
//nolint:cyclop
|
||||||
|
func handleServerKeyExchange(
|
||||||
|
_ flightConn,
|
||||||
|
state *State,
|
||||||
|
cfg *handshakeConfig,
|
||||||
|
keyExchangeMessage *handshake.MessageServerKeyExchange,
|
||||||
|
) (*alert.Alert, error) {
|
||||||
|
var err error
|
||||||
|
if state.cipherSuite == nil {
|
||||||
|
return &alert.Alert{Level: alert.Fatal, Description: alert.InsufficientSecurity}, errInvalidCipherSuite
|
||||||
|
}
|
||||||
|
if cfg.localPSKCallback != nil { //nolint:nestif
|
||||||
|
var psk []byte
|
||||||
|
if psk, err = cfg.localPSKCallback(keyExchangeMessage.IdentityHint); err != nil {
|
||||||
|
return &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}, err
|
||||||
|
}
|
||||||
|
state.IdentityHint = keyExchangeMessage.IdentityHint
|
||||||
|
switch state.cipherSuite.KeyExchangeAlgorithm() {
|
||||||
|
case types.KeyExchangeAlgorithmPsk:
|
||||||
|
state.preMasterSecret = prf.PSKPreMasterSecret(psk)
|
||||||
|
case (types.KeyExchangeAlgorithmEcdhe | types.KeyExchangeAlgorithmPsk):
|
||||||
|
if state.localKeypair, err = elliptic.GenerateKeypair(keyExchangeMessage.NamedCurve); err != nil {
|
||||||
|
return &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}, err
|
||||||
|
}
|
||||||
|
state.preMasterSecret, err = prf.EcdhePSKPreMasterSecret(
|
||||||
|
psk,
|
||||||
|
keyExchangeMessage.PublicKey,
|
||||||
|
state.localKeypair.PrivateKey,
|
||||||
|
state.localKeypair.Curve,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}, err
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return &alert.Alert{Level: alert.Fatal, Description: alert.InsufficientSecurity}, errInvalidCipherSuite
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if state.localKeypair, err = elliptic.GenerateKeypair(keyExchangeMessage.NamedCurve); err != nil {
|
||||||
|
return &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if state.preMasterSecret, err = prf.PreMasterSecret(
|
||||||
|
keyExchangeMessage.PublicKey,
|
||||||
|
state.localKeypair.PrivateKey,
|
||||||
|
state.localKeypair.Curve,
|
||||||
|
); err != nil {
|
||||||
|
return &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, nil //nolint:nilnil
|
||||||
|
}
|
||||||
|
|
||||||
|
func flight3Generate(
|
||||||
|
_ flightConn,
|
||||||
|
state *State,
|
||||||
|
_ *handshakeCache,
|
||||||
|
cfg *handshakeConfig,
|
||||||
|
) ([]*packet, *alert.Alert, error) {
|
||||||
|
extensions := []extension.Extension{
|
||||||
|
&extension.SupportedSignatureAlgorithms{
|
||||||
|
SignatureHashAlgorithms: cfg.localSignatureSchemes,
|
||||||
|
},
|
||||||
|
&extension.RenegotiationInfo{
|
||||||
|
RenegotiatedConnection: 0,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(cfg.localCertSignatureSchemes) > 0 {
|
||||||
|
extensions = append(extensions, &extension.SignatureAlgorithmsCert{
|
||||||
|
SignatureHashAlgorithms: cfg.localCertSignatureSchemes,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
if state.namedCurve != 0 {
|
||||||
|
extensions = append(extensions, []extension.Extension{
|
||||||
|
&extension.SupportedEllipticCurves{
|
||||||
|
EllipticCurves: cfg.ellipticCurves,
|
||||||
|
},
|
||||||
|
&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})
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(cfg.supportedProtocols) > 0 {
|
||||||
|
extensions = append(extensions, &extension.ALPN{ProtocolNameList: cfg.supportedProtocols})
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we sent a connection ID on the first ClientHello, send it on the
|
||||||
|
// second.
|
||||||
|
if state.getLocalConnectionID() != nil {
|
||||||
|
extensions = append(extensions, &extension.ConnectionID{CID: state.getLocalConnectionID()})
|
||||||
|
}
|
||||||
|
|
||||||
|
clientHello := &handshake.MessageClientHello{
|
||||||
|
Version: protocol.Version1_2,
|
||||||
|
SessionID: state.SessionID,
|
||||||
|
Cookie: state.cookie,
|
||||||
|
Random: state.localRandom,
|
||||||
|
CipherSuiteIDs: cipherSuiteIDs(cfg.localCipherSuites),
|
||||||
|
CompressionMethods: defaultCompressionMethods(),
|
||||||
|
Extensions: extensions,
|
||||||
|
}
|
||||||
|
|
||||||
|
var content handshake.Handshake
|
||||||
|
|
||||||
|
if cfg.clientHelloMessageHook != nil {
|
||||||
|
content = handshake.Handshake{Message: cfg.clientHelloMessageHook(*clientHello)}
|
||||||
|
} else {
|
||||||
|
content = handshake.Handshake{Message: clientHello}
|
||||||
|
}
|
||||||
|
|
||||||
|
return []*packet{
|
||||||
|
{
|
||||||
|
record: &recordlayer.RecordLayer{
|
||||||
|
Header: recordlayer.Header{
|
||||||
|
Version: protocol.Version1_2,
|
||||||
|
},
|
||||||
|
Content: &content,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}, nil, nil
|
||||||
|
}
|
||||||
163
vendor/github.com/pion/dtls/v3/flight4bhandler.go
generated
vendored
Normal file
163
vendor/github.com/pion/dtls/v3/flight4bhandler.go
generated
vendored
Normal file
|
|
@ -0,0 +1,163 @@
|
||||||
|
// SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package dtls
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"github.com/pion/dtls/v3/pkg/crypto/prf"
|
||||||
|
"github.com/pion/dtls/v3/pkg/protocol"
|
||||||
|
"github.com/pion/dtls/v3/pkg/protocol/alert"
|
||||||
|
"github.com/pion/dtls/v3/pkg/protocol/extension"
|
||||||
|
"github.com/pion/dtls/v3/pkg/protocol/handshake"
|
||||||
|
"github.com/pion/dtls/v3/pkg/protocol/recordlayer"
|
||||||
|
)
|
||||||
|
|
||||||
|
func flight4bParse(
|
||||||
|
_ context.Context,
|
||||||
|
_ flightConn,
|
||||||
|
state *State,
|
||||||
|
cache *handshakeCache,
|
||||||
|
cfg *handshakeConfig,
|
||||||
|
) (flightVal, *alert.Alert, error) {
|
||||||
|
_, msgs, ok := cache.fullPullMap(state.handshakeRecvSequence, state.cipherSuite,
|
||||||
|
handshakeCachePullRule{handshake.TypeFinished, cfg.initialEpoch + 1, true, 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.TypeFinished, cfg.initialEpoch + 1, false, false},
|
||||||
|
)
|
||||||
|
|
||||||
|
expectedVerifyData, err := prf.VerifyDataClient(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
|
||||||
|
}
|
||||||
|
|
||||||
|
// Other party may re-transmit the last flight. Keep state to be flight4b.
|
||||||
|
return flight4b, nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
//nolint:cyclop
|
||||||
|
func flight4bGenerate(
|
||||||
|
_ flightConn,
|
||||||
|
state *State,
|
||||||
|
cache *handshakeCache,
|
||||||
|
cfg *handshakeConfig,
|
||||||
|
) ([]*packet, *alert.Alert, error) {
|
||||||
|
var pkts []*packet
|
||||||
|
|
||||||
|
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.getSRTPProtectionProfile() != 0 {
|
||||||
|
extensions = append(extensions, &extension.UseSRTP{
|
||||||
|
ProtectionProfiles: []SRTPProtectionProfile{state.getSRTPProtectionProfile()},
|
||||||
|
MasterKeyIdentifier: cfg.localSRTPMasterKeyIdentifier,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
selectedProto, err := extension.ALPNProtocolSelection(cfg.supportedProtocols, state.peerSupportedProtocols)
|
||||||
|
if err != nil {
|
||||||
|
return nil, &alert.Alert{Level: alert.Fatal, Description: alert.NoApplicationProtocol}, err
|
||||||
|
}
|
||||||
|
if selectedProto != "" {
|
||||||
|
extensions = append(extensions, &extension.ALPN{
|
||||||
|
ProtocolNameList: []string{selectedProto},
|
||||||
|
})
|
||||||
|
state.NegotiatedProtocol = selectedProto
|
||||||
|
}
|
||||||
|
|
||||||
|
cipherSuiteID := uint16(state.cipherSuite.ID())
|
||||||
|
var serverHello handshake.Handshake
|
||||||
|
|
||||||
|
serverHelloMessage := &handshake.MessageServerHello{
|
||||||
|
Version: protocol.Version1_2,
|
||||||
|
Random: state.localRandom,
|
||||||
|
SessionID: state.SessionID,
|
||||||
|
CipherSuiteID: &cipherSuiteID,
|
||||||
|
CompressionMethod: defaultCompressionMethods()[0],
|
||||||
|
Extensions: extensions,
|
||||||
|
}
|
||||||
|
|
||||||
|
if cfg.serverHelloMessageHook != nil {
|
||||||
|
serverHello = handshake.Handshake{Message: cfg.serverHelloMessageHook(*serverHelloMessage)}
|
||||||
|
} else {
|
||||||
|
serverHello = handshake.Handshake{Message: serverHelloMessage}
|
||||||
|
}
|
||||||
|
|
||||||
|
serverHello.Header.MessageSequence = uint16(state.handshakeSendSequence) //nolint:gosec // G115
|
||||||
|
|
||||||
|
if len(state.localVerifyData) == 0 {
|
||||||
|
plainText := cache.pullAndMerge(
|
||||||
|
handshakeCachePullRule{handshake.TypeClientHello, cfg.initialEpoch, true, false},
|
||||||
|
)
|
||||||
|
raw, err := serverHello.Marshal()
|
||||||
|
if err != nil {
|
||||||
|
return nil, &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}, err
|
||||||
|
}
|
||||||
|
plainText = append(plainText, raw...)
|
||||||
|
|
||||||
|
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,
|
||||||
|
},
|
||||||
|
Content: &serverHello,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
&packet{
|
||||||
|
record: &recordlayer.RecordLayer{
|
||||||
|
Header: recordlayer.Header{
|
||||||
|
Version: protocol.Version1_2,
|
||||||
|
},
|
||||||
|
Content: &protocol.ChangeCipherSpec{},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
&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
|
||||||
|
}
|
||||||
500
vendor/github.com/pion/dtls/v3/flight4handler.go
generated
vendored
Normal file
500
vendor/github.com/pion/dtls/v3/flight4handler.go
generated
vendored
Normal file
|
|
@ -0,0 +1,500 @@
|
||||||
|
// SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package dtls
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"crypto"
|
||||||
|
"crypto/rand"
|
||||||
|
"crypto/x509"
|
||||||
|
|
||||||
|
"github.com/pion/dtls/v3/internal/ciphersuite"
|
||||||
|
"github.com/pion/dtls/v3/pkg/crypto/clientcertificate"
|
||||||
|
"github.com/pion/dtls/v3/pkg/crypto/elliptic"
|
||||||
|
"github.com/pion/dtls/v3/pkg/crypto/prf"
|
||||||
|
"github.com/pion/dtls/v3/pkg/crypto/signaturehash"
|
||||||
|
"github.com/pion/dtls/v3/pkg/protocol"
|
||||||
|
"github.com/pion/dtls/v3/pkg/protocol/alert"
|
||||||
|
"github.com/pion/dtls/v3/pkg/protocol/extension"
|
||||||
|
"github.com/pion/dtls/v3/pkg/protocol/handshake"
|
||||||
|
"github.com/pion/dtls/v3/pkg/protocol/recordlayer"
|
||||||
|
)
|
||||||
|
|
||||||
|
//nolint:gocognit,gocyclo,lll,cyclop,maintidx
|
||||||
|
func flight4Parse(
|
||||||
|
ctx context.Context,
|
||||||
|
conn flightConn,
|
||||||
|
state *State,
|
||||||
|
cache *handshakeCache,
|
||||||
|
cfg *handshakeConfig,
|
||||||
|
) (flightVal, *alert.Alert, error) {
|
||||||
|
seq, msgs, ok := cache.fullPullMap(state.handshakeRecvSequence, state.cipherSuite,
|
||||||
|
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 the client offer its certificate, just disable session resumption.
|
||||||
|
// Otherwise, we have to store the certificate identitfication and expire time.
|
||||||
|
// And we have to check whether this certificate expired, revoked or changed.
|
||||||
|
//
|
||||||
|
// https://curl.se/docs/CVE-2016-5419.html
|
||||||
|
state.SessionID = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
//nolint:nestif
|
||||||
|
if verify, hasVerify := msgs[handshake.TypeCertificateVerify].(*handshake.MessageCertificateVerify); hasVerify {
|
||||||
|
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 == verify.HashAlgorithm && ss.Signature == verify.SignatureAlgorithm {
|
||||||
|
validSignatureScheme = true
|
||||||
|
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !validSignatureScheme {
|
||||||
|
return 0, &alert.Alert{Level: alert.Fatal, Description: alert.InsufficientSecurity}, errNoAvailableSignatureSchemes
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := verifyCertificateVerify(
|
||||||
|
plainText,
|
||||||
|
verify.HashAlgorithm,
|
||||||
|
verify.SignatureAlgorithm,
|
||||||
|
verify.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 {
|
||||||
|
// Use cert-specific algorithms if present, otherwise fall back to signature_algorithms per RFC 8446
|
||||||
|
certAlgs := cfg.localCertSignatureSchemes
|
||||||
|
if len(certAlgs) == 0 {
|
||||||
|
certAlgs = cfg.localSignatureSchemes
|
||||||
|
}
|
||||||
|
if chains, err = verifyClientCert(state.PeerCertificates, cfg.clientCAs, certAlgs); 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
|
||||||
|
} else if state.PeerCertificates != nil {
|
||||||
|
// A certificate was received, but we haven't seen a CertificateVerify
|
||||||
|
// keep reading until we receive one
|
||||||
|
return 0, nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if !state.cipherSuite.IsInitialized() { //nolint:nestif
|
||||||
|
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
|
||||||
|
switch state.cipherSuite.KeyExchangeAlgorithm() {
|
||||||
|
case CipherSuiteKeyExchangeAlgorithmPsk:
|
||||||
|
preMasterSecret = prf.PSKPreMasterSecret(psk)
|
||||||
|
case (CipherSuiteKeyExchangeAlgorithmPsk | CipherSuiteKeyExchangeAlgorithmEcdhe):
|
||||||
|
if preMasterSecret, err = prf.EcdhePSKPreMasterSecret(
|
||||||
|
psk,
|
||||||
|
clientKeyExchange.PublicKey,
|
||||||
|
state.localKeypair.PrivateKey,
|
||||||
|
state.localKeypair.Curve,
|
||||||
|
); err != nil {
|
||||||
|
return 0, &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}, err
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return 0, &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}, errInvalidCipherSuite
|
||||||
|
}
|
||||||
|
} 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)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(state.SessionID) > 0 {
|
||||||
|
s := Session{
|
||||||
|
ID: state.SessionID,
|
||||||
|
Secret: state.masterSecret,
|
||||||
|
}
|
||||||
|
cfg.log.Tracef("[handshake] save new session: %x", s.ID)
|
||||||
|
if err := cfg.sessionStore.Set(state.SessionID, s); err != nil {
|
||||||
|
return 0, &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now, encrypted packets can be handled
|
||||||
|
if err := conn.handleQueuedPackets(ctx); err != nil {
|
||||||
|
return 0, &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
seq, msgs, ok = cache.fullPullMap(seq, state.cipherSuite,
|
||||||
|
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 { //nolint:nestif
|
||||||
|
if cfg.verifyConnection != nil {
|
||||||
|
stateClone, err := state.clone()
|
||||||
|
if err != nil {
|
||||||
|
return 0, &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}, err
|
||||||
|
}
|
||||||
|
if err := cfg.verifyConnection(stateClone); err != nil {
|
||||||
|
return 0, &alert.Alert{Level: alert.Fatal, Description: alert.BadCertificate}, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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:
|
||||||
|
// go to flight6
|
||||||
|
}
|
||||||
|
if cfg.verifyConnection != nil {
|
||||||
|
stateClone, err := state.clone()
|
||||||
|
if err != nil {
|
||||||
|
return 0, &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}, err
|
||||||
|
}
|
||||||
|
if err := cfg.verifyConnection(stateClone); err != nil {
|
||||||
|
return 0, &alert.Alert{Level: alert.Fatal, Description: alert.BadCertificate}, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return flight6, nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
//nolint:gocognit,cyclop,maintidx
|
||||||
|
func flight4Generate(
|
||||||
|
_ flightConn,
|
||||||
|
state *State,
|
||||||
|
_ *handshakeCache,
|
||||||
|
cfg *handshakeConfig,
|
||||||
|
) ([]*packet, *alert.Alert, error) {
|
||||||
|
extensions := []extension.Extension{}
|
||||||
|
|
||||||
|
if (cfg.extendedMasterSecret == RequestExtendedMasterSecret ||
|
||||||
|
cfg.extendedMasterSecret == RequireExtendedMasterSecret) && state.extendedMasterSecret {
|
||||||
|
extensions = append(extensions, &extension.UseExtendedMasterSecret{
|
||||||
|
Supported: true,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
if state.getSRTPProtectionProfile() != 0 {
|
||||||
|
extensions = append(extensions, &extension.UseSRTP{
|
||||||
|
ProtectionProfiles: []SRTPProtectionProfile{state.getSRTPProtectionProfile()},
|
||||||
|
MasterKeyIdentifier: cfg.localSRTPMasterKeyIdentifier,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
if state.remoteSupportsRenegotiation {
|
||||||
|
extensions = append(extensions, &extension.RenegotiationInfo{
|
||||||
|
RenegotiatedConnection: 0,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
if state.cipherSuite.AuthenticationType() == CipherSuiteAuthenticationTypeCertificate {
|
||||||
|
extensions = append(extensions, &extension.SupportedPointFormats{
|
||||||
|
PointFormats: []elliptic.CurvePointFormat{elliptic.CurvePointFormatUncompressed},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
selectedProto, err := extension.ALPNProtocolSelection(cfg.supportedProtocols, state.peerSupportedProtocols)
|
||||||
|
if err != nil {
|
||||||
|
return nil, &alert.Alert{Level: alert.Fatal, Description: alert.NoApplicationProtocol}, err
|
||||||
|
}
|
||||||
|
if selectedProto != "" {
|
||||||
|
extensions = append(extensions, &extension.ALPN{
|
||||||
|
ProtocolNameList: []string{selectedProto},
|
||||||
|
})
|
||||||
|
state.NegotiatedProtocol = selectedProto
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we have a connection ID generator, we are willing to use connection
|
||||||
|
// IDs. We already know whether the client supports connection IDs from
|
||||||
|
// parsing the ClientHello, so avoid setting local connection ID if the
|
||||||
|
// client won't send it.
|
||||||
|
if cfg.connectionIDGenerator != nil && state.remoteConnectionID != nil {
|
||||||
|
state.setLocalConnectionID(cfg.connectionIDGenerator())
|
||||||
|
extensions = append(extensions, &extension.ConnectionID{CID: state.getLocalConnectionID()})
|
||||||
|
}
|
||||||
|
|
||||||
|
var pkts []*packet
|
||||||
|
cipherSuiteID := uint16(state.cipherSuite.ID())
|
||||||
|
|
||||||
|
if cfg.sessionStore != nil {
|
||||||
|
state.SessionID = make([]byte, sessionLength)
|
||||||
|
if _, err := rand.Read(state.SessionID); err != nil {
|
||||||
|
return nil, &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
serverHello := &handshake.MessageServerHello{
|
||||||
|
Version: protocol.Version1_2,
|
||||||
|
Random: state.localRandom,
|
||||||
|
SessionID: state.SessionID,
|
||||||
|
CipherSuiteID: &cipherSuiteID,
|
||||||
|
CompressionMethod: defaultCompressionMethods()[0],
|
||||||
|
Extensions: extensions,
|
||||||
|
}
|
||||||
|
|
||||||
|
var content handshake.Handshake
|
||||||
|
|
||||||
|
if cfg.serverHelloMessageHook != nil {
|
||||||
|
content = handshake.Handshake{Message: cfg.serverHelloMessageHook(*serverHello)}
|
||||||
|
} else {
|
||||||
|
content = handshake.Handshake{Message: serverHello}
|
||||||
|
}
|
||||||
|
|
||||||
|
pkts = append(pkts, &packet{
|
||||||
|
record: &recordlayer.RecordLayer{
|
||||||
|
Header: recordlayer.Header{
|
||||||
|
Version: protocol.Version1_2,
|
||||||
|
},
|
||||||
|
Content: &content,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
switch {
|
||||||
|
case state.cipherSuite.AuthenticationType() == CipherSuiteAuthenticationTypeCertificate:
|
||||||
|
certificate, err := cfg.getCertificate(&ClientHelloInfo{
|
||||||
|
ServerName: state.serverName,
|
||||||
|
CipherSuites: []ciphersuite.ID{state.cipherSuite.ID()},
|
||||||
|
RandomBytes: state.remoteRandom.RandomBytes,
|
||||||
|
})
|
||||||
|
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()
|
||||||
|
|
||||||
|
signer, ok := certificate.PrivateKey.(crypto.Signer)
|
||||||
|
if !ok {
|
||||||
|
return nil, &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}, errInvalidPrivateKey
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find compatible signature scheme
|
||||||
|
signatureHashAlgo, err := signaturehash.SelectSignatureScheme(cfg.localSignatureSchemes, signer)
|
||||||
|
if err != nil {
|
||||||
|
return nil, &alert.Alert{Level: alert.Fatal, Description: alert.InsufficientSecurity}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
signature, err := generateKeySignature(
|
||||||
|
clientRandom[:],
|
||||||
|
serverRandom[:],
|
||||||
|
state.localKeypair.PublicKey,
|
||||||
|
state.namedCurve,
|
||||||
|
signer,
|
||||||
|
signatureHashAlgo.Hash,
|
||||||
|
signatureHashAlgo.Signature,
|
||||||
|
)
|
||||||
|
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 {
|
||||||
|
// An empty list of certificateAuthorities signals to
|
||||||
|
// the client that it may send any certificate in response
|
||||||
|
// to our request. When we know the CAs we trust, then
|
||||||
|
// we can send them down, so that the client can choose
|
||||||
|
// an appropriate certificate to give to us.
|
||||||
|
var certificateAuthorities [][]byte
|
||||||
|
if cfg.clientCAs != nil {
|
||||||
|
// nolint:staticcheck // ignoring tlsCert.RootCAs.Subjects is deprecated ERR
|
||||||
|
// because cert does not come from SystemCertPool and it's ok if certificate
|
||||||
|
// authorities is empty.
|
||||||
|
certificateAuthorities = cfg.clientCAs.Subjects()
|
||||||
|
}
|
||||||
|
|
||||||
|
certReq := &handshake.MessageCertificateRequest{
|
||||||
|
CertificateTypes: []clientcertificate.Type{clientcertificate.RSASign, clientcertificate.ECDSASign},
|
||||||
|
SignatureHashAlgorithms: cfg.localSignatureSchemes,
|
||||||
|
CertificateAuthoritiesNames: certificateAuthorities,
|
||||||
|
}
|
||||||
|
|
||||||
|
var content handshake.Handshake
|
||||||
|
|
||||||
|
if cfg.certificateRequestMessageHook != nil {
|
||||||
|
content = handshake.Handshake{Message: cfg.certificateRequestMessageHook(*certReq)}
|
||||||
|
} else {
|
||||||
|
content = handshake.Handshake{Message: certReq}
|
||||||
|
}
|
||||||
|
|
||||||
|
pkts = append(pkts, &packet{
|
||||||
|
record: &recordlayer.RecordLayer{
|
||||||
|
Header: recordlayer.Header{
|
||||||
|
Version: protocol.Version1_2,
|
||||||
|
},
|
||||||
|
Content: &content,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
case cfg.localPSKIdentityHint != nil ||
|
||||||
|
state.cipherSuite.KeyExchangeAlgorithm().Has(CipherSuiteKeyExchangeAlgorithmEcdhe):
|
||||||
|
// 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 and cipher suite doesn't use elliptic curve,
|
||||||
|
// the ServerKeyExchange message is omitted.
|
||||||
|
//
|
||||||
|
// https://tools.ietf.org/html/rfc4279#section-2
|
||||||
|
srvExchange := &handshake.MessageServerKeyExchange{
|
||||||
|
IdentityHint: cfg.localPSKIdentityHint,
|
||||||
|
}
|
||||||
|
if state.cipherSuite.KeyExchangeAlgorithm().Has(CipherSuiteKeyExchangeAlgorithmEcdhe) {
|
||||||
|
srvExchange.EllipticCurveType = elliptic.CurveTypeNamedCurve
|
||||||
|
srvExchange.NamedCurve = state.namedCurve
|
||||||
|
srvExchange.PublicKey = state.localKeypair.PublicKey
|
||||||
|
}
|
||||||
|
pkts = append(pkts, &packet{
|
||||||
|
record: &recordlayer.RecordLayer{
|
||||||
|
Header: recordlayer.Header{
|
||||||
|
Version: protocol.Version1_2,
|
||||||
|
},
|
||||||
|
Content: &handshake.Handshake{
|
||||||
|
Message: srvExchange,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pkts = append(pkts, &packet{
|
||||||
|
record: &recordlayer.RecordLayer{
|
||||||
|
Header: recordlayer.Header{
|
||||||
|
Version: protocol.Version1_2,
|
||||||
|
},
|
||||||
|
Content: &handshake.Handshake{
|
||||||
|
Message: &handshake.MessageServerHelloDone{},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
return pkts, nil, nil
|
||||||
|
}
|
||||||
89
vendor/github.com/pion/dtls/v3/flight5bhandler.go
generated
vendored
Normal file
89
vendor/github.com/pion/dtls/v3/flight5bhandler.go
generated
vendored
Normal file
|
|
@ -0,0 +1,89 @@
|
||||||
|
// SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package dtls
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"github.com/pion/dtls/v3/pkg/crypto/prf"
|
||||||
|
"github.com/pion/dtls/v3/pkg/protocol"
|
||||||
|
"github.com/pion/dtls/v3/pkg/protocol/alert"
|
||||||
|
"github.com/pion/dtls/v3/pkg/protocol/handshake"
|
||||||
|
"github.com/pion/dtls/v3/pkg/protocol/recordlayer"
|
||||||
|
)
|
||||||
|
|
||||||
|
func flight5bParse(
|
||||||
|
_ context.Context,
|
||||||
|
_ flightConn,
|
||||||
|
state *State,
|
||||||
|
cache *handshakeCache,
|
||||||
|
cfg *handshakeConfig,
|
||||||
|
) (flightVal, *alert.Alert, error) {
|
||||||
|
_, msgs, ok := cache.fullPullMap(state.handshakeRecvSequence-1, state.cipherSuite,
|
||||||
|
handshakeCachePullRule{handshake.TypeFinished, cfg.initialEpoch + 1, false, 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 may re-transmit the last flight. Keep state to be flight5b.
|
||||||
|
return flight5b, nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func flight5bGenerate(
|
||||||
|
_ flightConn,
|
||||||
|
state *State,
|
||||||
|
cache *handshakeCache,
|
||||||
|
cfg *handshakeConfig,
|
||||||
|
) ([]*packet, *alert.Alert, error) { //nolint:gocognit
|
||||||
|
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.TypeFinished, cfg.initialEpoch + 1, false, false},
|
||||||
|
)
|
||||||
|
|
||||||
|
var err error
|
||||||
|
state.localVerifyData, err = prf.VerifyDataClient(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
|
||||||
|
}
|
||||||
410
vendor/github.com/pion/dtls/v3/flight5handler.go
generated
vendored
Normal file
410
vendor/github.com/pion/dtls/v3/flight5handler.go
generated
vendored
Normal file
|
|
@ -0,0 +1,410 @@
|
||||||
|
// SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package dtls
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"context"
|
||||||
|
"crypto"
|
||||||
|
"crypto/x509"
|
||||||
|
|
||||||
|
"github.com/pion/dtls/v3/pkg/crypto/prf"
|
||||||
|
"github.com/pion/dtls/v3/pkg/crypto/signaturehash"
|
||||||
|
"github.com/pion/dtls/v3/pkg/protocol"
|
||||||
|
"github.com/pion/dtls/v3/pkg/protocol/alert"
|
||||||
|
"github.com/pion/dtls/v3/pkg/protocol/handshake"
|
||||||
|
"github.com/pion/dtls/v3/pkg/protocol/recordlayer"
|
||||||
|
)
|
||||||
|
|
||||||
|
func flight5Parse(
|
||||||
|
_ context.Context,
|
||||||
|
conn flightConn,
|
||||||
|
state *State,
|
||||||
|
cache *handshakeCache,
|
||||||
|
cfg *handshakeConfig,
|
||||||
|
) (flightVal, *alert.Alert, error) {
|
||||||
|
_, msgs, ok := cache.fullPullMap(state.handshakeRecvSequence, state.cipherSuite,
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(state.SessionID) > 0 {
|
||||||
|
s := Session{
|
||||||
|
ID: state.SessionID,
|
||||||
|
Secret: state.masterSecret,
|
||||||
|
}
|
||||||
|
cfg.log.Tracef("[handshake] save new session: %x", s.ID)
|
||||||
|
if err := cfg.sessionStore.Set(conn.sessionKey(), s); err != nil {
|
||||||
|
return 0, &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return flight5, nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
//nolint:gocognit,cyclop,maintidx
|
||||||
|
func flight5Generate(
|
||||||
|
conn flightConn,
|
||||||
|
state *State,
|
||||||
|
cache *handshakeCache,
|
||||||
|
cfg *handshakeConfig,
|
||||||
|
) ([]*packet, *alert.Alert, error) {
|
||||||
|
var signer crypto.Signer
|
||||||
|
var pkts []*packet
|
||||||
|
if state.remoteRequestedCertificate { //nolint:nestif
|
||||||
|
_, msgs, ok := cache.fullPullMap(state.handshakeRecvSequence-2, state.cipherSuite,
|
||||||
|
handshakeCachePullRule{handshake.TypeCertificateRequest, cfg.initialEpoch, false, false})
|
||||||
|
if !ok {
|
||||||
|
return nil, &alert.Alert{Level: alert.Fatal, Description: alert.HandshakeFailure}, errClientCertificateRequired
|
||||||
|
}
|
||||||
|
reqInfo := CertificateRequestInfo{}
|
||||||
|
if r, ok2 := msgs[handshake.TypeCertificateRequest].(*handshake.MessageCertificateRequest); ok2 {
|
||||||
|
reqInfo.AcceptableCAs = r.CertificateAuthoritiesNames
|
||||||
|
} else {
|
||||||
|
return nil, &alert.Alert{Level: alert.Fatal, Description: alert.HandshakeFailure}, errClientCertificateRequired
|
||||||
|
}
|
||||||
|
certificate, err := cfg.getClientCertificate(&reqInfo)
|
||||||
|
if err != nil {
|
||||||
|
return nil, &alert.Alert{Level: alert.Fatal, Description: alert.HandshakeFailure}, err
|
||||||
|
}
|
||||||
|
if certificate == nil {
|
||||||
|
return nil, &alert.Alert{Level: alert.Fatal, Description: alert.HandshakeFailure}, errNotAcceptableCertificateChain
|
||||||
|
}
|
||||||
|
if certificate.Certificate != nil {
|
||||||
|
signer, ok = certificate.PrivateKey.(crypto.Signer)
|
||||||
|
if !ok {
|
||||||
|
return nil, &alert.Alert{Level: alert.Fatal, Description: alert.HandshakeFailure}, errInvalidPrivateKey
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pkts = append(pkts,
|
||||||
|
&packet{
|
||||||
|
record: &recordlayer.RecordLayer{
|
||||||
|
Header: recordlayer.Header{
|
||||||
|
Version: protocol.Version1_2,
|
||||||
|
},
|
||||||
|
Content: &handshake.Handshake{
|
||||||
|
Message: &handshake.MessageCertificate{
|
||||||
|
Certificate: certificate.Certificate,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
clientKeyExchange := &handshake.MessageClientKeyExchange{}
|
||||||
|
if cfg.localPSKCallback == nil {
|
||||||
|
clientKeyExchange.PublicKey = state.localKeypair.PublicKey
|
||||||
|
} else {
|
||||||
|
clientKeyExchange.IdentityHint = cfg.localPSKIdentityHint
|
||||||
|
}
|
||||||
|
if state != nil && state.localKeypair != nil && len(state.localKeypair.PublicKey) > 0 {
|
||||||
|
clientKeyExchange.PublicKey = state.localKeypair.PublicKey
|
||||||
|
}
|
||||||
|
|
||||||
|
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(conn, state, cfg, &handshake.MessageServerKeyExchange{})
|
||||||
|
if err != nil {
|
||||||
|
return nil, alertPtr, err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
rawHandshake := &handshake.Handshake{
|
||||||
|
KeyExchangeAlgorithm: state.cipherSuite.KeyExchangeAlgorithm(),
|
||||||
|
}
|
||||||
|
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) //nolint:gosec // G115
|
||||||
|
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 := initializeCipherSuite(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 && signer != nil {
|
||||||
|
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(state.remoteCertRequestAlgs, signer)
|
||||||
|
if err != nil {
|
||||||
|
return nil, &alert.Alert{Level: alert.Fatal, Description: alert.InsufficientSecurity}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
certVerify, err := generateCertificateVerify(plainText, signer, signatureHashAlgo.Hash, signatureHashAlgo.Signature)
|
||||||
|
if err != nil {
|
||||||
|
return nil, &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}, err
|
||||||
|
}
|
||||||
|
state.localCertificatesVerify = certVerify
|
||||||
|
|
||||||
|
pkt := &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, pkt)
|
||||||
|
|
||||||
|
h, ok := pkt.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,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
shouldWrapCID: len(state.remoteConnectionID) > 0,
|
||||||
|
shouldEncrypt: true,
|
||||||
|
resetLocalSequenceNumber: true,
|
||||||
|
})
|
||||||
|
|
||||||
|
return pkts, nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
//nolint:gocognit,cyclop
|
||||||
|
func initializeCipherSuite(
|
||||||
|
state *State,
|
||||||
|
cache *handshakeCache,
|
||||||
|
cfg *handshakeConfig,
|
||||||
|
handshakeKeyExchange *handshake.MessageServerKeyExchange,
|
||||||
|
sendingPlainText []byte,
|
||||||
|
) (*alert.Alert, error) {
|
||||||
|
if state.cipherSuite.IsInitialized() {
|
||||||
|
return nil, nil //nolint
|
||||||
|
}
|
||||||
|
|
||||||
|
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 { //nolint:nestif
|
||||||
|
// Verify that the pair of hash algorithm and signiture is listed.
|
||||||
|
var validSignatureScheme bool
|
||||||
|
for _, ss := range cfg.localSignatureSchemes {
|
||||||
|
if ss.Hash == handshakeKeyExchange.HashAlgorithm && ss.Signature == handshakeKeyExchange.SignatureAlgorithm {
|
||||||
|
validSignatureScheme = true
|
||||||
|
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !validSignatureScheme {
|
||||||
|
return &alert.Alert{Level: alert.Fatal, Description: alert.InsufficientSecurity}, errNoAvailableSignatureSchemes
|
||||||
|
}
|
||||||
|
|
||||||
|
expectedMsg := valueKeyMessage(
|
||||||
|
clientRandom[:],
|
||||||
|
serverRandom[:],
|
||||||
|
handshakeKeyExchange.PublicKey,
|
||||||
|
handshakeKeyExchange.NamedCurve,
|
||||||
|
)
|
||||||
|
if err = verifyKeySignature(
|
||||||
|
expectedMsg,
|
||||||
|
handshakeKeyExchange.Signature,
|
||||||
|
handshakeKeyExchange.HashAlgorithm,
|
||||||
|
handshakeKeyExchange.SignatureAlgorithm,
|
||||||
|
state.PeerCertificates,
|
||||||
|
); err != nil {
|
||||||
|
return &alert.Alert{Level: alert.Fatal, Description: alert.BadCertificate}, err
|
||||||
|
}
|
||||||
|
var chains [][]*x509.Certificate
|
||||||
|
if !cfg.insecureSkipVerify {
|
||||||
|
certAlgs := cfg.localCertSignatureSchemes
|
||||||
|
if len(certAlgs) == 0 {
|
||||||
|
certAlgs = cfg.localSignatureSchemes
|
||||||
|
}
|
||||||
|
if chains, err = verifyServerCert(state.PeerCertificates, cfg.rootCAs, cfg.serverName, certAlgs); 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 cfg.verifyConnection != nil {
|
||||||
|
stateClone, errC := state.clone()
|
||||||
|
if errC != nil {
|
||||||
|
return &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}, errC
|
||||||
|
}
|
||||||
|
if errC = cfg.verifyConnection(stateClone); errC != nil {
|
||||||
|
return &alert.Alert{Level: alert.Fatal, Description: alert.BadCertificate}, errC
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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 //nolint
|
||||||
|
}
|
||||||
98
vendor/github.com/pion/dtls/v3/flight6handler.go
generated
vendored
Normal file
98
vendor/github.com/pion/dtls/v3/flight6handler.go
generated
vendored
Normal file
|
|
@ -0,0 +1,98 @@
|
||||||
|
// SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package dtls
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"github.com/pion/dtls/v3/pkg/crypto/prf"
|
||||||
|
"github.com/pion/dtls/v3/pkg/protocol"
|
||||||
|
"github.com/pion/dtls/v3/pkg/protocol/alert"
|
||||||
|
"github.com/pion/dtls/v3/pkg/protocol/handshake"
|
||||||
|
"github.com/pion/dtls/v3/pkg/protocol/recordlayer"
|
||||||
|
)
|
||||||
|
|
||||||
|
func flight6Parse(
|
||||||
|
_ context.Context,
|
||||||
|
_ flightConn,
|
||||||
|
state *State,
|
||||||
|
cache *handshakeCache,
|
||||||
|
cfg *handshakeConfig,
|
||||||
|
) (flightVal, *alert.Alert, error) {
|
||||||
|
_, msgs, ok := cache.fullPullMap(state.handshakeRecvSequence-1, state.cipherSuite,
|
||||||
|
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 may re-transmit the last flight. Keep state to be flight6.
|
||||||
|
return flight6, nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func flight6Generate(
|
||||||
|
_ 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,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
shouldWrapCID: len(state.remoteConnectionID) > 0,
|
||||||
|
shouldEncrypt: true,
|
||||||
|
resetLocalSequenceNumber: true,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
return pkts, nil, nil
|
||||||
|
}
|
||||||
74
vendor/github.com/pion/dtls/v3/flighthandler.go
generated
vendored
Normal file
74
vendor/github.com/pion/dtls/v3/flighthandler.go
generated
vendored
Normal file
|
|
@ -0,0 +1,74 @@
|
||||||
|
// SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package dtls
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"github.com/pion/dtls/v3/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) { //nolint:cyclop
|
||||||
|
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 flight4b:
|
||||||
|
return flight4bParse, nil
|
||||||
|
case flight5:
|
||||||
|
return flight5Parse, nil
|
||||||
|
case flight5b:
|
||||||
|
return flight5bParse, nil
|
||||||
|
case flight6:
|
||||||
|
return flight6Parse, nil
|
||||||
|
default:
|
||||||
|
return nil, errInvalidFlight
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f flightVal) getFlightGenerator() (gen flightGenerator, retransmit bool, err error) { //nolint:cyclop
|
||||||
|
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 flight4b:
|
||||||
|
return flight4bGenerate, true, nil
|
||||||
|
case flight5:
|
||||||
|
return flight5Generate, true, nil
|
||||||
|
case flight5b:
|
||||||
|
return flight5bGenerate, true, nil
|
||||||
|
case flight6:
|
||||||
|
return flight6Generate, true, nil
|
||||||
|
default:
|
||||||
|
return nil, false, errInvalidFlight
|
||||||
|
}
|
||||||
|
}
|
||||||
151
vendor/github.com/pion/dtls/v3/fragment_buffer.go
generated
vendored
Normal file
151
vendor/github.com/pion/dtls/v3/fragment_buffer.go
generated
vendored
Normal file
|
|
@ -0,0 +1,151 @@
|
||||||
|
// SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package dtls
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/pion/dtls/v3/pkg/protocol"
|
||||||
|
"github.com/pion/dtls/v3/pkg/protocol/handshake"
|
||||||
|
"github.com/pion/dtls/v3/pkg/protocol/recordlayer"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// 2 megabytes.
|
||||||
|
fragmentBufferMaxSize = 2000000
|
||||||
|
fragmentBufferMaxCount = 1000
|
||||||
|
)
|
||||||
|
|
||||||
|
type fragment struct {
|
||||||
|
recordLayerHeader recordlayer.Header
|
||||||
|
handshakeHeader handshake.Header
|
||||||
|
data []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
type fragments struct {
|
||||||
|
fragmentByOffset map[uint32]*fragment
|
||||||
|
fragmentsLength uint32
|
||||||
|
handshakeLength uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
type fragmentBuffer struct {
|
||||||
|
// map of MessageSequenceNumbers that hold slices of fragments
|
||||||
|
cache map[uint16]*fragments
|
||||||
|
|
||||||
|
currentMessageSequenceNumber uint16
|
||||||
|
|
||||||
|
totalBufferSize int
|
||||||
|
totalFragmentCount int
|
||||||
|
}
|
||||||
|
|
||||||
|
func newFragmentBuffer() *fragmentBuffer {
|
||||||
|
return &fragmentBuffer{cache: map[uint16]*fragments{}}
|
||||||
|
}
|
||||||
|
|
||||||
|
// current total size of buffer.
|
||||||
|
func (f *fragmentBuffer) size() int {
|
||||||
|
return f.totalBufferSize
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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) (isHandshake, isRetransmit bool, err error) { //nolint:cyclop
|
||||||
|
if f.size()+len(buf) >= fragmentBufferMaxSize || f.totalFragmentCount >= fragmentBufferMaxCount {
|
||||||
|
return false, false, errFragmentBufferOverflow
|
||||||
|
}
|
||||||
|
|
||||||
|
recordLayerHeader := recordlayer.Header{}
|
||||||
|
if err := recordLayerHeader.Unmarshal(buf); err != nil {
|
||||||
|
return false, false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// fragment isn't a handshake, we don't need to handle it
|
||||||
|
if recordLayerHeader.ContentType != protocol.ContentTypeHandshake {
|
||||||
|
return false, false, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
frag := new(fragment)
|
||||||
|
for buf = buf[recordlayer.FixedHeaderSize:]; len(buf) != 0; frag = new(fragment) { //nolint:gosec // G602
|
||||||
|
if err := frag.handshakeHeader.Unmarshal(buf); err != nil {
|
||||||
|
return false, false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fragment is a retransmission. We have already assembled it before successfully
|
||||||
|
isRetransmit = frag.handshakeHeader.FragmentOffset == 0 &&
|
||||||
|
frag.handshakeHeader.MessageSequence < f.currentMessageSequenceNumber
|
||||||
|
|
||||||
|
end := int(handshake.HeaderLength + frag.handshakeHeader.FragmentLength)
|
||||||
|
if end > len(buf) {
|
||||||
|
return false, false, errBufferTooSmall
|
||||||
|
}
|
||||||
|
if frag.handshakeHeader.MessageSequence < f.currentMessageSequenceNumber {
|
||||||
|
buf = buf[end:]
|
||||||
|
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
messageFragments, ok := f.cache[frag.handshakeHeader.MessageSequence]
|
||||||
|
if !ok {
|
||||||
|
messageFragments = &fragments{
|
||||||
|
fragmentByOffset: map[uint32]*fragment{}, handshakeLength: frag.handshakeHeader.Length,
|
||||||
|
}
|
||||||
|
f.cache[frag.handshakeHeader.MessageSequence] = messageFragments
|
||||||
|
}
|
||||||
|
|
||||||
|
// Discard all headers, when rebuilding the packet we will re-build
|
||||||
|
frag.data = append([]byte{}, buf[handshake.HeaderLength:end]...)
|
||||||
|
frag.recordLayerHeader = recordLayerHeader
|
||||||
|
|
||||||
|
if _, ok = messageFragments.fragmentByOffset[frag.handshakeHeader.FragmentOffset]; !ok {
|
||||||
|
messageFragments.fragmentByOffset[frag.handshakeHeader.FragmentOffset] = frag
|
||||||
|
messageFragments.fragmentsLength += frag.handshakeHeader.FragmentLength
|
||||||
|
f.totalBufferSize += int(frag.handshakeHeader.FragmentLength)
|
||||||
|
f.totalFragmentCount++
|
||||||
|
}
|
||||||
|
buf = buf[end:]
|
||||||
|
}
|
||||||
|
|
||||||
|
return true, isRetransmit, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *fragmentBuffer) pop() (content []byte, epoch uint16) {
|
||||||
|
frags, ok := f.cache[f.currentMessageSequenceNumber]
|
||||||
|
if !ok {
|
||||||
|
return nil, 0
|
||||||
|
}
|
||||||
|
|
||||||
|
if frags.fragmentsLength != frags.handshakeLength {
|
||||||
|
return nil, 0
|
||||||
|
}
|
||||||
|
|
||||||
|
var rawMessage []byte
|
||||||
|
targetOffset := uint32(0)
|
||||||
|
for i := 0; i < len(frags.fragmentByOffset) && targetOffset < frags.handshakeLength; i++ {
|
||||||
|
if frag, ok := frags.fragmentByOffset[targetOffset]; ok {
|
||||||
|
rawMessage = append(rawMessage, frag.data...)
|
||||||
|
targetOffset = frag.handshakeHeader.FragmentOffset + frag.handshakeHeader.FragmentLength
|
||||||
|
} else {
|
||||||
|
return nil, 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if int(frags.handshakeLength) != len(rawMessage) {
|
||||||
|
return nil, 0
|
||||||
|
}
|
||||||
|
|
||||||
|
firstHeader := frags.fragmentByOffset[0].handshakeHeader
|
||||||
|
firstHeader.FragmentOffset = 0
|
||||||
|
firstHeader.FragmentLength = firstHeader.Length
|
||||||
|
|
||||||
|
rawHeader, _ := firstHeader.Marshal()
|
||||||
|
|
||||||
|
messageEpoch := frags.fragmentByOffset[0].recordLayerHeader.Epoch
|
||||||
|
|
||||||
|
f.totalBufferSize -= int(frags.fragmentsLength)
|
||||||
|
f.totalFragmentCount -= len(frags.fragmentByOffset)
|
||||||
|
|
||||||
|
delete(f.cache, f.currentMessageSequenceNumber)
|
||||||
|
f.currentMessageSequenceNumber++
|
||||||
|
|
||||||
|
return append(rawHeader, rawMessage...), messageEpoch
|
||||||
|
}
|
||||||
185
vendor/github.com/pion/dtls/v3/handshake_cache.go
generated
vendored
Normal file
185
vendor/github.com/pion/dtls/v3/handshake_cache.go
generated
vendored
Normal file
|
|
@ -0,0 +1,185 @@
|
||||||
|
// SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package dtls
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"github.com/pion/dtls/v3/pkg/crypto/prf"
|
||||||
|
"github.com/pion/dtls/v3/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) {
|
||||||
|
h.mu.Lock()
|
||||||
|
defer h.mu.Unlock()
|
||||||
|
|
||||||
|
h.cache = append(h.cache, &handshakeCacheItem{
|
||||||
|
data: append([]byte{}, data...),
|
||||||
|
epoch: epoch,
|
||||||
|
messageSequence: messageSequence,
|
||||||
|
typ: typ,
|
||||||
|
isClient: isClient,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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.
|
||||||
|
//
|
||||||
|
//nolint:cyclop
|
||||||
|
func (h *handshakeCache) fullPullMap(
|
||||||
|
startSeq int,
|
||||||
|
cipherSuite CipherSuite,
|
||||||
|
rules ...handshakeCachePullRule,
|
||||||
|
) (int, map[handshake.Type]handshake.Message, bool) {
|
||||||
|
h.mu.Lock()
|
||||||
|
defer h.mu.Unlock()
|
||||||
|
|
||||||
|
ci := make(map[handshake.Type]*handshakeCacheItem)
|
||||||
|
for _, rule := range rules {
|
||||||
|
var item *handshakeCacheItem
|
||||||
|
for _, c := range h.cache {
|
||||||
|
if c.typ == rule.typ && c.isClient == rule.isClient && c.epoch == rule.epoch {
|
||||||
|
switch {
|
||||||
|
case item == nil:
|
||||||
|
item = c
|
||||||
|
case item.messageSequence < c.messageSequence:
|
||||||
|
item = c
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !rule.optional && item == nil {
|
||||||
|
// Missing mandatory message.
|
||||||
|
return startSeq, nil, false
|
||||||
|
}
|
||||||
|
ci[rule.typ] = item
|
||||||
|
}
|
||||||
|
out := make(map[handshake.Type]handshake.Message)
|
||||||
|
seq := startSeq
|
||||||
|
ok := false
|
||||||
|
for _, r := range rules {
|
||||||
|
typ := r.typ
|
||||||
|
i := ci[typ]
|
||||||
|
if i == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
var keyExchangeAlgorithm CipherSuiteKeyExchangeAlgorithm
|
||||||
|
if cipherSuite != nil {
|
||||||
|
keyExchangeAlgorithm = cipherSuite.KeyExchangeAlgorithm()
|
||||||
|
}
|
||||||
|
rawHandshake := &handshake.Handshake{
|
||||||
|
KeyExchangeAlgorithm: keyExchangeAlgorithm,
|
||||||
|
}
|
||||||
|
if err := rawHandshake.Unmarshal(i.data); err != nil {
|
||||||
|
return startSeq, nil, false
|
||||||
|
}
|
||||||
|
if uint16(seq) != rawHandshake.Header.MessageSequence { //nolint:gosec // G115
|
||||||
|
// There is a gap. Some messages are not arrived.
|
||||||
|
return startSeq, nil, false
|
||||||
|
}
|
||||||
|
seq++
|
||||||
|
ok = true
|
||||||
|
out[typ] = rawHandshake.Message
|
||||||
|
}
|
||||||
|
if !ok {
|
||||||
|
return seq, nil, false
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
364
vendor/github.com/pion/dtls/v3/handshaker.go
generated
vendored
Normal file
364
vendor/github.com/pion/dtls/v3/handshaker.go
generated
vendored
Normal file
|
|
@ -0,0 +1,364 @@
|
||||||
|
// SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package dtls
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"crypto/tls"
|
||||||
|
"crypto/x509"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/pion/dtls/v3/pkg/crypto/elliptic"
|
||||||
|
"github.com/pion/dtls/v3/pkg/crypto/signaturehash"
|
||||||
|
"github.com/pion/dtls/v3/pkg/protocol/alert"
|
||||||
|
"github.com/pion/dtls/v3/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
|
||||||
|
retransmitInterval time.Duration
|
||||||
|
state *State
|
||||||
|
cache *handshakeCache
|
||||||
|
cfg *handshakeConfig
|
||||||
|
closed chan struct{}
|
||||||
|
}
|
||||||
|
|
||||||
|
type handshakeConfig struct {
|
||||||
|
localPSKCallback PSKCallback
|
||||||
|
localPSKIdentityHint []byte
|
||||||
|
localCipherSuites []CipherSuite // Available CipherSuites
|
||||||
|
localSignatureSchemes []signaturehash.Algorithm // Available signature schemes
|
||||||
|
localCertSignatureSchemes []signaturehash.Algorithm // Available signature schemes for certificates
|
||||||
|
extendedMasterSecret ExtendedMasterSecretType // Policy for the Extended Master Support extension
|
||||||
|
localSRTPProtectionProfiles []SRTPProtectionProfile // Available SRTPProtectionProfiles, if empty no SRTP support
|
||||||
|
localSRTPMasterKeyIdentifier []byte
|
||||||
|
serverName string
|
||||||
|
supportedProtocols []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
|
||||||
|
verifyConnection func(*State) error
|
||||||
|
sessionStore SessionStore
|
||||||
|
rootCAs *x509.CertPool
|
||||||
|
clientCAs *x509.CertPool
|
||||||
|
initialRetransmitInterval time.Duration
|
||||||
|
disableRetransmitBackoff bool
|
||||||
|
customCipherSuites func() []CipherSuite
|
||||||
|
ellipticCurves []elliptic.Curve
|
||||||
|
insecureSkipHelloVerify bool
|
||||||
|
connectionIDGenerator func() []byte
|
||||||
|
helloRandomBytesGenerator func() [handshake.RandomBytesLength]byte
|
||||||
|
|
||||||
|
onFlightState func(flightVal, handshakeState)
|
||||||
|
log logging.LeveledLogger
|
||||||
|
keyLogWriter io.Writer
|
||||||
|
|
||||||
|
localGetCertificate func(*ClientHelloInfo) (*tls.Certificate, error)
|
||||||
|
localGetClientCertificate func(*CertificateRequestInfo) (*tls.Certificate, error)
|
||||||
|
|
||||||
|
initialEpoch uint16
|
||||||
|
|
||||||
|
mu sync.Mutex
|
||||||
|
|
||||||
|
clientHelloMessageHook func(handshake.MessageClientHello) handshake.Message
|
||||||
|
serverHelloMessageHook func(handshake.MessageServerHello) handshake.Message
|
||||||
|
certificateRequestMessageHook func(handshake.MessageCertificateRequest) handshake.Message
|
||||||
|
|
||||||
|
resumeState *State
|
||||||
|
}
|
||||||
|
|
||||||
|
type flightConn interface {
|
||||||
|
notify(ctx context.Context, level alert.Level, desc alert.Description) error
|
||||||
|
writePackets(context.Context, []*packet) error
|
||||||
|
recvHandshake() <-chan recvHandshakeState
|
||||||
|
setLocalEpoch(epoch uint16)
|
||||||
|
handleQueuedPackets(context.Context) error
|
||||||
|
sessionKey() []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *handshakeConfig) writeKeyLog(label string, clientRandom, secret []byte) {
|
||||||
|
if c.keyLogWriter == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
c.mu.Lock()
|
||||||
|
defer c.mu.Unlock()
|
||||||
|
_, err := fmt.Fprintf(c.keyLogWriter, "%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,
|
||||||
|
retransmitInterval: cfg.initialRetransmitInterval,
|
||||||
|
closed: make(chan struct{}),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *handshakeFSM) Run(ctx context.Context, conn 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, conn)
|
||||||
|
case handshakeSending:
|
||||||
|
state, err = s.send(ctx, conn)
|
||||||
|
case handshakeWaiting:
|
||||||
|
state, err = s.wait(ctx, conn)
|
||||||
|
case handshakeFinished:
|
||||||
|
state, err = s.finish(ctx, conn)
|
||||||
|
default:
|
||||||
|
return errInvalidFSMTransition
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *handshakeFSM) Done() <-chan struct{} {
|
||||||
|
return s.closed
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *handshakeFSM) prepare(ctx context.Context, conn flightConn) (handshakeState, error) {
|
||||||
|
s.flights = nil
|
||||||
|
// Prepare flights
|
||||||
|
var (
|
||||||
|
dtlsAlert *alert.Alert
|
||||||
|
err error
|
||||||
|
pkts []*packet
|
||||||
|
)
|
||||||
|
gen, retransmit, errFlight := s.currentFlight.getFlightGenerator()
|
||||||
|
if errFlight != nil {
|
||||||
|
err = errFlight
|
||||||
|
dtlsAlert = &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}
|
||||||
|
} else {
|
||||||
|
pkts, dtlsAlert, err = gen(conn, s.state, s.cache, s.cfg)
|
||||||
|
s.retransmit = retransmit
|
||||||
|
}
|
||||||
|
if dtlsAlert != nil {
|
||||||
|
if alertErr := conn.notify(ctx, dtlsAlert.Level, dtlsAlert.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) //nolint:gosec // G115
|
||||||
|
s.state.handshakeSendSequence++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if epoch != nextEpoch {
|
||||||
|
s.cfg.log.Tracef("[handshake:%s] -> changeCipherSpec (epoch: %d)", srvCliStr(s.state.isClient), nextEpoch)
|
||||||
|
conn.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, conn flightConn) (handshakeState, error) { //nolint:gocognit,cyclop
|
||||||
|
parse, errFlight := s.currentFlight.getFlightParser()
|
||||||
|
if errFlight != nil {
|
||||||
|
if alertErr := conn.notify(ctx, alert.Fatal, alert.InternalError); alertErr != nil {
|
||||||
|
return handshakeErrored, alertErr
|
||||||
|
}
|
||||||
|
|
||||||
|
return handshakeErrored, errFlight
|
||||||
|
}
|
||||||
|
|
||||||
|
retransmitTimer := time.NewTimer(s.retransmitInterval)
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case state := <-conn.recvHandshake():
|
||||||
|
if state.isRetransmit {
|
||||||
|
close(state.done)
|
||||||
|
// ignore incoming retransmit hints, only rely on the timer-driven path below
|
||||||
|
// https://github.com/pion/dtls/issues/758
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
nextFlight, alert, err := parse(ctx, conn, s.state, s.cache, s.cfg)
|
||||||
|
s.retransmitInterval = s.cfg.initialRetransmitInterval
|
||||||
|
close(state.done)
|
||||||
|
if alert != nil {
|
||||||
|
if alertErr := conn.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
|
||||||
|
}
|
||||||
|
|
||||||
|
// RFC 4347 4.2.4.1:
|
||||||
|
// Implementations SHOULD use an initial timer value of 1 second (the minimum defined in RFC 2988 [RFC2988])
|
||||||
|
// and double the value at each retransmission, up to no less than the RFC 2988 maximum of 60 seconds.
|
||||||
|
if !s.cfg.disableRetransmitBackoff {
|
||||||
|
s.retransmitInterval *= 2
|
||||||
|
}
|
||||||
|
if s.retransmitInterval > time.Second*60 {
|
||||||
|
s.retransmitInterval = time.Second * 60
|
||||||
|
}
|
||||||
|
|
||||||
|
return handshakeSending, nil
|
||||||
|
case <-ctx.Done():
|
||||||
|
s.retransmitInterval = s.cfg.initialRetransmitInterval
|
||||||
|
|
||||||
|
return handshakeErrored, ctx.Err()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *handshakeFSM) finish(ctx context.Context, c flightConn) (handshakeState, error) {
|
||||||
|
select {
|
||||||
|
case state := <-c.recvHandshake():
|
||||||
|
close(state.done)
|
||||||
|
if s.state.isClient {
|
||||||
|
return handshakeFinished, nil
|
||||||
|
} else {
|
||||||
|
return handshakeSending, nil
|
||||||
|
}
|
||||||
|
case <-ctx.Done():
|
||||||
|
return handshakeErrored, ctx.Err()
|
||||||
|
}
|
||||||
|
}
|
||||||
41
vendor/github.com/pion/dtls/v3/internal/ciphersuite/aes_128_ccm.go
generated
vendored
Normal file
41
vendor/github.com/pion/dtls/v3/internal/ciphersuite/aes_128_ccm.go
generated
vendored
Normal file
|
|
@ -0,0 +1,41 @@
|
||||||
|
// SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package ciphersuite
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/pion/dtls/v3/pkg/crypto/ciphersuite"
|
||||||
|
"github.com/pion/dtls/v3/pkg/crypto/clientcertificate"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Aes128Ccm is a base class used by multiple AES-CCM Ciphers.
|
||||||
|
type Aes128Ccm struct {
|
||||||
|
AesCcm
|
||||||
|
}
|
||||||
|
|
||||||
|
func newAes128Ccm(
|
||||||
|
clientCertificateType clientcertificate.Type,
|
||||||
|
id ID,
|
||||||
|
psk bool,
|
||||||
|
cryptoCCMTagLen ciphersuite.CCMTagLen,
|
||||||
|
keyExchangeAlgorithm KeyExchangeAlgorithm,
|
||||||
|
ecc bool,
|
||||||
|
) *Aes128Ccm {
|
||||||
|
return &Aes128Ccm{
|
||||||
|
AesCcm: AesCcm{
|
||||||
|
clientCertificateType: clientCertificateType,
|
||||||
|
id: id,
|
||||||
|
psk: psk,
|
||||||
|
cryptoCCMTagLen: cryptoCCMTagLen,
|
||||||
|
keyExchangeAlgorithm: keyExchangeAlgorithm,
|
||||||
|
ecc: ecc,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Init initializes the internal Cipher with keying material.
|
||||||
|
func (c *Aes128Ccm) Init(masterSecret, clientRandom, serverRandom []byte, isClient bool) error {
|
||||||
|
const prfKeyLen = 16
|
||||||
|
|
||||||
|
return c.AesCcm.Init(masterSecret, clientRandom, serverRandom, isClient, prfKeyLen)
|
||||||
|
}
|
||||||
41
vendor/github.com/pion/dtls/v3/internal/ciphersuite/aes_256_ccm.go
generated
vendored
Normal file
41
vendor/github.com/pion/dtls/v3/internal/ciphersuite/aes_256_ccm.go
generated
vendored
Normal file
|
|
@ -0,0 +1,41 @@
|
||||||
|
// SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package ciphersuite
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/pion/dtls/v3/pkg/crypto/ciphersuite"
|
||||||
|
"github.com/pion/dtls/v3/pkg/crypto/clientcertificate"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Aes256Ccm is a base class used by multiple AES-CCM Ciphers.
|
||||||
|
type Aes256Ccm struct {
|
||||||
|
AesCcm
|
||||||
|
}
|
||||||
|
|
||||||
|
func newAes256Ccm(
|
||||||
|
clientCertificateType clientcertificate.Type,
|
||||||
|
id ID,
|
||||||
|
psk bool,
|
||||||
|
cryptoCCMTagLen ciphersuite.CCMTagLen,
|
||||||
|
keyExchangeAlgorithm KeyExchangeAlgorithm,
|
||||||
|
ecc bool,
|
||||||
|
) *Aes256Ccm {
|
||||||
|
return &Aes256Ccm{
|
||||||
|
AesCcm: AesCcm{
|
||||||
|
clientCertificateType: clientCertificateType,
|
||||||
|
id: id,
|
||||||
|
psk: psk,
|
||||||
|
cryptoCCMTagLen: cryptoCCMTagLen,
|
||||||
|
keyExchangeAlgorithm: keyExchangeAlgorithm,
|
||||||
|
ecc: ecc,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Init initializes the internal Cipher with keying material.
|
||||||
|
func (c *Aes256Ccm) Init(masterSecret, clientRandom, serverRandom []byte, isClient bool) error {
|
||||||
|
const prfKeyLen = 32
|
||||||
|
|
||||||
|
return c.AesCcm.Init(masterSecret, clientRandom, serverRandom, isClient, prfKeyLen)
|
||||||
|
}
|
||||||
120
vendor/github.com/pion/dtls/v3/internal/ciphersuite/aes_ccm.go
generated
vendored
Normal file
120
vendor/github.com/pion/dtls/v3/internal/ciphersuite/aes_ccm.go
generated
vendored
Normal file
|
|
@ -0,0 +1,120 @@
|
||||||
|
// SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package ciphersuite
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/sha256"
|
||||||
|
"fmt"
|
||||||
|
"hash"
|
||||||
|
"sync/atomic"
|
||||||
|
|
||||||
|
"github.com/pion/dtls/v3/pkg/crypto/ciphersuite"
|
||||||
|
"github.com/pion/dtls/v3/pkg/crypto/clientcertificate"
|
||||||
|
"github.com/pion/dtls/v3/pkg/crypto/prf"
|
||||||
|
"github.com/pion/dtls/v3/pkg/protocol/recordlayer"
|
||||||
|
)
|
||||||
|
|
||||||
|
// AesCcm is a base class used by multiple AES-CCM Ciphers.
|
||||||
|
type AesCcm struct {
|
||||||
|
ccm atomic.Value // *cryptoCCM
|
||||||
|
clientCertificateType clientcertificate.Type
|
||||||
|
id ID
|
||||||
|
psk bool
|
||||||
|
keyExchangeAlgorithm KeyExchangeAlgorithm
|
||||||
|
cryptoCCMTagLen ciphersuite.CCMTagLen
|
||||||
|
ecc bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// CertificateType returns what type of certificate this CipherSuite exchanges.
|
||||||
|
func (c *AesCcm) CertificateType() clientcertificate.Type {
|
||||||
|
return c.clientCertificateType
|
||||||
|
}
|
||||||
|
|
||||||
|
// ID returns the ID of the CipherSuite.
|
||||||
|
func (c *AesCcm) ID() ID {
|
||||||
|
return c.id
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *AesCcm) String() string {
|
||||||
|
return c.id.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
// ECC uses Elliptic Curve Cryptography.
|
||||||
|
func (c *AesCcm) ECC() bool {
|
||||||
|
return c.ecc
|
||||||
|
}
|
||||||
|
|
||||||
|
// KeyExchangeAlgorithm controls what key exchange algorithm is using during the handshake.
|
||||||
|
func (c *AesCcm) KeyExchangeAlgorithm() KeyExchangeAlgorithm {
|
||||||
|
return c.keyExchangeAlgorithm
|
||||||
|
}
|
||||||
|
|
||||||
|
// HashFunc returns the hashing func for this CipherSuite.
|
||||||
|
func (c *AesCcm) HashFunc() func() hash.Hash {
|
||||||
|
return sha256.New
|
||||||
|
}
|
||||||
|
|
||||||
|
// AuthenticationType controls what authentication method is using during the handshake.
|
||||||
|
func (c *AesCcm) AuthenticationType() AuthenticationType {
|
||||||
|
if c.psk {
|
||||||
|
return AuthenticationTypePreSharedKey
|
||||||
|
}
|
||||||
|
|
||||||
|
return AuthenticationTypeCertificate
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsInitialized returns if the CipherSuite has keying material and can
|
||||||
|
// encrypt/decrypt packets.
|
||||||
|
func (c *AesCcm) IsInitialized() bool {
|
||||||
|
return c.ccm.Load() != nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Init initializes the internal Cipher with keying material.
|
||||||
|
func (c *AesCcm) Init(masterSecret, clientRandom, serverRandom []byte, isClient bool, prfKeyLen int) error {
|
||||||
|
const (
|
||||||
|
prfMacLen = 0
|
||||||
|
prfIvLen = 4
|
||||||
|
)
|
||||||
|
|
||||||
|
keys, err := prf.GenerateEncryptionKeys(
|
||||||
|
masterSecret, clientRandom, serverRandom, prfMacLen, prfKeyLen, prfIvLen, c.HashFunc(),
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
var ccm *ciphersuite.CCM
|
||||||
|
if isClient {
|
||||||
|
ccm, err = ciphersuite.NewCCM(
|
||||||
|
c.cryptoCCMTagLen, keys.ClientWriteKey, keys.ClientWriteIV, keys.ServerWriteKey, keys.ServerWriteIV,
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
ccm, err = ciphersuite.NewCCM(
|
||||||
|
c.cryptoCCMTagLen, keys.ServerWriteKey, keys.ServerWriteIV, keys.ClientWriteKey, keys.ClientWriteIV,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
c.ccm.Store(ccm)
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Encrypt encrypts a single TLS RecordLayer.
|
||||||
|
func (c *AesCcm) Encrypt(pkt *recordlayer.RecordLayer, raw []byte) ([]byte, error) {
|
||||||
|
cipherSuite, ok := c.ccm.Load().(*ciphersuite.CCM)
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf("%w, unable to encrypt", errCipherSuiteNotInit)
|
||||||
|
}
|
||||||
|
|
||||||
|
return cipherSuite.Encrypt(pkt, raw)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Decrypt decrypts a single TLS RecordLayer.
|
||||||
|
func (c *AesCcm) Decrypt(h recordlayer.Header, raw []byte) ([]byte, error) {
|
||||||
|
cipherSuite, ok := c.ccm.Load().(*ciphersuite.CCM)
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf("%w, unable to decrypt", errCipherSuiteNotInit)
|
||||||
|
}
|
||||||
|
|
||||||
|
return cipherSuite.Decrypt(h, raw)
|
||||||
|
}
|
||||||
100
vendor/github.com/pion/dtls/v3/internal/ciphersuite/ciphersuite.go
generated
vendored
Normal file
100
vendor/github.com/pion/dtls/v3/internal/ciphersuite/ciphersuite.go
generated
vendored
Normal file
|
|
@ -0,0 +1,100 @@
|
||||||
|
// SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
// Package ciphersuite provides TLS Ciphers as registered with the IANA
|
||||||
|
// https://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml#tls-parameters-4
|
||||||
|
package ciphersuite
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/pion/dtls/v3/internal/ciphersuite/types"
|
||||||
|
"github.com/pion/dtls/v3/pkg/protocol"
|
||||||
|
)
|
||||||
|
|
||||||
|
//nolint:err113
|
||||||
|
var errCipherSuiteNotInit = &protocol.TemporaryError{Err: errors.New("CipherSuite has not been initialized")}
|
||||||
|
|
||||||
|
// ID is an ID for our supported CipherSuites.
|
||||||
|
type ID uint16
|
||||||
|
|
||||||
|
func (i ID) String() string { //nolint:cyclop
|
||||||
|
switch i {
|
||||||
|
case TLS_ECDHE_ECDSA_WITH_AES_128_CCM:
|
||||||
|
return "TLS_ECDHE_ECDSA_WITH_AES_128_CCM"
|
||||||
|
case TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8:
|
||||||
|
return "TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8"
|
||||||
|
case TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256:
|
||||||
|
return "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256"
|
||||||
|
case TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256:
|
||||||
|
return "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256"
|
||||||
|
case TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA:
|
||||||
|
return "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA"
|
||||||
|
case TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA:
|
||||||
|
return "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA"
|
||||||
|
case TLS_PSK_WITH_AES_128_CCM:
|
||||||
|
return "TLS_PSK_WITH_AES_128_CCM"
|
||||||
|
case TLS_PSK_WITH_AES_128_CCM_8:
|
||||||
|
return "TLS_PSK_WITH_AES_128_CCM_8"
|
||||||
|
case TLS_PSK_WITH_AES_256_CCM_8:
|
||||||
|
return "TLS_PSK_WITH_AES_256_CCM_8"
|
||||||
|
case TLS_PSK_WITH_AES_128_GCM_SHA256:
|
||||||
|
return "TLS_PSK_WITH_AES_128_GCM_SHA256"
|
||||||
|
case TLS_PSK_WITH_AES_128_CBC_SHA256:
|
||||||
|
return "TLS_PSK_WITH_AES_128_CBC_SHA256"
|
||||||
|
case TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384:
|
||||||
|
return "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384"
|
||||||
|
case TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384:
|
||||||
|
return "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384"
|
||||||
|
case TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256:
|
||||||
|
return "TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256"
|
||||||
|
default:
|
||||||
|
return fmt.Sprintf("unknown(%v)", uint16(i))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Supported Cipher Suites.
|
||||||
|
const (
|
||||||
|
// AES-128-CCM.
|
||||||
|
TLS_ECDHE_ECDSA_WITH_AES_128_CCM ID = 0xc0ac // nolint: revive,staticcheck
|
||||||
|
TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8 ID = 0xc0ae // nolint: revive,staticcheck
|
||||||
|
|
||||||
|
// AES-128-GCM-SHA256.
|
||||||
|
TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 ID = 0xc02b // nolint: revive,staticcheck
|
||||||
|
TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 ID = 0xc02f // nolint: revive,staticcheck
|
||||||
|
|
||||||
|
TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 ID = 0xc02c // nolint: revive,staticcheck
|
||||||
|
TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 ID = 0xc030 // nolint: revive,staticcheck
|
||||||
|
// AES-256-CBC-SHA.
|
||||||
|
TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA ID = 0xc00a // nolint: revive,staticcheck
|
||||||
|
TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA ID = 0xc014 // nolint: revive,staticcheck
|
||||||
|
|
||||||
|
TLS_PSK_WITH_AES_128_CCM ID = 0xc0a4 // nolint: revive,staticcheck
|
||||||
|
TLS_PSK_WITH_AES_128_CCM_8 ID = 0xc0a8 // nolint: revive,staticcheck
|
||||||
|
TLS_PSK_WITH_AES_256_CCM_8 ID = 0xc0a9 // nolint: revive,staticcheck
|
||||||
|
TLS_PSK_WITH_AES_128_GCM_SHA256 ID = 0x00a8 // nolint: revive,staticcheck
|
||||||
|
TLS_PSK_WITH_AES_128_CBC_SHA256 ID = 0x00ae // nolint: revive,staticcheck
|
||||||
|
|
||||||
|
TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256 ID = 0xC037 // nolint: revive,staticcheck
|
||||||
|
)
|
||||||
|
|
||||||
|
// AuthenticationType controls what authentication method is using during the handshake.
|
||||||
|
type AuthenticationType = types.AuthenticationType
|
||||||
|
|
||||||
|
// AuthenticationType Enums.
|
||||||
|
const (
|
||||||
|
AuthenticationTypeCertificate AuthenticationType = types.AuthenticationTypeCertificate
|
||||||
|
AuthenticationTypePreSharedKey AuthenticationType = types.AuthenticationTypePreSharedKey
|
||||||
|
AuthenticationTypeAnonymous AuthenticationType = types.AuthenticationTypeAnonymous
|
||||||
|
)
|
||||||
|
|
||||||
|
// KeyExchangeAlgorithm controls what exchange algorithm was chosen.
|
||||||
|
type KeyExchangeAlgorithm = types.KeyExchangeAlgorithm
|
||||||
|
|
||||||
|
// KeyExchangeAlgorithm Bitmask.
|
||||||
|
const (
|
||||||
|
KeyExchangeAlgorithmNone KeyExchangeAlgorithm = types.KeyExchangeAlgorithmNone
|
||||||
|
KeyExchangeAlgorithmPsk KeyExchangeAlgorithm = types.KeyExchangeAlgorithmPsk
|
||||||
|
KeyExchangeAlgorithmEcdhe KeyExchangeAlgorithm = types.KeyExchangeAlgorithmEcdhe
|
||||||
|
)
|
||||||
21
vendor/github.com/pion/dtls/v3/internal/ciphersuite/tls_ecdhe_ecdsa_with_aes_128_ccm.go
generated
vendored
Normal file
21
vendor/github.com/pion/dtls/v3/internal/ciphersuite/tls_ecdhe_ecdsa_with_aes_128_ccm.go
generated
vendored
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
// SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package ciphersuite
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/pion/dtls/v3/pkg/crypto/ciphersuite"
|
||||||
|
"github.com/pion/dtls/v3/pkg/crypto/clientcertificate"
|
||||||
|
)
|
||||||
|
|
||||||
|
// NewTLSEcdheEcdsaWithAes128Ccm constructs a TLS_ECDHE_ECDSA_WITH_AES_128_CCM Cipher.
|
||||||
|
func NewTLSEcdheEcdsaWithAes128Ccm() *Aes128Ccm {
|
||||||
|
return newAes128Ccm(
|
||||||
|
clientcertificate.ECDSASign,
|
||||||
|
TLS_ECDHE_ECDSA_WITH_AES_128_CCM,
|
||||||
|
false,
|
||||||
|
ciphersuite.CCMTagLength,
|
||||||
|
KeyExchangeAlgorithmEcdhe,
|
||||||
|
true,
|
||||||
|
)
|
||||||
|
}
|
||||||
21
vendor/github.com/pion/dtls/v3/internal/ciphersuite/tls_ecdhe_ecdsa_with_aes_128_ccm8.go
generated
vendored
Normal file
21
vendor/github.com/pion/dtls/v3/internal/ciphersuite/tls_ecdhe_ecdsa_with_aes_128_ccm8.go
generated
vendored
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
// SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package ciphersuite
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/pion/dtls/v3/pkg/crypto/ciphersuite"
|
||||||
|
"github.com/pion/dtls/v3/pkg/crypto/clientcertificate"
|
||||||
|
)
|
||||||
|
|
||||||
|
// NewTLSEcdheEcdsaWithAes128Ccm8 creates a new TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8 CipherSuite.
|
||||||
|
func NewTLSEcdheEcdsaWithAes128Ccm8() *Aes128Ccm {
|
||||||
|
return newAes128Ccm(
|
||||||
|
clientcertificate.ECDSASign,
|
||||||
|
TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8,
|
||||||
|
false,
|
||||||
|
ciphersuite.CCMTagLength8,
|
||||||
|
KeyExchangeAlgorithmEcdhe,
|
||||||
|
true,
|
||||||
|
)
|
||||||
|
}
|
||||||
116
vendor/github.com/pion/dtls/v3/internal/ciphersuite/tls_ecdhe_ecdsa_with_aes_128_gcm_sha256.go
generated
vendored
Normal file
116
vendor/github.com/pion/dtls/v3/internal/ciphersuite/tls_ecdhe_ecdsa_with_aes_128_gcm_sha256.go
generated
vendored
Normal file
|
|
@ -0,0 +1,116 @@
|
||||||
|
// SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package ciphersuite
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/sha256"
|
||||||
|
"fmt"
|
||||||
|
"hash"
|
||||||
|
"sync/atomic"
|
||||||
|
|
||||||
|
"github.com/pion/dtls/v3/pkg/crypto/ciphersuite"
|
||||||
|
"github.com/pion/dtls/v3/pkg/crypto/clientcertificate"
|
||||||
|
"github.com/pion/dtls/v3/pkg/crypto/prf"
|
||||||
|
"github.com/pion/dtls/v3/pkg/protocol/recordlayer"
|
||||||
|
)
|
||||||
|
|
||||||
|
// TLSEcdheEcdsaWithAes128GcmSha256 represents a TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 CipherSuite.
|
||||||
|
type TLSEcdheEcdsaWithAes128GcmSha256 struct {
|
||||||
|
gcm atomic.Value // *cryptoGCM
|
||||||
|
}
|
||||||
|
|
||||||
|
// CertificateType returns what type of certficate this CipherSuite exchanges.
|
||||||
|
func (c *TLSEcdheEcdsaWithAes128GcmSha256) CertificateType() clientcertificate.Type {
|
||||||
|
return clientcertificate.ECDSASign
|
||||||
|
}
|
||||||
|
|
||||||
|
// KeyExchangeAlgorithm controls what key exchange algorithm is using during the handshake.
|
||||||
|
func (c *TLSEcdheEcdsaWithAes128GcmSha256) KeyExchangeAlgorithm() KeyExchangeAlgorithm {
|
||||||
|
return KeyExchangeAlgorithmEcdhe
|
||||||
|
}
|
||||||
|
|
||||||
|
// ECC uses Elliptic Curve Cryptography.
|
||||||
|
func (c *TLSEcdheEcdsaWithAes128GcmSha256) ECC() bool {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// ID returns the ID of the CipherSuite.
|
||||||
|
func (c *TLSEcdheEcdsaWithAes128GcmSha256) ID() ID {
|
||||||
|
return TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *TLSEcdheEcdsaWithAes128GcmSha256) String() string {
|
||||||
|
return "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256"
|
||||||
|
}
|
||||||
|
|
||||||
|
// HashFunc returns the hashing func for this CipherSuite.
|
||||||
|
func (c *TLSEcdheEcdsaWithAes128GcmSha256) HashFunc() func() hash.Hash {
|
||||||
|
return sha256.New
|
||||||
|
}
|
||||||
|
|
||||||
|
// AuthenticationType controls what authentication method is using during the handshake.
|
||||||
|
func (c *TLSEcdheEcdsaWithAes128GcmSha256) AuthenticationType() AuthenticationType {
|
||||||
|
return AuthenticationTypeCertificate
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsInitialized returns if the CipherSuite has keying material and can
|
||||||
|
// encrypt/decrypt packets.
|
||||||
|
func (c *TLSEcdheEcdsaWithAes128GcmSha256) IsInitialized() bool {
|
||||||
|
return c.gcm.Load() != nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *TLSEcdheEcdsaWithAes128GcmSha256) init(
|
||||||
|
masterSecret, clientRandom, serverRandom []byte,
|
||||||
|
isClient bool,
|
||||||
|
prfMacLen, prfKeyLen, prfIvLen int,
|
||||||
|
hashFunc func() hash.Hash,
|
||||||
|
) error {
|
||||||
|
keys, err := prf.GenerateEncryptionKeys(
|
||||||
|
masterSecret, clientRandom, serverRandom, prfMacLen, prfKeyLen, prfIvLen, hashFunc,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
var gcm *ciphersuite.GCM
|
||||||
|
if isClient {
|
||||||
|
gcm, err = ciphersuite.NewGCM(keys.ClientWriteKey, keys.ClientWriteIV, keys.ServerWriteKey, keys.ServerWriteIV)
|
||||||
|
} else {
|
||||||
|
gcm, err = ciphersuite.NewGCM(keys.ServerWriteKey, keys.ServerWriteIV, keys.ClientWriteKey, keys.ClientWriteIV)
|
||||||
|
}
|
||||||
|
c.gcm.Store(gcm)
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Init initializes the internal Cipher with keying material.
|
||||||
|
func (c *TLSEcdheEcdsaWithAes128GcmSha256) Init(masterSecret, clientRandom, serverRandom []byte, isClient bool) error {
|
||||||
|
const (
|
||||||
|
prfMacLen = 0
|
||||||
|
prfKeyLen = 16
|
||||||
|
prfIvLen = 4
|
||||||
|
)
|
||||||
|
|
||||||
|
return c.init(masterSecret, clientRandom, serverRandom, isClient, prfMacLen, prfKeyLen, prfIvLen, c.HashFunc())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Encrypt encrypts a single TLS RecordLayer.
|
||||||
|
func (c *TLSEcdheEcdsaWithAes128GcmSha256) Encrypt(pkt *recordlayer.RecordLayer, raw []byte) ([]byte, error) {
|
||||||
|
cipherSuite, ok := c.gcm.Load().(*ciphersuite.GCM)
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf("%w, unable to encrypt", errCipherSuiteNotInit)
|
||||||
|
}
|
||||||
|
|
||||||
|
return cipherSuite.Encrypt(pkt, raw)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Decrypt decrypts a single TLS RecordLayer.
|
||||||
|
func (c *TLSEcdheEcdsaWithAes128GcmSha256) Decrypt(h recordlayer.Header, raw []byte) ([]byte, error) {
|
||||||
|
cipherSuite, ok := c.gcm.Load().(*ciphersuite.GCM)
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf("%w, unable to decrypt", errCipherSuiteNotInit)
|
||||||
|
}
|
||||||
|
|
||||||
|
return cipherSuite.Decrypt(h, raw)
|
||||||
|
}
|
||||||
116
vendor/github.com/pion/dtls/v3/internal/ciphersuite/tls_ecdhe_ecdsa_with_aes_256_cbc_sha.go
generated
vendored
Normal file
116
vendor/github.com/pion/dtls/v3/internal/ciphersuite/tls_ecdhe_ecdsa_with_aes_256_cbc_sha.go
generated
vendored
Normal file
|
|
@ -0,0 +1,116 @@
|
||||||
|
// SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package ciphersuite
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/sha1" //nolint: gosec,gci
|
||||||
|
"crypto/sha256"
|
||||||
|
"fmt"
|
||||||
|
"hash"
|
||||||
|
"sync/atomic"
|
||||||
|
|
||||||
|
"github.com/pion/dtls/v3/pkg/crypto/ciphersuite"
|
||||||
|
"github.com/pion/dtls/v3/pkg/crypto/clientcertificate"
|
||||||
|
"github.com/pion/dtls/v3/pkg/crypto/prf"
|
||||||
|
"github.com/pion/dtls/v3/pkg/protocol/recordlayer"
|
||||||
|
)
|
||||||
|
|
||||||
|
// TLSEcdheEcdsaWithAes256CbcSha represents a TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA CipherSuite.
|
||||||
|
type TLSEcdheEcdsaWithAes256CbcSha struct {
|
||||||
|
cbc atomic.Value // *cryptoCBC
|
||||||
|
}
|
||||||
|
|
||||||
|
// CertificateType returns what type of certficate this CipherSuite exchanges.
|
||||||
|
func (c *TLSEcdheEcdsaWithAes256CbcSha) CertificateType() clientcertificate.Type {
|
||||||
|
return clientcertificate.ECDSASign
|
||||||
|
}
|
||||||
|
|
||||||
|
// KeyExchangeAlgorithm controls what key exchange algorithm is using during the handshake.
|
||||||
|
func (c *TLSEcdheEcdsaWithAes256CbcSha) KeyExchangeAlgorithm() KeyExchangeAlgorithm {
|
||||||
|
return KeyExchangeAlgorithmEcdhe
|
||||||
|
}
|
||||||
|
|
||||||
|
// ECC uses Elliptic Curve Cryptography.
|
||||||
|
func (c *TLSEcdheEcdsaWithAes256CbcSha) ECC() bool {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// ID returns the ID of the CipherSuite.
|
||||||
|
func (c *TLSEcdheEcdsaWithAes256CbcSha) ID() ID {
|
||||||
|
return TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *TLSEcdheEcdsaWithAes256CbcSha) String() string {
|
||||||
|
return "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA"
|
||||||
|
}
|
||||||
|
|
||||||
|
// HashFunc returns the hashing func for this CipherSuite.
|
||||||
|
func (c *TLSEcdheEcdsaWithAes256CbcSha) HashFunc() func() hash.Hash {
|
||||||
|
return sha256.New
|
||||||
|
}
|
||||||
|
|
||||||
|
// AuthenticationType controls what authentication method is using during the handshake.
|
||||||
|
func (c *TLSEcdheEcdsaWithAes256CbcSha) AuthenticationType() AuthenticationType {
|
||||||
|
return AuthenticationTypeCertificate
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsInitialized returns if the CipherSuite has keying material and can
|
||||||
|
// encrypt/decrypt packets.
|
||||||
|
func (c *TLSEcdheEcdsaWithAes256CbcSha) IsInitialized() bool {
|
||||||
|
return c.cbc.Load() != nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Init initializes the internal Cipher with keying material.
|
||||||
|
func (c *TLSEcdheEcdsaWithAes256CbcSha) Init(masterSecret, clientRandom, serverRandom []byte, isClient bool) error {
|
||||||
|
const (
|
||||||
|
prfMacLen = 20
|
||||||
|
prfKeyLen = 32
|
||||||
|
prfIvLen = 16
|
||||||
|
)
|
||||||
|
|
||||||
|
keys, err := prf.GenerateEncryptionKeys(
|
||||||
|
masterSecret, clientRandom, serverRandom, prfMacLen, prfKeyLen, prfIvLen, c.HashFunc(),
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
var cbc *ciphersuite.CBC
|
||||||
|
if isClient {
|
||||||
|
cbc, err = ciphersuite.NewCBC(
|
||||||
|
keys.ClientWriteKey, keys.ClientWriteIV, keys.ClientMACKey,
|
||||||
|
keys.ServerWriteKey, keys.ServerWriteIV, keys.ServerMACKey,
|
||||||
|
sha1.New,
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
cbc, err = ciphersuite.NewCBC(
|
||||||
|
keys.ServerWriteKey, keys.ServerWriteIV, keys.ServerMACKey,
|
||||||
|
keys.ClientWriteKey, keys.ClientWriteIV, keys.ClientMACKey,
|
||||||
|
sha1.New,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
c.cbc.Store(cbc)
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Encrypt encrypts a single TLS RecordLayer.
|
||||||
|
func (c *TLSEcdheEcdsaWithAes256CbcSha) Encrypt(pkt *recordlayer.RecordLayer, raw []byte) ([]byte, error) {
|
||||||
|
cipherSuite, ok := c.cbc.Load().(*ciphersuite.CBC)
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf("%w, unable to encrypt", errCipherSuiteNotInit)
|
||||||
|
}
|
||||||
|
|
||||||
|
return cipherSuite.Encrypt(pkt, raw)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Decrypt decrypts a single TLS RecordLayer.
|
||||||
|
func (c *TLSEcdheEcdsaWithAes256CbcSha) Decrypt(h recordlayer.Header, raw []byte) ([]byte, error) {
|
||||||
|
cipherSuite, ok := c.cbc.Load().(*ciphersuite.CBC)
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf("%w, unable to decrypt", errCipherSuiteNotInit)
|
||||||
|
}
|
||||||
|
|
||||||
|
return cipherSuite.Decrypt(h, raw)
|
||||||
|
}
|
||||||
39
vendor/github.com/pion/dtls/v3/internal/ciphersuite/tls_ecdhe_ecdsa_with_aes_256_gcm_sha384.go
generated
vendored
Normal file
39
vendor/github.com/pion/dtls/v3/internal/ciphersuite/tls_ecdhe_ecdsa_with_aes_256_gcm_sha384.go
generated
vendored
Normal file
|
|
@ -0,0 +1,39 @@
|
||||||
|
// SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package ciphersuite
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/sha512"
|
||||||
|
"hash"
|
||||||
|
)
|
||||||
|
|
||||||
|
// TLSEcdheEcdsaWithAes256GcmSha384 represents a TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 CipherSuite.
|
||||||
|
type TLSEcdheEcdsaWithAes256GcmSha384 struct {
|
||||||
|
TLSEcdheEcdsaWithAes128GcmSha256
|
||||||
|
}
|
||||||
|
|
||||||
|
// ID returns the ID of the CipherSuite.
|
||||||
|
func (c *TLSEcdheEcdsaWithAes256GcmSha384) ID() ID {
|
||||||
|
return TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *TLSEcdheEcdsaWithAes256GcmSha384) String() string {
|
||||||
|
return "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384"
|
||||||
|
}
|
||||||
|
|
||||||
|
// HashFunc returns the hashing func for this CipherSuite.
|
||||||
|
func (c *TLSEcdheEcdsaWithAes256GcmSha384) HashFunc() func() hash.Hash {
|
||||||
|
return sha512.New384
|
||||||
|
}
|
||||||
|
|
||||||
|
// Init initializes the internal Cipher with keying material.
|
||||||
|
func (c *TLSEcdheEcdsaWithAes256GcmSha384) Init(masterSecret, clientRandom, serverRandom []byte, isClient bool) error {
|
||||||
|
const (
|
||||||
|
prfMacLen = 0
|
||||||
|
prfKeyLen = 32
|
||||||
|
prfIvLen = 4
|
||||||
|
)
|
||||||
|
|
||||||
|
return c.init(masterSecret, clientRandom, serverRandom, isClient, prfMacLen, prfKeyLen, prfIvLen, c.HashFunc())
|
||||||
|
}
|
||||||
120
vendor/github.com/pion/dtls/v3/internal/ciphersuite/tls_ecdhe_psk_with_aes_128_cbc_sha256.go
generated
vendored
Normal file
120
vendor/github.com/pion/dtls/v3/internal/ciphersuite/tls_ecdhe_psk_with_aes_128_cbc_sha256.go
generated
vendored
Normal file
|
|
@ -0,0 +1,120 @@
|
||||||
|
// SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package ciphersuite
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/sha256"
|
||||||
|
"fmt"
|
||||||
|
"hash"
|
||||||
|
"sync/atomic"
|
||||||
|
|
||||||
|
"github.com/pion/dtls/v3/pkg/crypto/ciphersuite"
|
||||||
|
"github.com/pion/dtls/v3/pkg/crypto/clientcertificate"
|
||||||
|
"github.com/pion/dtls/v3/pkg/crypto/prf"
|
||||||
|
"github.com/pion/dtls/v3/pkg/protocol/recordlayer"
|
||||||
|
)
|
||||||
|
|
||||||
|
// TLSEcdhePskWithAes128CbcSha256 implements the TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256 CipherSuite.
|
||||||
|
type TLSEcdhePskWithAes128CbcSha256 struct {
|
||||||
|
cbc atomic.Value // *cryptoCBC
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewTLSEcdhePskWithAes128CbcSha256 creates TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256 cipher.
|
||||||
|
func NewTLSEcdhePskWithAes128CbcSha256() *TLSEcdhePskWithAes128CbcSha256 {
|
||||||
|
return &TLSEcdhePskWithAes128CbcSha256{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// CertificateType returns what type of certificate this CipherSuite exchanges.
|
||||||
|
func (c *TLSEcdhePskWithAes128CbcSha256) CertificateType() clientcertificate.Type {
|
||||||
|
return clientcertificate.Type(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
// KeyExchangeAlgorithm controls what key exchange algorithm is using during the handshake.
|
||||||
|
func (c *TLSEcdhePskWithAes128CbcSha256) KeyExchangeAlgorithm() KeyExchangeAlgorithm {
|
||||||
|
return (KeyExchangeAlgorithmPsk | KeyExchangeAlgorithmEcdhe)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ECC uses Elliptic Curve Cryptography.
|
||||||
|
func (c *TLSEcdhePskWithAes128CbcSha256) ECC() bool {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// ID returns the ID of the CipherSuite.
|
||||||
|
func (c *TLSEcdhePskWithAes128CbcSha256) ID() ID {
|
||||||
|
return TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *TLSEcdhePskWithAes128CbcSha256) String() string {
|
||||||
|
return "TLS-ECDHE-PSK-WITH-AES-128-CBC-SHA256"
|
||||||
|
}
|
||||||
|
|
||||||
|
// HashFunc returns the hashing func for this CipherSuite.
|
||||||
|
func (c *TLSEcdhePskWithAes128CbcSha256) HashFunc() func() hash.Hash {
|
||||||
|
return sha256.New
|
||||||
|
}
|
||||||
|
|
||||||
|
// AuthenticationType controls what authentication method is using during the handshake.
|
||||||
|
func (c *TLSEcdhePskWithAes128CbcSha256) AuthenticationType() AuthenticationType {
|
||||||
|
return AuthenticationTypePreSharedKey
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsInitialized returns if the CipherSuite has keying material and can
|
||||||
|
// encrypt/decrypt packets.
|
||||||
|
func (c *TLSEcdhePskWithAes128CbcSha256) IsInitialized() bool {
|
||||||
|
return c.cbc.Load() != nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Init initializes the internal Cipher with keying material.
|
||||||
|
func (c *TLSEcdhePskWithAes128CbcSha256) Init(masterSecret, clientRandom, serverRandom []byte, isClient bool) error {
|
||||||
|
const (
|
||||||
|
prfMacLen = 32
|
||||||
|
prfKeyLen = 16
|
||||||
|
prfIvLen = 16
|
||||||
|
)
|
||||||
|
|
||||||
|
keys, err := prf.GenerateEncryptionKeys(
|
||||||
|
masterSecret, clientRandom, serverRandom, prfMacLen, prfKeyLen, prfIvLen, c.HashFunc(),
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
var cbc *ciphersuite.CBC
|
||||||
|
if isClient {
|
||||||
|
cbc, err = ciphersuite.NewCBC(
|
||||||
|
keys.ClientWriteKey, keys.ClientWriteIV, keys.ClientMACKey,
|
||||||
|
keys.ServerWriteKey, keys.ServerWriteIV, keys.ServerMACKey,
|
||||||
|
c.HashFunc(),
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
cbc, err = ciphersuite.NewCBC(
|
||||||
|
keys.ServerWriteKey, keys.ServerWriteIV, keys.ServerMACKey,
|
||||||
|
keys.ClientWriteKey, keys.ClientWriteIV, keys.ClientMACKey,
|
||||||
|
c.HashFunc(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
c.cbc.Store(cbc)
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Encrypt encrypts a single TLS RecordLayer.
|
||||||
|
func (c *TLSEcdhePskWithAes128CbcSha256) Encrypt(pkt *recordlayer.RecordLayer, raw []byte) ([]byte, error) {
|
||||||
|
cipherSuite, ok := c.cbc.Load().(*ciphersuite.CBC)
|
||||||
|
if !ok { // !c.isInitialized()
|
||||||
|
return nil, fmt.Errorf("%w, unable to encrypt", errCipherSuiteNotInit)
|
||||||
|
}
|
||||||
|
|
||||||
|
return cipherSuite.Encrypt(pkt, raw)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Decrypt decrypts a single TLS RecordLayer.
|
||||||
|
func (c *TLSEcdhePskWithAes128CbcSha256) Decrypt(h recordlayer.Header, raw []byte) ([]byte, error) {
|
||||||
|
cipherSuite, ok := c.cbc.Load().(*ciphersuite.CBC)
|
||||||
|
if !ok { // !c.isInitialized()
|
||||||
|
return nil, fmt.Errorf("%w, unable to decrypt", errCipherSuiteNotInit)
|
||||||
|
}
|
||||||
|
|
||||||
|
return cipherSuite.Decrypt(h, raw)
|
||||||
|
}
|
||||||
25
vendor/github.com/pion/dtls/v3/internal/ciphersuite/tls_ecdhe_rsa_with_aes_128_gcm_sha256.go
generated
vendored
Normal file
25
vendor/github.com/pion/dtls/v3/internal/ciphersuite/tls_ecdhe_rsa_with_aes_128_gcm_sha256.go
generated
vendored
Normal file
|
|
@ -0,0 +1,25 @@
|
||||||
|
// SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package ciphersuite
|
||||||
|
|
||||||
|
import "github.com/pion/dtls/v3/pkg/crypto/clientcertificate"
|
||||||
|
|
||||||
|
// TLSEcdheRsaWithAes128GcmSha256 implements the TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 CipherSuite.
|
||||||
|
type TLSEcdheRsaWithAes128GcmSha256 struct {
|
||||||
|
TLSEcdheEcdsaWithAes128GcmSha256
|
||||||
|
}
|
||||||
|
|
||||||
|
// CertificateType returns what type of certificate this CipherSuite exchanges.
|
||||||
|
func (c *TLSEcdheRsaWithAes128GcmSha256) CertificateType() clientcertificate.Type {
|
||||||
|
return clientcertificate.RSASign
|
||||||
|
}
|
||||||
|
|
||||||
|
// ID returns the ID of the CipherSuite.
|
||||||
|
func (c *TLSEcdheRsaWithAes128GcmSha256) ID() ID {
|
||||||
|
return TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *TLSEcdheRsaWithAes128GcmSha256) String() string {
|
||||||
|
return "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256"
|
||||||
|
}
|
||||||
25
vendor/github.com/pion/dtls/v3/internal/ciphersuite/tls_ecdhe_rsa_with_aes_256_cbc_sha.go
generated
vendored
Normal file
25
vendor/github.com/pion/dtls/v3/internal/ciphersuite/tls_ecdhe_rsa_with_aes_256_cbc_sha.go
generated
vendored
Normal file
|
|
@ -0,0 +1,25 @@
|
||||||
|
// SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package ciphersuite
|
||||||
|
|
||||||
|
import "github.com/pion/dtls/v3/pkg/crypto/clientcertificate"
|
||||||
|
|
||||||
|
// TLSEcdheRsaWithAes256CbcSha implements the TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA CipherSuite.
|
||||||
|
type TLSEcdheRsaWithAes256CbcSha struct {
|
||||||
|
TLSEcdheEcdsaWithAes256CbcSha
|
||||||
|
}
|
||||||
|
|
||||||
|
// CertificateType returns what type of certificate this CipherSuite exchanges.
|
||||||
|
func (c *TLSEcdheRsaWithAes256CbcSha) CertificateType() clientcertificate.Type {
|
||||||
|
return clientcertificate.RSASign
|
||||||
|
}
|
||||||
|
|
||||||
|
// ID returns the ID of the CipherSuite.
|
||||||
|
func (c *TLSEcdheRsaWithAes256CbcSha) ID() ID {
|
||||||
|
return TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *TLSEcdheRsaWithAes256CbcSha) String() string {
|
||||||
|
return "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA"
|
||||||
|
}
|
||||||
25
vendor/github.com/pion/dtls/v3/internal/ciphersuite/tls_ecdhe_rsa_with_aes_256_gcm_sha384.go
generated
vendored
Normal file
25
vendor/github.com/pion/dtls/v3/internal/ciphersuite/tls_ecdhe_rsa_with_aes_256_gcm_sha384.go
generated
vendored
Normal file
|
|
@ -0,0 +1,25 @@
|
||||||
|
// SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package ciphersuite
|
||||||
|
|
||||||
|
import "github.com/pion/dtls/v3/pkg/crypto/clientcertificate"
|
||||||
|
|
||||||
|
// TLSEcdheRsaWithAes256GcmSha384 implements the TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 CipherSuite.
|
||||||
|
type TLSEcdheRsaWithAes256GcmSha384 struct {
|
||||||
|
TLSEcdheEcdsaWithAes256GcmSha384
|
||||||
|
}
|
||||||
|
|
||||||
|
// CertificateType returns what type of certificate this CipherSuite exchanges.
|
||||||
|
func (c *TLSEcdheRsaWithAes256GcmSha384) CertificateType() clientcertificate.Type {
|
||||||
|
return clientcertificate.RSASign
|
||||||
|
}
|
||||||
|
|
||||||
|
// ID returns the ID of the CipherSuite.
|
||||||
|
func (c *TLSEcdheRsaWithAes256GcmSha384) ID() ID {
|
||||||
|
return TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *TLSEcdheRsaWithAes256GcmSha384) String() string {
|
||||||
|
return "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384"
|
||||||
|
}
|
||||||
115
vendor/github.com/pion/dtls/v3/internal/ciphersuite/tls_psk_with_aes_128_cbc_sha256.go
generated
vendored
Normal file
115
vendor/github.com/pion/dtls/v3/internal/ciphersuite/tls_psk_with_aes_128_cbc_sha256.go
generated
vendored
Normal file
|
|
@ -0,0 +1,115 @@
|
||||||
|
// SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package ciphersuite
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/sha256"
|
||||||
|
"fmt"
|
||||||
|
"hash"
|
||||||
|
"sync/atomic"
|
||||||
|
|
||||||
|
"github.com/pion/dtls/v3/pkg/crypto/ciphersuite"
|
||||||
|
"github.com/pion/dtls/v3/pkg/crypto/clientcertificate"
|
||||||
|
"github.com/pion/dtls/v3/pkg/crypto/prf"
|
||||||
|
"github.com/pion/dtls/v3/pkg/protocol/recordlayer"
|
||||||
|
)
|
||||||
|
|
||||||
|
// TLSPskWithAes128CbcSha256 implements the TLS_PSK_WITH_AES_128_CBC_SHA256 CipherSuite.
|
||||||
|
type TLSPskWithAes128CbcSha256 struct {
|
||||||
|
cbc atomic.Value // *cryptoCBC
|
||||||
|
}
|
||||||
|
|
||||||
|
// CertificateType returns what type of certificate this CipherSuite exchanges.
|
||||||
|
func (c *TLSPskWithAes128CbcSha256) CertificateType() clientcertificate.Type {
|
||||||
|
return clientcertificate.Type(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
// KeyExchangeAlgorithm controls what key exchange algorithm is using during the handshake.
|
||||||
|
func (c *TLSPskWithAes128CbcSha256) KeyExchangeAlgorithm() KeyExchangeAlgorithm {
|
||||||
|
return KeyExchangeAlgorithmPsk
|
||||||
|
}
|
||||||
|
|
||||||
|
// ECC uses Elliptic Curve Cryptography.
|
||||||
|
func (c *TLSPskWithAes128CbcSha256) ECC() bool {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// ID returns the ID of the CipherSuite.
|
||||||
|
func (c *TLSPskWithAes128CbcSha256) ID() ID {
|
||||||
|
return TLS_PSK_WITH_AES_128_CBC_SHA256
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *TLSPskWithAes128CbcSha256) String() string {
|
||||||
|
return "TLS_PSK_WITH_AES_128_CBC_SHA256"
|
||||||
|
}
|
||||||
|
|
||||||
|
// HashFunc returns the hashing func for this CipherSuite.
|
||||||
|
func (c *TLSPskWithAes128CbcSha256) HashFunc() func() hash.Hash {
|
||||||
|
return sha256.New
|
||||||
|
}
|
||||||
|
|
||||||
|
// AuthenticationType controls what authentication method is using during the handshake.
|
||||||
|
func (c *TLSPskWithAes128CbcSha256) AuthenticationType() AuthenticationType {
|
||||||
|
return AuthenticationTypePreSharedKey
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsInitialized returns if the CipherSuite has keying material and can
|
||||||
|
// encrypt/decrypt packets.
|
||||||
|
func (c *TLSPskWithAes128CbcSha256) IsInitialized() bool {
|
||||||
|
return c.cbc.Load() != nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Init initializes the internal Cipher with keying material.
|
||||||
|
func (c *TLSPskWithAes128CbcSha256) Init(masterSecret, clientRandom, serverRandom []byte, isClient bool) error {
|
||||||
|
const (
|
||||||
|
prfMacLen = 32
|
||||||
|
prfKeyLen = 16
|
||||||
|
prfIvLen = 16
|
||||||
|
)
|
||||||
|
|
||||||
|
keys, err := prf.GenerateEncryptionKeys(
|
||||||
|
masterSecret, clientRandom, serverRandom, prfMacLen, prfKeyLen, prfIvLen, c.HashFunc(),
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
var cbc *ciphersuite.CBC
|
||||||
|
if isClient {
|
||||||
|
cbc, err = ciphersuite.NewCBC(
|
||||||
|
keys.ClientWriteKey, keys.ClientWriteIV, keys.ClientMACKey,
|
||||||
|
keys.ServerWriteKey, keys.ServerWriteIV, keys.ServerMACKey,
|
||||||
|
c.HashFunc(),
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
cbc, err = ciphersuite.NewCBC(
|
||||||
|
keys.ServerWriteKey, keys.ServerWriteIV, keys.ServerMACKey,
|
||||||
|
keys.ClientWriteKey, keys.ClientWriteIV, keys.ClientMACKey,
|
||||||
|
c.HashFunc(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
c.cbc.Store(cbc)
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Encrypt encrypts a single TLS RecordLayer.
|
||||||
|
func (c *TLSPskWithAes128CbcSha256) Encrypt(pkt *recordlayer.RecordLayer, raw []byte) ([]byte, error) {
|
||||||
|
cipherSuite, ok := c.cbc.Load().(*ciphersuite.CBC)
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf("%w, unable to encrypt", errCipherSuiteNotInit)
|
||||||
|
}
|
||||||
|
|
||||||
|
return cipherSuite.Encrypt(pkt, raw)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Decrypt decrypts a single TLS RecordLayer.
|
||||||
|
func (c *TLSPskWithAes128CbcSha256) Decrypt(h recordlayer.Header, raw []byte) ([]byte, error) {
|
||||||
|
cipherSuite, ok := c.cbc.Load().(*ciphersuite.CBC)
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf("%w, unable to decrypt", errCipherSuiteNotInit)
|
||||||
|
}
|
||||||
|
|
||||||
|
return cipherSuite.Decrypt(h, raw)
|
||||||
|
}
|
||||||
21
vendor/github.com/pion/dtls/v3/internal/ciphersuite/tls_psk_with_aes_128_ccm.go
generated
vendored
Normal file
21
vendor/github.com/pion/dtls/v3/internal/ciphersuite/tls_psk_with_aes_128_ccm.go
generated
vendored
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
// SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package ciphersuite
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/pion/dtls/v3/pkg/crypto/ciphersuite"
|
||||||
|
"github.com/pion/dtls/v3/pkg/crypto/clientcertificate"
|
||||||
|
)
|
||||||
|
|
||||||
|
// NewTLSPskWithAes128Ccm returns the TLS_PSK_WITH_AES_128_CCM CipherSuite.
|
||||||
|
func NewTLSPskWithAes128Ccm() *Aes128Ccm {
|
||||||
|
return newAes128Ccm(
|
||||||
|
clientcertificate.Type(0),
|
||||||
|
TLS_PSK_WITH_AES_128_CCM,
|
||||||
|
true,
|
||||||
|
ciphersuite.CCMTagLength,
|
||||||
|
KeyExchangeAlgorithmPsk,
|
||||||
|
false,
|
||||||
|
)
|
||||||
|
}
|
||||||
21
vendor/github.com/pion/dtls/v3/internal/ciphersuite/tls_psk_with_aes_128_ccm8.go
generated
vendored
Normal file
21
vendor/github.com/pion/dtls/v3/internal/ciphersuite/tls_psk_with_aes_128_ccm8.go
generated
vendored
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
// SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package ciphersuite
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/pion/dtls/v3/pkg/crypto/ciphersuite"
|
||||||
|
"github.com/pion/dtls/v3/pkg/crypto/clientcertificate"
|
||||||
|
)
|
||||||
|
|
||||||
|
// NewTLSPskWithAes128Ccm8 returns the TLS_PSK_WITH_AES_128_CCM_8 CipherSuite.
|
||||||
|
func NewTLSPskWithAes128Ccm8() *Aes128Ccm {
|
||||||
|
return newAes128Ccm(
|
||||||
|
clientcertificate.Type(0),
|
||||||
|
TLS_PSK_WITH_AES_128_CCM_8,
|
||||||
|
true,
|
||||||
|
ciphersuite.CCMTagLength8,
|
||||||
|
KeyExchangeAlgorithmPsk,
|
||||||
|
false,
|
||||||
|
)
|
||||||
|
}
|
||||||
35
vendor/github.com/pion/dtls/v3/internal/ciphersuite/tls_psk_with_aes_128_gcm_sha256.go
generated
vendored
Normal file
35
vendor/github.com/pion/dtls/v3/internal/ciphersuite/tls_psk_with_aes_128_gcm_sha256.go
generated
vendored
Normal file
|
|
@ -0,0 +1,35 @@
|
||||||
|
// SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package ciphersuite
|
||||||
|
|
||||||
|
import "github.com/pion/dtls/v3/pkg/crypto/clientcertificate"
|
||||||
|
|
||||||
|
// TLSPskWithAes128GcmSha256 implements the TLS_PSK_WITH_AES_128_GCM_SHA256 CipherSuite.
|
||||||
|
type TLSPskWithAes128GcmSha256 struct {
|
||||||
|
TLSEcdheEcdsaWithAes128GcmSha256
|
||||||
|
}
|
||||||
|
|
||||||
|
// CertificateType returns what type of certificate this CipherSuite exchanges.
|
||||||
|
func (c *TLSPskWithAes128GcmSha256) CertificateType() clientcertificate.Type {
|
||||||
|
return clientcertificate.Type(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
// KeyExchangeAlgorithm controls what key exchange algorithm is using during the handshake.
|
||||||
|
func (c *TLSPskWithAes128GcmSha256) KeyExchangeAlgorithm() KeyExchangeAlgorithm {
|
||||||
|
return KeyExchangeAlgorithmPsk
|
||||||
|
}
|
||||||
|
|
||||||
|
// ID returns the ID of the CipherSuite.
|
||||||
|
func (c *TLSPskWithAes128GcmSha256) ID() ID {
|
||||||
|
return TLS_PSK_WITH_AES_128_GCM_SHA256
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *TLSPskWithAes128GcmSha256) String() string {
|
||||||
|
return "TLS_PSK_WITH_AES_128_GCM_SHA256"
|
||||||
|
}
|
||||||
|
|
||||||
|
// AuthenticationType controls what authentication method is using during the handshake.
|
||||||
|
func (c *TLSPskWithAes128GcmSha256) AuthenticationType() AuthenticationType {
|
||||||
|
return AuthenticationTypePreSharedKey
|
||||||
|
}
|
||||||
21
vendor/github.com/pion/dtls/v3/internal/ciphersuite/tls_psk_with_aes_256_ccm8.go
generated
vendored
Normal file
21
vendor/github.com/pion/dtls/v3/internal/ciphersuite/tls_psk_with_aes_256_ccm8.go
generated
vendored
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
// SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package ciphersuite
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/pion/dtls/v3/pkg/crypto/ciphersuite"
|
||||||
|
"github.com/pion/dtls/v3/pkg/crypto/clientcertificate"
|
||||||
|
)
|
||||||
|
|
||||||
|
// NewTLSPskWithAes256Ccm8 returns the TLS_PSK_WITH_AES_256_CCM_8 CipherSuite.
|
||||||
|
func NewTLSPskWithAes256Ccm8() *Aes256Ccm {
|
||||||
|
return newAes256Ccm(
|
||||||
|
clientcertificate.Type(0),
|
||||||
|
TLS_PSK_WITH_AES_256_CCM_8,
|
||||||
|
true,
|
||||||
|
ciphersuite.CCMTagLength8,
|
||||||
|
KeyExchangeAlgorithmPsk,
|
||||||
|
false,
|
||||||
|
)
|
||||||
|
}
|
||||||
14
vendor/github.com/pion/dtls/v3/internal/ciphersuite/types/authentication_type.go
generated
vendored
Normal file
14
vendor/github.com/pion/dtls/v3/internal/ciphersuite/types/authentication_type.go
generated
vendored
Normal file
|
|
@ -0,0 +1,14 @@
|
||||||
|
// SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package types
|
||||||
|
|
||||||
|
// AuthenticationType controls what authentication method is using during the handshake.
|
||||||
|
type AuthenticationType int
|
||||||
|
|
||||||
|
// AuthenticationType Enums.
|
||||||
|
const (
|
||||||
|
AuthenticationTypeCertificate AuthenticationType = iota + 1
|
||||||
|
AuthenticationTypePreSharedKey
|
||||||
|
AuthenticationTypeAnonymous
|
||||||
|
)
|
||||||
20
vendor/github.com/pion/dtls/v3/internal/ciphersuite/types/key_exchange_algorithm.go
generated
vendored
Normal file
20
vendor/github.com/pion/dtls/v3/internal/ciphersuite/types/key_exchange_algorithm.go
generated
vendored
Normal file
|
|
@ -0,0 +1,20 @@
|
||||||
|
// SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
// Package types provides types for TLS Ciphers
|
||||||
|
package types // nolint:revive
|
||||||
|
|
||||||
|
// KeyExchangeAlgorithm controls what exchange algorithm was chosen.
|
||||||
|
type KeyExchangeAlgorithm int
|
||||||
|
|
||||||
|
// KeyExchangeAlgorithm Bitmask.
|
||||||
|
const (
|
||||||
|
KeyExchangeAlgorithmNone KeyExchangeAlgorithm = 0
|
||||||
|
KeyExchangeAlgorithmPsk KeyExchangeAlgorithm = iota << 1
|
||||||
|
KeyExchangeAlgorithmEcdhe
|
||||||
|
)
|
||||||
|
|
||||||
|
// Has check if keyExchangeAlgorithm is supported.
|
||||||
|
func (a KeyExchangeAlgorithm) Has(v KeyExchangeAlgorithm) bool {
|
||||||
|
return (a & v) == v
|
||||||
|
}
|
||||||
50
vendor/github.com/pion/dtls/v3/internal/closer/closer.go
generated
vendored
Normal file
50
vendor/github.com/pion/dtls/v3/internal/closer/closer.go
generated
vendored
Normal file
|
|
@ -0,0 +1,50 @@
|
||||||
|
// SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
// Package closer provides signaling channel for shutdown
|
||||||
|
package closer
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Closer allows for each signaling a channel for shutdown.
|
||||||
|
type Closer struct {
|
||||||
|
ctx context.Context //nolint:containedctx
|
||||||
|
closeFunc func()
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewCloser creates a new instance of Closer.
|
||||||
|
func NewCloser() *Closer {
|
||||||
|
ctx, closeFunc := context.WithCancel(context.Background())
|
||||||
|
|
||||||
|
return &Closer{
|
||||||
|
ctx: ctx,
|
||||||
|
closeFunc: closeFunc,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewCloserWithParent creates a new instance of Closer with a parent context.
|
||||||
|
func NewCloserWithParent(ctx context.Context) *Closer {
|
||||||
|
ctx, closeFunc := context.WithCancel(ctx)
|
||||||
|
|
||||||
|
return &Closer{
|
||||||
|
ctx: ctx,
|
||||||
|
closeFunc: closeFunc,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Done returns a channel signaling when it is done.
|
||||||
|
func (c *Closer) Done() <-chan struct{} {
|
||||||
|
return c.ctx.Done()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Err returns an error of the context.
|
||||||
|
func (c *Closer) Err() error {
|
||||||
|
return c.ctx.Err()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close sends a signal to trigger the ctx done channel.
|
||||||
|
func (c *Closer) Close() {
|
||||||
|
c.closeFunc()
|
||||||
|
}
|
||||||
242
vendor/github.com/pion/dtls/v3/internal/net/buffer.go
generated
vendored
Normal file
242
vendor/github.com/pion/dtls/v3/internal/net/buffer.go
generated
vendored
Normal file
|
|
@ -0,0 +1,242 @@
|
||||||
|
// SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
// Package net implements DTLS specific networking primitives.
|
||||||
|
// NOTE: this package is an adaption of pion/transport/packetio that allows for
|
||||||
|
// storing a remote address alongside each packet in the buffer and implements
|
||||||
|
// relevant methods of net.PacketConn. If possible, the updates made in this
|
||||||
|
// repository will be reflected back upstream. If not, it is likely that this
|
||||||
|
// will be moved to a public package in this repository.
|
||||||
|
//
|
||||||
|
// This package was migrated from pion/transport/packetio at
|
||||||
|
// https://github.com/pion/transport/commit/6890c795c807a617c054149eee40a69d7fdfbfdb
|
||||||
|
package net
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"errors"
|
||||||
|
"io"
|
||||||
|
"net"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/pion/transport/v4/deadline"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ErrTimeout indicates that deadline was reached before operation could be
|
||||||
|
// completed.
|
||||||
|
var ErrTimeout = errors.New("buffer: i/o timeout")
|
||||||
|
|
||||||
|
// AddrPacket is a packet payload and the associated remote address from which
|
||||||
|
// it was received.
|
||||||
|
type AddrPacket struct {
|
||||||
|
addr net.Addr
|
||||||
|
data bytes.Buffer
|
||||||
|
}
|
||||||
|
|
||||||
|
// PacketBuffer is a circular buffer for network packets. Each slot in the
|
||||||
|
// buffer contains the remote address from which the packet was received, as
|
||||||
|
// well as the packet data.
|
||||||
|
type PacketBuffer struct {
|
||||||
|
mutex sync.Mutex
|
||||||
|
|
||||||
|
packets []AddrPacket
|
||||||
|
write, read int
|
||||||
|
|
||||||
|
// full indicates whether the buffer is full, which is needed to distinguish
|
||||||
|
// when the write pointer and read pointer are at the same index.
|
||||||
|
full bool
|
||||||
|
|
||||||
|
notify chan struct{}
|
||||||
|
closed bool
|
||||||
|
|
||||||
|
readDeadline *deadline.Deadline
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewPacketBuffer creates a new PacketBuffer.
|
||||||
|
func NewPacketBuffer() *PacketBuffer {
|
||||||
|
return &PacketBuffer{
|
||||||
|
readDeadline: deadline.New(),
|
||||||
|
// In the narrow context in which this package is currently used, there
|
||||||
|
// will always be at least one packet written to the buffer. Therefore,
|
||||||
|
// we opt to allocate with size of 1 during construction, rather than
|
||||||
|
// waiting until that first packet is written.
|
||||||
|
packets: make([]AddrPacket, 1),
|
||||||
|
full: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// WriteTo writes a single packet to the buffer. The supplied address will
|
||||||
|
// remain associated with the packet.
|
||||||
|
func (b *PacketBuffer) WriteTo(pkt []byte, addr net.Addr) (int, error) {
|
||||||
|
b.mutex.Lock()
|
||||||
|
|
||||||
|
if b.closed {
|
||||||
|
b.mutex.Unlock()
|
||||||
|
|
||||||
|
return 0, io.ErrClosedPipe
|
||||||
|
}
|
||||||
|
|
||||||
|
var notify chan struct{}
|
||||||
|
if b.notify != nil {
|
||||||
|
notify = b.notify
|
||||||
|
b.notify = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check to see if we are full.
|
||||||
|
if b.full {
|
||||||
|
// If so, grow AddrPacket buffer.
|
||||||
|
var newSize int
|
||||||
|
if len(b.packets) < 128 {
|
||||||
|
// Double the number of packets.
|
||||||
|
newSize = len(b.packets) * 2
|
||||||
|
} else {
|
||||||
|
// Increase the number of packets by 25%.
|
||||||
|
newSize = 5 * len(b.packets) / 4
|
||||||
|
}
|
||||||
|
newBuf := make([]AddrPacket, newSize)
|
||||||
|
var n int
|
||||||
|
if b.read < b.write {
|
||||||
|
n = copy(newBuf, b.packets[b.read:b.write])
|
||||||
|
} else {
|
||||||
|
n = copy(newBuf, b.packets[b.read:])
|
||||||
|
n += copy(newBuf[n:], b.packets[:b.write])
|
||||||
|
}
|
||||||
|
|
||||||
|
b.packets = newBuf
|
||||||
|
|
||||||
|
// Update read/write pointers and mark buffer as not full.
|
||||||
|
b.read = 0
|
||||||
|
b.write = n
|
||||||
|
b.full = false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Store the packet at the write pointer.
|
||||||
|
packet := &b.packets[b.write]
|
||||||
|
packet.data.Reset()
|
||||||
|
n, err := packet.data.Write(pkt)
|
||||||
|
if err != nil {
|
||||||
|
b.mutex.Unlock()
|
||||||
|
|
||||||
|
return n, err
|
||||||
|
}
|
||||||
|
packet.addr = addr
|
||||||
|
|
||||||
|
// Increment write pointer.
|
||||||
|
b.write++
|
||||||
|
|
||||||
|
// If the write pointer is equal to the length of the buffer, wrap around.
|
||||||
|
if len(b.packets) == b.write {
|
||||||
|
b.write = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// If a write resulted in making write and read pointers equivalent, then we
|
||||||
|
// are full.
|
||||||
|
if b.write == b.read {
|
||||||
|
b.full = true
|
||||||
|
}
|
||||||
|
|
||||||
|
b.mutex.Unlock()
|
||||||
|
|
||||||
|
if notify != nil {
|
||||||
|
close(notify)
|
||||||
|
}
|
||||||
|
|
||||||
|
return n, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReadFrom reads a single packet from the buffer, or blocks until one is
|
||||||
|
// available.
|
||||||
|
func (b *PacketBuffer) ReadFrom(packet []byte) (n int, addr net.Addr, err error) { //nolint:cyclop
|
||||||
|
select {
|
||||||
|
case <-b.readDeadline.Done():
|
||||||
|
return 0, nil, ErrTimeout
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
|
||||||
|
for {
|
||||||
|
b.mutex.Lock()
|
||||||
|
|
||||||
|
if b.read != b.write || b.full {
|
||||||
|
ap := b.packets[b.read]
|
||||||
|
if len(packet) < ap.data.Len() {
|
||||||
|
b.mutex.Unlock()
|
||||||
|
|
||||||
|
return 0, nil, io.ErrShortBuffer
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy packet data from buffer.
|
||||||
|
n, err := ap.data.Read(packet)
|
||||||
|
if err != nil {
|
||||||
|
b.mutex.Unlock()
|
||||||
|
|
||||||
|
return n, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Advance read pointer.
|
||||||
|
b.read++
|
||||||
|
if len(b.packets) == b.read {
|
||||||
|
b.read = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we were full before reading and have successfully read, we are
|
||||||
|
// no longer full.
|
||||||
|
if b.full {
|
||||||
|
b.full = false
|
||||||
|
}
|
||||||
|
|
||||||
|
b.mutex.Unlock()
|
||||||
|
|
||||||
|
return n, ap.addr, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if b.closed {
|
||||||
|
b.mutex.Unlock()
|
||||||
|
|
||||||
|
return 0, nil, io.EOF
|
||||||
|
}
|
||||||
|
|
||||||
|
if b.notify == nil {
|
||||||
|
b.notify = make(chan struct{})
|
||||||
|
}
|
||||||
|
notify := b.notify
|
||||||
|
b.mutex.Unlock()
|
||||||
|
|
||||||
|
select {
|
||||||
|
case <-b.readDeadline.Done():
|
||||||
|
return 0, nil, ErrTimeout
|
||||||
|
case <-notify:
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close closes the buffer, allowing unread packets to be read, but erroring on
|
||||||
|
// any new writes.
|
||||||
|
func (b *PacketBuffer) Close() (err error) {
|
||||||
|
b.mutex.Lock()
|
||||||
|
|
||||||
|
if b.closed {
|
||||||
|
b.mutex.Unlock()
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
notify := b.notify
|
||||||
|
b.notify = nil
|
||||||
|
b.closed = true
|
||||||
|
|
||||||
|
b.mutex.Unlock()
|
||||||
|
|
||||||
|
if notify != nil {
|
||||||
|
close(notify)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetReadDeadline sets the read deadline for the buffer.
|
||||||
|
func (b *PacketBuffer) SetReadDeadline(t time.Time) error {
|
||||||
|
b.readDeadline.Set(t)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
413
vendor/github.com/pion/dtls/v3/internal/net/udp/packet_conn.go
generated
vendored
Normal file
413
vendor/github.com/pion/dtls/v3/internal/net/udp/packet_conn.go
generated
vendored
Normal file
|
|
@ -0,0 +1,413 @@
|
||||||
|
// SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
// Package udp implements DTLS specific UDP networking primitives.
|
||||||
|
// NOTE: this package is an adaption of pion/transport/udp that allows for
|
||||||
|
// routing datagrams based on identifiers other than the remote address. The
|
||||||
|
// primary use case for this functionality is routing based on DTLS connection
|
||||||
|
// IDs. In order to allow for consumers of this package to treat connections as
|
||||||
|
// generic net.PackageConn, routing and identitier establishment is based on
|
||||||
|
// custom introspecion of datagrams, rather than direct intervention by
|
||||||
|
// consumers. If possible, the updates made in this repository will be reflected
|
||||||
|
// back upstream. If not, it is likely that this will be moved to a public
|
||||||
|
// package in this repository.
|
||||||
|
//
|
||||||
|
// This package was migrated from pion/transport/udp at
|
||||||
|
// https://github.com/pion/transport/commit/6890c795c807a617c054149eee40a69d7fdfbfdb
|
||||||
|
package udp
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
"net"
|
||||||
|
"sync"
|
||||||
|
"sync/atomic"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
idtlsnet "github.com/pion/dtls/v3/internal/net"
|
||||||
|
dtlsnet "github.com/pion/dtls/v3/pkg/net"
|
||||||
|
"github.com/pion/transport/v4/deadline"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
receiveMTU = 8192
|
||||||
|
defaultListenBacklog = 128 // same as Linux default
|
||||||
|
)
|
||||||
|
|
||||||
|
// Typed errors.
|
||||||
|
var (
|
||||||
|
ErrClosedListener = errors.New("udp: listener closed")
|
||||||
|
ErrListenQueueExceeded = errors.New("udp: listen queue exceeded")
|
||||||
|
)
|
||||||
|
|
||||||
|
// listener augments a connection-oriented Listener over a UDP PacketConn.
|
||||||
|
type listener struct {
|
||||||
|
pConn *net.UDPConn
|
||||||
|
|
||||||
|
accepting atomic.Value // bool
|
||||||
|
acceptCh chan *PacketConn
|
||||||
|
doneCh chan struct{}
|
||||||
|
doneOnce sync.Once
|
||||||
|
acceptFilter func([]byte) bool
|
||||||
|
datagramRouter func([]byte) (string, bool)
|
||||||
|
connIdentifier func([]byte) (string, bool)
|
||||||
|
|
||||||
|
connLock sync.Mutex
|
||||||
|
conns map[string]*PacketConn
|
||||||
|
connWG sync.WaitGroup
|
||||||
|
|
||||||
|
readWG sync.WaitGroup
|
||||||
|
errClose atomic.Value // error
|
||||||
|
|
||||||
|
readDoneCh chan struct{}
|
||||||
|
errRead atomic.Value // error
|
||||||
|
}
|
||||||
|
|
||||||
|
// Accept waits for and returns the next connection to the listener.
|
||||||
|
func (l *listener) Accept() (net.PacketConn, net.Addr, error) {
|
||||||
|
select {
|
||||||
|
case c := <-l.acceptCh:
|
||||||
|
l.connWG.Add(1)
|
||||||
|
|
||||||
|
return c, c.raddr, nil
|
||||||
|
|
||||||
|
case <-l.readDoneCh:
|
||||||
|
err, _ := l.errRead.Load().(error)
|
||||||
|
|
||||||
|
return nil, nil, err
|
||||||
|
|
||||||
|
case <-l.doneCh:
|
||||||
|
return nil, nil, ErrClosedListener
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close closes the listener.
|
||||||
|
// Any blocked Accept operations will be unblocked and return errors.
|
||||||
|
func (l *listener) Close() error {
|
||||||
|
var err error
|
||||||
|
l.doneOnce.Do(func() {
|
||||||
|
l.accepting.Store(false)
|
||||||
|
close(l.doneCh)
|
||||||
|
|
||||||
|
l.connLock.Lock()
|
||||||
|
// Close unaccepted connections
|
||||||
|
lclose:
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case c := <-l.acceptCh:
|
||||||
|
close(c.doneCh)
|
||||||
|
// If we have an alternate identifier, remove it from the connection
|
||||||
|
// map.
|
||||||
|
if id := c.id.Load(); id != nil {
|
||||||
|
delete(l.conns, id.(string)) //nolint:forcetypeassert
|
||||||
|
}
|
||||||
|
// If we haven't already removed the remote address, remove it
|
||||||
|
// from the connection map.
|
||||||
|
if c.rmraddr.Load() == nil {
|
||||||
|
delete(l.conns, c.raddr.String())
|
||||||
|
c.rmraddr.Store(true)
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
break lclose
|
||||||
|
}
|
||||||
|
}
|
||||||
|
nConns := len(l.conns)
|
||||||
|
l.connLock.Unlock()
|
||||||
|
|
||||||
|
l.connWG.Done()
|
||||||
|
|
||||||
|
if nConns == 0 {
|
||||||
|
// Wait if this is the final connection.
|
||||||
|
l.readWG.Wait()
|
||||||
|
if errClose, ok := l.errClose.Load().(error); ok {
|
||||||
|
err = errClose
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
err = nil
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Addr returns the listener's network address.
|
||||||
|
func (l *listener) Addr() net.Addr {
|
||||||
|
return l.pConn.LocalAddr()
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListenConfig stores options for listening to an address.
|
||||||
|
type ListenConfig struct {
|
||||||
|
// Backlog defines the maximum length of the queue of pending
|
||||||
|
// connections. It is equivalent of the backlog argument of
|
||||||
|
// POSIX listen function.
|
||||||
|
// If a connection request arrives when the queue is full,
|
||||||
|
// the request will be silently discarded, unlike TCP.
|
||||||
|
// Set zero to use default value 128 which is same as Linux default.
|
||||||
|
Backlog int
|
||||||
|
|
||||||
|
// AcceptFilter determines whether the new conn should be made for
|
||||||
|
// the incoming packet. If not set, any packet creates new conn.
|
||||||
|
AcceptFilter func([]byte) bool
|
||||||
|
|
||||||
|
// DatagramRouter routes an incoming datagram to a connection by extracting
|
||||||
|
// an identifier from the its paylod
|
||||||
|
DatagramRouter func([]byte) (string, bool)
|
||||||
|
|
||||||
|
// ConnectionIdentifier extracts an identifier from an outgoing packet. If
|
||||||
|
// the identifier is not already associated with the connection, it will be
|
||||||
|
// added.
|
||||||
|
ConnectionIdentifier func([]byte) (string, bool)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Listen creates a new listener based on the ListenConfig.
|
||||||
|
func (lc *ListenConfig) Listen(network string, laddr *net.UDPAddr) (dtlsnet.PacketListener, error) {
|
||||||
|
if lc.Backlog == 0 {
|
||||||
|
lc.Backlog = defaultListenBacklog
|
||||||
|
}
|
||||||
|
|
||||||
|
conn, err := net.ListenUDP(network, laddr)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
packetListener := &listener{
|
||||||
|
pConn: conn,
|
||||||
|
acceptCh: make(chan *PacketConn, lc.Backlog),
|
||||||
|
conns: make(map[string]*PacketConn),
|
||||||
|
doneCh: make(chan struct{}),
|
||||||
|
acceptFilter: lc.AcceptFilter,
|
||||||
|
datagramRouter: lc.DatagramRouter,
|
||||||
|
connIdentifier: lc.ConnectionIdentifier,
|
||||||
|
readDoneCh: make(chan struct{}),
|
||||||
|
}
|
||||||
|
|
||||||
|
packetListener.accepting.Store(true)
|
||||||
|
packetListener.connWG.Add(1)
|
||||||
|
packetListener.readWG.Add(2) // wait readLoop and Close execution routine
|
||||||
|
|
||||||
|
go packetListener.readLoop()
|
||||||
|
go func() {
|
||||||
|
packetListener.connWG.Wait()
|
||||||
|
if err := packetListener.pConn.Close(); err != nil {
|
||||||
|
packetListener.errClose.Store(err)
|
||||||
|
}
|
||||||
|
packetListener.readWG.Done()
|
||||||
|
}()
|
||||||
|
|
||||||
|
return packetListener, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Listen creates a new listener using default ListenConfig.
|
||||||
|
func Listen(network string, laddr *net.UDPAddr) (dtlsnet.PacketListener, error) {
|
||||||
|
return (&ListenConfig{}).Listen(network, laddr)
|
||||||
|
}
|
||||||
|
|
||||||
|
// readLoop dispatches packets to the proper connection, creating a new one if
|
||||||
|
// necessary, until all connections are closed.
|
||||||
|
func (l *listener) readLoop() {
|
||||||
|
defer l.readWG.Done()
|
||||||
|
defer close(l.readDoneCh)
|
||||||
|
|
||||||
|
buf := make([]byte, receiveMTU)
|
||||||
|
|
||||||
|
for {
|
||||||
|
n, raddr, err := l.pConn.ReadFrom(buf)
|
||||||
|
if err != nil {
|
||||||
|
l.errRead.Store(err)
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
conn, ok, err := l.getConn(raddr, buf[:n])
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if ok {
|
||||||
|
_, _ = conn.buffer.WriteTo(buf[:n], raddr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// getConn gets an existing connection or creates a new one.
|
||||||
|
func (l *listener) getConn(raddr net.Addr, buf []byte) (*PacketConn, bool, error) { //nolint:cyclop
|
||||||
|
l.connLock.Lock()
|
||||||
|
defer l.connLock.Unlock()
|
||||||
|
// If we have a custom resolver, use it.
|
||||||
|
if l.datagramRouter != nil {
|
||||||
|
if id, ok := l.datagramRouter(buf); ok {
|
||||||
|
if conn, ok := l.conns[id]; ok {
|
||||||
|
return conn, true, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we don't have a custom resolver, or we were unable to find an
|
||||||
|
// associated connection, fall back to remote address.
|
||||||
|
conn, ok := l.conns[raddr.String()]
|
||||||
|
if !ok {
|
||||||
|
if isAccepting, ok := l.accepting.Load().(bool); !isAccepting || !ok {
|
||||||
|
return nil, false, ErrClosedListener
|
||||||
|
}
|
||||||
|
if l.acceptFilter != nil {
|
||||||
|
if !l.acceptFilter(buf) {
|
||||||
|
return nil, false, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
conn = l.newPacketConn(raddr)
|
||||||
|
select {
|
||||||
|
case l.acceptCh <- conn:
|
||||||
|
l.conns[raddr.String()] = conn
|
||||||
|
default:
|
||||||
|
return nil, false, ErrListenQueueExceeded
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return conn, true, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// PacketConn is a net.PacketConn implementation that is able to dictate its
|
||||||
|
// routing ID via an alternate identifier from its remote address. Internal
|
||||||
|
// buffering is performed for reads, and writes are passed through to the
|
||||||
|
// underlying net.PacketConn.
|
||||||
|
type PacketConn struct {
|
||||||
|
listener *listener
|
||||||
|
|
||||||
|
raddr net.Addr
|
||||||
|
rmraddr atomic.Value // bool
|
||||||
|
id atomic.Value // string
|
||||||
|
|
||||||
|
buffer *idtlsnet.PacketBuffer
|
||||||
|
|
||||||
|
doneCh chan struct{}
|
||||||
|
doneOnce sync.Once
|
||||||
|
|
||||||
|
writeDeadline *deadline.Deadline
|
||||||
|
}
|
||||||
|
|
||||||
|
// newPacketConn constructs a new PacketConn.
|
||||||
|
func (l *listener) newPacketConn(raddr net.Addr) *PacketConn {
|
||||||
|
return &PacketConn{
|
||||||
|
listener: l,
|
||||||
|
raddr: raddr,
|
||||||
|
buffer: idtlsnet.NewPacketBuffer(),
|
||||||
|
doneCh: make(chan struct{}),
|
||||||
|
writeDeadline: deadline.New(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReadFrom reads a single packet payload and its associated remote address from
|
||||||
|
// the underlying buffer.
|
||||||
|
func (c *PacketConn) ReadFrom(buff []byte) (int, net.Addr, error) {
|
||||||
|
return c.buffer.ReadFrom(buff)
|
||||||
|
}
|
||||||
|
|
||||||
|
// WriteTo writes len(payload) bytes from payload to the specified address.
|
||||||
|
func (c *PacketConn) WriteTo(payload []byte, addr net.Addr) (n int, err error) {
|
||||||
|
// If we have a connection identifier, check to see if the outgoing packet
|
||||||
|
// sets it.
|
||||||
|
if c.listener.connIdentifier != nil {
|
||||||
|
id := c.id.Load()
|
||||||
|
// Only update establish identifier if we haven't already done so.
|
||||||
|
if id == nil {
|
||||||
|
candidate, ok := c.listener.connIdentifier(payload)
|
||||||
|
// If we have an identifier, add entry to connection map.
|
||||||
|
if ok {
|
||||||
|
c.listener.connLock.Lock()
|
||||||
|
c.listener.conns[candidate] = c
|
||||||
|
c.listener.connLock.Unlock()
|
||||||
|
c.id.Store(candidate)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// If we are writing to a remote address that differs from the initial,
|
||||||
|
// we have an alternate identifier established, and we haven't already
|
||||||
|
// freed the remote address, free the remote address to be used by
|
||||||
|
// another connection.
|
||||||
|
// Note: this strategy results in holding onto a remote address after it
|
||||||
|
// is potentially no longer in use by the client. However, releasing
|
||||||
|
// earlier means that we could miss some packets that should have been
|
||||||
|
// routed to this connection. Ideally, we would drop the connection
|
||||||
|
// entry for the remote address as soon as the client starts sending
|
||||||
|
// using an alternate identifier, but in practice this proves
|
||||||
|
// challenging because any client could spoof a connection identifier,
|
||||||
|
// resulting in the remote address entry being dropped prior to the
|
||||||
|
// "real" client transitioning to sending using the alternate
|
||||||
|
// identifier.
|
||||||
|
if id != nil && c.rmraddr.Load() == nil && addr.String() != c.raddr.String() {
|
||||||
|
c.listener.connLock.Lock()
|
||||||
|
delete(c.listener.conns, c.raddr.String())
|
||||||
|
c.rmraddr.Store(true)
|
||||||
|
c.listener.connLock.Unlock()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
select {
|
||||||
|
case <-c.writeDeadline.Done():
|
||||||
|
return 0, context.DeadlineExceeded
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.listener.pConn.WriteTo(payload, addr)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close closes the conn and releases any Read calls.
|
||||||
|
func (c *PacketConn) Close() error {
|
||||||
|
var err error
|
||||||
|
c.doneOnce.Do(func() {
|
||||||
|
c.listener.connWG.Done()
|
||||||
|
close(c.doneCh)
|
||||||
|
c.listener.connLock.Lock()
|
||||||
|
// If we have an alternate identifier, remove it from the connection
|
||||||
|
// map.
|
||||||
|
if id := c.id.Load(); id != nil {
|
||||||
|
delete(c.listener.conns, id.(string)) //nolint:forcetypeassert
|
||||||
|
}
|
||||||
|
// If we haven't already removed the remote address, remove it from the
|
||||||
|
// connection map.
|
||||||
|
if c.rmraddr.Load() == nil {
|
||||||
|
delete(c.listener.conns, c.raddr.String())
|
||||||
|
c.rmraddr.Store(true)
|
||||||
|
}
|
||||||
|
nConns := len(c.listener.conns)
|
||||||
|
c.listener.connLock.Unlock()
|
||||||
|
|
||||||
|
if isAccepting, ok := c.listener.accepting.Load().(bool); nConns == 0 && !isAccepting && ok {
|
||||||
|
// Wait if this is the final connection
|
||||||
|
c.listener.readWG.Wait()
|
||||||
|
if errClose, ok := c.listener.errClose.Load().(error); ok {
|
||||||
|
err = errClose
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
err = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if errBuf := c.buffer.Close(); errBuf != nil && err == nil {
|
||||||
|
err = errBuf
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// LocalAddr implements net.PacketConn.LocalAddr.
|
||||||
|
func (c *PacketConn) LocalAddr() net.Addr {
|
||||||
|
return c.listener.pConn.LocalAddr()
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetDeadline implements net.PacketConn.SetDeadline.
|
||||||
|
func (c *PacketConn) SetDeadline(t time.Time) error {
|
||||||
|
c.writeDeadline.Set(t)
|
||||||
|
|
||||||
|
return c.SetReadDeadline(t)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetReadDeadline implements net.PacketConn.SetReadDeadline.
|
||||||
|
func (c *PacketConn) SetReadDeadline(t time.Time) error {
|
||||||
|
return c.buffer.SetReadDeadline(t)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetWriteDeadline implements net.PacketConn.SetWriteDeadline.
|
||||||
|
func (c *PacketConn) SetWriteDeadline(t time.Time) error {
|
||||||
|
c.writeDeadline.Set(t)
|
||||||
|
// Write deadline of underlying connection should not be changed
|
||||||
|
// since the connection can be shared.
|
||||||
|
return nil
|
||||||
|
}
|
||||||
53
vendor/github.com/pion/dtls/v3/internal/util/util.go
generated
vendored
Normal file
53
vendor/github.com/pion/dtls/v3/internal/util/util.go
generated
vendored
Normal file
|
|
@ -0,0 +1,53 @@
|
||||||
|
// SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
// Package util contains small helpers used across the repo
|
||||||
|
package util // nolint:revive
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/binary"
|
||||||
|
|
||||||
|
"golang.org/x/crypto/cryptobyte"
|
||||||
|
)
|
||||||
|
|
||||||
|
// BigEndianUint24 returns the value of a big endian uint24.
|
||||||
|
func BigEndianUint24(raw []byte) uint32 {
|
||||||
|
if len(raw) < 3 {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
rawCopy := make([]byte, 4)
|
||||||
|
copy(rawCopy[1:], raw)
|
||||||
|
|
||||||
|
return binary.BigEndian.Uint32(rawCopy)
|
||||||
|
}
|
||||||
|
|
||||||
|
// PutBigEndianUint24 encodes a uint24 and places into out.
|
||||||
|
func PutBigEndianUint24(out []byte, in uint32) {
|
||||||
|
tmp := make([]byte, 4)
|
||||||
|
binary.BigEndian.PutUint32(tmp, in)
|
||||||
|
copy(out, tmp[1:])
|
||||||
|
}
|
||||||
|
|
||||||
|
// PutBigEndianUint48 encodes a uint64 and places into out.
|
||||||
|
func PutBigEndianUint48(out []byte, in uint64) {
|
||||||
|
tmp := make([]byte, 8)
|
||||||
|
binary.BigEndian.PutUint64(tmp, in)
|
||||||
|
copy(out, tmp[2:])
|
||||||
|
}
|
||||||
|
|
||||||
|
// Max returns the larger value.
|
||||||
|
func Max(a, b int) int {
|
||||||
|
if a > b {
|
||||||
|
return a
|
||||||
|
}
|
||||||
|
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddUint48 appends a big-endian, 48-bit value to the byte string.
|
||||||
|
// Remove if / when https://github.com/golang/crypto/pull/265 is merged
|
||||||
|
// upstream.
|
||||||
|
func AddUint48(b *cryptobyte.Builder, v uint64) {
|
||||||
|
b.AddBytes([]byte{byte(v >> 40), byte(v >> 32), byte(v >> 24), byte(v >> 16), byte(v >> 8), byte(v)})
|
||||||
|
}
|
||||||
115
vendor/github.com/pion/dtls/v3/listener.go
generated
vendored
Normal file
115
vendor/github.com/pion/dtls/v3/listener.go
generated
vendored
Normal file
|
|
@ -0,0 +1,115 @@
|
||||||
|
// SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package dtls
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net"
|
||||||
|
|
||||||
|
"github.com/pion/dtls/v3/internal/net/udp"
|
||||||
|
dtlsnet "github.com/pion/dtls/v3/pkg/net"
|
||||||
|
"github.com/pion/dtls/v3/pkg/protocol"
|
||||||
|
"github.com/pion/dtls/v3/pkg/protocol/recordlayer"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Listen creates a DTLS listener.
|
||||||
|
//
|
||||||
|
// Deprecated: Use ListenWithOptions instead.
|
||||||
|
func Listen(network string, laddr *net.UDPAddr, config *Config) (net.Listener, error) {
|
||||||
|
if err := validateConfig(config); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
lc := udp.ListenConfig{
|
||||||
|
AcceptFilter: func(packet []byte) bool {
|
||||||
|
pkts, err := recordlayer.UnpackDatagram(packet)
|
||||||
|
if err != nil || len(pkts) < 1 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
h := &recordlayer.Header{}
|
||||||
|
if err := h.Unmarshal(pkts[0]); err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return h.ContentType == protocol.ContentTypeHandshake
|
||||||
|
},
|
||||||
|
}
|
||||||
|
// If connection ID support is enabled, then they must be supported in
|
||||||
|
// routing.
|
||||||
|
if config.ConnectionIDGenerator != nil {
|
||||||
|
lc.DatagramRouter = cidDatagramRouter(len(config.ConnectionIDGenerator()))
|
||||||
|
lc.ConnectionIdentifier = cidConnIdentifier()
|
||||||
|
}
|
||||||
|
parent, err := lc.Listen(network, laddr)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &listener{
|
||||||
|
config: config,
|
||||||
|
parent: parent,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListenWithOptions creates a DTLS listener.
|
||||||
|
func ListenWithOptions(network string, laddr *net.UDPAddr, opts ...ServerOption) (net.Listener, error) {
|
||||||
|
config, err := buildServerConfig(opts...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return Listen(network, laddr, config)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewListener creates a DTLS listener which accepts connections from an inner Listener.
|
||||||
|
//
|
||||||
|
// Deprecated: Use NewListenerWithOptions instead.
|
||||||
|
func NewListener(inner dtlsnet.PacketListener, config *Config) (net.Listener, error) {
|
||||||
|
if err := validateConfig(config); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &listener{
|
||||||
|
config: config,
|
||||||
|
parent: inner,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewListenerWithOptions creates a DTLS listener which accepts connections from an inner Listener.
|
||||||
|
func NewListenerWithOptions(inner dtlsnet.PacketListener, opts ...ServerOption) (net.Listener, error) {
|
||||||
|
config, err := buildServerConfig(opts...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return NewListener(inner, config)
|
||||||
|
}
|
||||||
|
|
||||||
|
// listener represents a DTLS listener.
|
||||||
|
type listener struct {
|
||||||
|
config *Config
|
||||||
|
parent dtlsnet.PacketListener
|
||||||
|
}
|
||||||
|
|
||||||
|
// Accept waits for and returns the next connection to the listener.
|
||||||
|
// You have to either close or read on all connection that are created.
|
||||||
|
func (l *listener) Accept() (net.Conn, error) {
|
||||||
|
c, raddr, err := l.parent.Accept()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return serverWithConfig(c, raddr, l.config)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close closes the listener.
|
||||||
|
// Any blocked Accept operations will be unblocked and return errors.
|
||||||
|
// Already Accepted connections are not closed.
|
||||||
|
func (l *listener) Close() error {
|
||||||
|
return l.parent.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Addr returns the listener's network address.
|
||||||
|
func (l *listener) Addr() net.Addr {
|
||||||
|
return l.parent.Addr()
|
||||||
|
}
|
||||||
656
vendor/github.com/pion/dtls/v3/options.go
generated
vendored
Normal file
656
vendor/github.com/pion/dtls/v3/options.go
generated
vendored
Normal file
|
|
@ -0,0 +1,656 @@
|
||||||
|
// SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package dtls
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/tls"
|
||||||
|
"crypto/x509"
|
||||||
|
"io"
|
||||||
|
"net"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/pion/dtls/v3/pkg/crypto/elliptic"
|
||||||
|
"github.com/pion/dtls/v3/pkg/protocol/handshake"
|
||||||
|
"github.com/pion/logging"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ServerOption configures a DTLS server.
|
||||||
|
type ServerOption interface {
|
||||||
|
applyServer(*dtlsConfig) error
|
||||||
|
}
|
||||||
|
|
||||||
|
// ClientOption configures a DTLS client.
|
||||||
|
type ClientOption interface {
|
||||||
|
applyClient(*dtlsConfig) error
|
||||||
|
}
|
||||||
|
|
||||||
|
// Option is an option that can be used with both client and server.
|
||||||
|
// This is used for options that apply to both sides of a connection,
|
||||||
|
// such as in the Resume function where the side is determined at runtime.
|
||||||
|
type Option interface {
|
||||||
|
ServerOption
|
||||||
|
ClientOption
|
||||||
|
}
|
||||||
|
|
||||||
|
// defensiveCopy copies a slice. This prevents the caller from mutating
|
||||||
|
// the config after construction. Returns empty slice if input is empty.
|
||||||
|
func defensiveCopy[T any](t ...T) []T {
|
||||||
|
return append([]T{}, t...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// dtlsConfig is the internal configuration structure.
|
||||||
|
// This will eventually replace the exported Config struct.
|
||||||
|
type dtlsConfig struct { //nolint:dupl
|
||||||
|
certificates []tls.Certificate
|
||||||
|
cipherSuites []CipherSuiteID
|
||||||
|
customCipherSuites func() []CipherSuite
|
||||||
|
signatureSchemes []tls.SignatureScheme
|
||||||
|
certificateSignatureSchemes []tls.SignatureScheme
|
||||||
|
srtpProtectionProfiles []SRTPProtectionProfile
|
||||||
|
srtpMasterKeyIdentifier []byte
|
||||||
|
clientAuth ClientAuthType
|
||||||
|
extendedMasterSecret ExtendedMasterSecretType
|
||||||
|
flightInterval time.Duration
|
||||||
|
disableRetransmitBackoff bool
|
||||||
|
psk PSKCallback
|
||||||
|
pskIdentityHint []byte
|
||||||
|
insecureSkipVerify bool
|
||||||
|
insecureHashes bool
|
||||||
|
verifyPeerCertificate func(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error
|
||||||
|
verifyConnection func(*State) error
|
||||||
|
rootCAs *x509.CertPool
|
||||||
|
clientCAs *x509.CertPool
|
||||||
|
serverName string
|
||||||
|
loggerFactory logging.LoggerFactory
|
||||||
|
mtu int
|
||||||
|
replayProtectionWindow int
|
||||||
|
keyLogWriter io.Writer
|
||||||
|
sessionStore SessionStore
|
||||||
|
supportedProtocols []string
|
||||||
|
ellipticCurves []elliptic.Curve
|
||||||
|
getCertificate func(*ClientHelloInfo) (*tls.Certificate, error)
|
||||||
|
getClientCertificate func(*CertificateRequestInfo) (*tls.Certificate, error)
|
||||||
|
insecureSkipVerifyHello bool
|
||||||
|
connectionIDGenerator func() []byte
|
||||||
|
paddingLengthGenerator func(uint) uint
|
||||||
|
helloRandomBytesGenerator func() [handshake.RandomBytesLength]byte
|
||||||
|
clientHelloMessageHook func(handshake.MessageClientHello) handshake.Message
|
||||||
|
serverHelloMessageHook func(handshake.MessageServerHello) handshake.Message
|
||||||
|
certificateRequestMessageHook func(handshake.MessageCertificateRequest) handshake.Message
|
||||||
|
onConnectionAttempt func(net.Addr) error
|
||||||
|
}
|
||||||
|
|
||||||
|
// applyDefaults applies default values to the config.
|
||||||
|
func (c *dtlsConfig) applyDefaults() {
|
||||||
|
c.extendedMasterSecret = RequestExtendedMasterSecret
|
||||||
|
c.flightInterval = time.Second
|
||||||
|
c.mtu = defaultMTU
|
||||||
|
c.replayProtectionWindow = defaultReplayProtectionWindow
|
||||||
|
}
|
||||||
|
|
||||||
|
// toConfig converts internal dtlsConfig to the exported Config struct.
|
||||||
|
// This is for backward compatibility and will be removed when Config is deprecated.
|
||||||
|
// All slice fields are copied to ensure immutability.
|
||||||
|
func (c *dtlsConfig) toConfig() *Config {
|
||||||
|
config := &Config{
|
||||||
|
CustomCipherSuites: c.customCipherSuites,
|
||||||
|
ClientAuth: c.clientAuth,
|
||||||
|
ExtendedMasterSecret: c.extendedMasterSecret,
|
||||||
|
FlightInterval: c.flightInterval,
|
||||||
|
DisableRetransmitBackoff: c.disableRetransmitBackoff,
|
||||||
|
PSK: c.psk,
|
||||||
|
InsecureSkipVerify: c.insecureSkipVerify,
|
||||||
|
InsecureHashes: c.insecureHashes,
|
||||||
|
VerifyPeerCertificate: c.verifyPeerCertificate,
|
||||||
|
VerifyConnection: c.verifyConnection,
|
||||||
|
RootCAs: c.rootCAs,
|
||||||
|
ClientCAs: c.clientCAs,
|
||||||
|
ServerName: c.serverName,
|
||||||
|
LoggerFactory: c.loggerFactory,
|
||||||
|
MTU: c.mtu,
|
||||||
|
ReplayProtectionWindow: c.replayProtectionWindow,
|
||||||
|
KeyLogWriter: c.keyLogWriter,
|
||||||
|
SessionStore: c.sessionStore,
|
||||||
|
GetCertificate: c.getCertificate,
|
||||||
|
GetClientCertificate: c.getClientCertificate,
|
||||||
|
InsecureSkipVerifyHello: c.insecureSkipVerifyHello,
|
||||||
|
ConnectionIDGenerator: c.connectionIDGenerator,
|
||||||
|
PaddingLengthGenerator: c.paddingLengthGenerator,
|
||||||
|
HelloRandomBytesGenerator: c.helloRandomBytesGenerator,
|
||||||
|
ClientHelloMessageHook: c.clientHelloMessageHook,
|
||||||
|
ServerHelloMessageHook: c.serverHelloMessageHook,
|
||||||
|
CertificateRequestMessageHook: c.certificateRequestMessageHook,
|
||||||
|
OnConnectionAttempt: c.onConnectionAttempt,
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(c.certificates) > 0 {
|
||||||
|
config.Certificates = append([]tls.Certificate(nil), c.certificates...)
|
||||||
|
}
|
||||||
|
if len(c.cipherSuites) > 0 {
|
||||||
|
config.CipherSuites = append([]CipherSuiteID(nil), c.cipherSuites...)
|
||||||
|
}
|
||||||
|
if len(c.signatureSchemes) > 0 {
|
||||||
|
config.SignatureSchemes = append([]tls.SignatureScheme(nil), c.signatureSchemes...)
|
||||||
|
}
|
||||||
|
if len(c.certificateSignatureSchemes) > 0 {
|
||||||
|
config.CertificateSignatureSchemes = append([]tls.SignatureScheme(nil), c.certificateSignatureSchemes...)
|
||||||
|
}
|
||||||
|
if len(c.srtpProtectionProfiles) > 0 {
|
||||||
|
config.SRTPProtectionProfiles = append([]SRTPProtectionProfile(nil), c.srtpProtectionProfiles...)
|
||||||
|
}
|
||||||
|
if len(c.srtpMasterKeyIdentifier) > 0 {
|
||||||
|
config.SRTPMasterKeyIdentifier = append([]byte(nil), c.srtpMasterKeyIdentifier...)
|
||||||
|
}
|
||||||
|
if len(c.pskIdentityHint) > 0 {
|
||||||
|
config.PSKIdentityHint = append([]byte(nil), c.pskIdentityHint...)
|
||||||
|
}
|
||||||
|
if len(c.supportedProtocols) > 0 {
|
||||||
|
config.SupportedProtocols = append([]string(nil), c.supportedProtocols...)
|
||||||
|
}
|
||||||
|
if len(c.ellipticCurves) > 0 {
|
||||||
|
config.EllipticCurves = append([]elliptic.Curve(nil), c.ellipticCurves...)
|
||||||
|
}
|
||||||
|
|
||||||
|
return config
|
||||||
|
}
|
||||||
|
|
||||||
|
// buildConfig builds a Config from the provided options, for mixed client/server cases.
|
||||||
|
func buildConfig(opts ...Option) (*Config, error) {
|
||||||
|
cfg := &dtlsConfig{}
|
||||||
|
cfg.applyDefaults()
|
||||||
|
|
||||||
|
for _, opt := range opts {
|
||||||
|
if err := opt.applyServer(cfg); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return cfg.toConfig(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// buildServerConfig builds a Config for server from the provided options.
|
||||||
|
func buildServerConfig(opts ...ServerOption) (*Config, error) {
|
||||||
|
cfg := &dtlsConfig{}
|
||||||
|
cfg.applyDefaults()
|
||||||
|
|
||||||
|
for _, opt := range opts {
|
||||||
|
if err := opt.applyServer(cfg); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return cfg.toConfig(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// buildClientConfig builds a Config for client from the provided options.
|
||||||
|
func buildClientConfig(opts ...ClientOption) (*Config, error) {
|
||||||
|
cfg := &dtlsConfig{}
|
||||||
|
cfg.applyDefaults()
|
||||||
|
|
||||||
|
for _, opt := range opts {
|
||||||
|
if err := opt.applyClient(cfg); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return cfg.toConfig(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// sharedOption wraps an apply function that works for both client and server.
|
||||||
|
// This eliminates code duplication for options that behave identically on both sides.
|
||||||
|
type sharedOption func(*dtlsConfig) error
|
||||||
|
|
||||||
|
func (o sharedOption) applyServer(c *dtlsConfig) error { return o(c) }
|
||||||
|
func (o sharedOption) applyClient(c *dtlsConfig) error { return o(c) }
|
||||||
|
|
||||||
|
// WithCertificates sets the certificate chain to present to the other side of the connection.
|
||||||
|
// For functional options, an explicitly empty slice is not allowed.
|
||||||
|
func WithCertificates(certs ...tls.Certificate) Option {
|
||||||
|
return sharedOption(func(c *dtlsConfig) error {
|
||||||
|
if len(certs) == 0 {
|
||||||
|
return errEmptyCertificates
|
||||||
|
}
|
||||||
|
c.certificates = defensiveCopy(certs...)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithCipherSuites sets the supported cipher suites.
|
||||||
|
// For functional options, an explicitly empty slice is not allowed.
|
||||||
|
func WithCipherSuites(suites ...CipherSuiteID) Option {
|
||||||
|
return sharedOption(func(c *dtlsConfig) error {
|
||||||
|
if len(suites) == 0 {
|
||||||
|
return errEmptyCipherSuites
|
||||||
|
}
|
||||||
|
c.cipherSuites = defensiveCopy(suites...)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithCustomCipherSuites sets the custom cipher suites provider.
|
||||||
|
// Returns an error if the provider is nil.
|
||||||
|
func WithCustomCipherSuites(fn func() []CipherSuite) Option {
|
||||||
|
return sharedOption(func(c *dtlsConfig) error {
|
||||||
|
if fn == nil {
|
||||||
|
return errNilCustomCipherSuites
|
||||||
|
}
|
||||||
|
c.customCipherSuites = fn
|
||||||
|
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithSignatureSchemes sets the signature schemes.
|
||||||
|
// For functional options, an explicitly empty slice is not allowed.
|
||||||
|
func WithSignatureSchemes(schemes ...tls.SignatureScheme) Option {
|
||||||
|
return sharedOption(func(c *dtlsConfig) error {
|
||||||
|
if len(schemes) == 0 {
|
||||||
|
return errEmptySignatureSchemes
|
||||||
|
}
|
||||||
|
c.signatureSchemes = defensiveCopy(schemes...)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithCertificateSignatureSchemes sets the signature and hash schemes that may be used
|
||||||
|
// in digital signatures for X.509 certificates. If not set, the signature_algorithms_cert
|
||||||
|
// extension is not sent, and SignatureSchemes is used for both handshake signatures and
|
||||||
|
// certificate chain validation, as specified in RFC 8446 Section 4.2.3.
|
||||||
|
// For functional options, an explicitly empty slice is not allowed.
|
||||||
|
func WithCertificateSignatureSchemes(schemes ...tls.SignatureScheme) Option {
|
||||||
|
return sharedOption(func(c *dtlsConfig) error {
|
||||||
|
if len(schemes) == 0 {
|
||||||
|
return errEmptyCertificateSignatureSchemes
|
||||||
|
}
|
||||||
|
c.certificateSignatureSchemes = defensiveCopy(schemes...)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithSRTPProtectionProfiles sets the SRTP protection profiles.
|
||||||
|
// For functional options, an explicitly empty slice is not allowed.
|
||||||
|
func WithSRTPProtectionProfiles(profiles ...SRTPProtectionProfile) Option {
|
||||||
|
return sharedOption(func(c *dtlsConfig) error {
|
||||||
|
if len(profiles) == 0 {
|
||||||
|
return errEmptySRTPProtectionProfiles
|
||||||
|
}
|
||||||
|
c.srtpProtectionProfiles = defensiveCopy(profiles...)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithSRTPMasterKeyIdentifier sets the SRTP master key identifier.
|
||||||
|
func WithSRTPMasterKeyIdentifier(identifier []byte) Option {
|
||||||
|
return sharedOption(func(c *dtlsConfig) error {
|
||||||
|
c.srtpMasterKeyIdentifier = defensiveCopy(identifier...)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithExtendedMasterSecret sets the extended master secret policy.
|
||||||
|
// Returns an error if the type is invalid.
|
||||||
|
func WithExtendedMasterSecret(ems ExtendedMasterSecretType) Option {
|
||||||
|
return sharedOption(func(c *dtlsConfig) error {
|
||||||
|
if ems < RequestExtendedMasterSecret || ems > DisableExtendedMasterSecret {
|
||||||
|
return errInvalidExtendedMasterSecretType
|
||||||
|
}
|
||||||
|
c.extendedMasterSecret = ems
|
||||||
|
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithFlightInterval sets the flight interval for handshake messages.
|
||||||
|
// Returns an error if the interval is not positive.
|
||||||
|
func WithFlightInterval(interval time.Duration) Option {
|
||||||
|
return sharedOption(func(c *dtlsConfig) error {
|
||||||
|
if interval <= 0 {
|
||||||
|
return errInvalidFlightInterval
|
||||||
|
}
|
||||||
|
c.flightInterval = interval
|
||||||
|
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithDisableRetransmitBackoff disables retransmit backoff.
|
||||||
|
func WithDisableRetransmitBackoff(disable bool) Option {
|
||||||
|
return sharedOption(func(c *dtlsConfig) error {
|
||||||
|
c.disableRetransmitBackoff = disable
|
||||||
|
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithPSK sets the pre-shared key callback.
|
||||||
|
// Returns an error if the callback is nil.
|
||||||
|
func WithPSK(callback PSKCallback) Option {
|
||||||
|
return sharedOption(func(c *dtlsConfig) error {
|
||||||
|
if callback == nil {
|
||||||
|
return errNilPSKCallback
|
||||||
|
}
|
||||||
|
c.psk = callback
|
||||||
|
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithPSKIdentityHint sets the PSK identity hint.
|
||||||
|
func WithPSKIdentityHint(hint []byte) Option {
|
||||||
|
return sharedOption(func(c *dtlsConfig) error {
|
||||||
|
c.pskIdentityHint = defensiveCopy(hint...)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithInsecureSkipVerify skips certificate verification.
|
||||||
|
// This should only be used for testing.
|
||||||
|
func WithInsecureSkipVerify(skip bool) Option {
|
||||||
|
return sharedOption(func(c *dtlsConfig) error {
|
||||||
|
c.insecureSkipVerify = skip
|
||||||
|
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithInsecureHashes allows the use of insecure hash algorithms.
|
||||||
|
func WithInsecureHashes(allow bool) Option {
|
||||||
|
return sharedOption(func(c *dtlsConfig) error {
|
||||||
|
c.insecureHashes = allow
|
||||||
|
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithVerifyPeerCertificate sets the peer certificate verification callback.
|
||||||
|
// Returns an error if the callback is nil.
|
||||||
|
func WithVerifyPeerCertificate(fn func(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error) Option {
|
||||||
|
return sharedOption(func(c *dtlsConfig) error {
|
||||||
|
if fn == nil {
|
||||||
|
return errNilVerifyPeerCertificate
|
||||||
|
}
|
||||||
|
c.verifyPeerCertificate = fn
|
||||||
|
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithVerifyConnection sets the connection verification callback.
|
||||||
|
// Returns an error if the callback is nil.
|
||||||
|
func WithVerifyConnection(fn func(*State) error) Option {
|
||||||
|
return sharedOption(func(c *dtlsConfig) error {
|
||||||
|
if fn == nil {
|
||||||
|
return errNilVerifyConnection
|
||||||
|
}
|
||||||
|
c.verifyConnection = fn
|
||||||
|
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithRootCAs sets the root certificate authorities.
|
||||||
|
func WithRootCAs(pool *x509.CertPool) Option {
|
||||||
|
return sharedOption(func(c *dtlsConfig) error {
|
||||||
|
c.rootCAs = pool
|
||||||
|
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithServerName sets the server name for certificate verification.
|
||||||
|
func WithServerName(name string) Option {
|
||||||
|
return sharedOption(func(c *dtlsConfig) error {
|
||||||
|
c.serverName = name
|
||||||
|
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithLoggerFactory sets the logger factory for creating loggers.
|
||||||
|
func WithLoggerFactory(factory logging.LoggerFactory) Option {
|
||||||
|
return sharedOption(func(c *dtlsConfig) error {
|
||||||
|
c.loggerFactory = factory
|
||||||
|
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithMTU sets the maximum transmission unit.
|
||||||
|
// Returns an error if the MTU is not positive.
|
||||||
|
func WithMTU(mtu int) Option {
|
||||||
|
return sharedOption(func(c *dtlsConfig) error {
|
||||||
|
if mtu <= 0 {
|
||||||
|
return errInvalidMTU
|
||||||
|
}
|
||||||
|
c.mtu = mtu
|
||||||
|
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithReplayProtectionWindow sets the replay protection window size.
|
||||||
|
// Returns an error if the window size is negative.
|
||||||
|
func WithReplayProtectionWindow(window int) Option {
|
||||||
|
return sharedOption(func(c *dtlsConfig) error {
|
||||||
|
if window < 0 {
|
||||||
|
return errInvalidReplayProtectionWindow
|
||||||
|
}
|
||||||
|
c.replayProtectionWindow = window
|
||||||
|
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithKeyLogWriter sets the key log writer for debugging.
|
||||||
|
// Use of KeyLogWriter compromises security and should only be used for debugging.
|
||||||
|
func WithKeyLogWriter(writer io.Writer) Option {
|
||||||
|
return sharedOption(func(c *dtlsConfig) error {
|
||||||
|
c.keyLogWriter = writer
|
||||||
|
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithSessionStore sets the session store for resumption.
|
||||||
|
func WithSessionStore(store SessionStore) Option {
|
||||||
|
return sharedOption(func(c *dtlsConfig) error {
|
||||||
|
c.sessionStore = store
|
||||||
|
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithSupportedProtocols sets the supported application protocols for ALPN.
|
||||||
|
// For functional options, an explicitly empty slice is not allowed.
|
||||||
|
func WithSupportedProtocols(protocols ...string) Option {
|
||||||
|
return sharedOption(func(c *dtlsConfig) error {
|
||||||
|
if len(protocols) == 0 {
|
||||||
|
return errEmptySupportedProtocols
|
||||||
|
}
|
||||||
|
c.supportedProtocols = defensiveCopy(protocols...)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithEllipticCurves sets the elliptic curves.
|
||||||
|
// For functional options, an explicitly empty slice is not allowed.
|
||||||
|
func WithEllipticCurves(curves ...elliptic.Curve) Option {
|
||||||
|
return sharedOption(func(c *dtlsConfig) error {
|
||||||
|
if len(curves) == 0 {
|
||||||
|
return errEmptyEllipticCurves
|
||||||
|
}
|
||||||
|
c.ellipticCurves = defensiveCopy(curves...)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithGetClientCertificate sets the client certificate getter callback.
|
||||||
|
// Returns an error if the callback is nil.
|
||||||
|
func WithGetClientCertificate(fn func(*CertificateRequestInfo) (*tls.Certificate, error)) Option {
|
||||||
|
return sharedOption(func(c *dtlsConfig) error {
|
||||||
|
if fn == nil {
|
||||||
|
return errNilGetClientCertificate
|
||||||
|
}
|
||||||
|
c.getClientCertificate = fn
|
||||||
|
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithConnectionIDGenerator sets the connection ID generator.
|
||||||
|
// Returns an error if the generator is nil.
|
||||||
|
func WithConnectionIDGenerator(fn func() []byte) Option {
|
||||||
|
return sharedOption(func(c *dtlsConfig) error {
|
||||||
|
if fn == nil {
|
||||||
|
return errNilConnectionIDGenerator
|
||||||
|
}
|
||||||
|
c.connectionIDGenerator = fn
|
||||||
|
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithPaddingLengthGenerator sets the padding length generator.
|
||||||
|
// Returns an error if the generator is nil.
|
||||||
|
func WithPaddingLengthGenerator(fn func(uint) uint) Option {
|
||||||
|
return sharedOption(func(c *dtlsConfig) error {
|
||||||
|
if fn == nil {
|
||||||
|
return errNilPaddingLengthGenerator
|
||||||
|
}
|
||||||
|
c.paddingLengthGenerator = fn
|
||||||
|
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithHelloRandomBytesGenerator sets the hello random bytes generator.
|
||||||
|
// Returns an error if the generator is nil.
|
||||||
|
func WithHelloRandomBytesGenerator(fn func() [handshake.RandomBytesLength]byte) Option {
|
||||||
|
return sharedOption(func(c *dtlsConfig) error {
|
||||||
|
if fn == nil {
|
||||||
|
return errNilHelloRandomBytesGenerator
|
||||||
|
}
|
||||||
|
c.helloRandomBytesGenerator = fn
|
||||||
|
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithClientHelloMessageHook sets the client hello message hook.
|
||||||
|
// Returns an error if the hook is nil.
|
||||||
|
func WithClientHelloMessageHook(fn func(handshake.MessageClientHello) handshake.Message) Option {
|
||||||
|
return sharedOption(func(c *dtlsConfig) error {
|
||||||
|
if fn == nil {
|
||||||
|
return errNilClientHelloMessageHook
|
||||||
|
}
|
||||||
|
c.clientHelloMessageHook = fn
|
||||||
|
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// serverOnlyOption wraps an apply function for server-only options.
|
||||||
|
type serverOnlyOption func(*dtlsConfig) error
|
||||||
|
|
||||||
|
func (o serverOnlyOption) applyServer(c *dtlsConfig) error { return o(c) }
|
||||||
|
|
||||||
|
// WithClientAuth sets the client authentication policy.
|
||||||
|
// Returns an error if the type is invalid.
|
||||||
|
// This option is only applicable to servers.
|
||||||
|
func WithClientAuth(auth ClientAuthType) ServerOption {
|
||||||
|
return serverOnlyOption(func(c *dtlsConfig) error {
|
||||||
|
if auth < NoClientCert || auth > RequireAndVerifyClientCert {
|
||||||
|
return errInvalidClientAuthType
|
||||||
|
}
|
||||||
|
c.clientAuth = auth
|
||||||
|
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithClientCAs sets the client certificate authorities.
|
||||||
|
// This option is only applicable to servers.
|
||||||
|
func WithClientCAs(pool *x509.CertPool) ServerOption {
|
||||||
|
return serverOnlyOption(func(c *dtlsConfig) error {
|
||||||
|
c.clientCAs = pool
|
||||||
|
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithGetCertificate sets the certificate getter callback.
|
||||||
|
// Returns an error if the callback is nil.
|
||||||
|
// This option is only applicable to servers.
|
||||||
|
func WithGetCertificate(fn func(*ClientHelloInfo) (*tls.Certificate, error)) ServerOption {
|
||||||
|
return serverOnlyOption(func(c *dtlsConfig) error {
|
||||||
|
if fn == nil {
|
||||||
|
return errNilGetCertificate
|
||||||
|
}
|
||||||
|
c.getCertificate = fn
|
||||||
|
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithInsecureSkipVerifyHello skips hello verify phase on the server.
|
||||||
|
// This has implication on DoS attack resistance.
|
||||||
|
// This option is only applicable to servers.
|
||||||
|
func WithInsecureSkipVerifyHello(skip bool) ServerOption {
|
||||||
|
return serverOnlyOption(func(c *dtlsConfig) error {
|
||||||
|
c.insecureSkipVerifyHello = skip
|
||||||
|
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithServerHelloMessageHook sets the server hello message hook.
|
||||||
|
// Returns an error if the hook is nil.
|
||||||
|
// This option is only applicable to servers.
|
||||||
|
func WithServerHelloMessageHook(fn func(handshake.MessageServerHello) handshake.Message) ServerOption {
|
||||||
|
return serverOnlyOption(func(c *dtlsConfig) error {
|
||||||
|
if fn == nil {
|
||||||
|
return errNilServerHelloMessageHook
|
||||||
|
}
|
||||||
|
c.serverHelloMessageHook = fn
|
||||||
|
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithCertificateRequestMessageHook sets the certificate request message hook.
|
||||||
|
// Returns an error if the hook is nil.
|
||||||
|
// This option is only applicable to servers.
|
||||||
|
func WithCertificateRequestMessageHook(fn func(handshake.MessageCertificateRequest) handshake.Message) ServerOption {
|
||||||
|
return serverOnlyOption(func(c *dtlsConfig) error {
|
||||||
|
if fn == nil {
|
||||||
|
return errNilCertificateRequestMessageHook
|
||||||
|
}
|
||||||
|
c.certificateRequestMessageHook = fn
|
||||||
|
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithOnConnectionAttempt sets the connection attempt callback.
|
||||||
|
// Returns an error if the callback is nil.
|
||||||
|
// This option is only applicable to servers.
|
||||||
|
func WithOnConnectionAttempt(fn func(net.Addr) error) ServerOption {
|
||||||
|
return serverOnlyOption(func(c *dtlsConfig) error {
|
||||||
|
if fn == nil {
|
||||||
|
return errNilOnConnectionAttempt
|
||||||
|
}
|
||||||
|
c.onConnectionAttempt = fn
|
||||||
|
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
15
vendor/github.com/pion/dtls/v3/packet.go
generated
vendored
Normal file
15
vendor/github.com/pion/dtls/v3/packet.go
generated
vendored
Normal file
|
|
@ -0,0 +1,15 @@
|
||||||
|
// SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package dtls
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/pion/dtls/v3/pkg/protocol/recordlayer"
|
||||||
|
)
|
||||||
|
|
||||||
|
type packet struct {
|
||||||
|
record *recordlayer.RecordLayer
|
||||||
|
shouldEncrypt bool
|
||||||
|
shouldWrapCID bool
|
||||||
|
resetLocalSequenceNumber bool
|
||||||
|
}
|
||||||
260
vendor/github.com/pion/dtls/v3/pkg/crypto/ccm/ccm.go
generated
vendored
Normal file
260
vendor/github.com/pion/dtls/v3/pkg/crypto/ccm/ccm.go
generated
vendored
Normal file
|
|
@ -0,0 +1,260 @@
|
||||||
|
// SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
// Package ccm implements a CCM, Counter with CBC-MAC
|
||||||
|
// as per RFC 3610.
|
||||||
|
//
|
||||||
|
// See https://tools.ietf.org/html/rfc3610
|
||||||
|
//
|
||||||
|
// This code was lifted from https://github.com/bocajim/dtls/blob/a3300364a283fcb490d28a93d7fcfa7ba437fbbe/ccm/ccm.go
|
||||||
|
// and as such was not written by the Pions authors. Like Pions this
|
||||||
|
// code is licensed under MIT.
|
||||||
|
//
|
||||||
|
// A request for including CCM into the Go standard library
|
||||||
|
// can be found as issue #27484 on the https://github.com/golang/go/
|
||||||
|
// repository.
|
||||||
|
package ccm
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/cipher"
|
||||||
|
"crypto/subtle"
|
||||||
|
"encoding/binary"
|
||||||
|
"errors"
|
||||||
|
"math"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ccm represents a Counter with CBC-MAC with a specific key.
|
||||||
|
type ccm struct {
|
||||||
|
b cipher.Block
|
||||||
|
M uint8
|
||||||
|
L uint8
|
||||||
|
}
|
||||||
|
|
||||||
|
const ccmBlockSize = 16
|
||||||
|
|
||||||
|
// CCM is a block cipher in Counter with CBC-MAC mode.
|
||||||
|
// Providing authenticated encryption with associated data via the cipher.AEAD interface.
|
||||||
|
type CCM interface {
|
||||||
|
cipher.AEAD
|
||||||
|
// MaxLength returns the maxium length of plaintext in calls to Seal.
|
||||||
|
// The maximum length of ciphertext in calls to Open is MaxLength()+Overhead().
|
||||||
|
// The maximum length is related to CCM's `L` parameter (15-noncesize) and
|
||||||
|
// is 1<<(8*L) - 1 (but also limited by the maxium size of an int).
|
||||||
|
MaxLength() int
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
errInvalidBlockSize = errors.New("ccm: NewCCM requires 128-bit block cipher")
|
||||||
|
errInvalidTagSize = errors.New("ccm: tagsize must be 4, 6, 8, 10, 12, 14, or 16")
|
||||||
|
errInvalidNonceSize = errors.New("ccm: invalid nonce size")
|
||||||
|
)
|
||||||
|
|
||||||
|
// NewCCM returns the given 128-bit block cipher wrapped in CCM.
|
||||||
|
// The tagsize must be an even integer between 4 and 16 inclusive
|
||||||
|
// and is used as CCM's `M` parameter.
|
||||||
|
// The noncesize must be an integer between 7 and 13 inclusive,
|
||||||
|
// 15-noncesize is used as CCM's `L` parameter.
|
||||||
|
func NewCCM(b cipher.Block, tagsize, noncesize int) (CCM, error) {
|
||||||
|
if b.BlockSize() != ccmBlockSize {
|
||||||
|
return nil, errInvalidBlockSize
|
||||||
|
}
|
||||||
|
if tagsize < 4 || tagsize > 16 || tagsize&1 != 0 {
|
||||||
|
return nil, errInvalidTagSize
|
||||||
|
}
|
||||||
|
lensize := 15 - noncesize
|
||||||
|
if lensize < 2 || lensize > 8 {
|
||||||
|
return nil, errInvalidNonceSize
|
||||||
|
}
|
||||||
|
c := &ccm{b: b, M: uint8(tagsize), L: uint8(lensize)} //nolint:gosec // G114
|
||||||
|
|
||||||
|
return c, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *ccm) NonceSize() int { return 15 - int(c.L) }
|
||||||
|
func (c *ccm) Overhead() int { return int(c.M) }
|
||||||
|
func (c *ccm) MaxLength() int { return maxlen(c.L, c.Overhead()) }
|
||||||
|
|
||||||
|
func maxlen(l uint8, tagsize int) int {
|
||||||
|
mLen := (uint64(1) << (8 * l)) - 1
|
||||||
|
if m64 := uint64(math.MaxInt64) - uint64(tagsize); l > 8 || mLen > m64 { //nolint:gosec // G114
|
||||||
|
mLen = m64 // The maximum lentgh on a 64bit arch
|
||||||
|
}
|
||||||
|
if mLen != uint64(int(mLen)) { //nolint:gosec // G114
|
||||||
|
return math.MaxInt32 - tagsize // We have only 32bit int's
|
||||||
|
}
|
||||||
|
|
||||||
|
return int(mLen) //nolint:gosec // G114
|
||||||
|
}
|
||||||
|
|
||||||
|
// MaxNonceLength returns the maximum nonce length for a given plaintext length.
|
||||||
|
// A return value <= 0 indicates that plaintext length is too large for
|
||||||
|
// any nonce length.
|
||||||
|
func MaxNonceLength(pdatalen int) int {
|
||||||
|
const tagsize = 16
|
||||||
|
for L := 2; L <= 8; L++ {
|
||||||
|
if maxlen(uint8(L), tagsize) >= pdatalen { //nolint:gosec // G115
|
||||||
|
return 15 - L
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *ccm) cbcRound(mac, data []byte) {
|
||||||
|
for i := 0; i < ccmBlockSize; i++ {
|
||||||
|
mac[i] ^= data[i]
|
||||||
|
}
|
||||||
|
c.b.Encrypt(mac, mac)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *ccm) cbcData(mac, data []byte) {
|
||||||
|
for len(data) >= ccmBlockSize {
|
||||||
|
c.cbcRound(mac, data[:ccmBlockSize])
|
||||||
|
data = data[ccmBlockSize:]
|
||||||
|
}
|
||||||
|
if len(data) > 0 {
|
||||||
|
var block [ccmBlockSize]byte
|
||||||
|
copy(block[:], data)
|
||||||
|
c.cbcRound(mac, block[:])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var errPlaintextTooLong = errors.New("ccm: plaintext too large")
|
||||||
|
|
||||||
|
func (c *ccm) tag(nonce, plaintext, adata []byte) ([]byte, error) {
|
||||||
|
var mac [ccmBlockSize]byte
|
||||||
|
|
||||||
|
if len(adata) > 0 {
|
||||||
|
mac[0] |= 1 << 6
|
||||||
|
}
|
||||||
|
mac[0] |= (c.M - 2) << 2
|
||||||
|
mac[0] |= c.L - 1
|
||||||
|
if len(nonce) != c.NonceSize() {
|
||||||
|
return nil, errInvalidNonceSize
|
||||||
|
}
|
||||||
|
if len(plaintext) > c.MaxLength() {
|
||||||
|
return nil, errPlaintextTooLong
|
||||||
|
}
|
||||||
|
binary.BigEndian.PutUint64(mac[ccmBlockSize-8:], uint64(len(plaintext)))
|
||||||
|
copy(mac[1:ccmBlockSize-c.L], nonce)
|
||||||
|
c.b.Encrypt(mac[:], mac[:])
|
||||||
|
|
||||||
|
var block [ccmBlockSize]byte
|
||||||
|
if adataLength := uint64(len(adata)); adataLength > 0 { //nolint:nestif
|
||||||
|
// First adata block includes adata length
|
||||||
|
i := 2
|
||||||
|
if adataLength <= 0xfeff {
|
||||||
|
binary.BigEndian.PutUint16(block[:i], uint16(adataLength))
|
||||||
|
} else {
|
||||||
|
binary.BigEndian.PutUint16(block[0:2], 0xfeff)
|
||||||
|
if adataLength < uint64(1<<32) {
|
||||||
|
i = 2 + 4
|
||||||
|
binary.BigEndian.PutUint32(block[2:i], uint32(adataLength)) //nolint:gosec // G115
|
||||||
|
} else {
|
||||||
|
i = 2 + 8
|
||||||
|
binary.BigEndian.PutUint64(block[2:i], adataLength)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
i = copy(block[i:], adata)
|
||||||
|
c.cbcRound(mac[:], block[:])
|
||||||
|
c.cbcData(mac[:], adata[i:])
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(plaintext) > 0 {
|
||||||
|
c.cbcData(mac[:], plaintext)
|
||||||
|
}
|
||||||
|
|
||||||
|
return mac[:c.M], nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// sliceForAppend takes a slice and a requested number of bytes. It returns a
|
||||||
|
// slice with the contents of the given slice followed by that many bytes and a
|
||||||
|
// second slice that aliases into it and contains only the extra bytes. If the
|
||||||
|
// original slice has sufficient capacity then no allocation is performed.
|
||||||
|
// From crypto/cipher/gcm.go
|
||||||
|
// .
|
||||||
|
func sliceForAppend(in []byte, n int) (head, tail []byte) {
|
||||||
|
if total := len(in) + n; cap(in) >= total {
|
||||||
|
head = in[:total]
|
||||||
|
} else {
|
||||||
|
head = make([]byte, total)
|
||||||
|
copy(head, in)
|
||||||
|
}
|
||||||
|
tail = head[len(in):]
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Seal encrypts and authenticates plaintext, authenticates the
|
||||||
|
// additional data and appends the result to dst, returning the updated
|
||||||
|
// slice. The nonce must be NonceSize() bytes long and unique for all
|
||||||
|
// time, for a given key.
|
||||||
|
// The plaintext must be no longer than MaxLength() bytes long.
|
||||||
|
//
|
||||||
|
// The plaintext and dst may alias exactly or not at all.
|
||||||
|
func (c *ccm) Seal(dst, nonce, plaintext, adata []byte) []byte {
|
||||||
|
tag, err := c.tag(nonce, plaintext, adata)
|
||||||
|
if err != nil {
|
||||||
|
// The cipher.AEAD interface doesn't allow for an error return.
|
||||||
|
panic(err) // nolint
|
||||||
|
}
|
||||||
|
|
||||||
|
var iv, s0 [ccmBlockSize]byte
|
||||||
|
iv[0] = c.L - 1
|
||||||
|
copy(iv[1:ccmBlockSize-c.L], nonce)
|
||||||
|
c.b.Encrypt(s0[:], iv[:])
|
||||||
|
for i := 0; i < int(c.M); i++ {
|
||||||
|
tag[i] ^= s0[i]
|
||||||
|
}
|
||||||
|
iv[len(iv)-1] |= 1
|
||||||
|
stream := cipher.NewCTR(c.b, iv[:])
|
||||||
|
ret, out := sliceForAppend(dst, len(plaintext)+int(c.M))
|
||||||
|
stream.XORKeyStream(out, plaintext)
|
||||||
|
copy(out[len(plaintext):], tag)
|
||||||
|
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
errOpen = errors.New("ccm: message authentication failed")
|
||||||
|
errCiphertextTooShort = errors.New("ccm: ciphertext too short")
|
||||||
|
errCiphertextTooLong = errors.New("ccm: ciphertext too long")
|
||||||
|
)
|
||||||
|
|
||||||
|
func (c *ccm) Open(dst, nonce, ciphertext, adata []byte) ([]byte, error) {
|
||||||
|
if len(ciphertext) < int(c.M) {
|
||||||
|
return nil, errCiphertextTooShort
|
||||||
|
}
|
||||||
|
if len(ciphertext) > c.MaxLength()+c.Overhead() {
|
||||||
|
return nil, errCiphertextTooLong
|
||||||
|
}
|
||||||
|
|
||||||
|
tag := make([]byte, int(c.M))
|
||||||
|
copy(tag, ciphertext[len(ciphertext)-int(c.M):])
|
||||||
|
ciphertextWithoutTag := ciphertext[:len(ciphertext)-int(c.M)]
|
||||||
|
|
||||||
|
var iv, s0 [ccmBlockSize]byte
|
||||||
|
iv[0] = c.L - 1
|
||||||
|
copy(iv[1:ccmBlockSize-c.L], nonce)
|
||||||
|
c.b.Encrypt(s0[:], iv[:])
|
||||||
|
for i := 0; i < int(c.M); i++ {
|
||||||
|
tag[i] ^= s0[i]
|
||||||
|
}
|
||||||
|
iv[len(iv)-1] |= 1
|
||||||
|
stream := cipher.NewCTR(c.b, iv[:])
|
||||||
|
|
||||||
|
// Cannot decrypt directly to dst since we're not supposed to
|
||||||
|
// reveal the plaintext to the caller if authentication fails.
|
||||||
|
plaintext := make([]byte, len(ciphertextWithoutTag))
|
||||||
|
stream.XORKeyStream(plaintext, ciphertextWithoutTag)
|
||||||
|
expectedTag, err := c.tag(nonce, plaintext, adata)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if subtle.ConstantTimeCompare(tag, expectedTag) != 1 {
|
||||||
|
return nil, errOpen
|
||||||
|
}
|
||||||
|
|
||||||
|
return append(dst, plaintext...), nil
|
||||||
|
}
|
||||||
128
vendor/github.com/pion/dtls/v3/pkg/crypto/ciphersuite/README.md
generated
vendored
Normal file
128
vendor/github.com/pion/dtls/v3/pkg/crypto/ciphersuite/README.md
generated
vendored
Normal file
|
|
@ -0,0 +1,128 @@
|
||||||
|
# Ciphersuite Package
|
||||||
|
|
||||||
|
This package provides DTLS cipher suite implementations for GCM, CCM, and CBC modes.
|
||||||
|
|
||||||
|
## Benchmarking
|
||||||
|
|
||||||
|
The package includes comprehensive benchmarks for all cipher operations across multiple payload sizes.
|
||||||
|
|
||||||
|
**Note:** Benchmarks are excluded from regular test runs using build tags. You must specify `-tags=bench` to run them.
|
||||||
|
|
||||||
|
### Running all ciphersuite benchmarks
|
||||||
|
|
||||||
|
```bash
|
||||||
|
go test -tags=bench -bench=. -benchmem
|
||||||
|
```
|
||||||
|
|
||||||
|
### Running a specific benchmark
|
||||||
|
|
||||||
|
- GCM benchmarks only:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
go test -tags=bench -bench=BenchmarkGCM -benchmem
|
||||||
|
```
|
||||||
|
|
||||||
|
- GCM `Encrypt` benchmark only:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
go test -tags=bench -bench=BenchmarkGCMEncrypt -benchmem
|
||||||
|
```
|
||||||
|
|
||||||
|
- GCM `Decrypt` benchmark only:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
go test -tags=bench -bench=BenchmarkGCMDecrypt -benchmem
|
||||||
|
```
|
||||||
|
|
||||||
|
- CCM benchmarks only:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
go test -tags=bench -bench=BenchmarkCCM -benchmem
|
||||||
|
```
|
||||||
|
|
||||||
|
- CCM `Encrypt` benchmark only:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
go test -tags=bench -bench=BenchmarkCCMEncrypt -benchmem
|
||||||
|
```
|
||||||
|
|
||||||
|
- CCM `Decrypt` benchmark only:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
go test -tags=bench -bench=BenchmarkCCMDecrypt -benchmem
|
||||||
|
```
|
||||||
|
|
||||||
|
- CBC benchmarks only:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
go test -tags=bench -bench=BenchmarkCBC -benchmem
|
||||||
|
```
|
||||||
|
|
||||||
|
- CBC `Encrypt` benchmark only:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
go test -tags=bench -bench=BenchmarkCBCEncrypt -benchmem
|
||||||
|
```
|
||||||
|
|
||||||
|
- CBC `Decrypt` benchmark only:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
go test -tags=bench -bench=BenchmarkCBCDecrypt -benchmem
|
||||||
|
```
|
||||||
|
|
||||||
|
- All ciphers, with 1KB payloads only
|
||||||
|
|
||||||
|
```bash
|
||||||
|
go test -tags=bench -bench=/1KB -benchmem
|
||||||
|
```
|
||||||
|
|
||||||
|
- All ciphers, with 16B payloads only
|
||||||
|
|
||||||
|
```bash
|
||||||
|
go test -tags=bench -bench=/16B -benchmem
|
||||||
|
```
|
||||||
|
|
||||||
|
### Benchmark Options
|
||||||
|
|
||||||
|
Increase benchmark time for more accurate results:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
go test -tags=bench -bench=BenchmarkGCM -benchmem -benchtime=5s
|
||||||
|
```
|
||||||
|
|
||||||
|
Run benchmarks multiple times:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
go test -tags=bench -bench=BenchmarkGCM -benchmem -count=5
|
||||||
|
```
|
||||||
|
|
||||||
|
### Understanding Results
|
||||||
|
|
||||||
|
Example output:
|
||||||
|
|
||||||
|
```
|
||||||
|
BenchmarkGCMEncrypt/016B-8 5895367 202.6 ns/op 78.99 MB/s 160 B/op 5 allocs/op
|
||||||
|
```
|
||||||
|
|
||||||
|
- `5895367`: Number of iterations
|
||||||
|
- `202.6 ns/op`: Time per operation
|
||||||
|
- `78.99 MB/s`: Throughput
|
||||||
|
- `160 B/op`: Bytes allocated per operation
|
||||||
|
- `5 allocs/op`: Number of allocations per operation
|
||||||
|
|
||||||
|
|
||||||
|
## Profiling
|
||||||
|
|
||||||
|
Generate CPU profile:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
go test -tags=bench -bench=BenchmarkGCMEncrypt -benchmem -cpuprofile=cpu.prof
|
||||||
|
go tool pprof -top cpu.prof
|
||||||
|
```
|
||||||
|
|
||||||
|
Generate memory profile:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
go test -tags=bench -bench=BenchmarkGCMEncrypt -benchmem -memprofile=mem.prof
|
||||||
|
go tool pprof -top -alloc_objects mem.prof
|
||||||
|
```
|
||||||
250
vendor/github.com/pion/dtls/v3/pkg/crypto/ciphersuite/cbc.go
generated
vendored
Normal file
250
vendor/github.com/pion/dtls/v3/pkg/crypto/ciphersuite/cbc.go
generated
vendored
Normal file
|
|
@ -0,0 +1,250 @@
|
||||||
|
// SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package ciphersuite
|
||||||
|
|
||||||
|
import ( //nolint:gci
|
||||||
|
"crypto/aes"
|
||||||
|
"crypto/cipher"
|
||||||
|
"crypto/hmac"
|
||||||
|
"crypto/rand"
|
||||||
|
"encoding/binary"
|
||||||
|
"hash"
|
||||||
|
|
||||||
|
"github.com/pion/dtls/v3/internal/util"
|
||||||
|
"github.com/pion/dtls/v3/pkg/crypto/prf"
|
||||||
|
"github.com/pion/dtls/v3/pkg/protocol"
|
||||||
|
"github.com/pion/dtls/v3/pkg/protocol/recordlayer"
|
||||||
|
"golang.org/x/crypto/cryptobyte"
|
||||||
|
)
|
||||||
|
|
||||||
|
// block ciphers using cipher block chaining.
|
||||||
|
type cbcMode interface {
|
||||||
|
cipher.BlockMode
|
||||||
|
SetIV([]byte)
|
||||||
|
}
|
||||||
|
|
||||||
|
// CBC Provides an API to Encrypt/Decrypt DTLS 1.2 Packets.
|
||||||
|
type CBC struct {
|
||||||
|
writeCBC, readCBC cbcMode
|
||||||
|
writeMac, readMac []byte
|
||||||
|
h prf.HashFunc
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewCBC creates a DTLS CBC Cipher.
|
||||||
|
func NewCBC(
|
||||||
|
localKey, localWriteIV, localMac, remoteKey, remoteWriteIV, remoteMac []byte,
|
||||||
|
hashFunc prf.HashFunc,
|
||||||
|
) (*CBC, error) {
|
||||||
|
writeBlock, err := aes.NewCipher(localKey)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
readBlock, err := aes.NewCipher(remoteKey)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
writeCBC, ok := cipher.NewCBCEncrypter(writeBlock, localWriteIV).(cbcMode)
|
||||||
|
if !ok {
|
||||||
|
return nil, errFailedToCast
|
||||||
|
}
|
||||||
|
|
||||||
|
readCBC, ok := cipher.NewCBCDecrypter(readBlock, remoteWriteIV).(cbcMode)
|
||||||
|
if !ok {
|
||||||
|
return nil, errFailedToCast
|
||||||
|
}
|
||||||
|
|
||||||
|
return &CBC{
|
||||||
|
writeCBC: writeCBC,
|
||||||
|
writeMac: localMac,
|
||||||
|
|
||||||
|
readCBC: readCBC,
|
||||||
|
readMac: remoteMac,
|
||||||
|
h: hashFunc,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Encrypt encrypt a DTLS RecordLayer message.
|
||||||
|
func (c *CBC) Encrypt(pkt *recordlayer.RecordLayer, raw []byte) ([]byte, error) {
|
||||||
|
payload := raw[pkt.Header.Size():]
|
||||||
|
raw = raw[:pkt.Header.Size()]
|
||||||
|
blockSize := c.writeCBC.BlockSize()
|
||||||
|
|
||||||
|
// Generate + Append MAC
|
||||||
|
h := pkt.Header
|
||||||
|
|
||||||
|
var err error
|
||||||
|
var mac []byte
|
||||||
|
if h.ContentType == protocol.ContentTypeConnectionID {
|
||||||
|
mac, err = c.hmacCID(h.Epoch, h.SequenceNumber, h.Version, payload, c.writeMac, c.h, h.ConnectionID)
|
||||||
|
} else {
|
||||||
|
mac, err = c.hmac(h.Epoch, h.SequenceNumber, h.ContentType, h.Version, payload, c.writeMac, c.h)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
payload = append(payload, mac...)
|
||||||
|
|
||||||
|
// Generate + Append padding
|
||||||
|
padding := make([]byte, blockSize-len(payload)%blockSize)
|
||||||
|
paddingLen := len(padding)
|
||||||
|
for i := 0; i < paddingLen; i++ {
|
||||||
|
padding[i] = byte(paddingLen - 1)
|
||||||
|
}
|
||||||
|
payload = append(payload, padding...)
|
||||||
|
|
||||||
|
// Generate IV
|
||||||
|
iv := make([]byte, blockSize)
|
||||||
|
if _, err := rand.Read(iv); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set IV + Encrypt + Prepend IV
|
||||||
|
c.writeCBC.SetIV(iv)
|
||||||
|
c.writeCBC.CryptBlocks(payload, payload)
|
||||||
|
payload = append(iv, payload...) //nolint:makezero // todo: FIX
|
||||||
|
|
||||||
|
// Prepend unencrypted header with encrypted payload
|
||||||
|
raw = append(raw, payload...)
|
||||||
|
|
||||||
|
// Update recordLayer size to include IV+MAC+Padding
|
||||||
|
binary.BigEndian.PutUint16(raw[pkt.Header.Size()-2:], uint16(len(raw)-pkt.Header.Size())) //nolint:gosec //G115
|
||||||
|
|
||||||
|
return raw, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Decrypt decrypts a DTLS RecordLayer message.
|
||||||
|
func (c *CBC) Decrypt(header recordlayer.Header, in []byte) ([]byte, error) {
|
||||||
|
blockSize := c.readCBC.BlockSize()
|
||||||
|
mac := c.h()
|
||||||
|
|
||||||
|
if err := header.Unmarshal(in); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
body := in[header.Size():]
|
||||||
|
|
||||||
|
switch {
|
||||||
|
case header.ContentType == protocol.ContentTypeChangeCipherSpec:
|
||||||
|
// Nothing to encrypt with ChangeCipherSpec
|
||||||
|
return in, nil
|
||||||
|
case len(body)%blockSize != 0 || len(body) < blockSize+util.Max(mac.Size()+1, blockSize):
|
||||||
|
return nil, errNotEnoughRoomForNonce
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set + remove per record IV
|
||||||
|
c.readCBC.SetIV(body[:blockSize])
|
||||||
|
body = body[blockSize:]
|
||||||
|
|
||||||
|
// Decrypt
|
||||||
|
c.readCBC.CryptBlocks(body, body)
|
||||||
|
|
||||||
|
// Padding+MAC needs to be checked in constant time
|
||||||
|
// Otherwise we reveal information about the level of correctness
|
||||||
|
paddingLen, paddingGood := examinePadding(body)
|
||||||
|
if paddingGood != 255 {
|
||||||
|
return nil, errInvalidMAC
|
||||||
|
}
|
||||||
|
|
||||||
|
macSize := mac.Size()
|
||||||
|
if len(body) < macSize {
|
||||||
|
return nil, errInvalidMAC
|
||||||
|
}
|
||||||
|
|
||||||
|
dataEnd := len(body) - macSize - paddingLen
|
||||||
|
|
||||||
|
expectedMAC := body[dataEnd : dataEnd+macSize]
|
||||||
|
var err error
|
||||||
|
var actualMAC []byte
|
||||||
|
if header.ContentType == protocol.ContentTypeConnectionID {
|
||||||
|
actualMAC, err = c.hmacCID(
|
||||||
|
header.Epoch, header.SequenceNumber, header.Version, body[:dataEnd], c.readMac, c.h, header.ConnectionID,
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
actualMAC, err = c.hmac(
|
||||||
|
header.Epoch, header.SequenceNumber, header.ContentType, header.Version, body[:dataEnd], c.readMac, c.h,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
// Compute Local MAC and compare
|
||||||
|
if err != nil || !hmac.Equal(actualMAC, expectedMAC) {
|
||||||
|
return nil, errInvalidMAC
|
||||||
|
}
|
||||||
|
|
||||||
|
return append(in[:header.Size()], body[:dataEnd]...), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *CBC) hmac(
|
||||||
|
epoch uint16,
|
||||||
|
sequenceNumber uint64,
|
||||||
|
contentType protocol.ContentType,
|
||||||
|
protocolVersion protocol.Version,
|
||||||
|
payload []byte,
|
||||||
|
key []byte,
|
||||||
|
hf func() hash.Hash,
|
||||||
|
) ([]byte, error) {
|
||||||
|
hmacHash := hmac.New(hf, key)
|
||||||
|
|
||||||
|
msg := make([]byte, 13)
|
||||||
|
|
||||||
|
binary.BigEndian.PutUint16(msg, epoch)
|
||||||
|
util.PutBigEndianUint48(msg[2:], sequenceNumber)
|
||||||
|
msg[8] = byte(contentType)
|
||||||
|
msg[9] = protocolVersion.Major
|
||||||
|
msg[10] = protocolVersion.Minor
|
||||||
|
binary.BigEndian.PutUint16(msg[11:], uint16(len(payload))) //nolint:gosec //G115
|
||||||
|
|
||||||
|
if _, err := hmacHash.Write(msg); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if _, err := hmacHash.Write(payload); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return hmacHash.Sum(nil), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// hmacCID calculates a MAC according to
|
||||||
|
// https://datatracker.ietf.org/doc/html/rfc9146#section-5.1
|
||||||
|
func (c *CBC) hmacCID(
|
||||||
|
epoch uint16,
|
||||||
|
sequenceNumber uint64,
|
||||||
|
protocolVersion protocol.Version,
|
||||||
|
payload []byte,
|
||||||
|
key []byte,
|
||||||
|
hf func() hash.Hash,
|
||||||
|
cid []byte,
|
||||||
|
) ([]byte, error) {
|
||||||
|
// Must unmarshal inner plaintext in orde to perform MAC.
|
||||||
|
ip := &recordlayer.InnerPlaintext{}
|
||||||
|
if err := ip.Unmarshal(payload); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
hmacHash := hmac.New(hf, key)
|
||||||
|
|
||||||
|
var msg cryptobyte.Builder
|
||||||
|
|
||||||
|
msg.AddUint64(seqNumPlaceholder)
|
||||||
|
msg.AddUint8(uint8(protocol.ContentTypeConnectionID))
|
||||||
|
msg.AddUint8(uint8(len(cid))) //nolint:gosec //G115
|
||||||
|
msg.AddUint8(uint8(protocol.ContentTypeConnectionID))
|
||||||
|
msg.AddUint8(protocolVersion.Major)
|
||||||
|
msg.AddUint8(protocolVersion.Minor)
|
||||||
|
msg.AddUint16(epoch)
|
||||||
|
util.AddUint48(&msg, sequenceNumber)
|
||||||
|
msg.AddBytes(cid)
|
||||||
|
msg.AddUint16(uint16(len(payload))) //nolint:gosec //G115
|
||||||
|
msg.AddBytes(ip.Content)
|
||||||
|
msg.AddUint8(uint8(ip.RealType))
|
||||||
|
msg.AddBytes(make([]byte, ip.Zeros))
|
||||||
|
|
||||||
|
if _, err := hmacHash.Write(msg.BytesOrPanic()); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if _, err := hmacHash.Write(payload); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return hmacHash.Sum(nil), nil
|
||||||
|
}
|
||||||
68
vendor/github.com/pion/dtls/v3/pkg/crypto/ciphersuite/ccm.go
generated
vendored
Normal file
68
vendor/github.com/pion/dtls/v3/pkg/crypto/ciphersuite/ccm.go
generated
vendored
Normal file
|
|
@ -0,0 +1,68 @@
|
||||||
|
// SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package ciphersuite
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/aes"
|
||||||
|
|
||||||
|
"github.com/pion/dtls/v3/pkg/crypto/ccm"
|
||||||
|
"github.com/pion/dtls/v3/pkg/protocol/recordlayer"
|
||||||
|
)
|
||||||
|
|
||||||
|
// CCMTagLen is the length of Authentication Tag.
|
||||||
|
type CCMTagLen int
|
||||||
|
|
||||||
|
// CCM Enums.
|
||||||
|
const (
|
||||||
|
CCMTagLength8 CCMTagLen = 8
|
||||||
|
CCMTagLength CCMTagLen = 16
|
||||||
|
ccmNonceLength = 12
|
||||||
|
)
|
||||||
|
|
||||||
|
// CCM Provides an API to Encrypt/Decrypt DTLS 1.2 Packets.
|
||||||
|
type CCM struct {
|
||||||
|
aead *aead
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewCCM creates a DTLS GCM Cipher.
|
||||||
|
func NewCCM(tagLen CCMTagLen, localKey, localWriteIV, remoteKey, remoteWriteIV []byte) (*CCM, error) {
|
||||||
|
localBlock, err := aes.NewCipher(localKey)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
localCCM, err := ccm.NewCCM(localBlock, int(tagLen), ccmNonceLength)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
remoteBlock, err := aes.NewCipher(remoteKey)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
remoteCCM, err := ccm.NewCCM(remoteBlock, int(tagLen), ccmNonceLength)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &CCM{
|
||||||
|
aead: newAEAD(
|
||||||
|
localCCM,
|
||||||
|
localWriteIV,
|
||||||
|
remoteCCM,
|
||||||
|
remoteWriteIV,
|
||||||
|
ccmNonceLength,
|
||||||
|
int(tagLen),
|
||||||
|
),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Encrypt encrypt a DTLS RecordLayer message.
|
||||||
|
func (c *CCM) Encrypt(pkt *recordlayer.RecordLayer, raw []byte) ([]byte, error) {
|
||||||
|
return c.aead.encrypt(pkt, raw)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Decrypt decrypts a DTLS RecordLayer message.
|
||||||
|
func (c *CCM) Decrypt(header recordlayer.Header, in []byte) ([]byte, error) {
|
||||||
|
return c.aead.decrypt(header, in)
|
||||||
|
}
|
||||||
226
vendor/github.com/pion/dtls/v3/pkg/crypto/ciphersuite/ciphersuite.go
generated
vendored
Normal file
226
vendor/github.com/pion/dtls/v3/pkg/crypto/ciphersuite/ciphersuite.go
generated
vendored
Normal file
|
|
@ -0,0 +1,226 @@
|
||||||
|
// SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
// Package ciphersuite provides the crypto operations needed for a DTLS CipherSuite
|
||||||
|
package ciphersuite
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/cipher"
|
||||||
|
"encoding/binary"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"github.com/pion/dtls/v3/internal/util"
|
||||||
|
"github.com/pion/dtls/v3/pkg/protocol"
|
||||||
|
"github.com/pion/dtls/v3/pkg/protocol/recordlayer"
|
||||||
|
"golang.org/x/crypto/cryptobyte"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// 8 bytes of 0xff.
|
||||||
|
// https://datatracker.ietf.org/doc/html/rfc9146#name-record-payload-protection
|
||||||
|
seqNumPlaceholder = 0xffffffffffffffff
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
//nolint:err113
|
||||||
|
errNotEnoughRoomForNonce = &protocol.InternalError{Err: errors.New("buffer not long enough to contain nonce")}
|
||||||
|
//nolint:err113
|
||||||
|
errDecryptPacket = &protocol.TemporaryError{Err: errors.New("failed to decrypt packet")}
|
||||||
|
//nolint:err113
|
||||||
|
errInvalidMAC = &protocol.TemporaryError{Err: errors.New("invalid mac")}
|
||||||
|
//nolint:err113
|
||||||
|
errFailedToCast = &protocol.FatalError{Err: errors.New("failed to cast")}
|
||||||
|
)
|
||||||
|
|
||||||
|
// aead provides a generic API to Encrypt/Decrypt DTLS 1.2 Packets.
|
||||||
|
type aead struct {
|
||||||
|
localAEAD cipher.AEAD
|
||||||
|
remoteAEAD cipher.AEAD
|
||||||
|
localWriteIV []byte
|
||||||
|
remoteWriteIV []byte
|
||||||
|
nonceLength int
|
||||||
|
tagLength int
|
||||||
|
|
||||||
|
// buffer pool for (fixed-size) nonces.
|
||||||
|
nonceBufferPool sync.Pool
|
||||||
|
}
|
||||||
|
|
||||||
|
// newAEAD creates a generic DTLS AEAD-based Cipher.
|
||||||
|
func newAEAD(
|
||||||
|
localAEAD cipher.AEAD,
|
||||||
|
localWriteIV []byte,
|
||||||
|
remoteAEAD cipher.AEAD,
|
||||||
|
remoteWriteIV []byte,
|
||||||
|
nonceLength int,
|
||||||
|
tagLength int,
|
||||||
|
) *aead {
|
||||||
|
return &aead{
|
||||||
|
localAEAD: localAEAD,
|
||||||
|
localWriteIV: localWriteIV,
|
||||||
|
remoteAEAD: remoteAEAD,
|
||||||
|
remoteWriteIV: remoteWriteIV,
|
||||||
|
nonceLength: nonceLength,
|
||||||
|
tagLength: tagLength,
|
||||||
|
nonceBufferPool: sync.Pool{
|
||||||
|
New: func() any {
|
||||||
|
b := make([]byte, nonceLength)
|
||||||
|
return &b // nolint:nlreturn
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// encrypt encrypts a DTLS RecordLayer message.
|
||||||
|
func (a *aead) encrypt(pkt *recordlayer.RecordLayer, raw []byte) ([]byte, error) {
|
||||||
|
payload := raw[pkt.Header.Size():]
|
||||||
|
raw = raw[:pkt.Header.Size()]
|
||||||
|
|
||||||
|
// Get nonce buffer from pool
|
||||||
|
noncePtr := a.nonceBufferPool.Get().(*[]byte) // nolint:forcetypeassert
|
||||||
|
nonce := *noncePtr
|
||||||
|
|
||||||
|
copy(nonce, a.localWriteIV[:4])
|
||||||
|
|
||||||
|
// https://www.rfc-editor.org/rfc/rfc9325#name-nonce-reuse-in-tls-12
|
||||||
|
seq64 := (uint64(pkt.Header.Epoch) << 48) | (pkt.Header.SequenceNumber & 0x0000ffffffffffff)
|
||||||
|
binary.BigEndian.PutUint64(nonce[4:], seq64)
|
||||||
|
|
||||||
|
var additionalData []byte
|
||||||
|
if pkt.Header.ContentType == protocol.ContentTypeConnectionID {
|
||||||
|
additionalData = generateAEADAdditionalDataCID(&pkt.Header, len(payload))
|
||||||
|
} else {
|
||||||
|
additionalData = generateAEADAdditionalData(&pkt.Header, len(payload))
|
||||||
|
}
|
||||||
|
finalSize := len(raw) + 8 + len(payload) + a.tagLength
|
||||||
|
r := make([]byte, finalSize)
|
||||||
|
copy(r, raw)
|
||||||
|
copy(r[len(raw):], nonce[4:])
|
||||||
|
|
||||||
|
a.localAEAD.Seal(r[len(raw)+8:len(raw)+8], nonce, payload, additionalData)
|
||||||
|
|
||||||
|
// Update recordLayer size to include explicit nonce
|
||||||
|
binary.BigEndian.PutUint16(r[pkt.Header.Size()-2:], uint16(len(r)-pkt.Header.Size())) //nolint:gosec //G115
|
||||||
|
|
||||||
|
// Return nonce buffer to pool
|
||||||
|
a.nonceBufferPool.Put(noncePtr)
|
||||||
|
|
||||||
|
return r, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// decrypt decrypts a DTLS RecordLayer message.
|
||||||
|
func (a *aead) decrypt(header recordlayer.Header, in []byte) ([]byte, error) {
|
||||||
|
err := header.Unmarshal(in)
|
||||||
|
switch {
|
||||||
|
case err != nil:
|
||||||
|
return nil, err
|
||||||
|
case header.ContentType == protocol.ContentTypeChangeCipherSpec:
|
||||||
|
// Nothing to encrypt with ChangeCipherSpec
|
||||||
|
return in, nil
|
||||||
|
case len(in) <= (8 + header.Size()):
|
||||||
|
return nil, errNotEnoughRoomForNonce
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get nonce buffer from pool
|
||||||
|
noncePtr := a.nonceBufferPool.Get().(*[]byte) // nolint:forcetypeassert
|
||||||
|
nonce := *noncePtr
|
||||||
|
|
||||||
|
copy(nonce[:4], a.remoteWriteIV[:4])
|
||||||
|
copy(nonce[4:], in[header.Size():header.Size()+8])
|
||||||
|
out := in[header.Size()+8:]
|
||||||
|
|
||||||
|
var additionalData []byte
|
||||||
|
if header.ContentType == protocol.ContentTypeConnectionID {
|
||||||
|
additionalData = generateAEADAdditionalDataCID(&header, len(out)-a.tagLength)
|
||||||
|
} else {
|
||||||
|
additionalData = generateAEADAdditionalData(&header, len(out)-a.tagLength)
|
||||||
|
}
|
||||||
|
out, err = a.remoteAEAD.Open(out[:0], nonce, out, additionalData)
|
||||||
|
if err != nil {
|
||||||
|
// Return nonce buffer to pool
|
||||||
|
a.nonceBufferPool.Put(noncePtr)
|
||||||
|
|
||||||
|
return nil, fmt.Errorf("%w: %v", errDecryptPacket, err) //nolint:errorlint
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return nonce buffer to pool
|
||||||
|
a.nonceBufferPool.Put(noncePtr)
|
||||||
|
|
||||||
|
return append(in[:header.Size()], out...), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func generateAEADAdditionalData(h *recordlayer.Header, payloadLen int) []byte {
|
||||||
|
var additionalData [13]byte
|
||||||
|
|
||||||
|
// SequenceNumber MUST be set first
|
||||||
|
// we only want uint48, clobbering an extra 2 (using uint64, Golang doesn't have uint48)
|
||||||
|
binary.BigEndian.PutUint64(additionalData[:], h.SequenceNumber)
|
||||||
|
binary.BigEndian.PutUint16(additionalData[:], h.Epoch)
|
||||||
|
additionalData[8] = byte(h.ContentType)
|
||||||
|
additionalData[9] = h.Version.Major
|
||||||
|
additionalData[10] = h.Version.Minor
|
||||||
|
//nolint:gosec //G115
|
||||||
|
binary.BigEndian.PutUint16(additionalData[len(additionalData)-2:], uint16(payloadLen))
|
||||||
|
|
||||||
|
return additionalData[:]
|
||||||
|
}
|
||||||
|
|
||||||
|
// generateAEADAdditionalDataCID generates additional data for AEAD ciphers
|
||||||
|
// according to https://datatracker.ietf.org/doc/html/rfc9146#name-aead-ciphers
|
||||||
|
func generateAEADAdditionalDataCID(h *recordlayer.Header, payloadLen int) []byte {
|
||||||
|
var builder cryptobyte.Builder
|
||||||
|
|
||||||
|
builder.AddUint64(seqNumPlaceholder)
|
||||||
|
builder.AddUint8(uint8(protocol.ContentTypeConnectionID))
|
||||||
|
builder.AddUint8(uint8(len(h.ConnectionID))) //nolint:gosec //G115
|
||||||
|
builder.AddUint8(uint8(protocol.ContentTypeConnectionID))
|
||||||
|
builder.AddUint8(h.Version.Major)
|
||||||
|
builder.AddUint8(h.Version.Minor)
|
||||||
|
builder.AddUint16(h.Epoch)
|
||||||
|
util.AddUint48(&builder, h.SequenceNumber)
|
||||||
|
builder.AddBytes(h.ConnectionID)
|
||||||
|
builder.AddUint16(uint16(payloadLen)) //nolint:gosec //G115
|
||||||
|
|
||||||
|
return builder.BytesOrPanic()
|
||||||
|
}
|
||||||
|
|
||||||
|
// examinePadding returns, in constant time, the length of the padding to remove
|
||||||
|
// from the end of payload. It also returns a byte which is equal to 255 if the
|
||||||
|
// padding was valid and 0 otherwise. See RFC 2246, Section 6.2.3.2.
|
||||||
|
//
|
||||||
|
// https://github.com/golang/go/blob/039c2081d1178f90a8fa2f4e6958693129f8de33/src/crypto/tls/conn.go#L245
|
||||||
|
func examinePadding(payload []byte) (toRemove int, good byte) {
|
||||||
|
if len(payload) < 1 {
|
||||||
|
return 0, 0
|
||||||
|
}
|
||||||
|
|
||||||
|
paddingLen := payload[len(payload)-1]
|
||||||
|
t := uint(len(payload)-1) - uint(paddingLen) //nolint:gosec //G115
|
||||||
|
// if len(payload) >= (paddingLen - 1) then the MSB of t is zero
|
||||||
|
good = byte(int32(^t) >> 31) //nolint:gosec //G115
|
||||||
|
|
||||||
|
// The maximum possible padding length plus the actual length field
|
||||||
|
toCheck := min(
|
||||||
|
// The length of the padded data is public, so we can use an if here
|
||||||
|
256, len(payload))
|
||||||
|
|
||||||
|
for i := 0; i < toCheck; i++ {
|
||||||
|
t := uint(paddingLen) - uint(i) //nolint:gosec //G115
|
||||||
|
// if i <= paddingLen then the MSB of t is zero
|
||||||
|
mask := byte(int32(^t) >> 31) //nolint:gosec //G115
|
||||||
|
b := payload[len(payload)-1-i]
|
||||||
|
good &^= mask&paddingLen ^ mask&b
|
||||||
|
}
|
||||||
|
|
||||||
|
// We AND together the bits of good and replicate the result across
|
||||||
|
// all the bits.
|
||||||
|
good &= good << 4
|
||||||
|
good &= good << 2
|
||||||
|
good &= good << 1
|
||||||
|
good = uint8(int8(good) >> 7) //nolint:gosec //G115
|
||||||
|
|
||||||
|
toRemove = int(paddingLen) + 1
|
||||||
|
|
||||||
|
return toRemove, good
|
||||||
|
}
|
||||||
63
vendor/github.com/pion/dtls/v3/pkg/crypto/ciphersuite/gcm.go
generated
vendored
Normal file
63
vendor/github.com/pion/dtls/v3/pkg/crypto/ciphersuite/gcm.go
generated
vendored
Normal file
|
|
@ -0,0 +1,63 @@
|
||||||
|
// SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package ciphersuite
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/aes"
|
||||||
|
"crypto/cipher"
|
||||||
|
|
||||||
|
"github.com/pion/dtls/v3/pkg/protocol/recordlayer"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
gcmTagLength = 16
|
||||||
|
gcmNonceLength = 12
|
||||||
|
)
|
||||||
|
|
||||||
|
// GCM Provides an API to Encrypt/Decrypt DTLS 1.2 Packets.
|
||||||
|
type GCM struct {
|
||||||
|
aead *aead
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewGCM creates a DTLS GCM Cipher.
|
||||||
|
func NewGCM(localKey, localWriteIV, remoteKey, remoteWriteIV []byte) (*GCM, error) {
|
||||||
|
localBlock, err := aes.NewCipher(localKey)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
localGCM, err := cipher.NewGCM(localBlock)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
remoteBlock, err := aes.NewCipher(remoteKey)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
remoteGCM, err := cipher.NewGCM(remoteBlock)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &GCM{
|
||||||
|
aead: newAEAD(
|
||||||
|
localGCM,
|
||||||
|
localWriteIV,
|
||||||
|
remoteGCM,
|
||||||
|
remoteWriteIV,
|
||||||
|
gcmNonceLength,
|
||||||
|
gcmTagLength,
|
||||||
|
),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Encrypt encrypts a DTLS RecordLayer message.
|
||||||
|
func (g *GCM) Encrypt(pkt *recordlayer.RecordLayer, raw []byte) ([]byte, error) {
|
||||||
|
return g.aead.encrypt(pkt, raw)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Decrypt decrypts a DTLS RecordLayer message.
|
||||||
|
func (g *GCM) Decrypt(header recordlayer.Header, in []byte) ([]byte, error) {
|
||||||
|
return g.aead.decrypt(header, in)
|
||||||
|
}
|
||||||
25
vendor/github.com/pion/dtls/v3/pkg/crypto/clientcertificate/client_certificate.go
generated
vendored
Normal file
25
vendor/github.com/pion/dtls/v3/pkg/crypto/clientcertificate/client_certificate.go
generated
vendored
Normal file
|
|
@ -0,0 +1,25 @@
|
||||||
|
// SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
// Package clientcertificate provides all the support Client Certificate types
|
||||||
|
package clientcertificate
|
||||||
|
|
||||||
|
// Type is used to communicate what
|
||||||
|
// type of certificate is being transported
|
||||||
|
//
|
||||||
|
// https://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml#tls-parameters-2
|
||||||
|
type Type byte
|
||||||
|
|
||||||
|
// ClientCertificateType enums.
|
||||||
|
const (
|
||||||
|
RSASign Type = 1
|
||||||
|
ECDSASign Type = 64
|
||||||
|
)
|
||||||
|
|
||||||
|
// Types returns all valid ClientCertificate Types.
|
||||||
|
func Types() map[Type]bool {
|
||||||
|
return map[Type]bool{
|
||||||
|
RSASign: true,
|
||||||
|
ECDSASign: true,
|
||||||
|
}
|
||||||
|
}
|
||||||
121
vendor/github.com/pion/dtls/v3/pkg/crypto/elliptic/elliptic.go
generated
vendored
Normal file
121
vendor/github.com/pion/dtls/v3/pkg/crypto/elliptic/elliptic.go
generated
vendored
Normal file
|
|
@ -0,0 +1,121 @@
|
||||||
|
// SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
// Package elliptic provides elliptic curve cryptography for DTLS
|
||||||
|
package elliptic
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/ecdh"
|
||||||
|
"crypto/rand"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
var errInvalidNamedCurve = errors.New("invalid named curve")
|
||||||
|
|
||||||
|
// CurvePointFormat is used to represent the IANA registered curve points
|
||||||
|
//
|
||||||
|
// https://www.iana.org/assignments/tls-parameters/tls-parameters.xml#tls-parameters-9
|
||||||
|
type CurvePointFormat byte
|
||||||
|
|
||||||
|
// CurvePointFormat enums.
|
||||||
|
const (
|
||||||
|
CurvePointFormatUncompressed CurvePointFormat = 0
|
||||||
|
)
|
||||||
|
|
||||||
|
// Keypair is a Curve with a Private/Public Keypair.
|
||||||
|
type Keypair struct {
|
||||||
|
Curve Curve
|
||||||
|
PublicKey []byte
|
||||||
|
PrivateKey []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
// CurveType is used to represent the IANA registered curve types for TLS
|
||||||
|
//
|
||||||
|
// https://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml#tls-parameters-10
|
||||||
|
type CurveType byte
|
||||||
|
|
||||||
|
// CurveType enums.
|
||||||
|
const (
|
||||||
|
CurveTypeNamedCurve CurveType = 0x03
|
||||||
|
)
|
||||||
|
|
||||||
|
// CurveTypes returns all known curves.
|
||||||
|
func CurveTypes() map[CurveType]struct{} {
|
||||||
|
return map[CurveType]struct{}{
|
||||||
|
CurveTypeNamedCurve: {},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Curve is used to represent the IANA registered curves for TLS
|
||||||
|
//
|
||||||
|
// https://www.iana.org/assignments/tls-parameters/tls-parameters.xml#tls-parameters-8
|
||||||
|
type Curve uint16
|
||||||
|
|
||||||
|
// Curve enums.
|
||||||
|
const (
|
||||||
|
P256 Curve = 0x0017
|
||||||
|
P384 Curve = 0x0018
|
||||||
|
X25519 Curve = 0x001d
|
||||||
|
// X25519MLKEM768
|
||||||
|
// https://pkg.go.dev/crypto/internal/fips140/mlkem
|
||||||
|
// https://datatracker.ietf.org/doc/draft-ietf-tls-hybrid-design/
|
||||||
|
// https://datatracker.ietf.org/doc/draft-ietf-tls-ecdhe-mlkem/
|
||||||
|
)
|
||||||
|
|
||||||
|
func (c Curve) String() string {
|
||||||
|
switch c {
|
||||||
|
case P256:
|
||||||
|
return "P-256"
|
||||||
|
case P384:
|
||||||
|
return "P-384"
|
||||||
|
case X25519:
|
||||||
|
return "X25519"
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt.Sprintf("%#x", uint16(c))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Curves returns all curves we implement.
|
||||||
|
func Curves() map[Curve]bool {
|
||||||
|
return map[Curve]bool{
|
||||||
|
X25519: true,
|
||||||
|
P256: true,
|
||||||
|
P384: true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GenerateKeypair generates a keypair for the given Curve.
|
||||||
|
func GenerateKeypair(curve Curve) (*Keypair, error) {
|
||||||
|
ec, err := curve.toECDH()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
sk, err := ec.GenerateKey(rand.Reader)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
pk := sk.PublicKey()
|
||||||
|
|
||||||
|
return &Keypair{
|
||||||
|
Curve: curve,
|
||||||
|
PublicKey: pk.Bytes(), // NIST: SEC1 uncompressed (04||X||Y); X25519: 32 bytes
|
||||||
|
PrivateKey: sk.Bytes(), // Scalar suitable for ecdh.NewPrivateKey
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// toECDH returns the crypto/ecdh curve for our enum.
|
||||||
|
func (c Curve) toECDH() (ecdh.Curve, error) {
|
||||||
|
switch c {
|
||||||
|
case X25519:
|
||||||
|
return ecdh.X25519(), nil
|
||||||
|
case P256:
|
||||||
|
return ecdh.P256(), nil
|
||||||
|
case P384:
|
||||||
|
return ecdh.P384(), nil
|
||||||
|
default:
|
||||||
|
return nil, errInvalidNamedCurve
|
||||||
|
}
|
||||||
|
}
|
||||||
53
vendor/github.com/pion/dtls/v3/pkg/crypto/fingerprint/fingerprint.go
generated
vendored
Normal file
53
vendor/github.com/pion/dtls/v3/pkg/crypto/fingerprint/fingerprint.go
generated
vendored
Normal file
|
|
@ -0,0 +1,53 @@
|
||||||
|
// SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
// Package fingerprint provides a helper to create fingerprint string from certificate
|
||||||
|
package fingerprint
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto"
|
||||||
|
"crypto/x509"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
errHashUnavailable = errors.New("fingerprint: hash algorithm is not linked into the binary")
|
||||||
|
errInvalidFingerprintLength = errors.New("fingerprint: invalid fingerprint length")
|
||||||
|
)
|
||||||
|
|
||||||
|
// Fingerprint creates a fingerprint for a certificate using the specified hash algorithm.
|
||||||
|
func Fingerprint(cert *x509.Certificate, algo crypto.Hash) (string, error) {
|
||||||
|
if !algo.Available() {
|
||||||
|
return "", errHashUnavailable
|
||||||
|
}
|
||||||
|
h := algo.New()
|
||||||
|
for i := 0; i < len(cert.Raw); {
|
||||||
|
n, _ := h.Write(cert.Raw[i:])
|
||||||
|
// Hash.Writer is specified to be never returning an error.
|
||||||
|
// https://golang.org/pkg/hash/#Hash
|
||||||
|
i += n
|
||||||
|
}
|
||||||
|
digest := fmt.Appendf(nil, "%x", h.Sum(nil))
|
||||||
|
|
||||||
|
digestlen := len(digest)
|
||||||
|
if digestlen == 0 {
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
if digestlen%2 != 0 {
|
||||||
|
return "", errInvalidFingerprintLength
|
||||||
|
}
|
||||||
|
res := make([]byte, digestlen>>1+digestlen-1)
|
||||||
|
|
||||||
|
pos := 0
|
||||||
|
for i, c := range digest {
|
||||||
|
res[pos] = c
|
||||||
|
pos++
|
||||||
|
if (i)%2 != 0 && i < digestlen-1 {
|
||||||
|
res[pos] = byte(':')
|
||||||
|
pos++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return string(res), nil
|
||||||
|
}
|
||||||
43
vendor/github.com/pion/dtls/v3/pkg/crypto/fingerprint/hash.go
generated
vendored
Normal file
43
vendor/github.com/pion/dtls/v3/pkg/crypto/fingerprint/hash.go
generated
vendored
Normal file
|
|
@ -0,0 +1,43 @@
|
||||||
|
// SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package fingerprint
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto"
|
||||||
|
"errors"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
var errInvalidHashAlgorithm = errors.New("fingerprint: invalid hash algorithm")
|
||||||
|
|
||||||
|
func nameToHash() map[string]crypto.Hash {
|
||||||
|
return map[string]crypto.Hash{
|
||||||
|
"md5": crypto.MD5, // [RFC3279]
|
||||||
|
"sha-1": crypto.SHA1, // [RFC3279]
|
||||||
|
"sha-224": crypto.SHA224, // [RFC4055]
|
||||||
|
"sha-256": crypto.SHA256, // [RFC4055]
|
||||||
|
"sha-384": crypto.SHA384, // [RFC4055]
|
||||||
|
"sha-512": crypto.SHA512, // [RFC4055]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// HashFromString allows looking up a hash algorithm by it's string representation.
|
||||||
|
func HashFromString(s string) (crypto.Hash, error) {
|
||||||
|
if h, ok := nameToHash()[strings.ToLower(s)]; ok {
|
||||||
|
return h, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0, errInvalidHashAlgorithm
|
||||||
|
}
|
||||||
|
|
||||||
|
// StringFromHash allows looking up a string representation of the crypto.Hash.
|
||||||
|
func StringFromHash(hash crypto.Hash) (string, error) {
|
||||||
|
for s, h := range nameToHash() {
|
||||||
|
if h == hash {
|
||||||
|
return s, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return "", errInvalidHashAlgorithm
|
||||||
|
}
|
||||||
157
vendor/github.com/pion/dtls/v3/pkg/crypto/hash/hash.go
generated
vendored
Normal file
157
vendor/github.com/pion/dtls/v3/pkg/crypto/hash/hash.go
generated
vendored
Normal file
|
|
@ -0,0 +1,157 @@
|
||||||
|
// SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
// Package hash provides TLS HashAlgorithm as defined in TLS 1.2
|
||||||
|
package hash
|
||||||
|
|
||||||
|
import ( //nolint:gci
|
||||||
|
"crypto"
|
||||||
|
"crypto/md5" //nolint:gosec
|
||||||
|
"crypto/sha1" //nolint:gosec
|
||||||
|
"crypto/sha256"
|
||||||
|
"crypto/sha512"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Algorithm is used to indicate the hash algorithm used
|
||||||
|
// https://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml#tls-parameters-18
|
||||||
|
type Algorithm uint16
|
||||||
|
|
||||||
|
// Supported hash algorithms.
|
||||||
|
const (
|
||||||
|
None Algorithm = 0 // Blacklisted
|
||||||
|
MD5 Algorithm = 1 // Blacklisted
|
||||||
|
SHA1 Algorithm = 2 // Blacklisted
|
||||||
|
SHA224 Algorithm = 3
|
||||||
|
SHA256 Algorithm = 4
|
||||||
|
SHA384 Algorithm = 5
|
||||||
|
SHA512 Algorithm = 6
|
||||||
|
Ed25519 Algorithm = 8
|
||||||
|
)
|
||||||
|
|
||||||
|
// String makes hashAlgorithm printable.
|
||||||
|
func (a Algorithm) String() string {
|
||||||
|
switch a {
|
||||||
|
case None:
|
||||||
|
return "none"
|
||||||
|
case MD5:
|
||||||
|
return "md5" // [RFC3279]
|
||||||
|
case SHA1:
|
||||||
|
return "sha-1" // [RFC3279]
|
||||||
|
case SHA224:
|
||||||
|
return "sha-224" // [RFC4055]
|
||||||
|
case SHA256:
|
||||||
|
return "sha-256" // [RFC4055]
|
||||||
|
case SHA384:
|
||||||
|
return "sha-384" // [RFC4055]
|
||||||
|
case SHA512:
|
||||||
|
return "sha-512" // [RFC4055]
|
||||||
|
case Ed25519:
|
||||||
|
return "null"
|
||||||
|
default:
|
||||||
|
return "unknown or unsupported hash algorithm"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Digest performs a digest on the passed value.
|
||||||
|
func (a Algorithm) Digest(b []byte) []byte {
|
||||||
|
switch a {
|
||||||
|
case None:
|
||||||
|
return nil
|
||||||
|
case MD5:
|
||||||
|
hash := md5.Sum(b) // #nosec
|
||||||
|
|
||||||
|
return hash[:]
|
||||||
|
case SHA1:
|
||||||
|
hash := sha1.Sum(b) // #nosec
|
||||||
|
|
||||||
|
return hash[:]
|
||||||
|
case SHA224:
|
||||||
|
hash := sha256.Sum224(b)
|
||||||
|
|
||||||
|
return hash[:]
|
||||||
|
case SHA256:
|
||||||
|
hash := sha256.Sum256(b)
|
||||||
|
|
||||||
|
return hash[:]
|
||||||
|
case SHA384:
|
||||||
|
hash := sha512.Sum384(b)
|
||||||
|
|
||||||
|
return hash[:]
|
||||||
|
case SHA512:
|
||||||
|
hash := sha512.Sum512(b)
|
||||||
|
|
||||||
|
return hash[:]
|
||||||
|
default:
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Insecure returns if the given HashAlgorithm is considered secure in DTLS 1.2
|
||||||
|
// .
|
||||||
|
func (a Algorithm) Insecure() bool {
|
||||||
|
switch a {
|
||||||
|
case None, MD5, SHA1:
|
||||||
|
return true
|
||||||
|
default:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// CryptoHash returns the crypto.Hash implementation for the given HashAlgorithm.
|
||||||
|
func (a Algorithm) CryptoHash() crypto.Hash {
|
||||||
|
switch a {
|
||||||
|
case None:
|
||||||
|
return crypto.Hash(0)
|
||||||
|
case MD5:
|
||||||
|
return crypto.MD5
|
||||||
|
case SHA1:
|
||||||
|
return crypto.SHA1
|
||||||
|
case SHA224:
|
||||||
|
return crypto.SHA224
|
||||||
|
case SHA256:
|
||||||
|
return crypto.SHA256
|
||||||
|
case SHA384:
|
||||||
|
return crypto.SHA384
|
||||||
|
case SHA512:
|
||||||
|
return crypto.SHA512
|
||||||
|
case Ed25519:
|
||||||
|
return crypto.Hash(0)
|
||||||
|
default:
|
||||||
|
return crypto.Hash(0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Algorithms returns all the supported Hash Algorithms.
|
||||||
|
func Algorithms() map[Algorithm]struct{} {
|
||||||
|
return map[Algorithm]struct{}{
|
||||||
|
None: {},
|
||||||
|
MD5: {},
|
||||||
|
SHA1: {},
|
||||||
|
SHA224: {},
|
||||||
|
SHA256: {},
|
||||||
|
SHA384: {},
|
||||||
|
SHA512: {},
|
||||||
|
Ed25519: {},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExtractHashFromPSS extracts the hash algorithm from an RSA-PSS SignatureScheme value.
|
||||||
|
// This handles TLS 1.3 PSS schemes.
|
||||||
|
// Returns None if the scheme is not a recognized PSS scheme.
|
||||||
|
func ExtractHashFromPSS(pssScheme uint16) Algorithm {
|
||||||
|
// Note: We can't import signature package here due to circular dependency,
|
||||||
|
// so we use the raw values. These correspond to:
|
||||||
|
// 0x0804 = RSA_PSS_RSAE_SHA256, 0x0809 = RSA_PSS_PSS_SHA256
|
||||||
|
// 0x0805 = RSA_PSS_RSAE_SHA384, 0x080a = RSA_PSS_PSS_SHA384
|
||||||
|
// 0x0806 = RSA_PSS_RSAE_SHA512, 0x080b = RSA_PSS_PSS_SHA512
|
||||||
|
switch pssScheme {
|
||||||
|
case 0x0804, 0x0809:
|
||||||
|
return SHA256
|
||||||
|
case 0x0805, 0x080a:
|
||||||
|
return SHA384
|
||||||
|
case 0x0806, 0x080b:
|
||||||
|
return SHA512
|
||||||
|
default:
|
||||||
|
return None
|
||||||
|
}
|
||||||
|
}
|
||||||
264
vendor/github.com/pion/dtls/v3/pkg/crypto/prf/prf.go
generated
vendored
Normal file
264
vendor/github.com/pion/dtls/v3/pkg/crypto/prf/prf.go
generated
vendored
Normal file
|
|
@ -0,0 +1,264 @@
|
||||||
|
// SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
// Package prf implements TLS 1.2 Pseudorandom functions
|
||||||
|
package prf
|
||||||
|
|
||||||
|
import ( //nolint:gci
|
||||||
|
"crypto/ecdh"
|
||||||
|
"crypto/hmac"
|
||||||
|
"encoding/binary"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"hash"
|
||||||
|
"math"
|
||||||
|
|
||||||
|
"github.com/pion/dtls/v3/pkg/crypto/elliptic"
|
||||||
|
"github.com/pion/dtls/v3/pkg/protocol"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
masterSecretLabel = "master secret"
|
||||||
|
extendedMasterSecretLabel = "extended master secret"
|
||||||
|
keyExpansionLabel = "key expansion"
|
||||||
|
verifyDataClientLabel = "client finished"
|
||||||
|
verifyDataServerLabel = "server finished"
|
||||||
|
)
|
||||||
|
|
||||||
|
// HashFunc allows callers to decide what hash is used in PRF.
|
||||||
|
type HashFunc func() hash.Hash
|
||||||
|
|
||||||
|
// EncryptionKeys is all the state needed for a TLS CipherSuite.
|
||||||
|
type EncryptionKeys struct {
|
||||||
|
MasterSecret []byte
|
||||||
|
ClientMACKey []byte
|
||||||
|
ServerMACKey []byte
|
||||||
|
ClientWriteKey []byte
|
||||||
|
ServerWriteKey []byte
|
||||||
|
ClientWriteIV []byte
|
||||||
|
ServerWriteIV []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
var errInvalidNamedCurve = &protocol.FatalError{Err: errors.New("invalid named curve")} //nolint:err113
|
||||||
|
|
||||||
|
func (e *EncryptionKeys) String() string {
|
||||||
|
return fmt.Sprintf(`encryptionKeys:
|
||||||
|
- masterSecret: %#v
|
||||||
|
- clientMACKey: %#v
|
||||||
|
- serverMACKey: %#v
|
||||||
|
- clientWriteKey: %#v
|
||||||
|
- serverWriteKey: %#v
|
||||||
|
- clientWriteIV: %#v
|
||||||
|
- serverWriteIV: %#v
|
||||||
|
`,
|
||||||
|
e.MasterSecret,
|
||||||
|
e.ClientMACKey,
|
||||||
|
e.ServerMACKey,
|
||||||
|
e.ClientWriteKey,
|
||||||
|
e.ServerWriteKey,
|
||||||
|
e.ClientWriteIV,
|
||||||
|
e.ServerWriteIV)
|
||||||
|
}
|
||||||
|
|
||||||
|
// PSKPreMasterSecret generates the PSK Premaster Secret
|
||||||
|
// The premaster secret is formed as follows: if the PSK is N octets
|
||||||
|
// long, concatenate a uint16 with the value N, N zero octets, a second
|
||||||
|
// uint16 with the value N, and the PSK itself.
|
||||||
|
//
|
||||||
|
// https://tools.ietf.org/html/rfc4279#section-2
|
||||||
|
func PSKPreMasterSecret(psk []byte) []byte {
|
||||||
|
pskLen := uint16(len(psk)) //nolint:gosec // G115
|
||||||
|
|
||||||
|
out := append(make([]byte, 2+pskLen+2), psk...)
|
||||||
|
binary.BigEndian.PutUint16(out, pskLen)
|
||||||
|
binary.BigEndian.PutUint16(out[2+pskLen:], pskLen)
|
||||||
|
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
// EcdhePSKPreMasterSecret implements TLS 1.2 Premaster Secret generation given a psk, a keypair and a curve
|
||||||
|
//
|
||||||
|
// https://datatracker.ietf.org/doc/html/rfc5489#section-2
|
||||||
|
func EcdhePSKPreMasterSecret(psk, publicKey, privateKey []byte, curve elliptic.Curve) ([]byte, error) {
|
||||||
|
preMasterSecret, err := PreMasterSecret(publicKey, privateKey, curve)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
out := make([]byte, 2+len(preMasterSecret)+2+len(psk))
|
||||||
|
|
||||||
|
// write preMasterSecret length
|
||||||
|
offset := 0
|
||||||
|
binary.BigEndian.PutUint16(out[offset:], uint16(len(preMasterSecret))) //nolint:gosec // G115
|
||||||
|
offset += 2
|
||||||
|
|
||||||
|
// write preMasterSecret
|
||||||
|
copy(out[offset:], preMasterSecret)
|
||||||
|
offset += len(preMasterSecret)
|
||||||
|
|
||||||
|
// write psk length
|
||||||
|
binary.BigEndian.PutUint16(out[offset:], uint16(len(psk))) //nolint:gosec // G115
|
||||||
|
offset += 2
|
||||||
|
|
||||||
|
// write psk
|
||||||
|
copy(out[offset:], psk)
|
||||||
|
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// PreMasterSecret implements TLS 1.2 Premaster Secret generation given a keypair and a curve.
|
||||||
|
func PreMasterSecret(publicKey, privateKey []byte, curve elliptic.Curve) ([]byte, error) {
|
||||||
|
var ec ecdh.Curve
|
||||||
|
|
||||||
|
switch curve {
|
||||||
|
case elliptic.X25519:
|
||||||
|
ec = ecdh.X25519()
|
||||||
|
case elliptic.P256:
|
||||||
|
ec = ecdh.P256()
|
||||||
|
case elliptic.P384:
|
||||||
|
ec = ecdh.P384()
|
||||||
|
default:
|
||||||
|
return nil, errInvalidNamedCurve
|
||||||
|
}
|
||||||
|
|
||||||
|
sk, err := ec.NewPrivateKey(privateKey)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
pk, err := ec.NewPublicKey(publicKey) // NIST: SEC1 uncompressed; X25519: 32-byte u
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return sk.ECDH(pk)
|
||||||
|
}
|
||||||
|
|
||||||
|
// PHash is PRF is the SHA-256 hash function is used for all cipher suites
|
||||||
|
// defined in this TLS 1.2 document and in TLS documents published prior to this
|
||||||
|
// document when TLS 1.2 is negotiated. New cipher suites MUST explicitly
|
||||||
|
// specify a PRF and, in general, SHOULD use the TLS PRF with SHA-256 or a
|
||||||
|
// stronger standard hash function.
|
||||||
|
//
|
||||||
|
// P_hash(secret, seed) = HMAC_hash(secret, A(1) + seed) +
|
||||||
|
// HMAC_hash(secret, A(2) + seed) +
|
||||||
|
// HMAC_hash(secret, A(3) + seed) + ...
|
||||||
|
//
|
||||||
|
// A() is defined as:
|
||||||
|
//
|
||||||
|
// A(0) = seed
|
||||||
|
// A(i) = HMAC_hash(secret, A(i-1))
|
||||||
|
//
|
||||||
|
// P_hash can be iterated as many times as necessary to produce the
|
||||||
|
// required quantity of data. For example, if P_SHA256 is being used to
|
||||||
|
// create 80 bytes of data, it will have to be iterated three times
|
||||||
|
// (through A(3)), creating 96 bytes of output data; the last 16 bytes
|
||||||
|
// of the final iteration will then be discarded, leaving 80 bytes of
|
||||||
|
// output data.
|
||||||
|
//
|
||||||
|
// https://tools.ietf.org/html/rfc4346w
|
||||||
|
func PHash(secret, seed []byte, requestedLength int, hashFunc HashFunc) ([]byte, error) {
|
||||||
|
hmacSHA256 := func(key, data []byte) ([]byte, error) {
|
||||||
|
mac := hmac.New(hashFunc, key)
|
||||||
|
if _, err := mac.Write(data); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return mac.Sum(nil), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var err error
|
||||||
|
lastRound := seed
|
||||||
|
out := []byte{}
|
||||||
|
|
||||||
|
iterations := int(math.Ceil(float64(requestedLength) / float64(hashFunc().Size())))
|
||||||
|
for i := 0; i < iterations; i++ {
|
||||||
|
lastRound, err = hmacSHA256(secret, lastRound)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
withSecret, err := hmacSHA256(secret, append(lastRound, seed...))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
out = append(out, withSecret...)
|
||||||
|
}
|
||||||
|
|
||||||
|
return out[:requestedLength], nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExtendedMasterSecret generates a Extended MasterSecret as defined in
|
||||||
|
// https://tools.ietf.org/html/rfc7627
|
||||||
|
func ExtendedMasterSecret(preMasterSecret, sessionHash []byte, h HashFunc) ([]byte, error) {
|
||||||
|
seed := append([]byte(extendedMasterSecretLabel), sessionHash...)
|
||||||
|
|
||||||
|
return PHash(preMasterSecret, seed, 48, h)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MasterSecret generates a TLS 1.2 MasterSecret.
|
||||||
|
func MasterSecret(preMasterSecret, clientRandom, serverRandom []byte, h HashFunc) ([]byte, error) {
|
||||||
|
seed := append(append([]byte(masterSecretLabel), clientRandom...), serverRandom...)
|
||||||
|
|
||||||
|
return PHash(preMasterSecret, seed, 48, h)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GenerateEncryptionKeys is the final step TLS 1.2 PRF. Given all state generated so far generates
|
||||||
|
// the final keys need for encryption.
|
||||||
|
func GenerateEncryptionKeys(
|
||||||
|
masterSecret, clientRandom, serverRandom []byte,
|
||||||
|
macLen, keyLen, ivLen int,
|
||||||
|
h HashFunc,
|
||||||
|
) (*EncryptionKeys, error) {
|
||||||
|
seed := append(append([]byte(keyExpansionLabel), serverRandom...), clientRandom...)
|
||||||
|
keyMaterial, err := PHash(masterSecret, seed, (2*macLen)+(2*keyLen)+(2*ivLen), h)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
clientMACKey := keyMaterial[:macLen]
|
||||||
|
keyMaterial = keyMaterial[macLen:]
|
||||||
|
|
||||||
|
serverMACKey := keyMaterial[:macLen]
|
||||||
|
keyMaterial = keyMaterial[macLen:]
|
||||||
|
|
||||||
|
clientWriteKey := keyMaterial[:keyLen]
|
||||||
|
keyMaterial = keyMaterial[keyLen:]
|
||||||
|
|
||||||
|
serverWriteKey := keyMaterial[:keyLen]
|
||||||
|
keyMaterial = keyMaterial[keyLen:]
|
||||||
|
|
||||||
|
clientWriteIV := keyMaterial[:ivLen]
|
||||||
|
keyMaterial = keyMaterial[ivLen:]
|
||||||
|
|
||||||
|
serverWriteIV := keyMaterial[:ivLen]
|
||||||
|
|
||||||
|
return &EncryptionKeys{
|
||||||
|
MasterSecret: masterSecret,
|
||||||
|
ClientMACKey: clientMACKey,
|
||||||
|
ServerMACKey: serverMACKey,
|
||||||
|
ClientWriteKey: clientWriteKey,
|
||||||
|
ServerWriteKey: serverWriteKey,
|
||||||
|
ClientWriteIV: clientWriteIV,
|
||||||
|
ServerWriteIV: serverWriteIV,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func prfVerifyData(masterSecret, handshakeBodies []byte, label string, hashFunc HashFunc) ([]byte, error) {
|
||||||
|
h := hashFunc()
|
||||||
|
if _, err := h.Write(handshakeBodies); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
seed := append([]byte(label), h.Sum(nil)...)
|
||||||
|
|
||||||
|
return PHash(masterSecret, seed, 12, hashFunc)
|
||||||
|
}
|
||||||
|
|
||||||
|
// VerifyDataClient is caled on the Client Side to either verify or generate the VerifyData message.
|
||||||
|
func VerifyDataClient(masterSecret, handshakeBodies []byte, h HashFunc) ([]byte, error) {
|
||||||
|
return prfVerifyData(masterSecret, handshakeBodies, verifyDataClientLabel, h)
|
||||||
|
}
|
||||||
|
|
||||||
|
// VerifyDataServer is caled on the Server Side to either verify or generate the VerifyData message.
|
||||||
|
func VerifyDataServer(masterSecret, handshakeBodies []byte, h HashFunc) ([]byte, error) {
|
||||||
|
return prfVerifyData(masterSecret, handshakeBodies, verifyDataServerLabel, h)
|
||||||
|
}
|
||||||
83
vendor/github.com/pion/dtls/v3/pkg/crypto/signature/signature.go
generated
vendored
Normal file
83
vendor/github.com/pion/dtls/v3/pkg/crypto/signature/signature.go
generated
vendored
Normal file
|
|
@ -0,0 +1,83 @@
|
||||||
|
// SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
// Package signature provides our implemented Signature Algorithms
|
||||||
|
package signature
|
||||||
|
|
||||||
|
import "github.com/pion/dtls/v3/pkg/crypto/hash"
|
||||||
|
|
||||||
|
// Algorithm as defined in TLS 1.2
|
||||||
|
// https://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml#tls-parameters-16
|
||||||
|
type Algorithm uint16
|
||||||
|
|
||||||
|
// SignatureAlgorithm enums.
|
||||||
|
const (
|
||||||
|
Anonymous Algorithm = 0
|
||||||
|
RSA Algorithm = 1
|
||||||
|
ECDSA Algorithm = 3
|
||||||
|
Ed25519 Algorithm = 7
|
||||||
|
|
||||||
|
// RSA-PSS (DTLS 1.3 only) - full SignatureScheme values
|
||||||
|
// https://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml#tls-signaturescheme
|
||||||
|
RSA_PSS_RSAE_SHA256 Algorithm = 0x0804 // nolint: staticcheck
|
||||||
|
RSA_PSS_RSAE_SHA384 Algorithm = 0x0805 // nolint: staticcheck
|
||||||
|
RSA_PSS_RSAE_SHA512 Algorithm = 0x0806 // nolint: revive,staticcheck
|
||||||
|
RSA_PSS_PSS_SHA256 Algorithm = 0x0809 // nolint: revive,staticcheck
|
||||||
|
RSA_PSS_PSS_SHA384 Algorithm = 0x080a // nolint: revive,staticcheck
|
||||||
|
RSA_PSS_PSS_SHA512 Algorithm = 0x080b // nolint: revive,staticcheck
|
||||||
|
)
|
||||||
|
|
||||||
|
// Algorithms returns all implemented Signature Algorithms.
|
||||||
|
func Algorithms() map[Algorithm]struct{} {
|
||||||
|
return map[Algorithm]struct{}{
|
||||||
|
Anonymous: {},
|
||||||
|
RSA: {},
|
||||||
|
ECDSA: {},
|
||||||
|
Ed25519: {},
|
||||||
|
RSA_PSS_RSAE_SHA256: {},
|
||||||
|
RSA_PSS_RSAE_SHA384: {},
|
||||||
|
RSA_PSS_RSAE_SHA512: {},
|
||||||
|
RSA_PSS_PSS_SHA256: {},
|
||||||
|
RSA_PSS_PSS_SHA384: {},
|
||||||
|
RSA_PSS_PSS_SHA512: {},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsPSS returns true if the algorithm is an RSA-PSS signature scheme.
|
||||||
|
// It's tempting to check for range between 0x0804 and 0x080b, but 0x0807 is Ed25519
|
||||||
|
// and 0x0808 is Ed448, which are NOT PSS, so we check specific values instead.
|
||||||
|
func (a Algorithm) IsPSS() bool {
|
||||||
|
return a == RSA_PSS_RSAE_SHA256 ||
|
||||||
|
a == RSA_PSS_RSAE_SHA384 ||
|
||||||
|
a == RSA_PSS_RSAE_SHA512 ||
|
||||||
|
a == RSA_PSS_PSS_SHA256 ||
|
||||||
|
a == RSA_PSS_PSS_SHA384 ||
|
||||||
|
a == RSA_PSS_PSS_SHA512
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsUnsupported returns true if the algorithm is a signature scheme that is
|
||||||
|
// not supported by pion/dtls.
|
||||||
|
func (a Algorithm) IsUnsupported() bool {
|
||||||
|
// Skip RSA_PSS_PSS schemes (0x0809-0x080b). We parse them for interoperability
|
||||||
|
// but don't negotiate them to avoid unnecessary complexity for certificates that
|
||||||
|
// don't exist in practice. This follows the pragmatic approach of Go's crypto/tls
|
||||||
|
// and BoringSSL: target real-world WebPKI use cases rather than RFC completeness.
|
||||||
|
return a == RSA_PSS_PSS_SHA256 ||
|
||||||
|
a == RSA_PSS_PSS_SHA384 ||
|
||||||
|
a == RSA_PSS_PSS_SHA512
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetPSSHash returns the hash algorithm associated with an RSA-PSS signature scheme.
|
||||||
|
// Returns hash.None if the algorithm is not an RSA-PSS scheme.
|
||||||
|
func (a Algorithm) GetPSSHash() hash.Algorithm {
|
||||||
|
switch a {
|
||||||
|
case RSA_PSS_RSAE_SHA256, RSA_PSS_PSS_SHA256:
|
||||||
|
return hash.SHA256
|
||||||
|
case RSA_PSS_RSAE_SHA384, RSA_PSS_PSS_SHA384:
|
||||||
|
return hash.SHA384
|
||||||
|
case RSA_PSS_RSAE_SHA512, RSA_PSS_PSS_SHA512:
|
||||||
|
return hash.SHA512
|
||||||
|
default:
|
||||||
|
return hash.None
|
||||||
|
}
|
||||||
|
}
|
||||||
13
vendor/github.com/pion/dtls/v3/pkg/crypto/signaturehash/errors.go
generated
vendored
Normal file
13
vendor/github.com/pion/dtls/v3/pkg/crypto/signaturehash/errors.go
generated
vendored
Normal file
|
|
@ -0,0 +1,13 @@
|
||||||
|
// SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package signaturehash
|
||||||
|
|
||||||
|
import "errors"
|
||||||
|
|
||||||
|
var (
|
||||||
|
errNoAvailableSignatureSchemes = errors.New("connection can not be created, no SignatureScheme satisfy this Config")
|
||||||
|
errInvalidSignatureAlgorithm = errors.New("invalid signature algorithm")
|
||||||
|
errInvalidHashAlgorithm = errors.New("invalid hash algorithm")
|
||||||
|
errInvalidPrivateKey = errors.New("invalid private key type")
|
||||||
|
)
|
||||||
32
vendor/github.com/pion/dtls/v3/pkg/crypto/signaturehash/select_13.go
generated
vendored
Normal file
32
vendor/github.com/pion/dtls/v3/pkg/crypto/signaturehash/select_13.go
generated
vendored
Normal file
|
|
@ -0,0 +1,32 @@
|
||||||
|
// SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package signaturehash
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto"
|
||||||
|
)
|
||||||
|
|
||||||
|
// selectSignatureScheme13 returns most preferred and compatible scheme.
|
||||||
|
// It's compatible with all DTLS versions up to and including 1.3.
|
||||||
|
func selectSignatureScheme13(sigs []Algorithm, privateKey crypto.PrivateKey, is13 bool) (Algorithm, error) {
|
||||||
|
signer, ok := privateKey.(crypto.Signer)
|
||||||
|
if !ok {
|
||||||
|
return Algorithm{}, errInvalidPrivateKey
|
||||||
|
}
|
||||||
|
for _, ss := range sigs {
|
||||||
|
// Skip PSS schemes for DTLS 1.2 (PSS is only supported in DTLS 1.3)
|
||||||
|
if !is13 && ss.Signature.IsPSS() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
// Skip schemes understood but not supported by pion/dtls.
|
||||||
|
if ss.Signature.IsUnsupported() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if ss.isCompatible(signer) {
|
||||||
|
return ss, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Algorithm{}, errNoAvailableSignatureSchemes
|
||||||
|
}
|
||||||
187
vendor/github.com/pion/dtls/v3/pkg/crypto/signaturehash/signaturehash.go
generated
vendored
Normal file
187
vendor/github.com/pion/dtls/v3/pkg/crypto/signaturehash/signaturehash.go
generated
vendored
Normal file
|
|
@ -0,0 +1,187 @@
|
||||||
|
// SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
// Package signaturehash provides the SignatureHashAlgorithm as defined in TLS 1.2
|
||||||
|
package signaturehash
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto"
|
||||||
|
"crypto/ecdsa"
|
||||||
|
"crypto/ed25519"
|
||||||
|
"crypto/rsa"
|
||||||
|
"crypto/tls"
|
||||||
|
"crypto/x509"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/pion/dtls/v3/pkg/crypto/hash"
|
||||||
|
"github.com/pion/dtls/v3/pkg/crypto/signature"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Algorithm is a signature/hash algorithm pairs which may be used in
|
||||||
|
// digital signatures.
|
||||||
|
//
|
||||||
|
// https://tools.ietf.org/html/rfc5246#section-7.4.1.4.1
|
||||||
|
type Algorithm struct {
|
||||||
|
Hash hash.Algorithm
|
||||||
|
Signature signature.Algorithm
|
||||||
|
}
|
||||||
|
|
||||||
|
// Algorithms returns signature algorithms compatible with DTLS 1.2 / TLS 1.2.
|
||||||
|
// This excludes TLS 1.3-specific schemes like RSA-PSS to ensure compatibility
|
||||||
|
// with implementations like OpenSSL that don't recognize TLS 1.3 signature
|
||||||
|
// algorithm IDs in DTLS 1.2 handshakes.
|
||||||
|
//
|
||||||
|
// IMPORTANT: order in this slice determines priority used by SelectSignatureScheme.
|
||||||
|
//
|
||||||
|
// Order follows industry standard preference (ECDSA-first) as used by OpenSSL,
|
||||||
|
// BoringSSL, Firefox, Chrome, and other major TLS implementations.
|
||||||
|
func Algorithms() []Algorithm {
|
||||||
|
return []Algorithm{
|
||||||
|
// ECDSA schemes (modern, efficient - industry standard preference)
|
||||||
|
{hash.SHA256, signature.ECDSA},
|
||||||
|
{hash.SHA384, signature.ECDSA},
|
||||||
|
{hash.SHA512, signature.ECDSA},
|
||||||
|
|
||||||
|
// Ed25519
|
||||||
|
{hash.Ed25519, signature.Ed25519},
|
||||||
|
|
||||||
|
// RSA PKCS#1 v1.5 schemes (legacy, DTLS 1.2)
|
||||||
|
{hash.SHA256, signature.RSA},
|
||||||
|
{hash.SHA384, signature.RSA},
|
||||||
|
{hash.SHA512, signature.RSA},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SelectSignatureScheme returns most preferred and compatible scheme for DTLS <= 1.2.
|
||||||
|
func SelectSignatureScheme(sigs []Algorithm, privateKey crypto.PrivateKey) (Algorithm, error) {
|
||||||
|
return selectSignatureScheme13(sigs, privateKey, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SelectSignatureScheme13 returns most preferred and compatible scheme for DTLS 1.3.
|
||||||
|
func SelectSignatureScheme13(sigs []Algorithm, privateKey crypto.PrivateKey) (Algorithm, error) {
|
||||||
|
return selectSignatureScheme13(sigs, privateKey, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
// isCompatible checks that given private key is compatible with the signature scheme.
|
||||||
|
func (a *Algorithm) isCompatible(signer crypto.Signer) bool {
|
||||||
|
switch signer.Public().(type) {
|
||||||
|
case ed25519.PublicKey:
|
||||||
|
return a.Signature == signature.Ed25519
|
||||||
|
case *ecdsa.PublicKey:
|
||||||
|
return a.Signature == signature.ECDSA
|
||||||
|
case *rsa.PublicKey:
|
||||||
|
// RSA keys are compatible with both PKCS#1 v1.5 and PSS signatures
|
||||||
|
return a.Signature == signature.RSA || a.Signature.IsPSS()
|
||||||
|
default:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParseSignatureSchemes translates []tls.SignatureScheme to []signatureHashAlgorithm.
|
||||||
|
// It returns default signature scheme list if no SignatureScheme is passed.
|
||||||
|
// This function handles both TLS 1.2 byte-split encoding and TLS 1.3 PSS full uint16 schemes.
|
||||||
|
//
|
||||||
|
// For DTLS 1.2 / TLS 1.2, this returns Algorithms() which excludes TLS 1.3-specific
|
||||||
|
// schemes like RSA-PSS for compatibility with implementations like OpenSSL.
|
||||||
|
// When DTLS 1.3 is implemented, use Algorithms13() or create ParseSignatureSchemes13().
|
||||||
|
func ParseSignatureSchemes(sigs []tls.SignatureScheme, insecureHashes bool) ([]Algorithm, error) {
|
||||||
|
if len(sigs) == 0 {
|
||||||
|
return Algorithms(), nil
|
||||||
|
}
|
||||||
|
out := []Algorithm{}
|
||||||
|
for _, ss := range sigs {
|
||||||
|
hashAlg, sigAlg, err := parseSignatureScheme(ss)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if hashAlg.Insecure() && !insecureHashes {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
out = append(out, Algorithm{
|
||||||
|
Hash: hashAlg,
|
||||||
|
Signature: sigAlg,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(out) == 0 {
|
||||||
|
return nil, errNoAvailableSignatureSchemes
|
||||||
|
}
|
||||||
|
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// FromCertificate maps x509.SignatureAlgorithm to the corresponding Algorithm type.
|
||||||
|
func FromCertificate(cert *x509.Certificate) (Algorithm, error) { //nolint:cyclop
|
||||||
|
var hashAlg hash.Algorithm
|
||||||
|
var sigAlg signature.Algorithm
|
||||||
|
|
||||||
|
switch cert.SignatureAlgorithm {
|
||||||
|
case x509.SHA256WithRSA, x509.SHA256WithRSAPSS:
|
||||||
|
hashAlg = hash.SHA256
|
||||||
|
sigAlg = signature.RSA
|
||||||
|
case x509.SHA384WithRSA, x509.SHA384WithRSAPSS:
|
||||||
|
hashAlg = hash.SHA384
|
||||||
|
sigAlg = signature.RSA
|
||||||
|
case x509.SHA512WithRSA, x509.SHA512WithRSAPSS:
|
||||||
|
hashAlg = hash.SHA512
|
||||||
|
sigAlg = signature.RSA
|
||||||
|
case x509.ECDSAWithSHA256:
|
||||||
|
hashAlg = hash.SHA256
|
||||||
|
sigAlg = signature.ECDSA
|
||||||
|
case x509.ECDSAWithSHA384:
|
||||||
|
hashAlg = hash.SHA384
|
||||||
|
sigAlg = signature.ECDSA
|
||||||
|
case x509.ECDSAWithSHA512:
|
||||||
|
hashAlg = hash.SHA512
|
||||||
|
sigAlg = signature.ECDSA
|
||||||
|
case x509.PureEd25519:
|
||||||
|
hashAlg = hash.None // Ed25519 doesn't use a separate hash
|
||||||
|
sigAlg = signature.Ed25519
|
||||||
|
case x509.SHA1WithRSA:
|
||||||
|
hashAlg = hash.SHA1
|
||||||
|
sigAlg = signature.RSA
|
||||||
|
case x509.ECDSAWithSHA1:
|
||||||
|
hashAlg = hash.SHA1
|
||||||
|
sigAlg = signature.ECDSA
|
||||||
|
default:
|
||||||
|
return Algorithm{}, errInvalidSignatureAlgorithm
|
||||||
|
}
|
||||||
|
|
||||||
|
return Algorithm{Hash: hashAlg, Signature: sigAlg}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// parseSignatureScheme translates a tls.SignatureScheme to a hash.Algorithm
|
||||||
|
// and signature.Algorithm. It returns default signature scheme list if no
|
||||||
|
// SignatureScheme is passed. This function handles both TLS 1.2 byte-split
|
||||||
|
// encoding and TLS 1.3 PSS full uint16 schemes.
|
||||||
|
func parseSignatureScheme(sigScheme tls.SignatureScheme) (hash.Algorithm, signature.Algorithm, error) {
|
||||||
|
var sigAlg signature.Algorithm
|
||||||
|
var hashAlg hash.Algorithm
|
||||||
|
|
||||||
|
if signature.Algorithm(sigScheme).IsPSS() {
|
||||||
|
// TLS 1.3 PSS scheme - full uint16 is the signature algorithm
|
||||||
|
sigAlg = signature.Algorithm(sigScheme)
|
||||||
|
hashAlg = hash.ExtractHashFromPSS(uint16(sigScheme))
|
||||||
|
if hashAlg == hash.None {
|
||||||
|
return 0, 0, fmt.Errorf("SignatureScheme %04x: %w", sigScheme, errInvalidHashAlgorithm)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// TLS 1.2 style - split into hash (high byte) and signature (low byte)
|
||||||
|
sigAlg = signature.Algorithm(sigScheme & 0xFF)
|
||||||
|
hashAlg = hash.Algorithm(sigScheme >> 8)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate signature algorithm
|
||||||
|
if _, ok := signature.Algorithms()[sigAlg]; !ok {
|
||||||
|
return 0, 0, fmt.Errorf("SignatureScheme %04x: %w", sigScheme, errInvalidSignatureAlgorithm)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate hash algorithm
|
||||||
|
if _, ok := hash.Algorithms()[hashAlg]; !ok || (ok && hashAlg == hash.None) {
|
||||||
|
return 0, 0, fmt.Errorf("SignatureScheme %04x: %w", sigScheme, errInvalidHashAlgorithm)
|
||||||
|
}
|
||||||
|
|
||||||
|
return hashAlg, sigAlg, nil
|
||||||
|
}
|
||||||
48
vendor/github.com/pion/dtls/v3/pkg/crypto/signaturehash/signaturehash_13.go
generated
vendored
Normal file
48
vendor/github.com/pion/dtls/v3/pkg/crypto/signaturehash/signaturehash_13.go
generated
vendored
Normal file
|
|
@ -0,0 +1,48 @@
|
||||||
|
// SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package signaturehash
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/pion/dtls/v3/pkg/crypto/hash"
|
||||||
|
"github.com/pion/dtls/v3/pkg/crypto/signature"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Algorithms13 returns signature algorithms compatible with DTLS 1.3. This.
|
||||||
|
// includes DTLS 1.3-specific schemes like RSA-PSS in addition to DTLS 1.2 schemes.
|
||||||
|
//
|
||||||
|
// IMPORTANT: order in this slice determines priority used by SelectSignatureScheme13.
|
||||||
|
//
|
||||||
|
// Order follows industry standard preference (ECDSA-first) as used by OpenSSL,
|
||||||
|
// BoringSSL, Firefox, Chrome, and other major TLS 1.3 implementations.
|
||||||
|
func Algorithms13() []Algorithm {
|
||||||
|
return []Algorithm{
|
||||||
|
// ECDSA schemes (modern, efficient - industry standard preference)
|
||||||
|
{hash.SHA256, signature.ECDSA},
|
||||||
|
{hash.SHA384, signature.ECDSA},
|
||||||
|
{hash.SHA512, signature.ECDSA},
|
||||||
|
|
||||||
|
// Ed25519
|
||||||
|
{hash.Ed25519, signature.Ed25519},
|
||||||
|
|
||||||
|
// RSA-PSS RSAE schemes (TLS 1.3 / DTLS 1.3 compatible with standard RSA certs)
|
||||||
|
// Note: We only offer RSA_PSS_RSAE variants (0x0804-0x0806), not RSA_PSS_PSS
|
||||||
|
// (0x0809-0x080b). RSA-PSS certificates with OID id-RSASSA-PSS are virtually
|
||||||
|
// unused in the real world and are not allowed by the CA/Browser Forum Baseline
|
||||||
|
// Requirements for WebPKI. We avoid unnecessary complexity for certificates that
|
||||||
|
// don't exist in practice, following the pragmatic approach of Go's crypto/tls
|
||||||
|
// and BoringSSL: target real-world WebPKI use cases rather than RFC completeness.
|
||||||
|
// RSA_PSS_PSS schemes are parsed for wire-format compatibility but never negotiated.
|
||||||
|
{hash.SHA256, signature.RSA_PSS_RSAE_SHA256},
|
||||||
|
{hash.SHA384, signature.RSA_PSS_RSAE_SHA384},
|
||||||
|
{hash.SHA512, signature.RSA_PSS_RSAE_SHA512},
|
||||||
|
// {hash.SHA256, signature.RSA_PSS_PSS_SHA256},
|
||||||
|
// {hash.SHA384, signature.RSA_PSS_PSS_SHA384},
|
||||||
|
// {hash.SHA512, signature.RSA_PSS_PSS_SHA512},
|
||||||
|
|
||||||
|
// RSA PKCS#1 v1.5 schemes (backward compatibility with DTLS 1.2)
|
||||||
|
{hash.SHA256, signature.RSA},
|
||||||
|
{hash.SHA384, signature.RSA},
|
||||||
|
{hash.SHA512, signature.RSA},
|
||||||
|
}
|
||||||
|
}
|
||||||
111
vendor/github.com/pion/dtls/v3/pkg/net/net.go
generated
vendored
Normal file
111
vendor/github.com/pion/dtls/v3/pkg/net/net.go
generated
vendored
Normal file
|
|
@ -0,0 +1,111 @@
|
||||||
|
// SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
// Package net defines packet-oriented primitives that are compatible with net
|
||||||
|
// in the standard library.
|
||||||
|
package net
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// A PacketListener is the same as net.Listener but returns a net.PacketConn on
|
||||||
|
// Accept() rather than a net.Conn.
|
||||||
|
//
|
||||||
|
// Multiple goroutines may invoke methods on a PacketListener simultaneously.
|
||||||
|
type PacketListener interface {
|
||||||
|
// Accept waits for and returns the next connection to the listener.
|
||||||
|
Accept() (net.PacketConn, net.Addr, error)
|
||||||
|
|
||||||
|
// Close closes the listener.
|
||||||
|
// Any blocked Accept operations will be unblocked and return errors.
|
||||||
|
Close() error
|
||||||
|
|
||||||
|
// Addr returns the listener's network address.
|
||||||
|
Addr() net.Addr
|
||||||
|
}
|
||||||
|
|
||||||
|
// PacketListenerFromListener converts a net.Listener into a
|
||||||
|
// dtlsnet.PacketListener.
|
||||||
|
func PacketListenerFromListener(l net.Listener) PacketListener {
|
||||||
|
return &packetListenerWrapper{
|
||||||
|
l: l,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// packetListenerWrapper wraps a net.Listener and implements
|
||||||
|
// dtlsnet.PacketListener.
|
||||||
|
type packetListenerWrapper struct {
|
||||||
|
l net.Listener
|
||||||
|
}
|
||||||
|
|
||||||
|
// Accept calls Accept on the underlying net.Listener and converts the returned
|
||||||
|
// net.Conn into a net.PacketConn.
|
||||||
|
func (p *packetListenerWrapper) Accept() (net.PacketConn, net.Addr, error) {
|
||||||
|
c, err := p.l.Accept()
|
||||||
|
if err != nil {
|
||||||
|
return PacketConnFromConn(c), nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return PacketConnFromConn(c), c.RemoteAddr(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close closes the underlying net.Listener.
|
||||||
|
func (p *packetListenerWrapper) Close() error {
|
||||||
|
return p.l.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Addr returns the address of the underlying net.Listener.
|
||||||
|
func (p *packetListenerWrapper) Addr() net.Addr {
|
||||||
|
return p.l.Addr()
|
||||||
|
}
|
||||||
|
|
||||||
|
// PacketConnFromConn converts a net.Conn into a net.PacketConn.
|
||||||
|
func PacketConnFromConn(conn net.Conn) net.PacketConn {
|
||||||
|
return &packetConnWrapper{conn}
|
||||||
|
}
|
||||||
|
|
||||||
|
// packetConnWrapper wraps a net.Conn and implements net.PacketConn.
|
||||||
|
type packetConnWrapper struct {
|
||||||
|
conn net.Conn
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReadFrom reads from the underlying net.Conn and returns its remote address.
|
||||||
|
func (p *packetConnWrapper) ReadFrom(b []byte) (int, net.Addr, error) {
|
||||||
|
n, err := p.conn.Read(b)
|
||||||
|
|
||||||
|
return n, p.conn.RemoteAddr(), err
|
||||||
|
}
|
||||||
|
|
||||||
|
// WriteTo writes to the underlying net.Conn.
|
||||||
|
func (p *packetConnWrapper) WriteTo(b []byte, _ net.Addr) (int, error) {
|
||||||
|
n, err := p.conn.Write(b)
|
||||||
|
|
||||||
|
return n, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close closes the underlying net.Conn.
|
||||||
|
func (p *packetConnWrapper) Close() error {
|
||||||
|
return p.conn.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
// LocalAddr returns the local address of the underlying net.Conn.
|
||||||
|
func (p *packetConnWrapper) LocalAddr() net.Addr {
|
||||||
|
return p.conn.LocalAddr()
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetDeadline sets the deadline on the underlying net.Conn.
|
||||||
|
func (p *packetConnWrapper) SetDeadline(t time.Time) error {
|
||||||
|
return p.conn.SetDeadline(t)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetReadDeadline sets the read deadline on the underlying net.Conn.
|
||||||
|
func (p *packetConnWrapper) SetReadDeadline(t time.Time) error {
|
||||||
|
return p.conn.SetReadDeadline(t)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetWriteDeadline sets the write deadline on the underlying net.Conn.
|
||||||
|
func (p *packetConnWrapper) SetWriteDeadline(t time.Time) error {
|
||||||
|
return p.conn.SetWriteDeadline(t)
|
||||||
|
}
|
||||||
167
vendor/github.com/pion/dtls/v3/pkg/protocol/alert/alert.go
generated
vendored
Normal file
167
vendor/github.com/pion/dtls/v3/pkg/protocol/alert/alert.go
generated
vendored
Normal file
|
|
@ -0,0 +1,167 @@
|
||||||
|
// SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
// Package alert implements TLS alert protocol https://tools.ietf.org/html/rfc5246#section-7.2
|
||||||
|
package alert
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/pion/dtls/v3/pkg/protocol"
|
||||||
|
)
|
||||||
|
|
||||||
|
var errBufferTooSmall = &protocol.TemporaryError{Err: errors.New("buffer is too small")} //nolint:err113
|
||||||
|
|
||||||
|
// Level is the level of the TLS Alert.
|
||||||
|
type Level byte
|
||||||
|
|
||||||
|
// Level enums.
|
||||||
|
const (
|
||||||
|
Warning Level = 1
|
||||||
|
Fatal Level = 2
|
||||||
|
)
|
||||||
|
|
||||||
|
func (l Level) String() string {
|
||||||
|
switch l {
|
||||||
|
case Warning:
|
||||||
|
return "Warning"
|
||||||
|
case Fatal:
|
||||||
|
return "Fatal"
|
||||||
|
default:
|
||||||
|
return "Invalid alert level"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Description is the extended info of the TLS Alert.
|
||||||
|
type Description byte
|
||||||
|
|
||||||
|
// Description enums.
|
||||||
|
const (
|
||||||
|
CloseNotify Description = 0
|
||||||
|
UnexpectedMessage Description = 10
|
||||||
|
BadRecordMac Description = 20
|
||||||
|
DecryptionFailed Description = 21
|
||||||
|
RecordOverflow Description = 22
|
||||||
|
DecompressionFailure Description = 30
|
||||||
|
HandshakeFailure Description = 40
|
||||||
|
NoCertificate Description = 41
|
||||||
|
BadCertificate Description = 42
|
||||||
|
UnsupportedCertificate Description = 43
|
||||||
|
CertificateRevoked Description = 44
|
||||||
|
CertificateExpired Description = 45
|
||||||
|
CertificateUnknown Description = 46
|
||||||
|
IllegalParameter Description = 47
|
||||||
|
UnknownCA Description = 48
|
||||||
|
AccessDenied Description = 49
|
||||||
|
DecodeError Description = 50
|
||||||
|
DecryptError Description = 51
|
||||||
|
ExportRestriction Description = 60
|
||||||
|
ProtocolVersion Description = 70
|
||||||
|
InsufficientSecurity Description = 71
|
||||||
|
InternalError Description = 80
|
||||||
|
UserCanceled Description = 90
|
||||||
|
NoRenegotiation Description = 100
|
||||||
|
UnsupportedExtension Description = 110
|
||||||
|
NoApplicationProtocol Description = 120
|
||||||
|
)
|
||||||
|
|
||||||
|
func (d Description) String() string { //nolint:cyclop
|
||||||
|
switch d {
|
||||||
|
case CloseNotify:
|
||||||
|
return "CloseNotify"
|
||||||
|
case UnexpectedMessage:
|
||||||
|
return "UnexpectedMessage"
|
||||||
|
case BadRecordMac:
|
||||||
|
return "BadRecordMac"
|
||||||
|
case DecryptionFailed:
|
||||||
|
return "DecryptionFailed"
|
||||||
|
case RecordOverflow:
|
||||||
|
return "RecordOverflow"
|
||||||
|
case DecompressionFailure:
|
||||||
|
return "DecompressionFailure"
|
||||||
|
case HandshakeFailure:
|
||||||
|
return "HandshakeFailure"
|
||||||
|
case NoCertificate:
|
||||||
|
return "NoCertificate"
|
||||||
|
case BadCertificate:
|
||||||
|
return "BadCertificate"
|
||||||
|
case UnsupportedCertificate:
|
||||||
|
return "UnsupportedCertificate"
|
||||||
|
case CertificateRevoked:
|
||||||
|
return "CertificateRevoked"
|
||||||
|
case CertificateExpired:
|
||||||
|
return "CertificateExpired"
|
||||||
|
case CertificateUnknown:
|
||||||
|
return "CertificateUnknown"
|
||||||
|
case IllegalParameter:
|
||||||
|
return "IllegalParameter"
|
||||||
|
case UnknownCA:
|
||||||
|
return "UnknownCA"
|
||||||
|
case AccessDenied:
|
||||||
|
return "AccessDenied"
|
||||||
|
case DecodeError:
|
||||||
|
return "DecodeError"
|
||||||
|
case DecryptError:
|
||||||
|
return "DecryptError"
|
||||||
|
case ExportRestriction:
|
||||||
|
return "ExportRestriction"
|
||||||
|
case ProtocolVersion:
|
||||||
|
return "ProtocolVersion"
|
||||||
|
case InsufficientSecurity:
|
||||||
|
return "InsufficientSecurity"
|
||||||
|
case InternalError:
|
||||||
|
return "InternalError"
|
||||||
|
case UserCanceled:
|
||||||
|
return "UserCanceled"
|
||||||
|
case NoRenegotiation:
|
||||||
|
return "NoRenegotiation"
|
||||||
|
case UnsupportedExtension:
|
||||||
|
return "UnsupportedExtension"
|
||||||
|
case NoApplicationProtocol:
|
||||||
|
return "NoApplicationProtocol"
|
||||||
|
default:
|
||||||
|
return "Invalid alert description"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Alert is one of the content types supported by the TLS record layer.
|
||||||
|
// Alert messages convey the severity of the message
|
||||||
|
// (warning or fatal) and a description of the alert. Alert messages
|
||||||
|
// with a level of fatal result in the immediate termination of the
|
||||||
|
// connection. In this case, other connections corresponding to the
|
||||||
|
// session may continue, but the session identifier MUST be invalidated,
|
||||||
|
// preventing the failed session from being used to establish new
|
||||||
|
// connections. Like other messages, alert messages are encrypted and
|
||||||
|
// compressed, as specified by the current connection state.
|
||||||
|
// https://tools.ietf.org/html/rfc5246#section-7.2
|
||||||
|
type Alert struct {
|
||||||
|
Level Level
|
||||||
|
Description Description
|
||||||
|
}
|
||||||
|
|
||||||
|
// ContentType returns the ContentType of this Content.
|
||||||
|
func (a Alert) ContentType() protocol.ContentType {
|
||||||
|
return protocol.ContentTypeAlert
|
||||||
|
}
|
||||||
|
|
||||||
|
// Marshal returns the encoded alert.
|
||||||
|
func (a *Alert) Marshal() ([]byte, error) {
|
||||||
|
return []byte{byte(a.Level), byte(a.Description)}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unmarshal populates the alert from binary data.
|
||||||
|
func (a *Alert) Unmarshal(data []byte) error {
|
||||||
|
if len(data) != 2 {
|
||||||
|
return errBufferTooSmall
|
||||||
|
}
|
||||||
|
|
||||||
|
a.Level = Level(data[0])
|
||||||
|
a.Description = Description(data[1])
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *Alert) String() string {
|
||||||
|
return fmt.Sprintf("Alert %s: %s", a.Level, a.Description)
|
||||||
|
}
|
||||||
30
vendor/github.com/pion/dtls/v3/pkg/protocol/application_data.go
generated
vendored
Normal file
30
vendor/github.com/pion/dtls/v3/pkg/protocol/application_data.go
generated
vendored
Normal file
|
|
@ -0,0 +1,30 @@
|
||||||
|
// SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package protocol
|
||||||
|
|
||||||
|
// ApplicationData messages are carried by the record layer and are
|
||||||
|
// fragmented, compressed, and encrypted based on the current connection
|
||||||
|
// state. The messages are treated as transparent data to the record
|
||||||
|
// layer.
|
||||||
|
// https://tools.ietf.org/html/rfc5246#section-10
|
||||||
|
type ApplicationData struct {
|
||||||
|
Data []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
// ContentType returns the ContentType of this content.
|
||||||
|
func (a ApplicationData) ContentType() ContentType {
|
||||||
|
return ContentTypeApplicationData
|
||||||
|
}
|
||||||
|
|
||||||
|
// Marshal encodes the ApplicationData to binary.
|
||||||
|
func (a *ApplicationData) Marshal() ([]byte, error) {
|
||||||
|
return append([]byte{}, a.Data...), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unmarshal populates the ApplicationData from binary.
|
||||||
|
func (a *ApplicationData) Unmarshal(data []byte) error {
|
||||||
|
a.Data = append([]byte{}, data...)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
30
vendor/github.com/pion/dtls/v3/pkg/protocol/change_cipher_spec.go
generated
vendored
Normal file
30
vendor/github.com/pion/dtls/v3/pkg/protocol/change_cipher_spec.go
generated
vendored
Normal file
|
|
@ -0,0 +1,30 @@
|
||||||
|
// SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package protocol
|
||||||
|
|
||||||
|
// ChangeCipherSpec protocol exists to signal transitions in
|
||||||
|
// ciphering strategies. The protocol consists of a single message,
|
||||||
|
// which is encrypted and compressed under the current (not the pending)
|
||||||
|
// connection state. The message consists of a single byte of value 1.
|
||||||
|
// https://tools.ietf.org/html/rfc5246#section-7.1
|
||||||
|
type ChangeCipherSpec struct{}
|
||||||
|
|
||||||
|
// ContentType returns the ContentType of this content.
|
||||||
|
func (c ChangeCipherSpec) ContentType() ContentType {
|
||||||
|
return ContentTypeChangeCipherSpec
|
||||||
|
}
|
||||||
|
|
||||||
|
// Marshal encodes the ChangeCipherSpec to binary.
|
||||||
|
func (c *ChangeCipherSpec) Marshal() ([]byte, error) {
|
||||||
|
return []byte{0x01}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unmarshal populates the ChangeCipherSpec from binary.
|
||||||
|
func (c *ChangeCipherSpec) Unmarshal(data []byte) error {
|
||||||
|
if len(data) == 1 && data[0] == 0x01 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return errInvalidCipherSpec
|
||||||
|
}
|
||||||
53
vendor/github.com/pion/dtls/v3/pkg/protocol/compression_method.go
generated
vendored
Normal file
53
vendor/github.com/pion/dtls/v3/pkg/protocol/compression_method.go
generated
vendored
Normal file
|
|
@ -0,0 +1,53 @@
|
||||||
|
// SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package protocol
|
||||||
|
|
||||||
|
// CompressionMethodID is the ID for a CompressionMethod.
|
||||||
|
type CompressionMethodID byte
|
||||||
|
|
||||||
|
const (
|
||||||
|
compressionMethodNull CompressionMethodID = 0
|
||||||
|
)
|
||||||
|
|
||||||
|
// CompressionMethod represents a TLS Compression Method.
|
||||||
|
type CompressionMethod struct {
|
||||||
|
ID CompressionMethodID
|
||||||
|
}
|
||||||
|
|
||||||
|
// CompressionMethods returns all supported CompressionMethods.
|
||||||
|
func CompressionMethods() map[CompressionMethodID]*CompressionMethod {
|
||||||
|
return map[CompressionMethodID]*CompressionMethod{
|
||||||
|
compressionMethodNull: {ID: compressionMethodNull},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// DecodeCompressionMethods the given compression methods.
|
||||||
|
func DecodeCompressionMethods(buf []byte) ([]*CompressionMethod, error) {
|
||||||
|
if len(buf) < 1 {
|
||||||
|
return nil, errBufferTooSmall
|
||||||
|
}
|
||||||
|
compressionMethodsCount := int(buf[0])
|
||||||
|
c := []*CompressionMethod{}
|
||||||
|
for i := 0; i < compressionMethodsCount; i++ {
|
||||||
|
if len(buf) <= i+1 {
|
||||||
|
return nil, errBufferTooSmall
|
||||||
|
}
|
||||||
|
id := CompressionMethodID(buf[i+1])
|
||||||
|
if compressionMethod, ok := CompressionMethods()[id]; ok {
|
||||||
|
c = append(c, compressionMethod)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return c, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// EncodeCompressionMethods the given compression methods.
|
||||||
|
func EncodeCompressionMethods(c []*CompressionMethod) []byte {
|
||||||
|
out := []byte{byte(len(c))}
|
||||||
|
for i := len(c); i > 0; i-- {
|
||||||
|
out = append(out, byte(c[i-1].ID))
|
||||||
|
}
|
||||||
|
|
||||||
|
return out
|
||||||
|
}
|
||||||
25
vendor/github.com/pion/dtls/v3/pkg/protocol/content.go
generated
vendored
Normal file
25
vendor/github.com/pion/dtls/v3/pkg/protocol/content.go
generated
vendored
Normal file
|
|
@ -0,0 +1,25 @@
|
||||||
|
// SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package protocol
|
||||||
|
|
||||||
|
// ContentType represents the IANA Registered ContentTypes
|
||||||
|
//
|
||||||
|
// https://tools.ietf.org/html/rfc4346#section-6.2.1
|
||||||
|
type ContentType uint8
|
||||||
|
|
||||||
|
// ContentType enums.
|
||||||
|
const (
|
||||||
|
ContentTypeChangeCipherSpec ContentType = 20
|
||||||
|
ContentTypeAlert ContentType = 21
|
||||||
|
ContentTypeHandshake ContentType = 22
|
||||||
|
ContentTypeApplicationData ContentType = 23
|
||||||
|
ContentTypeConnectionID ContentType = 25
|
||||||
|
)
|
||||||
|
|
||||||
|
// Content is the top level distinguisher for a DTLS Datagram.
|
||||||
|
type Content interface {
|
||||||
|
ContentType() ContentType
|
||||||
|
Marshal() ([]byte, error)
|
||||||
|
Unmarshal(data []byte) error
|
||||||
|
}
|
||||||
112
vendor/github.com/pion/dtls/v3/pkg/protocol/errors.go
generated
vendored
Normal file
112
vendor/github.com/pion/dtls/v3/pkg/protocol/errors.go
generated
vendored
Normal file
|
|
@ -0,0 +1,112 @@
|
||||||
|
// SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package protocol
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
errBufferTooSmall = &TemporaryError{Err: errors.New("buffer is too small")} //nolint:err113
|
||||||
|
errInvalidCipherSpec = &FatalError{Err: errors.New("cipher spec invalid")} //nolint:err113
|
||||||
|
)
|
||||||
|
|
||||||
|
// FatalError indicates that the DTLS connection is no longer available.
|
||||||
|
// It is mainly caused by wrong configuration of server or client.
|
||||||
|
type FatalError struct {
|
||||||
|
Err error
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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 struct {
|
||||||
|
Err error
|
||||||
|
}
|
||||||
|
|
||||||
|
// TemporaryError indicates that the DTLS connection is still available, but the request was failed temporary.
|
||||||
|
type TemporaryError struct {
|
||||||
|
Err error
|
||||||
|
}
|
||||||
|
|
||||||
|
// TimeoutError indicates that the request was timed out.
|
||||||
|
type TimeoutError struct {
|
||||||
|
Err error
|
||||||
|
}
|
||||||
|
|
||||||
|
// HandshakeError indicates that the handshake failed.
|
||||||
|
type HandshakeError struct {
|
||||||
|
Err error
|
||||||
|
}
|
||||||
|
|
||||||
|
// Timeout implements net.Error.Timeout().
|
||||||
|
func (*FatalError) Timeout() bool { return false }
|
||||||
|
|
||||||
|
// Temporary implements net.Error.Temporary().
|
||||||
|
func (*FatalError) Temporary() bool { return false }
|
||||||
|
|
||||||
|
// Unwrap implements Go1.13 error unwrapper.
|
||||||
|
func (e *FatalError) Unwrap() error { return e.Err }
|
||||||
|
|
||||||
|
func (e *FatalError) Error() string { return fmt.Sprintf("dtls fatal: %v", e.Err) }
|
||||||
|
|
||||||
|
// Timeout implements net.Error.Timeout().
|
||||||
|
func (*InternalError) Timeout() bool { return false }
|
||||||
|
|
||||||
|
// Temporary implements net.Error.Temporary().
|
||||||
|
func (*InternalError) Temporary() bool { return false }
|
||||||
|
|
||||||
|
// Unwrap implements Go1.13 error unwrapper.
|
||||||
|
func (e *InternalError) Unwrap() error { return e.Err }
|
||||||
|
|
||||||
|
func (e *InternalError) Error() string { return fmt.Sprintf("dtls internal: %v", e.Err) }
|
||||||
|
|
||||||
|
// Timeout implements net.Error.Timeout().
|
||||||
|
func (*TemporaryError) Timeout() bool { return false }
|
||||||
|
|
||||||
|
// Temporary implements net.Error.Temporary().
|
||||||
|
func (*TemporaryError) Temporary() bool { return true }
|
||||||
|
|
||||||
|
// Unwrap implements Go1.13 error unwrapper.
|
||||||
|
func (e *TemporaryError) Unwrap() error { return e.Err }
|
||||||
|
|
||||||
|
func (e *TemporaryError) Error() string { return fmt.Sprintf("dtls temporary: %v", e.Err) }
|
||||||
|
|
||||||
|
// Timeout implements net.Error.Timeout().
|
||||||
|
func (*TimeoutError) Timeout() bool { return true }
|
||||||
|
|
||||||
|
// Temporary implements net.Error.Temporary().
|
||||||
|
func (*TimeoutError) Temporary() bool { return true }
|
||||||
|
|
||||||
|
// Unwrap implements Go1.13 error unwrapper.
|
||||||
|
func (e *TimeoutError) Unwrap() error { return e.Err }
|
||||||
|
|
||||||
|
func (e *TimeoutError) Error() string { return fmt.Sprintf("dtls timeout: %v", e.Err) }
|
||||||
|
|
||||||
|
// Timeout implements net.Error.Timeout().
|
||||||
|
func (e *HandshakeError) Timeout() bool {
|
||||||
|
var netErr net.Error
|
||||||
|
if errors.As(e.Err, &netErr) {
|
||||||
|
return netErr.Timeout()
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Temporary implements net.Error.Temporary().
|
||||||
|
func (e *HandshakeError) Temporary() bool {
|
||||||
|
var netErr net.Error
|
||||||
|
if errors.As(e.Err, &netErr) {
|
||||||
|
return netErr.Temporary() //nolint
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unwrap implements Go1.13 error unwrapper.
|
||||||
|
func (e *HandshakeError) Unwrap() error { return e.Err }
|
||||||
|
|
||||||
|
func (e *HandshakeError) Error() string { return fmt.Sprintf("handshake error: %v", e.Err) }
|
||||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue