diff options
Diffstat (limited to 'vendor/github.com/pion/srtp/v2')
27 files changed, 2232 insertions, 0 deletions
diff --git a/vendor/github.com/pion/srtp/v2/.gitignore b/vendor/github.com/pion/srtp/v2/.gitignore new file mode 100644 index 0000000..83db74b --- /dev/null +++ b/vendor/github.com/pion/srtp/v2/.gitignore @@ -0,0 +1,24 @@ +### JetBrains IDE ### +##################### +.idea/ + +### Emacs Temporary Files ### +############################# +*~ + +### Folders ### +############### +bin/ +vendor/ +node_modules/ + +### Files ### +############# +*.ivf +*.ogg +tags +cover.out +*.sw[poe] +*.wasm +examples/sfu-ws/cert.pem +examples/sfu-ws/key.pem diff --git a/vendor/github.com/pion/srtp/v2/.golangci.yml b/vendor/github.com/pion/srtp/v2/.golangci.yml new file mode 100644 index 0000000..d6162c9 --- /dev/null +++ b/vendor/github.com/pion/srtp/v2/.golangci.yml @@ -0,0 +1,89 @@ +linters-settings: + govet: + check-shadowing: true + misspell: + locale: US + exhaustive: + default-signifies-exhaustive: true + gomodguard: + blocked: + modules: + - github.com/pkg/errors: + recommendations: + - errors + +linters: + enable: + - asciicheck # Simple linter to check that your code does not contain non-ASCII identifiers + - bodyclose # checks whether HTTP response body is closed successfully + - deadcode # Finds unused code + - depguard # Go linter that checks if package imports are in a list of acceptable packages + - dogsled # Checks assignments with too many blank identifiers (e.g. x, _, _, _, := f()) + - dupl # Tool for code clone detection + - errcheck # Errcheck is a program for checking for unchecked errors in go programs. These unchecked errors can be critical bugs in some cases + - exhaustive # check exhaustiveness of enum switch statements + - exportloopref # checks for pointers to enclosing loop variables + - gci # Gci control golang package import order and make it always deterministic. + - gochecknoglobals # Checks that no globals are present in Go code + - gochecknoinits # Checks that no init functions are present in Go code + - gocognit # Computes and checks the cognitive complexity of functions + - goconst # Finds repeated strings that could be replaced by a constant + - gocritic # The most opinionated Go source code linter + - godox # Tool for detection of FIXME, TODO and other comment keywords + - goerr113 # Golang linter to check the errors handling expressions + - gofmt # Gofmt checks whether code was gofmt-ed. By default this tool runs with -s option to check for code simplification + - gofumpt # Gofumpt checks whether code was gofumpt-ed. + - goheader # Checks is file header matches to pattern + - goimports # Goimports does everything that gofmt does. Additionally it checks unused imports + - golint # Golint differs from gofmt. Gofmt reformats Go source code, whereas golint prints out style mistakes + - gomodguard # Allow and block list linter for direct Go module dependencies. This is different from depguard where there are different block types for example version constraints and module recommendations. + - goprintffuncname # Checks that printf-like functions are named with `f` at the end + - gosec # Inspects source code for security problems + - gosimple # Linter for Go source code that specializes in simplifying a code + - govet # Vet examines Go source code and reports suspicious constructs, such as Printf calls whose arguments do not align with the format string + - ineffassign # Detects when assignments to existing variables are not used + - misspell # Finds commonly misspelled English words in comments + - nakedret # Finds naked returns in functions greater than a specified function length + - noctx # noctx finds sending http request without context.Context + - scopelint # Scopelint checks for unpinned variables in go programs + - staticcheck # Staticcheck is a go vet on steroids, applying a ton of static analysis checks + - structcheck # Finds unused struct fields + - stylecheck # Stylecheck is a replacement for golint + - typecheck # Like the front-end of a Go compiler, parses and type-checks Go code + - unconvert # Remove unnecessary type conversions + - unparam # Reports unused function parameters + - unused # Checks Go code for unused constants, variables, functions and types + - varcheck # Finds unused global variables and constants + - whitespace # Tool for detection of leading and trailing whitespace + disable: + - funlen # Tool for detection of long functions + - gocyclo # Computes and checks the cyclomatic complexity of functions + - godot # Check if comments end in a period + - gomnd # An analyzer to detect magic numbers. + - lll # Reports long lines + - maligned # Tool to detect Go structs that would take less memory if their fields were sorted + - nestif # Reports deeply nested if statements + - nlreturn # nlreturn checks for a new line before return and branch statements to increase code clarity + - nolintlint # Reports ill-formed or insufficient nolint directives + - prealloc # Finds slice declarations that could potentially be preallocated + - rowserrcheck # checks whether Err of rows is checked successfully + - sqlclosecheck # Checks that sql.Rows and sql.Stmt are closed. + - testpackage # linter that makes you use a separate _test package + - wsl # Whitespace Linter - Forces you to use empty lines! + +issues: + exclude-use-default: false + exclude-rules: + # Allow complex tests, better to be self contained + - path: _test\.go + linters: + - gocognit + + # Allow complex main function in examples + - path: examples + text: "of func `main` is high" + linters: + - gocognit + +run: + skip-dirs-use-default: false diff --git a/vendor/github.com/pion/srtp/v2/DESIGN.md b/vendor/github.com/pion/srtp/v2/DESIGN.md new file mode 100644 index 0000000..8742540 --- /dev/null +++ b/vendor/github.com/pion/srtp/v2/DESIGN.md @@ -0,0 +1,20 @@ +<h1 align="center"> + Design +</h1> + +### Portable +Pion SRTP is written in Go and extremely portable. Anywhere Golang runs, Pion SRTP should work as well! Instead of dealing with complicated +cross-compiling of multiple libraries, you now can run anywhere with one `go build` + +### Simple API +The API is based on an io.ReadWriteCloser. + +### Readable +If code comes from an RFC we try to make sure everything is commented with a link to the spec. +This makes learning and debugging easier, this library was written to also serve as a guide for others. + +### Tested +Every commit is tested via travis-ci Go provides fantastic facilities for testing, and more will be added as time goes on. + +### Shared libraries +Every pion product is built using shared libraries, allowing others to review and reuse our libraries. diff --git a/vendor/github.com/pion/srtp/v2/LICENSE b/vendor/github.com/pion/srtp/v2/LICENSE new file mode 100644 index 0000000..ab60297 --- /dev/null +++ b/vendor/github.com/pion/srtp/v2/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2018 + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/vendor/github.com/pion/srtp/v2/README.md b/vendor/github.com/pion/srtp/v2/README.md new file mode 100644 index 0000000..19551e3 --- /dev/null +++ b/vendor/github.com/pion/srtp/v2/README.md @@ -0,0 +1,54 @@ +<h1 align="center"> + <br> + Pion SRTP + <br> +</h1> +<h4 align="center">A Go implementation of SRTP</h4> +<p align="center"> + <a href="https://pion.ly"><img src="https://img.shields.io/badge/pion-srtp-gray.svg?longCache=true&colorB=brightgreen" alt="Pion SRTP"></a> + <a href="https://sourcegraph.com/github.com/pion/srtp?badge"><img src="https://sourcegraph.com/github.com/pion/srtp/-/badge.svg" alt="Sourcegraph Widget"></a> + <a href="https://pion.ly/slack"><img src="https://img.shields.io/badge/join-us%20on%20slack-gray.svg?longCache=true&logo=slack&colorB=brightgreen" alt="Slack Widget"></a> + <br> + <a href="https://travis-ci.org/pion/srtp"><img src="https://travis-ci.org/pion/srtp.svg?branch=master" alt="Build Status"></a> + <a href="https://pkg.go.dev/github.com/pion/srtp"><img src="https://godoc.org/github.com/pion/srtp?status.svg" alt="GoDoc"></a> + <a href="https://codecov.io/gh/pion/srtp"><img src="https://codecov.io/gh/pion/srtp/branch/master/graph/badge.svg" alt="Coverage Status"></a> + <a href="https://goreportcard.com/report/github.com/pion/srtp"><img src="https://goreportcard.com/badge/github.com/pion/srtp" 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> + +See [DESIGN.md](DESIGN.md) for an overview of features and future goals. + +### 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 [Golang Slack](https://invite.slack.golangbridge.org/). Sign up and join the **#pion** channel for discussions and support. You can also use [Pion mailing list](https://groups.google.com/forum/#!forum/pion). + +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: + +* [Sean DuBois](https://github.com/Sean-Der) - *Original Author* +* [Michiel De Backker](https://github.com/backkem) - *io.Writer interfaces* +* [Tobias Fridén](https://github.com/tobiasfriden) *SRTP authentication verification* +* [chenkaiC4](https://github.com/chenkaiC4) - *Fix GolangCI Linter* +* [Luke Curley](https://github.com/kixelated) - *Performance* +* [Chris Hiszpanski](https://github.com/thinkski) - *Fix out-of-bounds access* +* [Yutaka Takeda](https://github.com/enobufs) - *Fix log messages* +* [Max Hawkins](https://github.com/maxhawkins) +* [Woodrow Douglass](https://github.com/wdouglass) +* [Hugo Arregui](https://github.com/hugoArregui) +* [Atsushi Watanabe](https://github.com/at-wat) +* [Novel Corpse](https://github.com/NovelCorpse) +* [Jerko Steiner](https://github.com/jeremija) +* [Juliusz Chroboczek](https://github.com/jech) +* [Mission Liao](https://github.com/mission-liao) +* [Orlando](https://github.com/OrlandoCo) +* [Tarrence van As](https://github.com/tarrencev) + +### License +MIT License - see [LICENSE](LICENSE) for full text diff --git a/vendor/github.com/pion/srtp/v2/codecov.yml b/vendor/github.com/pion/srtp/v2/codecov.yml new file mode 100644 index 0000000..085200a --- /dev/null +++ b/vendor/github.com/pion/srtp/v2/codecov.yml @@ -0,0 +1,20 @@ +# +# DO NOT EDIT THIS FILE +# +# It is automatically copied from https://github.com/pion/.goassets repository. +# + +coverage: + status: + project: + default: + # Allow decreasing 2% of total coverage to avoid noise. + threshold: 2% + patch: + default: + target: 70% + only_pulls: true + +ignore: + - "examples/*" + - "examples/**/*" diff --git a/vendor/github.com/pion/srtp/v2/context.go b/vendor/github.com/pion/srtp/v2/context.go new file mode 100644 index 0000000..63566de --- /dev/null +++ b/vendor/github.com/pion/srtp/v2/context.go @@ -0,0 +1,196 @@ +package srtp + +import ( + "fmt" + + "github.com/pion/transport/replaydetector" +) + +const ( + labelSRTPEncryption = 0x00 + labelSRTPAuthenticationTag = 0x01 + labelSRTPSalt = 0x02 + + labelSRTCPEncryption = 0x03 + labelSRTCPAuthenticationTag = 0x04 + labelSRTCPSalt = 0x05 + + maxROCDisorder = 100 + maxSequenceNumber = 65535 + + srtcpIndexSize = 4 +) + +// Encrypt/Decrypt state for a single SRTP SSRC +type srtpSSRCState struct { + ssrc uint32 + rolloverCounter uint32 + rolloverHasProcessed bool + lastSequenceNumber uint16 + replayDetector replaydetector.ReplayDetector +} + +// Encrypt/Decrypt state for a single SRTCP SSRC +type srtcpSSRCState struct { + srtcpIndex uint32 + ssrc uint32 + replayDetector replaydetector.ReplayDetector +} + +// Context represents a SRTP cryptographic context. +// Context can only be used for one-way operations. +// it must either used ONLY for encryption or ONLY for decryption. +type Context struct { + cipher srtpCipher + + srtpSSRCStates map[uint32]*srtpSSRCState + srtcpSSRCStates map[uint32]*srtcpSSRCState + + newSRTCPReplayDetector func() replaydetector.ReplayDetector + newSRTPReplayDetector func() replaydetector.ReplayDetector +} + +// CreateContext creates a new SRTP Context. +// +// CreateContext receives variable number of ContextOption-s. +// Passing multiple options which set the same parameter let the last one valid. +// Following example create SRTP Context with replay protection with window size of 256. +// +// decCtx, err := srtp.CreateContext(key, salt, profile, srtp.SRTPReplayProtection(256)) +// +func CreateContext(masterKey, masterSalt []byte, profile ProtectionProfile, opts ...ContextOption) (c *Context, err error) { + keyLen, err := profile.keyLen() + if err != nil { + return nil, err + } + + saltLen, err := profile.saltLen() + if err != nil { + return nil, err + } + + if masterKeyLen := len(masterKey); masterKeyLen != keyLen { + return c, fmt.Errorf("%w expected(%d) actual(%d)", errShortSrtpMasterKey, masterKey, keyLen) + } else if masterSaltLen := len(masterSalt); masterSaltLen != saltLen { + return c, fmt.Errorf("%w expected(%d) actual(%d)", errShortSrtpMasterSalt, saltLen, masterSaltLen) + } + + c = &Context{ + srtpSSRCStates: map[uint32]*srtpSSRCState{}, + srtcpSSRCStates: map[uint32]*srtcpSSRCState{}, + } + + switch profile { + case ProtectionProfileAeadAes128Gcm: + c.cipher, err = newSrtpCipherAeadAesGcm(masterKey, masterSalt) + case ProtectionProfileAes128CmHmacSha1_80: + c.cipher, err = newSrtpCipherAesCmHmacSha1(masterKey, masterSalt) + default: + return nil, fmt.Errorf("%w: %#v", errNoSuchSRTPProfile, profile) + } + if err != nil { + return nil, err + } + + for _, o := range append( + []ContextOption{ // Default options + SRTPNoReplayProtection(), + SRTCPNoReplayProtection(), + }, + opts..., // User specified options + ) { + if errOpt := o(c); errOpt != nil { + return nil, errOpt + } + } + + return c, nil +} + +// https://tools.ietf.org/html/rfc3550#appendix-A.1 +func (s *srtpSSRCState) nextRolloverCount(sequenceNumber uint16) (uint32, func()) { + roc := s.rolloverCounter + + switch { + case !s.rolloverHasProcessed: + case sequenceNumber == 0: // We exactly hit the rollover count + // Only update rolloverCounter if lastSequenceNumber is greater then maxROCDisorder + // otherwise we already incremented for disorder + if s.lastSequenceNumber > maxROCDisorder { + roc++ + } + case s.lastSequenceNumber < maxROCDisorder && + sequenceNumber > (maxSequenceNumber-maxROCDisorder): + // Our last sequence number incremented because we crossed 0, but then our current number was within maxROCDisorder of the max + // So we fell behind, drop to account for jitter + roc-- + case sequenceNumber < maxROCDisorder && + s.lastSequenceNumber > (maxSequenceNumber-maxROCDisorder): + // our current is within a maxROCDisorder of 0 + // and our last sequence number was a high sequence number, increment to account for jitter + roc++ + } + return roc, func() { + s.rolloverHasProcessed = true + s.lastSequenceNumber = sequenceNumber + s.rolloverCounter = roc + } +} + +func (c *Context) getSRTPSSRCState(ssrc uint32) *srtpSSRCState { + s, ok := c.srtpSSRCStates[ssrc] + if ok { + return s + } + + s = &srtpSSRCState{ + ssrc: ssrc, + replayDetector: c.newSRTPReplayDetector(), + } + c.srtpSSRCStates[ssrc] = s + return s +} + +func (c *Context) getSRTCPSSRCState(ssrc uint32) *srtcpSSRCState { + s, ok := c.srtcpSSRCStates[ssrc] + if ok { + return s + } + + s = &srtcpSSRCState{ + ssrc: ssrc, + replayDetector: c.newSRTCPReplayDetector(), + } + c.srtcpSSRCStates[ssrc] = s + return s +} + +// ROC returns SRTP rollover counter value of specified SSRC. +func (c *Context) ROC(ssrc uint32) (uint32, bool) { + s, ok := c.srtpSSRCStates[ssrc] + if !ok { + return 0, false + } + return s.rolloverCounter, true +} + +// SetROC sets SRTP rollover counter value of specified SSRC. +func (c *Context) SetROC(ssrc uint32, roc uint32) { + s := c.getSRTPSSRCState(ssrc) + s.rolloverCounter = roc +} + +// Index returns SRTCP index value of specified SSRC. +func (c *Context) Index(ssrc uint32) (uint32, bool) { + s, ok := c.srtcpSSRCStates[ssrc] + if !ok { + return 0, false + } + return s.srtcpIndex, true +} + +// SetIndex sets SRTCP index value of specified SSRC. +func (c *Context) SetIndex(ssrc uint32, index uint32) { + s := c.getSRTCPSSRCState(ssrc) + s.srtcpIndex = index % (maxSRTCPIndex + 1) +} diff --git a/vendor/github.com/pion/srtp/v2/errors.go b/vendor/github.com/pion/srtp/v2/errors.go new file mode 100644 index 0000000..a702621 --- /dev/null +++ b/vendor/github.com/pion/srtp/v2/errors.go @@ -0,0 +1,40 @@ +package srtp + +import ( + "errors" + "fmt" +) + +var ( + errDuplicated = errors.New("duplicated packet") + errShortSrtpMasterKey = errors.New("SRTP master key is not long enough") + errShortSrtpMasterSalt = errors.New("SRTP master salt is not long enough") + errNoSuchSRTPProfile = errors.New("no such SRTP Profile") + errNonZeroKDRNotSupported = errors.New("indexOverKdr > 0 is not supported yet") + errExporterWrongLabel = errors.New("exporter called with wrong label") + errNoConfig = errors.New("no config provided") + errNoConn = errors.New("no conn provided") + errFailedToVerifyAuthTag = errors.New("failed to verify auth tag") + errTooShortRTCP = errors.New("packet is too short to be rtcp packet") + errPayloadDiffers = errors.New("payload differs") + errStartedChannelUsedIncorrectly = errors.New("started channel used incorrectly, should only be closed") + + errStreamNotInited = errors.New("stream has not been inited, unable to close") + errStreamAlreadyClosed = errors.New("stream is already closed") + errStreamAlreadyInited = errors.New("stream is already inited") + errFailedTypeAssertion = errors.New("failed to cast child") +) + +type errorDuplicated struct { + Proto string // srtp or srtcp + SSRC uint32 + Index uint32 // sequence number or index +} + +func (e *errorDuplicated) Error() string { + return fmt.Sprintf("%s ssrc=%d index=%d: %v", e.Proto, e.SSRC, e.Index, errDuplicated) +} + +func (e *errorDuplicated) Unwrap() error { + return errDuplicated +} diff --git a/vendor/github.com/pion/srtp/v2/go.mod b/vendor/github.com/pion/srtp/v2/go.mod new file mode 100644 index 0000000..bc04f63 --- /dev/null +++ b/vendor/github.com/pion/srtp/v2/go.mod @@ -0,0 +1,11 @@ +module github.com/pion/srtp/v2 + +go 1.14 + +require ( + github.com/pion/logging v0.2.2 + github.com/pion/rtcp v1.2.6 + github.com/pion/rtp v1.6.2 + github.com/pion/transport v0.12.2 + github.com/stretchr/testify v1.7.0 +) diff --git a/vendor/github.com/pion/srtp/v2/go.sum b/vendor/github.com/pion/srtp/v2/go.sum new file mode 100644 index 0000000..6066df9 --- /dev/null +++ b/vendor/github.com/pion/srtp/v2/go.sum @@ -0,0 +1,34 @@ +github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/pion/logging v0.2.2 h1:M9+AIj/+pxNsDfAT64+MAVgJO0rsyLnoJKCqf//DoeY= +github.com/pion/logging v0.2.2/go.mod h1:k0/tDVsRCX2Mb2ZEmTqNa7CWsQPc+YYCB7Q+5pahoms= +github.com/pion/randutil v0.1.0 h1:CFG1UdESneORglEsnimhUjf33Rwjubwj6xfiOXBa3mA= +github.com/pion/randutil v0.1.0/go.mod h1:XcJrSMMbbMRhASFVOlj/5hQial/Y8oH/HVo7TBZq+j8= +github.com/pion/rtcp v1.2.6 h1:1zvwBbyd0TeEuuWftrd/4d++m+/kZSeiguxU61LFWpo= +github.com/pion/rtcp v1.2.6/go.mod h1:52rMNPWFsjr39z9B9MhnkqhPLoeHTv1aN63o/42bWE0= +github.com/pion/rtp v1.6.2 h1:iGBerLX6JiDjB9NXuaPzHyxHFG9JsIEdgwTC0lp5n/U= +github.com/pion/rtp v1.6.2/go.mod h1:bDb5n+BFZxXx0Ea7E5qe+klMuqiBrP+w8XSjiWtCUko= +github.com/pion/transport v0.12.2 h1:WYEjhloRHt1R86LhUKjC5y+P52Y11/QqEUalvtzVoys= +github.com/pion/transport v0.12.2/go.mod h1:N3+vZQD9HlDP5GWkZ85LohxNsDcNgofQmyL6ojX5d8Q= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20201201195509-5d6afe98e0b7 h1:3uJsdck53FDIpWwLeAXlia9p4C8j0BO2xZrqzKpL0D8= +golang.org/x/net v0.0.0-20201201195509-5d6afe98e0b7/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/vendor/github.com/pion/srtp/v2/key_derivation.go b/vendor/github.com/pion/srtp/v2/key_derivation.go new file mode 100644 index 0000000..5bbf3aa --- /dev/null +++ b/vendor/github.com/pion/srtp/v2/key_derivation.go @@ -0,0 +1,63 @@ +package srtp + +import ( + "crypto/aes" + "encoding/binary" +) + +func aesCmKeyDerivation(label byte, masterKey, masterSalt []byte, indexOverKdr int, outLen int) ([]byte, error) { + if indexOverKdr != 0 { + // 24-bit "index DIV kdr" must be xored to prf input. + return nil, errNonZeroKDRNotSupported + } + + // https://tools.ietf.org/html/rfc3711#appendix-B.3 + // The input block for AES-CM is generated by exclusive-oring the master salt with the + // concatenation of the encryption key label 0x00 with (index DIV kdr), + // - index is 'rollover count' and DIV is 'divided by' + + nMasterKey := len(masterKey) + nMasterSalt := len(masterSalt) + + prfIn := make([]byte, nMasterKey) + copy(prfIn[:nMasterSalt], masterSalt) + + prfIn[7] ^= label + + // The resulting value is then AES encrypted using the master key to get the cipher key. + block, err := aes.NewCipher(masterKey) + if err != nil { + return nil, err + } + + out := make([]byte, ((outLen+nMasterKey)/nMasterKey)*nMasterKey) + var i uint16 + for n := 0; n < outLen; n += nMasterKey { + binary.BigEndian.PutUint16(prfIn[nMasterKey-2:], i) + block.Encrypt(out[n:n+nMasterKey], prfIn) + i++ + } + return out[:outLen], nil +} + +// Generate IV https://tools.ietf.org/html/rfc3711#section-4.1.1 +// where the 128-bit integer value IV SHALL be defined by the SSRC, the +// SRTP packet index i, and the SRTP session salting key k_s, as below. +// - ROC = a 32-bit unsigned rollover counter (ROC), which records how many +// - times the 16-bit RTP sequence number has been reset to zero after +// - passing through 65,535 +// i = 2^16 * ROC + SEQ +// IV = (salt*2 ^ 16) | (ssrc*2 ^ 64) | (i*2 ^ 16) +func generateCounter(sequenceNumber uint16, rolloverCounter uint32, ssrc uint32, sessionSalt []byte) []byte { + counter := make([]byte, 16) + + binary.BigEndian.PutUint32(counter[4:], ssrc) + binary.BigEndian.PutUint32(counter[8:], rolloverCounter) + binary.BigEndian.PutUint32(counter[12:], uint32(sequenceNumber)<<16) + + for i := range sessionSalt { + counter[i] ^= sessionSalt[i] + } + + return counter +} diff --git a/vendor/github.com/pion/srtp/v2/keying.go b/vendor/github.com/pion/srtp/v2/keying.go new file mode 100644 index 0000000..82fd4d9 --- /dev/null +++ b/vendor/github.com/pion/srtp/v2/keying.go @@ -0,0 +1,54 @@ +package srtp + +const labelExtractorDtlsSrtp = "EXTRACTOR-dtls_srtp" + +// KeyingMaterialExporter allows package SRTP to extract keying material +type KeyingMaterialExporter interface { + ExportKeyingMaterial(label string, context []byte, length int) ([]byte, error) +} + +// ExtractSessionKeysFromDTLS allows setting the Config SessionKeys by +// extracting them from DTLS. This behavior is defined in RFC5764: +// https://tools.ietf.org/html/rfc5764 +func (c *Config) ExtractSessionKeysFromDTLS(exporter KeyingMaterialExporter, isClient bool) error { + keyLen, err := c.Profile.keyLen() + if err != nil { + return err + } + + saltLen, err := c.Profile.saltLen() + if err != nil { + return err + } + + keyingMaterial, err := exporter.ExportKeyingMaterial(labelExtractorDtlsSrtp, nil, (keyLen*2)+(saltLen*2)) + if err != nil { + return err + } + + offset := 0 + clientWriteKey := append([]byte{}, keyingMaterial[offset:offset+keyLen]...) + offset += keyLen + + serverWriteKey := append([]byte{}, keyingMaterial[offset:offset+keyLen]...) + offset += keyLen + + clientWriteKey = append(clientWriteKey, keyingMaterial[offset:offset+saltLen]...) + offset += saltLen + + serverWriteKey = append(serverWriteKey, keyingMaterial[offset:offset+saltLen]...) + + if isClient { + c.Keys.LocalMasterKey = clientWriteKey[0:keyLen] + c.Keys.LocalMasterSalt = clientWriteKey[keyLen:] + c.Keys.RemoteMasterKey = serverWriteKey[0:keyLen] + c.Keys.RemoteMasterSalt = serverWriteKey[keyLen:] + return nil + } + + c.Keys.LocalMasterKey = serverWriteKey[0:keyLen] + c.Keys.LocalMasterSalt = serverWriteKey[keyLen:] + c.Keys.RemoteMasterKey = clientWriteKey[0:keyLen] + c.Keys.RemoteMasterSalt = clientWriteKey[keyLen:] + return nil +} diff --git a/vendor/github.com/pion/srtp/v2/option.go b/vendor/github.com/pion/srtp/v2/option.go new file mode 100644 index 0000000..d6159f1 --- /dev/null +++ b/vendor/github.com/pion/srtp/v2/option.go @@ -0,0 +1,54 @@ +package srtp + +import ( + "github.com/pion/transport/replaydetector" +) + +// ContextOption represents option of Context using the functional options pattern. +type ContextOption func(*Context) error + +// SRTPReplayProtection sets SRTP replay protection window size. +func SRTPReplayProtection(windowSize uint) ContextOption { // nolint:golint + return func(c *Context) error { + c.newSRTPReplayDetector = func() replaydetector.ReplayDetector { + return replaydetector.WithWrap(windowSize, maxSequenceNumber) + } + return nil + } +} + +// SRTCPReplayProtection sets SRTCP replay protection window size. +func SRTCPReplayProtection(windowSize uint) ContextOption { + return func(c *Context) error { + c.newSRTCPReplayDetector = func() replaydetector.ReplayDetector { + return replaydetector.WithWrap(windowSize, maxSRTCPIndex) + } + return nil + } +} + +// SRTPNoReplayProtection disables SRTP replay protection. +func SRTPNoReplayProtection() ContextOption { // nolint:golint + return func(c *Context) error { + c.newSRTPReplayDetector = func() replaydetector.ReplayDetector { + return &nopReplayDetector{} + } + return nil + } +} + +// SRTCPNoReplayProtection disables SRTCP replay protection. +func SRTCPNoReplayProtection() ContextOption { + return func(c *Context) error { + c.newSRTCPReplayDetector = func() replaydetector.ReplayDetector { + return &nopReplayDetector{} + } + return nil + } +} + +type nopReplayDetector struct{} + +func (s *nopReplayDetector) Check(uint64) (func(), bool) { + return func() {}, true +} diff --git a/vendor/github.com/pion/srtp/v2/protection_profile.go b/vendor/github.com/pion/srtp/v2/protection_profile.go new file mode 100644 index 0000000..94476ad --- /dev/null +++ b/vendor/github.com/pion/srtp/v2/protection_profile.go @@ -0,0 +1,67 @@ +package srtp + +import "fmt" + +// ProtectionProfile specifies Cipher and AuthTag details, similar to TLS cipher suite +type ProtectionProfile uint16 + +// Supported protection profiles +const ( + ProtectionProfileAes128CmHmacSha1_80 ProtectionProfile = 0x0001 + ProtectionProfileAeadAes128Gcm ProtectionProfile = 0x0007 +) + +func (p ProtectionProfile) keyLen() (int, error) { + switch p { + case ProtectionProfileAes128CmHmacSha1_80: + fallthrough + case ProtectionProfileAeadAes128Gcm: + return 16, nil + default: + return 0, fmt.Errorf("%w: %#v", errNoSuchSRTPProfile, p) + } +} + +func (p ProtectionProfile) saltLen() (int, error) { + switch p { + case ProtectionProfileAes128CmHmacSha1_80: + return 14, nil + case ProtectionProfileAeadAes128Gcm: + return 12, nil + default: + return 0, fmt.Errorf("%w: %#v", errNoSuchSRTPProfile, p) + } +} + +func (p ProtectionProfile) authTagLen() (int, error) { + switch p { + case ProtectionProfileAes128CmHmacSha1_80: + return (&srtpCipherAesCmHmacSha1{}).authTagLen(), nil + case ProtectionProfileAeadAes128Gcm: + return (&srtpCipherAeadAesGcm{}).authTagLen(), nil + default: + return 0, fmt.Errorf("%w: %#v", errNoSuchSRTPProfile, p) + } +} + +func (p ProtectionProfile) aeadAuthTagLen() (int, error) { + switch p { + case ProtectionProfileAes128CmHmacSha1_80: + return (&srtpCipherAesCmHmacSha1{}).aeadAuthTagLen(), nil + case ProtectionProfileAeadAes128Gcm: + return (&srtpCipherAeadAesGcm{}).aeadAuthTagLen(), nil + default: + return 0, fmt.Errorf("%w: %#v", errNoSuchSRTPProfile, p) + } +} + +func (p ProtectionProfile) authKeyLen() (int, error) { + switch p { + case ProtectionProfileAes128CmHmacSha1_80: + return 20, nil + case ProtectionProfileAeadAes128Gcm: + return 0, nil + default: + return 0, fmt.Errorf("%w: %#v", errNoSuchSRTPProfile, p) + } +} diff --git a/vendor/github.com/pion/srtp/v2/renovate.json b/vendor/github.com/pion/srtp/v2/renovate.json new file mode 100644 index 0000000..4400fd9 --- /dev/null +++ b/vendor/github.com/pion/srtp/v2/renovate.json @@ -0,0 +1,15 @@ +{ + "extends": [ + "config:base" + ], + "postUpdateOptions": [ + "gomodTidy" + ], + "commitBody": "Generated by renovateBot", + "packageRules": [ + { + "packagePatterns": ["^golang.org/x/"], + "schedule": ["on the first day of the month"] + } + ] +} diff --git a/vendor/github.com/pion/srtp/v2/session.go b/vendor/github.com/pion/srtp/v2/session.go new file mode 100644 index 0000000..95520f7 --- /dev/null +++ b/vendor/github.com/pion/srtp/v2/session.go @@ -0,0 +1,150 @@ +package srtp + +import ( + "io" + "net" + "sync" + + "github.com/pion/logging" + "github.com/pion/transport/packetio" +) + +type streamSession interface { + Close() error + write([]byte) (int, error) + decrypt([]byte) error +} + +type session struct { + localContextMutex sync.Mutex + localContext, remoteContext *Context + localOptions, remoteOptions []ContextOption + + newStream chan readStream + + started chan interface{} + closed chan interface{} + + readStreamsClosed bool + readStreams map[uint32]readStream + readStreamsLock sync.Mutex + + log logging.LeveledLogger + bufferFactory func(packetType packetio.BufferPacketType, ssrc uint32) io.ReadWriteCloser + + nextConn net.Conn +} + +// Config is used to configure a session. +// You can provide either a KeyingMaterialExporter to export keys +// or directly pass the keys themselves. +// After a Config is passed to a session it must not be modified. +type Config struct { + Keys SessionKeys + Profile ProtectionProfile + BufferFactory func(packetType packetio.BufferPacketType, ssrc uint32) io.ReadWriteCloser + LoggerFactory logging.LoggerFactory + + // List of local/remote context options. + // ReplayProtection is enabled on remote context by default. + // Default replay protection window size is 64. + LocalOptions, RemoteOptions []ContextOption +} + +// SessionKeys bundles the keys required to setup an SRTP session +type SessionKeys struct { + LocalMasterKey []byte + LocalMasterSalt []byte + RemoteMasterKey []byte + RemoteMasterSalt []byte +} + +func (s *session) getOrCreateReadStream(ssrc uint32, child streamSession, proto func() readStream) (readStream, bool) { + s.readStreamsLock.Lock() + defer s.readStreamsLock.Unlock() + + if s.readStreamsClosed { + return nil, false + } + + r, ok := s.readStreams[ssrc] + if ok { + return r, false + } + + // Create the readStream. + r = proto() + + if err := r.init(child, ssrc); err != nil { + return nil, false + } + + s.readStreams[ssrc] = r + return r, true +} + +func (s *session) removeReadStream(ssrc uint32) { + s.readStreamsLock.Lock() + defer s.readStreamsLock.Unlock() + + if s.readStreamsClosed { + return + } + + delete(s.readStreams, ssrc) +} + +func (s *session) close() error { + if s.nextConn == nil { + return nil + } else if err := s.nextConn.Close(); err != nil { + return err + } + + <-s.closed + return nil +} + +func (s *session) start(localMasterKey, localMasterSalt, remoteMasterKey, remoteMasterSalt []byte, profile ProtectionProfile, child streamSession) error { + var err error + s.localContext, err = CreateContext(localMasterKey, localMasterSalt, profile, s.localOptions...) + if err != nil { + return err + } + + s.remoteContext, err = CreateContext(remoteMasterKey, remoteMasterSalt, profile, s.remoteOptions...) + if err != nil { + return err + } + + go func() { + defer func() { + close(s.newStream) + + s.readStreamsLock.Lock() + s.readStreamsClosed = true + s.readStreamsLock.Unlock() + close(s.closed) + }() + + b := make([]byte, 8192) + for { + var i int + i, err = s.nextConn.Read(b) + if err != nil { + if err != io.EOF { + s.log.Error(err.Error()) + } + return + } + + if err = child.decrypt(b[:i]); err != nil { + s.log.Info(err.Error()) + } + } + }() + + close(s.started) + + return nil +} diff --git a/vendor/github.com/pion/srtp/v2/session_srtcp.go b/vendor/github.com/pion/srtp/v2/session_srtcp.go new file mode 100644 index 0000000..a5fb656 --- /dev/null +++ b/vendor/github.com/pion/srtp/v2/session_srtcp.go @@ -0,0 +1,180 @@ +package srtp + +import ( + "net" + "time" + + "github.com/pion/logging" + "github.com/pion/rtcp" +) + +const defaultSessionSRTCPReplayProtectionWindow = 64 + +// SessionSRTCP implements io.ReadWriteCloser and provides a bi-directional SRTCP session +// SRTCP itself does not have a design like this, but it is common in most applications +// for local/remote to each have their own keying material. This provides those patterns +// instead of making everyone re-implement +type SessionSRTCP struct { + session + writeStream *WriteStreamSRTCP +} + +// NewSessionSRTCP creates a SRTCP session using conn as the underlying transport. +func NewSessionSRTCP(conn net.Conn, config *Config) (*SessionSRTCP, error) { //nolint:dupl + if config == nil { + return nil, errNoConfig + } else if conn == nil { + return nil, errNoConn + } + + loggerFactory := config.LoggerFactory + if loggerFactory == nil { + loggerFactory = logging.NewDefaultLoggerFactory() + } + + localOpts := append( + []ContextOption{}, + config.LocalOptions..., + ) + remoteOpts := append( + []ContextOption{ + // Default options + SRTCPReplayProtection(defaultSessionSRTCPReplayProtectionWindow), + }, + config.RemoteOptions..., + ) + + s := &SessionSRTCP{ + session: session{ + nextConn: conn, + localOptions: localOpts, + remoteOptions: remoteOpts, + readStreams: map[uint32]readStream{}, + newStream: make(chan readStream), + started: make(chan interface{}), + closed: make(chan interface{}), + bufferFactory: config.BufferFactory, + log: loggerFactory.NewLogger("srtp"), + }, + } + s.writeStream = &WriteStreamSRTCP{s} + + err := s.session.start( + config.Keys.LocalMasterKey, config.Keys.LocalMasterSalt, + config.Keys.RemoteMasterKey, config.Keys.RemoteMasterSalt, + config.Profile, + s, + ) + if err != nil { + return nil, err + } + return s, nil +} + +// OpenWriteStream returns the global write stream for the Session +func (s *SessionSRTCP) OpenWriteStream() (*WriteStreamSRTCP, error) { + return s.writeStream, nil +} + +// OpenReadStream opens a read stream for the given SSRC, it can be used +// if you want a certain SSRC, but don't want to wait for AcceptStream +func (s *SessionSRTCP) OpenReadStream(ssrc uint32) (*ReadStreamSRTCP, error) { + r, _ := s.session.getOrCreateReadStream(ssrc, s, newReadStreamSRTCP) + + if readStream, ok := r.(*ReadStreamSRTCP); ok { + return readStream, nil + } + return nil, errFailedTypeAssertion +} + +// AcceptStream returns a stream to handle RTCP for a single SSRC +func (s *SessionSRTCP) AcceptStream() (*ReadStreamSRTCP, uint32, error) { + stream, ok := <-s.newStream + if !ok { + return nil, 0, errStreamAlreadyClosed + } + + readStream, ok := stream.(*ReadStreamSRTCP) + if !ok { + return nil, 0, errFailedTypeAssertion + } + + return readStream, stream.GetSSRC(), nil +} + +// Close ends the session +func (s *SessionSRTCP) Close() error { + return s.session.close() +} + +// Private + +func (s *SessionSRTCP) write(buf []byte) (int, error) { + if _, ok := <-s.session.started; ok { + return 0, errStartedChannelUsedIncorrectly + } + + s.session.localContextMutex.Lock() + encrypted, err := s.localContext.EncryptRTCP(nil, buf, nil) + s.session.localContextMutex.Unlock() + + if err != nil { + return 0, err + } + return s.session.nextConn.Write(encrypted) +} + +func (s *SessionSRTCP) setWriteDeadline(t time.Time) error { + return s.session.nextConn.SetWriteDeadline(t) +} + +// create a list of Destination SSRCs +// that's a superset of all Destinations in the slice. +func destinationSSRC(pkts []rtcp.Packet) []uint32 { + ssrcSet := make(map[uint32]struct{}) + for _, p := range pkts { + for _, ssrc := range p.DestinationSSRC() { + ssrcSet[ssrc] = struct{}{} + } + } + + out := make([]uint32, 0, len(ssrcSet)) + for ssrc := range ssrcSet { + out = append(out, ssrc) + } + + return out +} + +func (s *SessionSRTCP) decrypt(buf []byte) error { + decrypted, err := s.remoteContext.DecryptRTCP(buf, buf, nil) + if err != nil { + return err + } + + pkt, err := rtcp.Unmarshal(decrypted) + if err != nil { + return err + } + + for _, ssrc := range destinationSSRC(pkt) { + r, isNew := s.session.getOrCreateReadStream(ssrc, s, newReadStreamSRTCP) + if r == nil { + return nil // Session has been closed + } else if isNew { + s.session.newStream <- r // Notify AcceptStream + } + + readStream, ok := r.(*ReadStreamSRTCP) + if !ok { + return errFailedTypeAssertion + } + + _, err = readStream.write(decrypted) + if err != nil { + return err + } + } + + return nil +} diff --git a/vendor/github.com/pion/srtp/v2/session_srtp.go b/vendor/github.com/pion/srtp/v2/session_srtp.go new file mode 100644 index 0000000..dc815af --- /dev/null +++ b/vendor/github.com/pion/srtp/v2/session_srtp.go @@ -0,0 +1,171 @@ +package srtp + +import ( + "net" + "time" + + "github.com/pion/logging" + "github.com/pion/rtp" +) + +const defaultSessionSRTPReplayProtectionWindow = 64 + +// SessionSRTP implements io.ReadWriteCloser and provides a bi-directional SRTP session +// SRTP itself does not have a design like this, but it is common in most applications +// for local/remote to each have their own keying material. This provides those patterns +// instead of making everyone re-implement +type SessionSRTP struct { + session + writeStream *WriteStreamSRTP +} + +// NewSessionSRTP creates a SRTP session using conn as the underlying transport. +func NewSessionSRTP(conn net.Conn, config *Config) (*SessionSRTP, error) { //nolint:dupl + if config == nil { + return nil, errNoConfig + } else if conn == nil { + return nil, errNoConn + } + + loggerFactory := config.LoggerFactory + if loggerFactory == nil { + loggerFactory = logging.NewDefaultLoggerFactory() + } + + localOpts := append( + []ContextOption{}, + config.LocalOptions..., + ) + remoteOpts := append( + []ContextOption{ + // Default options + SRTPReplayProtection(defaultSessionSRTPReplayProtectionWindow), + }, + config.RemoteOptions..., + ) + + s := &SessionSRTP{ + session: session{ + nextConn: conn, + localOptions: localOpts, + remoteOptions: remoteOpts, + readStreams: map[uint32]readStream{}, + newStream: make(chan readStream), + started: make(chan interface{}), + closed: make(chan interface{}), + bufferFactory: config.BufferFactory, + log: loggerFactory.NewLogger("srtp"), + }, + } + s.writeStream = &WriteStreamSRTP{s} + + err := s.session.start( + config.Keys.LocalMasterKey, config.Keys.LocalMasterSalt, + config.Keys.RemoteMasterKey, config.Keys.RemoteMasterSalt, + config.Profile, + s, + ) + if err != nil { + return nil, err + } + return s, nil +} + +// OpenWriteStream returns the global write stream for the Session +func (s *SessionSRTP) OpenWriteStream() (*WriteStreamSRTP, error) { + return s.writeStream, nil +} + +// OpenReadStream opens a read stream for the given SSRC, it can be used +// if you want a certain SSRC, but don't want to wait for AcceptStream +func (s *SessionSRTP) OpenReadStream(ssrc uint32) (*ReadStreamSRTP, error) { + r, _ := s.session.getOrCreateReadStream(ssrc, s, newReadStreamSRTP) + + if readStream, ok := r.(*ReadStreamSRTP); ok { + return readStream, nil + } + + return nil, errFailedTypeAssertion +} + +// AcceptStream returns a stream to handle RTCP for a single SSRC +func (s *SessionSRTP) AcceptStream() (*ReadStreamSRTP, uint32, error) { + stream, ok := <-s.newStream + if !ok { + return nil, 0, errStreamAlreadyClosed + } + + readStream, ok := stream.(*ReadStreamSRTP) + if !ok { + return nil, 0, errFailedTypeAssertion + } + + return readStream, stream.GetSSRC(), nil +} + +// Close ends the session +func (s *SessionSRTP) Close() error { + return s.session.close() +} + +func (s *SessionSRTP) write(b []byte) (int, error) { + packet := &rtp.Packet{} + + err := packet.Unmarshal(b) + if err != nil { + return 0, nil + } + + return s.writeRTP(&packet.Header, packet.Payload) +} + +func (s *SessionSRTP) writeRTP(header *rtp.Header, payload []byte) (int, error) { + if _, ok := <-s.session.started; ok { + return 0, errStartedChannelUsedIncorrectly + } + + s.session.localContextMutex.Lock() + encrypted, err := s.localContext.encryptRTP(nil, header, payload) + s.session.localContextMutex.Unlock() + + if err != nil { + return 0, err + } + + return s.session.nextConn.Write(encrypted) +} + +func (s *SessionSRTP) setWriteDeadline(t time.Time) error { + return s.session.nextConn.SetWriteDeadline(t) +} + +func (s *SessionSRTP) decrypt(buf []byte) error { + h := &rtp.Header{} + if err := h.Unmarshal(buf); err != nil { + return err + } + + r, isNew := s.session.getOrCreateReadStream(h.SSRC, s, newReadStreamSRTP) + if r == nil { + return nil // Session has been closed + } else if isNew { + s.session.newStream <- r // Notify AcceptStream + } + + readStream, ok := r.(*ReadStreamSRTP) + if !ok { + return errFailedTypeAssertion + } + + decrypted, err := s.remoteContext.decryptRTP(buf, buf, h) + if err != nil { + return err + } + + _, err = readStream.write(decrypted) + if err != nil { + return err + } + + return nil +} diff --git a/vendor/github.com/pion/srtp/v2/srtcp.go b/vendor/github.com/pion/srtp/v2/srtcp.go new file mode 100644 index 0000000..dbf5125 --- /dev/null +++ b/vendor/github.com/pion/srtp/v2/srtcp.go @@ -0,0 +1,77 @@ +package srtp + +import ( + "encoding/binary" + "fmt" + + "github.com/pion/rtcp" +) + +const maxSRTCPIndex = 0x7FFFFFFF + +func (c *Context) decryptRTCP(dst, encrypted []byte) ([]byte, error) { + out := allocateIfMismatch(dst, encrypted) + tailOffset := len(encrypted) - (c.cipher.authTagLen() + srtcpIndexSize) + + if tailOffset < 0 { + return nil, fmt.Errorf("%w: %d", errTooShortRTCP, len(encrypted)) + } else if isEncrypted := encrypted[tailOffset] >> 7; isEncrypted == 0 { + return out, nil + } + + index := c.cipher.getRTCPIndex(encrypted) + ssrc := binary.BigEndian.Uint32(encrypted[4:]) + + s := c.getSRTCPSSRCState(ssrc) + markAsValid, ok := s.replayDetector.Check(uint64(index)) + if !ok { + return nil, &errorDuplicated{Proto: "srtcp", SSRC: ssrc, Index: index} + } + + out, err := c.cipher.decryptRTCP(out, encrypted, index, ssrc) + if err != nil { + return nil, err + } + + markAsValid() + return out, nil +} + +// DecryptRTCP decrypts a buffer that contains a RTCP packet +func (c *Context) DecryptRTCP(dst, encrypted []byte, header *rtcp.Header) ([]byte, error) { + if header == nil { + header = &rtcp.Header{} + } + + if err := header.Unmarshal(encrypted); err != nil { + return nil, err + } + + return c.decryptRTCP(dst, encrypted) +} + +func (c *Context) encryptRTCP(dst, decrypted []byte) ([]byte, error) { + ssrc := binary.BigEndian.Uint32(decrypted[4:]) + s := c.getSRTCPSSRCState(ssrc) + + // We roll over early because MSB is used for marking as encrypted + s.srtcpIndex++ + if s.srtcpIndex > maxSRTCPIndex { + s.srtcpIndex = 0 + } + + return c.cipher.encryptRTCP(dst, decrypted, s.srtcpIndex, ssrc) +} + +// EncryptRTCP Encrypts a RTCP packet +func (c *Context) EncryptRTCP(dst, decrypted []byte, header *rtcp.Header) ([]byte, error) { + if header == nil { + header = &rtcp.Header{} + } + + if err := header.Unmarshal(decrypted); err != nil { + return nil, err + } + + return c.encryptRTCP(dst, decrypted) +} diff --git a/vendor/github.com/pion/srtp/v2/srtp.go b/vendor/github.com/pion/srtp/v2/srtp.go new file mode 100644 index 0000000..c4ed3ac --- /dev/null +++ b/vendor/github.com/pion/srtp/v2/srtp.go @@ -0,0 +1,68 @@ +// Package srtp implements Secure Real-time Transport Protocol +package srtp + +import ( + "github.com/pion/rtp" +) + +func (c *Context) decryptRTP(dst, ciphertext []byte, header *rtp.Header) ([]byte, error) { + s := c.getSRTPSSRCState(header.SSRC) + + markAsValid, ok := s.replayDetector.Check(uint64(header.SequenceNumber)) + if !ok { + return nil, &errorDuplicated{ + Proto: "srtp", SSRC: header.SSRC, Index: uint32(header.SequenceNumber), + } + } + + dst = growBufferSize(dst, len(ciphertext)-c.cipher.authTagLen()) + roc, updateROC := s.nextRolloverCount(header.SequenceNumber) + + dst, err := c.cipher.decryptRTP(dst, ciphertext, header, roc) + if err != nil { + return nil, err + } + + markAsValid() + updateROC() + return dst, nil +} + +// DecryptRTP decrypts a RTP packet with an encrypted payload +func (c *Context) DecryptRTP(dst, encrypted []byte, header *rtp.Header) ([]byte, error) { + if header == nil { + header = &rtp.Header{} + } + + if err := header.Unmarshal(encrypted); err != nil { + return nil, err + } + + return c.decryptRTP(dst, encrypted, header) +} + +// EncryptRTP marshals and encrypts an RTP packet, writing to the dst buffer provided. +// If the dst buffer does not have the capacity to hold `len(plaintext) + 10` bytes, a new one will be allocated and returned. +// If a rtp.Header is provided, it will be Unmarshaled using the plaintext. +func (c *Context) EncryptRTP(dst []byte, plaintext []byte, header *rtp.Header) ([]byte, error) { + if header == nil { + header = &rtp.Header{} + } + + if err := header.Unmarshal(plaintext); err != nil { + return nil, err + } + + return c.encryptRTP(dst, header, plaintext[header.PayloadOffset:]) +} + +// encryptRTP marshals and encrypts an RTP packet, writing to the dst buffer provided. +// If the dst buffer does not have the capacity, a new one will be allocated and returned. +// Similar to above but faster because it can avoid unmarshaling the header and marshaling the payload. +func (c *Context) encryptRTP(dst []byte, header *rtp.Header, payload []byte) (ciphertext []byte, err error) { + s := c.getSRTPSSRCState(header.SSRC) + roc, updateROC := s.nextRolloverCount(header.SequenceNumber) + updateROC() + + return c.cipher.encryptRTP(dst, header, payload, roc) +} diff --git a/vendor/github.com/pion/srtp/v2/srtp_cipher.go b/vendor/github.com/pion/srtp/v2/srtp_cipher.go new file mode 100644 index 0000000..4c5cd88 --- /dev/null +++ b/vendor/github.com/pion/srtp/v2/srtp_cipher.go @@ -0,0 +1,46 @@ +package srtp + +import "github.com/pion/rtp" + +// cipher represents a implementation of one +// of the SRTP Specific ciphers +type srtpCipher interface { + // authTagLen returns auth key length of the cipher. + // See the note below. + authTagLen() int + // aeadAuthTagLen returns AEAD auth key length of the cipher. + // See the note below. + aeadAuthTagLen() int + getRTCPIndex([]byte) uint32 + + encryptRTP([]byte, *rtp.Header, []byte, uint32) ([]byte, error) + encryptRTCP([]byte, []byte, uint32, uint32) ([]byte, error) + + decryptRTP([]byte, []byte, *rtp.Header, uint32) ([]byte, error) + decryptRTCP([]byte, []byte, uint32, uint32) ([]byte, error) +} + +/* +NOTE: Auth tag and AEAD auth tag are placed at the different position in SRTCP + +In non-AEAD cipher, the authentication tag is placed *after* the ESRTCP word +(Encrypted-flag and SRTCP index). + +> AES_128_CM_HMAC_SHA1_80 +> | RTCP Header | Encrypted payload |E| SRTCP Index | Auth tag | +> ^ |----------| +> | ^ +> | authTagLen=10 +> aeadAuthTagLen=0 + +In AEAD cipher, the AEAD authentication tag is embedded in the ciphertext. +It is *before* the ESRTCP word (Encrypted-flag and SRTCP index). + +> AEAD_AES_128_GCM +> | RTCP Header | Encrypted payload | AEAD auth tag |E| SRTCP Index | +> |---------------| ^ +> ^ authTagLen=0 +> aeadAuthTagLen=16 + +See https://tools.ietf.org/html/rfc7714 for the full specifications. +*/ diff --git a/vendor/github.com/pion/srtp/v2/srtp_cipher_aead_aes_gcm.go b/vendor/github.com/pion/srtp/v2/srtp_cipher_aead_aes_gcm.go new file mode 100644 index 0000000..2720679 --- /dev/null +++ b/vendor/github.com/pion/srtp/v2/srtp_cipher_aead_aes_gcm.go @@ -0,0 +1,198 @@ +package srtp + +import ( + "crypto/aes" + "crypto/cipher" + "encoding/binary" + + "github.com/pion/rtp" +) + +const ( + rtcpEncryptionFlag = 0x80 +) + +type srtpCipherAeadAesGcm struct { + srtpCipher, srtcpCipher cipher.AEAD + + srtpSessionSalt, srtcpSessionSalt []byte +} + +func newSrtpCipherAeadAesGcm(masterKey, masterSalt []byte) (*srtpCipherAeadAesGcm, error) { + s := &srtpCipherAeadAesGcm{} + + srtpSessionKey, err := aesCmKeyDerivation(labelSRTPEncryption, masterKey, masterSalt, 0, len(masterKey)) + if err != nil { + return nil, err + } + + srtpBlock, err := aes.NewCipher(srtpSessionKey) + if err != nil { + return nil, err + } + + s.srtpCipher, err = cipher.NewGCM(srtpBlock) + if err != nil { + return nil, err + } + + srtcpSessionKey, err := aesCmKeyDerivation(labelSRTCPEncryption, masterKey, masterSalt, 0, len(masterKey)) + if err != nil { + return nil, err + } + + srtcpBlock, err := aes.NewCipher(srtcpSessionKey) + if err != nil { + return nil, err + } + + s.srtcpCipher, err = cipher.NewGCM(srtcpBlock) + if err != nil { + return nil, err + } + + if s.srtpSessionSalt, err = aesCmKeyDerivation(labelSRTPSalt, masterKey, masterSalt, 0, len(masterSalt)); err != nil { + return nil, err + } else if s.srtcpSessionSalt, err = aesCmKeyDerivation(labelSRTCPSalt, masterKey, masterSalt, 0, len(masterSalt)); err != nil { + return nil, err + } + + return s, nil +} + +func (s *srtpCipherAeadAesGcm) authTagLen() int { + return 0 +} + +func (s *srtpCipherAeadAesGcm) aeadAuthTagLen() int { + return 16 +} + +func (s *srtpCipherAeadAesGcm) encryptRTP(dst []byte, header *rtp.Header, payload []byte, roc uint32) (ciphertext []byte, err error) { + // Grow the given buffer to fit the output. + dst = growBufferSize(dst, header.MarshalSize()+len(payload)+s.aeadAuthTagLen()) + + hdr, err := header.Marshal() + if err != nil { + return nil, err + } + + iv := s.rtpInitializationVector(header, roc) + nHdr := len(hdr) + s.srtpCipher.Seal(dst[nHdr:nHdr], iv, payload, hdr) + copy(dst[:nHdr], hdr) + return dst, nil +} + +func (s *srtpCipherAeadAesGcm) decryptRTP(dst, ciphertext []byte, header *rtp.Header, roc uint32) ([]byte, error) { + // Grow the given buffer to fit the output. + nDst := len(ciphertext) - s.aeadAuthTagLen() + if nDst < 0 { + // Size of ciphertext is shorter than AEAD auth tag len. + return nil, errFailedToVerifyAuthTag + } + dst = growBufferSize(dst, nDst) + + iv := s.rtpInitializationVector(header, roc) + + if _, err := s.srtpCipher.Open( + dst[header.PayloadOffset:header.PayloadOffset], iv, ciphertext[header.PayloadOffset:], ciphertext[:header.PayloadOffset], + ); err != nil { + return nil, err + } + + copy(dst[:header.PayloadOffset], ciphertext[:header.PayloadOffset]) + return dst, nil +} + +func (s *srtpCipherAeadAesGcm) encryptRTCP(dst, decrypted []byte, srtcpIndex uint32, ssrc uint32) ([]byte, error) { + aadPos := len(decrypted) + s.aeadAuthTagLen() + // Grow the given buffer to fit the output. + dst = growBufferSize(dst, aadPos+srtcpIndexSize) + + iv := s.rtcpInitializationVector(srtcpIndex, ssrc) + aad := s.rtcpAdditionalAuthenticatedData(decrypted, srtcpIndex) + + s.srtcpCipher.Seal(dst[8:8], iv, decrypted[8:], aad) + + copy(dst[:8], decrypted[:8]) + copy(dst[aadPos:aadPos+4], aad[8:12]) + return dst, nil +} + +func (s *srtpCipherAeadAesGcm) decryptRTCP(dst, encrypted []byte, srtcpIndex, ssrc uint32) ([]byte, error) { + aadPos := len(encrypted) - srtcpIndexSize + // Grow the given buffer to fit the output. + nDst := aadPos - s.aeadAuthTagLen() + if nDst < 0 { + // Size of ciphertext is shorter than AEAD auth tag len. + return nil, errFailedToVerifyAuthTag + } + dst = growBufferSize(dst, nDst) + + iv := s.rtcpInitializationVector(srtcpIndex, ssrc) + aad := s.rtcpAdditionalAuthenticatedData(encrypted, srtcpIndex) + + if _, err := s.srtcpCipher.Open(dst[8:8], iv, encrypted[8:aadPos], aad); err != nil { + return nil, err + } + + copy(dst[:8], encrypted[:8]) + return dst, nil +} + +// The 12-octet IV used by AES-GCM SRTP is formed by first concatenating +// 2 octets of zeroes, the 4-octet SSRC, the 4-octet rollover counter +// (ROC), and the 2-octet sequence number (SEQ). The resulting 12-octet +// value is then XORed to the 12-octet salt to form the 12-octet IV. +// +// https://tools.ietf.org/html/rfc7714#section-8.1 +func (s *srtpCipherAeadAesGcm) rtpInitializationVector(header *rtp.Header, roc uint32) []byte { + iv := make([]byte, 12) + binary.BigEndian.PutUint32(iv[2:], header.SSRC) + binary.BigEndian.PutUint32(iv[6:], roc) + binary.BigEndian.PutUint16(iv[10:], header.SequenceNumber) + + for i := range iv { + iv[i] ^= s.srtpSessionSalt[i] + } + return iv +} + +// The 12-octet IV used by AES-GCM SRTCP is formed by first +// concatenating 2 octets of zeroes, the 4-octet SSRC identifier, +// 2 octets of zeroes, a single "0" bit, and the 31-bit SRTCP index. +// The resulting 12-octet value is then XORed to the 12-octet salt to +// form the 12-octet IV. +// +// https://tools.ietf.org/html/rfc7714#section-9.1 +func (s *srtpCipherAeadAesGcm) rtcpInitializationVector(srtcpIndex uint32, ssrc uint32) []byte { + iv := make([]byte, 12) + + binary.BigEndian.PutUint32(iv[2:], ssrc) + binary.BigEndian.PutUint32(iv[8:], srtcpIndex) + + for i := range iv { + iv[i] ^= s.srtcpSessionSalt[i] + } + return iv +} + +// In an SRTCP packet, a 1-bit Encryption flag is prepended to the +// 31-bit SRTCP index to form a 32-bit value we shall call the +// "ESRTCP word" +// +// https://tools.ietf.org/html/rfc7714#section-17 +func (s *srtpCipherAeadAesGcm) rtcpAdditionalAuthenticatedData(rtcpPacket []byte, srtcpIndex uint32) []byte { + aad := make([]byte, 12) + + copy(aad, rtcpPacket[:8]) + binary.BigEndian.PutUint32(aad[8:], srtcpIndex) + aad[8] |= rtcpEncryptionFlag + + return aad +} + +func (s *srtpCipherAeadAesGcm) getRTCPIndex(in []byte) uint32 { + return binary.BigEndian.Uint32(in[len(in)-4:]) &^ (rtcpEncryptionFlag << 24) +} diff --git a/vendor/github.com/pion/srtp/v2/srtp_cipher_aes_cm_hmac_sha1.go b/vendor/github.com/pion/srtp/v2/srtp_cipher_aes_cm_hmac_sha1.go new file mode 100644 index 0000000..9d783d1 --- /dev/null +++ b/vendor/github.com/pion/srtp/v2/srtp_cipher_aes_cm_hmac_sha1.go @@ -0,0 +1,228 @@ +package srtp + +import ( //nolint:gci + "crypto/aes" + "crypto/cipher" + "crypto/hmac" + "crypto/sha1" //nolint:gosec + "crypto/subtle" + "encoding/binary" + "hash" + + "github.com/pion/rtp" +) + +type srtpCipherAesCmHmacSha1 struct { + srtpSessionSalt []byte + srtpSessionAuth hash.Hash + srtpBlock cipher.Block + + srtcpSessionSalt []byte + srtcpSessionAuth hash.Hash + srtcpBlock cipher.Block +} + +func newSrtpCipherAesCmHmacSha1(masterKey, masterSalt []byte) (*srtpCipherAesCmHmacSha1, error) { + s := &srtpCipherAesCmHmacSha1{} + srtpSessionKey, err := aesCmKeyDerivation(labelSRTPEncryption, masterKey, masterSalt, 0, len(masterKey)) + if err != nil { + return nil, err + } else if s.srtpBlock, err = aes.NewCipher(srtpSessionKey); err != nil { + return nil, err + } + + srtcpSessionKey, err := aesCmKeyDerivation(labelSRTCPEncryption, masterKey, masterSalt, 0, len(masterKey)) + if err != nil { + return nil, err + } else if s.srtcpBlock, err = aes.NewCipher(srtcpSessionKey); err != nil { + return nil, err + } + + if s.srtpSessionSalt, err = aesCmKeyDerivation(labelSRTPSalt, masterKey, masterSalt, 0, len(masterSalt)); err != nil { + return nil, err + } else if s.srtcpSessionSalt, err = aesCmKeyDerivation(labelSRTCPSalt, masterKey, masterSalt, 0, len(masterSalt)); err != nil { + return nil, err + } + + authKeyLen, err := ProtectionProfileAes128CmHmacSha1_80.authKeyLen() + if err != nil { + return nil, err + } + + srtpSessionAuthTag, err := aesCmKeyDerivation(labelSRTPAuthenticationTag, masterKey, masterSalt, 0, authKeyLen) + if err != nil { + return nil, err + } + + srtcpSessionAuthTag, err := aesCmKeyDerivation(labelSRTCPAuthenticationTag, masterKey, masterSalt, 0, authKeyLen) + if err != nil { + return nil, err + } + + s.srtcpSessionAuth = hmac.New(sha1.New, srtcpSessionAuthTag) + s.srtpSessionAuth = hmac.New(sha1.New, srtpSessionAuthTag) + return s, nil +} + +func (s *srtpCipherAesCmHmacSha1) authTagLen() int { + return 10 +} + +func (s *srtpCipherAesCmHmacSha1) aeadAuthTagLen() int { + return 0 +} + +func (s *srtpCipherAesCmHmacSha1) encryptRTP(dst []byte, header *rtp.Header, payload []byte, roc uint32) (ciphertext []byte, err error) { + // Grow the given buffer to fit the output. + dst = growBufferSize(dst, header.MarshalSize()+len(payload)+s.authTagLen()) + + // Copy the header unencrypted. + n, err := header.MarshalTo(dst) + if err != nil { + return nil, err + } + + // Encrypt the payload + counter := generateCounter(header.SequenceNumber, roc, header.SSRC, s.srtpSessionSalt) + stream := cipher.NewCTR(s.srtpBlock, counter) + stream.XORKeyStream(dst[n:], payload) + n += len(payload) + + // Generate the auth tag. + authTag, err := s.generateSrtpAuthTag(dst[:n], roc) + if err != nil { + return nil, err + } + + // Write the auth tag to the dest. + copy(dst[n:], authTag) + + return dst, nil +} + +func (s *srtpCipherAesCmHmacSha1) decryptRTP(dst, ciphertext []byte, header *rtp.Header, roc uint32) ([]byte, error) { + // Split the auth tag and the cipher text into two parts. + actualTag := ciphertext[len(ciphertext)-s.authTagLen():] + ciphertext = ciphertext[:len(ciphertext)-s.authTagLen()] + + // Generate the auth tag we expect to see from the ciphertext. + expectedTag, err := s.generateSrtpAuthTag(ciphertext, roc) + if err != nil { + return nil, err + } + + // See if the auth tag actually matches. + // We use a constant time comparison to prevent timing attacks. + if subtle.ConstantTimeCompare(actualTag, expectedTag) != 1 { + return nil, errFailedToVerifyAuthTag + } + + // Write the plaintext header to the destination buffer. + copy(dst, ciphertext[:header.PayloadOffset]) + + // Decrypt the ciphertext for the payload. + counter := generateCounter(header.SequenceNumber, roc, header.SSRC, s.srtpSessionSalt) + stream := cipher.NewCTR(s.srtpBlock, counter) + stream.XORKeyStream(dst[header.PayloadOffset:], ciphertext[header.PayloadOffset:]) + return dst, nil +} + +func (s *srtpCipherAesCmHmacSha1) encryptRTCP(dst, decrypted []byte, srtcpIndex uint32, ssrc uint32) ([]byte, error) { + dst = allocateIfMismatch(dst, decrypted) + + // Encrypt everything after header + stream := cipher.NewCTR(s.srtcpBlock, generateCounter(uint16(srtcpIndex&0xffff), srtcpIndex>>16, ssrc, s.srtcpSessionSalt)) + stream.XORKeyStream(dst[8:], dst[8:]) + + // Add SRTCP Index and set Encryption bit + dst = append(dst, make([]byte, 4)...) + binary.BigEndian.PutUint32(dst[len(dst)-4:], srtcpIndex) + dst[len(dst)-4] |= 0x80 + + authTag, err := s.generateSrtcpAuthTag(dst) + if err != nil { + return nil, err + } + return append(dst, authTag...), nil +} + +func (s *srtpCipherAesCmHmacSha1) decryptRTCP(out, encrypted []byte, index, ssrc uint32) ([]byte, error) { + tailOffset := len(encrypted) - (s.authTagLen() + srtcpIndexSize) + out = out[0:tailOffset] + + expectedTag, err := s.generateSrtcpAuthTag(encrypted[:len(encrypted)-s.authTagLen()]) + if err != nil { + return nil, err + } + + actualTag := encrypted[len(encrypted)-s.authTagLen():] + if subtle.ConstantTimeCompare(actualTag, expectedTag) != 1 { + return nil, errFailedToVerifyAuthTag + } + + stream := cipher.NewCTR(s.srtcpBlock, generateCounter(uint16(index&0xffff), index>>16, ssrc, s.srtcpSessionSalt)) + stream.XORKeyStream(out[8:], out[8:]) + + return out, nil +} + +func (s *srtpCipherAesCmHmacSha1) generateSrtpAuthTag(buf []byte, roc uint32) ([]byte, error) { + // https://tools.ietf.org/html/rfc3711#section-4.2 + // In the case of SRTP, M SHALL consist of the Authenticated + // Portion of the packet (as specified in Figure 1) concatenated with + // the ROC, M = Authenticated Portion || ROC; + // + // The pre-defined authentication transform for SRTP is HMAC-SHA1 + // [RFC2104]. With HMAC-SHA1, the SRTP_PREFIX_LENGTH (Figure 3) SHALL + // be 0. For SRTP (respectively SRTCP), the HMAC SHALL be applied to + // the session authentication key and M as specified above, i.e., + // HMAC(k_a, M). The HMAC output SHALL then be truncated to the n_tag + // left-most bits. + // - Authenticated portion of the packet is everything BEFORE MKI + // - k_a is the session message authentication key + // - n_tag is the bit-length of the output authentication tag + s.srtpSessionAuth.Reset() + + if _, err := s.srtpSessionAuth.Write(buf); err != nil { + return nil, err + } + + // For SRTP only, we need to hash the rollover counter as well. + rocRaw := [4]byte{} + binary.BigEndian.PutUint32(rocRaw[:], roc) + + _, err := s.srtpSessionAuth.Write(rocRaw[:]) + if err != nil { + return nil, err + } + + // Truncate the hash to the first 10 bytes. + return s.srtpSessionAuth.Sum(nil)[0:s.authTagLen()], nil +} + +func (s *srtpCipherAesCmHmacSha1) generateSrtcpAuthTag(buf []byte) ([]byte, error) { + // https://tools.ietf.org/html/rfc3711#section-4.2 + // + // The pre-defined authentication transform for SRTP is HMAC-SHA1 + // [RFC2104]. With HMAC-SHA1, the SRTP_PREFIX_LENGTH (Figure 3) SHALL + // be 0. For SRTP (respectively SRTCP), the HMAC SHALL be applied to + // the session authentication key and M as specified above, i.e., + // HMAC(k_a, M). The HMAC output SHALL then be truncated to the n_tag + // left-most bits. + // - Authenticated portion of the packet is everything BEFORE MKI + // - k_a is the session message authentication key + // - n_tag is the bit-length of the output authentication tag + s.srtcpSessionAuth.Reset() + + if _, err := s.srtcpSessionAuth.Write(buf); err != nil { + return nil, err + } + + return s.srtcpSessionAuth.Sum(nil)[0:s.authTagLen()], nil +} + +func (s *srtpCipherAesCmHmacSha1) getRTCPIndex(in []byte) uint32 { + tailOffset := len(in) - (s.authTagLen() + srtcpIndexSize) + srtcpIndexBuffer := in[tailOffset : tailOffset+srtcpIndexSize] + return binary.BigEndian.Uint32(srtcpIndexBuffer) &^ (1 << 31) +} diff --git a/vendor/github.com/pion/srtp/v2/stream.go b/vendor/github.com/pion/srtp/v2/stream.go new file mode 100644 index 0000000..7b7a0cf --- /dev/null +++ b/vendor/github.com/pion/srtp/v2/stream.go @@ -0,0 +1,8 @@ +package srtp + +type readStream interface { + init(child streamSession, ssrc uint32) error + + Read(buf []byte) (int, error) + GetSSRC() uint32 +} diff --git a/vendor/github.com/pion/srtp/v2/stream_srtcp.go b/vendor/github.com/pion/srtp/v2/stream_srtcp.go new file mode 100644 index 0000000..e335937 --- /dev/null +++ b/vendor/github.com/pion/srtp/v2/stream_srtcp.go @@ -0,0 +1,157 @@ +package srtp + +import ( + "errors" + "io" + "sync" + "time" + + "github.com/pion/rtcp" + "github.com/pion/transport/packetio" +) + +// Limit the buffer size to 100KB +const srtcpBufferSize = 100 * 1000 + +// ReadStreamSRTCP handles decryption for a single RTCP SSRC +type ReadStreamSRTCP struct { + mu sync.Mutex + + isInited bool + isClosed chan bool + + session *SessionSRTCP + ssrc uint32 + + buffer io.ReadWriteCloser +} + +func (r *ReadStreamSRTCP) write(buf []byte) (n int, err error) { + n, err = r.buffer.Write(buf) + + if errors.Is(err, packetio.ErrFull) { + // Silently drop data when the buffer is full. + return len(buf), nil + } + + return n, err +} + +// Used by getOrCreateReadStream +func newReadStreamSRTCP() readStream { + return &ReadStreamSRTCP{} +} + +// ReadRTCP reads and decrypts full RTCP packet and its header from the nextConn +func (r *ReadStreamSRTCP) ReadRTCP(buf []byte) (int, *rtcp.Header, error) { + n, err := r.Read(buf) + if err != nil { + return 0, nil, err + } + + header := &rtcp.Header{} + err = header.Unmarshal(buf[:n]) + if err != nil { + return 0, nil, err + } + + return n, header, nil +} + +// Read reads and decrypts full RTCP packet from the nextConn +func (r *ReadStreamSRTCP) Read(buf []byte) (int, error) { + return r.buffer.Read(buf) +} + +// SetReadDeadline sets the deadline for the Read operation. +// Setting to zero means no deadline. +func (r *ReadStreamSRTCP) SetReadDeadline(t time.Time) error { + if b, ok := r.buffer.(interface { + SetReadDeadline(time.Time) error + }); ok { + return b.SetReadDeadline(t) + } + return nil +} + +// Close removes the ReadStream from the session and cleans up any associated state +func (r *ReadStreamSRTCP) Close() error { + r.mu.Lock() + defer r.mu.Unlock() + + if !r.isInited { + return errStreamNotInited + } + + select { + case <-r.isClosed: + return errStreamAlreadyClosed + default: + err := r.buffer.Close() + if err != nil { + return err + } + + r.session.removeReadStream(r.ssrc) + return nil + } +} + +func (r *ReadStreamSRTCP) init(child streamSession, ssrc uint32) error { + sessionSRTCP, ok := child.(*SessionSRTCP) + + r.mu.Lock() + defer r.mu.Unlock() + if !ok { + return errFailedTypeAssertion + } else if r.isInited { + return errStreamAlreadyInited + } + + r.session = sessionSRTCP + r.ssrc = ssrc + r.isInited = true + r.isClosed = make(chan bool) + + if r.session.bufferFactory != nil { + r.buffer = r.session.bufferFactory(packetio.RTCPBufferPacket, ssrc) + } else { + // Create a buffer and limit it to 100KB + buff := packetio.NewBuffer() + buff.SetLimitSize(srtcpBufferSize) + r.buffer = buff + } + + return nil +} + +// GetSSRC returns the SSRC we are demuxing for +func (r *ReadStreamSRTCP) GetSSRC() uint32 { + return r.ssrc +} + +// WriteStreamSRTCP is stream for a single Session that is used to encrypt RTCP +type WriteStreamSRTCP struct { + session *SessionSRTCP +} + +// WriteRTCP encrypts a RTCP header and its payload to the nextConn +func (w *WriteStreamSRTCP) WriteRTCP(header *rtcp.Header, payload []byte) (int, error) { + headerRaw, err := header.Marshal() + if err != nil { + return 0, err + } + + return w.session.write(append(headerRaw, payload...)) +} + +// Write encrypts and writes a full RTCP packets to the nextConn +func (w *WriteStreamSRTCP) Write(b []byte) (int, error) { + return w.session.write(b) +} + +// SetWriteDeadline sets the deadline for the Write operation. +// Setting to zero means no deadline. +func (w *WriteStreamSRTCP) SetWriteDeadline(t time.Time) error { + return w.session.setWriteDeadline(t) +} diff --git a/vendor/github.com/pion/srtp/v2/stream_srtp.go b/vendor/github.com/pion/srtp/v2/stream_srtp.go new file mode 100644 index 0000000..c391adb --- /dev/null +++ b/vendor/github.com/pion/srtp/v2/stream_srtp.go @@ -0,0 +1,154 @@ +package srtp + +import ( + "errors" + "io" + "sync" + "time" + + "github.com/pion/rtp" + "github.com/pion/transport/packetio" +) + +// Limit the buffer size to 1MB +const srtpBufferSize = 1000 * 1000 + +// ReadStreamSRTP handles decryption for a single RTP SSRC +type ReadStreamSRTP struct { + mu sync.Mutex + + isInited bool + isClosed chan bool + + session *SessionSRTP + ssrc uint32 + + buffer io.ReadWriteCloser +} + +// Used by getOrCreateReadStream +func newReadStreamSRTP() readStream { + return &ReadStreamSRTP{} +} + +func (r *ReadStreamSRTP) init(child streamSession, ssrc uint32) error { + sessionSRTP, ok := child.(*SessionSRTP) + + r.mu.Lock() + defer r.mu.Unlock() + + if !ok { + return errFailedTypeAssertion + } else if r.isInited { + return errStreamAlreadyInited + } + + r.session = sessionSRTP + r.ssrc = ssrc + r.isInited = true + r.isClosed = make(chan bool) + + // Create a buffer with a 1MB limit + if r.session.bufferFactory != nil { + r.buffer = r.session.bufferFactory(packetio.RTPBufferPacket, ssrc) + } else { + buff := packetio.NewBuffer() + buff.SetLimitSize(srtpBufferSize) + r.buffer = buff + } + + return nil +} + +func (r *ReadStreamSRTP) write(buf []byte) (n int, err error) { + n, err = r.buffer.Write(buf) + + if errors.Is(err, packetio.ErrFull) { + // Silently drop data when the buffer is full. + return len(buf), nil + } + + return n, err +} + +// Read reads and decrypts full RTP packet from the nextConn +func (r *ReadStreamSRTP) Read(buf []byte) (int, error) { + return r.buffer.Read(buf) +} + +// ReadRTP reads and decrypts full RTP packet and its header from the nextConn +func (r *ReadStreamSRTP) ReadRTP(buf []byte) (int, *rtp.Header, error) { + n, err := r.Read(buf) + if err != nil { + return 0, nil, err + } + + header := &rtp.Header{} + + err = header.Unmarshal(buf[:n]) + if err != nil { + return 0, nil, err + } + + return n, header, nil +} + +// SetReadDeadline sets the deadline for the Read operation. +// Setting to zero means no deadline. +func (r *ReadStreamSRTP) SetReadDeadline(t time.Time) error { + if b, ok := r.buffer.(interface { + SetReadDeadline(time.Time) error + }); ok { + return b.SetReadDeadline(t) + } + return nil +} + +// Close removes the ReadStream from the session and cleans up any associated state +func (r *ReadStreamSRTP) Close() error { + r.mu.Lock() + defer r.mu.Unlock() + + if !r.isInited { + return errStreamNotInited + } + + select { + case <-r.isClosed: + return errStreamAlreadyClosed + default: + err := r.buffer.Close() + if err != nil { + return err + } + + r.session.removeReadStream(r.ssrc) + return nil + } +} + +// GetSSRC returns the SSRC we are demuxing for +func (r *ReadStreamSRTP) GetSSRC() uint32 { + return r.ssrc +} + +// WriteStreamSRTP is stream for a single Session that is used to encrypt RTP +type WriteStreamSRTP struct { + session *SessionSRTP +} + +// WriteRTP encrypts a RTP packet and writes to the connection +func (w *WriteStreamSRTP) WriteRTP(header *rtp.Header, payload []byte) (int, error) { + return w.session.writeRTP(header, payload) +} + +// Write encrypts and writes a full RTP packets to the nextConn +func (w *WriteStreamSRTP) Write(b []byte) (int, error) { + return w.session.write(b) +} + +// SetWriteDeadline sets the deadline for the Write operation. +// Setting to zero means no deadline. +func (w *WriteStreamSRTP) SetWriteDeadline(t time.Time) error { + return w.session.setWriteDeadline(t) +} diff --git a/vendor/github.com/pion/srtp/v2/util.go b/vendor/github.com/pion/srtp/v2/util.go new file mode 100644 index 0000000..1ae34a6 --- /dev/null +++ b/vendor/github.com/pion/srtp/v2/util.go @@ -0,0 +1,33 @@ +package srtp + +import "bytes" + +// Grow the buffer size to the given number of bytes. +func growBufferSize(buf []byte, size int) []byte { + if size <= cap(buf) { + return buf[:size] + } + + buf2 := make([]byte, size) + copy(buf2, buf) + return buf2 +} + +// Check if buffers match, if not allocate a new buffer and return it +func allocateIfMismatch(dst, src []byte) []byte { + if dst == nil { + dst = make([]byte, len(src)) + copy(dst, src) + } else if !bytes.Equal(dst, src) { // bytes.Equal returns on ref equality, no optimization needed + extraNeeded := len(src) - len(dst) + if extraNeeded > 0 { + dst = append(dst, make([]byte, extraNeeded)...) + } else if extraNeeded < 0 { + dst = dst[:len(dst)+extraNeeded] + } + + copy(dst, src) + } + + return dst +} |