diff options
Diffstat (limited to 'vendor/github.com/pion/dtls')
103 files changed, 8887 insertions, 0 deletions
diff --git a/vendor/github.com/pion/dtls/v2/.editorconfig b/vendor/github.com/pion/dtls/v2/.editorconfig new file mode 100644 index 0000000..d2b3206 --- /dev/null +++ b/vendor/github.com/pion/dtls/v2/.editorconfig @@ -0,0 +1,21 @@ +# http://editorconfig.org/ + +root = true + +[*] +charset = utf-8 +insert_final_newline = true +trim_trailing_whitespace = true +end_of_line = lf + +[*.go] +indent_style = tab +indent_size = 4 + +[{*.yml,*.yaml}] +indent_style = space +indent_size = 2 + +# Makefiles always use tabs for indentation +[Makefile] +indent_style = tab diff --git a/vendor/github.com/pion/dtls/v2/.gitignore b/vendor/github.com/pion/dtls/v2/.gitignore new file mode 100644 index 0000000..83db74b --- /dev/null +++ b/vendor/github.com/pion/dtls/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/dtls/v2/.golangci.yml b/vendor/github.com/pion/dtls/v2/.golangci.yml new file mode 100644 index 0000000..d6162c9 --- /dev/null +++ b/vendor/github.com/pion/dtls/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/dtls/v2/LICENSE b/vendor/github.com/pion/dtls/v2/LICENSE new file mode 100644 index 0000000..ab60297 --- /dev/null +++ b/vendor/github.com/pion/dtls/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/dtls/v2/Makefile b/vendor/github.com/pion/dtls/v2/Makefile new file mode 100644 index 0000000..1df38b2 --- /dev/null +++ b/vendor/github.com/pion/dtls/v2/Makefile @@ -0,0 +1,6 @@ +fuzz-build-record-layer: fuzz-prepare + go-fuzz-build -tags gofuzz -func FuzzRecordLayer +fuzz-run-record-layer: + go-fuzz -bin dtls-fuzz.zip -workdir fuzz +fuzz-prepare: + @GO111MODULE=on go mod vendor diff --git a/vendor/github.com/pion/dtls/v2/README.md b/vendor/github.com/pion/dtls/v2/README.md new file mode 100644 index 0000000..62cac7c --- /dev/null +++ b/vendor/github.com/pion/dtls/v2/README.md @@ -0,0 +1,155 @@ +<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://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/dtls"><img src="https://travis-ci.org/pion/dtls.svg?branch=master" alt="Build Status"></a> + <a href="https://pkg.go.dev/github.com/pion/dtls"><img src="https://godoc.org/github.com/pion/dtls?status.svg" alt="GoDoc"></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"><img src="https://goreportcard.com/badge/github.com/pion/dtls" alt="Go Report Card"></a> + <a href="https://www.codacy.com/app/Sean-Der/dtls"><img src="https://api.codacy.com/project/badge/Grade/18f4aec384894e6aac0b94effe51961d" alt="Codacy Badge"></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 maye inclusion in stdlib. + +[rfc6347]: https://tools.ietf.org/html/rfc6347 + +### Goals/Progress +This will only be targeting DTLS 1.2, and the most modern/common cipher suites. +We would love contributes that fall under the 'Planned Features' and fixing any bugs! + +#### Current features +* DTLS 1.2 Client/Server +* Key Exchange via ECDHE(curve25519, nistp256, nistp384) and PSK +* Packet loss and re-ordering is handled during handshaking +* Key export ([RFC 5705][rfc5705]) +* Serialization and Resumption of sessions +* Extended Master Secret extension ([RFC 7627][rfc7627]) + +[rfc5705]: https://tools.ietf.org/html/rfc5705 +[rfc7627]: https://tools.ietf.org/html/rfc7627 + +#### Supported ciphers + +##### ECDHE +* TLS_ECDHE_ECDSA_WITH_AES_128_CCM ([RFC 6655][rfc6655]) +* TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8 ([RFC 6655][rfc6655]) +* TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 ([RFC 5289][rfc5289]) +* TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 ([RFC 5289][rfc5289]) +* TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA ([RFC 8422][rfc8422]) +* TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA ([RFC 8422][rfc8422]) + +##### PSK +* TLS_PSK_WITH_AES_128_CCM ([RFC 6655][rfc6655]) +* TLS_PSK_WITH_AES_128_CCM_8 ([RFC 6655][rfc6655]) +* TLS_PSK_WITH_AES_128_GCM_SHA256 ([RFC 5487][rfc5487]) +* TLS_PSK_WITH_AES_128_CBC_SHA256 ([RFC 5487][rfc5487]) + +[rfc5289]: https://tools.ietf.org/html/rfc5289 +[rfc8422]: https://tools.ietf.org/html/rfc8422 +[rfc6655]: https://tools.ietf.org/html/rfc6655 +[rfc5487]: https://tools.ietf.org/html/rfc5487 + +#### Planned Features +* Chacha20Poly1305 + +#### Excluded Features +* DTLS 1.0 +* Renegotiation +* Compression + +### Using + +This library needs at least Go 1.13, and you should have [Go modules +enabled](https://github.com/golang/go/wiki/Modules). + +#### Pion DTLS +For a DTLS 1.2 Server that listens on 127.0.0.1:4444 +```sh +go run examples/listen/selfsign/main.go +``` + +For a DTLS 1.2 Client that connects to 127.0.0.1:4444 +```sh +go run examples/dial/selfsign/main.go +``` + +#### OpenSSL +Pion DTLS can connect to itself and OpenSSL. +``` + // Generate a certificate + openssl ecparam -out key.pem -name prime256v1 -genkey + openssl req -new -sha256 -key key.pem -out server.csr + openssl x509 -req -sha256 -days 365 -in server.csr -signkey key.pem -out cert.pem + + // Use with examples/dial/selfsign/main.go + openssl s_server -dtls1_2 -cert cert.pem -key key.pem -accept 4444 + + // Use with examples/listen/selfsign/main.go + openssl s_client -dtls1_2 -connect 127.0.0.1:4444 -debug -cert cert.pem -key key.pem +``` + +### Using with PSK +Pion DTLS also comes with examples that do key exchange via PSK + + +#### Pion DTLS +```sh +go run examples/listen/psk/main.go +``` + +```sh +go run examples/dial/psk/main.go +``` + +#### OpenSSL +``` + // Use with examples/dial/psk/main.go + openssl s_server -dtls1_2 -accept 4444 -nocert -psk abc123 -cipher PSK-AES128-CCM8 + + // Use with examples/listen/psk/main.go + openssl s_client -dtls1_2 -connect 127.0.0.1:4444 -psk abc123 -cipher PSK-AES128-CCM8 +``` + +### Contributing +Check out the **[contributing wiki](https://github.com/pion/webrtc/wiki/Contributing)** to join the group of amazing people making this project possible: + +* [Sean DuBois](https://github.com/Sean-Der) - *Original Author* +* [Michiel De Backker](https://github.com/backkem) - *Public API* +* [Chris Hiszpanski](https://github.com/thinkski) - *Support Signature Algorithms Extension* +* [Iñigo Garcia Olaizola](https://github.com/igolaizola) - *Serialization & resumption, cert verification, E2E* +* [Daniele Sluijters](https://github.com/daenney) - *AES-CCM support* +* [Jin Lei](https://github.com/jinleileiking) - *Logging* +* [Hugo Arregui](https://github.com/hugoArregui) +* [Lander Noterman](https://github.com/LanderN) +* [Aleksandr Razumov](https://github.com/ernado) - *Fuzzing* +* [Ryan Gordon](https://github.com/ryangordon) +* [Stefan Tatschner](https://rumpelsepp.org/contact.html) +* [Hayden James](https://github.com/hjames9) +* [Jozef Kralik](https://github.com/jkralik) +* [Robert Eperjesi](https://github.com/epes) +* [Atsushi Watanabe](https://github.com/at-wat) +* [Julien Salleyron](https://github.com/juliens) - *Server Name Indication* +* [Jeroen de Bruijn](https://github.com/vidavidorra) +* [bjdgyc](https://github.com/bjdgyc) +* [Jeffrey Stoke (Jeff Ctor)](https://github.com/jeffreystoke) - *Fragmentbuffer Fix* +* [Frank Olbricht](https://github.com/folbricht) +* [ZHENK](https://github.com/scorpionknifes) +* [Carson Hoffman](https://github.com/CarsonHoffman) +* [Vadim Filimonov](https://github.com/fffilimonov) +* [Jim Wert](https://github.com/bocajim) +* [Alvaro Viebrantz](https://github.com/alvarowolfx) +* [Kegan Dougal](https://github.com/Kegsay) + +### License +MIT License - see [LICENSE](LICENSE) for full text diff --git a/vendor/github.com/pion/dtls/v2/certificate.go b/vendor/github.com/pion/dtls/v2/certificate.go new file mode 100644 index 0000000..c99e1c9 --- /dev/null +++ b/vendor/github.com/pion/dtls/v2/certificate.go @@ -0,0 +1,67 @@ +package dtls + +import ( + "crypto/tls" + "crypto/x509" + "strings" +) + +func (c *handshakeConfig) getCertificate(serverName string) (*tls.Certificate, error) { + c.mu.Lock() + defer c.mu.Unlock() + + if c.nameToCertificate == nil { + nameToCertificate := make(map[string]*tls.Certificate) + for i := range c.localCertificates { + cert := &c.localCertificates[i] + x509Cert := cert.Leaf + if x509Cert == nil { + var parseErr error + x509Cert, parseErr = x509.ParseCertificate(cert.Certificate[0]) + if parseErr != nil { + continue + } + } + if len(x509Cert.Subject.CommonName) > 0 { + nameToCertificate[strings.ToLower(x509Cert.Subject.CommonName)] = cert + } + for _, san := range x509Cert.DNSNames { + nameToCertificate[strings.ToLower(san)] = cert + } + } + c.nameToCertificate = nameToCertificate + } + + if len(c.localCertificates) == 0 { + return nil, errNoCertificates + } + + if len(c.localCertificates) == 1 { + // There's only one choice, so no point doing any work. + return &c.localCertificates[0], nil + } + + if len(serverName) == 0 { + return &c.localCertificates[0], nil + } + + name := strings.TrimRight(strings.ToLower(serverName), ".") + + if cert, ok := c.nameToCertificate[name]; ok { + return cert, nil + } + + // try replacing labels in the name with wildcards until we get a + // match. + labels := strings.Split(name, ".") + for i := range labels { + labels[i] = "*" + candidate := strings.Join(labels, ".") + if cert, ok := c.nameToCertificate[candidate]; ok { + return cert, nil + } + } + + // If nothing matches, return the first certificate. + return &c.localCertificates[0], nil +} diff --git a/vendor/github.com/pion/dtls/v2/cipher_suite.go b/vendor/github.com/pion/dtls/v2/cipher_suite.go new file mode 100644 index 0000000..ed10609 --- /dev/null +++ b/vendor/github.com/pion/dtls/v2/cipher_suite.go @@ -0,0 +1,213 @@ +package dtls + +import ( + "fmt" + "hash" + + "github.com/pion/dtls/v2/internal/ciphersuite" + "github.com/pion/dtls/v2/pkg/crypto/clientcertificate" + "github.com/pion/dtls/v2/pkg/protocol/recordlayer" +) + +// CipherSuiteID is an ID for our supported CipherSuites +type CipherSuiteID = ciphersuite.ID + +// Supported Cipher Suites +const ( + // AES-128-CCM + TLS_ECDHE_ECDSA_WITH_AES_128_CCM CipherSuiteID = ciphersuite.TLS_ECDHE_ECDSA_WITH_AES_128_CCM //nolint:golint,stylecheck + TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8 CipherSuiteID = ciphersuite.TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8 //nolint:golint,stylecheck + + // AES-128-GCM-SHA256 + TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 CipherSuiteID = ciphersuite.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 //nolint:golint,stylecheck + TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 CipherSuiteID = ciphersuite.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 //nolint:golint,stylecheck + + // AES-256-CBC-SHA + TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA CipherSuiteID = ciphersuite.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA //nolint:golint,stylecheck + TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA CipherSuiteID = ciphersuite.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA //nolint:golint,stylecheck + + TLS_PSK_WITH_AES_128_CCM CipherSuiteID = ciphersuite.TLS_PSK_WITH_AES_128_CCM //nolint:golint,stylecheck + TLS_PSK_WITH_AES_128_CCM_8 CipherSuiteID = ciphersuite.TLS_PSK_WITH_AES_128_CCM_8 //nolint:golint,stylecheck + TLS_PSK_WITH_AES_128_GCM_SHA256 CipherSuiteID = ciphersuite.TLS_PSK_WITH_AES_128_GCM_SHA256 //nolint:golint,stylecheck + TLS_PSK_WITH_AES_128_CBC_SHA256 CipherSuiteID = ciphersuite.TLS_PSK_WITH_AES_128_CBC_SHA256 //nolint:golint,stylecheck +) + +// CipherSuiteAuthenticationType controls what authentication method is using during the handshake for a CipherSuite +type CipherSuiteAuthenticationType = ciphersuite.AuthenticationType + +// AuthenticationType Enums +const ( + CipherSuiteAuthenticationTypeCertificate CipherSuiteAuthenticationType = ciphersuite.AuthenticationTypeCertificate + CipherSuiteAuthenticationTypePreSharedKey CipherSuiteAuthenticationType = ciphersuite.AuthenticationTypePreSharedKey + CipherSuiteAuthenticationTypeAnonymous CipherSuiteAuthenticationType = ciphersuite.AuthenticationTypeAnonymous +) + +var _ = allCipherSuites() // Necessary until this function isn't only used by Go 1.14 + +// CipherSuite is an interface that all DTLS CipherSuites must satisfy +type CipherSuite interface { + // String of CipherSuite, only used for logging + String() string + + // ID of CipherSuite. + ID() CipherSuiteID + + // What type of Certificate does this CipherSuite use + CertificateType() clientcertificate.Type + + // What Hash function is used during verification + HashFunc() func() hash.Hash + + // AuthenticationType controls what authentication method is using during the handshake + AuthenticationType() CipherSuiteAuthenticationType + + // Called when keying material has been generated, should initialize the internal cipher + Init(masterSecret, clientRandom, serverRandom []byte, isClient bool) error + IsInitialized() bool + + Encrypt(pkt *recordlayer.RecordLayer, raw []byte) ([]byte, error) + Decrypt(in []byte) ([]byte, error) +} + +// CipherSuiteName provides the same functionality as tls.CipherSuiteName +// that appeared first in Go 1.14. +// +// Our implementation differs slightly in that it takes in a CiperSuiteID, +// like the rest of our library, instead of a uint16 like crypto/tls. +func CipherSuiteName(id CipherSuiteID) string { + suite := cipherSuiteForID(id, nil) + if suite != nil { + return suite.String() + } + return fmt.Sprintf("0x%04X", uint16(id)) +} + +// Taken from https://www.iana.org/assignments/tls-parameters/tls-parameters.xml +// A cipherSuite is a specific combination of key agreement, cipher and MAC +// function. +func cipherSuiteForID(id CipherSuiteID, customCiphers func() []CipherSuite) CipherSuite { + switch id { //nolint:exhaustive + case TLS_ECDHE_ECDSA_WITH_AES_128_CCM: + return ciphersuite.NewTLSEcdheEcdsaWithAes128Ccm() + case TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8: + return ciphersuite.NewTLSEcdheEcdsaWithAes128Ccm8() + case TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256: + return &ciphersuite.TLSEcdheEcdsaWithAes128GcmSha256{} + case TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256: + return &ciphersuite.TLSEcdheRsaWithAes128GcmSha256{} + case TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA: + return &ciphersuite.TLSEcdheEcdsaWithAes256CbcSha{} + case TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA: + return &ciphersuite.TLSEcdheRsaWithAes256CbcSha{} + case TLS_PSK_WITH_AES_128_CCM: + return ciphersuite.NewTLSPskWithAes128Ccm() + case TLS_PSK_WITH_AES_128_CCM_8: + return ciphersuite.NewTLSPskWithAes128Ccm8() + case TLS_PSK_WITH_AES_128_GCM_SHA256: + return &ciphersuite.TLSPskWithAes128GcmSha256{} + case TLS_PSK_WITH_AES_128_CBC_SHA256: + return &ciphersuite.TLSPskWithAes128CbcSha256{} + } + + if customCiphers != nil { + for _, c := range customCiphers() { + if c.ID() == id { + return c + } + } + } + + return nil +} + +// CipherSuites we support in order of preference +func defaultCipherSuites() []CipherSuite { + return []CipherSuite{ + &ciphersuite.TLSEcdheEcdsaWithAes128GcmSha256{}, + &ciphersuite.TLSEcdheRsaWithAes128GcmSha256{}, + &ciphersuite.TLSEcdheEcdsaWithAes256CbcSha{}, + &ciphersuite.TLSEcdheRsaWithAes256CbcSha{}, + } +} + +func allCipherSuites() []CipherSuite { + return []CipherSuite{ + ciphersuite.NewTLSEcdheEcdsaWithAes128Ccm(), + ciphersuite.NewTLSEcdheEcdsaWithAes128Ccm8(), + &ciphersuite.TLSEcdheEcdsaWithAes128GcmSha256{}, + &ciphersuite.TLSEcdheRsaWithAes128GcmSha256{}, + &ciphersuite.TLSEcdheEcdsaWithAes256CbcSha{}, + &ciphersuite.TLSEcdheRsaWithAes256CbcSha{}, + ciphersuite.NewTLSPskWithAes128Ccm(), + ciphersuite.NewTLSPskWithAes128Ccm8(), + &ciphersuite.TLSPskWithAes128GcmSha256{}, + } +} + +func cipherSuiteIDs(cipherSuites []CipherSuite) []uint16 { + rtrn := []uint16{} + for _, c := range cipherSuites { + rtrn = append(rtrn, uint16(c.ID())) + } + return rtrn +} + +func parseCipherSuites(userSelectedSuites []CipherSuiteID, customCipherSuites func() []CipherSuite, includeCertificateSuites, includePSKSuites bool) ([]CipherSuite, error) { + cipherSuitesForIDs := func(ids []CipherSuiteID) ([]CipherSuite, error) { + cipherSuites := []CipherSuite{} + for _, id := range ids { + c := cipherSuiteForID(id, nil) + if c == nil { + return nil, &invalidCipherSuite{id} + } + cipherSuites = append(cipherSuites, c) + } + return cipherSuites, nil + } + + var ( + cipherSuites []CipherSuite + err error + i int + ) + if userSelectedSuites != nil { + cipherSuites, err = cipherSuitesForIDs(userSelectedSuites) + if err != nil { + return nil, err + } + } else { + cipherSuites = defaultCipherSuites() + } + + // Put CustomCipherSuites before ID selected suites + if customCipherSuites != nil { + cipherSuites = append(customCipherSuites(), cipherSuites...) + } + + var foundCertificateSuite, foundPSKSuite, foundAnonymousSuite bool + for _, c := range cipherSuites { + switch { + case includeCertificateSuites && c.AuthenticationType() == CipherSuiteAuthenticationTypeCertificate: + foundCertificateSuite = true + case includePSKSuites && c.AuthenticationType() == CipherSuiteAuthenticationTypePreSharedKey: + foundPSKSuite = true + case c.AuthenticationType() == CipherSuiteAuthenticationTypeAnonymous: + foundAnonymousSuite = true + default: + continue + } + cipherSuites[i] = c + i++ + } + + switch { + case includeCertificateSuites && !foundCertificateSuite && !foundAnonymousSuite: + return nil, errNoAvailableCertificateCipherSuite + case includePSKSuites && !foundPSKSuite: + return nil, errNoAvailablePSKCipherSuite + case i == 0: + return nil, errNoAvailableCipherSuites + } + + return cipherSuites[:i], nil +} diff --git a/vendor/github.com/pion/dtls/v2/cipher_suite_go114.go b/vendor/github.com/pion/dtls/v2/cipher_suite_go114.go new file mode 100644 index 0000000..7bba16e --- /dev/null +++ b/vendor/github.com/pion/dtls/v2/cipher_suite_go114.go @@ -0,0 +1,40 @@ +// +build go1.14 + +package dtls + +import ( + "crypto/tls" +) + +// VersionDTLS12 is the DTLS version in the same style as +// VersionTLSXX from crypto/tls +const VersionDTLS12 = 0xfefd + +// Convert from our cipherSuite interface to a tls.CipherSuite struct +func toTLSCipherSuite(c CipherSuite) *tls.CipherSuite { + return &tls.CipherSuite{ + ID: uint16(c.ID()), + Name: c.String(), + SupportedVersions: []uint16{VersionDTLS12}, + Insecure: false, + } +} + +// CipherSuites returns a list of cipher suites currently implemented by this +// package, excluding those with security issues, which are returned by +// InsecureCipherSuites. +func CipherSuites() []*tls.CipherSuite { + suites := allCipherSuites() + res := make([]*tls.CipherSuite, len(suites)) + for i, c := range suites { + res[i] = toTLSCipherSuite(c) + } + return res +} + +// InsecureCipherSuites returns a list of cipher suites currently implemented by +// this package and which have security issues. +func InsecureCipherSuites() []*tls.CipherSuite { + var res []*tls.CipherSuite + return res +} diff --git a/vendor/github.com/pion/dtls/v2/codecov.yml b/vendor/github.com/pion/dtls/v2/codecov.yml new file mode 100644 index 0000000..085200a --- /dev/null +++ b/vendor/github.com/pion/dtls/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/dtls/v2/compression_method.go b/vendor/github.com/pion/dtls/v2/compression_method.go new file mode 100644 index 0000000..693eb7a --- /dev/null +++ b/vendor/github.com/pion/dtls/v2/compression_method.go @@ -0,0 +1,9 @@ +package dtls + +import "github.com/pion/dtls/v2/pkg/protocol" + +func defaultCompressionMethods() []*protocol.CompressionMethod { + return []*protocol.CompressionMethod{ + {}, + } +} diff --git a/vendor/github.com/pion/dtls/v2/config.go b/vendor/github.com/pion/dtls/v2/config.go new file mode 100644 index 0000000..5ad42da --- /dev/null +++ b/vendor/github.com/pion/dtls/v2/config.go @@ -0,0 +1,193 @@ +package dtls + +import ( + "context" + "crypto/ecdsa" + "crypto/ed25519" + "crypto/tls" + "crypto/x509" + "io" + "time" + + "github.com/pion/logging" +) + +const keyLogLabelTLS12 = "CLIENT_RANDOM" + +// Config is used to configure a DTLS client or server. +// After a Config is passed to a DTLS function it must not be modified. +type Config struct { + // Certificates contains certificate chain to present to the other side of the connection. + // Server MUST set this if PSK is non-nil + // client SHOULD sets this so CertificateRequests can be handled if PSK is non-nil + Certificates []tls.Certificate + + // CipherSuites is a list of supported cipher suites. + // If CipherSuites is nil, a default list is used + CipherSuites []CipherSuiteID + + // CustomCipherSuites is a list of CipherSuites that can be + // provided by the user. This allow users to user Ciphers that are reserved + // for private usage. + CustomCipherSuites func() []CipherSuite + + // SignatureSchemes contains the signature and hash schemes that the peer requests to verify. + SignatureSchemes []tls.SignatureScheme + + // SRTPProtectionProfiles are the supported protection profiles + // Clients will send this via use_srtp and assert that the server properly responds + // Servers will assert that clients send one of these profiles and will respond as needed + SRTPProtectionProfiles []SRTPProtectionProfile + + // ClientAuth determines the server's policy for + // TLS Client Authentication. The default is NoClientCert. + ClientAuth ClientAuthType + + // RequireExtendedMasterSecret determines if the "Extended Master Secret" extension + // should be disabled, requested, or required (default requested). + ExtendedMasterSecret ExtendedMasterSecretType + + // FlightInterval controls how often we send outbound handshake messages + // defaults to time.Second + FlightInterval time.Duration + + // PSK sets the pre-shared key used by this DTLS connection + // If PSK is non-nil only PSK CipherSuites will be used + PSK PSKCallback + PSKIdentityHint []byte + + // InsecureSkipVerify controls whether a client verifies the + // server's certificate chain and host name. + // If InsecureSkipVerify is true, TLS accepts any certificate + // presented by the server and any host name in that certificate. + // In this mode, TLS is susceptible to man-in-the-middle attacks. + // This should be used only for testing. + InsecureSkipVerify bool + + // InsecureHashes allows the use of hashing algorithms that are known + // to be vulnerable. + InsecureHashes bool + + // VerifyPeerCertificate, if not nil, is called after normal + // certificate verification by either a client or server. It + // receives the certificate provided by the peer and also a flag + // that tells if normal verification has succeedded. If it returns a + // non-nil error, the handshake is aborted and that error results. + // + // If normal verification fails then the handshake will abort before + // considering this callback. If normal verification is disabled by + // setting InsecureSkipVerify, or (for a server) when ClientAuth is + // RequestClientCert or RequireAnyClientCert, then this callback will + // be considered but the verifiedChains will always be nil. + VerifyPeerCertificate func(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error + + // RootCAs defines the set of root certificate authorities + // that one peer uses when verifying the other peer's certificates. + // If RootCAs is nil, TLS uses the host's root CA set. + RootCAs *x509.CertPool + + // ClientCAs defines the set of root certificate authorities + // that servers use if required to verify a client certificate + // by the policy in ClientAuth. + ClientCAs *x509.CertPool + + // ServerName is used to verify the hostname on the returned + // certificates unless InsecureSkipVerify is given. + ServerName string + + LoggerFactory logging.LoggerFactory + + // ConnectContextMaker is a function to make a context used in Dial(), + // Client(), Server(), and Accept(). If nil, the default ConnectContextMaker + // is used. It can be implemented as following. + // + // func ConnectContextMaker() (context.Context, func()) { + // return context.WithTimeout(context.Background(), 30*time.Second) + // } + ConnectContextMaker func() (context.Context, func()) + + // MTU is the length at which handshake messages will be fragmented to + // fit within the maximum transmission unit (default is 1200 bytes) + MTU int + + // ReplayProtectionWindow is the size of the replay attack protection window. + // Duplication of the sequence number is checked in this window size. + // Packet with sequence number older than this value compared to the latest + // accepted packet will be discarded. (default is 64) + ReplayProtectionWindow int + + // KeyLogWriter optionally specifies a destination for TLS master secrets + // in NSS key log format that can be used to allow external programs + // such as Wireshark to decrypt TLS connections. + // See https://developer.mozilla.org/en-US/docs/Mozilla/Projects/NSS/Key_Log_Format. + // Use of KeyLogWriter compromises security and should only be + // used for debugging. + KeyLogWriter io.Writer +} + +func defaultConnectContextMaker() (context.Context, func()) { + return context.WithTimeout(context.Background(), 30*time.Second) +} + +func (c *Config) connectContextMaker() (context.Context, func()) { + if c.ConnectContextMaker == nil { + return defaultConnectContextMaker() + } + return c.ConnectContextMaker() +} + +const defaultMTU = 1200 // bytes + +// PSKCallback is called once we have the remote's PSKIdentityHint. +// If the remote provided none it will be nil +type PSKCallback func([]byte) ([]byte, error) + +// ClientAuthType declares the policy the server will follow for +// TLS Client Authentication. +type ClientAuthType int + +// ClientAuthType enums +const ( + NoClientCert ClientAuthType = iota + RequestClientCert + RequireAnyClientCert + VerifyClientCertIfGiven + RequireAndVerifyClientCert +) + +// ExtendedMasterSecretType declares the policy the client and server +// will follow for the Extended Master Secret extension +type ExtendedMasterSecretType int + +// ExtendedMasterSecretType enums +const ( + RequestExtendedMasterSecret ExtendedMasterSecretType = iota + RequireExtendedMasterSecret + DisableExtendedMasterSecret +) + +func validateConfig(config *Config) error { + switch { + case config == nil: + return errNoConfigProvided + case config.PSKIdentityHint != nil && config.PSK == nil: + return errIdentityNoPSK + } + + for _, cert := range config.Certificates { + if cert.Certificate == nil { + return errInvalidCertificate + } + if cert.PrivateKey != nil { + switch cert.PrivateKey.(type) { + case ed25519.PrivateKey: + case *ecdsa.PrivateKey: + default: + return errInvalidPrivateKey + } + } + } + + _, err := parseCipherSuites(config.CipherSuites, config.CustomCipherSuites, config.PSK == nil || len(config.Certificates) > 0, config.PSK != nil) + return err +} diff --git a/vendor/github.com/pion/dtls/v2/conn.go b/vendor/github.com/pion/dtls/v2/conn.go new file mode 100644 index 0000000..42d732f --- /dev/null +++ b/vendor/github.com/pion/dtls/v2/conn.go @@ -0,0 +1,978 @@ +package dtls + +import ( + "context" + "errors" + "fmt" + "io" + "net" + "sync" + "sync/atomic" + "time" + + "github.com/pion/dtls/v2/internal/closer" + "github.com/pion/dtls/v2/pkg/crypto/elliptic" + "github.com/pion/dtls/v2/pkg/crypto/signaturehash" + "github.com/pion/dtls/v2/pkg/protocol" + "github.com/pion/dtls/v2/pkg/protocol/alert" + "github.com/pion/dtls/v2/pkg/protocol/handshake" + "github.com/pion/dtls/v2/pkg/protocol/recordlayer" + "github.com/pion/logging" + "github.com/pion/transport/connctx" + "github.com/pion/transport/deadline" + "github.com/pion/transport/replaydetector" +) + +const ( + initialTickerInterval = time.Second + cookieLength = 20 + defaultNamedCurve = elliptic.X25519 + inboundBufferSize = 8192 + // Default replay protection window is specified by RFC 6347 Section 4.1.2.6 + defaultReplayProtectionWindow = 64 +) + +func invalidKeyingLabels() map[string]bool { + return map[string]bool{ + "client finished": true, + "server finished": true, + "master secret": true, + "key expansion": true, + } +} + +// Conn represents a DTLS connection +type Conn struct { + lock sync.RWMutex // Internal lock (must not be public) + nextConn connctx.ConnCtx // Embedded Conn, typically a udpconn we read/write from + fragmentBuffer *fragmentBuffer // out-of-order and missing fragment handling + handshakeCache *handshakeCache // caching of handshake messages for verifyData generation + decrypted chan interface{} // Decrypted Application Data or error, pull by calling `Read` + + state State // Internal state + + maximumTransmissionUnit int + + handshakeCompletedSuccessfully atomic.Value + + encryptedPackets [][]byte + + connectionClosedByUser bool + closeLock sync.Mutex + closed *closer.Closer + handshakeLoopsFinished sync.WaitGroup + + readDeadline *deadline.Deadline + writeDeadline *deadline.Deadline + + log logging.LeveledLogger + + reading chan struct{} + handshakeRecv chan chan struct{} + cancelHandshaker func() + cancelHandshakeReader func() + + fsm *handshakeFSM + + replayProtectionWindow uint +} + +func createConn(ctx context.Context, nextConn net.Conn, config *Config, isClient bool, initialState *State) (*Conn, error) { + err := validateConfig(config) + if err != nil { + return nil, err + } + + if nextConn == nil { + return nil, errNilNextConn + } + + cipherSuites, err := parseCipherSuites(config.CipherSuites, config.CustomCipherSuites, config.PSK == nil || len(config.Certificates) > 0, config.PSK != nil) + if err != nil { + return nil, err + } + + signatureSchemes, err := signaturehash.ParseSignatureSchemes(config.SignatureSchemes, config.InsecureHashes) + if err != nil { + return nil, err + } + + workerInterval := initialTickerInterval + if config.FlightInterval != 0 { + workerInterval = config.FlightInterval + } + + loggerFactory := config.LoggerFactory + if loggerFactory == nil { + loggerFactory = logging.NewDefaultLoggerFactory() + } + + logger := loggerFactory.NewLogger("dtls") + + mtu := config.MTU + if mtu <= 0 { + mtu = defaultMTU + } + + replayProtectionWindow := config.ReplayProtectionWindow + if replayProtectionWindow <= 0 { + replayProtectionWindow = defaultReplayProtectionWindow + } + + c := &Conn{ + nextConn: connctx.New(nextConn), + fragmentBuffer: newFragmentBuffer(), + handshakeCache: newHandshakeCache(), + maximumTransmissionUnit: mtu, + + decrypted: make(chan interface{}, 1), + log: logger, + + readDeadline: deadline.New(), + writeDeadline: deadline.New(), + + reading: make(chan struct{}, 1), + handshakeRecv: make(chan chan struct{}), + closed: closer.NewCloser(), + cancelHandshaker: func() {}, + + replayProtectionWindow: uint(replayProtectionWindow), + + state: State{ + isClient: isClient, + }, + } + + c.setRemoteEpoch(0) + c.setLocalEpoch(0) + + serverName := config.ServerName + // Use host from conn address when serverName is not provided + if isClient && serverName == "" && nextConn.RemoteAddr() != nil { + remoteAddr := nextConn.RemoteAddr().String() + var host string + host, _, err = net.SplitHostPort(remoteAddr) + if err != nil { + serverName = remoteAddr + } else { + serverName = host + } + } + + hsCfg := &handshakeConfig{ + localPSKCallback: config.PSK, + localPSKIdentityHint: config.PSKIdentityHint, + localCipherSuites: cipherSuites, + localSignatureSchemes: signatureSchemes, + extendedMasterSecret: config.ExtendedMasterSecret, + localSRTPProtectionProfiles: config.SRTPProtectionProfiles, + serverName: serverName, + clientAuth: config.ClientAuth, + localCertificates: config.Certificates, + insecureSkipVerify: config.InsecureSkipVerify, + verifyPeerCertificate: config.VerifyPeerCertificate, + rootCAs: config.RootCAs, + clientCAs: config.ClientCAs, + customCipherSuites: config.CustomCipherSuites, + retransmitInterval: workerInterval, + log: logger, + initialEpoch: 0, + keyLogWriter: config.KeyLogWriter, + } + + var initialFlight flightVal + var initialFSMState handshakeState + + if initialState != nil { + if c.state.isClient { + initialFlight = flight5 + } else { + initialFlight = flight6 + } + initialFSMState = handshakeFinished + + c.state = *initialState + } else { + if c.state.isClient { + initialFlight = flight1 + } else { + initialFlight = flight0 + } + initialFSMState = handshakePreparing + } + // Do handshake + if err := c.handshake(ctx, hsCfg, initialFlight, initialFSMState); err != nil { + return nil, err + } + + c.log.Trace("Handshake Completed") + + return c, nil +} + +// Dial connects to the given network address and establishes a DTLS connection on top. +// Connection handshake will timeout using ConnectContextMaker in the Config. +// If you want to specify the timeout duration, use DialWithContext() instead. +func Dial(network string, raddr *net.UDPAddr, config *Config) (*Conn, error) { + ctx, cancel := config.connectContextMaker() + defer cancel() + + return DialWithContext(ctx, network, raddr, config) +} + +// Client establishes a DTLS connection over an existing connection. +// Connection handshake will timeout using ConnectContextMaker in the Config. +// If you want to specify the timeout duration, use ClientWithContext() instead. +func Client(conn net.Conn, config *Config) (*Conn, error) { + ctx, cancel := config.connectContextMaker() + defer cancel() + + return ClientWithContext(ctx, conn, config) +} + +// Server listens for incoming DTLS connections. +// Connection handshake will timeout using ConnectContextMaker in the Config. +// If you want to specify the timeout duration, use ServerWithContext() instead. +func Server(conn net.Conn, config *Config) (*Conn, error) { + ctx, cancel := config.connectContextMaker() + defer cancel() + + return ServerWithContext(ctx, conn, config) +} + +// DialWithContext connects to the given network address and establishes a DTLS connection on top. +func DialWithContext(ctx context.Context, network string, raddr *net.UDPAddr, config *Config) (*Conn, error) { + pConn, err := net.DialUDP(network, nil, raddr) + if err != nil { + return nil, err + } + return ClientWithContext(ctx, pConn, config) +} + +// ClientWithContext establishes a DTLS connection over an existing connection. +func ClientWithContext(ctx context.Context, conn net.Conn, config *Config) (*Conn, error) { + switch { + case config == nil: + return nil, errNoConfigProvided + case config.PSK != nil && config.PSKIdentityHint == nil: + return nil, errPSKAndIdentityMustBeSetForClient + } + + return createConn(ctx, conn, config, true, nil) +} + +// ServerWithContext listens for incoming DTLS connections. +func ServerWithContext(ctx context.Context, conn net.Conn, config *Config) (*Conn, error) { + if config == nil { + return nil, errNoConfigProvided + } + + return createConn(ctx, conn, config, false, nil) +} + +// Read reads data from the connection. +func (c *Conn) Read(p []byte) (n int, err error) { + if !c.isHandshakeCompletedSuccessfully() { + return 0, errHandshakeInProgress + } + + select { + case <-c.readDeadline.Done(): + return 0, errDeadlineExceeded + default: + } + + for { + select { + case <-c.readDeadline.Done(): + return 0, errDeadlineExceeded + case out, ok := <-c.decrypted: + if !ok { + return 0, io.EOF + } + switch val := out.(type) { + case ([]byte): + if len(p) < len(val) { + return 0, errBufferTooSmall + } + copy(p, val) + return len(val), nil + case (error): + return 0, val + } + } + } +} + +// Write writes len(p) bytes from p to the DTLS connection +func (c *Conn) Write(p []byte) (int, error) { + if c.isConnectionClosed() { + return 0, ErrConnClosed + } + + select { + case <-c.writeDeadline.Done(): + return 0, errDeadlineExceeded + default: + } + + if !c.isHandshakeCompletedSuccessfully() { + return 0, errHandshakeInProgress + } + + return len(p), c.writePackets(c.writeDeadline, []*packet{ + { + record: &recordlayer.RecordLayer{ + Header: recordlayer.Header{ + Epoch: c.getLocalEpoch(), + Version: protocol.Version1_2, + }, + Content: &protocol.ApplicationData{ + Data: p, + }, + }, + shouldEncrypt: true, + }, + }) +} + +// Close closes the connection. +func (c *Conn) Close() error { + err := c.close(true) + c.handshakeLoopsFinished.Wait() + return err +} + +// ConnectionState returns basic DTLS details about the connection. +// Note that this replaced the `Export` function of v1. +func (c *Conn) ConnectionState() State { + c.lock.RLock() + defer c.lock.RUnlock() + return *c.state.clone() +} + +// SelectedSRTPProtectionProfile returns the selected SRTPProtectionProfile +func (c *Conn) SelectedSRTPProtectionProfile() (SRTPProtectionProfile, bool) { + c.lock.RLock() + defer c.lock.RUnlock() + + if c.state.srtpProtectionProfile == 0 { + return 0, false + } + + return c.state.srtpProtectionProfile, true +} + +func (c *Conn) writePackets(ctx context.Context, pkts []*packet) error { + c.lock.Lock() + defer c.lock.Unlock() + + var rawPackets [][]byte + + for _, p := range pkts { + if h, ok := p.record.Content.(*handshake.Handshake); ok { + handshakeRaw, err := p.record.Marshal() + if err != nil { + return err + } + + c.log.Tracef("[handshake:%v] -> %s (epoch: %d, seq: %d)", + srvCliStr(c.state.isClient), h.Header.Type.String(), + p.record.Header.Epoch, h.Header.MessageSequence) + c.handshakeCache.push(handshakeRaw[recordlayer.HeaderSize:], p.record.Header.Epoch, h.Header.MessageSequence, h.Header.Type, c.state.isClient) + + rawHandshakePackets, err := c.processHandshakePacket(p, h) + if err != nil { + return err + } + rawPackets = append(rawPackets, rawHandshakePackets...) + } else { + rawPacket, err := c.processPacket(p) + if err != nil { + return err + } + rawPackets = append(rawPackets, rawPacket) + } + } + if len(rawPackets) == 0 { + return nil + } + compactedRawPackets := c.compactRawPackets(rawPackets) + + for _, compactedRawPackets := range compactedRawPackets { + if _, err := c.nextConn.WriteContext(ctx, compactedRawPackets); err != nil { + return netError(err) + } + } + + return nil +} + +func (c *Conn) compactRawPackets(rawPackets [][]byte) [][]byte { + combinedRawPackets := make([][]byte, 0) + currentCombinedRawPacket := make([]byte, 0) + + for _, rawPacket := range rawPackets { + if len(currentCombinedRawPacket) > 0 && len(currentCombinedRawPacket)+len(rawPacket) >= c.maximumTransmissionUnit { + combinedRawPackets = append(combinedRawPackets, currentCombinedRawPacket) + currentCombinedRawPacket = []byte{} + } + currentCombinedRawPacket = append(currentCombinedRawPacket, rawPacket...) + } + + combinedRawPackets = append(combinedRawPackets, currentCombinedRawPacket) + + return combinedRawPackets +} + +func (c *Conn) processPacket(p *packet) ([]byte, error) { + epoch := p.record.Header.Epoch + for len(c.state.localSequenceNumber) <= int(epoch) { + c.state.localSequenceNumber = append(c.state.localSequenceNumber, uint64(0)) + } + seq := atomic.AddUint64(&c.state.localSequenceNumber[epoch], 1) - 1 + if seq > recordlayer.MaxSequenceNumber { + // RFC 6347 Section 4.1.0 + // The implementation must either abandon an association or rehandshake + // prior to allowing the sequence number to wrap. + return nil, errSequenceNumberOverflow + } + p.record.Header.SequenceNumber = seq + + rawPacket, err := p.record.Marshal() + if err != nil { + return nil, err + } + + if p.shouldEncrypt { + var err error + rawPacket, err = c.state.cipherSuite.Encrypt(p.record, rawPacket) + if err != nil { + return nil, err + } + } + + return rawPacket, nil +} + +func (c *Conn) processHandshakePacket(p *packet, h *handshake.Handshake) ([][]byte, error) { + rawPackets := make([][]byte, 0) + + handshakeFragments, err := c.fragmentHandshake(h) + if err != nil { + return nil, err + } + epoch := p.record.Header.Epoch + for len(c.state.localSequenceNumber) <= int(epoch) { + c.state.localSequenceNumber = append(c.state.localSequenceNumber, uint64(0)) + } + + for _, handshakeFragment := range handshakeFragments { + seq := atomic.AddUint64(&c.state.localSequenceNumber[epoch], 1) - 1 + if seq > recordlayer.MaxSequenceNumber { + return nil, errSequenceNumberOverflow + } + + recordlayerHeader := &recordlayer.Header{ + Version: p.record.Header.Version, + ContentType: p.record.Header.ContentType, + ContentLen: uint16(len(handshakeFragment)), + Epoch: p.record.Header.Epoch, + SequenceNumber: seq, + } + + recordlayerHeaderBytes, err := recordlayerHeader.Marshal() + if err != nil { + return nil, err + } + + p.record.Header = *recordlayerHeader + + rawPacket := append(recordlayerHeaderBytes, handshakeFragment...) + if p.shouldEncrypt { + var err error + rawPacket, err = c.state.cipherSuite.Encrypt(p.record, rawPacket) + if err != nil { + return nil, err + } + } + + rawPackets = append(rawPackets, rawPacket) + } + + return rawPackets, nil +} + +func (c *Conn) fragmentHandshake(h *handshake.Handshake) ([][]byte, error) { + content, err := h.Message.Marshal() + if err != nil { + return nil, err + } + + fragmentedHandshakes := make([][]byte, 0) + + contentFragments := splitBytes(content, c.maximumTransmissionUnit) + if len(contentFragments) == 0 { + contentFragments = [][]byte{ + {}, + } + } + + offset := 0 + for _, contentFragment := range contentFragments { + contentFragmentLen := len(contentFragment) + + headerFragment := &handshake.Header{ + Type: h.Header.Type, + Length: h.Header.Length, + MessageSequence: h.Header.MessageSequence, + FragmentOffset: uint32(offset), + FragmentLength: uint32(contentFragmentLen), + } + + offset += contentFragmentLen + + headerFragmentRaw, err := headerFragment.Marshal() + if err != nil { + return nil, err + } + + fragmentedHandshake := append(headerFragmentRaw, contentFragment...) + fragmentedHandshakes = append(fragmentedHandshakes, fragmentedHandshake) + } + + return fragmentedHandshakes, nil +} + +var poolReadBuffer = sync.Pool{ //nolint:gochecknoglobals + New: func() interface{} { + b := make([]byte, inboundBufferSize) + return &b + }, +} + +func (c *Conn) readAndBuffer(ctx context.Context) error { + bufptr := poolReadBuffer.Get().(*[]byte) + defer poolReadBuffer.Put(bufptr) + + b := *bufptr + i, err := c.nextConn.ReadContext(ctx, b) + if err != nil { + return netError(err) + } + + pkts, err := recordlayer.UnpackDatagram(b[:i]) + if err != nil { + return err + } + + var hasHandshake bool + for _, p := range pkts { + hs, alert, err := c.handleIncomingPacket(p, true) + if alert != nil { + if alertErr := c.notify(ctx, alert.Level, alert.Description); alertErr != nil { + if err == nil { + err = alertErr + } + } + } + if hs { + hasHandshake = true + } + switch e := err.(type) { + case nil: + case *errAlert: + if e.IsFatalOrCloseNotify() { + return e + } + default: + return e + } + } + if hasHandshake { + done := make(chan struct{}) + select { + case c.handshakeRecv <- done: + // If the other party may retransmit the flight, + // we should respond even if it not a new message. + <-done + case <-c.fsm.Done(): + } + } + return nil +} + +func (c *Conn) handleQueuedPackets(ctx context.Context) error { + pkts := c.encryptedPackets + c.encryptedPackets = nil + + for _, p := range pkts { + _, alert, err := c.handleIncomingPacket(p, false) // don't re-enqueue + if alert != nil { + if alertErr := c.notify(ctx, alert.Level, alert.Description); alertErr != nil { + if err == nil { + err = alertErr + } + } + } + switch e := err.(type) { + case nil: + case *errAlert: + if e.IsFatalOrCloseNotify() { + return e + } + default: + return e + } + } + return nil +} + +func (c *Conn) handleIncomingPacket(buf []byte, enqueue bool) (bool, *alert.Alert, error) { //nolint:gocognit + h := &recordlayer.Header{} + if err := h.Unmarshal(buf); err != nil { + // Decode error must be silently discarded + // [RFC6347 Section-4.1.2.7] + c.log.Debugf("discarded broken packet: %v", err) + return false, nil, nil + } + + // Validate epoch + remoteEpoch := c.getRemoteEpoch() + if h.Epoch > remoteEpoch { + if h.Epoch > remoteEpoch+1 { + c.log.Debugf("discarded future packet (epoch: %d, seq: %d)", + h.Epoch, h.SequenceNumber, + ) + return false, nil, nil + } + if enqueue { + c.log.Debug("received packet of next epoch, queuing packet") + c.encryptedPackets = append(c.encryptedPackets, buf) + } + return false, nil, nil + } + + // Anti-replay protection + for len(c.state.replayDetector) <= int(h.Epoch) { + c.state.replayDetector = append(c.state.replayDetector, + replaydetector.New(c.replayProtectionWindow, recordlayer.MaxSequenceNumber), + ) + } + markPacketAsValid, ok := c.state.replayDetector[int(h.Epoch)].Check(h.SequenceNumber) + if !ok { + c.log.Debugf("discarded duplicated packet (epoch: %d, seq: %d)", + h.Epoch, h.SequenceNumber, + ) + return false, nil, nil + } + + // Decrypt + if h.Epoch != 0 { + if c.state.cipherSuite == nil || !c.state.cipherSuite.IsInitialized() { + if enqueue { + c.encryptedPackets = append(c.encryptedPackets, buf) + c.log.Debug("handshake not finished, queuing packet") + } + return false, nil, nil + } + + var err error + buf, err = c.state.cipherSuite.Decrypt(buf) + if err != nil { + c.log.Debugf("%s: decrypt failed: %s", srvCliStr(c.state.isClient), err) + return false, nil, nil + } + } + + isHandshake, err := c.fragmentBuffer.push(append([]byte{}, buf...)) + if err != nil { + // Decode error must be silently discarded + // [RFC6347 Section-4.1.2.7] + c.log.Debugf("defragment failed: %s", err) + return false, nil, nil + } else if isHandshake { + markPacketAsValid() + for out, epoch := c.fragmentBuffer.pop(); out != nil; out, epoch = c.fragmentBuffer.pop() { + rawHandshake := &handshake.Handshake{} + if err := rawHandshake.Unmarshal(out); err != nil { + c.log.Debugf("%s: handshake parse failed: %s", srvCliStr(c.state.isClient), err) + continue + } + + _ = c.handshakeCache.push(out, epoch, rawHandshake.Header.MessageSequence, rawHandshake.Header.Type, !c.state.isClient) + } + + return true, nil, nil + } + + r := &recordlayer.RecordLayer{} + if err := r.Unmarshal(buf); err != nil { + return false, &alert.Alert{Level: alert.Fatal, Description: alert.DecodeError}, err + } + + switch content := r.Content.(type) { + case *alert.Alert: + c.log.Tracef("%s: <- %s", srvCliStr(c.state.isClient), content.String()) + var a *alert.Alert + if content.Description == alert.CloseNotify { + // Respond with a close_notify [RFC5246 Section 7.2.1] + a = &alert.Alert{Level: alert.Warning, Description: alert.CloseNotify} + } + markPacketAsValid() + return false, a, &errAlert{content} + case *protocol.ChangeCipherSpec: + if c.state.cipherSuite == nil || !c.state.cipherSuite.IsInitialized() { + if enqueue { + c.encryptedPackets = append(c.encryptedPackets, buf) + c.log.Debugf("CipherSuite not initialized, queuing packet") + } + return false, nil, nil + } + + newRemoteEpoch := h.Epoch + 1 + c.log.Tracef("%s: <- ChangeCipherSpec (epoch: %d)", srvCliStr(c.state.isClient), newRemoteEpoch) + + if c.getRemoteEpoch()+1 == newRemoteEpoch { + c.setRemoteEpoch(newRemoteEpoch) + markPacketAsValid() + } + case *protocol.ApplicationData: + if h.Epoch == 0 { + return false, &alert.Alert{Level: alert.Fatal, Description: alert.UnexpectedMessage}, errApplicationDataEpochZero + } + + markPacketAsValid() + + select { + case c.decrypted <- content.Data: + case <-c.closed.Done(): + } + + default: + return false, &alert.Alert{Level: alert.Fatal, Description: alert.UnexpectedMessage}, fmt.Errorf("%w: %d", errUnhandledContextType, content.ContentType()) + } + return false, nil, nil +} + +func (c *Conn) recvHandshake() <-chan chan struct{} { + return c.handshakeRecv +} + +func (c *Conn) notify(ctx context.Context, level alert.Level, desc alert.Description) error { + return c.writePackets(ctx, []*packet{ + { + record: &recordlayer.RecordLayer{ + Header: recordlayer.Header{ + Epoch: c.getLocalEpoch(), + Version: protocol.Version1_2, + }, + Content: &alert.Alert{ + Level: level, + Description: desc, + }, + }, + shouldEncrypt: c.isHandshakeCompletedSuccessfully(), + }, + }) +} + +func (c *Conn) setHandshakeCompletedSuccessfully() { + c.handshakeCompletedSuccessfully.Store(struct{ bool }{true}) +} + +func (c *Conn) isHandshakeCompletedSuccessfully() bool { + boolean, _ := c.handshakeCompletedSuccessfully.Load().(struct{ bool }) + return boolean.bool +} + +func (c *Conn) handshake(ctx context.Context, cfg *handshakeConfig, initialFlight flightVal, initialState handshakeState) error { //nolint:gocognit + c.fsm = newHandshakeFSM(&c.state, c.handshakeCache, cfg, initialFlight) + + done := make(chan struct{}) + ctxRead, cancelRead := context.WithCancel(context.Background()) + c.cancelHandshakeReader = cancelRead + cfg.onFlightState = func(f flightVal, s handshakeState) { + if s == handshakeFinished && !c.isHandshakeCompletedSuccessfully() { + c.setHandshakeCompletedSuccessfully() + close(done) + } + } + + ctxHs, cancel := context.WithCancel(context.Background()) + c.cancelHandshaker = cancel + + firstErr := make(chan error, 1) + + c.handshakeLoopsFinished.Add(2) + + // Handshake routine should be live until close. + // The other party may request retransmission of the last flight to cope with packet drop. + go func() { + defer c.handshakeLoopsFinished.Done() + err := c.fsm.Run(ctxHs, c, initialState) + if !errors.Is(err, context.Canceled) { + select { + case firstErr <- err: + default: + } + } + }() + go func() { + defer func() { + // Escaping read loop. + // It's safe to close decrypted channnel now. + close(c.decrypted) + + // Force stop handshaker when the underlying connection is closed. + cancel() + }() + defer c.handshakeLoopsFinished.Done() + for { + if err := c.readAndBuffer(ctxRead); err != nil { + switch e := err.(type) { + case *errAlert: + if !e.IsFatalOrCloseNotify() { + if c.isHandshakeCompletedSuccessfully() { + // Pass the error to Read() + select { + case c.decrypted <- err: + case <-c.closed.Done(): + } + } + continue // non-fatal alert must not stop read loop + } + case error: + switch err { + case context.DeadlineExceeded, context.Canceled, io.EOF: + default: + if c.isHandshakeCompletedSuccessfully() { + // Keep read loop and pass the read error to Read() + select { + case c.decrypted <- err: + case <-c.closed.Done(): + } + continue // non-fatal alert must not stop read loop + } + } + } + select { + case firstErr <- err: + default: + } + + if e, ok := err.(*errAlert); ok { + if e.IsFatalOrCloseNotify() { + _ = c.close(false) + } + } + return + } + } + }() + + select { + case err := <-firstErr: + cancelRead() + cancel() + return c.translateHandshakeCtxError(err) + case <-ctx.Done(): + cancelRead() + cancel() + return c.translateHandshakeCtxError(ctx.Err()) + case <-done: + return nil + } +} + +func (c *Conn) translateHandshakeCtxError(err error) error { + if err == nil { + return nil + } + if errors.Is(err, context.Canceled) && c.isHandshakeCompletedSuccessfully() { + return nil + } + return &HandshakeError{Err: err} +} + +func (c *Conn) close(byUser bool) error { + c.cancelHandshaker() + c.cancelHandshakeReader() + + if c.isHandshakeCompletedSuccessfully() && byUser { + // Discard error from notify() to return non-error on the first user call of Close() + // even if the underlying connection is already closed. + _ = c.notify(context.Background(), alert.Warning, alert.CloseNotify) + } + + c.closeLock.Lock() + // Don't return ErrConnClosed at the first time of the call from user. + closedByUser := c.connectionClosedByUser + if byUser { + c.connectionClosedByUser = true + } + c.closed.Close() + c.closeLock.Unlock() + + if closedByUser { + return ErrConnClosed + } + + return c.nextConn.Close() +} + +func (c *Conn) isConnectionClosed() bool { + select { + case <-c.closed.Done(): + return true + default: + return false + } +} + +func (c *Conn) setLocalEpoch(epoch uint16) { + c.state.localEpoch.Store(epoch) +} + +func (c *Conn) getLocalEpoch() uint16 { + return c.state.localEpoch.Load().(uint16) +} + +func (c *Conn) setRemoteEpoch(epoch uint16) { + c.state.remoteEpoch.Store(epoch) +} + +func (c *Conn) getRemoteEpoch() uint16 { + return c.state.remoteEpoch.Load().(uint16) +} + +// LocalAddr implements net.Conn.LocalAddr +func (c *Conn) LocalAddr() net.Addr { + return c.nextConn.LocalAddr() +} + +// RemoteAddr implements net.Conn.RemoteAddr +func (c *Conn) RemoteAddr() net.Addr { + return c.nextConn.RemoteAddr() +} + +// SetDeadline implements net.Conn.SetDeadline +func (c *Conn) SetDeadline(t time.Time) error { + c.readDeadline.Set(t) + return c.SetWriteDeadline(t) +} + +// SetReadDeadline implements net.Conn.SetReadDeadline +func (c *Conn) SetReadDeadline(t time.Time) error { + c.readDeadline.Set(t) + // Read deadline is fully managed by this layer. + // Don't set read deadline to underlying connection. + return nil +} + +// SetWriteDeadline implements net.Conn.SetWriteDeadline +func (c *Conn) SetWriteDeadline(t time.Time) error { + c.writeDeadline.Set(t) + // Write deadline is also fully managed by this layer. + return nil +} diff --git a/vendor/github.com/pion/dtls/v2/crypto.go b/vendor/github.com/pion/dtls/v2/crypto.go new file mode 100644 index 0000000..768ee47 --- /dev/null +++ b/vendor/github.com/pion/dtls/v2/crypto.go @@ -0,0 +1,221 @@ +package dtls + +import ( + "crypto" + "crypto/ecdsa" + "crypto/ed25519" + "crypto/rand" + "crypto/rsa" + "crypto/sha256" + "crypto/x509" + "encoding/asn1" + "encoding/binary" + "math/big" + "time" + + "github.com/pion/dtls/v2/pkg/crypto/elliptic" + "github.com/pion/dtls/v2/pkg/crypto/hash" +) + +type ecdsaSignature struct { + R, S *big.Int +} + +func valueKeyMessage(clientRandom, serverRandom, publicKey []byte, namedCurve elliptic.Curve) []byte { + serverECDHParams := make([]byte, 4) + serverECDHParams[0] = 3 // named curve + binary.BigEndian.PutUint16(serverECDHParams[1:], uint16(namedCurve)) + serverECDHParams[3] = byte(len(publicKey)) + + plaintext := []byte{} + plaintext = append(plaintext, clientRandom...) + plaintext = append(plaintext, serverRandom...) + plaintext = append(plaintext, serverECDHParams...) + plaintext = append(plaintext, publicKey...) + + return plaintext +} + +// If the client provided a "signature_algorithms" extension, then all +// certificates provided by the server MUST be signed by a +// hash/signature algorithm pair that appears in that extension +// +// https://tools.ietf.org/html/rfc5246#section-7.4.2 +func generateKeySignature(clientRandom, serverRandom, publicKey []byte, namedCurve elliptic.Curve, privateKey crypto.PrivateKey, hashAlgorithm hash.Algorithm) ([]byte, error) { + msg := valueKeyMessage(clientRandom, serverRandom, publicKey, namedCurve) + switch p := privateKey.(type) { + case ed25519.PrivateKey: + // https://crypto.stackexchange.com/a/55483 + return p.Sign(rand.Reader, msg, crypto.Hash(0)) + case *ecdsa.PrivateKey: + hashed := hashAlgorithm.Digest(msg) + return p.Sign(rand.Reader, hashed, hashAlgorithm.CryptoHash()) + case *rsa.PrivateKey: + hashed := hashAlgorithm.Digest(msg) + return p.Sign(rand.Reader, hashed, hashAlgorithm.CryptoHash()) + } + + return nil, errKeySignatureGenerateUnimplemented +} + +func verifyKeySignature(message, remoteKeySignature []byte, hashAlgorithm hash.Algorithm, rawCertificates [][]byte) error { //nolint:dupl + if len(rawCertificates) == 0 { + return errLengthMismatch + } + certificate, err := x509.ParseCertificate(rawCertificates[0]) + if err != nil { + return err + } + + switch p := certificate.PublicKey.(type) { + case ed25519.PublicKey: + if ok := ed25519.Verify(p, message, remoteKeySignature); !ok { + return errKeySignatureMismatch + } + return nil + case *ecdsa.PublicKey: + ecdsaSig := &ecdsaSignature{} + if _, err := asn1.Unmarshal(remoteKeySignature, ecdsaSig); err != nil { + return err + } + if ecdsaSig.R.Sign() <= 0 || ecdsaSig.S.Sign() <= 0 { + return errInvalidECDSASignature + } + hashed := hashAlgorithm.Digest(message) + if !ecdsa.Verify(p, hashed, ecdsaSig.R, ecdsaSig.S) { + return errKeySignatureMismatch + } + return nil + case *rsa.PublicKey: + switch certificate.SignatureAlgorithm { + case x509.SHA1WithRSA, x509.SHA256WithRSA, x509.SHA384WithRSA, x509.SHA512WithRSA: + hashed := hashAlgorithm.Digest(message) + return rsa.VerifyPKCS1v15(p, hashAlgorithm.CryptoHash(), hashed, remoteKeySignature) + default: + return errKeySignatureVerifyUnimplemented + } + } + + return errKeySignatureVerifyUnimplemented +} + +// If the server has sent a CertificateRequest message, the client MUST send the Certificate +// message. The ClientKeyExchange message is now sent, and the content +// of that message will depend on the public key algorithm selected +// between the ClientHello and the ServerHello. If the client has sent +// a certificate with signing ability, a digitally-signed +// CertificateVerify message is sent to explicitly verify possession of +// the private key in the certificate. +// https://tools.ietf.org/html/rfc5246#section-7.3 +func generateCertificateVerify(handshakeBodies []byte, privateKey crypto.PrivateKey, hashAlgorithm hash.Algorithm) ([]byte, error) { + h := sha256.New() + if _, err := h.Write(handshakeBodies); err != nil { + return nil, err + } + hashed := h.Sum(nil) + + switch p := privateKey.(type) { + case ed25519.PrivateKey: + // https://crypto.stackexchange.com/a/55483 + return p.Sign(rand.Reader, hashed, crypto.Hash(0)) + case *ecdsa.PrivateKey: + return p.Sign(rand.Reader, hashed, hashAlgorithm.CryptoHash()) + case *rsa.PrivateKey: + return p.Sign(rand.Reader, hashed, hashAlgorithm.CryptoHash()) + } + + return nil, errInvalidSignatureAlgorithm +} + +func verifyCertificateVerify(handshakeBodies []byte, hashAlgorithm hash.Algorithm, remoteKeySignature []byte, rawCertificates [][]byte) error { //nolint:dupl + if len(rawCertificates) == 0 { + return errLengthMismatch + } + certificate, err := x509.ParseCertificate(rawCertificates[0]) + if err != nil { + return err + } + + switch p := certificate.PublicKey.(type) { + case ed25519.PublicKey: + if ok := ed25519.Verify(p, handshakeBodies, remoteKeySignature); !ok { + return errKeySignatureMismatch + } + return nil + case *ecdsa.PublicKey: + ecdsaSig := &ecdsaSignature{} + if _, err := asn1.Unmarshal(remoteKeySignature, ecdsaSig); err != nil { + return err + } + if ecdsaSig.R.Sign() <= 0 || ecdsaSig.S.Sign() <= 0 { + return errInvalidECDSASignature + } + hash := hashAlgorithm.Digest(handshakeBodies) + if !ecdsa.Verify(p, hash, ecdsaSig.R, ecdsaSig.S) { + return errKeySignatureMismatch + } + return nil + case *rsa.PublicKey: + switch certificate.SignatureAlgorithm { + case x509.SHA1WithRSA, x509.SHA256WithRSA, x509.SHA384WithRSA, x509.SHA512WithRSA: + hash := hashAlgorithm.Digest(handshakeBodies) + return rsa.VerifyPKCS1v15(p, hashAlgorithm.CryptoHash(), hash, remoteKeySignature) + default: + return errKeySignatureVerifyUnimplemented + } + } + + return errKeySignatureVerifyUnimplemented +} + +func loadCerts(rawCertificates [][]byte) ([]*x509.Certificate, error) { + if len(rawCertificates) == 0 { + return nil, errLengthMismatch + } + + certs := make([]*x509.Certificate, 0, len(rawCertificates)) + for _, rawCert := range rawCertificates { + cert, err := x509.ParseCertificate(rawCert) + if err != nil { + return nil, err + } + certs = append(certs, cert) + } + return certs, nil +} + +func verifyClientCert(rawCertificates [][]byte, roots *x509.CertPool) (chains [][]*x509.Certificate, err error) { + certificate, err := loadCerts(rawCertificates) + if err != nil { + return nil, err + } + intermediateCAPool := x509.NewCertPool() + for _, cert := range certificate[1:] { + intermediateCAPool.AddCert(cert) + } + opts := x509.VerifyOptions{ + Roots: roots, + CurrentTime: time.Now(), + Intermediates: intermediateCAPool, + KeyUsages: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth}, + } + return certificate[0].Verify(opts) +} + +func verifyServerCert(rawCertificates [][]byte, roots *x509.CertPool, serverName string) (chains [][]*x509.Certificate, err error) { + certificate, err := loadCerts(rawCertificates) + if err != nil { + return nil, err + } + intermediateCAPool := x509.NewCertPool() + for _, cert := range certificate[1:] { + intermediateCAPool.AddCert(cert) + } + opts := x509.VerifyOptions{ + Roots: roots, + CurrentTime: time.Now(), + DNSName: serverName, + Intermediates: intermediateCAPool, + } + return certificate[0].Verify(opts) +} diff --git a/vendor/github.com/pion/dtls/v2/dtls.go b/vendor/github.com/pion/dtls/v2/dtls.go new file mode 100644 index 0000000..125b904 --- /dev/null +++ b/vendor/github.com/pion/dtls/v2/dtls.go @@ -0,0 +1,2 @@ +// Package dtls implements Datagram Transport Layer Security (DTLS) 1.2 +package dtls diff --git a/vendor/github.com/pion/dtls/v2/errors.go b/vendor/github.com/pion/dtls/v2/errors.go new file mode 100644 index 0000000..2e16388 --- /dev/null +++ b/vendor/github.com/pion/dtls/v2/errors.go @@ -0,0 +1,141 @@ +package dtls + +import ( + "context" + "errors" + "fmt" + "io" + "net" + "os" + + "github.com/pion/dtls/v2/pkg/protocol" + "github.com/pion/dtls/v2/pkg/protocol/alert" + "golang.org/x/xerrors" +) + +// Typed errors +var ( + ErrConnClosed = &FatalError{Err: errors.New("conn is closed")} //nolint:goerr113 + + errDeadlineExceeded = &TimeoutError{Err: xerrors.Errorf("read/write timeout: %w", context.DeadlineExceeded)} + errInvalidContentType = &TemporaryError{Err: errors.New("invalid content type")} //nolint:goerr113 + + errBufferTooSmall = &TemporaryError{Err: errors.New("buffer is too small")} //nolint:goerr113 + errContextUnsupported = &TemporaryError{Err: errors.New("context is not supported for ExportKeyingMaterial")} //nolint:goerr113 + errHandshakeInProgress = &TemporaryError{Err: errors.New("handshake is in progress")} //nolint:goerr113 + errReservedExportKeyingMaterial = &TemporaryError{Err: errors.New("ExportKeyingMaterial can not be used with a reserved label")} //nolint:goerr113 + errApplicationDataEpochZero = &TemporaryError{Err: errors.New("ApplicationData with epoch of 0")} //nolint:goerr113 + errUnhandledContextType = &TemporaryError{Err: errors.New("unhandled contentType")} //nolint:goerr113 + + errCertificateVerifyNoCertificate = &FatalError{Err: errors.New("client sent certificate verify but we have no certificate to verify")} //nolint:goerr113 + errCipherSuiteNoIntersection = &FatalError{Err: errors.New("client+server do not support any shared cipher suites")} //nolint:goerr113 + errClientCertificateNotVerified = &FatalError{Err: errors.New("client sent certificate but did not verify it")} //nolint:goerr113 + errClientCertificateRequired = &FatalError{Err: errors.New("server required client verification, but got none")} //nolint:goerr113 + errClientNoMatchingSRTPProfile = &FatalError{Err: errors.New("server responded with SRTP Profile we do not support")} //nolint:goerr113 + errClientRequiredButNoServerEMS = &FatalError{Err: errors.New("client required Extended Master Secret extension, but server does not support it")} //nolint:goerr113 + errCookieMismatch = &FatalError{Err: errors.New("client+server cookie does not match")} //nolint:goerr113 + errIdentityNoPSK = &FatalError{Err: errors.New("PSK Identity Hint provided but PSK is nil")} //nolint:goerr113 + errInvalidCertificate = &FatalError{Err: errors.New("no certificate provided")} //nolint:goerr113 + errInvalidCipherSuite = &FatalError{Err: errors.New("invalid or unknown cipher suite")} //nolint:goerr113 + errInvalidECDSASignature = &FatalError{Err: errors.New("ECDSA signature contained zero or negative values")} //nolint:goerr113 + errInvalidPrivateKey = &FatalError{Err: errors.New("invalid private key type")} //nolint:goerr113 + errInvalidSignatureAlgorithm = &FatalError{Err: errors.New("invalid signature algorithm")} //nolint:goerr113 + errKeySignatureMismatch = &FatalError{Err: errors.New("expected and actual key signature do not match")} //nolint:goerr113 + errNilNextConn = &FatalError{Err: errors.New("Conn can not be created with a nil nextConn")} //nolint:goerr113 + errNoAvailableCipherSuites = &FatalError{Err: errors.New("connection can not be created, no CipherSuites satisfy this Config")} //nolint:goerr113 + errNoAvailablePSKCipherSuite = &FatalError{Err: errors.New("connection can not be created, pre-shared key present but no compatible CipherSuite")} //nolint:goerr113 + errNoAvailableCertificateCipherSuite = &FatalError{Err: errors.New("connection can not be created, certificate present but no compatible CipherSuite")} //nolint:goerr113 + errNoAvailableSignatureSchemes = &FatalError{Err: errors.New("connection can not be created, no SignatureScheme satisfy this Config")} //nolint:goerr113 + errNoCertificates = &FatalError{Err: errors.New("no certificates configured")} //nolint:goerr113 + errNoConfigProvided = &FatalError{Err: errors.New("no config provided")} //nolint:goerr113 + errNoSupportedEllipticCurves = &FatalError{Err: errors.New("client requested zero or more elliptic curves that are not supported by the server")} //nolint:goerr113 + errUnsupportedProtocolVersion = &FatalError{Err: errors.New("unsupported protocol version")} //nolint:goerr113 + errPSKAndIdentityMustBeSetForClient = &FatalError{Err: errors.New("PSK and PSK Identity Hint must both be set for client")} //nolint:goerr113 + errRequestedButNoSRTPExtension = &FatalError{Err: errors.New("SRTP support was requested but server did not respond with use_srtp extension")} //nolint:goerr113 + errServerNoMatchingSRTPProfile = &FatalError{Err: errors.New("client requested SRTP but we have no matching profiles")} //nolint:goerr113 + errServerRequiredButNoClientEMS = &FatalError{Err: errors.New("server requires the Extended Master Secret extension, but the client does not support it")} //nolint:goerr113 + errVerifyDataMismatch = &FatalError{Err: errors.New("expected and actual verify data does not match")} //nolint:goerr113 + + errInvalidFlight = &InternalError{Err: errors.New("invalid flight number")} //nolint:goerr113 + errKeySignatureGenerateUnimplemented = &InternalError{Err: errors.New("unable to generate key signature, unimplemented")} //nolint:goerr113 + errKeySignatureVerifyUnimplemented = &InternalError{Err: errors.New("unable to verify key signature, unimplemented")} //nolint:goerr113 + errLengthMismatch = &InternalError{Err: errors.New("data length and declared length do not match")} //nolint:goerr113 + errSequenceNumberOverflow = &InternalError{Err: errors.New("sequence number overflow")} //nolint:goerr113 + errInvalidFSMTransition = &InternalError{Err: errors.New("invalid state machine transition")} //nolint:goerr113 +) + +// FatalError indicates that the DTLS connection is no longer available. +// It is mainly caused by wrong configuration of server or client. +type FatalError = protocol.FatalError + +// InternalError indicates and internal error caused by the implementation, and the DTLS connection is no longer available. +// It is mainly caused by bugs or tried to use unimplemented features. +type InternalError = protocol.InternalError + +// TemporaryError indicates that the DTLS connection is still available, but the request was failed temporary. +type TemporaryError = protocol.TemporaryError + +// TimeoutError indicates that the request was timed out. +type TimeoutError = protocol.TimeoutError + +// HandshakeError indicates that the handshake failed. +type HandshakeError = protocol.HandshakeError + +// invalidCipherSuite indicates an attempt at using an unsupported cipher suite. +type invalidCipherSuite struct { + id CipherSuiteID +} + +func (e *invalidCipherSuite) Error() string { + return fmt.Sprintf("CipherSuite with id(%d) is not valid", e.id) +} + +func (e *invalidCipherSuite) Is(err error) bool { + if other, ok := err.(*invalidCipherSuite); ok { + return e.id == other.id + } + return false +} + +// errAlert wraps DTLS alert notification as an error +type errAlert struct { + *alert.Alert +} + +func (e *errAlert) Error() string { + return fmt.Sprintf("alert: %s", e.Alert.String()) +} + +func (e *errAlert) IsFatalOrCloseNotify() bool { + return e.Level == alert.Fatal || e.Description == alert.CloseNotify +} + +func (e *errAlert) Is(err error) bool { + if other, ok := err.(*errAlert); ok { + return e.Level == other.Level && e.Description == other.Description + } + return false +} + +// netError translates an error from underlying Conn to corresponding net.Error. +func netError(err error) error { + switch err { + case io.EOF, context.Canceled, context.DeadlineExceeded: + // Return io.EOF and context errors as is. + return err + } + switch e := err.(type) { + case (*net.OpError): + if se, ok := e.Err.(*os.SyscallError); ok { + if se.Timeout() { + return &TimeoutError{Err: err} + } + if isOpErrorTemporary(se) { + return &TemporaryError{Err: err} + } + } + case (net.Error): + return err + } + return &FatalError{Err: err} +} diff --git a/vendor/github.com/pion/dtls/v2/errors_errno.go b/vendor/github.com/pion/dtls/v2/errors_errno.go new file mode 100644 index 0000000..a9a439b --- /dev/null +++ b/vendor/github.com/pion/dtls/v2/errors_errno.go @@ -0,0 +1,25 @@ +// +build aix darwin dragonfly freebsd linux nacl nacljs netbsd openbsd solaris windows + +// For systems having syscall.Errno. +// Update build targets by following command: +// $ grep -R ECONN $(go env GOROOT)/src/syscall/zerrors_*.go \ +// | tr "." "_" | cut -d"_" -f"2" | sort | uniq + +package dtls + +import ( + "os" + "syscall" +) + +func isOpErrorTemporary(err *os.SyscallError) bool { + if ne, ok := err.Err.(syscall.Errno); ok { + switch ne { + case syscall.ECONNREFUSED: + return true + default: + return false + } + } + return false +} diff --git a/vendor/github.com/pion/dtls/v2/errors_noerrno.go b/vendor/github.com/pion/dtls/v2/errors_noerrno.go new file mode 100644 index 0000000..fcc37ce --- /dev/null +++ b/vendor/github.com/pion/dtls/v2/errors_noerrno.go @@ -0,0 +1,14 @@ +// +build !aix,!darwin,!dragonfly,!freebsd,!linux,!nacl,!nacljs,!netbsd,!openbsd,!solaris,!windows + +// For systems without syscall.Errno. +// Build targets must be inverse of errors_errno.go + +package dtls + +import ( + "os" +) + +func isOpErrorTemporary(err *os.SyscallError) bool { + return false +} diff --git a/vendor/github.com/pion/dtls/v2/flight.go b/vendor/github.com/pion/dtls/v2/flight.go new file mode 100644 index 0000000..580ee48 --- /dev/null +++ b/vendor/github.com/pion/dtls/v2/flight.go @@ -0,0 +1,75 @@ +package dtls + +/* + DTLS messages are grouped into a series of message flights, according + to the diagrams below. Although each flight of messages may consist + of a number of messages, they should be viewed as monolithic for the + purpose of timeout and retransmission. + https://tools.ietf.org/html/rfc4347#section-4.2.4 + Client Server + ------ ------ + Waiting Flight 0 + + ClientHello --------> Flight 1 + + <------- HelloVerifyRequest Flight 2 + + ClientHello --------> Flight 3 + + ServerHello \ + Certificate* \ + ServerKeyExchange* Flight 4 + CertificateRequest* / + <-------- ServerHelloDone / + + Certificate* \ + ClientKeyExchange \ + CertificateVerify* Flight 5 + [ChangeCipherSpec] / + Finished --------> / + + [ChangeCipherSpec] \ Flight 6 + <-------- Finished / + +*/ + +type flightVal uint8 + +const ( + flight0 flightVal = iota + 1 + flight1 + flight2 + flight3 + flight4 + flight5 + flight6 +) + +func (f flightVal) String() string { + switch f { + case flight0: + return "Flight 0" + case flight1: + return "Flight 1" + case flight2: + return "Flight 2" + case flight3: + return "Flight 3" + case flight4: + return "Flight 4" + case flight5: + return "Flight 5" + case flight6: + return "Flight 6" + default: + return "Invalid Flight" + } +} + +func (f flightVal) isLastSendFlight() bool { + return f == flight6 +} + +func (f flightVal) isLastRecvFlight() bool { + return f == flight5 +} diff --git a/vendor/github.com/pion/dtls/v2/flight0handler.go b/vendor/github.com/pion/dtls/v2/flight0handler.go new file mode 100644 index 0000000..949d7c0 --- /dev/null +++ b/vendor/github.com/pion/dtls/v2/flight0handler.go @@ -0,0 +1,102 @@ +package dtls + +import ( + "context" + "crypto/rand" + + "github.com/pion/dtls/v2/pkg/crypto/elliptic" + "github.com/pion/dtls/v2/pkg/protocol" + "github.com/pion/dtls/v2/pkg/protocol/alert" + "github.com/pion/dtls/v2/pkg/protocol/extension" + "github.com/pion/dtls/v2/pkg/protocol/handshake" +) + +func flight0Parse(ctx context.Context, c flightConn, state *State, cache *handshakeCache, cfg *handshakeConfig) (flightVal, *alert.Alert, error) { + seq, msgs, ok := cache.fullPullMap(0, + handshakeCachePullRule{handshake.TypeClientHello, cfg.initialEpoch, true, false}, + ) + if !ok { + // No valid message received. Keep reading + return 0, nil, nil + } + state.handshakeRecvSequence = seq + + var clientHello *handshake.MessageClientHello + + // Validate type + if clientHello, ok = msgs[handshake.TypeClientHello].(*handshake.MessageClientHello); !ok { + return 0, &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}, nil + } + + if !clientHello.Version.Equal(protocol.Version1_2) { + return 0, &alert.Alert{Level: alert.Fatal, Description: alert.ProtocolVersion}, errUnsupportedProtocolVersion + } + + state.remoteRandom = clientHello.Random + + cipherSuites := []CipherSuite{} + for _, id := range clientHello.CipherSuiteIDs { + if c := cipherSuiteForID(CipherSuiteID(id), cfg.customCipherSuites); c != nil { + cipherSuites = append(cipherSuites, c) + } + } + + if state.cipherSuite, ok = findMatchingCipherSuite(cipherSuites, cfg.localCipherSuites); !ok { + return 0, &alert.Alert{Level: alert.Fatal, Description: alert.InsufficientSecurity}, errCipherSuiteNoIntersection + } + + for _, val := range clientHello.Extensions { + switch e := val.(type) { + case *extension.SupportedEllipticCurves: + if len(e.EllipticCurves) == 0 { + return 0, &alert.Alert{Level: alert.Fatal, Description: alert.InsufficientSecurity}, errNoSupportedEllipticCurves + } + state.namedCurve = e.EllipticCurves[0] + case *extension.UseSRTP: + profile, ok := findMatchingSRTPProfile(e.ProtectionProfiles, cfg.localSRTPProtectionProfiles) + if !ok { + return 0, &alert.Alert{Level: alert.Fatal, Description: alert.InsufficientSecurity}, errServerNoMatchingSRTPProfile + } + state.srtpProtectionProfile = profile + case *extension.UseExtendedMasterSecret: + if cfg.extendedMasterSecret != DisableExtendedMasterSecret { + state.extendedMasterSecret = true + } + case *extension.ServerName: + state.serverName = e.ServerName // remote server name + } + } + + if cfg.extendedMasterSecret == RequireExtendedMasterSecret && !state.extendedMasterSecret { + return 0, &alert.Alert{Level: alert.Fatal, Description: alert.InsufficientSecurity}, errServerRequiredButNoClientEMS + } + + if state.localKeypair == nil { + var err error + state.localKeypair, err = elliptic.GenerateKeypair(state.namedCurve) + if err != nil { + return 0, &alert.Alert{Level: alert.Fatal, Description: alert.IllegalParameter}, err + } + } + + return flight2, nil, nil +} + +func flight0Generate(c flightConn, state *State, cache *handshakeCache, cfg *handshakeConfig) ([]*packet, *alert.Alert, error) { + // Initialize + state.cookie = make([]byte, cookieLength) + if _, err := rand.Read(state.cookie); err != nil { + return nil, nil, err + } + + var zeroEpoch uint16 + state.localEpoch.Store(zeroEpoch) + state.remoteEpoch.Store(zeroEpoch) + state.namedCurve = defaultNamedCurve + + if err := state.localRandom.Populate(); err != nil { + return nil, nil, err + } + + return nil, nil, nil +} diff --git a/vendor/github.com/pion/dtls/v2/flight1handler.go b/vendor/github.com/pion/dtls/v2/flight1handler.go new file mode 100644 index 0000000..9229292 --- /dev/null +++ b/vendor/github.com/pion/dtls/v2/flight1handler.go @@ -0,0 +1,112 @@ +package dtls + +import ( + "context" + + "github.com/pion/dtls/v2/pkg/crypto/elliptic" + "github.com/pion/dtls/v2/pkg/protocol" + "github.com/pion/dtls/v2/pkg/protocol/alert" + "github.com/pion/dtls/v2/pkg/protocol/extension" + "github.com/pion/dtls/v2/pkg/protocol/handshake" + "github.com/pion/dtls/v2/pkg/protocol/recordlayer" +) + +func flight1Parse(ctx context.Context, c flightConn, state *State, cache *handshakeCache, cfg *handshakeConfig) (flightVal, *alert.Alert, error) { + // HelloVerifyRequest can be skipped by the server, + // so allow ServerHello during flight1 also + seq, msgs, ok := cache.fullPullMap(state.handshakeRecvSequence, + handshakeCachePullRule{handshake.TypeHelloVerifyRequest, cfg.initialEpoch, false, true}, + handshakeCachePullRule{handshake.TypeServerHello, cfg.initialEpoch, false, true}, + ) + if !ok { + // No valid message received. Keep reading + return 0, nil, nil + } + + if _, ok := msgs[handshake.TypeServerHello]; ok { + // Flight1 and flight2 were skipped. + // Parse as flight3. + return flight3Parse(ctx, c, state, cache, cfg) + } + + if h, ok := msgs[handshake.TypeHelloVerifyRequest].(*handshake.MessageHelloVerifyRequest); ok { + // DTLS 1.2 clients must not assume that the server will use the protocol version + // specified in HelloVerifyRequest message. RFC 6347 Section 4.2.1 + if !h.Version.Equal(protocol.Version1_0) && !h.Version.Equal(protocol.Version1_2) { + return 0, &alert.Alert{Level: alert.Fatal, Description: alert.ProtocolVersion}, errUnsupportedProtocolVersion + } + state.cookie = append([]byte{}, h.Cookie...) + state.handshakeRecvSequence = seq + return flight3, nil, nil + } + + return 0, &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}, nil +} + +func flight1Generate(c flightConn, state *State, cache *handshakeCache, cfg *handshakeConfig) ([]*packet, *alert.Alert, error) { + var zeroEpoch uint16 + state.localEpoch.Store(zeroEpoch) + state.remoteEpoch.Store(zeroEpoch) + state.namedCurve = defaultNamedCurve + state.cookie = nil + + if err := state.localRandom.Populate(); err != nil { + return nil, nil, err + } + + extensions := []extension.Extension{ + &extension.SupportedSignatureAlgorithms{ + SignatureHashAlgorithms: cfg.localSignatureSchemes, + }, + &extension.RenegotiationInfo{ + RenegotiatedConnection: 0, + }, + } + if cfg.localPSKCallback == nil { + extensions = append(extensions, []extension.Extension{ + &extension.SupportedEllipticCurves{ + EllipticCurves: []elliptic.Curve{elliptic.X25519, elliptic.P256, elliptic.P384}, + }, + &extension.SupportedPointFormats{ + PointFormats: []elliptic.CurvePointFormat{elliptic.CurvePointFormatUncompressed}, + }, + }...) + } + + if len(cfg.localSRTPProtectionProfiles) > 0 { + extensions = append(extensions, &extension.UseSRTP{ + ProtectionProfiles: cfg.localSRTPProtectionProfiles, + }) + } + + if cfg.extendedMasterSecret == RequestExtendedMasterSecret || + cfg.extendedMasterSecret == RequireExtendedMasterSecret { + extensions = append(extensions, &extension.UseExtendedMasterSecret{ + Supported: true, + }) + } + + if len(cfg.serverName) > 0 { + extensions = append(extensions, &extension.ServerName{ServerName: cfg.serverName}) + } + + return []*packet{ + { + record: &recordlayer.RecordLayer{ + Header: recordlayer.Header{ + Version: protocol.Version1_2, + }, + Content: &handshake.Handshake{ + Message: &handshake.MessageClientHello{ + Version: protocol.Version1_2, + Cookie: state.cookie, + Random: state.localRandom, + CipherSuiteIDs: cipherSuiteIDs(cfg.localCipherSuites), + CompressionMethods: defaultCompressionMethods(), + Extensions: extensions, + }, + }, + }, + }, + }, nil, nil +} diff --git a/vendor/github.com/pion/dtls/v2/flight2handler.go b/vendor/github.com/pion/dtls/v2/flight2handler.go new file mode 100644 index 0000000..bb8e91d --- /dev/null +++ b/vendor/github.com/pion/dtls/v2/flight2handler.go @@ -0,0 +1,61 @@ +package dtls + +import ( + "bytes" + "context" + + "github.com/pion/dtls/v2/pkg/protocol" + "github.com/pion/dtls/v2/pkg/protocol/alert" + "github.com/pion/dtls/v2/pkg/protocol/handshake" + "github.com/pion/dtls/v2/pkg/protocol/recordlayer" +) + +func flight2Parse(ctx context.Context, c flightConn, state *State, cache *handshakeCache, cfg *handshakeConfig) (flightVal, *alert.Alert, error) { + seq, msgs, ok := cache.fullPullMap(state.handshakeRecvSequence, + handshakeCachePullRule{handshake.TypeClientHello, cfg.initialEpoch, true, false}, + ) + if !ok { + // Client may retransmit the first ClientHello when HelloVerifyRequest is dropped. + // Parse as flight 0 in this case. + return flight0Parse(ctx, c, state, cache, cfg) + } + state.handshakeRecvSequence = seq + + var clientHello *handshake.MessageClientHello + + // Validate type + if clientHello, ok = msgs[handshake.TypeClientHello].(*handshake.MessageClientHello); !ok { + return 0, &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}, nil + } + + if !clientHello.Version.Equal(protocol.Version1_2) { + return 0, &alert.Alert{Level: alert.Fatal, Description: alert.ProtocolVersion}, errUnsupportedProtocolVersion + } + + if len(clientHello.Cookie) == 0 { + return 0, nil, nil + } + if !bytes.Equal(state.cookie, clientHello.Cookie) { + return 0, &alert.Alert{Level: alert.Fatal, Description: alert.AccessDenied}, errCookieMismatch + } + return flight4, nil, nil +} + +func flight2Generate(c flightConn, state *State, cache *handshakeCache, cfg *handshakeConfig) ([]*packet, *alert.Alert, error) { + state.handshakeSendSequence = 0 + return []*packet{ + { + record: &recordlayer.RecordLayer{ + Header: recordlayer.Header{ + Version: protocol.Version1_2, + }, + Content: &handshake.Handshake{ + Message: &handshake.MessageHelloVerifyRequest{ + Version: protocol.Version1_2, + Cookie: state.cookie, + }, + }, + }, + }, + }, nil, nil +} diff --git a/vendor/github.com/pion/dtls/v2/flight3handler.go b/vendor/github.com/pion/dtls/v2/flight3handler.go new file mode 100644 index 0000000..f953be8 --- /dev/null +++ b/vendor/github.com/pion/dtls/v2/flight3handler.go @@ -0,0 +1,194 @@ +package dtls + +import ( + "context" + + "github.com/pion/dtls/v2/pkg/crypto/elliptic" + "github.com/pion/dtls/v2/pkg/crypto/prf" + "github.com/pion/dtls/v2/pkg/protocol" + "github.com/pion/dtls/v2/pkg/protocol/alert" + "github.com/pion/dtls/v2/pkg/protocol/extension" + "github.com/pion/dtls/v2/pkg/protocol/handshake" + "github.com/pion/dtls/v2/pkg/protocol/recordlayer" +) + +func flight3Parse(ctx context.Context, c flightConn, state *State, cache *handshakeCache, cfg *handshakeConfig) (flightVal, *alert.Alert, error) { //nolint:gocognit + // Clients may receive multiple HelloVerifyRequest messages with different cookies. + // Clients SHOULD handle this by sending a new ClientHello with a cookie in response + // to the new HelloVerifyRequest. RFC 6347 Section 4.2.1 + seq, msgs, ok := cache.fullPullMap(state.handshakeRecvSequence, + handshakeCachePullRule{handshake.TypeHelloVerifyRequest, cfg.initialEpoch, false, true}, + ) + if ok { + if h, msgOk := msgs[handshake.TypeHelloVerifyRequest].(*handshake.MessageHelloVerifyRequest); msgOk { + // DTLS 1.2 clients must not assume that the server will use the protocol version + // specified in HelloVerifyRequest message. RFC 6347 Section 4.2.1 + if !h.Version.Equal(protocol.Version1_0) && !h.Version.Equal(protocol.Version1_2) { + return 0, &alert.Alert{Level: alert.Fatal, Description: alert.ProtocolVersion}, errUnsupportedProtocolVersion + } + state.cookie = append([]byte{}, h.Cookie...) + state.handshakeRecvSequence = seq + return flight3, nil, nil + } + } + + if cfg.localPSKCallback != nil { + seq, msgs, ok = cache.fullPullMap(state.handshakeRecvSequence, + handshakeCachePullRule{handshake.TypeServerHello, cfg.initialEpoch, false, false}, + handshakeCachePullRule{handshake.TypeServerKeyExchange, cfg.initialEpoch, false, true}, + handshakeCachePullRule{handshake.TypeServerHelloDone, cfg.initialEpoch, false, false}, + ) + } else { + seq, msgs, ok = cache.fullPullMap(state.handshakeRecvSequence, + handshakeCachePullRule{handshake.TypeServerHello, cfg.initialEpoch, false, false}, + handshakeCachePullRule{handshake.TypeCertificate, cfg.initialEpoch, false, true}, + handshakeCachePullRule{handshake.TypeServerKeyExchange, cfg.initialEpoch, false, false}, + handshakeCachePullRule{handshake.TypeCertificateRequest, cfg.initialEpoch, false, true}, + handshakeCachePullRule{handshake.TypeServerHelloDone, cfg.initialEpoch, false, false}, + ) + } + if !ok { + // Don't have enough messages. Keep reading + return 0, nil, nil + } + state.handshakeRecvSequence = seq + + if h, ok := msgs[handshake.TypeServerHello].(*handshake.MessageServerHello); ok { + if !h.Version.Equal(protocol.Version1_2) { + return 0, &alert.Alert{Level: alert.Fatal, Description: alert.ProtocolVersion}, errUnsupportedProtocolVersion + } + for _, v := range h.Extensions { + switch e := v.(type) { + case *extension.UseSRTP: + profile, ok := findMatchingSRTPProfile(e.ProtectionProfiles, cfg.localSRTPProtectionProfiles) + if !ok { + return 0, &alert.Alert{Level: alert.Fatal, Description: alert.IllegalParameter}, errClientNoMatchingSRTPProfile + } + state.srtpProtectionProfile = profile + case *extension.UseExtendedMasterSecret: + if cfg.extendedMasterSecret != DisableExtendedMasterSecret { + state.extendedMasterSecret = true + } + } + } + if cfg.extendedMasterSecret == RequireExtendedMasterSecret && !state.extendedMasterSecret { + return 0, &alert.Alert{Level: alert.Fatal, Description: alert.InsufficientSecurity}, errClientRequiredButNoServerEMS + } + if len(cfg.localSRTPProtectionProfiles) > 0 && state.srtpProtectionProfile == 0 { + return 0, &alert.Alert{Level: alert.Fatal, Description: alert.InsufficientSecurity}, errRequestedButNoSRTPExtension + } + + remoteCipherSuite := cipherSuiteForID(CipherSuiteID(*h.CipherSuiteID), cfg.customCipherSuites) + if remoteCipherSuite == nil { + return 0, &alert.Alert{Level: alert.Fatal, Description: alert.InsufficientSecurity}, errCipherSuiteNoIntersection + } + + selectedCipherSuite, ok := findMatchingCipherSuite([]CipherSuite{remoteCipherSuite}, cfg.localCipherSuites) + if !ok { + return 0, &alert.Alert{Level: alert.Fatal, Description: alert.InsufficientSecurity}, errInvalidCipherSuite + } + + state.cipherSuite = selectedCipherSuite + state.remoteRandom = h.Random + cfg.log.Tracef("[handshake] use cipher suite: %s", selectedCipherSuite.String()) + } + + if h, ok := msgs[handshake.TypeCertificate].(*handshake.MessageCertificate); ok { + state.PeerCertificates = h.Certificate + } else if state.cipherSuite.AuthenticationType() == CipherSuiteAuthenticationTypeCertificate { + return 0, &alert.Alert{Level: alert.Fatal, Description: alert.NoCertificate}, errInvalidCertificate + } + + if h, ok := msgs[handshake.TypeServerKeyExchange].(*handshake.MessageServerKeyExchange); ok { + alertPtr, err := handleServerKeyExchange(c, state, cfg, h) + if err != nil { + return 0, alertPtr, err + } + } + + if _, ok := msgs[handshake.TypeCertificateRequest].(*handshake.MessageCertificateRequest); ok { + state.remoteRequestedCertificate = true + } + + return flight5, nil, nil +} + +func handleServerKeyExchange(_ flightConn, state *State, cfg *handshakeConfig, h *handshake.MessageServerKeyExchange) (*alert.Alert, error) { + var err error + if cfg.localPSKCallback != nil { + var psk []byte + if psk, err = cfg.localPSKCallback(h.IdentityHint); err != nil { + return &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}, err + } + state.IdentityHint = h.IdentityHint + state.preMasterSecret = prf.PSKPreMasterSecret(psk) + } else { + if state.localKeypair, err = elliptic.GenerateKeypair(h.NamedCurve); err != nil { + return &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}, err + } + + if state.preMasterSecret, err = prf.PreMasterSecret(h.PublicKey, state.localKeypair.PrivateKey, state.localKeypair.Curve); err != nil { + return &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}, err + } + } + + return nil, nil +} + +func flight3Generate(c flightConn, state *State, cache *handshakeCache, cfg *handshakeConfig) ([]*packet, *alert.Alert, error) { + extensions := []extension.Extension{ + &extension.SupportedSignatureAlgorithms{ + SignatureHashAlgorithms: cfg.localSignatureSchemes, + }, + &extension.RenegotiationInfo{ + RenegotiatedConnection: 0, + }, + } + if cfg.localPSKCallback == nil { + extensions = append(extensions, []extension.Extension{ + &extension.SupportedEllipticCurves{ + EllipticCurves: []elliptic.Curve{elliptic.X25519, elliptic.P256, elliptic.P384}, + }, + &extension.SupportedPointFormats{ + PointFormats: []elliptic.CurvePointFormat{elliptic.CurvePointFormatUncompressed}, + }, + }...) + } + + if len(cfg.localSRTPProtectionProfiles) > 0 { + extensions = append(extensions, &extension.UseSRTP{ + ProtectionProfiles: cfg.localSRTPProtectionProfiles, + }) + } + + if cfg.extendedMasterSecret == RequestExtendedMasterSecret || + cfg.extendedMasterSecret == RequireExtendedMasterSecret { + extensions = append(extensions, &extension.UseExtendedMasterSecret{ + Supported: true, + }) + } + + if len(cfg.serverName) > 0 { + extensions = append(extensions, &extension.ServerName{ServerName: cfg.serverName}) + } + + return []*packet{ + { + record: &recordlayer.RecordLayer{ + Header: recordlayer.Header{ + Version: protocol.Version1_2, + }, + Content: &handshake.Handshake{ + Message: &handshake.MessageClientHello{ + Version: protocol.Version1_2, + Cookie: state.cookie, + Random: state.localRandom, + CipherSuiteIDs: cipherSuiteIDs(cfg.localCipherSuites), + CompressionMethods: defaultCompressionMethods(), + Extensions: extensions, + }, + }, + }, + }, + }, nil, nil +} diff --git a/vendor/github.com/pion/dtls/v2/flight4handler.go b/vendor/github.com/pion/dtls/v2/flight4handler.go new file mode 100644 index 0000000..d791c9d --- /dev/null +++ b/vendor/github.com/pion/dtls/v2/flight4handler.go @@ -0,0 +1,337 @@ +package dtls + +import ( + "context" + "crypto/x509" + + "github.com/pion/dtls/v2/pkg/crypto/clientcertificate" + "github.com/pion/dtls/v2/pkg/crypto/elliptic" + "github.com/pion/dtls/v2/pkg/crypto/prf" + "github.com/pion/dtls/v2/pkg/crypto/signaturehash" + "github.com/pion/dtls/v2/pkg/protocol" + "github.com/pion/dtls/v2/pkg/protocol/alert" + "github.com/pion/dtls/v2/pkg/protocol/extension" + "github.com/pion/dtls/v2/pkg/protocol/handshake" + "github.com/pion/dtls/v2/pkg/protocol/recordlayer" +) + +func flight4Parse(ctx context.Context, c flightConn, state *State, cache *handshakeCache, cfg *handshakeConfig) (flightVal, *alert.Alert, error) { //nolint:gocognit + seq, msgs, ok := cache.fullPullMap(state.handshakeRecvSequence, + handshakeCachePullRule{handshake.TypeCertificate, cfg.initialEpoch, true, true}, + handshakeCachePullRule{handshake.TypeClientKeyExchange, cfg.initialEpoch, true, false}, + handshakeCachePullRule{handshake.TypeCertificateVerify, cfg.initialEpoch, true, true}, + ) + if !ok { + // No valid message received. Keep reading + return 0, nil, nil + } + + // Validate type + var clientKeyExchange *handshake.MessageClientKeyExchange + if clientKeyExchange, ok = msgs[handshake.TypeClientKeyExchange].(*handshake.MessageClientKeyExchange); !ok { + return 0, &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}, nil + } + + if h, hasCert := msgs[handshake.TypeCertificate].(*handshake.MessageCertificate); hasCert { + state.PeerCertificates = h.Certificate + } + + if h, hasCertVerify := msgs[handshake.TypeCertificateVerify].(*handshake.MessageCertificateVerify); hasCertVerify { + if state.PeerCertificates == nil { + return 0, &alert.Alert{Level: alert.Fatal, Description: alert.NoCertificate}, errCertificateVerifyNoCertificate + } + + plainText := cache.pullAndMerge( + handshakeCachePullRule{handshake.TypeClientHello, cfg.initialEpoch, true, false}, + handshakeCachePullRule{handshake.TypeServerHello, cfg.initialEpoch, false, false}, + handshakeCachePullRule{handshake.TypeCertificate, cfg.initialEpoch, false, false}, + handshakeCachePullRule{handshake.TypeServerKeyExchange, cfg.initialEpoch, false, false}, + handshakeCachePullRule{handshake.TypeCertificateRequest, cfg.initialEpoch, false, false}, + handshakeCachePullRule{handshake.TypeServerHelloDone, cfg.initialEpoch, false, false}, + handshakeCachePullRule{handshake.TypeCertificate, cfg.initialEpoch, true, false}, + handshakeCachePullRule{handshake.TypeClientKeyExchange, cfg.initialEpoch, true, false}, + ) + + // Verify that the pair of hash algorithm and signiture is listed. + var validSignatureScheme bool + for _, ss := range cfg.localSignatureSchemes { + if ss.Hash == h.HashAlgorithm && ss.Signature == h.SignatureAlgorithm { + validSignatureScheme = true + break + } + } + if !validSignatureScheme { + return 0, &alert.Alert{Level: alert.Fatal, Description: alert.InsufficientSecurity}, errNoAvailableSignatureSchemes + } + + if err := verifyCertificateVerify(plainText, h.HashAlgorithm, h.Signature, state.PeerCertificates); err != nil { + return 0, &alert.Alert{Level: alert.Fatal, Description: alert.BadCertificate}, err + } + var chains [][]*x509.Certificate + var err error + var verified bool + if cfg.clientAuth >= VerifyClientCertIfGiven { + if chains, err = verifyClientCert(state.PeerCertificates, cfg.clientCAs); err != nil { + return 0, &alert.Alert{Level: alert.Fatal, Description: alert.BadCertificate}, err + } + verified = true + } + if cfg.verifyPeerCertificate != nil { + if err := cfg.verifyPeerCertificate(state.PeerCertificates, chains); err != nil { + return 0, &alert.Alert{Level: alert.Fatal, Description: alert.BadCertificate}, err + } + } + state.peerCertificatesVerified = verified + } + + if !state.cipherSuite.IsInitialized() { + serverRandom := state.localRandom.MarshalFixed() + clientRandom := state.remoteRandom.MarshalFixed() + + var err error + var preMasterSecret []byte + if state.cipherSuite.AuthenticationType() == CipherSuiteAuthenticationTypePreSharedKey { + var psk []byte + if psk, err = cfg.localPSKCallback(clientKeyExchange.IdentityHint); err != nil { + return 0, &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}, err + } + state.IdentityHint = clientKeyExchange.IdentityHint + preMasterSecret = prf.PSKPreMasterSecret(psk) + } else { + preMasterSecret, err = prf.PreMasterSecret(clientKeyExchange.PublicKey, state.localKeypair.PrivateKey, state.localKeypair.Curve) + if err != nil { + return 0, &alert.Alert{Level: alert.Fatal, Description: alert.IllegalParameter}, err + } + } + + if state.extendedMasterSecret { + var sessionHash []byte + sessionHash, err = cache.sessionHash(state.cipherSuite.HashFunc(), cfg.initialEpoch) + if err != nil { + return 0, &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}, err + } + + state.masterSecret, err = prf.ExtendedMasterSecret(preMasterSecret, sessionHash, state.cipherSuite.HashFunc()) + if err != nil { + return 0, &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}, err + } + } else { + state.masterSecret, err = prf.MasterSecret(preMasterSecret, clientRandom[:], serverRandom[:], state.cipherSuite.HashFunc()) + if err != nil { + return 0, &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}, err + } + } + + if err := state.cipherSuite.Init(state.masterSecret, clientRandom[:], serverRandom[:], false); err != nil { + return 0, &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}, err + } + cfg.writeKeyLog(keyLogLabelTLS12, clientRandom[:], state.masterSecret) + } + + // Now, encrypted packets can be handled + if err := c.handleQueuedPackets(ctx); err != nil { + return 0, &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}, err + } + + seq, msgs, ok = cache.fullPullMap(seq, + handshakeCachePullRule{handshake.TypeFinished, cfg.initialEpoch + 1, true, false}, + ) + if !ok { + // No valid message received. Keep reading + return 0, nil, nil + } + state.handshakeRecvSequence = seq + + if _, ok = msgs[handshake.TypeFinished].(*handshake.MessageFinished); !ok { + return 0, &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}, nil + } + + if state.cipherSuite.AuthenticationType() == CipherSuiteAuthenticationTypeAnonymous { + return flight6, nil, nil + } + + switch cfg.clientAuth { + case RequireAnyClientCert: + if state.PeerCertificates == nil { + return 0, &alert.Alert{Level: alert.Fatal, Description: alert.NoCertificate}, errClientCertificateRequired + } + case VerifyClientCertIfGiven: + if state.PeerCertificates != nil && !state.peerCertificatesVerified { + return 0, &alert.Alert{Level: alert.Fatal, Description: alert.BadCertificate}, errClientCertificateNotVerified + } + case RequireAndVerifyClientCert: + if state.PeerCertificates == nil { + return 0, &alert.Alert{Level: alert.Fatal, Description: alert.NoCertificate}, errClientCertificateRequired + } + if !state.peerCertificatesVerified { + return 0, &alert.Alert{Level: alert.Fatal, Description: alert.BadCertificate}, errClientCertificateNotVerified + } + case NoClientCert, RequestClientCert: + return flight6, nil, nil + } + + return flight6, nil, nil +} + +func flight4Generate(c flightConn, state *State, cache *handshakeCache, cfg *handshakeConfig) ([]*packet, *alert.Alert, error) { + extensions := []extension.Extension{&extension.RenegotiationInfo{ + RenegotiatedConnection: 0, + }} + if (cfg.extendedMasterSecret == RequestExtendedMasterSecret || + cfg.extendedMasterSecret == RequireExtendedMasterSecret) && state.extendedMasterSecret { + extensions = append(extensions, &extension.UseExtendedMasterSecret{ + Supported: true, + }) + } + if state.srtpProtectionProfile != 0 { + extensions = append(extensions, &extension.UseSRTP{ + ProtectionProfiles: []SRTPProtectionProfile{state.srtpProtectionProfile}, + }) + } + if state.cipherSuite.AuthenticationType() == CipherSuiteAuthenticationTypeCertificate { + extensions = append(extensions, []extension.Extension{ + &extension.SupportedEllipticCurves{ + EllipticCurves: []elliptic.Curve{elliptic.X25519, elliptic.P256, elliptic.P384}, + }, + &extension.SupportedPointFormats{ + PointFormats: []elliptic.CurvePointFormat{elliptic.CurvePointFormatUncompressed}, + }, + }...) + } + + var pkts []*packet + cipherSuiteID := uint16(state.cipherSuite.ID()) + + pkts = append(pkts, &packet{ + record: &recordlayer.RecordLayer{ + Header: recordlayer.Header{ + Version: protocol.Version1_2, + }, + Content: &handshake.Handshake{ + Message: &handshake.MessageServerHello{ + Version: protocol.Version1_2, + Random: state.localRandom, + CipherSuiteID: &cipherSuiteID, + CompressionMethod: defaultCompressionMethods()[0], + Extensions: extensions, + }, + }, + }, + }) + + switch { + case state.cipherSuite.AuthenticationType() == CipherSuiteAuthenticationTypeCertificate: + certificate, err := cfg.getCertificate(cfg.serverName) + if err != nil { + return nil, &alert.Alert{Level: alert.Fatal, Description: alert.HandshakeFailure}, err + } + + pkts = append(pkts, &packet{ + record: &recordlayer.RecordLayer{ + Header: recordlayer.Header{ + Version: protocol.Version1_2, + }, + Content: &handshake.Handshake{ + Message: &handshake.MessageCertificate{ + Certificate: certificate.Certificate, + }, + }, + }, + }) + + serverRandom := state.localRandom.MarshalFixed() + clientRandom := state.remoteRandom.MarshalFixed() + + // Find compatible signature scheme + signatureHashAlgo, err := signaturehash.SelectSignatureScheme(cfg.localSignatureSchemes, certificate.PrivateKey) + if err != nil { + return nil, &alert.Alert{Level: alert.Fatal, Description: alert.InsufficientSecurity}, err + } + + signature, err := generateKeySignature(clientRandom[:], serverRandom[:], state.localKeypair.PublicKey, state.namedCurve, certificate.PrivateKey, signatureHashAlgo.Hash) + if err != nil { + return nil, &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}, err + } + state.localKeySignature = signature + + pkts = append(pkts, &packet{ + record: &recordlayer.RecordLayer{ + Header: recordlayer.Header{ + Version: protocol.Version1_2, + }, + Content: &handshake.Handshake{ + Message: &handshake.MessageServerKeyExchange{ + EllipticCurveType: elliptic.CurveTypeNamedCurve, + NamedCurve: state.namedCurve, + PublicKey: state.localKeypair.PublicKey, + HashAlgorithm: signatureHashAlgo.Hash, + SignatureAlgorithm: signatureHashAlgo.Signature, + Signature: state.localKeySignature, + }, + }, + }, + }) + + if cfg.clientAuth > NoClientCert { + pkts = append(pkts, &packet{ + record: &recordlayer.RecordLayer{ + Header: recordlayer.Header{ + Version: protocol.Version1_2, + }, + Content: &handshake.Handshake{ + Message: &handshake.MessageCertificateRequest{ + CertificateTypes: []clientcertificate.Type{clientcertificate.RSASign, clientcertificate.ECDSASign}, + SignatureHashAlgorithms: cfg.localSignatureSchemes, + }, + }, + }, + }) + } + case cfg.localPSKIdentityHint != nil: + // To help the client in selecting which identity to use, the server + // can provide a "PSK identity hint" in the ServerKeyExchange message. + // If no hint is provided, the ServerKeyExchange message is omitted. + // + // https://tools.ietf.org/html/rfc4279#section-2 + pkts = append(pkts, &packet{ + record: &recordlayer.RecordLayer{ + Header: recordlayer.Header{ + Version: protocol.Version1_2, + }, + Content: &handshake.Handshake{ + Message: &handshake.MessageServerKeyExchange{ + IdentityHint: cfg.localPSKIdentityHint, + }, + }, + }, + }) + case state.cipherSuite.AuthenticationType() == CipherSuiteAuthenticationTypeAnonymous: + pkts = append(pkts, &packet{ + record: &recordlayer.RecordLayer{ + Header: recordlayer.Header{ + Version: protocol.Version1_2, + }, + Content: &handshake.Handshake{ + Message: &handshake.MessageServerKeyExchange{ + EllipticCurveType: elliptic.CurveTypeNamedCurve, + NamedCurve: state.namedCurve, + PublicKey: state.localKeypair.PublicKey, + }, + }, + }, + }) + } + + pkts = append(pkts, &packet{ + record: &recordlayer.RecordLayer{ + Header: recordlayer.Header{ + Version: protocol.Version1_2, + }, + Content: &handshake.Handshake{ + Message: &handshake.MessageServerHelloDone{}, + }, + }, + }) + + return pkts, nil, nil +} diff --git a/vendor/github.com/pion/dtls/v2/flight5handler.go b/vendor/github.com/pion/dtls/v2/flight5handler.go new file mode 100644 index 0000000..baa1d5c --- /dev/null +++ b/vendor/github.com/pion/dtls/v2/flight5handler.go @@ -0,0 +1,323 @@ +package dtls + +import ( + "bytes" + "context" + "crypto" + "crypto/x509" + + "github.com/pion/dtls/v2/pkg/crypto/prf" + "github.com/pion/dtls/v2/pkg/crypto/signaturehash" + "github.com/pion/dtls/v2/pkg/protocol" + "github.com/pion/dtls/v2/pkg/protocol/alert" + "github.com/pion/dtls/v2/pkg/protocol/handshake" + "github.com/pion/dtls/v2/pkg/protocol/recordlayer" +) + +func flight5Parse(ctx context.Context, c flightConn, state *State, cache *handshakeCache, cfg *handshakeConfig) (flightVal, *alert.Alert, error) { + _, msgs, ok := cache.fullPullMap(state.handshakeRecvSequence, + handshakeCachePullRule{handshake.TypeFinished, cfg.initialEpoch + 1, false, false}, + ) + if !ok { + // No valid message received. Keep reading + return 0, nil, nil + } + + var finished *handshake.MessageFinished + if finished, ok = msgs[handshake.TypeFinished].(*handshake.MessageFinished); !ok { + return 0, &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}, nil + } + plainText := cache.pullAndMerge( + handshakeCachePullRule{handshake.TypeClientHello, cfg.initialEpoch, true, false}, + handshakeCachePullRule{handshake.TypeServerHello, cfg.initialEpoch, false, false}, + handshakeCachePullRule{handshake.TypeCertificate, cfg.initialEpoch, false, false}, + handshakeCachePullRule{handshake.TypeServerKeyExchange, cfg.initialEpoch, false, false}, + handshakeCachePullRule{handshake.TypeCertificateRequest, cfg.initialEpoch, false, false}, + handshakeCachePullRule{handshake.TypeServerHelloDone, cfg.initialEpoch, false, false}, + handshakeCachePullRule{handshake.TypeCertificate, cfg.initialEpoch, true, false}, + handshakeCachePullRule{handshake.TypeClientKeyExchange, cfg.initialEpoch, true, false}, + handshakeCachePullRule{handshake.TypeCertificateVerify, cfg.initialEpoch, true, false}, + handshakeCachePullRule{handshake.TypeFinished, cfg.initialEpoch + 1, true, false}, + ) + + expectedVerifyData, err := prf.VerifyDataServer(state.masterSecret, plainText, state.cipherSuite.HashFunc()) + if err != nil { + return 0, &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}, err + } + if !bytes.Equal(expectedVerifyData, finished.VerifyData) { + return 0, &alert.Alert{Level: alert.Fatal, Description: alert.HandshakeFailure}, errVerifyDataMismatch + } + + return flight5, nil, nil +} + +func flight5Generate(c flightConn, state *State, cache *handshakeCache, cfg *handshakeConfig) ([]*packet, *alert.Alert, error) { //nolint:gocognit + var certBytes [][]byte + var privateKey crypto.PrivateKey + if len(cfg.localCertificates) > 0 { + certificate, err := cfg.getCertificate(cfg.serverName) + if err != nil { + return nil, &alert.Alert{Level: alert.Fatal, Description: alert.HandshakeFailure}, err + } + certBytes = certificate.Certificate + privateKey = certificate.PrivateKey + } + + var pkts []*packet + + if state.remoteRequestedCertificate { + pkts = append(pkts, + &packet{ + record: &recordlayer.RecordLayer{ + Header: recordlayer.Header{ + Version: protocol.Version1_2, + }, + Content: &handshake.Handshake{ + Message: &handshake.MessageCertificate{ + Certificate: certBytes, + }, + }, + }, + }) + } + + clientKeyExchange := &handshake.MessageClientKeyExchange{} + if cfg.localPSKCallback == nil { + clientKeyExchange.PublicKey = state.localKeypair.PublicKey + } else { + clientKeyExchange.IdentityHint = cfg.localPSKIdentityHint + } + + pkts = append(pkts, + &packet{ + record: &recordlayer.RecordLayer{ + Header: recordlayer.Header{ + Version: protocol.Version1_2, + }, + Content: &handshake.Handshake{ + Message: clientKeyExchange, + }, + }, + }) + + serverKeyExchangeData := cache.pullAndMerge( + handshakeCachePullRule{handshake.TypeServerKeyExchange, cfg.initialEpoch, false, false}, + ) + + serverKeyExchange := &handshake.MessageServerKeyExchange{} + + // handshakeMessageServerKeyExchange is optional for PSK + if len(serverKeyExchangeData) == 0 { + alertPtr, err := handleServerKeyExchange(c, state, cfg, &handshake.MessageServerKeyExchange{}) + if err != nil { + return nil, alertPtr, err + } + } else { + rawHandshake := &handshake.Handshake{} + err := rawHandshake.Unmarshal(serverKeyExchangeData) + if err != nil { + return nil, &alert.Alert{Level: alert.Fatal, Description: alert.UnexpectedMessage}, err + } + + switch h := rawHandshake.Message.(type) { + case *handshake.MessageServerKeyExchange: + serverKeyExchange = h + default: + return nil, &alert.Alert{Level: alert.Fatal, Description: alert.UnexpectedMessage}, errInvalidContentType + } + } + + // Append not-yet-sent packets + merged := []byte{} + seqPred := uint16(state.handshakeSendSequence) + for _, p := range pkts { + h, ok := p.record.Content.(*handshake.Handshake) + if !ok { + return nil, &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}, errInvalidContentType + } + h.Header.MessageSequence = seqPred + seqPred++ + raw, err := h.Marshal() + if err != nil { + return nil, &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}, err + } + merged = append(merged, raw...) + } + + if alertPtr, err := initalizeCipherSuite(state, cache, cfg, serverKeyExchange, merged); err != nil { + return nil, alertPtr, err + } + + // If the client has sent a certificate with signing ability, a digitally-signed + // CertificateVerify message is sent to explicitly verify possession of the + // private key in the certificate. + if state.remoteRequestedCertificate && len(cfg.localCertificates) > 0 { + plainText := append(cache.pullAndMerge( + handshakeCachePullRule{handshake.TypeClientHello, cfg.initialEpoch, true, false}, + handshakeCachePullRule{handshake.TypeServerHello, cfg.initialEpoch, false, false}, + handshakeCachePullRule{handshake.TypeCertificate, cfg.initialEpoch, false, false}, + handshakeCachePullRule{handshake.TypeServerKeyExchange, cfg.initialEpoch, false, false}, + handshakeCachePullRule{handshake.TypeCertificateRequest, cfg.initialEpoch, false, false}, + handshakeCachePullRule{handshake.TypeServerHelloDone, cfg.initialEpoch, false, false}, + handshakeCachePullRule{handshake.TypeCertificate, cfg.initialEpoch, true, false}, + handshakeCachePullRule{handshake.TypeClientKeyExchange, cfg.initialEpoch, true, false}, + ), merged...) + + // Find compatible signature scheme + signatureHashAlgo, err := signaturehash.SelectSignatureScheme(cfg.localSignatureSchemes, privateKey) + if err != nil { + return nil, &alert.Alert{Level: alert.Fatal, Description: alert.InsufficientSecurity}, err + } + + certVerify, err := generateCertificateVerify(plainText, privateKey, signatureHashAlgo.Hash) + if err != nil { + return nil, &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}, err + } + state.localCertificatesVerify = certVerify + + p := &packet{ + record: &recordlayer.RecordLayer{ + Header: recordlayer.Header{ + Version: protocol.Version1_2, + }, + Content: &handshake.Handshake{ + Message: &handshake.MessageCertificateVerify{ + HashAlgorithm: signatureHashAlgo.Hash, + SignatureAlgorithm: signatureHashAlgo.Signature, + Signature: state.localCertificatesVerify, + }, + }, + }, + } + pkts = append(pkts, p) + + h, ok := p.record.Content.(*handshake.Handshake) + if !ok { + return nil, &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}, errInvalidContentType + } + h.Header.MessageSequence = seqPred + // seqPred++ // this is the last use of seqPred + raw, err := h.Marshal() + if err != nil { + return nil, &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}, err + } + merged = append(merged, raw...) + } + + pkts = append(pkts, + &packet{ + record: &recordlayer.RecordLayer{ + Header: recordlayer.Header{ + Version: protocol.Version1_2, + }, + Content: &protocol.ChangeCipherSpec{}, + }, + }) + + if len(state.localVerifyData) == 0 { + plainText := cache.pullAndMerge( + handshakeCachePullRule{handshake.TypeClientHello, cfg.initialEpoch, true, false}, + handshakeCachePullRule{handshake.TypeServerHello, cfg.initialEpoch, false, false}, + handshakeCachePullRule{handshake.TypeCertificate, cfg.initialEpoch, false, false}, + handshakeCachePullRule{handshake.TypeServerKeyExchange, cfg.initialEpoch, false, false}, + handshakeCachePullRule{handshake.TypeCertificateRequest, cfg.initialEpoch, false, false}, + handshakeCachePullRule{handshake.TypeServerHelloDone, cfg.initialEpoch, false, false}, + handshakeCachePullRule{handshake.TypeCertificate, cfg.initialEpoch, true, false}, + handshakeCachePullRule{handshake.TypeClientKeyExchange, cfg.initialEpoch, true, false}, + handshakeCachePullRule{handshake.TypeCertificateVerify, cfg.initialEpoch, true, false}, + handshakeCachePullRule{handshake.TypeFinished, cfg.initialEpoch + 1, true, false}, + ) + + var err error + state.localVerifyData, err = prf.VerifyDataClient(state.masterSecret, append(plainText, merged...), state.cipherSuite.HashFunc()) + if err != nil { + return nil, &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}, err + } + } + + pkts = append(pkts, + &packet{ + record: &recordlayer.RecordLayer{ + Header: recordlayer.Header{ + Version: protocol.Version1_2, + Epoch: 1, + }, + Content: &handshake.Handshake{ + Message: &handshake.MessageFinished{ + VerifyData: state.localVerifyData, + }, + }, + }, + shouldEncrypt: true, + resetLocalSequenceNumber: true, + }) + + return pkts, nil, nil +} + +func initalizeCipherSuite(state *State, cache *handshakeCache, cfg *handshakeConfig, h *handshake.MessageServerKeyExchange, sendingPlainText []byte) (*alert.Alert, error) { //nolint:gocognit + if state.cipherSuite.IsInitialized() { + return nil, nil + } + + clientRandom := state.localRandom.MarshalFixed() + serverRandom := state.remoteRandom.MarshalFixed() + + var err error + + if state.extendedMasterSecret { + var sessionHash []byte + sessionHash, err = cache.sessionHash(state.cipherSuite.HashFunc(), cfg.initialEpoch, sendingPlainText) + if err != nil { + return &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}, err + } + + state.masterSecret, err = prf.ExtendedMasterSecret(state.preMasterSecret, sessionHash, state.cipherSuite.HashFunc()) + if err != nil { + return &alert.Alert{Level: alert.Fatal, Description: alert.IllegalParameter}, err + } + } else { + state.masterSecret, err = prf.MasterSecret(state.preMasterSecret, clientRandom[:], serverRandom[:], state.cipherSuite.HashFunc()) + if err != nil { + return &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}, err + } + } + + if state.cipherSuite.AuthenticationType() == CipherSuiteAuthenticationTypeCertificate { + // Verify that the pair of hash algorithm and signiture is listed. + var validSignatureScheme bool + for _, ss := range cfg.localSignatureSchemes { + if ss.Hash == h.HashAlgorithm && ss.Signature == h.SignatureAlgorithm { + validSignatureScheme = true + break + } + } + if !validSignatureScheme { + return &alert.Alert{Level: alert.Fatal, Description: alert.InsufficientSecurity}, errNoAvailableSignatureSchemes + } + + expectedMsg := valueKeyMessage(clientRandom[:], serverRandom[:], h.PublicKey, h.NamedCurve) + if err = verifyKeySignature(expectedMsg, h.Signature, h.HashAlgorithm, state.PeerCertificates); err != nil { + return &alert.Alert{Level: alert.Fatal, Description: alert.BadCertificate}, err + } + var chains [][]*x509.Certificate + if !cfg.insecureSkipVerify { + if chains, err = verifyServerCert(state.PeerCertificates, cfg.rootCAs, cfg.serverName); err != nil { + return &alert.Alert{Level: alert.Fatal, Description: alert.BadCertificate}, err + } + } + if cfg.verifyPeerCertificate != nil { + if err = cfg.verifyPeerCertificate(state.PeerCertificates, chains); err != nil { + return &alert.Alert{Level: alert.Fatal, Description: alert.BadCertificate}, err + } + } + } + + if err = state.cipherSuite.Init(state.masterSecret, clientRandom[:], serverRandom[:], true); err != nil { + return &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}, err + } + + cfg.writeKeyLog(keyLogLabelTLS12, clientRandom[:], state.masterSecret) + + return nil, nil +} diff --git a/vendor/github.com/pion/dtls/v2/flight6handler.go b/vendor/github.com/pion/dtls/v2/flight6handler.go new file mode 100644 index 0000000..10de5ad --- /dev/null +++ b/vendor/github.com/pion/dtls/v2/flight6handler.go @@ -0,0 +1,82 @@ +package dtls + +import ( + "context" + + "github.com/pion/dtls/v2/pkg/crypto/prf" + "github.com/pion/dtls/v2/pkg/protocol" + "github.com/pion/dtls/v2/pkg/protocol/alert" + "github.com/pion/dtls/v2/pkg/protocol/handshake" + "github.com/pion/dtls/v2/pkg/protocol/recordlayer" +) + +func flight6Parse(ctx context.Context, c flightConn, state *State, cache *handshakeCache, cfg *handshakeConfig) (flightVal, *alert.Alert, error) { + _, msgs, ok := cache.fullPullMap(state.handshakeRecvSequence-1, + handshakeCachePullRule{handshake.TypeFinished, cfg.initialEpoch + 1, true, false}, + ) + if !ok { + // No valid message received. Keep reading + return 0, nil, nil + } + + if _, ok = msgs[handshake.TypeFinished].(*handshake.MessageFinished); !ok { + return 0, &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}, nil + } + + // Other party retransmitted the last flight. + return flight6, nil, nil +} + +func flight6Generate(c flightConn, state *State, cache *handshakeCache, cfg *handshakeConfig) ([]*packet, *alert.Alert, error) { + var pkts []*packet + + pkts = append(pkts, + &packet{ + record: &recordlayer.RecordLayer{ + Header: recordlayer.Header{ + Version: protocol.Version1_2, + }, + Content: &protocol.ChangeCipherSpec{}, + }, + }) + + if len(state.localVerifyData) == 0 { + plainText := cache.pullAndMerge( + handshakeCachePullRule{handshake.TypeClientHello, cfg.initialEpoch, true, false}, + handshakeCachePullRule{handshake.TypeServerHello, cfg.initialEpoch, false, false}, + handshakeCachePullRule{handshake.TypeCertificate, cfg.initialEpoch, false, false}, + handshakeCachePullRule{handshake.TypeServerKeyExchange, cfg.initialEpoch, false, false}, + handshakeCachePullRule{handshake.TypeCertificateRequest, cfg.initialEpoch, false, false}, + handshakeCachePullRule{handshake.TypeServerHelloDone, cfg.initialEpoch, false, false}, + handshakeCachePullRule{handshake.TypeCertificate, cfg.initialEpoch, true, false}, + handshakeCachePullRule{handshake.TypeClientKeyExchange, cfg.initialEpoch, true, false}, + handshakeCachePullRule{handshake.TypeCertificateVerify, cfg.initialEpoch, true, false}, + handshakeCachePullRule{handshake.TypeFinished, cfg.initialEpoch + 1, true, false}, + ) + + var err error + state.localVerifyData, err = prf.VerifyDataServer(state.masterSecret, plainText, state.cipherSuite.HashFunc()) + if err != nil { + return nil, &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}, err + } + } + + pkts = append(pkts, + &packet{ + record: &recordlayer.RecordLayer{ + Header: recordlayer.Header{ + Version: protocol.Version1_2, + Epoch: 1, + }, + Content: &handshake.Handshake{ + Message: &handshake.MessageFinished{ + VerifyData: state.localVerifyData, + }, + }, + }, + shouldEncrypt: true, + resetLocalSequenceNumber: true, + }, + ) + return pkts, nil, nil +} diff --git a/vendor/github.com/pion/dtls/v2/flighthandler.go b/vendor/github.com/pion/dtls/v2/flighthandler.go new file mode 100644 index 0000000..b364c09 --- /dev/null +++ b/vendor/github.com/pion/dtls/v2/flighthandler.go @@ -0,0 +1,57 @@ +package dtls + +import ( + "context" + + "github.com/pion/dtls/v2/pkg/protocol/alert" +) + +// Parse received handshakes and return next flightVal +type flightParser func(context.Context, flightConn, *State, *handshakeCache, *handshakeConfig) (flightVal, *alert.Alert, error) + +// Generate flights +type flightGenerator func(flightConn, *State, *handshakeCache, *handshakeConfig) ([]*packet, *alert.Alert, error) + +func (f flightVal) getFlightParser() (flightParser, error) { + switch f { + case flight0: + return flight0Parse, nil + case flight1: + return flight1Parse, nil + case flight2: + return flight2Parse, nil + case flight3: + return flight3Parse, nil + case flight4: + return flight4Parse, nil + case flight5: + return flight5Parse, nil + case flight6: + return flight6Parse, nil + default: + return nil, errInvalidFlight + } +} + +func (f flightVal) getFlightGenerator() (gen flightGenerator, retransmit bool, err error) { + switch f { + case flight0: + return flight0Generate, true, nil + case flight1: + return flight1Generate, true, nil + case flight2: + // https://tools.ietf.org/html/rfc6347#section-3.2.1 + // HelloVerifyRequests must not be retransmitted. + return flight2Generate, false, nil + case flight3: + return flight3Generate, true, nil + case flight4: + return flight4Generate, true, nil + case flight5: + return flight5Generate, true, nil + case flight6: + return flight6Generate, true, nil + default: + return nil, false, errInvalidFlight + } +} diff --git a/vendor/github.com/pion/dtls/v2/fragment_buffer.go b/vendor/github.com/pion/dtls/v2/fragment_buffer.go new file mode 100644 index 0000000..0274993 --- /dev/null +++ b/vendor/github.com/pion/dtls/v2/fragment_buffer.go @@ -0,0 +1,111 @@ +package dtls + +import ( + "github.com/pion/dtls/v2/pkg/protocol" + "github.com/pion/dtls/v2/pkg/protocol/handshake" + "github.com/pion/dtls/v2/pkg/protocol/recordlayer" +) + +type fragment struct { + recordLayerHeader recordlayer.Header + handshakeHeader handshake.Header + data []byte +} + +type fragmentBuffer struct { + // map of MessageSequenceNumbers that hold slices of fragments + cache map[uint16][]*fragment + + currentMessageSequenceNumber uint16 +} + +func newFragmentBuffer() *fragmentBuffer { + return &fragmentBuffer{cache: map[uint16][]*fragment{}} +} + +// Attempts to push a DTLS packet to the fragmentBuffer +// when it returns true it means the fragmentBuffer has inserted and the buffer shouldn't be handled +// when an error returns it is fatal, and the DTLS connection should be stopped +func (f *fragmentBuffer) push(buf []byte) (bool, error) { + frag := new(fragment) + if err := frag.recordLayerHeader.Unmarshal(buf); err != nil { + return false, err + } + + // fragment isn't a handshake, we don't need to handle it + if frag.recordLayerHeader.ContentType != protocol.ContentTypeHandshake { + return false, nil + } + + for buf = buf[recordlayer.HeaderSize:]; len(buf) != 0; frag = new(fragment) { + if err := frag.handshakeHeader.Unmarshal(buf); err != nil { + return false, err + } + + if _, ok := f.cache[frag.handshakeHeader.MessageSequence]; !ok { + f.cache[frag.handshakeHeader.MessageSequence] = []*fragment{} + } + + // end index should be the length of handshake header but if the handshake + // was fragmented, we should keep them all + end := int(handshake.HeaderLength + frag.handshakeHeader.Length) + if size := len(buf); end > size { + end = size + } + + // Discard all headers, when rebuilding the packet we will re-build + frag.data = append([]byte{}, buf[handshake.HeaderLength:end]...) + f.cache[frag.handshakeHeader.MessageSequence] = append(f.cache[frag.handshakeHeader.MessageSequence], frag) + buf = buf[end:] + } + + return true, nil +} + +func (f *fragmentBuffer) pop() (content []byte, epoch uint16) { + frags, ok := f.cache[f.currentMessageSequenceNumber] + if !ok { + return nil, 0 + } + + // Go doesn't support recursive lambdas + var appendMessage func(targetOffset uint32) bool + + rawMessage := []byte{} + appendMessage = func(targetOffset uint32) bool { + for _, f := range frags { + if f.handshakeHeader.FragmentOffset == targetOffset { + fragmentEnd := (f.handshakeHeader.FragmentOffset + f.handshakeHeader.FragmentLength) + if fragmentEnd != f.handshakeHeader.Length { + if !appendMessage(fragmentEnd) { + return false + } + } + + rawMessage = append(f.data, rawMessage...) + return true + } + } + return false + } + + // Recursively collect up + if !appendMessage(0) { + return nil, 0 + } + + firstHeader := frags[0].handshakeHeader + firstHeader.FragmentOffset = 0 + firstHeader.FragmentLength = firstHeader.Length + + rawHeader, err := firstHeader.Marshal() + if err != nil { + return nil, 0 + } + + messageEpoch := frags[0].recordLayerHeader.Epoch + + delete(f.cache, f.currentMessageSequenceNumber) + f.currentMessageSequenceNumber++ + return append(rawHeader, rawMessage...), messageEpoch +} diff --git a/vendor/github.com/pion/dtls/v2/fuzz.go b/vendor/github.com/pion/dtls/v2/fuzz.go new file mode 100644 index 0000000..56c1bf2 --- /dev/null +++ b/vendor/github.com/pion/dtls/v2/fuzz.go @@ -0,0 +1,38 @@ +// +build gofuzz + +package dtls + +import "fmt" + +func partialHeaderMismatch(a, b recordlayer.Header) bool { + // Ignoring content length for now. + a.contentLen = b.contentLen + return a != b +} + +func FuzzRecordLayer(data []byte) int { + var r recordLayer + if err := r.Unmarshal(data); err != nil { + return 0 + } + buf, err := r.Marshal() + if err != nil { + return 1 + } + if len(buf) == 0 { + panic("zero buff") // nolint + } + var nr recordLayer + if err = nr.Unmarshal(data); err != nil { + panic(err) // nolint + } + if partialHeaderMismatch(nr.recordlayer.Header, r.recordlayer.Header) { + panic( // nolint + fmt.Sprintf("header mismatch: %+v != %+v", + nr.recordlayer.Header, r.recordlayer.Header, + ), + ) + } + + return 1 +} diff --git a/vendor/github.com/pion/dtls/v2/go.mod b/vendor/github.com/pion/dtls/v2/go.mod new file mode 100644 index 0000000..33b60e0 --- /dev/null +++ b/vendor/github.com/pion/dtls/v2/go.mod @@ -0,0 +1,12 @@ +module github.com/pion/dtls/v2 + +require ( + github.com/pion/logging v0.2.2 + github.com/pion/transport v0.12.2 + github.com/pion/udp v0.1.0 + golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad + golang.org/x/net v0.0.0-20210119194325-5f4716e94777 + golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 +) + +go 1.13 diff --git a/vendor/github.com/pion/dtls/v2/go.sum b/vendor/github.com/pion/dtls/v2/go.sum new file mode 100644 index 0000000..fa7c410 --- /dev/null +++ b/vendor/github.com/pion/dtls/v2/go.sum @@ -0,0 +1,48 @@ +github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/pion/logging v0.2.2 h1:M9+AIj/+pxNsDfAT64+MAVgJO0rsyLnoJKCqf//DoeY= +github.com/pion/logging v0.2.2/go.mod h1:k0/tDVsRCX2Mb2ZEmTqNa7CWsQPc+YYCB7Q+5pahoms= +github.com/pion/transport v0.10.0 h1:9M12BSneJm6ggGhJyWpDveFOstJsTiQjkLf4M44rm80= +github.com/pion/transport v0.10.0/go.mod h1:BnHnUipd0rZQyTVB2SBGojFHT9CBt5C5TcsJSQGkvSE= +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/pion/udp v0.1.0 h1:uGxQsNyrqG3GLINv36Ff60covYmfrLoxzwnCsIYspXI= +github.com/pion/udp v0.1.0/go.mod h1:BPELIjbwE9PRbd/zxI/KYBnbo7B6+oA6YuEaNE8lths= +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.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4= +github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0= +github.com/stretchr/testify v1.6.1/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 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad h1:DN0cp81fZ3njFcrLCytUHRSUkqBjfTo4Tx9RJTWs0EY= +golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3 h1:0GoQqolDA55aaLxZyTzK/Y2ePZzZTUrRacwib7cNsYQ= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +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/net v0.0.0-20210119194325-5f4716e94777 h1:003p0dJM77cxMSyCPFphvZf/Y5/NXf5fzg6ufd1/Oew= +golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d h1:+R4KGOnez64A81RvjARKc4UT5/tI9ujCIVX+P5KiHuI= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +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/dtls/v2/handshake_cache.go b/vendor/github.com/pion/dtls/v2/handshake_cache.go new file mode 100644 index 0000000..063a858 --- /dev/null +++ b/vendor/github.com/pion/dtls/v2/handshake_cache.go @@ -0,0 +1,171 @@ +package dtls + +import ( + "sync" + + "github.com/pion/dtls/v2/pkg/crypto/prf" + "github.com/pion/dtls/v2/pkg/protocol/handshake" +) + +type handshakeCacheItem struct { + typ handshake.Type + isClient bool + epoch uint16 + messageSequence uint16 + data []byte +} + +type handshakeCachePullRule struct { + typ handshake.Type + epoch uint16 + isClient bool + optional bool +} + +type handshakeCache struct { + cache []*handshakeCacheItem + mu sync.Mutex +} + +func newHandshakeCache() *handshakeCache { + return &handshakeCache{} +} + +func (h *handshakeCache) push(data []byte, epoch, messageSequence uint16, typ handshake.Type, isClient bool) bool { //nolint + h.mu.Lock() + defer h.mu.Unlock() + + for _, i := range h.cache { + if i.messageSequence == messageSequence && + i.isClient == isClient { + return false + } + } + + h.cache = append(h.cache, &handshakeCacheItem{ + data: append([]byte{}, data...), + epoch: epoch, + messageSequence: messageSequence, + typ: typ, + isClient: isClient, + }) + return true +} + +// returns a list handshakes that match the requested rules +// the list will contain null entries for rules that can't be satisfied +// multiple entries may match a rule, but only the last match is returned (ie ClientHello with cookies) +func (h *handshakeCache) pull(rules ...handshakeCachePullRule) []*handshakeCacheItem { + h.mu.Lock() + defer h.mu.Unlock() + + out := make([]*handshakeCacheItem, len(rules)) + for i, r := range rules { + for _, c := range h.cache { + if c.typ == r.typ && c.isClient == r.isClient && c.epoch == r.epoch { + switch { + case out[i] == nil: + out[i] = c + case out[i].messageSequence < c.messageSequence: + out[i] = c + } + } + } + } + + return out +} + +// fullPullMap pulls all handshakes between rules[0] to rules[len(rules)-1] as map. +func (h *handshakeCache) fullPullMap(startSeq int, rules ...handshakeCachePullRule) (int, map[handshake.Type]handshake.Message, bool) { + h.mu.Lock() + defer h.mu.Unlock() + + ci := make(map[handshake.Type]*handshakeCacheItem) + for _, r := range rules { + var item *handshakeCacheItem + for _, c := range h.cache { + if c.typ == r.typ && c.isClient == r.isClient && c.epoch == r.epoch { + switch { + case item == nil: + item = c + case item.messageSequence < c.messageSequence: + item = c + } + } + } + if !r.optional && item == nil { + // Missing mandatory message. + return startSeq, nil, false + } + ci[r.typ] = item + } + out := make(map[handshake.Type]handshake.Message) + seq := startSeq + for _, r := range rules { + t := r.typ + i := ci[t] + if i == nil { + continue + } + rawHandshake := &handshake.Handshake{} + if err := rawHandshake.Unmarshal(i.data); err != nil { + return startSeq, nil, false + } + if uint16(seq) != rawHandshake.Header.MessageSequence { + // There is a gap. Some messages are not arrived. + return startSeq, nil, false + } + seq++ + out[t] = rawHandshake.Message + } + return seq, out, true +} + +// pullAndMerge calls pull and then merges the results, ignoring any null entries +func (h *handshakeCache) pullAndMerge(rules ...handshakeCachePullRule) []byte { + merged := []byte{} + + for _, p := range h.pull(rules...) { + if p != nil { + merged = append(merged, p.data...) + } + } + return merged +} + +// sessionHash returns the session hash for Extended Master Secret support +// https://tools.ietf.org/html/draft-ietf-tls-session-hash-06#section-4 +func (h *handshakeCache) sessionHash(hf prf.HashFunc, epoch uint16, additional ...[]byte) ([]byte, error) { + merged := []byte{} + + // Order defined by https://tools.ietf.org/html/rfc5246#section-7.3 + handshakeBuffer := h.pull( + handshakeCachePullRule{handshake.TypeClientHello, epoch, true, false}, + handshakeCachePullRule{handshake.TypeServerHello, epoch, false, false}, + handshakeCachePullRule{handshake.TypeCertificate, epoch, false, false}, + handshakeCachePullRule{handshake.TypeServerKeyExchange, epoch, false, false}, + handshakeCachePullRule{handshake.TypeCertificateRequest, epoch, false, false}, + handshakeCachePullRule{handshake.TypeServerHelloDone, epoch, false, false}, + handshakeCachePullRule{handshake.TypeCertificate, epoch, true, false}, + handshakeCachePullRule{handshake.TypeClientKeyExchange, epoch, true, false}, + ) + + for _, p := range handshakeBuffer { + if p == nil { + continue + } + + merged = append(merged, p.data...) + } + for _, a := range additional { + merged = append(merged, a...) + } + + hash := hf() + if _, err := hash.Write(merged); err != nil { + return []byte{}, err + } + + return hash.Sum(nil), nil +} diff --git a/vendor/github.com/pion/dtls/v2/handshaker.go b/vendor/github.com/pion/dtls/v2/handshaker.go new file mode 100644 index 0000000..0f5077e --- /dev/null +++ b/vendor/github.com/pion/dtls/v2/handshaker.go @@ -0,0 +1,334 @@ +package dtls + +import ( + "context" + "crypto/tls" + "crypto/x509" + "fmt" + "io" + "sync" + "time" + + "github.com/pion/dtls/v2/pkg/crypto/signaturehash" + "github.com/pion/dtls/v2/pkg/protocol/alert" + "github.com/pion/dtls/v2/pkg/protocol/handshake" + "github.com/pion/logging" +) + +// [RFC6347 Section-4.2.4] +// +-----------+ +// +---> | PREPARING | <--------------------+ +// | +-----------+ | +// | | | +// | | Buffer next flight | +// | | | +// | \|/ | +// | +-----------+ | +// | | SENDING |<------------------+ | Send +// | +-----------+ | | HelloRequest +// Receive | | | | +// next | | Send flight | | or +// flight | +--------+ | | +// | | | Set retransmit timer | | Receive +// | | \|/ | | HelloRequest +// | | +-----------+ | | Send +// +--)--| WAITING |-------------------+ | ClientHello +// | | +-----------+ Timer expires | | +// | | | | | +// | | +------------------------+ | +// Receive | | Send Read retransmit | +// last | | last | +// flight | | flight | +// | | | +// \|/\|/ | +// +-----------+ | +// | FINISHED | -------------------------------+ +// +-----------+ +// | /|\ +// | | +// +---+ +// Read retransmit +// Retransmit last flight + +type handshakeState uint8 + +const ( + handshakeErrored handshakeState = iota + handshakePreparing + handshakeSending + handshakeWaiting + handshakeFinished +) + +func (s handshakeState) String() string { + switch s { + case handshakeErrored: + return "Errored" + case handshakePreparing: + return "Preparing" + case handshakeSending: + return "Sending" + case handshakeWaiting: + return "Waiting" + case handshakeFinished: + return "Finished" + default: + return "Unknown" + } +} + +type handshakeFSM struct { + currentFlight flightVal + flights []*packet + retransmit bool + state *State + cache *handshakeCache + cfg *handshakeConfig + closed chan struct{} +} + +type handshakeConfig struct { + localPSKCallback PSKCallback + localPSKIdentityHint []byte + localCipherSuites []CipherSuite // Available CipherSuites + localSignatureSchemes []signaturehash.Algorithm // Available signature schemes + extendedMasterSecret ExtendedMasterSecretType // Policy for the Extended Master Support extension + localSRTPProtectionProfiles []SRTPProtectionProfile // Available SRTPProtectionProfiles, if empty no SRTP support + serverName string + clientAuth ClientAuthType // If we are a client should we request a client certificate + localCertificates []tls.Certificate + nameToCertificate map[string]*tls.Certificate + insecureSkipVerify bool + verifyPeerCertificate func(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error + rootCAs *x509.CertPool + clientCAs *x509.CertPool + retransmitInterval time.Duration + customCipherSuites func() []CipherSuite + + onFlightState func(flightVal, handshakeState) + log logging.LeveledLogger + keyLogWriter io.Writer + + initialEpoch uint16 + + mu sync.Mutex +} + +type flightConn interface { + notify(ctx context.Context, level alert.Level, desc alert.Description) error + writePackets(context.Context, []*packet) error + recvHandshake() <-chan chan struct{} + setLocalEpoch(epoch uint16) + handleQueuedPackets(context.Context) error +} + +func (c *handshakeConfig) writeKeyLog(label string, clientRandom, secret []byte) { + if c.keyLogWriter == nil { + return + } + c.mu.Lock() + defer c.mu.Unlock() + _, err := c.keyLogWriter.Write([]byte(fmt.Sprintf("%s %x %x\n", label, clientRandom, secret))) + if err != nil { + c.log.Debugf("failed to write key log file: %s", err) + } +} + +func srvCliStr(isClient bool) string { + if isClient { + return "client" + } + return "server" +} + +func newHandshakeFSM( + s *State, cache *handshakeCache, cfg *handshakeConfig, + initialFlight flightVal, +) *handshakeFSM { + return &handshakeFSM{ + currentFlight: initialFlight, + state: s, + cache: cache, + cfg: cfg, + closed: make(chan struct{}), + } +} + +func (s *handshakeFSM) Run(ctx context.Context, c flightConn, initialState handshakeState) error { + state := initialState + defer func() { + close(s.closed) + }() + for { + s.cfg.log.Tracef("[handshake:%s] %s: %s", srvCliStr(s.state.isClient), s.currentFlight.String(), state.String()) + if s.cfg.onFlightState != nil { + s.cfg.onFlightState(s.currentFlight, state) + } + var err error + switch state { + case handshakePreparing: + state, err = s.prepare(ctx, c) + case handshakeSending: + state, err = s.send(ctx, c) + case handshakeWaiting: + state, err = s.wait(ctx, c) + case handshakeFinished: + state, err = s.finish(ctx, c) + default: + return errInvalidFSMTransition + } + if err != nil { + return err + } + } +} + +func (s *handshakeFSM) Done() <-chan struct{} { + return s.closed +} + +func (s *handshakeFSM) prepare(ctx context.Context, c flightConn) (handshakeState, error) { + s.flights = nil + // Prepare flights + var ( + a *alert.Alert + err error + pkts []*packet + ) + gen, retransmit, errFlight := s.currentFlight.getFlightGenerator() + if errFlight != nil { + err = errFlight + a = &alert.Alert{Level: alert.Fatal, Description: alert.InternalError} + } else { + pkts, a, err = gen(c, s.state, s.cache, s.cfg) + s.retransmit = retransmit + } + if a != nil { + if alertErr := c.notify(ctx, a.Level, a.Description); alertErr != nil { + if err != nil { + err = alertErr + } + } + } + if err != nil { + return handshakeErrored, err + } + + s.flights = pkts + epoch := s.cfg.initialEpoch + nextEpoch := epoch + for _, p := range s.flights { + p.record.Header.Epoch += epoch + if p.record.Header.Epoch > nextEpoch { + nextEpoch = p.record.Header.Epoch + } + if h, ok := p.record.Content.(*handshake.Handshake); ok { + h.Header.MessageSequence = uint16(s.state.handshakeSendSequence) + s.state.handshakeSendSequence++ + } + } + if epoch != nextEpoch { + s.cfg.log.Tracef("[handshake:%s] -> changeCipherSpec (epoch: %d)", srvCliStr(s.state.isClient), nextEpoch) + c.setLocalEpoch(nextEpoch) + } + return handshakeSending, nil +} + +func (s *handshakeFSM) send(ctx context.Context, c flightConn) (handshakeState, error) { + // Send flights + if err := c.writePackets(ctx, s.flights); err != nil { + return handshakeErrored, err + } + + if s.currentFlight.isLastSendFlight() { + return handshakeFinished, nil + } + return handshakeWaiting, nil +} + +func (s *handshakeFSM) wait(ctx context.Context, c flightConn) (handshakeState, error) { //nolint:gocognit + parse, errFlight := s.currentFlight.getFlightParser() + if errFlight != nil { + if alertErr := c.notify(ctx, alert.Fatal, alert.InternalError); alertErr != nil { + if errFlight != nil { + return handshakeErrored, alertErr + } + } + return handshakeErrored, errFlight + } + + retransmitTimer := time.NewTimer(s.cfg.retransmitInterval) + for { + select { + case done := <-c.recvHandshake(): + nextFlight, alert, err := parse(ctx, c, s.state, s.cache, s.cfg) + close(done) + if alert != nil { + if alertErr := c.notify(ctx, alert.Level, alert.Description); alertErr != nil { + if err != nil { + err = alertErr + } + } + } + if err != nil { + return handshakeErrored, err + } + if nextFlight == 0 { + break + } + s.cfg.log.Tracef("[handshake:%s] %s -> %s", srvCliStr(s.state.isClient), s.currentFlight.String(), nextFlight.String()) + if nextFlight.isLastRecvFlight() && s.currentFlight == nextFlight { + return handshakeFinished, nil + } + s.currentFlight = nextFlight + return handshakePreparing, nil + + case <-retransmitTimer.C: + if !s.retransmit { + return handshakeWaiting, nil + } + return handshakeSending, nil + case <-ctx.Done(): + return handshakeErrored, ctx.Err() + } + } +} + +func (s *handshakeFSM) finish(ctx context.Context, c flightConn) (handshakeState, error) { + parse, errFlight := s.currentFlight.getFlightParser() + if errFlight != nil { + if alertErr := c.notify(ctx, alert.Fatal, alert.InternalError); alertErr != nil { + if errFlight != nil { + return handshakeErrored, alertErr + } + } + return handshakeErrored, errFlight + } + + retransmitTimer := time.NewTimer(s.cfg.retransmitInterval) + select { + case done := <-c.recvHandshake(): + nextFlight, alert, err := parse(ctx, c, s.state, s.cache, s.cfg) + close(done) + if alert != nil { + if alertErr := c.notify(ctx, alert.Level, alert.Description); alertErr != nil { + if err != nil { + err = alertErr + } + } + } + if err != nil { + return handshakeErrored, err + } + if nextFlight == 0 { + break + } + <-retransmitTimer.C + // Retransmit last flight + return handshakeSending, nil + + case <-ctx.Done(): + return handshakeErrored, ctx.Err() + } + return handshakeFinished, nil +} diff --git a/vendor/github.com/pion/dtls/v2/internal/ciphersuite/aes_128_ccm.go b/vendor/github.com/pion/dtls/v2/internal/ciphersuite/aes_128_ccm.go new file mode 100644 index 0000000..dcc5379 --- /dev/null +++ b/vendor/github.com/pion/dtls/v2/internal/ciphersuite/aes_128_ccm.go @@ -0,0 +1,108 @@ +package ciphersuite + +import ( + "crypto/sha256" + "fmt" + "hash" + "sync/atomic" + + "github.com/pion/dtls/v2/pkg/crypto/ciphersuite" + "github.com/pion/dtls/v2/pkg/crypto/clientcertificate" + "github.com/pion/dtls/v2/pkg/crypto/prf" + "github.com/pion/dtls/v2/pkg/protocol/recordlayer" +) + +// Aes128Ccm is a base class used by multiple AES-CCM Ciphers +type Aes128Ccm struct { + ccm atomic.Value // *cryptoCCM + clientCertificateType clientcertificate.Type + id ID + psk bool + cryptoCCMTagLen ciphersuite.CCMTagLen +} + +func newAes128Ccm(clientCertificateType clientcertificate.Type, id ID, psk bool, cryptoCCMTagLen ciphersuite.CCMTagLen) *Aes128Ccm { + return &Aes128Ccm{ + clientCertificateType: clientCertificateType, + id: id, + psk: psk, + cryptoCCMTagLen: cryptoCCMTagLen, + } +} + +// CertificateType returns what type of certificate this CipherSuite exchanges +func (c *Aes128Ccm) CertificateType() clientcertificate.Type { + return c.clientCertificateType +} + +// ID returns the ID of the CipherSuite +func (c *Aes128Ccm) ID() ID { + return c.id +} + +func (c *Aes128Ccm) String() string { + return c.id.String() +} + +// HashFunc returns the hashing func for this CipherSuite +func (c *Aes128Ccm) HashFunc() func() hash.Hash { + return sha256.New +} + +// AuthenticationType controls what authentication method is using during the handshake +func (c *Aes128Ccm) AuthenticationType() AuthenticationType { + if c.psk { + return AuthenticationTypePreSharedKey + } + return AuthenticationTypeCertificate +} + +// IsInitialized returns if the CipherSuite has keying material and can +// encrypt/decrypt packets +func (c *Aes128Ccm) IsInitialized() bool { + return c.ccm.Load() != nil +} + +// Init initializes the internal Cipher with keying material +func (c *Aes128Ccm) Init(masterSecret, clientRandom, serverRandom []byte, isClient bool) error { + const ( + prfMacLen = 0 + prfKeyLen = 16 + 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 *Aes128Ccm) Encrypt(pkt *recordlayer.RecordLayer, raw []byte) ([]byte, error) { + ccm := c.ccm.Load() + if ccm == nil { + return nil, fmt.Errorf("%w, unable to encrypt", errCipherSuiteNotInit) + } + + return ccm.(*ciphersuite.CCM).Encrypt(pkt, raw) +} + +// Decrypt decrypts a single TLS RecordLayer +func (c *Aes128Ccm) Decrypt(raw []byte) ([]byte, error) { + ccm := c.ccm.Load() + if ccm == nil { + return nil, fmt.Errorf("%w, unable to decrypt", errCipherSuiteNotInit) + } + + return ccm.(*ciphersuite.CCM).Decrypt(raw) +} diff --git a/vendor/github.com/pion/dtls/v2/internal/ciphersuite/ciphersuite.go b/vendor/github.com/pion/dtls/v2/internal/ciphersuite/ciphersuite.go new file mode 100644 index 0000000..3a4fb43 --- /dev/null +++ b/vendor/github.com/pion/dtls/v2/internal/ciphersuite/ciphersuite.go @@ -0,0 +1,71 @@ +// 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/v2/pkg/protocol" +) + +var errCipherSuiteNotInit = &protocol.TemporaryError{Err: errors.New("CipherSuite has not been initialized")} //nolint:goerr113 + +// ID is an ID for our supported CipherSuites +type ID uint16 + +func (i ID) String() string { + 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_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" + 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:golint,stylecheck + TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8 ID = 0xc0ae //nolint:golint,stylecheck + + // AES-128-GCM-SHA256 + TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 ID = 0xc02b //nolint:golint,stylecheck + TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 ID = 0xc02f //nolint:golint,stylecheck + + // AES-256-CBC-SHA + TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA ID = 0xc00a //nolint:golint,stylecheck + TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA ID = 0xc014 //nolint:golint,stylecheck + + TLS_PSK_WITH_AES_128_CCM ID = 0xc0a4 //nolint:golint,stylecheck + TLS_PSK_WITH_AES_128_CCM_8 ID = 0xc0a8 //nolint:golint,stylecheck + TLS_PSK_WITH_AES_128_GCM_SHA256 ID = 0x00a8 //nolint:golint,stylecheck + TLS_PSK_WITH_AES_128_CBC_SHA256 ID = 0x00ae //nolint:golint,stylecheck +) + +// AuthenticationType controls what authentication method is using during the handshake +type AuthenticationType int + +// AuthenticationType Enums +const ( + AuthenticationTypeCertificate AuthenticationType = iota + 1 + AuthenticationTypePreSharedKey + AuthenticationTypeAnonymous +) diff --git a/vendor/github.com/pion/dtls/v2/internal/ciphersuite/tls_ecdhe_ecdsa_with_aes_128_ccm.go b/vendor/github.com/pion/dtls/v2/internal/ciphersuite/tls_ecdhe_ecdsa_with_aes_128_ccm.go new file mode 100644 index 0000000..ac73556 --- /dev/null +++ b/vendor/github.com/pion/dtls/v2/internal/ciphersuite/tls_ecdhe_ecdsa_with_aes_128_ccm.go @@ -0,0 +1,11 @@ +package ciphersuite + +import ( + "github.com/pion/dtls/v2/pkg/crypto/ciphersuite" + "github.com/pion/dtls/v2/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) +} diff --git a/vendor/github.com/pion/dtls/v2/internal/ciphersuite/tls_ecdhe_ecdsa_with_aes_128_ccm8.go b/vendor/github.com/pion/dtls/v2/internal/ciphersuite/tls_ecdhe_ecdsa_with_aes_128_ccm8.go new file mode 100644 index 0000000..49b1a83 --- /dev/null +++ b/vendor/github.com/pion/dtls/v2/internal/ciphersuite/tls_ecdhe_ecdsa_with_aes_128_ccm8.go @@ -0,0 +1,11 @@ +package ciphersuite + +import ( + "github.com/pion/dtls/v2/pkg/crypto/ciphersuite" + "github.com/pion/dtls/v2/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) +} diff --git a/vendor/github.com/pion/dtls/v2/internal/ciphersuite/tls_ecdhe_ecdsa_with_aes_128_gcm_sha256.go b/vendor/github.com/pion/dtls/v2/internal/ciphersuite/tls_ecdhe_ecdsa_with_aes_128_gcm_sha256.go new file mode 100644 index 0000000..b491320 --- /dev/null +++ b/vendor/github.com/pion/dtls/v2/internal/ciphersuite/tls_ecdhe_ecdsa_with_aes_128_gcm_sha256.go @@ -0,0 +1,92 @@ +package ciphersuite + +import ( + "crypto/sha256" + "fmt" + "hash" + "sync/atomic" + + "github.com/pion/dtls/v2/pkg/crypto/ciphersuite" + "github.com/pion/dtls/v2/pkg/crypto/clientcertificate" + "github.com/pion/dtls/v2/pkg/crypto/prf" + "github.com/pion/dtls/v2/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 +} + +// 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 +} + +// 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 + ) + + keys, err := prf.GenerateEncryptionKeys(masterSecret, clientRandom, serverRandom, prfMacLen, prfKeyLen, prfIvLen, c.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 +} + +// Encrypt encrypts a single TLS RecordLayer +func (c *TLSEcdheEcdsaWithAes128GcmSha256) Encrypt(pkt *recordlayer.RecordLayer, raw []byte) ([]byte, error) { + gcm := c.gcm.Load() + if gcm == nil { + return nil, fmt.Errorf("%w, unable to encrypt", errCipherSuiteNotInit) + } + + return gcm.(*ciphersuite.GCM).Encrypt(pkt, raw) +} + +// Decrypt decrypts a single TLS RecordLayer +func (c *TLSEcdheEcdsaWithAes128GcmSha256) Decrypt(raw []byte) ([]byte, error) { + gcm := c.gcm.Load() + if gcm == nil { + return nil, fmt.Errorf("%w, unable to decrypt", errCipherSuiteNotInit) + } + + return gcm.(*ciphersuite.GCM).Decrypt(raw) +} diff --git a/vendor/github.com/pion/dtls/v2/internal/ciphersuite/tls_ecdhe_ecdsa_with_aes_256_cbc_sha.go b/vendor/github.com/pion/dtls/v2/internal/ciphersuite/tls_ecdhe_ecdsa_with_aes_256_cbc_sha.go new file mode 100644 index 0000000..f7a33ad --- /dev/null +++ b/vendor/github.com/pion/dtls/v2/internal/ciphersuite/tls_ecdhe_ecdsa_with_aes_256_cbc_sha.go @@ -0,0 +1,101 @@ +package ciphersuite + +import ( + "crypto/sha1" //nolint: gosec,gci + "crypto/sha256" + "fmt" + "hash" + "sync/atomic" + + "github.com/pion/dtls/v2/pkg/crypto/ciphersuite" + "github.com/pion/dtls/v2/pkg/crypto/clientcertificate" + "github.com/pion/dtls/v2/pkg/crypto/prf" + "github.com/pion/dtls/v2/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 +} + +// 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) { + cbc := c.cbc.Load() + if cbc == nil { // !c.isInitialized() + return nil, fmt.Errorf("%w, unable to encrypt", errCipherSuiteNotInit) + } + + return cbc.(*ciphersuite.CBC).Encrypt(pkt, raw) +} + +// Decrypt decrypts a single TLS RecordLayer +func (c *TLSEcdheEcdsaWithAes256CbcSha) Decrypt(raw []byte) ([]byte, error) { + cbc := c.cbc.Load() + if cbc == nil { // !c.isInitialized() + return nil, fmt.Errorf("%w, unable to decrypt", errCipherSuiteNotInit) + } + + return cbc.(*ciphersuite.CBC).Decrypt(raw) +} diff --git a/vendor/github.com/pion/dtls/v2/internal/ciphersuite/tls_ecdhe_rsa_with_aes_128_gcm_sha256.go b/vendor/github.com/pion/dtls/v2/internal/ciphersuite/tls_ecdhe_rsa_with_aes_128_gcm_sha256.go new file mode 100644 index 0000000..70400c3 --- /dev/null +++ b/vendor/github.com/pion/dtls/v2/internal/ciphersuite/tls_ecdhe_rsa_with_aes_128_gcm_sha256.go @@ -0,0 +1,22 @@ +package ciphersuite + +import "github.com/pion/dtls/v2/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" +} diff --git a/vendor/github.com/pion/dtls/v2/internal/ciphersuite/tls_ecdhe_rsa_with_aes_256_cbc_sha.go b/vendor/github.com/pion/dtls/v2/internal/ciphersuite/tls_ecdhe_rsa_with_aes_256_cbc_sha.go new file mode 100644 index 0000000..0d82dc3 --- /dev/null +++ b/vendor/github.com/pion/dtls/v2/internal/ciphersuite/tls_ecdhe_rsa_with_aes_256_cbc_sha.go @@ -0,0 +1,22 @@ +package ciphersuite + +import "github.com/pion/dtls/v2/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" +} diff --git a/vendor/github.com/pion/dtls/v2/internal/ciphersuite/tls_psk_with_aes_128_cbc_sha256.go b/vendor/github.com/pion/dtls/v2/internal/ciphersuite/tls_psk_with_aes_128_cbc_sha256.go new file mode 100644 index 0000000..43e5e38 --- /dev/null +++ b/vendor/github.com/pion/dtls/v2/internal/ciphersuite/tls_psk_with_aes_128_cbc_sha256.go @@ -0,0 +1,100 @@ +package ciphersuite + +import ( + "crypto/sha256" + "fmt" + "hash" + "sync/atomic" + + "github.com/pion/dtls/v2/pkg/crypto/ciphersuite" + "github.com/pion/dtls/v2/pkg/crypto/clientcertificate" + "github.com/pion/dtls/v2/pkg/crypto/prf" + "github.com/pion/dtls/v2/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) +} + +// 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) { + cbc := c.cbc.Load() + if cbc == nil { // !c.isInitialized() + return nil, fmt.Errorf("%w, unable to decrypt", errCipherSuiteNotInit) + } + + return cbc.(*ciphersuite.CBC).Encrypt(pkt, raw) +} + +// Decrypt decrypts a single TLS RecordLayer +func (c *TLSPskWithAes128CbcSha256) Decrypt(raw []byte) ([]byte, error) { + cbc := c.cbc.Load() + if cbc == nil { // !c.isInitialized() + return nil, fmt.Errorf("%w, unable to decrypt", errCipherSuiteNotInit) + } + + return cbc.(*ciphersuite.CBC).Decrypt(raw) +} diff --git a/vendor/github.com/pion/dtls/v2/internal/ciphersuite/tls_psk_with_aes_128_ccm.go b/vendor/github.com/pion/dtls/v2/internal/ciphersuite/tls_psk_with_aes_128_ccm.go new file mode 100644 index 0000000..8c13bb1 --- /dev/null +++ b/vendor/github.com/pion/dtls/v2/internal/ciphersuite/tls_psk_with_aes_128_ccm.go @@ -0,0 +1,11 @@ +package ciphersuite + +import ( + "github.com/pion/dtls/v2/pkg/crypto/ciphersuite" + "github.com/pion/dtls/v2/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) +} diff --git a/vendor/github.com/pion/dtls/v2/internal/ciphersuite/tls_psk_with_aes_128_ccm8.go b/vendor/github.com/pion/dtls/v2/internal/ciphersuite/tls_psk_with_aes_128_ccm8.go new file mode 100644 index 0000000..d04abb4 --- /dev/null +++ b/vendor/github.com/pion/dtls/v2/internal/ciphersuite/tls_psk_with_aes_128_ccm8.go @@ -0,0 +1,11 @@ +package ciphersuite + +import ( + "github.com/pion/dtls/v2/pkg/crypto/ciphersuite" + "github.com/pion/dtls/v2/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) +} diff --git a/vendor/github.com/pion/dtls/v2/internal/ciphersuite/tls_psk_with_aes_128_gcm_sha256.go b/vendor/github.com/pion/dtls/v2/internal/ciphersuite/tls_psk_with_aes_128_gcm_sha256.go new file mode 100644 index 0000000..5f10335 --- /dev/null +++ b/vendor/github.com/pion/dtls/v2/internal/ciphersuite/tls_psk_with_aes_128_gcm_sha256.go @@ -0,0 +1,27 @@ +package ciphersuite + +import "github.com/pion/dtls/v2/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) +} + +// 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 +} diff --git a/vendor/github.com/pion/dtls/v2/internal/closer/closer.go b/vendor/github.com/pion/dtls/v2/internal/closer/closer.go new file mode 100644 index 0000000..b99e13e --- /dev/null +++ b/vendor/github.com/pion/dtls/v2/internal/closer/closer.go @@ -0,0 +1,45 @@ +// 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 + 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() +} diff --git a/vendor/github.com/pion/dtls/v2/internal/util/util.go b/vendor/github.com/pion/dtls/v2/internal/util/util.go new file mode 100644 index 0000000..746a670 --- /dev/null +++ b/vendor/github.com/pion/dtls/v2/internal/util/util.go @@ -0,0 +1,39 @@ +// Package util contains small helpers used across the repo +package util + +import ( + "encoding/binary" +) + +// 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 +} diff --git a/vendor/github.com/pion/dtls/v2/listener.go b/vendor/github.com/pion/dtls/v2/listener.go new file mode 100644 index 0000000..bf80345 --- /dev/null +++ b/vendor/github.com/pion/dtls/v2/listener.go @@ -0,0 +1,80 @@ +package dtls + +import ( + "net" + + "github.com/pion/dtls/v2/pkg/protocol" + "github.com/pion/dtls/v2/pkg/protocol/recordlayer" + "github.com/pion/udp" +) + +// Listen creates a DTLS listener +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 + }, + } + parent, err := lc.Listen(network, laddr) + if err != nil { + return nil, err + } + return &listener{ + config: config, + parent: parent, + }, nil +} + +// NewListener creates a DTLS listener which accepts connections from an inner Listener. +func NewListener(inner net.Listener, config *Config) (net.Listener, error) { + if err := validateConfig(config); err != nil { + return nil, err + } + + return &listener{ + config: config, + parent: inner, + }, nil +} + +// listener represents a DTLS listener +type listener struct { + config *Config + parent net.Listener +} + +// Accept waits for and returns the next connection to the listener. +// You have to either close or read on all connection that are created. +// Connection handshake will timeout using ConnectContextMaker in the Config. +// If you want to specify the timeout duration, set ConnectContextMaker. +func (l *listener) Accept() (net.Conn, error) { + c, err := l.parent.Accept() + if err != nil { + return nil, err + } + return Server(c, 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() +} diff --git a/vendor/github.com/pion/dtls/v2/packet.go b/vendor/github.com/pion/dtls/v2/packet.go new file mode 100644 index 0000000..8366a3c --- /dev/null +++ b/vendor/github.com/pion/dtls/v2/packet.go @@ -0,0 +1,9 @@ +package dtls + +import "github.com/pion/dtls/v2/pkg/protocol/recordlayer" + +type packet struct { + record *recordlayer.RecordLayer + shouldEncrypt bool + resetLocalSequenceNumber bool +} diff --git a/vendor/github.com/pion/dtls/v2/pkg/crypto/ccm/ccm.go b/vendor/github.com/pion/dtls/v2/pkg/crypto/ccm/ccm.go new file mode 100644 index 0000000..20e3436 --- /dev/null +++ b/vendor/github.com/pion/dtls/v2/pkg/crypto/ccm/ccm.go @@ -0,0 +1,251 @@ +// 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)} + 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 { + max := (uint64(1) << (8 * l)) - 1 + if m64 := uint64(math.MaxInt64) - uint64(tagsize); l > 8 || max > m64 { + max = m64 // The maximum lentgh on a 64bit arch + } + if max != uint64(int(max)) { + return math.MaxInt32 - tagsize // We have only 32bit int's + } + return int(max) +} + +// 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 { + 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 n := uint64(len(adata)); n > 0 { + // First adata block includes adata length + i := 2 + if n <= 0xfeff { + binary.BigEndian.PutUint16(block[:i], uint16(n)) + } else { + block[0] = 0xfe + block[1] = 0xff + if n < uint64(1<<32) { + i = 2 + 4 + binary.BigEndian.PutUint32(block[2:i], uint32(n)) + } else { + i = 2 + 8 + binary.BigEndian.PutUint64(block[2:i], n) + } + } + 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 +} diff --git a/vendor/github.com/pion/dtls/v2/pkg/crypto/ciphersuite/cbc.go b/vendor/github.com/pion/dtls/v2/pkg/crypto/ciphersuite/cbc.go new file mode 100644 index 0000000..8ff1634 --- /dev/null +++ b/vendor/github.com/pion/dtls/v2/pkg/crypto/ciphersuite/cbc.go @@ -0,0 +1,164 @@ +package ciphersuite + +import ( //nolint:gci + "crypto/aes" + "crypto/cipher" + "crypto/hmac" + "crypto/rand" + "encoding/binary" + "hash" + + "github.com/pion/dtls/v2/internal/util" + "github.com/pion/dtls/v2/pkg/crypto/prf" + "github.com/pion/dtls/v2/pkg/protocol" + "github.com/pion/dtls/v2/pkg/protocol/recordlayer" +) + +// 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, h 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 + } + + return &CBC{ + writeCBC: cipher.NewCBCEncrypter(writeBlock, localWriteIV).(cbcMode), + writeMac: localMac, + + readCBC: cipher.NewCBCDecrypter(readBlock, remoteWriteIV).(cbcMode), + readMac: remoteMac, + h: h, + }, nil +} + +// Encrypt encrypt a DTLS RecordLayer message +func (c *CBC) Encrypt(pkt *recordlayer.RecordLayer, raw []byte) ([]byte, error) { + payload := raw[recordlayer.HeaderSize:] + raw = raw[:recordlayer.HeaderSize] + blockSize := c.writeCBC.BlockSize() + + // Generate + Append MAC + h := pkt.Header + + 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...) + + // Prepend unencrypte header with encrypted payload + raw = append(raw, payload...) + + // Update recordLayer size to include IV+MAC+Padding + binary.BigEndian.PutUint16(raw[recordlayer.HeaderSize-2:], uint16(len(raw)-recordlayer.HeaderSize)) + + return raw, nil +} + +// Decrypt decrypts a DTLS RecordLayer message +func (c *CBC) Decrypt(in []byte) ([]byte, error) { + body := in[recordlayer.HeaderSize:] + blockSize := c.readCBC.BlockSize() + mac := c.h() + + var h recordlayer.Header + err := h.Unmarshal(in) + switch { + case err != nil: + return nil, err + case h.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] + actualMAC, err := c.hmac(h.Epoch, h.SequenceNumber, h.ContentType, h.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[:recordlayer.HeaderSize], 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) { + h := 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))) + + if _, err := h.Write(msg); err != nil { + return nil, err + } else if _, err := h.Write(payload); err != nil { + return nil, err + } + + return h.Sum(nil), nil +} diff --git a/vendor/github.com/pion/dtls/v2/pkg/crypto/ciphersuite/ccm.go b/vendor/github.com/pion/dtls/v2/pkg/crypto/ciphersuite/ccm.go new file mode 100644 index 0000000..354b1cc --- /dev/null +++ b/vendor/github.com/pion/dtls/v2/pkg/crypto/ciphersuite/ccm.go @@ -0,0 +1,104 @@ +package ciphersuite + +import ( + "crypto/aes" + "crypto/rand" + "encoding/binary" + "fmt" + + "github.com/pion/dtls/v2/pkg/crypto/ccm" + "github.com/pion/dtls/v2/pkg/protocol" + "github.com/pion/dtls/v2/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 { + localCCM, remoteCCM ccm.CCM + localWriteIV, remoteWriteIV []byte + tagLen CCMTagLen +} + +// 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{ + localCCM: localCCM, + localWriteIV: localWriteIV, + remoteCCM: remoteCCM, + remoteWriteIV: remoteWriteIV, + tagLen: tagLen, + }, nil +} + +// Encrypt encrypt a DTLS RecordLayer message +func (c *CCM) Encrypt(pkt *recordlayer.RecordLayer, raw []byte) ([]byte, error) { + payload := raw[recordlayer.HeaderSize:] + raw = raw[:recordlayer.HeaderSize] + + nonce := append(append([]byte{}, c.localWriteIV[:4]...), make([]byte, 8)...) + if _, err := rand.Read(nonce[4:]); err != nil { + return nil, err + } + + additionalData := generateAEADAdditionalData(&pkt.Header, len(payload)) + encryptedPayload := c.localCCM.Seal(nil, nonce, payload, additionalData) + + encryptedPayload = append(nonce[4:], encryptedPayload...) + raw = append(raw, encryptedPayload...) + + // Update recordLayer size to include explicit nonce + binary.BigEndian.PutUint16(raw[recordlayer.HeaderSize-2:], uint16(len(raw)-recordlayer.HeaderSize)) + return raw, nil +} + +// Decrypt decrypts a DTLS RecordLayer message +func (c *CCM) Decrypt(in []byte) ([]byte, error) { + var h recordlayer.Header + err := h.Unmarshal(in) + switch { + case err != nil: + return nil, err + case h.ContentType == protocol.ContentTypeChangeCipherSpec: + // Nothing to encrypt with ChangeCipherSpec + return in, nil + case len(in) <= (8 + recordlayer.HeaderSize): + return nil, errNotEnoughRoomForNonce + } + + nonce := append(append([]byte{}, c.remoteWriteIV[:4]...), in[recordlayer.HeaderSize:recordlayer.HeaderSize+8]...) + out := in[recordlayer.HeaderSize+8:] + + additionalData := generateAEADAdditionalData(&h, len(out)-int(c.tagLen)) + out, err = c.remoteCCM.Open(out[:0], nonce, out, additionalData) + if err != nil { + return nil, fmt.Errorf("%w: %v", errDecryptPacket, err) + } + return append(in[:recordlayer.HeaderSize], out...), nil +} diff --git a/vendor/github.com/pion/dtls/v2/pkg/crypto/ciphersuite/ciphersuite.go b/vendor/github.com/pion/dtls/v2/pkg/crypto/ciphersuite/ciphersuite.go new file mode 100644 index 0000000..72beffd --- /dev/null +++ b/vendor/github.com/pion/dtls/v2/pkg/crypto/ciphersuite/ciphersuite.go @@ -0,0 +1,72 @@ +// Package ciphersuite provides the crypto operations needed for a DTLS CipherSuite +package ciphersuite + +import ( + "encoding/binary" + "errors" + + "github.com/pion/dtls/v2/pkg/protocol" + "github.com/pion/dtls/v2/pkg/protocol/recordlayer" +) + +var ( + errNotEnoughRoomForNonce = &protocol.InternalError{Err: errors.New("buffer not long enough to contain nonce")} //nolint:goerr113 + errDecryptPacket = &protocol.TemporaryError{Err: errors.New("failed to decrypt packet")} //nolint:goerr113 + errInvalidMAC = &protocol.TemporaryError{Err: errors.New("invalid mac")} //nolint:goerr113 +) + +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 + binary.BigEndian.PutUint16(additionalData[len(additionalData)-2:], uint16(payloadLen)) + + return additionalData[:] +} + +// 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) + // if len(payload) >= (paddingLen - 1) then the MSB of t is zero + good = byte(int32(^t) >> 31) + + // The maximum possible padding length plus the actual length field + toCheck := 256 + // The length of the padded data is public, so we can use an if here + if toCheck > len(payload) { + toCheck = len(payload) + } + + for i := 0; i < toCheck; i++ { + t := uint(paddingLen) - uint(i) + // if i <= paddingLen then the MSB of t is zero + mask := byte(int32(^t) >> 31) + 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) + + toRemove = int(paddingLen) + 1 + + return toRemove, good +} diff --git a/vendor/github.com/pion/dtls/v2/pkg/crypto/ciphersuite/gcm.go b/vendor/github.com/pion/dtls/v2/pkg/crypto/ciphersuite/gcm.go new file mode 100644 index 0000000..af986d4 --- /dev/null +++ b/vendor/github.com/pion/dtls/v2/pkg/crypto/ciphersuite/gcm.go @@ -0,0 +1,100 @@ +package ciphersuite + +import ( + "crypto/aes" + "crypto/cipher" + "crypto/rand" + "encoding/binary" + "fmt" + + "github.com/pion/dtls/v2/pkg/protocol" + "github.com/pion/dtls/v2/pkg/protocol/recordlayer" +) + +const ( + gcmTagLength = 16 + gcmNonceLength = 12 +) + +// GCM Provides an API to Encrypt/Decrypt DTLS 1.2 Packets +type GCM struct { + localGCM, remoteGCM cipher.AEAD + localWriteIV, remoteWriteIV []byte +} + +// 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{ + localGCM: localGCM, + localWriteIV: localWriteIV, + remoteGCM: remoteGCM, + remoteWriteIV: remoteWriteIV, + }, nil +} + +// Encrypt encrypt a DTLS RecordLayer message +func (g *GCM) Encrypt(pkt *recordlayer.RecordLayer, raw []byte) ([]byte, error) { + payload := raw[recordlayer.HeaderSize:] + raw = raw[:recordlayer.HeaderSize] + + nonce := make([]byte, gcmNonceLength) + copy(nonce, g.localWriteIV[:4]) + if _, err := rand.Read(nonce[4:]); err != nil { + return nil, err + } + + additionalData := generateAEADAdditionalData(&pkt.Header, len(payload)) + encryptedPayload := g.localGCM.Seal(nil, nonce, payload, additionalData) + r := make([]byte, len(raw)+len(nonce[4:])+len(encryptedPayload)) + copy(r, raw) + copy(r[len(raw):], nonce[4:]) + copy(r[len(raw)+len(nonce[4:]):], encryptedPayload) + + // Update recordLayer size to include explicit nonce + binary.BigEndian.PutUint16(r[recordlayer.HeaderSize-2:], uint16(len(r)-recordlayer.HeaderSize)) + return r, nil +} + +// Decrypt decrypts a DTLS RecordLayer message +func (g *GCM) Decrypt(in []byte) ([]byte, error) { + var h recordlayer.Header + err := h.Unmarshal(in) + switch { + case err != nil: + return nil, err + case h.ContentType == protocol.ContentTypeChangeCipherSpec: + // Nothing to encrypt with ChangeCipherSpec + return in, nil + case len(in) <= (8 + recordlayer.HeaderSize): + return nil, errNotEnoughRoomForNonce + } + + nonce := make([]byte, 0, gcmNonceLength) + nonce = append(append(nonce, g.remoteWriteIV[:4]...), in[recordlayer.HeaderSize:recordlayer.HeaderSize+8]...) + out := in[recordlayer.HeaderSize+8:] + + additionalData := generateAEADAdditionalData(&h, len(out)-gcmTagLength) + out, err = g.remoteGCM.Open(out[:0], nonce, out, additionalData) + if err != nil { + return nil, fmt.Errorf("%w: %v", errDecryptPacket, err) + } + return append(in[:recordlayer.HeaderSize], out...), nil +} diff --git a/vendor/github.com/pion/dtls/v2/pkg/crypto/clientcertificate/client_certificate.go b/vendor/github.com/pion/dtls/v2/pkg/crypto/clientcertificate/client_certificate.go new file mode 100644 index 0000000..c222c01 --- /dev/null +++ b/vendor/github.com/pion/dtls/v2/pkg/crypto/clientcertificate/client_certificate.go @@ -0,0 +1,22 @@ +// 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, + } +} diff --git a/vendor/github.com/pion/dtls/v2/pkg/crypto/elliptic/elliptic.go b/vendor/github.com/pion/dtls/v2/pkg/crypto/elliptic/elliptic.go new file mode 100644 index 0000000..5b0e4fa --- /dev/null +++ b/vendor/github.com/pion/dtls/v2/pkg/crypto/elliptic/elliptic.go @@ -0,0 +1,99 @@ +// Package elliptic provides elliptic curve cryptography for DTLS +package elliptic + +import ( + "crypto/elliptic" + "crypto/rand" + "errors" + + "golang.org/x/crypto/curve25519" +) + +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 +) + +// 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(c Curve) (*Keypair, error) { + switch c { //nolint:golint + case X25519: + tmp := make([]byte, 32) + if _, err := rand.Read(tmp); err != nil { + return nil, err + } + + var public, private [32]byte + copy(private[:], tmp) + + curve25519.ScalarBaseMult(&public, &private) + return &Keypair{X25519, public[:], private[:]}, nil + case P256: + return ellipticCurveKeypair(P256, elliptic.P256(), elliptic.P256()) + case P384: + return ellipticCurveKeypair(P384, elliptic.P384(), elliptic.P384()) + default: + return nil, errInvalidNamedCurve + } +} + +func ellipticCurveKeypair(nc Curve, c1, c2 elliptic.Curve) (*Keypair, error) { + privateKey, x, y, err := elliptic.GenerateKey(c1, rand.Reader) + if err != nil { + return nil, err + } + + return &Keypair{nc, elliptic.Marshal(c2, x, y), privateKey}, nil +} diff --git a/vendor/github.com/pion/dtls/v2/pkg/crypto/fingerprint/fingerprint.go b/vendor/github.com/pion/dtls/v2/pkg/crypto/fingerprint/fingerprint.go new file mode 100644 index 0000000..215b44e --- /dev/null +++ b/vendor/github.com/pion/dtls/v2/pkg/crypto/fingerprint/fingerprint.go @@ -0,0 +1,50 @@ +// 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 := []byte(fmt.Sprintf("%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 +} diff --git a/vendor/github.com/pion/dtls/v2/pkg/crypto/fingerprint/hash.go b/vendor/github.com/pion/dtls/v2/pkg/crypto/fingerprint/hash.go new file mode 100644 index 0000000..09107db --- /dev/null +++ b/vendor/github.com/pion/dtls/v2/pkg/crypto/fingerprint/hash.go @@ -0,0 +1,37 @@ +package fingerprint + +import ( + "crypto" + "errors" +) + +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()[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 +} diff --git a/vendor/github.com/pion/dtls/v2/pkg/crypto/hash/hash.go b/vendor/github.com/pion/dtls/v2/pkg/crypto/hash/hash.go new file mode 100644 index 0000000..660326f --- /dev/null +++ b/vendor/github.com/pion/dtls/v2/pkg/crypto/hash/hash.go @@ -0,0 +1,126 @@ +// 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: {}, + } +} diff --git a/vendor/github.com/pion/dtls/v2/pkg/crypto/prf/prf.go b/vendor/github.com/pion/dtls/v2/pkg/crypto/prf/prf.go new file mode 100644 index 0000000..d33df19 --- /dev/null +++ b/vendor/github.com/pion/dtls/v2/pkg/crypto/prf/prf.go @@ -0,0 +1,224 @@ +// Package prf implements TLS 1.2 Pseudorandom functions +package prf + +import ( //nolint:gci + ellipticStdlib "crypto/elliptic" + "crypto/hmac" + "encoding/binary" + "errors" + "fmt" + "hash" + "math" + + "github.com/pion/dtls/v2/pkg/crypto/elliptic" + "github.com/pion/dtls/v2/pkg/protocol" + "golang.org/x/crypto/curve25519" +) + +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:goerr113 + +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)) + + out := append(make([]byte, 2+pskLen+2), psk...) + binary.BigEndian.PutUint16(out, pskLen) + binary.BigEndian.PutUint16(out[2+pskLen:], pskLen) + + return out +} + +// PreMasterSecret implements TLS 1.2 Premaster Secret generation given a keypair and a curve +func PreMasterSecret(publicKey, privateKey []byte, curve elliptic.Curve) ([]byte, error) { + switch curve { + case elliptic.X25519: + return curve25519.X25519(privateKey, publicKey) + case elliptic.P256: + return ellipticCurvePreMasterSecret(publicKey, privateKey, ellipticStdlib.P256(), ellipticStdlib.P256()) + case elliptic.P384: + return ellipticCurvePreMasterSecret(publicKey, privateKey, ellipticStdlib.P384(), ellipticStdlib.P384()) + default: + return nil, errInvalidNamedCurve + } +} + +func ellipticCurvePreMasterSecret(publicKey, privateKey []byte, c1, c2 ellipticStdlib.Curve) ([]byte, error) { + x, y := ellipticStdlib.Unmarshal(c1, publicKey) + if x == nil || y == nil { + return nil, errInvalidNamedCurve + } + + result, _ := c2.ScalarMult(x, y, privateKey) + preMasterSecret := make([]byte, (c2.Params().BitSize+7)>>3) + resultBytes := result.Bytes() + copy(preMasterSecret[len(preMasterSecret)-len(resultBytes):], resultBytes) + return preMasterSecret, nil +} + +// 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, h HashFunc) ([]byte, error) { + hmacSHA256 := func(key, data []byte) ([]byte, error) { + mac := hmac.New(h, 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(h().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) +} diff --git a/vendor/github.com/pion/dtls/v2/pkg/crypto/signature/signature.go b/vendor/github.com/pion/dtls/v2/pkg/crypto/signature/signature.go new file mode 100644 index 0000000..d9150eb --- /dev/null +++ b/vendor/github.com/pion/dtls/v2/pkg/crypto/signature/signature.go @@ -0,0 +1,24 @@ +// Package signature provides our implemented Signature Algorithms +package signature + +// 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 +) + +// Algorithms returns all implemented Signature Algorithms +func Algorithms() map[Algorithm]struct{} { + return map[Algorithm]struct{}{ + Anonymous: {}, + RSA: {}, + ECDSA: {}, + Ed25519: {}, + } +} diff --git a/vendor/github.com/pion/dtls/v2/pkg/crypto/signaturehash/errors.go b/vendor/github.com/pion/dtls/v2/pkg/crypto/signaturehash/errors.go new file mode 100644 index 0000000..9d9d3b3 --- /dev/null +++ b/vendor/github.com/pion/dtls/v2/pkg/crypto/signaturehash/errors.go @@ -0,0 +1,9 @@ +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") +) diff --git a/vendor/github.com/pion/dtls/v2/pkg/crypto/signaturehash/signaturehash.go b/vendor/github.com/pion/dtls/v2/pkg/crypto/signaturehash/signaturehash.go new file mode 100644 index 0000000..f2017bc --- /dev/null +++ b/vendor/github.com/pion/dtls/v2/pkg/crypto/signaturehash/signaturehash.go @@ -0,0 +1,93 @@ +// Package signaturehash provides the SignatureHashAlgorithm as defined in TLS 1.2 +package signaturehash + +import ( + "crypto" + "crypto/ecdsa" + "crypto/ed25519" + "crypto/rsa" + "crypto/tls" + + "github.com/pion/dtls/v2/pkg/crypto/hash" + "github.com/pion/dtls/v2/pkg/crypto/signature" + "golang.org/x/xerrors" +) + +// 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 are all the know SignatureHash Algorithms +func Algorithms() []Algorithm { + return []Algorithm{ + {hash.SHA256, signature.ECDSA}, + {hash.SHA384, signature.ECDSA}, + {hash.SHA512, signature.ECDSA}, + {hash.SHA256, signature.RSA}, + {hash.SHA384, signature.RSA}, + {hash.SHA512, signature.RSA}, + {hash.Ed25519, signature.Ed25519}, + } +} + +// SelectSignatureScheme returns most preferred and compatible scheme. +func SelectSignatureScheme(sigs []Algorithm, privateKey crypto.PrivateKey) (Algorithm, error) { + for _, ss := range sigs { + if ss.isCompatible(privateKey) { + return ss, nil + } + } + return Algorithm{}, errNoAvailableSignatureSchemes +} + +// isCompatible checks that given private key is compatible with the signature scheme. +func (a *Algorithm) isCompatible(privateKey crypto.PrivateKey) bool { + switch privateKey.(type) { + case ed25519.PrivateKey: + return a.Signature == signature.Ed25519 + case *ecdsa.PrivateKey: + return a.Signature == signature.ECDSA + case *rsa.PrivateKey: + return a.Signature == signature.RSA + default: + return false + } +} + +// ParseSignatureSchemes translates []tls.SignatureScheme to []signatureHashAlgorithm. +// It returns default signature scheme list if no SignatureScheme is passed. +func ParseSignatureSchemes(sigs []tls.SignatureScheme, insecureHashes bool) ([]Algorithm, error) { + if len(sigs) == 0 { + return Algorithms(), nil + } + out := []Algorithm{} + for _, ss := range sigs { + sig := signature.Algorithm(ss & 0xFF) + if _, ok := signature.Algorithms()[sig]; !ok { + return nil, + xerrors.Errorf("SignatureScheme %04x: %w", ss, errInvalidSignatureAlgorithm) + } + h := hash.Algorithm(ss >> 8) + if _, ok := hash.Algorithms()[h]; !ok || (ok && h == hash.None) { + return nil, xerrors.Errorf("SignatureScheme %04x: %w", ss, errInvalidHashAlgorithm) + } + if h.Insecure() && !insecureHashes { + continue + } + out = append(out, Algorithm{ + Hash: h, + Signature: sig, + }) + } + + if len(out) == 0 { + return nil, errNoAvailableSignatureSchemes + } + + return out, nil +} diff --git a/vendor/github.com/pion/dtls/v2/pkg/protocol/alert/alert.go b/vendor/github.com/pion/dtls/v2/pkg/protocol/alert/alert.go new file mode 100644 index 0000000..9eb2e6a --- /dev/null +++ b/vendor/github.com/pion/dtls/v2/pkg/protocol/alert/alert.go @@ -0,0 +1,160 @@ +// Package alert implements TLS alert protocol https://tools.ietf.org/html/rfc5246#section-7.2 +package alert + +import ( + "errors" + "fmt" + + "github.com/pion/dtls/v2/pkg/protocol" +) + +var errBufferTooSmall = &protocol.TemporaryError{Err: errors.New("buffer is too small")} //nolint:goerr113 + +// 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 +) + +func (d Description) String() string { + 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" + 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) +} diff --git a/vendor/github.com/pion/dtls/v2/pkg/protocol/application_data.go b/vendor/github.com/pion/dtls/v2/pkg/protocol/application_data.go new file mode 100644 index 0000000..e5fd6f5 --- /dev/null +++ b/vendor/github.com/pion/dtls/v2/pkg/protocol/application_data.go @@ -0,0 +1,26 @@ +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 +} diff --git a/vendor/github.com/pion/dtls/v2/pkg/protocol/change_cipher_spec.go b/vendor/github.com/pion/dtls/v2/pkg/protocol/change_cipher_spec.go new file mode 100644 index 0000000..67b0052 --- /dev/null +++ b/vendor/github.com/pion/dtls/v2/pkg/protocol/change_cipher_spec.go @@ -0,0 +1,28 @@ +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 +} diff --git a/vendor/github.com/pion/dtls/v2/pkg/protocol/compression_method.go b/vendor/github.com/pion/dtls/v2/pkg/protocol/compression_method.go new file mode 100644 index 0000000..678e816 --- /dev/null +++ b/vendor/github.com/pion/dtls/v2/pkg/protocol/compression_method.go @@ -0,0 +1,48 @@ +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 +} diff --git a/vendor/github.com/pion/dtls/v2/pkg/protocol/content.go b/vendor/github.com/pion/dtls/v2/pkg/protocol/content.go new file mode 100644 index 0000000..47e5c96 --- /dev/null +++ b/vendor/github.com/pion/dtls/v2/pkg/protocol/content.go @@ -0,0 +1,21 @@ +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 +) + +// Content is the top level distinguisher for a DTLS Datagram +type Content interface { + ContentType() ContentType + Marshal() ([]byte, error) + Unmarshal(data []byte) error +} diff --git a/vendor/github.com/pion/dtls/v2/pkg/protocol/errors.go b/vendor/github.com/pion/dtls/v2/pkg/protocol/errors.go new file mode 100644 index 0000000..e52014a --- /dev/null +++ b/vendor/github.com/pion/dtls/v2/pkg/protocol/errors.go @@ -0,0 +1,104 @@ +package protocol + +import ( + "errors" + "fmt" + "net" +) + +var ( + errBufferTooSmall = &TemporaryError{Err: errors.New("buffer is too small")} //nolint:goerr113 + errInvalidCipherSpec = &FatalError{Err: errors.New("cipher spec invalid")} //nolint:goerr113 +) + +// FatalError indicates that the DTLS connection is no longer available. +// It is mainly caused by wrong configuration of server or client. +type FatalError 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 { + if netErr, ok := e.Err.(net.Error); ok { + return netErr.Timeout() + } + return false +} + +// Temporary implements net.Error.Temporary() +func (e *HandshakeError) Temporary() bool { + if netErr, ok := e.Err.(net.Error); ok { + return netErr.Temporary() + } + 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) } diff --git a/vendor/github.com/pion/dtls/v2/pkg/protocol/extension/errors.go b/vendor/github.com/pion/dtls/v2/pkg/protocol/extension/errors.go new file mode 100644 index 0000000..23ed9b2 --- /dev/null +++ b/vendor/github.com/pion/dtls/v2/pkg/protocol/extension/errors.go @@ -0,0 +1,14 @@ +package extension + +import ( + "errors" + + "github.com/pion/dtls/v2/pkg/protocol" +) + +var ( + errBufferTooSmall = &protocol.TemporaryError{Err: errors.New("buffer is too small")} //nolint:goerr113 + errInvalidExtensionType = &protocol.FatalError{Err: errors.New("invalid extension type")} //nolint:goerr113 + errInvalidSNIFormat = &protocol.FatalError{Err: errors.New("invalid server name format")} //nolint:goerr113 + errLengthMismatch = &protocol.InternalError{Err: errors.New("data length and declared length do not match")} //nolint:goerr113 +) diff --git a/vendor/github.com/pion/dtls/v2/pkg/protocol/extension/extension.go b/vendor/github.com/pion/dtls/v2/pkg/protocol/extension/extension.go new file mode 100644 index 0000000..39b1fc8 --- /dev/null +++ b/vendor/github.com/pion/dtls/v2/pkg/protocol/extension/extension.go @@ -0,0 +1,96 @@ +// Package extension implements the extension values in the ClientHello/ServerHello +package extension + +import "encoding/binary" + +// TypeValue is the 2 byte value for a TLS Extension as registered in the IANA +// +// https://www.iana.org/assignments/tls-extensiontype-values/tls-extensiontype-values.xhtml +type TypeValue uint16 + +// TypeValue constants +const ( + ServerNameTypeValue TypeValue = 0 + SupportedEllipticCurvesTypeValue TypeValue = 10 + SupportedPointFormatsTypeValue TypeValue = 11 + SupportedSignatureAlgorithmsTypeValue TypeValue = 13 + UseSRTPTypeValue TypeValue = 14 + UseExtendedMasterSecretTypeValue TypeValue = 23 + RenegotiationInfoTypeValue TypeValue = 65281 +) + +// Extension represents a single TLS extension +type Extension interface { + Marshal() ([]byte, error) + Unmarshal(data []byte) error + TypeValue() TypeValue +} + +// Unmarshal many extensions at once +func Unmarshal(buf []byte) ([]Extension, error) { + switch { + case len(buf) == 0: + return []Extension{}, nil + case len(buf) < 2: + return nil, errBufferTooSmall + } + + declaredLen := binary.BigEndian.Uint16(buf) + if len(buf)-2 != int(declaredLen) { + return nil, errLengthMismatch + } + + extensions := []Extension{} + unmarshalAndAppend := func(data []byte, e Extension) error { + err := e.Unmarshal(data) + if err != nil { + return err + } + extensions = append(extensions, e) + return nil + } + + for offset := 2; offset < len(buf); { + if len(buf) < (offset + 2) { + return nil, errBufferTooSmall + } + var err error + switch TypeValue(binary.BigEndian.Uint16(buf[offset:])) { + case ServerNameTypeValue: + err = unmarshalAndAppend(buf[offset:], &ServerName{}) + case SupportedEllipticCurvesTypeValue: + err = unmarshalAndAppend(buf[offset:], &SupportedEllipticCurves{}) + case UseSRTPTypeValue: + err = unmarshalAndAppend(buf[offset:], &UseSRTP{}) + case UseExtendedMasterSecretTypeValue: + err = unmarshalAndAppend(buf[offset:], &UseExtendedMasterSecret{}) + case RenegotiationInfoTypeValue: + err = unmarshalAndAppend(buf[offset:], &RenegotiationInfo{}) + default: + } + if err != nil { + return nil, err + } + if len(buf) < (offset + 4) { + return nil, errBufferTooSmall + } + extensionLength := binary.BigEndian.Uint16(buf[offset+2:]) + offset += (4 + int(extensionLength)) + } + return extensions, nil +} + +// Marshal many extensions at once +func Marshal(e []Extension) ([]byte, error) { + extensions := []byte{} + for _, e := range e { + raw, err := e.Marshal() + if err != nil { + return nil, err + } + extensions = append(extensions, raw...) + } + out := []byte{0x00, 0x00} + binary.BigEndian.PutUint16(out, uint16(len(extensions))) + return append(out, extensions...), nil +} diff --git a/vendor/github.com/pion/dtls/v2/pkg/protocol/extension/renegotiation_info.go b/vendor/github.com/pion/dtls/v2/pkg/protocol/extension/renegotiation_info.go new file mode 100644 index 0000000..8378c3d --- /dev/null +++ b/vendor/github.com/pion/dtls/v2/pkg/protocol/extension/renegotiation_info.go @@ -0,0 +1,43 @@ +package extension + +import "encoding/binary" + +const ( + renegotiationInfoHeaderSize = 5 +) + +// RenegotiationInfo allows a Client/Server to +// communicate their renegotation support +// +// https://tools.ietf.org/html/rfc5746 +type RenegotiationInfo struct { + RenegotiatedConnection uint8 +} + +// TypeValue returns the extension TypeValue +func (r RenegotiationInfo) TypeValue() TypeValue { + return RenegotiationInfoTypeValue +} + +// Marshal encodes the extension +func (r *RenegotiationInfo) Marshal() ([]byte, error) { + out := make([]byte, renegotiationInfoHeaderSize) + + binary.BigEndian.PutUint16(out, uint16(r.TypeValue())) + binary.BigEndian.PutUint16(out[2:], uint16(1)) // length + out[4] = r.RenegotiatedConnection + return out, nil +} + +// Unmarshal populates the extension from encoded data +func (r *RenegotiationInfo) Unmarshal(data []byte) error { + if len(data) < renegotiationInfoHeaderSize { + return errBufferTooSmall + } else if TypeValue(binary.BigEndian.Uint16(data)) != r.TypeValue() { + return errInvalidExtensionType + } + + r.RenegotiatedConnection = data[4] + + return nil +} diff --git a/vendor/github.com/pion/dtls/v2/pkg/protocol/extension/server_name.go b/vendor/github.com/pion/dtls/v2/pkg/protocol/extension/server_name.go new file mode 100644 index 0000000..a08033f --- /dev/null +++ b/vendor/github.com/pion/dtls/v2/pkg/protocol/extension/server_name.go @@ -0,0 +1,78 @@ +package extension + +import ( + "strings" + + "golang.org/x/crypto/cryptobyte" +) + +const serverNameTypeDNSHostName = 0 + +// ServerName allows the client to inform the server the specific +// name it wishs to contact. Useful if multiple DNS names resolve +// to one IP +// +// https://tools.ietf.org/html/rfc6066#section-3 +type ServerName struct { + ServerName string +} + +// TypeValue returns the extension TypeValue +func (s ServerName) TypeValue() TypeValue { + return ServerNameTypeValue +} + +// Marshal encodes the extension +func (s *ServerName) Marshal() ([]byte, error) { + var b cryptobyte.Builder + b.AddUint16(uint16(s.TypeValue())) + b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) { + b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) { + b.AddUint8(serverNameTypeDNSHostName) + b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) { + b.AddBytes([]byte(s.ServerName)) + }) + }) + }) + return b.Bytes() +} + +// Unmarshal populates the extension from encoded data +func (s *ServerName) Unmarshal(data []byte) error { + val := cryptobyte.String(data) + var extension uint16 + val.ReadUint16(&extension) + if TypeValue(extension) != s.TypeValue() { + return errInvalidExtensionType + } + + var extData cryptobyte.String + val.ReadUint16LengthPrefixed(&extData) + + var nameList cryptobyte.String + if !extData.ReadUint16LengthPrefixed(&nameList) || nameList.Empty() { + return errInvalidSNIFormat + } + for !nameList.Empty() { + var nameType uint8 + var serverName cryptobyte.String + if !nameList.ReadUint8(&nameType) || + !nameList.ReadUint16LengthPrefixed(&serverName) || + serverName.Empty() { + return errInvalidSNIFormat + } + if nameType != serverNameTypeDNSHostName { + continue + } + if len(s.ServerName) != 0 { + // Multiple names of the same name_type are prohibited. + return errInvalidSNIFormat + } + s.ServerName = string(serverName) + // An SNI value may not include a trailing dot. + if strings.HasSuffix(s.ServerName, ".") { + return errInvalidSNIFormat + } + } + return nil +} diff --git a/vendor/github.com/pion/dtls/v2/pkg/protocol/extension/srtp_protection_profile.go b/vendor/github.com/pion/dtls/v2/pkg/protocol/extension/srtp_protection_profile.go new file mode 100644 index 0000000..2c4d1d4 --- /dev/null +++ b/vendor/github.com/pion/dtls/v2/pkg/protocol/extension/srtp_protection_profile.go @@ -0,0 +1,21 @@ +package extension + +// SRTPProtectionProfile defines the parameters and options that are in effect for the SRTP processing +// https://tools.ietf.org/html/rfc5764#section-4.1.2 +type SRTPProtectionProfile uint16 + +const ( + SRTP_AES128_CM_HMAC_SHA1_80 SRTPProtectionProfile = 0x0001 // nolint + SRTP_AES128_CM_HMAC_SHA1_32 SRTPProtectionProfile = 0x0002 // nolint + SRTP_AEAD_AES_128_GCM SRTPProtectionProfile = 0x0007 // nolint + SRTP_AEAD_AES_256_GCM SRTPProtectionProfile = 0x0008 // nolint +) + +func srtpProtectionProfiles() map[SRTPProtectionProfile]bool { + return map[SRTPProtectionProfile]bool{ + SRTP_AES128_CM_HMAC_SHA1_80: true, + SRTP_AES128_CM_HMAC_SHA1_32: true, + SRTP_AEAD_AES_128_GCM: true, + SRTP_AEAD_AES_256_GCM: true, + } +} diff --git a/vendor/github.com/pion/dtls/v2/pkg/protocol/extension/supported_elliptic_curves.go b/vendor/github.com/pion/dtls/v2/pkg/protocol/extension/supported_elliptic_curves.go new file mode 100644 index 0000000..8f077fc --- /dev/null +++ b/vendor/github.com/pion/dtls/v2/pkg/protocol/extension/supported_elliptic_curves.go @@ -0,0 +1,62 @@ +package extension + +import ( + "encoding/binary" + + "github.com/pion/dtls/v2/pkg/crypto/elliptic" +) + +const ( + supportedGroupsHeaderSize = 6 +) + +// SupportedEllipticCurves allows a Client/Server to communicate +// what curves they both support +// +// https://tools.ietf.org/html/rfc8422#section-5.1.1 +type SupportedEllipticCurves struct { + EllipticCurves []elliptic.Curve +} + +// TypeValue returns the extension TypeValue +func (s SupportedEllipticCurves) TypeValue() TypeValue { + return SupportedEllipticCurvesTypeValue +} + +// Marshal encodes the extension +func (s *SupportedEllipticCurves) Marshal() ([]byte, error) { + out := make([]byte, supportedGroupsHeaderSize) + + binary.BigEndian.PutUint16(out, uint16(s.TypeValue())) + binary.BigEndian.PutUint16(out[2:], uint16(2+(len(s.EllipticCurves)*2))) + binary.BigEndian.PutUint16(out[4:], uint16(len(s.EllipticCurves)*2)) + + for _, v := range s.EllipticCurves { + out = append(out, []byte{0x00, 0x00}...) + binary.BigEndian.PutUint16(out[len(out)-2:], uint16(v)) + } + + return out, nil +} + +// Unmarshal populates the extension from encoded data +func (s *SupportedEllipticCurves) Unmarshal(data []byte) error { + if len(data) <= supportedGroupsHeaderSize { + return errBufferTooSmall + } else if TypeValue(binary.BigEndian.Uint16(data)) != s.TypeValue() { + return errInvalidExtensionType + } + + groupCount := int(binary.BigEndian.Uint16(data[4:]) / 2) + if supportedGroupsHeaderSize+(groupCount*2) > len(data) { + return errLengthMismatch + } + + for i := 0; i < groupCount; i++ { + supportedGroupID := elliptic.Curve(binary.BigEndian.Uint16(data[(supportedGroupsHeaderSize + (i * 2)):])) + if _, ok := elliptic.Curves()[supportedGroupID]; ok { + s.EllipticCurves = append(s.EllipticCurves, supportedGroupID) + } + } + return nil +} diff --git a/vendor/github.com/pion/dtls/v2/pkg/protocol/extension/supported_point_formats.go b/vendor/github.com/pion/dtls/v2/pkg/protocol/extension/supported_point_formats.go new file mode 100644 index 0000000..873d078 --- /dev/null +++ b/vendor/github.com/pion/dtls/v2/pkg/protocol/extension/supported_point_formats.go @@ -0,0 +1,62 @@ +package extension + +import ( + "encoding/binary" + + "github.com/pion/dtls/v2/pkg/crypto/elliptic" +) + +const ( + supportedPointFormatsSize = 5 +) + +// SupportedPointFormats allows a Client/Server to negotiate +// the EllipticCurvePointFormats +// +// https://tools.ietf.org/html/rfc4492#section-5.1.2 +type SupportedPointFormats struct { + PointFormats []elliptic.CurvePointFormat +} + +// TypeValue returns the extension TypeValue +func (s SupportedPointFormats) TypeValue() TypeValue { + return SupportedPointFormatsTypeValue +} + +// Marshal encodes the extension +func (s *SupportedPointFormats) Marshal() ([]byte, error) { + out := make([]byte, supportedPointFormatsSize) + + binary.BigEndian.PutUint16(out, uint16(s.TypeValue())) + binary.BigEndian.PutUint16(out[2:], uint16(1+(len(s.PointFormats)))) + out[4] = byte(len(s.PointFormats)) + + for _, v := range s.PointFormats { + out = append(out, byte(v)) + } + return out, nil +} + +// Unmarshal populates the extension from encoded data +func (s *SupportedPointFormats) Unmarshal(data []byte) error { + if len(data) <= supportedPointFormatsSize { + return errBufferTooSmall + } else if TypeValue(binary.BigEndian.Uint16(data)) != s.TypeValue() { + return errInvalidExtensionType + } + + pointFormatCount := int(binary.BigEndian.Uint16(data[4:])) + if supportedGroupsHeaderSize+(pointFormatCount) > len(data) { + return errLengthMismatch + } + + for i := 0; i < pointFormatCount; i++ { + p := elliptic.CurvePointFormat(data[supportedPointFormatsSize+i]) + switch p { + case elliptic.CurvePointFormatUncompressed: + s.PointFormats = append(s.PointFormats, p) + default: + } + } + return nil +} diff --git a/vendor/github.com/pion/dtls/v2/pkg/protocol/extension/supported_signature_algorithms.go b/vendor/github.com/pion/dtls/v2/pkg/protocol/extension/supported_signature_algorithms.go new file mode 100644 index 0000000..ee284f6 --- /dev/null +++ b/vendor/github.com/pion/dtls/v2/pkg/protocol/extension/supported_signature_algorithms.go @@ -0,0 +1,70 @@ +package extension + +import ( + "encoding/binary" + + "github.com/pion/dtls/v2/pkg/crypto/hash" + "github.com/pion/dtls/v2/pkg/crypto/signature" + "github.com/pion/dtls/v2/pkg/crypto/signaturehash" +) + +const ( + supportedSignatureAlgorithmsHeaderSize = 6 +) + +// SupportedSignatureAlgorithms allows a Client/Server to +// negotiate what SignatureHash Algorithms they both support +// +// https://tools.ietf.org/html/rfc5246#section-7.4.1.4.1 +type SupportedSignatureAlgorithms struct { + SignatureHashAlgorithms []signaturehash.Algorithm +} + +// TypeValue returns the extension TypeValue +func (s SupportedSignatureAlgorithms) TypeValue() TypeValue { + return SupportedSignatureAlgorithmsTypeValue +} + +// Marshal encodes the extension +func (s *SupportedSignatureAlgorithms) Marshal() ([]byte, error) { + out := make([]byte, supportedSignatureAlgorithmsHeaderSize) + + binary.BigEndian.PutUint16(out, uint16(s.TypeValue())) + binary.BigEndian.PutUint16(out[2:], uint16(2+(len(s.SignatureHashAlgorithms)*2))) + binary.BigEndian.PutUint16(out[4:], uint16(len(s.SignatureHashAlgorithms)*2)) + for _, v := range s.SignatureHashAlgorithms { + out = append(out, []byte{0x00, 0x00}...) + out[len(out)-2] = byte(v.Hash) + out[len(out)-1] = byte(v.Signature) + } + + return out, nil +} + +// Unmarshal populates the extension from encoded data +func (s *SupportedSignatureAlgorithms) Unmarshal(data []byte) error { + if len(data) <= supportedSignatureAlgorithmsHeaderSize { + return errBufferTooSmall + } else if TypeValue(binary.BigEndian.Uint16(data)) != s.TypeValue() { + return errInvalidExtensionType + } + + algorithmCount := int(binary.BigEndian.Uint16(data[4:]) / 2) + if supportedSignatureAlgorithmsHeaderSize+(algorithmCount*2) > len(data) { + return errLengthMismatch + } + for i := 0; i < algorithmCount; i++ { + supportedHashAlgorithm := hash.Algorithm(data[supportedSignatureAlgorithmsHeaderSize+(i*2)]) + supportedSignatureAlgorithm := signature.Algorithm(data[supportedSignatureAlgorithmsHeaderSize+(i*2)+1]) + if _, ok := hash.Algorithms()[supportedHashAlgorithm]; ok { + if _, ok := signature.Algorithms()[supportedSignatureAlgorithm]; ok { + s.SignatureHashAlgorithms = append(s.SignatureHashAlgorithms, signaturehash.Algorithm{ + Hash: supportedHashAlgorithm, + Signature: supportedSignatureAlgorithm, + }) + } + } + } + + return nil +} diff --git a/vendor/github.com/pion/dtls/v2/pkg/protocol/extension/use_master_secret.go b/vendor/github.com/pion/dtls/v2/pkg/protocol/extension/use_master_secret.go new file mode 100644 index 0000000..04ddc95 --- /dev/null +++ b/vendor/github.com/pion/dtls/v2/pkg/protocol/extension/use_master_secret.go @@ -0,0 +1,45 @@ +package extension + +import "encoding/binary" + +const ( + useExtendedMasterSecretHeaderSize = 4 +) + +// UseExtendedMasterSecret defines a TLS extension that contextually binds the +// master secret to a log of the full handshake that computes it, thus +// preventing MITM attacks. +type UseExtendedMasterSecret struct { + Supported bool +} + +// TypeValue returns the extension TypeValue +func (u UseExtendedMasterSecret) TypeValue() TypeValue { + return UseExtendedMasterSecretTypeValue +} + +// Marshal encodes the extension +func (u *UseExtendedMasterSecret) Marshal() ([]byte, error) { + if !u.Supported { + return []byte{}, nil + } + + out := make([]byte, useExtendedMasterSecretHeaderSize) + + binary.BigEndian.PutUint16(out, uint16(u.TypeValue())) + binary.BigEndian.PutUint16(out[2:], uint16(0)) // length + return out, nil +} + +// Unmarshal populates the extension from encoded data +func (u *UseExtendedMasterSecret) Unmarshal(data []byte) error { + if len(data) < useExtendedMasterSecretHeaderSize { + return errBufferTooSmall + } else if TypeValue(binary.BigEndian.Uint16(data)) != u.TypeValue() { + return errInvalidExtensionType + } + + u.Supported = true + + return nil +} diff --git a/vendor/github.com/pion/dtls/v2/pkg/protocol/extension/use_srtp.go b/vendor/github.com/pion/dtls/v2/pkg/protocol/extension/use_srtp.go new file mode 100644 index 0000000..729fa3a --- /dev/null +++ b/vendor/github.com/pion/dtls/v2/pkg/protocol/extension/use_srtp.go @@ -0,0 +1,59 @@ +package extension + +import "encoding/binary" + +const ( + useSRTPHeaderSize = 6 +) + +// UseSRTP allows a Client/Server to negotiate what SRTPProtectionProfiles +// they both support +// +// https://tools.ietf.org/html/rfc8422 +type UseSRTP struct { + ProtectionProfiles []SRTPProtectionProfile +} + +// TypeValue returns the extension TypeValue +func (u UseSRTP) TypeValue() TypeValue { + return UseSRTPTypeValue +} + +// Marshal encodes the extension +func (u *UseSRTP) Marshal() ([]byte, error) { + out := make([]byte, useSRTPHeaderSize) + + binary.BigEndian.PutUint16(out, uint16(u.TypeValue())) + binary.BigEndian.PutUint16(out[2:], uint16(2+(len(u.ProtectionProfiles)*2)+ /* MKI Length */ 1)) + binary.BigEndian.PutUint16(out[4:], uint16(len(u.ProtectionProfiles)*2)) + + for _, v := range u.ProtectionProfiles { + out = append(out, []byte{0x00, 0x00}...) + binary.BigEndian.PutUint16(out[len(out)-2:], uint16(v)) + } + + out = append(out, 0x00) /* MKI Length */ + return out, nil +} + +// Unmarshal populates the extension from encoded data +func (u *UseSRTP) Unmarshal(data []byte) error { + if len(data) <= useSRTPHeaderSize { + return errBufferTooSmall + } else if TypeValue(binary.BigEndian.Uint16(data)) != u.TypeValue() { + return errInvalidExtensionType + } + + profileCount := int(binary.BigEndian.Uint16(data[4:]) / 2) + if supportedGroupsHeaderSize+(profileCount*2) > len(data) { + return errLengthMismatch + } + + for i := 0; i < profileCount; i++ { + supportedProfile := SRTPProtectionProfile(binary.BigEndian.Uint16(data[(useSRTPHeaderSize + (i * 2)):])) + if _, ok := srtpProtectionProfiles()[supportedProfile]; ok { + u.ProtectionProfiles = append(u.ProtectionProfiles, supportedProfile) + } + } + return nil +} diff --git a/vendor/github.com/pion/dtls/v2/pkg/protocol/handshake/cipher_suite.go b/vendor/github.com/pion/dtls/v2/pkg/protocol/handshake/cipher_suite.go new file mode 100644 index 0000000..e8fbdea --- /dev/null +++ b/vendor/github.com/pion/dtls/v2/pkg/protocol/handshake/cipher_suite.go @@ -0,0 +1,29 @@ +package handshake + +import "encoding/binary" + +func decodeCipherSuiteIDs(buf []byte) ([]uint16, error) { + if len(buf) < 2 { + return nil, errBufferTooSmall + } + cipherSuitesCount := int(binary.BigEndian.Uint16(buf[0:])) / 2 + rtrn := make([]uint16, cipherSuitesCount) + for i := 0; i < cipherSuitesCount; i++ { + if len(buf) < (i*2 + 4) { + return nil, errBufferTooSmall + } + + rtrn[i] = binary.BigEndian.Uint16(buf[(i*2)+2:]) + } + return rtrn, nil +} + +func encodeCipherSuiteIDs(cipherSuiteIDs []uint16) []byte { + out := []byte{0x00, 0x00} + binary.BigEndian.PutUint16(out[len(out)-2:], uint16(len(cipherSuiteIDs)*2)) + for _, id := range cipherSuiteIDs { + out = append(out, []byte{0x00, 0x00}...) + binary.BigEndian.PutUint16(out[len(out)-2:], id) + } + return out +} diff --git a/vendor/github.com/pion/dtls/v2/pkg/protocol/handshake/errors.go b/vendor/github.com/pion/dtls/v2/pkg/protocol/handshake/errors.go new file mode 100644 index 0000000..ac77c04 --- /dev/null +++ b/vendor/github.com/pion/dtls/v2/pkg/protocol/handshake/errors.go @@ -0,0 +1,25 @@ +package handshake + +import ( + "errors" + + "github.com/pion/dtls/v2/pkg/protocol" +) + +// Typed errors +var ( + errUnableToMarshalFragmented = &protocol.InternalError{Err: errors.New("unable to marshal fragmented handshakes")} //nolint:goerr113 + errHandshakeMessageUnset = &protocol.InternalError{Err: errors.New("handshake message unset, unable to marshal")} //nolint:goerr113 + errBufferTooSmall = &protocol.TemporaryError{Err: errors.New("buffer is too small")} //nolint:goerr113 + errLengthMismatch = &protocol.InternalError{Err: errors.New("data length and declared length do not match")} //nolint:goerr113 + errInvalidClientKeyExchange = &protocol.FatalError{Err: errors.New("unable to determine if ClientKeyExchange is a public key or PSK Identity")} //nolint:goerr113 + errInvalidHashAlgorithm = &protocol.FatalError{Err: errors.New("invalid hash algorithm")} //nolint:goerr113 + errInvalidSignatureAlgorithm = &protocol.FatalError{Err: errors.New("invalid signature algorithm")} //nolint:goerr113 + errCookieTooLong = &protocol.FatalError{Err: errors.New("cookie must not be longer then 255 bytes")} //nolint:goerr113 + errInvalidEllipticCurveType = &protocol.FatalError{Err: errors.New("invalid or unknown elliptic curve type")} //nolint:goerr113 + errInvalidNamedCurve = &protocol.FatalError{Err: errors.New("invalid named curve")} //nolint:goerr113 + errCipherSuiteUnset = &protocol.FatalError{Err: errors.New("server hello can not be created without a cipher suite")} //nolint:goerr113 + errCompressionMethodUnset = &protocol.FatalError{Err: errors.New("server hello can not be created without a compression method")} //nolint:goerr113 + errInvalidCompressionMethod = &protocol.FatalError{Err: errors.New("invalid or unknown compression method")} //nolint:goerr113 + errNotImplemented = &protocol.InternalError{Err: errors.New("feature has not been implemented yet")} //nolint:goerr113 +) diff --git a/vendor/github.com/pion/dtls/v2/pkg/protocol/handshake/handshake.go b/vendor/github.com/pion/dtls/v2/pkg/protocol/handshake/handshake.go new file mode 100644 index 0000000..4aa493e --- /dev/null +++ b/vendor/github.com/pion/dtls/v2/pkg/protocol/handshake/handshake.go @@ -0,0 +1,145 @@ +// Package handshake provides the DTLS wire protocol for handshakes +package handshake + +import ( + "github.com/pion/dtls/v2/internal/util" + "github.com/pion/dtls/v2/pkg/protocol" +) + +// Type is the unique identifier for each handshake message +// https://tools.ietf.org/html/rfc5246#section-7.4 +type Type uint8 + +// Types of DTLS Handshake messages we know about +const ( + TypeHelloRequest Type = 0 + TypeClientHello Type = 1 + TypeServerHello Type = 2 + TypeHelloVerifyRequest Type = 3 + TypeCertificate Type = 11 + TypeServerKeyExchange Type = 12 + TypeCertificateRequest Type = 13 + TypeServerHelloDone Type = 14 + TypeCertificateVerify Type = 15 + TypeClientKeyExchange Type = 16 + TypeFinished Type = 20 +) + +// String returns the string representation of this type +func (t Type) String() string { + switch t { + case TypeHelloRequest: + return "HelloRequest" + case TypeClientHello: + return "ClientHello" + case TypeServerHello: + return "ServerHello" + case TypeHelloVerifyRequest: + return "HelloVerifyRequest" + case TypeCertificate: + return "TypeCertificate" + case TypeServerKeyExchange: + return "ServerKeyExchange" + case TypeCertificateRequest: + return "CertificateRequest" + case TypeServerHelloDone: + return "ServerHelloDone" + case TypeCertificateVerify: + return "CertificateVerify" + case TypeClientKeyExchange: + return "ClientKeyExchange" + case TypeFinished: + return "Finished" + } + return "" +} + +// Message is the body of a Handshake datagram +type Message interface { + Marshal() ([]byte, error) + Unmarshal(data []byte) error + + Type() Type +} + +// Handshake protocol is responsible for selecting a cipher spec and +// generating a master secret, which together comprise the primary +// cryptographic parameters associated with a secure session. The +// handshake protocol can also optionally authenticate parties who have +// certificates signed by a trusted certificate authority. +// https://tools.ietf.org/html/rfc5246#section-7.3 +type Handshake struct { + Header Header + Message Message +} + +// ContentType returns what kind of content this message is carying +func (h Handshake) ContentType() protocol.ContentType { + return protocol.ContentTypeHandshake +} + +// Marshal encodes a handshake into a binary message +func (h *Handshake) Marshal() ([]byte, error) { + if h.Message == nil { + return nil, errHandshakeMessageUnset + } else if h.Header.FragmentOffset != 0 { + return nil, errUnableToMarshalFragmented + } + + msg, err := h.Message.Marshal() + if err != nil { + return nil, err + } + + h.Header.Length = uint32(len(msg)) + h.Header.FragmentLength = h.Header.Length + h.Header.Type = h.Message.Type() + header, err := h.Header.Marshal() + if err != nil { + return nil, err + } + + return append(header, msg...), nil +} + +// Unmarshal decodes a handshake from a binary message +func (h *Handshake) Unmarshal(data []byte) error { + if err := h.Header.Unmarshal(data); err != nil { + return err + } + + reportedLen := util.BigEndianUint24(data[1:]) + if uint32(len(data)-HeaderLength) != reportedLen { + return errLengthMismatch + } else if reportedLen != h.Header.FragmentLength { + return errLengthMismatch + } + + switch Type(data[0]) { + case TypeHelloRequest: + return errNotImplemented + case TypeClientHello: + h.Message = &MessageClientHello{} + case TypeHelloVerifyRequest: + h.Message = &MessageHelloVerifyRequest{} + case TypeServerHello: + h.Message = &MessageServerHello{} + case TypeCertificate: + h.Message = &MessageCertificate{} + case TypeServerKeyExchange: + h.Message = &MessageServerKeyExchange{} + case TypeCertificateRequest: + h.Message = &MessageCertificateRequest{} + case TypeServerHelloDone: + h.Message = &MessageServerHelloDone{} + case TypeClientKeyExchange: + h.Message = &MessageClientKeyExchange{} + case TypeFinished: + h.Message = &MessageFinished{} + case TypeCertificateVerify: + h.Message = &MessageCertificateVerify{} + default: + return errNotImplemented + } + return h.Message.Unmarshal(data[HeaderLength:]) +} diff --git a/vendor/github.com/pion/dtls/v2/pkg/protocol/handshake/header.go b/vendor/github.com/pion/dtls/v2/pkg/protocol/handshake/header.go new file mode 100644 index 0000000..cb6a224 --- /dev/null +++ b/vendor/github.com/pion/dtls/v2/pkg/protocol/handshake/header.go @@ -0,0 +1,50 @@ +package handshake + +import ( + "encoding/binary" + + "github.com/pion/dtls/v2/internal/util" +) + +// HeaderLength msg_len for Handshake messages assumes an extra +// 12 bytes for sequence, fragment and version information vs TLS +const HeaderLength = 12 + +// Header is the static first 12 bytes of each RecordLayer +// of type Handshake. These fields allow us to support message loss, reordering, and +// message fragmentation, +// +// https://tools.ietf.org/html/rfc6347#section-4.2.2 +type Header struct { + Type Type + Length uint32 // uint24 in spec + MessageSequence uint16 + FragmentOffset uint32 // uint24 in spec + FragmentLength uint32 // uint24 in spec +} + +// Marshal encodes the Header +func (h *Header) Marshal() ([]byte, error) { + out := make([]byte, HeaderLength) + + out[0] = byte(h.Type) + util.PutBigEndianUint24(out[1:], h.Length) + binary.BigEndian.PutUint16(out[4:], h.MessageSequence) + util.PutBigEndianUint24(out[6:], h.FragmentOffset) + util.PutBigEndianUint24(out[9:], h.FragmentLength) + return out, nil +} + +// Unmarshal populates the header from encoded data +func (h *Header) Unmarshal(data []byte) error { + if len(data) < HeaderLength { + return errBufferTooSmall + } + + h.Type = Type(data[0]) + h.Length = util.BigEndianUint24(data[1:]) + h.MessageSequence = binary.BigEndian.Uint16(data[4:]) + h.FragmentOffset = util.BigEndianUint24(data[6:]) + h.FragmentLength = util.BigEndianUint24(data[9:]) + return nil +} diff --git a/vendor/github.com/pion/dtls/v2/pkg/protocol/handshake/message_certificate.go b/vendor/github.com/pion/dtls/v2/pkg/protocol/handshake/message_certificate.go new file mode 100644 index 0000000..05fb746 --- /dev/null +++ b/vendor/github.com/pion/dtls/v2/pkg/protocol/handshake/message_certificate.go @@ -0,0 +1,66 @@ +package handshake + +import ( + "github.com/pion/dtls/v2/internal/util" +) + +// MessageCertificate is a DTLS Handshake Message +// it can contain either a Client or Server Certificate +// +// https://tools.ietf.org/html/rfc5246#section-7.4.2 +type MessageCertificate struct { + Certificate [][]byte +} + +// Type returns the Handshake Type +func (m MessageCertificate) Type() Type { + return TypeCertificate +} + +const ( + handshakeMessageCertificateLengthFieldSize = 3 +) + +// Marshal encodes the Handshake +func (m *MessageCertificate) Marshal() ([]byte, error) { + out := make([]byte, handshakeMessageCertificateLengthFieldSize) + + for _, r := range m.Certificate { + // Certificate Length + out = append(out, make([]byte, handshakeMessageCertificateLengthFieldSize)...) + util.PutBigEndianUint24(out[len(out)-handshakeMessageCertificateLengthFieldSize:], uint32(len(r))) + + // Certificate body + out = append(out, append([]byte{}, r...)...) + } + + // Total Payload Size + util.PutBigEndianUint24(out[0:], uint32(len(out[handshakeMessageCertificateLengthFieldSize:]))) + return out, nil +} + +// Unmarshal populates the message from encoded data +func (m *MessageCertificate) Unmarshal(data []byte) error { + if len(data) < handshakeMessageCertificateLengthFieldSize { + return errBufferTooSmall + } + + if certificateBodyLen := int(util.BigEndianUint24(data)); certificateBodyLen+handshakeMessageCertificateLengthFieldSize != len(data) { + return errLengthMismatch + } + + offset := handshakeMessageCertificateLengthFieldSize + for offset < len(data) { + certificateLen := int(util.BigEndianUint24(data[offset:])) + offset += handshakeMessageCertificateLengthFieldSize + + if offset+certificateLen > len(data) { + return errLengthMismatch + } + + m.Certificate = append(m.Certificate, append([]byte{}, data[offset:offset+certificateLen]...)) + offset += certificateLen + } + + return nil +} diff --git a/vendor/github.com/pion/dtls/v2/pkg/protocol/handshake/message_certificate_request.go b/vendor/github.com/pion/dtls/v2/pkg/protocol/handshake/message_certificate_request.go new file mode 100644 index 0000000..e711f39 --- /dev/null +++ b/vendor/github.com/pion/dtls/v2/pkg/protocol/handshake/message_certificate_request.go @@ -0,0 +1,100 @@ +package handshake + +import ( + "encoding/binary" + + "github.com/pion/dtls/v2/pkg/crypto/clientcertificate" + "github.com/pion/dtls/v2/pkg/crypto/hash" + "github.com/pion/dtls/v2/pkg/crypto/signature" + "github.com/pion/dtls/v2/pkg/crypto/signaturehash" +) + +/* +MessageCertificateRequest is so a non-anonymous server can optionally +request a certificate from the client, if appropriate for the selected cipher +suite. This message, if sent, will immediately follow the ServerKeyExchange +message (if it is sent; otherwise, this message follows the +server's Certificate message). + +https://tools.ietf.org/html/rfc5246#section-7.4.4 +*/ +type MessageCertificateRequest struct { + CertificateTypes []clientcertificate.Type + SignatureHashAlgorithms []signaturehash.Algorithm +} + +const ( + messageCertificateRequestMinLength = 5 +) + +// Type returns the Handshake Type +func (m MessageCertificateRequest) Type() Type { + return TypeCertificateRequest +} + +// Marshal encodes the Handshake +func (m *MessageCertificateRequest) Marshal() ([]byte, error) { + out := []byte{byte(len(m.CertificateTypes))} + for _, v := range m.CertificateTypes { + out = append(out, byte(v)) + } + + out = append(out, []byte{0x00, 0x00}...) + binary.BigEndian.PutUint16(out[len(out)-2:], uint16(len(m.SignatureHashAlgorithms)*2)) + for _, v := range m.SignatureHashAlgorithms { + out = append(out, byte(v.Hash)) + out = append(out, byte(v.Signature)) + } + + out = append(out, []byte{0x00, 0x00}...) // Distinguished Names Length + return out, nil +} + +// Unmarshal populates the message from encoded data +func (m *MessageCertificateRequest) Unmarshal(data []byte) error { + if len(data) < messageCertificateRequestMinLength { + return errBufferTooSmall + } + + offset := 0 + certificateTypesLength := int(data[0]) + offset++ + + if (offset + certificateTypesLength) > len(data) { + return errBufferTooSmall + } + + for i := 0; i < certificateTypesLength; i++ { + certType := clientcertificate.Type(data[offset+i]) + if _, ok := clientcertificate.Types()[certType]; ok { + m.CertificateTypes = append(m.CertificateTypes, certType) + } + } + offset += certificateTypesLength + if len(data) < offset+2 { + return errBufferTooSmall + } + signatureHashAlgorithmsLength := int(binary.BigEndian.Uint16(data[offset:])) + offset += 2 + + if (offset + signatureHashAlgorithmsLength) > len(data) { + return errBufferTooSmall + } + + for i := 0; i < signatureHashAlgorithmsLength; i += 2 { + if len(data) < (offset + i + 2) { + return errBufferTooSmall + } + h := hash.Algorithm(data[offset+i]) + s := signature.Algorithm(data[offset+i+1]) + + if _, ok := hash.Algorithms()[h]; !ok { + continue + } else if _, ok := signature.Algorithms()[s]; !ok { + continue + } + m.SignatureHashAlgorithms = append(m.SignatureHashAlgorithms, signaturehash.Algorithm{Signature: s, Hash: h}) + } + + return nil +} diff --git a/vendor/github.com/pion/dtls/v2/pkg/protocol/handshake/message_certificate_verify.go b/vendor/github.com/pion/dtls/v2/pkg/protocol/handshake/message_certificate_verify.go new file mode 100644 index 0000000..fb5e463 --- /dev/null +++ b/vendor/github.com/pion/dtls/v2/pkg/protocol/handshake/message_certificate_verify.go @@ -0,0 +1,61 @@ +package handshake + +import ( + "encoding/binary" + + "github.com/pion/dtls/v2/pkg/crypto/hash" + "github.com/pion/dtls/v2/pkg/crypto/signature" +) + +// MessageCertificateVerify provide explicit verification of a +// client certificate. +// +// https://tools.ietf.org/html/rfc5246#section-7.4.8 +type MessageCertificateVerify struct { + HashAlgorithm hash.Algorithm + SignatureAlgorithm signature.Algorithm + Signature []byte +} + +const handshakeMessageCertificateVerifyMinLength = 4 + +// Type returns the Handshake Type +func (m MessageCertificateVerify) Type() Type { + return TypeCertificateVerify +} + +// Marshal encodes the Handshake +func (m *MessageCertificateVerify) Marshal() ([]byte, error) { + out := make([]byte, 1+1+2+len(m.Signature)) + + out[0] = byte(m.HashAlgorithm) + out[1] = byte(m.SignatureAlgorithm) + binary.BigEndian.PutUint16(out[2:], uint16(len(m.Signature))) + copy(out[4:], m.Signature) + return out, nil +} + +// Unmarshal populates the message from encoded data +func (m *MessageCertificateVerify) Unmarshal(data []byte) error { + if len(data) < handshakeMessageCertificateVerifyMinLength { + return errBufferTooSmall + } + + m.HashAlgorithm = hash.Algorithm(data[0]) + if _, ok := hash.Algorithms()[m.HashAlgorithm]; !ok { + return errInvalidHashAlgorithm + } + + m.SignatureAlgorithm = signature.Algorithm(data[1]) + if _, ok := signature.Algorithms()[m.SignatureAlgorithm]; !ok { + return errInvalidSignatureAlgorithm + } + + signatureLength := int(binary.BigEndian.Uint16(data[2:])) + if (signatureLength + 4) != len(data) { + return errBufferTooSmall + } + + m.Signature = append([]byte{}, data[4:]...) + return nil +} diff --git a/vendor/github.com/pion/dtls/v2/pkg/protocol/handshake/message_client_hello.go b/vendor/github.com/pion/dtls/v2/pkg/protocol/handshake/message_client_hello.go new file mode 100644 index 0000000..cb8a100 --- /dev/null +++ b/vendor/github.com/pion/dtls/v2/pkg/protocol/handshake/message_client_hello.go @@ -0,0 +1,125 @@ +package handshake + +import ( + "encoding/binary" + + "github.com/pion/dtls/v2/pkg/protocol" + "github.com/pion/dtls/v2/pkg/protocol/extension" +) + +/* +MessageClientHello is for when a client first connects to a server it is +required to send the client hello as its first message. The client can also send a +client hello in response to a hello request or on its own +initiative in order to renegotiate the security parameters in an +existing connection. +*/ +type MessageClientHello struct { + Version protocol.Version + Random Random + Cookie []byte + + CipherSuiteIDs []uint16 + CompressionMethods []*protocol.CompressionMethod + Extensions []extension.Extension +} + +const handshakeMessageClientHelloVariableWidthStart = 34 + +// Type returns the Handshake Type +func (m MessageClientHello) Type() Type { + return TypeClientHello +} + +// Marshal encodes the Handshake +func (m *MessageClientHello) Marshal() ([]byte, error) { + if len(m.Cookie) > 255 { + return nil, errCookieTooLong + } + + out := make([]byte, handshakeMessageClientHelloVariableWidthStart) + out[0] = m.Version.Major + out[1] = m.Version.Minor + + rand := m.Random.MarshalFixed() + copy(out[2:], rand[:]) + + out = append(out, 0x00) // SessionID + + out = append(out, byte(len(m.Cookie))) + out = append(out, m.Cookie...) + out = append(out, encodeCipherSuiteIDs(m.CipherSuiteIDs)...) + out = append(out, protocol.EncodeCompressionMethods(m.CompressionMethods)...) + + extensions, err := extension.Marshal(m.Extensions) + if err != nil { + return nil, err + } + + return append(out, extensions...), nil +} + +// Unmarshal populates the message from encoded data +func (m *MessageClientHello) Unmarshal(data []byte) error { + if len(data) < 2+RandomLength { + return errBufferTooSmall + } + + m.Version.Major = data[0] + m.Version.Minor = data[1] + + var random [RandomLength]byte + copy(random[:], data[2:]) + m.Random.UnmarshalFixed(random) + + // rest of packet has variable width sections + currOffset := handshakeMessageClientHelloVariableWidthStart + currOffset += int(data[currOffset]) + 1 // SessionID + + currOffset++ + if len(data) <= currOffset { + return errBufferTooSmall + } + n := int(data[currOffset-1]) + if len(data) <= currOffset+n { + return errBufferTooSmall + } + m.Cookie = append([]byte{}, data[currOffset:currOffset+n]...) + currOffset += len(m.Cookie) + + // Cipher Suites + if len(data) < currOffset { + return errBufferTooSmall + } + cipherSuiteIDs, err := decodeCipherSuiteIDs(data[currOffset:]) + if err != nil { + return err + } + m.CipherSuiteIDs = cipherSuiteIDs + if len(data) < currOffset+2 { + return errBufferTooSmall + } + currOffset += int(binary.BigEndian.Uint16(data[currOffset:])) + 2 + + // Compression Methods + if len(data) < currOffset { + return errBufferTooSmall + } + compressionMethods, err := protocol.DecodeCompressionMethods(data[currOffset:]) + if err != nil { + return err + } + m.CompressionMethods = compressionMethods + if len(data) < currOffset { + return errBufferTooSmall + } + currOffset += int(data[currOffset]) + 1 + + // Extensions + extensions, err := extension.Unmarshal(data[currOffset:]) + if err != nil { + return err + } + m.Extensions = extensions + return nil +} diff --git a/vendor/github.com/pion/dtls/v2/pkg/protocol/handshake/message_client_key_exchange.go b/vendor/github.com/pion/dtls/v2/pkg/protocol/handshake/message_client_key_exchange.go new file mode 100644 index 0000000..f8fc369 --- /dev/null +++ b/vendor/github.com/pion/dtls/v2/pkg/protocol/handshake/message_client_key_exchange.go @@ -0,0 +1,56 @@ +package handshake + +import ( + "encoding/binary" +) + +// MessageClientKeyExchange is a DTLS Handshake Message +// With this message, the premaster secret is set, either by direct +// transmission of the RSA-encrypted secret or by the transmission of +// Diffie-Hellman parameters that will allow each side to agree upon +// the same premaster secret. +// +// https://tools.ietf.org/html/rfc5246#section-7.4.7 +type MessageClientKeyExchange struct { + IdentityHint []byte + PublicKey []byte +} + +// Type returns the Handshake Type +func (m MessageClientKeyExchange) Type() Type { + return TypeClientKeyExchange +} + +// Marshal encodes the Handshake +func (m *MessageClientKeyExchange) Marshal() ([]byte, error) { + switch { + case (m.IdentityHint != nil && m.PublicKey != nil) || (m.IdentityHint == nil && m.PublicKey == nil): + return nil, errInvalidClientKeyExchange + case m.PublicKey != nil: + return append([]byte{byte(len(m.PublicKey))}, m.PublicKey...), nil + default: + out := append([]byte{0x00, 0x00}, m.IdentityHint...) + binary.BigEndian.PutUint16(out, uint16(len(out)-2)) + return out, nil + } +} + +// Unmarshal populates the message from encoded data +func (m *MessageClientKeyExchange) Unmarshal(data []byte) error { + if len(data) < 2 { + return errBufferTooSmall + } + + // If parsed as PSK return early and only populate PSK Identity Hint + if pskLength := binary.BigEndian.Uint16(data); len(data) == int(pskLength+2) { + m.IdentityHint = append([]byte{}, data[2:]...) + return nil + } + + if publicKeyLength := int(data[0]); len(data) != publicKeyLength+1 { + return errBufferTooSmall + } + + m.PublicKey = append([]byte{}, data[1:]...) + return nil +} diff --git a/vendor/github.com/pion/dtls/v2/pkg/protocol/handshake/message_finished.go b/vendor/github.com/pion/dtls/v2/pkg/protocol/handshake/message_finished.go new file mode 100644 index 0000000..c65d42a --- /dev/null +++ b/vendor/github.com/pion/dtls/v2/pkg/protocol/handshake/message_finished.go @@ -0,0 +1,27 @@ +package handshake + +// MessageFinished is a DTLS Handshake Message +// this message is the first one protected with the just +// negotiated algorithms, keys, and secrets. Recipients of Finished +// messages MUST verify that the contents are correct. +// +// https://tools.ietf.org/html/rfc5246#section-7.4.9 +type MessageFinished struct { + VerifyData []byte +} + +// Type returns the Handshake Type +func (m MessageFinished) Type() Type { + return TypeFinished +} + +// Marshal encodes the Handshake +func (m *MessageFinished) Marshal() ([]byte, error) { + return append([]byte{}, m.VerifyData...), nil +} + +// Unmarshal populates the message from encoded data +func (m *MessageFinished) Unmarshal(data []byte) error { + m.VerifyData = append([]byte{}, data...) + return nil +} diff --git a/vendor/github.com/pion/dtls/v2/pkg/protocol/handshake/message_hello_verify_request.go b/vendor/github.com/pion/dtls/v2/pkg/protocol/handshake/message_hello_verify_request.go new file mode 100644 index 0000000..ef834dc --- /dev/null +++ b/vendor/github.com/pion/dtls/v2/pkg/protocol/handshake/message_hello_verify_request.go @@ -0,0 +1,62 @@ +package handshake + +import ( + "github.com/pion/dtls/v2/pkg/protocol" +) + +// MessageHelloVerifyRequest is as follows: +// +// struct { +// ProtocolVersion server_version; +// opaque cookie<0..2^8-1>; +// } HelloVerifyRequest; +// +// The HelloVerifyRequest message type is hello_verify_request(3). +// +// When the client sends its ClientHello message to the server, the server +// MAY respond with a HelloVerifyRequest message. This message contains +// a stateless cookie generated using the technique of [PHOTURIS]. The +// client MUST retransmit the ClientHello with the cookie added. +// +// https://tools.ietf.org/html/rfc6347#section-4.2.1 +type MessageHelloVerifyRequest struct { + Version protocol.Version + Cookie []byte +} + +// Type returns the Handshake Type +func (m MessageHelloVerifyRequest) Type() Type { + return TypeHelloVerifyRequest +} + +// Marshal encodes the Handshake +func (m *MessageHelloVerifyRequest) Marshal() ([]byte, error) { + if len(m.Cookie) > 255 { + return nil, errCookieTooLong + } + + out := make([]byte, 3+len(m.Cookie)) + out[0] = m.Version.Major + out[1] = m.Version.Minor + out[2] = byte(len(m.Cookie)) + copy(out[3:], m.Cookie) + + return out, nil +} + +// Unmarshal populates the message from encoded data +func (m *MessageHelloVerifyRequest) Unmarshal(data []byte) error { + if len(data) < 3 { + return errBufferTooSmall + } + m.Version.Major = data[0] + m.Version.Minor = data[1] + cookieLength := data[2] + if len(data) < (int(cookieLength) + 3) { + return errBufferTooSmall + } + m.Cookie = make([]byte, cookieLength) + + copy(m.Cookie, data[3:3+cookieLength]) + return nil +} diff --git a/vendor/github.com/pion/dtls/v2/pkg/protocol/handshake/message_server_hello.go b/vendor/github.com/pion/dtls/v2/pkg/protocol/handshake/message_server_hello.go new file mode 100644 index 0000000..c4b181f --- /dev/null +++ b/vendor/github.com/pion/dtls/v2/pkg/protocol/handshake/message_server_hello.go @@ -0,0 +1,106 @@ +package handshake + +import ( + "encoding/binary" + + "github.com/pion/dtls/v2/pkg/protocol" + "github.com/pion/dtls/v2/pkg/protocol/extension" +) + +// MessageServerHello is sent in response to a ClientHello +// message when it was able to find an acceptable set of algorithms. +// If it cannot find such a match, it will respond with a handshake +// failure alert. +// +// https://tools.ietf.org/html/rfc5246#section-7.4.1.3 +type MessageServerHello struct { + Version protocol.Version + Random Random + + CipherSuiteID *uint16 + CompressionMethod *protocol.CompressionMethod + Extensions []extension.Extension +} + +const messageServerHelloVariableWidthStart = 2 + RandomLength + +// Type returns the Handshake Type +func (m MessageServerHello) Type() Type { + return TypeServerHello +} + +// Marshal encodes the Handshake +func (m *MessageServerHello) Marshal() ([]byte, error) { + if m.CipherSuiteID == nil { + return nil, errCipherSuiteUnset + } else if m.CompressionMethod == nil { + return nil, errCompressionMethodUnset + } + + out := make([]byte, messageServerHelloVariableWidthStart) + out[0] = m.Version.Major + out[1] = m.Version.Minor + + rand := m.Random.MarshalFixed() + copy(out[2:], rand[:]) + + out = append(out, 0x00) // SessionID + + out = append(out, []byte{0x00, 0x00}...) + binary.BigEndian.PutUint16(out[len(out)-2:], *m.CipherSuiteID) + + out = append(out, byte(m.CompressionMethod.ID)) + + extensions, err := extension.Marshal(m.Extensions) + if err != nil { + return nil, err + } + + return append(out, extensions...), nil +} + +// Unmarshal populates the message from encoded data +func (m *MessageServerHello) Unmarshal(data []byte) error { + if len(data) < 2+RandomLength { + return errBufferTooSmall + } + + m.Version.Major = data[0] + m.Version.Minor = data[1] + + var random [RandomLength]byte + copy(random[:], data[2:]) + m.Random.UnmarshalFixed(random) + + currOffset := messageServerHelloVariableWidthStart + currOffset += int(data[currOffset]) + 1 // SessionID + if len(data) < (currOffset + 2) { + return errBufferTooSmall + } + + m.CipherSuiteID = new(uint16) + *m.CipherSuiteID = binary.BigEndian.Uint16(data[currOffset:]) + currOffset += 2 + + if len(data) < currOffset { + return errBufferTooSmall + } + if compressionMethod, ok := protocol.CompressionMethods()[protocol.CompressionMethodID(data[currOffset])]; ok { + m.CompressionMethod = compressionMethod + currOffset++ + } else { + return errInvalidCompressionMethod + } + + if len(data) <= currOffset { + m.Extensions = []extension.Extension{} + return nil + } + + extensions, err := extension.Unmarshal(data[currOffset:]) + if err != nil { + return err + } + m.Extensions = extensions + return nil +} diff --git a/vendor/github.com/pion/dtls/v2/pkg/protocol/handshake/message_server_hello_done.go b/vendor/github.com/pion/dtls/v2/pkg/protocol/handshake/message_server_hello_done.go new file mode 100644 index 0000000..a004802 --- /dev/null +++ b/vendor/github.com/pion/dtls/v2/pkg/protocol/handshake/message_server_hello_done.go @@ -0,0 +1,22 @@ +package handshake + +// MessageServerHelloDone is final non-encrypted message from server +// this communicates server has sent all its handshake messages and next +// should be MessageFinished +type MessageServerHelloDone struct { +} + +// Type returns the Handshake Type +func (m MessageServerHelloDone) Type() Type { + return TypeServerHelloDone +} + +// Marshal encodes the Handshake +func (m *MessageServerHelloDone) Marshal() ([]byte, error) { + return []byte{}, nil +} + +// Unmarshal populates the message from encoded data +func (m *MessageServerHelloDone) Unmarshal(data []byte) error { + return nil +} diff --git a/vendor/github.com/pion/dtls/v2/pkg/protocol/handshake/message_server_key_exchange.go b/vendor/github.com/pion/dtls/v2/pkg/protocol/handshake/message_server_key_exchange.go new file mode 100644 index 0000000..4148fe0 --- /dev/null +++ b/vendor/github.com/pion/dtls/v2/pkg/protocol/handshake/message_server_key_exchange.go @@ -0,0 +1,119 @@ +package handshake + +import ( + "encoding/binary" + + "github.com/pion/dtls/v2/pkg/crypto/elliptic" + "github.com/pion/dtls/v2/pkg/crypto/hash" + "github.com/pion/dtls/v2/pkg/crypto/signature" +) + +// MessageServerKeyExchange supports ECDH and PSK +type MessageServerKeyExchange struct { + IdentityHint []byte + + EllipticCurveType elliptic.CurveType + NamedCurve elliptic.Curve + PublicKey []byte + HashAlgorithm hash.Algorithm + SignatureAlgorithm signature.Algorithm + Signature []byte +} + +// Type returns the Handshake Type +func (m MessageServerKeyExchange) Type() Type { + return TypeServerKeyExchange +} + +// Marshal encodes the Handshake +func (m *MessageServerKeyExchange) Marshal() ([]byte, error) { + if m.IdentityHint != nil { + out := append([]byte{0x00, 0x00}, m.IdentityHint...) + binary.BigEndian.PutUint16(out, uint16(len(out)-2)) + return out, nil + } + + out := []byte{byte(m.EllipticCurveType), 0x00, 0x00} + binary.BigEndian.PutUint16(out[1:], uint16(m.NamedCurve)) + + out = append(out, byte(len(m.PublicKey))) + out = append(out, m.PublicKey...) + + if m.HashAlgorithm == hash.None && m.SignatureAlgorithm == signature.Anonymous && len(m.Signature) == 0 { + return out, nil + } + + out = append(out, []byte{byte(m.HashAlgorithm), byte(m.SignatureAlgorithm), 0x00, 0x00}...) + binary.BigEndian.PutUint16(out[len(out)-2:], uint16(len(m.Signature))) + out = append(out, m.Signature...) + + return out, nil +} + +// Unmarshal populates the message from encoded data +func (m *MessageServerKeyExchange) Unmarshal(data []byte) error { + if len(data) < 2 { + return errBufferTooSmall + } + + // If parsed as PSK return early and only populate PSK Identity Hint + if pskLength := binary.BigEndian.Uint16(data); len(data) == int(pskLength+2) { + m.IdentityHint = append([]byte{}, data[2:]...) + return nil + } + + if _, ok := elliptic.CurveTypes()[elliptic.CurveType(data[0])]; ok { + m.EllipticCurveType = elliptic.CurveType(data[0]) + } else { + return errInvalidEllipticCurveType + } + + if len(data[1:]) < 2 { + return errBufferTooSmall + } + m.NamedCurve = elliptic.Curve(binary.BigEndian.Uint16(data[1:3])) + if _, ok := elliptic.Curves()[m.NamedCurve]; !ok { + return errInvalidNamedCurve + } + if len(data) < 4 { + return errBufferTooSmall + } + + publicKeyLength := int(data[3]) + offset := 4 + publicKeyLength + if len(data) < offset { + return errBufferTooSmall + } + m.PublicKey = append([]byte{}, data[4:offset]...) + + // Anon connection doesn't contains hashAlgorithm, signatureAlgorithm, signature + if len(data) == offset { + return nil + } else if len(data) <= offset { + return errBufferTooSmall + } + + m.HashAlgorithm = hash.Algorithm(data[offset]) + if _, ok := hash.Algorithms()[m.HashAlgorithm]; !ok { + return errInvalidHashAlgorithm + } + offset++ + if len(data) <= offset { + return errBufferTooSmall + } + m.SignatureAlgorithm = signature.Algorithm(data[offset]) + if _, ok := signature.Algorithms()[m.SignatureAlgorithm]; !ok { + return errInvalidSignatureAlgorithm + } + offset++ + if len(data) < offset+2 { + return errBufferTooSmall + } + signatureLength := int(binary.BigEndian.Uint16(data[offset:])) + offset += 2 + if len(data) < offset+signatureLength { + return errBufferTooSmall + } + m.Signature = append([]byte{}, data[offset:offset+signatureLength]...) + return nil +} diff --git a/vendor/github.com/pion/dtls/v2/pkg/protocol/handshake/random.go b/vendor/github.com/pion/dtls/v2/pkg/protocol/handshake/random.go new file mode 100644 index 0000000..0ade936 --- /dev/null +++ b/vendor/github.com/pion/dtls/v2/pkg/protocol/handshake/random.go @@ -0,0 +1,49 @@ +package handshake + +import ( + "crypto/rand" + "encoding/binary" + "time" +) + +// Consts for Random in Handshake +const ( + RandomBytesLength = 28 + RandomLength = RandomBytesLength + 4 +) + +// Random value that is used in ClientHello and ServerHello +// +// https://tools.ietf.org/html/rfc4346#section-7.4.1.2 +type Random struct { + GMTUnixTime time.Time + RandomBytes [RandomBytesLength]byte +} + +// MarshalFixed encodes the Handshake +func (r *Random) MarshalFixed() [RandomLength]byte { + var out [RandomLength]byte + + binary.BigEndian.PutUint32(out[0:], uint32(r.GMTUnixTime.Unix())) + copy(out[4:], r.RandomBytes[:]) + + return out +} + +// UnmarshalFixed populates the message from encoded data +func (r *Random) UnmarshalFixed(data [RandomLength]byte) { + r.GMTUnixTime = time.Unix(int64(binary.BigEndian.Uint32(data[0:])), 0) + copy(r.RandomBytes[:], data[4:]) +} + +// Populate fills the handshakeRandom with random values +// may be called multiple times +func (r *Random) Populate() error { + r.GMTUnixTime = time.Now() + + tmp := make([]byte, RandomBytesLength) + _, err := rand.Read(tmp) + copy(r.RandomBytes[:], tmp) + + return err +} diff --git a/vendor/github.com/pion/dtls/v2/pkg/protocol/recordlayer/errors.go b/vendor/github.com/pion/dtls/v2/pkg/protocol/recordlayer/errors.go new file mode 100644 index 0000000..7033d40 --- /dev/null +++ b/vendor/github.com/pion/dtls/v2/pkg/protocol/recordlayer/errors.go @@ -0,0 +1,16 @@ +// Package recordlayer implements the TLS Record Layer https://tools.ietf.org/html/rfc5246#section-6 +package recordlayer + +import ( + "errors" + + "github.com/pion/dtls/v2/pkg/protocol" +) + +var ( + errBufferTooSmall = &protocol.TemporaryError{Err: errors.New("buffer is too small")} //nolint:goerr113 + errInvalidPacketLength = &protocol.TemporaryError{Err: errors.New("packet length and declared length do not match")} //nolint:goerr113 + errSequenceNumberOverflow = &protocol.InternalError{Err: errors.New("sequence number overflow")} //nolint:goerr113 + errUnsupportedProtocolVersion = &protocol.FatalError{Err: errors.New("unsupported protocol version")} //nolint:goerr113 + errInvalidContentType = &protocol.TemporaryError{Err: errors.New("invalid content type")} //nolint:goerr113 +) diff --git a/vendor/github.com/pion/dtls/v2/pkg/protocol/recordlayer/header.go b/vendor/github.com/pion/dtls/v2/pkg/protocol/recordlayer/header.go new file mode 100644 index 0000000..65047d7 --- /dev/null +++ b/vendor/github.com/pion/dtls/v2/pkg/protocol/recordlayer/header.go @@ -0,0 +1,61 @@ +package recordlayer + +import ( + "encoding/binary" + + "github.com/pion/dtls/v2/internal/util" + "github.com/pion/dtls/v2/pkg/protocol" +) + +// Header implements a TLS RecordLayer header +type Header struct { + ContentType protocol.ContentType + ContentLen uint16 + Version protocol.Version + Epoch uint16 + SequenceNumber uint64 // uint48 in spec +} + +// RecordLayer enums +const ( + HeaderSize = 13 + MaxSequenceNumber = 0x0000FFFFFFFFFFFF +) + +// Marshal encodes a TLS RecordLayer Header to binary +func (h *Header) Marshal() ([]byte, error) { + if h.SequenceNumber > MaxSequenceNumber { + return nil, errSequenceNumberOverflow + } + + out := make([]byte, HeaderSize) + out[0] = byte(h.ContentType) + out[1] = h.Version.Major + out[2] = h.Version.Minor + binary.BigEndian.PutUint16(out[3:], h.Epoch) + util.PutBigEndianUint48(out[5:], h.SequenceNumber) + binary.BigEndian.PutUint16(out[HeaderSize-2:], h.ContentLen) + return out, nil +} + +// Unmarshal populates a TLS RecordLayer Header from binary +func (h *Header) Unmarshal(data []byte) error { + if len(data) < HeaderSize { + return errBufferTooSmall + } + h.ContentType = protocol.ContentType(data[0]) + h.Version.Major = data[1] + h.Version.Minor = data[2] + h.Epoch = binary.BigEndian.Uint16(data[3:]) + + // SequenceNumber is stored as uint48, make into uint64 + seqCopy := make([]byte, 8) + copy(seqCopy[2:], data[5:11]) + h.SequenceNumber = binary.BigEndian.Uint64(seqCopy) + + if !h.Version.Equal(protocol.Version1_0) && !h.Version.Equal(protocol.Version1_2) { + return errUnsupportedProtocolVersion + } + + return nil +} diff --git a/vendor/github.com/pion/dtls/v2/pkg/protocol/recordlayer/recordlayer.go b/vendor/github.com/pion/dtls/v2/pkg/protocol/recordlayer/recordlayer.go new file mode 100644 index 0000000..67e5a72 --- /dev/null +++ b/vendor/github.com/pion/dtls/v2/pkg/protocol/recordlayer/recordlayer.go @@ -0,0 +1,99 @@ +package recordlayer + +import ( + "encoding/binary" + + "github.com/pion/dtls/v2/pkg/protocol" + "github.com/pion/dtls/v2/pkg/protocol/alert" + "github.com/pion/dtls/v2/pkg/protocol/handshake" +) + +// RecordLayer which handles all data transport. +// The record layer is assumed to sit directly on top of some +// reliable transport such as TCP. The record layer can carry four types of content: +// +// 1. Handshake messages—used for algorithm negotiation and key establishment. +// 2. ChangeCipherSpec messages—really part of the handshake but technically a separate kind of message. +// 3. Alert messages—used to signal that errors have occurred +// 4. Application layer data +// +// The DTLS record layer is extremely similar to that of TLS 1.1. The +// only change is the inclusion of an explicit sequence number in the +// record. This sequence number allows the recipient to correctly +// verify the TLS MAC. +// +// https://tools.ietf.org/html/rfc4347#section-4.1 +type RecordLayer struct { + Header Header + Content protocol.Content +} + +// Marshal encodes the RecordLayer to binary +func (r *RecordLayer) Marshal() ([]byte, error) { + contentRaw, err := r.Content.Marshal() + if err != nil { + return nil, err + } + + r.Header.ContentLen = uint16(len(contentRaw)) + r.Header.ContentType = r.Content.ContentType() + + headerRaw, err := r.Header.Marshal() + if err != nil { + return nil, err + } + + return append(headerRaw, contentRaw...), nil +} + +// Unmarshal populates the RecordLayer from binary +func (r *RecordLayer) Unmarshal(data []byte) error { + if len(data) < HeaderSize { + return errBufferTooSmall + } + if err := r.Header.Unmarshal(data); err != nil { + return err + } + + switch protocol.ContentType(data[0]) { + case protocol.ContentTypeChangeCipherSpec: + r.Content = &protocol.ChangeCipherSpec{} + case protocol.ContentTypeAlert: + r.Content = &alert.Alert{} + case protocol.ContentTypeHandshake: + r.Content = &handshake.Handshake{} + case protocol.ContentTypeApplicationData: + r.Content = &protocol.ApplicationData{} + default: + return errInvalidContentType + } + + return r.Content.Unmarshal(data[HeaderSize:]) +} + +// UnpackDatagram extracts all RecordLayer messages from a single datagram. +// Note that as with TLS, multiple handshake messages may be placed in +// the same DTLS record, provided that there is room and that they are +// part of the same flight. Thus, there are two acceptable ways to pack +// two DTLS messages into the same datagram: in the same record or in +// separate records. +// https://tools.ietf.org/html/rfc6347#section-4.2.3 +func UnpackDatagram(buf []byte) ([][]byte, error) { + out := [][]byte{} + + for offset := 0; len(buf) != offset; { + if len(buf)-offset <= HeaderSize { + return nil, errInvalidPacketLength + } + + pktLen := (HeaderSize + int(binary.BigEndian.Uint16(buf[offset+11:]))) + if offset+pktLen > len(buf) { + return nil, errInvalidPacketLength + } + + out = append(out, buf[offset:offset+pktLen]) + offset += pktLen + } + + return out, nil +} diff --git a/vendor/github.com/pion/dtls/v2/pkg/protocol/version.go b/vendor/github.com/pion/dtls/v2/pkg/protocol/version.go new file mode 100644 index 0000000..d5ddb1d --- /dev/null +++ b/vendor/github.com/pion/dtls/v2/pkg/protocol/version.go @@ -0,0 +1,21 @@ +// Package protocol provides the DTLS wire format +package protocol + +// Version enums +var ( + Version1_0 = Version{Major: 0xfe, Minor: 0xff} //nolint:gochecknoglobals + Version1_2 = Version{Major: 0xfe, Minor: 0xfd} //nolint:gochecknoglobals +) + +// Version is the minor/major value in the RecordLayer +// and ClientHello/ServerHello +// +// https://tools.ietf.org/html/rfc4346#section-6.2.1 +type Version struct { + Major, Minor uint8 +} + +// Equal determines if two protocol versions are equal +func (v Version) Equal(x Version) bool { + return v.Major == x.Major && v.Minor == x.Minor +} diff --git a/vendor/github.com/pion/dtls/v2/renovate.json b/vendor/github.com/pion/dtls/v2/renovate.json new file mode 100644 index 0000000..4400fd9 --- /dev/null +++ b/vendor/github.com/pion/dtls/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/dtls/v2/resume.go b/vendor/github.com/pion/dtls/v2/resume.go new file mode 100644 index 0000000..40e55e4 --- /dev/null +++ b/vendor/github.com/pion/dtls/v2/resume.go @@ -0,0 +1,19 @@ +package dtls + +import ( + "context" + "net" +) + +// Resume imports an already established dtls connection using a specific dtls state +func Resume(state *State, conn net.Conn, config *Config) (*Conn, error) { + if err := state.initCipherSuite(); err != nil { + return nil, err + } + c, err := createConn(context.Background(), conn, config, state.isClient, state) + if err != nil { + return nil, err + } + + return c, nil +} diff --git a/vendor/github.com/pion/dtls/v2/srtp_protection_profile.go b/vendor/github.com/pion/dtls/v2/srtp_protection_profile.go new file mode 100644 index 0000000..1c3ae55 --- /dev/null +++ b/vendor/github.com/pion/dtls/v2/srtp_protection_profile.go @@ -0,0 +1,14 @@ +package dtls + +import "github.com/pion/dtls/v2/pkg/protocol/extension" + +// SRTPProtectionProfile defines the parameters and options that are in effect for the SRTP processing +// https://tools.ietf.org/html/rfc5764#section-4.1.2 +type SRTPProtectionProfile = extension.SRTPProtectionProfile + +const ( + SRTP_AES128_CM_HMAC_SHA1_80 SRTPProtectionProfile = extension.SRTP_AES128_CM_HMAC_SHA1_80 // nolint + SRTP_AES128_CM_HMAC_SHA1_32 SRTPProtectionProfile = extension.SRTP_AES128_CM_HMAC_SHA1_32 // nolint + SRTP_AEAD_AES_128_GCM SRTPProtectionProfile = extension.SRTP_AEAD_AES_128_GCM // nolint + SRTP_AEAD_AES_256_GCM SRTPProtectionProfile = extension.SRTP_AEAD_AES_256_GCM // nolint +) diff --git a/vendor/github.com/pion/dtls/v2/state.go b/vendor/github.com/pion/dtls/v2/state.go new file mode 100644 index 0000000..53fca6a --- /dev/null +++ b/vendor/github.com/pion/dtls/v2/state.go @@ -0,0 +1,194 @@ +package dtls + +import ( + "bytes" + "encoding/gob" + "sync/atomic" + + "github.com/pion/dtls/v2/pkg/crypto/elliptic" + "github.com/pion/dtls/v2/pkg/crypto/prf" + "github.com/pion/dtls/v2/pkg/protocol/handshake" + "github.com/pion/transport/replaydetector" +) + +// State holds the dtls connection state and implements both encoding.BinaryMarshaler and encoding.BinaryUnmarshaler +type State struct { + localEpoch, remoteEpoch atomic.Value + localSequenceNumber []uint64 // uint48 + localRandom, remoteRandom handshake.Random + masterSecret []byte + cipherSuite CipherSuite // nil if a cipherSuite hasn't been chosen + + srtpProtectionProfile SRTPProtectionProfile // Negotiated SRTPProtectionProfile + PeerCertificates [][]byte + IdentityHint []byte + + isClient bool + + preMasterSecret []byte + extendedMasterSecret bool + + namedCurve elliptic.Curve + localKeypair *elliptic.Keypair + cookie []byte + handshakeSendSequence int + handshakeRecvSequence int + serverName string + remoteRequestedCertificate bool // Did we get a CertificateRequest + localCertificatesVerify []byte // cache CertificateVerify + localVerifyData []byte // cached VerifyData + localKeySignature []byte // cached keySignature + peerCertificatesVerified bool + + replayDetector []replaydetector.ReplayDetector +} + +type serializedState struct { + LocalEpoch uint16 + RemoteEpoch uint16 + LocalRandom [handshake.RandomLength]byte + RemoteRandom [handshake.RandomLength]byte + CipherSuiteID uint16 + MasterSecret []byte + SequenceNumber uint64 + SRTPProtectionProfile uint16 + PeerCertificates [][]byte + IdentityHint []byte + IsClient bool +} + +func (s *State) clone() *State { + serialized := s.serialize() + state := &State{} + state.deserialize(*serialized) + + return state +} + +func (s *State) serialize() *serializedState { + // Marshal random values + localRnd := s.localRandom.MarshalFixed() + remoteRnd := s.remoteRandom.MarshalFixed() + + epoch := s.localEpoch.Load().(uint16) + return &serializedState{ + LocalEpoch: epoch, + RemoteEpoch: s.remoteEpoch.Load().(uint16), + CipherSuiteID: uint16(s.cipherSuite.ID()), + MasterSecret: s.masterSecret, + SequenceNumber: atomic.LoadUint64(&s.localSequenceNumber[epoch]), + LocalRandom: localRnd, + RemoteRandom: remoteRnd, + SRTPProtectionProfile: uint16(s.srtpProtectionProfile), + PeerCertificates: s.PeerCertificates, + IdentityHint: s.IdentityHint, + IsClient: s.isClient, + } +} + +func (s *State) deserialize(serialized serializedState) { + // Set epoch values + epoch := serialized.LocalEpoch + s.localEpoch.Store(serialized.LocalEpoch) + s.remoteEpoch.Store(serialized.RemoteEpoch) + + for len(s.localSequenceNumber) <= int(epoch) { + s.localSequenceNumber = append(s.localSequenceNumber, uint64(0)) + } + + // Set random values + localRandom := &handshake.Random{} + localRandom.UnmarshalFixed(serialized.LocalRandom) + s.localRandom = *localRandom + + remoteRandom := &handshake.Random{} + remoteRandom.UnmarshalFixed(serialized.RemoteRandom) + s.remoteRandom = *remoteRandom + + s.isClient = serialized.IsClient + + // Set master secret + s.masterSecret = serialized.MasterSecret + + // Set cipher suite + s.cipherSuite = cipherSuiteForID(CipherSuiteID(serialized.CipherSuiteID), nil) + + atomic.StoreUint64(&s.localSequenceNumber[epoch], serialized.SequenceNumber) + s.srtpProtectionProfile = SRTPProtectionProfile(serialized.SRTPProtectionProfile) + + // Set remote certificate + s.PeerCertificates = serialized.PeerCertificates + s.IdentityHint = serialized.IdentityHint +} + +func (s *State) initCipherSuite() error { + if s.cipherSuite.IsInitialized() { + return nil + } + + localRandom := s.localRandom.MarshalFixed() + remoteRandom := s.remoteRandom.MarshalFixed() + + var err error + if s.isClient { + err = s.cipherSuite.Init(s.masterSecret, localRandom[:], remoteRandom[:], true) + } else { + err = s.cipherSuite.Init(s.masterSecret, remoteRandom[:], localRandom[:], false) + } + if err != nil { + return err + } + return nil +} + +// MarshalBinary is a binary.BinaryMarshaler.MarshalBinary implementation +func (s *State) MarshalBinary() ([]byte, error) { + serialized := s.serialize() + + var buf bytes.Buffer + enc := gob.NewEncoder(&buf) + if err := enc.Encode(*serialized); err != nil { + return nil, err + } + return buf.Bytes(), nil +} + +// UnmarshalBinary is a binary.BinaryUnmarshaler.UnmarshalBinary implementation +func (s *State) UnmarshalBinary(data []byte) error { + enc := gob.NewDecoder(bytes.NewBuffer(data)) + var serialized serializedState + if err := enc.Decode(&serialized); err != nil { + return err + } + + s.deserialize(serialized) + if err := s.initCipherSuite(); err != nil { + return err + } + return nil +} + +// ExportKeyingMaterial returns length bytes of exported key material in a new +// slice as defined in RFC 5705. +// This allows protocols to use DTLS for key establishment, but +// then use some of the keying material for their own purposes +func (s *State) ExportKeyingMaterial(label string, context []byte, length int) ([]byte, error) { + if s.localEpoch.Load().(uint16) == 0 { + return nil, errHandshakeInProgress + } else if len(context) != 0 { + return nil, errContextUnsupported + } else if _, ok := invalidKeyingLabels()[label]; ok { + return nil, errReservedExportKeyingMaterial + } + + localRandom := s.localRandom.MarshalFixed() + remoteRandom := s.remoteRandom.MarshalFixed() + + seed := []byte(label) + if s.isClient { + seed = append(append(seed, localRandom[:]...), remoteRandom[:]...) + } else { + seed = append(append(seed, remoteRandom[:]...), localRandom[:]...) + } + return prf.PHash(s.masterSecret, seed, length, s.cipherSuite.HashFunc()) +} diff --git a/vendor/github.com/pion/dtls/v2/util.go b/vendor/github.com/pion/dtls/v2/util.go new file mode 100644 index 0000000..745182d --- /dev/null +++ b/vendor/github.com/pion/dtls/v2/util.go @@ -0,0 +1,38 @@ +package dtls + +func findMatchingSRTPProfile(a, b []SRTPProtectionProfile) (SRTPProtectionProfile, bool) { + for _, aProfile := range a { + for _, bProfile := range b { + if aProfile == bProfile { + return aProfile, true + } + } + } + return 0, false +} + +func findMatchingCipherSuite(a, b []CipherSuite) (CipherSuite, bool) { //nolint + for _, aSuite := range a { + for _, bSuite := range b { + if aSuite.ID() == bSuite.ID() { + return aSuite, true + } + } + } + return nil, false +} + +func splitBytes(bytes []byte, splitLen int) [][]byte { + splitBytes := make([][]byte, 0) + numBytes := len(bytes) + for i := 0; i < numBytes; i += splitLen { + j := i + splitLen + if j > numBytes { + j = numBytes + } + + splitBytes = append(splitBytes, bytes[i:j]) + } + + return splitBytes +} |