直播:后台 JWT 推流、前台画中画;WebRTC 服务与 Nginx WebSocket 代理

Made-with: Cursor
This commit is contained in:
whm
2026-03-25 15:00:14 +08:00
parent b83ec91b1a
commit 7811adca66
1050 changed files with 146524 additions and 37 deletions

23
server/vendor/github.com/pion/dtls/v2/.editorconfig generated vendored Normal file
View File

@@ -0,0 +1,23 @@
# http://editorconfig.org/
# SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
# SPDX-License-Identifier: MIT
root = true
[*]
charset = utf-8
insert_final_newline = true
trim_trailing_whitespace = true
end_of_line = lf
[*.go]
indent_style = tab
indent_size = 4
[{*.yml,*.yaml}]
indent_style = space
indent_size = 2
# Makefiles always use tabs for indentation
[Makefile]
indent_style = tab

28
server/vendor/github.com/pion/dtls/v2/.gitignore generated vendored Normal file
View File

@@ -0,0 +1,28 @@
# SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
# SPDX-License-Identifier: MIT
### JetBrains IDE ###
#####################
.idea/
### Emacs Temporary Files ###
#############################
*~
### Folders ###
###############
bin/
vendor/
node_modules/
### Files ###
#############
*.ivf
*.ogg
tags
cover.out
*.sw[poe]
*.wasm
examples/sfu-ws/cert.pem
examples/sfu-ws/key.pem
wasm_exec.js

137
server/vendor/github.com/pion/dtls/v2/.golangci.yml generated vendored Normal file
View File

@@ -0,0 +1,137 @@
# SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
# SPDX-License-Identifier: MIT
linters-settings:
govet:
check-shadowing: true
misspell:
locale: US
exhaustive:
default-signifies-exhaustive: true
gomodguard:
blocked:
modules:
- github.com/pkg/errors:
recommendations:
- errors
forbidigo:
forbid:
- ^fmt.Print(f|ln)?$
- ^log.(Panic|Fatal|Print)(f|ln)?$
- ^os.Exit$
- ^panic$
- ^print(ln)?$
linters:
enable:
- asciicheck # Simple linter to check that your code does not contain non-ASCII identifiers
- bidichk # Checks for dangerous unicode character sequences
- bodyclose # checks whether HTTP response body is closed successfully
- contextcheck # check the function whether use a non-inherited context
- decorder # check declaration order and count of types, constants, variables and functions
- 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
- durationcheck # check for two durations multiplied together
- errcheck # Errcheck is a program for checking for unchecked errors in go programs. These unchecked errors can be critical bugs in some cases
- errchkjson # Checks types passed to the json encoding functions. Reports unsupported types and optionally reports occations, where the check for the returned error can be omitted.
- errname # Checks that sentinel errors are prefixed with the `Err` and error types are suffixed with the `Error`.
- errorlint # errorlint is a linter for that can be used to find code that will cause problems with the error wrapping scheme introduced in Go 1.13.
- exhaustive # check exhaustiveness of enum switch statements
- exportloopref # checks for pointers to enclosing loop variables
- forbidigo # Forbids identifiers
- forcetypeassert # finds forced type assertions
- 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
- gomoddirectives # Manage the use of 'replace', 'retract', and 'excludes' directives in go.mod.
- 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
- grouper # An analyzer to analyze expression groups.
- importas # Enforces consistent import aliases
- 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
- nilerr # Finds the code that returns nil even if it checks that the error is not nil.
- nilnil # Checks that there is no simultaneous return of `nil` error and an invalid value.
- noctx # noctx finds sending http request without context.Context
- predeclared # find code that shadows one of Go's predeclared identifiers
- revive # golint replacement, finds style mistakes
- staticcheck # Staticcheck is a go vet on steroids, applying a ton of static analysis checks
- stylecheck # Stylecheck is a replacement for golint
- tagliatelle # Checks the struct tags.
- tenv # tenv is analyzer that detects using os.Setenv instead of t.Setenv since Go1.17
- tparallel # tparallel detects inappropriate usage of t.Parallel() method in your Go test codes
- 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
- wastedassign # wastedassign finds wasted assignment statements
- whitespace # Tool for detection of leading and trailing whitespace
disable:
- containedctx # containedctx is a linter that detects struct contained context.Context field
- cyclop # checks function and package cyclomatic complexity
- exhaustivestruct # Checks if all struct's fields are initialized
- 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.
- ifshort # Checks that your code uses short syntax for if-statements whenever possible
- ireturn # Accept Interfaces, Return Concrete Types
- lll # Reports long lines
- maintidx # maintidx measures the maintainability index of each function.
- makezero # Finds slice declarations with non-zero initial length
- 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
- paralleltest # paralleltest detects missing usage of t.Parallel() method in your Go test
- prealloc # Finds slice declarations that could potentially be preallocated
- promlinter # Check Prometheus metrics naming via promlint
- rowserrcheck # checks whether Err of rows is checked successfully
- sqlclosecheck # Checks that sql.Rows and sql.Stmt are closed.
- testpackage # linter that makes you use a separate _test package
- thelper # thelper detects golang test helpers without t.Helper() call and checks the consistency of test helpers
- varnamelen # checks that the length of a variable's name matches its scope
- wrapcheck # Checks that errors returned from external packages are wrapped
- 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
- forbidigo
# Allow complex main function in examples
- path: examples
text: "of func `main` is high"
linters:
- gocognit
# Allow forbidden identifiers in examples
- path: examples
linters:
- forbidigo
# Allow forbidden identifiers in CLI commands
- path: cmd
linters:
- forbidigo
run:
skip-dirs-use-default: false

View File

@@ -0,0 +1,5 @@
# SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
# SPDX-License-Identifier: MIT
builds:
- skip: true

57
server/vendor/github.com/pion/dtls/v2/AUTHORS.txt generated vendored Normal file
View File

@@ -0,0 +1,57 @@
# Thank you to everyone that made Pion possible. If you are interested in contributing
# we would love to have you https://github.com/pion/webrtc/wiki/Contributing
#
# This file is auto generated, using git to list all individuals contributors.
# see https://github.com/pion/.goassets/blob/master/scripts/generate-authors.sh for the scripting
Aleksandr Razumov <ar@gortc.io>
alvarowolfx <alvarowolfx@gmail.com>
Arlo Breault <arlolra@gmail.com>
Atsushi Watanabe <atsushi.w@ieee.org>
backkem <mail@backkem.me>
bjdgyc <bjdgyc@163.com>
boks1971 <raja.gobi@tutanota.com>
Bragadeesh <bragboy@gmail.com>
Carson Hoffman <c@rsonhoffman.com>
Cecylia Bocovich <cohosh@torproject.org>
Chris Hiszpanski <thinkski@users.noreply.github.com>
cnderrauber <zengjie9004@gmail.com>
Daniele Sluijters <daenney@users.noreply.github.com>
folbrich <frank.olbricht@gmail.com>
Hayden James <hayden.james@gmail.com>
Hugo Arregui <hugo.arregui@gmail.com>
Hugo Arregui <hugo@decentraland.org>
igolaizola <11333576+igolaizola@users.noreply.github.com>
Jeffrey Stoke <me@arhat.dev>
Jeroen de Bruijn <vidavidorra+jdbruijn@gmail.com>
Jeroen de Bruijn <vidavidorra@gmail.com>
Jim Wert <jimwert@gmail.com>
jinleileiking <jinleileiking@gmail.com>
Jozef Kralik <jojo.lwin@gmail.com>
Julien Salleyron <julien.salleyron@gmail.com>
Juliusz Chroboczek <jch@irif.fr>
Kegan Dougal <kegan@matrix.org>
Kevin Wang <kevmo314@gmail.com>
Lander Noterman <lander.noterman@basalte.be>
Len <len@hpcnt.com>
Lukas Lihotzki <lukas@lihotzki.de>
ManuelBk <26275612+ManuelBk@users.noreply.github.com>
Michael Zabka <zabka.michael@gmail.com>
Michiel De Backker <mail@backkem.me>
Rachel Chen <rachel@chens.email>
Robert Eperjesi <eperjesi@uber.com>
Ryan Gordon <ryan.gordon@getcruise.com>
Sam Lancia <sam.lancia@motorolasolutions.com>
Sean DuBois <duboisea@justin.tv>
Sean DuBois <seaduboi@amazon.com>
Sean DuBois <sean@siobud.com>
Shelikhoo <xiaokangwang@outlook.com>
Stefan Tatschner <stefan@rumpelsepp.org>
Steffen Vogel <post@steffenvogel.de>
Vadim <fffilimonov@yandex.ru>
Vadim Filimonov <fffilimonov@yandex.ru>
wmiao <wu.miao@viasat.com>
ZHENK <chengzhenyang@gmail.com>
吕海涛 <hi@taoshu.in>
# List of contributors not appearing in Git history

9
server/vendor/github.com/pion/dtls/v2/LICENSE generated vendored Normal file
View File

@@ -0,0 +1,9 @@
MIT License
Copyright (c) 2023 The Pion community <https://pion.ly>
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

151
server/vendor/github.com/pion/dtls/v2/README.md generated vendored Normal file
View File

@@ -0,0 +1,151 @@
<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>
<img alt="GitHub Workflow Status" src="https://img.shields.io/github/actions/workflow/status/pion/dtls/test.yaml">
<a href="https://pkg.go.dev/github.com/pion/dtls/v2"><img src="https://pkg.go.dev/badge/github.com/pion/dtls/v2.svg" alt="Go Reference"></a>
<a href="https://codecov.io/gh/pion/dtls"><img src="https://codecov.io/gh/pion/dtls/branch/master/graph/badge.svg" alt="Coverage Status"></a>
<a href="https://goreportcard.com/report/github.com/pion/dtls/v2"><img src="https://goreportcard.com/badge/github.com/pion/dtls/v2" alt="Go Report Card"></a>
<a href="LICENSE"><img src="https://img.shields.io/badge/License-MIT-yellow.svg" alt="License: MIT"></a>
</p>
<br>
Native [DTLS 1.2][rfc6347] implementation in the Go programming language.
A long term goal is a professional security review, and maybe an inclusion in stdlib.
### RFCs
#### Implemented
- **RFC 6347**: [Datagram Transport Layer Security Version 1.2][rfc6347]
- **RFC 5705**: [Keying Material Exporters for Transport Layer Security (TLS)][rfc5705]
- **RFC 7627**: [Transport Layer Security (TLS) - Session Hash and Extended Master Secret Extension][rfc7627]
- **RFC 7301**: [Transport Layer Security (TLS) - Application-Layer Protocol Negotiation Extension][rfc7301]
[rfc5289]: https://tools.ietf.org/html/rfc5289
[rfc5487]: https://tools.ietf.org/html/rfc5487
[rfc5489]: https://tools.ietf.org/html/rfc5489
[rfc5705]: https://tools.ietf.org/html/rfc5705
[rfc6347]: https://tools.ietf.org/html/rfc6347
[rfc6655]: https://tools.ietf.org/html/rfc6655
[rfc7301]: https://tools.ietf.org/html/rfc7301
[rfc7627]: https://tools.ietf.org/html/rfc7627
[rfc8422]: https://tools.ietf.org/html/rfc8422
### Goals/Progress
This will only be targeting DTLS 1.2, and the most modern/common cipher suites.
We would love contributions that fall under the 'Planned Features' and any bug fixes!
#### Current features
* DTLS 1.2 Client/Server
* Key Exchange via ECDHE(curve25519, nistp256, nistp384) and PSK
* Packet loss and re-ordering is handled during handshaking
* Key export ([RFC 5705][rfc5705])
* Serialization and Resumption of sessions
* Extended Master Secret extension ([RFC 7627][rfc7627])
* ALPN extension ([RFC 7301][rfc7301])
#### Supported ciphers
##### ECDHE
* TLS_ECDHE_ECDSA_WITH_AES_128_CCM ([RFC 6655][rfc6655])
* TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8 ([RFC 6655][rfc6655])
* TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 ([RFC 5289][rfc5289])
* TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 ([RFC 5289][rfc5289])
* TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 ([RFC 5289][rfc5289])
* TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 ([RFC 5289][rfc5289])
* TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA ([RFC 8422][rfc8422])
* TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA ([RFC 8422][rfc8422])
##### PSK
* TLS_PSK_WITH_AES_128_CCM ([RFC 6655][rfc6655])
* TLS_PSK_WITH_AES_128_CCM_8 ([RFC 6655][rfc6655])
* TLS_PSK_WITH_AES_256_CCM_8 ([RFC 6655][rfc6655])
* TLS_PSK_WITH_AES_128_GCM_SHA256 ([RFC 5487][rfc5487])
* TLS_PSK_WITH_AES_128_CBC_SHA256 ([RFC 5487][rfc5487])
##### ECDHE & PSK
* TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256 ([RFC 5489][rfc5489])
#### Planned Features
* 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
```
### Community
Pion has an active community on the [Slack](https://pion.ly/slack).
Follow the [Pion Twitter](https://twitter.com/_pion) for project updates and important WebRTC news.
We are always looking to support **your projects**. Please reach out if you have something to build!
If you need commercial support or don't want to use public methods you can contact us at [team@pion.ly](mailto:team@pion.ly)
### Contributing
Check out the [contributing wiki](https://github.com/pion/webrtc/wiki/Contributing) to join the group of amazing people making this project possible: [AUTHORS.txt](./AUTHORS.txt)
### License
MIT License - see [LICENSE](LICENSE) for full text

157
server/vendor/github.com/pion/dtls/v2/certificate.go generated vendored Normal file
View File

@@ -0,0 +1,157 @@
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
// SPDX-License-Identifier: MIT
package dtls
import (
"bytes"
"crypto/tls"
"crypto/x509"
"fmt"
"strings"
)
// ClientHelloInfo contains information from a ClientHello message in order to
// guide application logic in the GetCertificate.
type ClientHelloInfo struct {
// ServerName indicates the name of the server requested by the client
// in order to support virtual hosting. ServerName is only set if the
// client is using SNI (see RFC 4366, Section 3.1).
ServerName string
// CipherSuites lists the CipherSuites supported by the client (e.g.
// TLS_AES_128_GCM_SHA256, TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256).
CipherSuites []CipherSuiteID
}
// CertificateRequestInfo contains information from a server's
// CertificateRequest message, which is used to demand a certificate and proof
// of control from a client.
type CertificateRequestInfo struct {
// AcceptableCAs contains zero or more, DER-encoded, X.501
// Distinguished Names. These are the names of root or intermediate CAs
// that the server wishes the returned certificate to be signed by. An
// empty slice indicates that the server has no preference.
AcceptableCAs [][]byte
}
// SupportsCertificate returns nil if the provided certificate is supported by
// the server that sent the CertificateRequest. Otherwise, it returns an error
// describing the reason for the incompatibility.
// NOTE: original src: https://github.com/golang/go/blob/29b9a328d268d53833d2cc063d1d8b4bf6852675/src/crypto/tls/common.go#L1273
func (cri *CertificateRequestInfo) SupportsCertificate(c *tls.Certificate) error {
if len(cri.AcceptableCAs) == 0 {
return nil
}
for j, cert := range c.Certificate {
x509Cert := c.Leaf
// Parse the certificate if this isn't the leaf node, or if
// chain.Leaf was nil.
if j != 0 || x509Cert == nil {
var err error
if x509Cert, err = x509.ParseCertificate(cert); err != nil {
return fmt.Errorf("failed to parse certificate #%d in the chain: %w", j, err)
}
}
for _, ca := range cri.AcceptableCAs {
if bytes.Equal(x509Cert.RawIssuer, ca) {
return nil
}
}
}
return errNotAcceptableCertificateChain
}
func (c *handshakeConfig) setNameToCertificateLocked() {
nameToCertificate := make(map[string]*tls.Certificate)
for i := range c.localCertificates {
cert := &c.localCertificates[i]
x509Cert := cert.Leaf
if x509Cert == nil {
var parseErr error
x509Cert, parseErr = x509.ParseCertificate(cert.Certificate[0])
if parseErr != nil {
continue
}
}
if len(x509Cert.Subject.CommonName) > 0 {
nameToCertificate[strings.ToLower(x509Cert.Subject.CommonName)] = cert
}
for _, san := range x509Cert.DNSNames {
nameToCertificate[strings.ToLower(san)] = cert
}
}
c.nameToCertificate = nameToCertificate
}
func (c *handshakeConfig) getCertificate(clientHelloInfo *ClientHelloInfo) (*tls.Certificate, error) {
c.mu.Lock()
defer c.mu.Unlock()
if c.localGetCertificate != nil &&
(len(c.localCertificates) == 0 || len(clientHelloInfo.ServerName) > 0) {
cert, err := c.localGetCertificate(clientHelloInfo)
if cert != nil || err != nil {
return cert, err
}
}
if c.nameToCertificate == nil {
c.setNameToCertificateLocked()
}
if len(c.localCertificates) == 0 {
return nil, errNoCertificates
}
if len(c.localCertificates) == 1 {
// There's only one choice, so no point doing any work.
return &c.localCertificates[0], nil
}
if len(clientHelloInfo.ServerName) == 0 {
return &c.localCertificates[0], nil
}
name := strings.TrimRight(strings.ToLower(clientHelloInfo.ServerName), ".")
if cert, ok := c.nameToCertificate[name]; ok {
return cert, nil
}
// try replacing labels in the name with wildcards until we get a
// match.
labels := strings.Split(name, ".")
for i := range labels {
labels[i] = "*"
candidate := strings.Join(labels, ".")
if cert, ok := c.nameToCertificate[candidate]; ok {
return cert, nil
}
}
// If nothing matches, return the first certificate.
return &c.localCertificates[0], nil
}
// NOTE: original src: https://github.com/golang/go/blob/29b9a328d268d53833d2cc063d1d8b4bf6852675/src/crypto/tls/handshake_client.go#L974
func (c *handshakeConfig) getClientCertificate(cri *CertificateRequestInfo) (*tls.Certificate, error) {
c.mu.Lock()
defer c.mu.Unlock()
if c.localGetClientCertificate != nil {
return c.localGetClientCertificate(cri)
}
for i := range c.localCertificates {
chain := c.localCertificates[i]
if err := cri.SupportsCertificate(&chain); err != nil {
continue
}
return &chain, nil
}
// No acceptable certificate found. Don't send a certificate.
return new(tls.Certificate), nil
}

276
server/vendor/github.com/pion/dtls/v2/cipher_suite.go generated vendored Normal file
View File

@@ -0,0 +1,276 @@
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
// SPDX-License-Identifier: MIT
package dtls
import (
"crypto/ecdsa"
"crypto/ed25519"
"crypto/rsa"
"crypto/tls"
"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:revive,stylecheck
TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8 CipherSuiteID = ciphersuite.TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8 //nolint:revive,stylecheck
// AES-128-GCM-SHA256
TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 CipherSuiteID = ciphersuite.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 //nolint:revive,stylecheck
TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 CipherSuiteID = ciphersuite.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 //nolint:revive,stylecheck
TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 CipherSuiteID = ciphersuite.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 //nolint:revive,stylecheck
TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 CipherSuiteID = ciphersuite.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 //nolint:revive,stylecheck
// AES-256-CBC-SHA
TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA CipherSuiteID = ciphersuite.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA //nolint:revive,stylecheck
TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA CipherSuiteID = ciphersuite.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA //nolint:revive,stylecheck
TLS_PSK_WITH_AES_128_CCM CipherSuiteID = ciphersuite.TLS_PSK_WITH_AES_128_CCM //nolint:revive,stylecheck
TLS_PSK_WITH_AES_128_CCM_8 CipherSuiteID = ciphersuite.TLS_PSK_WITH_AES_128_CCM_8 //nolint:revive,stylecheck
TLS_PSK_WITH_AES_256_CCM_8 CipherSuiteID = ciphersuite.TLS_PSK_WITH_AES_256_CCM_8 //nolint:revive,stylecheck
TLS_PSK_WITH_AES_128_GCM_SHA256 CipherSuiteID = ciphersuite.TLS_PSK_WITH_AES_128_GCM_SHA256 //nolint:revive,stylecheck
TLS_PSK_WITH_AES_128_CBC_SHA256 CipherSuiteID = ciphersuite.TLS_PSK_WITH_AES_128_CBC_SHA256 //nolint:revive,stylecheck
TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256 CipherSuiteID = ciphersuite.TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256 //nolint:revive,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
)
// CipherSuiteKeyExchangeAlgorithm controls what exchange algorithm is using during the handshake for a CipherSuite
type CipherSuiteKeyExchangeAlgorithm = ciphersuite.KeyExchangeAlgorithm
// CipherSuiteKeyExchangeAlgorithm Bitmask
const (
CipherSuiteKeyExchangeAlgorithmNone CipherSuiteKeyExchangeAlgorithm = ciphersuite.KeyExchangeAlgorithmNone
CipherSuiteKeyExchangeAlgorithmPsk CipherSuiteKeyExchangeAlgorithm = ciphersuite.KeyExchangeAlgorithmPsk
CipherSuiteKeyExchangeAlgorithmEcdhe CipherSuiteKeyExchangeAlgorithm = ciphersuite.KeyExchangeAlgorithmEcdhe
)
var _ = allCipherSuites() // Necessary until this function isn't only used by Go 1.14
// CipherSuite is an interface that all DTLS CipherSuites must satisfy
type CipherSuite interface {
// String of CipherSuite, only used for logging
String() string
// ID of CipherSuite.
ID() CipherSuiteID
// What type of Certificate does this CipherSuite use
CertificateType() clientcertificate.Type
// What Hash function is used during verification
HashFunc() func() hash.Hash
// AuthenticationType controls what authentication method is using during the handshake
AuthenticationType() CipherSuiteAuthenticationType
// KeyExchangeAlgorithm controls what exchange algorithm is using during the handshake
KeyExchangeAlgorithm() CipherSuiteKeyExchangeAlgorithm
// ECC (Elliptic Curve Cryptography) determines whether ECC extesions will be send during handshake.
// https://datatracker.ietf.org/doc/html/rfc4492#page-10
ECC() bool
// Called when keying material has been generated, should initialize the internal cipher
Init(masterSecret, clientRandom, serverRandom []byte, isClient bool) error
IsInitialized() bool
Encrypt(pkt *recordlayer.RecordLayer, raw []byte) ([]byte, error)
Decrypt(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_256_CCM_8:
return ciphersuite.NewTLSPskWithAes256Ccm8()
case TLS_PSK_WITH_AES_128_GCM_SHA256:
return &ciphersuite.TLSPskWithAes128GcmSha256{}
case TLS_PSK_WITH_AES_128_CBC_SHA256:
return &ciphersuite.TLSPskWithAes128CbcSha256{}
case TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384:
return &ciphersuite.TLSEcdheEcdsaWithAes256GcmSha384{}
case TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384:
return &ciphersuite.TLSEcdheRsaWithAes256GcmSha384{}
case TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256:
return ciphersuite.NewTLSEcdhePskWithAes128CbcSha256()
}
if customCiphers != nil {
for _, c := range customCiphers() {
if c.ID() == id {
return c
}
}
}
return nil
}
// CipherSuites we support in order of preference
func defaultCipherSuites() []CipherSuite {
return []CipherSuite{
&ciphersuite.TLSEcdheEcdsaWithAes128GcmSha256{},
&ciphersuite.TLSEcdheRsaWithAes128GcmSha256{},
&ciphersuite.TLSEcdheEcdsaWithAes256CbcSha{},
&ciphersuite.TLSEcdheRsaWithAes256CbcSha{},
&ciphersuite.TLSEcdheEcdsaWithAes256GcmSha384{},
&ciphersuite.TLSEcdheRsaWithAes256GcmSha384{},
}
}
func allCipherSuites() []CipherSuite {
return []CipherSuite{
ciphersuite.NewTLSEcdheEcdsaWithAes128Ccm(),
ciphersuite.NewTLSEcdheEcdsaWithAes128Ccm8(),
&ciphersuite.TLSEcdheEcdsaWithAes128GcmSha256{},
&ciphersuite.TLSEcdheRsaWithAes128GcmSha256{},
&ciphersuite.TLSEcdheEcdsaWithAes256CbcSha{},
&ciphersuite.TLSEcdheRsaWithAes256CbcSha{},
ciphersuite.NewTLSPskWithAes128Ccm(),
ciphersuite.NewTLSPskWithAes128Ccm8(),
ciphersuite.NewTLSPskWithAes256Ccm8(),
&ciphersuite.TLSPskWithAes128GcmSha256{},
&ciphersuite.TLSEcdheEcdsaWithAes256GcmSha384{},
&ciphersuite.TLSEcdheRsaWithAes256GcmSha384{},
}
}
func cipherSuiteIDs(cipherSuites []CipherSuite) []uint16 {
rtrn := []uint16{}
for _, c := range cipherSuites {
rtrn = append(rtrn, uint16(c.ID()))
}
return rtrn
}
func parseCipherSuites(userSelectedSuites []CipherSuiteID, customCipherSuites func() []CipherSuite, includeCertificateSuites, includePSKSuites bool) ([]CipherSuite, error) {
cipherSuitesForIDs := func(ids []CipherSuiteID) ([]CipherSuite, error) {
cipherSuites := []CipherSuite{}
for _, id := range ids {
c := cipherSuiteForID(id, nil)
if c == nil {
return nil, &invalidCipherSuiteError{id}
}
cipherSuites = append(cipherSuites, c)
}
return cipherSuites, nil
}
var (
cipherSuites []CipherSuite
err error
i int
)
if userSelectedSuites != nil {
cipherSuites, err = cipherSuitesForIDs(userSelectedSuites)
if err != nil {
return nil, err
}
} else {
cipherSuites = defaultCipherSuites()
}
// Put CustomCipherSuites before ID selected suites
if customCipherSuites != nil {
cipherSuites = append(customCipherSuites(), cipherSuites...)
}
var foundCertificateSuite, foundPSKSuite, foundAnonymousSuite bool
for _, c := range cipherSuites {
switch {
case includeCertificateSuites && c.AuthenticationType() == CipherSuiteAuthenticationTypeCertificate:
foundCertificateSuite = true
case includePSKSuites && c.AuthenticationType() == CipherSuiteAuthenticationTypePreSharedKey:
foundPSKSuite = true
case c.AuthenticationType() == CipherSuiteAuthenticationTypeAnonymous:
foundAnonymousSuite = true
default:
continue
}
cipherSuites[i] = c
i++
}
switch {
case includeCertificateSuites && !foundCertificateSuite && !foundAnonymousSuite:
return nil, errNoAvailableCertificateCipherSuite
case includePSKSuites && !foundPSKSuite:
return nil, errNoAvailablePSKCipherSuite
case i == 0:
return nil, errNoAvailableCipherSuites
}
return cipherSuites[:i], nil
}
func filterCipherSuitesForCertificate(cert *tls.Certificate, cipherSuites []CipherSuite) []CipherSuite {
if cert == nil || cert.PrivateKey == nil {
return cipherSuites
}
var certType clientcertificate.Type
switch cert.PrivateKey.(type) {
case ed25519.PrivateKey, *ecdsa.PrivateKey:
certType = clientcertificate.ECDSASign
case *rsa.PrivateKey:
certType = clientcertificate.RSASign
}
filtered := []CipherSuite{}
for _, c := range cipherSuites {
if c.AuthenticationType() != CipherSuiteAuthenticationTypeCertificate || certType == c.CertificateType() {
filtered = append(filtered, c)
}
}
return filtered
}

View File

@@ -0,0 +1,44 @@
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
// SPDX-License-Identifier: MIT
//go:build go1.14
// +build go1.14
package dtls
import (
"crypto/tls"
)
// VersionDTLS12 is the DTLS version in the same style as
// VersionTLSXX from crypto/tls
const VersionDTLS12 = 0xfefd
// Convert from our cipherSuite interface to a tls.CipherSuite struct
func toTLSCipherSuite(c CipherSuite) *tls.CipherSuite {
return &tls.CipherSuite{
ID: uint16(c.ID()),
Name: c.String(),
SupportedVersions: []uint16{VersionDTLS12},
Insecure: false,
}
}
// CipherSuites returns a list of cipher suites currently implemented by this
// package, excluding those with security issues, which are returned by
// InsecureCipherSuites.
func CipherSuites() []*tls.CipherSuite {
suites := allCipherSuites()
res := make([]*tls.CipherSuite, len(suites))
for i, c := range suites {
res[i] = toTLSCipherSuite(c)
}
return res
}
// InsecureCipherSuites returns a list of cipher suites currently implemented by
// this package and which have security issues.
func InsecureCipherSuites() []*tls.CipherSuite {
var res []*tls.CipherSuite
return res
}

22
server/vendor/github.com/pion/dtls/v2/codecov.yml generated vendored Normal file
View File

@@ -0,0 +1,22 @@
#
# DO NOT EDIT THIS FILE
#
# It is automatically copied from https://github.com/pion/.goassets repository.
#
# SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
# SPDX-License-Identifier: MIT
coverage:
status:
project:
default:
# Allow decreasing 2% of total coverage to avoid noise.
threshold: 2%
patch:
default:
target: 70%
only_pulls: true
ignore:
- "examples/*"
- "examples/**/*"

View File

@@ -0,0 +1,12 @@
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
// SPDX-License-Identifier: MIT
package dtls
import "github.com/pion/dtls/v2/pkg/protocol"
func defaultCompressionMethods() []*protocol.CompressionMethod {
return []*protocol.CompressionMethod{
{},
}
}

253
server/vendor/github.com/pion/dtls/v2/config.go generated vendored Normal file
View File

@@ -0,0 +1,253 @@
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
// SPDX-License-Identifier: MIT
package dtls
import (
"context"
"crypto/ecdsa"
"crypto/ed25519"
"crypto/rsa"
"crypto/tls"
"crypto/x509"
"io"
"time"
"github.com/pion/dtls/v2/pkg/crypto/elliptic"
"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
// VerifyConnection, if not nil, is called after normal certificate
// verification/PSK and after VerifyPeerCertificate by either a TLS client
// or server. If it returns a non-nil error, the handshake is aborted
// and that error results.
//
// If normal verification fails then the handshake will abort before
// considering this callback. This callback will run for all connections
// regardless of InsecureSkipVerify or ClientAuth settings.
VerifyConnection func(*State) error
// RootCAs defines the set of root certificate authorities
// that one peer uses when verifying the other peer's certificates.
// If RootCAs is nil, TLS uses the host's root CA set.
RootCAs *x509.CertPool
// ClientCAs defines the set of root certificate authorities
// that servers use if required to verify a client certificate
// by the policy in ClientAuth.
ClientCAs *x509.CertPool
// ServerName is used to verify the hostname on the returned
// certificates unless InsecureSkipVerify is given.
ServerName string
LoggerFactory logging.LoggerFactory
// 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
// SessionStore is the container to store session for resumption.
SessionStore SessionStore
// List of application protocols the peer supports, for ALPN
SupportedProtocols []string
// List of Elliptic Curves to use
//
// If an ECC ciphersuite is configured and EllipticCurves is empty
// it will default to X25519, P-256, P-384 in this specific order.
EllipticCurves []elliptic.Curve
// GetCertificate returns a Certificate based on the given
// ClientHelloInfo. It will only be called if the client supplies SNI
// information or if Certificates is empty.
//
// If GetCertificate is nil or returns nil, then the certificate is
// retrieved from NameToCertificate. If NameToCertificate is nil, the
// best element of Certificates will be used.
GetCertificate func(*ClientHelloInfo) (*tls.Certificate, error)
// GetClientCertificate, if not nil, is called when a server requests a
// certificate from a client. If set, the contents of Certificates will
// be ignored.
//
// If GetClientCertificate returns an error, the handshake will be
// aborted and that error will be returned. Otherwise
// GetClientCertificate must return a non-nil Certificate. If
// Certificate.Certificate is empty then no certificate will be sent to
// the server. If this is unacceptable to the server then it may abort
// the handshake.
GetClientCertificate func(*CertificateRequestInfo) (*tls.Certificate, error)
// InsecureSkipVerifyHello, if true and when acting as server, allow client to
// skip hello verify phase and receive ServerHello after initial ClientHello.
// This have implication on DoS attack resistance.
InsecureSkipVerifyHello bool
}
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()
}
func (c *Config) includeCertificateSuites() bool {
return c.PSK == nil || len(c.Certificates) > 0 || c.GetCertificate != nil || c.GetClientCertificate != nil
}
const defaultMTU = 1200 // bytes
var defaultCurves = []elliptic.Curve{elliptic.X25519, elliptic.P256, elliptic.P384} //nolint:gochecknoglobals
// PSKCallback is called once we have the remote's PSKIdentityHint.
// If the remote provided none it will be nil
type PSKCallback func([]byte) ([]byte, error)
// ClientAuthType declares the policy the server will follow for
// TLS Client Authentication.
type ClientAuthType int
// ClientAuthType enums
const (
NoClientCert ClientAuthType = iota
RequestClientCert
RequireAnyClientCert
VerifyClientCertIfGiven
RequireAndVerifyClientCert
)
// ExtendedMasterSecretType declares the policy the client and server
// will follow for the Extended Master Secret extension
type ExtendedMasterSecretType int
// ExtendedMasterSecretType enums
const (
RequestExtendedMasterSecret ExtendedMasterSecretType = iota
RequireExtendedMasterSecret
DisableExtendedMasterSecret
)
func validateConfig(config *Config) error {
switch {
case config == nil:
return errNoConfigProvided
case config.PSKIdentityHint != nil && config.PSK == nil:
return errIdentityNoPSK
}
for _, cert := range config.Certificates {
if cert.Certificate == nil {
return errInvalidCertificate
}
if cert.PrivateKey != nil {
switch cert.PrivateKey.(type) {
case ed25519.PrivateKey:
case *ecdsa.PrivateKey:
case *rsa.PrivateKey:
default:
return errInvalidPrivateKey
}
}
}
_, err := parseCipherSuites(config.CipherSuites, config.CustomCipherSuites, config.includeCertificateSuites(), config.PSK != nil)
return err
}

1057
server/vendor/github.com/pion/dtls/v2/conn.go generated vendored Normal file

File diff suppressed because it is too large Load Diff

228
server/vendor/github.com/pion/dtls/v2/crypto.go generated vendored Normal file
View File

@@ -0,0 +1,228 @@
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
// SPDX-License-Identifier: MIT
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) {
if p, ok := privateKey.(ed25519.PrivateKey); ok {
// https://pkg.go.dev/crypto/ed25519#PrivateKey.Sign
// Sign signs the given message with priv. Ed25519 performs two passes over
// messages to be signed and therefore cannot handle pre-hashed messages.
return p.Sign(rand.Reader, handshakeBodies, crypto.Hash(0))
}
h := sha256.New()
if _, err := h.Write(handshakeBodies); err != nil {
return nil, err
}
hashed := h.Sum(nil)
switch p := privateKey.(type) {
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)
}

5
server/vendor/github.com/pion/dtls/v2/dtls.go generated vendored Normal file
View File

@@ -0,0 +1,5 @@
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
// SPDX-License-Identifier: MIT
// Package dtls implements Datagram Transport Layer Security (DTLS) 1.2
package dtls

157
server/vendor/github.com/pion/dtls/v2/errors.go generated vendored Normal file
View File

@@ -0,0 +1,157 @@
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
// SPDX-License-Identifier: MIT
package dtls
import (
"context"
"errors"
"fmt"
"io"
"net"
"os"
"github.com/pion/dtls/v2/pkg/protocol"
"github.com/pion/dtls/v2/pkg/protocol/alert"
)
// Typed errors
var (
ErrConnClosed = &FatalError{Err: errors.New("conn is closed")} //nolint:goerr113
errDeadlineExceeded = &TimeoutError{Err: fmt.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
errNotAcceptableCertificateChain = &FatalError{Err: errors.New("certificate chain is not signed by an acceptable CA")} //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
errFailedToAccessPoolReadBuffer = &InternalError{Err: errors.New("failed to access pool read buffer")} //nolint:goerr113
errFragmentBufferOverflow = &InternalError{Err: errors.New("fragment buffer overflow")} //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
// errInvalidCipherSuite indicates an attempt at using an unsupported cipher suite.
type invalidCipherSuiteError struct {
id CipherSuiteID
}
func (e *invalidCipherSuiteError) Error() string {
return fmt.Sprintf("CipherSuite with id(%d) is not valid", e.id)
}
func (e *invalidCipherSuiteError) Is(err error) bool {
var other *invalidCipherSuiteError
if errors.As(err, &other) {
return e.id == other.id
}
return false
}
// errAlert wraps DTLS alert notification as an error
type alertError struct {
*alert.Alert
}
func (e *alertError) Error() string {
return fmt.Sprintf("alert: %s", e.Alert.String())
}
func (e *alertError) IsFatalOrCloseNotify() bool {
return e.Level == alert.Fatal || e.Description == alert.CloseNotify
}
func (e *alertError) Is(err error) bool {
var other *alertError
if errors.As(err, &other) {
return e.Level == other.Level && e.Description == other.Description
}
return false
}
// netError translates an error from underlying Conn to corresponding net.Error.
func netError(err error) error {
switch {
case errors.Is(err, io.EOF), errors.Is(err, context.Canceled), errors.Is(err, context.DeadlineExceeded):
// Return io.EOF and context errors as is.
return err
}
var (
ne net.Error
opError *net.OpError
se *os.SyscallError
)
if errors.As(err, &opError) {
if errors.As(opError, &se) {
if se.Timeout() {
return &TimeoutError{Err: err}
}
if isOpErrorTemporary(se) {
return &TemporaryError{Err: err}
}
}
}
if errors.As(err, &ne) {
return err
}
return &FatalError{Err: err}
}

22
server/vendor/github.com/pion/dtls/v2/errors_errno.go generated vendored Normal file
View File

@@ -0,0 +1,22 @@
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
// SPDX-License-Identifier: MIT
//go:build aix || darwin || dragonfly || freebsd || linux || nacl || nacljs || netbsd || openbsd || solaris || windows
// +build aix darwin dragonfly freebsd linux nacl nacljs netbsd openbsd solaris windows
// For systems having syscall.Errno.
// Update build targets by following command:
// $ grep -R ECONN $(go env GOROOT)/src/syscall/zerrors_*.go \
// | tr "." "_" | cut -d"_" -f"2" | sort | uniq
package dtls
import (
"errors"
"os"
"syscall"
)
func isOpErrorTemporary(err *os.SyscallError) bool {
return errors.Is(err.Err, syscall.ECONNREFUSED)
}

View File

@@ -0,0 +1,18 @@
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
// SPDX-License-Identifier: MIT
//go:build !aix && !darwin && !dragonfly && !freebsd && !linux && !nacl && !nacljs && !netbsd && !openbsd && !solaris && !windows
// +build !aix,!darwin,!dragonfly,!freebsd,!linux,!nacl,!nacljs,!netbsd,!openbsd,!solaris,!windows
// For systems without syscall.Errno.
// Build targets must be inverse of errors_errno.go
package dtls
import (
"os"
)
func isOpErrorTemporary(err *os.SyscallError) bool {
return false
}

104
server/vendor/github.com/pion/dtls/v2/flight.go generated vendored Normal file
View File

@@ -0,0 +1,104 @@
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
// SPDX-License-Identifier: MIT
package dtls
/*
DTLS messages are grouped into a series of message flights, according
to the diagrams below. Although each flight of messages may consist
of a number of messages, they should be viewed as monolithic for the
purpose of timeout and retransmission.
https://tools.ietf.org/html/rfc4347#section-4.2.4
Message flights for full handshake:
Client Server
------ ------
Waiting Flight 0
ClientHello --------> Flight 1
<------- HelloVerifyRequest Flight 2
ClientHello --------> Flight 3
ServerHello \
Certificate* \
ServerKeyExchange* Flight 4
CertificateRequest* /
<-------- ServerHelloDone /
Certificate* \
ClientKeyExchange \
CertificateVerify* Flight 5
[ChangeCipherSpec] /
Finished --------> /
[ChangeCipherSpec] \ Flight 6
<-------- Finished /
Message flights for session-resuming handshake (no cookie exchange):
Client Server
------ ------
Waiting Flight 0
ClientHello --------> Flight 1
ServerHello \
[ChangeCipherSpec] Flight 4b
<-------- Finished /
[ChangeCipherSpec] \ Flight 5b
Finished --------> /
[ChangeCipherSpec] \ Flight 6
<-------- Finished /
*/
type flightVal uint8
const (
flight0 flightVal = iota + 1
flight1
flight2
flight3
flight4
flight4b
flight5
flight5b
flight6
)
func (f flightVal) String() string {
switch f {
case flight0:
return "Flight 0"
case flight1:
return "Flight 1"
case flight2:
return "Flight 2"
case flight3:
return "Flight 3"
case flight4:
return "Flight 4"
case flight4b:
return "Flight 4b"
case flight5:
return "Flight 5"
case flight5b:
return "Flight 5b"
case flight6:
return "Flight 6"
default:
return "Invalid Flight"
}
}
func (f flightVal) isLastSendFlight() bool {
return f == flight6 || f == flight5b
}
func (f flightVal) isLastRecvFlight() bool {
return f == flight5 || f == flight4b
}

138
server/vendor/github.com/pion/dtls/v2/flight0handler.go generated vendored Normal file
View File

@@ -0,0 +1,138 @@
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
// SPDX-License-Identifier: MIT
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(_ context.Context, _ flightConn, state *State, cache *handshakeCache, cfg *handshakeConfig) (flightVal, *alert.Alert, error) {
seq, msgs, ok := cache.fullPullMap(0, state.cipherSuite,
handshakeCachePullRule{handshake.TypeClientHello, cfg.initialEpoch, true, false},
)
if !ok {
// No valid message received. Keep reading
return 0, nil, nil
}
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.setSRTPProtectionProfile(profile)
case *extension.UseExtendedMasterSecret:
if cfg.extendedMasterSecret != DisableExtendedMasterSecret {
state.extendedMasterSecret = true
}
case *extension.ServerName:
state.serverName = e.ServerName // remote server name
case *extension.ALPN:
state.peerSupportedProtocols = e.ProtocolNameList
}
}
if cfg.extendedMasterSecret == RequireExtendedMasterSecret && !state.extendedMasterSecret {
return 0, &alert.Alert{Level: alert.Fatal, Description: alert.InsufficientSecurity}, errServerRequiredButNoClientEMS
}
if state.localKeypair == nil {
var err error
state.localKeypair, err = elliptic.GenerateKeypair(state.namedCurve)
if err != nil {
return 0, &alert.Alert{Level: alert.Fatal, Description: alert.IllegalParameter}, err
}
}
nextFlight := flight2
if cfg.insecureSkipHelloVerify {
nextFlight = flight4
}
return handleHelloResume(clientHello.SessionID, state, cfg, nextFlight)
}
func handleHelloResume(sessionID []byte, state *State, cfg *handshakeConfig, next flightVal) (flightVal, *alert.Alert, error) {
if len(sessionID) > 0 && cfg.sessionStore != nil {
if s, err := cfg.sessionStore.Get(sessionID); err != nil {
return 0, &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}, err
} else if s.ID != nil {
cfg.log.Tracef("[handshake] resume session: %x", sessionID)
state.SessionID = sessionID
state.masterSecret = s.Secret
if err := state.initCipherSuite(); err != nil {
return 0, &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}, err
}
clientRandom := state.localRandom.MarshalFixed()
cfg.writeKeyLog(keyLogLabelTLS12, clientRandom[:], state.masterSecret)
return flight4b, nil, nil
}
}
return next, nil, nil
}
func flight0Generate(_ flightConn, state *State, _ *handshakeCache, cfg *handshakeConfig) ([]*packet, *alert.Alert, error) {
// Initialize
if !cfg.insecureSkipHelloVerify {
state.cookie = make([]byte, cookieLength)
if _, err := rand.Read(state.cookie); err != nil {
return nil, nil, err
}
}
var zeroEpoch uint16
state.localEpoch.Store(zeroEpoch)
state.remoteEpoch.Store(zeroEpoch)
state.namedCurve = defaultNamedCurve
if err := state.localRandom.Populate(); err != nil {
return nil, nil, err
}
return nil, nil, nil
}

141
server/vendor/github.com/pion/dtls/v2/flight1handler.go generated vendored Normal file
View File

@@ -0,0 +1,141 @@
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
// SPDX-License-Identifier: MIT
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, state.cipherSuite,
handshakeCachePullRule{handshake.TypeHelloVerifyRequest, cfg.initialEpoch, false, true},
handshakeCachePullRule{handshake.TypeServerHello, cfg.initialEpoch, false, true},
)
if !ok {
// No valid message received. Keep reading
return 0, nil, nil
}
if _, ok := msgs[handshake.TypeServerHello]; ok {
// Flight1 and flight2 were skipped.
// Parse as flight3.
return flight3Parse(ctx, 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, _ *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,
},
}
var setEllipticCurveCryptographyClientHelloExtensions bool
for _, c := range cfg.localCipherSuites {
if c.ECC() {
setEllipticCurveCryptographyClientHelloExtensions = true
break
}
}
if setEllipticCurveCryptographyClientHelloExtensions {
extensions = append(extensions, []extension.Extension{
&extension.SupportedEllipticCurves{
EllipticCurves: cfg.ellipticCurves,
},
&extension.SupportedPointFormats{
PointFormats: []elliptic.CurvePointFormat{elliptic.CurvePointFormatUncompressed},
},
}...)
}
if len(cfg.localSRTPProtectionProfiles) > 0 {
extensions = append(extensions, &extension.UseSRTP{
ProtectionProfiles: cfg.localSRTPProtectionProfiles,
})
}
if cfg.extendedMasterSecret == RequestExtendedMasterSecret ||
cfg.extendedMasterSecret == RequireExtendedMasterSecret {
extensions = append(extensions, &extension.UseExtendedMasterSecret{
Supported: true,
})
}
if len(cfg.serverName) > 0 {
extensions = append(extensions, &extension.ServerName{ServerName: cfg.serverName})
}
if len(cfg.supportedProtocols) > 0 {
extensions = append(extensions, &extension.ALPN{ProtocolNameList: cfg.supportedProtocols})
}
if cfg.sessionStore != nil {
cfg.log.Tracef("[handshake] try to resume session")
if s, err := cfg.sessionStore.Get(c.sessionKey()); err != nil {
return nil, &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}, err
} else if s.ID != nil {
cfg.log.Tracef("[handshake] get saved session: %x", s.ID)
state.SessionID = s.ID
state.masterSecret = s.Secret
}
}
return []*packet{
{
record: &recordlayer.RecordLayer{
Header: recordlayer.Header{
Version: protocol.Version1_2,
},
Content: &handshake.Handshake{
Message: &handshake.MessageClientHello{
Version: protocol.Version1_2,
SessionID: state.SessionID,
Cookie: state.cookie,
Random: state.localRandom,
CipherSuiteIDs: cipherSuiteIDs(cfg.localCipherSuites),
CompressionMethods: defaultCompressionMethods(),
Extensions: extensions,
},
},
},
},
}, nil, nil
}

View File

@@ -0,0 +1,64 @@
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
// SPDX-License-Identifier: MIT
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, state.cipherSuite,
handshakeCachePullRule{handshake.TypeClientHello, cfg.initialEpoch, true, false},
)
if !ok {
// Client may retransmit the first ClientHello when HelloVerifyRequest is dropped.
// Parse as flight 0 in this case.
return flight0Parse(ctx, c, state, cache, cfg)
}
state.handshakeRecvSequence = seq
var clientHello *handshake.MessageClientHello
// Validate type
if clientHello, ok = msgs[handshake.TypeClientHello].(*handshake.MessageClientHello); !ok {
return 0, &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}, nil
}
if !clientHello.Version.Equal(protocol.Version1_2) {
return 0, &alert.Alert{Level: alert.Fatal, Description: alert.ProtocolVersion}, errUnsupportedProtocolVersion
}
if len(clientHello.Cookie) == 0 {
return 0, nil, nil
}
if !bytes.Equal(state.cookie, clientHello.Cookie) {
return 0, &alert.Alert{Level: alert.Fatal, Description: alert.AccessDenied}, errCookieMismatch
}
return flight4, nil, nil
}
func flight2Generate(_ flightConn, state *State, _ *handshakeCache, _ *handshakeConfig) ([]*packet, *alert.Alert, error) {
state.handshakeSendSequence = 0
return []*packet{
{
record: &recordlayer.RecordLayer{
Header: recordlayer.Header{
Version: protocol.Version1_2,
},
Content: &handshake.Handshake{
Message: &handshake.MessageHelloVerifyRequest{
Version: protocol.Version1_2,
Cookie: state.cookie,
},
},
},
},
}, nil, nil
}

291
server/vendor/github.com/pion/dtls/v2/flight3handler.go generated vendored Normal file
View File

@@ -0,0 +1,291 @@
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
// SPDX-License-Identifier: MIT
package dtls
import (
"bytes"
"context"
"github.com/pion/dtls/v2/internal/ciphersuite/types"
"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, state.cipherSuite,
handshakeCachePullRule{handshake.TypeHelloVerifyRequest, cfg.initialEpoch, false, true},
)
if ok {
if h, msgOk := msgs[handshake.TypeHelloVerifyRequest].(*handshake.MessageHelloVerifyRequest); msgOk {
// DTLS 1.2 clients must not assume that the server will use the protocol version
// specified in HelloVerifyRequest message. RFC 6347 Section 4.2.1
if !h.Version.Equal(protocol.Version1_0) && !h.Version.Equal(protocol.Version1_2) {
return 0, &alert.Alert{Level: alert.Fatal, Description: alert.ProtocolVersion}, errUnsupportedProtocolVersion
}
state.cookie = append([]byte{}, h.Cookie...)
state.handshakeRecvSequence = seq
return flight3, nil, nil
}
}
_, msgs, ok = cache.fullPullMap(state.handshakeRecvSequence, state.cipherSuite,
handshakeCachePullRule{handshake.TypeServerHello, cfg.initialEpoch, false, false},
)
if !ok {
// Don't have enough messages. Keep reading
return 0, nil, nil
}
if h, msgOk := msgs[handshake.TypeServerHello].(*handshake.MessageServerHello); msgOk {
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, found := findMatchingSRTPProfile(e.ProtectionProfiles, cfg.localSRTPProtectionProfiles)
if !found {
return 0, &alert.Alert{Level: alert.Fatal, Description: alert.IllegalParameter}, errClientNoMatchingSRTPProfile
}
state.setSRTPProtectionProfile(profile)
case *extension.UseExtendedMasterSecret:
if cfg.extendedMasterSecret != DisableExtendedMasterSecret {
state.extendedMasterSecret = true
}
case *extension.ALPN:
if len(e.ProtocolNameList) > 1 { // This should be exactly 1, the zero case is handle when unmarshalling
return 0, &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}, extension.ErrALPNInvalidFormat // Meh, internal error?
}
state.NegotiatedProtocol = e.ProtocolNameList[0]
}
}
if cfg.extendedMasterSecret == RequireExtendedMasterSecret && !state.extendedMasterSecret {
return 0, &alert.Alert{Level: alert.Fatal, Description: alert.InsufficientSecurity}, errClientRequiredButNoServerEMS
}
if len(cfg.localSRTPProtectionProfiles) > 0 && state.getSRTPProtectionProfile() == 0 {
return 0, &alert.Alert{Level: alert.Fatal, Description: alert.InsufficientSecurity}, errRequestedButNoSRTPExtension
}
remoteCipherSuite := cipherSuiteForID(CipherSuiteID(*h.CipherSuiteID), cfg.customCipherSuites)
if remoteCipherSuite == nil {
return 0, &alert.Alert{Level: alert.Fatal, Description: alert.InsufficientSecurity}, errCipherSuiteNoIntersection
}
selectedCipherSuite, found := findMatchingCipherSuite([]CipherSuite{remoteCipherSuite}, cfg.localCipherSuites)
if !found {
return 0, &alert.Alert{Level: alert.Fatal, Description: alert.InsufficientSecurity}, errInvalidCipherSuite
}
state.cipherSuite = selectedCipherSuite
state.remoteRandom = h.Random
cfg.log.Tracef("[handshake] use cipher suite: %s", selectedCipherSuite.String())
if len(h.SessionID) > 0 && bytes.Equal(state.SessionID, h.SessionID) {
return handleResumption(ctx, c, state, cache, cfg)
}
if len(state.SessionID) > 0 {
cfg.log.Tracef("[handshake] clean old session : %s", state.SessionID)
if err := cfg.sessionStore.Del(state.SessionID); err != nil {
return 0, &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}, err
}
}
if cfg.sessionStore == nil {
state.SessionID = []byte{}
} else {
state.SessionID = h.SessionID
}
state.masterSecret = []byte{}
}
if cfg.localPSKCallback != nil {
seq, msgs, ok = cache.fullPullMap(state.handshakeRecvSequence+1, state.cipherSuite,
handshakeCachePullRule{handshake.TypeServerKeyExchange, cfg.initialEpoch, false, true},
handshakeCachePullRule{handshake.TypeServerHelloDone, cfg.initialEpoch, false, false},
)
} else {
seq, msgs, ok = cache.fullPullMap(state.handshakeRecvSequence+1, state.cipherSuite,
handshakeCachePullRule{handshake.TypeCertificate, cfg.initialEpoch, false, true},
handshakeCachePullRule{handshake.TypeServerKeyExchange, cfg.initialEpoch, false, false},
handshakeCachePullRule{handshake.TypeCertificateRequest, cfg.initialEpoch, false, true},
handshakeCachePullRule{handshake.TypeServerHelloDone, cfg.initialEpoch, false, false},
)
}
if !ok {
// Don't have enough messages. Keep reading
return 0, nil, nil
}
state.handshakeRecvSequence = seq
if h, ok := msgs[handshake.TypeCertificate].(*handshake.MessageCertificate); ok {
state.PeerCertificates = h.Certificate
} else if state.cipherSuite.AuthenticationType() == CipherSuiteAuthenticationTypeCertificate {
return 0, &alert.Alert{Level: alert.Fatal, Description: alert.NoCertificate}, errInvalidCertificate
}
if h, ok := msgs[handshake.TypeServerKeyExchange].(*handshake.MessageServerKeyExchange); ok {
alertPtr, err := handleServerKeyExchange(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 handleResumption(ctx context.Context, c flightConn, state *State, cache *handshakeCache, cfg *handshakeConfig) (flightVal, *alert.Alert, error) {
if err := state.initCipherSuite(); err != nil {
return 0, &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}, err
}
// Now, encrypted packets can be handled
if err := c.handleQueuedPackets(ctx); err != nil {
return 0, &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}, err
}
_, msgs, ok := cache.fullPullMap(state.handshakeRecvSequence+1, state.cipherSuite,
handshakeCachePullRule{handshake.TypeFinished, cfg.initialEpoch + 1, false, false},
)
if !ok {
// No valid message received. Keep reading
return 0, nil, nil
}
var finished *handshake.MessageFinished
if finished, ok = msgs[handshake.TypeFinished].(*handshake.MessageFinished); !ok {
return 0, &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}, nil
}
plainText := cache.pullAndMerge(
handshakeCachePullRule{handshake.TypeClientHello, cfg.initialEpoch, true, false},
handshakeCachePullRule{handshake.TypeServerHello, cfg.initialEpoch, false, false},
)
expectedVerifyData, err := prf.VerifyDataServer(state.masterSecret, plainText, state.cipherSuite.HashFunc())
if err != nil {
return 0, &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}, err
}
if !bytes.Equal(expectedVerifyData, finished.VerifyData) {
return 0, &alert.Alert{Level: alert.Fatal, Description: alert.HandshakeFailure}, errVerifyDataMismatch
}
clientRandom := state.localRandom.MarshalFixed()
cfg.writeKeyLog(keyLogLabelTLS12, clientRandom[:], state.masterSecret)
return flight5b, nil, nil
}
func handleServerKeyExchange(_ flightConn, state *State, cfg *handshakeConfig, h *handshake.MessageServerKeyExchange) (*alert.Alert, error) {
var err error
if state.cipherSuite == nil {
return &alert.Alert{Level: alert.Fatal, Description: alert.InsufficientSecurity}, errInvalidCipherSuite
}
if cfg.localPSKCallback != nil {
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
switch state.cipherSuite.KeyExchangeAlgorithm() {
case types.KeyExchangeAlgorithmPsk:
state.preMasterSecret = prf.PSKPreMasterSecret(psk)
case (types.KeyExchangeAlgorithmEcdhe | types.KeyExchangeAlgorithmPsk):
if state.localKeypair, err = elliptic.GenerateKeypair(h.NamedCurve); err != nil {
return &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}, err
}
state.preMasterSecret, err = prf.EcdhePSKPreMasterSecret(psk, h.PublicKey, state.localKeypair.PrivateKey, state.localKeypair.Curve)
if err != nil {
return &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}, err
}
default:
return &alert.Alert{Level: alert.Fatal, Description: alert.InsufficientSecurity}, errInvalidCipherSuite
}
} else {
if state.localKeypair, err = elliptic.GenerateKeypair(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 //nolint:nilnil
}
func flight3Generate(_ flightConn, state *State, _ *handshakeCache, cfg *handshakeConfig) ([]*packet, *alert.Alert, error) {
extensions := []extension.Extension{
&extension.SupportedSignatureAlgorithms{
SignatureHashAlgorithms: cfg.localSignatureSchemes,
},
&extension.RenegotiationInfo{
RenegotiatedConnection: 0,
},
}
if state.namedCurve != 0 {
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})
}
if len(cfg.supportedProtocols) > 0 {
extensions = append(extensions, &extension.ALPN{ProtocolNameList: cfg.supportedProtocols})
}
return []*packet{
{
record: &recordlayer.RecordLayer{
Header: recordlayer.Header{
Version: protocol.Version1_2,
},
Content: &handshake.Handshake{
Message: &handshake.MessageClientHello{
Version: protocol.Version1_2,
SessionID: state.SessionID,
Cookie: state.cookie,
Random: state.localRandom,
CipherSuiteIDs: cipherSuiteIDs(cfg.localCipherSuites),
CompressionMethods: defaultCompressionMethods(),
Extensions: extensions,
},
},
},
},
}, nil, nil
}

View File

@@ -0,0 +1,144 @@
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
// SPDX-License-Identifier: MIT
package dtls
import (
"bytes"
"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/extension"
"github.com/pion/dtls/v2/pkg/protocol/handshake"
"github.com/pion/dtls/v2/pkg/protocol/recordlayer"
)
func flight4bParse(_ context.Context, _ flightConn, state *State, cache *handshakeCache, cfg *handshakeConfig) (flightVal, *alert.Alert, error) {
_, msgs, ok := cache.fullPullMap(state.handshakeRecvSequence, state.cipherSuite,
handshakeCachePullRule{handshake.TypeFinished, cfg.initialEpoch + 1, true, false},
)
if !ok {
// No valid message received. Keep reading
return 0, nil, nil
}
var finished *handshake.MessageFinished
if finished, ok = msgs[handshake.TypeFinished].(*handshake.MessageFinished); !ok {
return 0, &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}, nil
}
plainText := cache.pullAndMerge(
handshakeCachePullRule{handshake.TypeClientHello, cfg.initialEpoch, true, false},
handshakeCachePullRule{handshake.TypeServerHello, cfg.initialEpoch, false, false},
handshakeCachePullRule{handshake.TypeFinished, cfg.initialEpoch + 1, false, false},
)
expectedVerifyData, err := prf.VerifyDataClient(state.masterSecret, plainText, state.cipherSuite.HashFunc())
if err != nil {
return 0, &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}, err
}
if !bytes.Equal(expectedVerifyData, finished.VerifyData) {
return 0, &alert.Alert{Level: alert.Fatal, Description: alert.HandshakeFailure}, errVerifyDataMismatch
}
// Other party may re-transmit the last flight. Keep state to be flight4b.
return flight4b, nil, nil
}
func flight4bGenerate(_ flightConn, state *State, cache *handshakeCache, cfg *handshakeConfig) ([]*packet, *alert.Alert, error) {
var pkts []*packet
extensions := []extension.Extension{&extension.RenegotiationInfo{
RenegotiatedConnection: 0,
}}
if (cfg.extendedMasterSecret == RequestExtendedMasterSecret ||
cfg.extendedMasterSecret == RequireExtendedMasterSecret) && state.extendedMasterSecret {
extensions = append(extensions, &extension.UseExtendedMasterSecret{
Supported: true,
})
}
if state.getSRTPProtectionProfile() != 0 {
extensions = append(extensions, &extension.UseSRTP{
ProtectionProfiles: []SRTPProtectionProfile{state.getSRTPProtectionProfile()},
})
}
selectedProto, err := extension.ALPNProtocolSelection(cfg.supportedProtocols, state.peerSupportedProtocols)
if err != nil {
return nil, &alert.Alert{Level: alert.Fatal, Description: alert.NoApplicationProtocol}, err
}
if selectedProto != "" {
extensions = append(extensions, &extension.ALPN{
ProtocolNameList: []string{selectedProto},
})
state.NegotiatedProtocol = selectedProto
}
cipherSuiteID := uint16(state.cipherSuite.ID())
serverHello := &handshake.Handshake{
Message: &handshake.MessageServerHello{
Version: protocol.Version1_2,
Random: state.localRandom,
SessionID: state.SessionID,
CipherSuiteID: &cipherSuiteID,
CompressionMethod: defaultCompressionMethods()[0],
Extensions: extensions,
},
}
serverHello.Header.MessageSequence = uint16(state.handshakeSendSequence)
if len(state.localVerifyData) == 0 {
plainText := cache.pullAndMerge(
handshakeCachePullRule{handshake.TypeClientHello, cfg.initialEpoch, true, false},
)
raw, err := serverHello.Marshal()
if err != nil {
return nil, &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}, err
}
plainText = append(plainText, raw...)
state.localVerifyData, err = prf.VerifyDataServer(state.masterSecret, plainText, state.cipherSuite.HashFunc())
if err != nil {
return nil, &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}, err
}
}
pkts = append(pkts,
&packet{
record: &recordlayer.RecordLayer{
Header: recordlayer.Header{
Version: protocol.Version1_2,
},
Content: serverHello,
},
},
&packet{
record: &recordlayer.RecordLayer{
Header: recordlayer.Header{
Version: protocol.Version1_2,
},
Content: &protocol.ChangeCipherSpec{},
},
},
&packet{
record: &recordlayer.RecordLayer{
Header: recordlayer.Header{
Version: protocol.Version1_2,
Epoch: 1,
},
Content: &handshake.Handshake{
Message: &handshake.MessageFinished{
VerifyData: state.localVerifyData,
},
},
},
shouldEncrypt: true,
resetLocalSequenceNumber: true,
},
)
return pkts, nil, nil
}

402
server/vendor/github.com/pion/dtls/v2/flight4handler.go generated vendored Normal file
View File

@@ -0,0 +1,402 @@
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
// SPDX-License-Identifier: MIT
package dtls
import (
"context"
"crypto/rand"
"crypto/x509"
"github.com/pion/dtls/v2/internal/ciphersuite"
"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, state.cipherSuite,
handshakeCachePullRule{handshake.TypeCertificate, cfg.initialEpoch, true, true},
handshakeCachePullRule{handshake.TypeClientKeyExchange, cfg.initialEpoch, true, false},
handshakeCachePullRule{handshake.TypeCertificateVerify, cfg.initialEpoch, true, true},
)
if !ok {
// No valid message received. Keep reading
return 0, nil, nil
}
// Validate type
var clientKeyExchange *handshake.MessageClientKeyExchange
if clientKeyExchange, ok = msgs[handshake.TypeClientKeyExchange].(*handshake.MessageClientKeyExchange); !ok {
return 0, &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}, nil
}
if h, hasCert := msgs[handshake.TypeCertificate].(*handshake.MessageCertificate); hasCert {
state.PeerCertificates = h.Certificate
// If the client offer its certificate, just disable session resumption.
// Otherwise, we have to store the certificate identitfication and expire time.
// And we have to check whether this certificate expired, revoked or changed.
//
// https://curl.se/docs/CVE-2016-5419.html
state.SessionID = nil
}
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
} else if state.PeerCertificates != nil {
// A certificate was received, but we haven't seen a CertificateVerify
// keep reading until we receive one
return 0, nil, nil
}
if !state.cipherSuite.IsInitialized() {
serverRandom := state.localRandom.MarshalFixed()
clientRandom := state.remoteRandom.MarshalFixed()
var err error
var preMasterSecret []byte
if state.cipherSuite.AuthenticationType() == CipherSuiteAuthenticationTypePreSharedKey {
var psk []byte
if psk, err = cfg.localPSKCallback(clientKeyExchange.IdentityHint); err != nil {
return 0, &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}, err
}
state.IdentityHint = clientKeyExchange.IdentityHint
switch state.cipherSuite.KeyExchangeAlgorithm() {
case CipherSuiteKeyExchangeAlgorithmPsk:
preMasterSecret = prf.PSKPreMasterSecret(psk)
case (CipherSuiteKeyExchangeAlgorithmPsk | CipherSuiteKeyExchangeAlgorithmEcdhe):
if preMasterSecret, err = prf.EcdhePSKPreMasterSecret(psk, clientKeyExchange.PublicKey, state.localKeypair.PrivateKey, state.localKeypair.Curve); err != nil {
return 0, &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}, err
}
default:
return 0, &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}, errInvalidCipherSuite
}
} else {
preMasterSecret, err = prf.PreMasterSecret(clientKeyExchange.PublicKey, state.localKeypair.PrivateKey, state.localKeypair.Curve)
if err != nil {
return 0, &alert.Alert{Level: alert.Fatal, Description: alert.IllegalParameter}, err
}
}
if state.extendedMasterSecret {
var sessionHash []byte
sessionHash, err = cache.sessionHash(state.cipherSuite.HashFunc(), cfg.initialEpoch)
if err != nil {
return 0, &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}, err
}
state.masterSecret, err = prf.ExtendedMasterSecret(preMasterSecret, sessionHash, state.cipherSuite.HashFunc())
if err != nil {
return 0, &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}, err
}
} else {
state.masterSecret, err = prf.MasterSecret(preMasterSecret, clientRandom[:], serverRandom[:], state.cipherSuite.HashFunc())
if err != nil {
return 0, &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}, err
}
}
if err := state.cipherSuite.Init(state.masterSecret, clientRandom[:], serverRandom[:], false); err != nil {
return 0, &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}, err
}
cfg.writeKeyLog(keyLogLabelTLS12, clientRandom[:], state.masterSecret)
}
if len(state.SessionID) > 0 {
s := Session{
ID: state.SessionID,
Secret: state.masterSecret,
}
cfg.log.Tracef("[handshake] save new session: %x", s.ID)
if err := cfg.sessionStore.Set(state.SessionID, s); err != nil {
return 0, &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}, err
}
}
// Now, encrypted packets can be handled
if err := c.handleQueuedPackets(ctx); err != nil {
return 0, &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}, err
}
seq, msgs, ok = cache.fullPullMap(seq, state.cipherSuite,
handshakeCachePullRule{handshake.TypeFinished, cfg.initialEpoch + 1, true, false},
)
if !ok {
// No valid message received. Keep reading
return 0, nil, nil
}
state.handshakeRecvSequence = seq
if _, ok = msgs[handshake.TypeFinished].(*handshake.MessageFinished); !ok {
return 0, &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}, nil
}
if state.cipherSuite.AuthenticationType() == CipherSuiteAuthenticationTypeAnonymous {
if cfg.verifyConnection != nil {
if err := cfg.verifyConnection(state.clone()); err != nil {
return 0, &alert.Alert{Level: alert.Fatal, Description: alert.BadCertificate}, err
}
}
return flight6, nil, nil
}
switch cfg.clientAuth {
case RequireAnyClientCert:
if state.PeerCertificates == nil {
return 0, &alert.Alert{Level: alert.Fatal, Description: alert.NoCertificate}, errClientCertificateRequired
}
case VerifyClientCertIfGiven:
if state.PeerCertificates != nil && !state.peerCertificatesVerified {
return 0, &alert.Alert{Level: alert.Fatal, Description: alert.BadCertificate}, errClientCertificateNotVerified
}
case RequireAndVerifyClientCert:
if state.PeerCertificates == nil {
return 0, &alert.Alert{Level: alert.Fatal, Description: alert.NoCertificate}, errClientCertificateRequired
}
if !state.peerCertificatesVerified {
return 0, &alert.Alert{Level: alert.Fatal, Description: alert.BadCertificate}, errClientCertificateNotVerified
}
case NoClientCert, RequestClientCert:
// go to flight6
}
if cfg.verifyConnection != nil {
if err := cfg.verifyConnection(state.clone()); err != nil {
return 0, &alert.Alert{Level: alert.Fatal, Description: alert.BadCertificate}, err
}
}
return flight6, nil, nil
}
func flight4Generate(_ flightConn, state *State, _ *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.getSRTPProtectionProfile() != 0 {
extensions = append(extensions, &extension.UseSRTP{
ProtectionProfiles: []SRTPProtectionProfile{state.getSRTPProtectionProfile()},
})
}
if state.cipherSuite.AuthenticationType() == CipherSuiteAuthenticationTypeCertificate {
extensions = append(extensions, &extension.SupportedPointFormats{
PointFormats: []elliptic.CurvePointFormat{elliptic.CurvePointFormatUncompressed},
})
}
selectedProto, err := extension.ALPNProtocolSelection(cfg.supportedProtocols, state.peerSupportedProtocols)
if err != nil {
return nil, &alert.Alert{Level: alert.Fatal, Description: alert.NoApplicationProtocol}, err
}
if selectedProto != "" {
extensions = append(extensions, &extension.ALPN{
ProtocolNameList: []string{selectedProto},
})
state.NegotiatedProtocol = selectedProto
}
var pkts []*packet
cipherSuiteID := uint16(state.cipherSuite.ID())
if cfg.sessionStore != nil {
state.SessionID = make([]byte, sessionLength)
if _, err := rand.Read(state.SessionID); err != nil {
return nil, &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}, err
}
}
pkts = append(pkts, &packet{
record: &recordlayer.RecordLayer{
Header: recordlayer.Header{
Version: protocol.Version1_2,
},
Content: &handshake.Handshake{
Message: &handshake.MessageServerHello{
Version: protocol.Version1_2,
Random: state.localRandom,
SessionID: state.SessionID,
CipherSuiteID: &cipherSuiteID,
CompressionMethod: defaultCompressionMethods()[0],
Extensions: extensions,
},
},
},
})
switch {
case state.cipherSuite.AuthenticationType() == CipherSuiteAuthenticationTypeCertificate:
certificate, err := cfg.getCertificate(&ClientHelloInfo{
ServerName: state.serverName,
CipherSuites: []ciphersuite.ID{state.cipherSuite.ID()},
})
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 {
// An empty list of certificateAuthorities signals to
// the client that it may send any certificate in response
// to our request. When we know the CAs we trust, then
// we can send them down, so that the client can choose
// an appropriate certificate to give to us.
var certificateAuthorities [][]byte
if cfg.clientCAs != nil {
// nolint:staticcheck // ignoring tlsCert.RootCAs.Subjects is deprecated ERR because cert does not come from SystemCertPool and it's ok if certificate authorities is empty.
certificateAuthorities = cfg.clientCAs.Subjects()
}
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,
CertificateAuthoritiesNames: certificateAuthorities,
},
},
},
})
}
case cfg.localPSKIdentityHint != nil || state.cipherSuite.KeyExchangeAlgorithm().Has(CipherSuiteKeyExchangeAlgorithmEcdhe):
// To help the client in selecting which identity to use, the server
// can provide a "PSK identity hint" in the ServerKeyExchange message.
// If no hint is provided and cipher suite doesn't use elliptic curve,
// the ServerKeyExchange message is omitted.
//
// https://tools.ietf.org/html/rfc4279#section-2
srvExchange := &handshake.MessageServerKeyExchange{
IdentityHint: cfg.localPSKIdentityHint,
}
if state.cipherSuite.KeyExchangeAlgorithm().Has(CipherSuiteKeyExchangeAlgorithmEcdhe) {
srvExchange.EllipticCurveType = elliptic.CurveTypeNamedCurve
srvExchange.NamedCurve = state.namedCurve
srvExchange.PublicKey = state.localKeypair.PublicKey
}
pkts = append(pkts, &packet{
record: &recordlayer.RecordLayer{
Header: recordlayer.Header{
Version: protocol.Version1_2,
},
Content: &handshake.Handshake{
Message: srvExchange,
},
},
})
}
pkts = append(pkts, &packet{
record: &recordlayer.RecordLayer{
Header: recordlayer.Header{
Version: protocol.Version1_2,
},
Content: &handshake.Handshake{
Message: &handshake.MessageServerHelloDone{},
},
},
})
return pkts, nil, nil
}

View File

@@ -0,0 +1,78 @@
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
// SPDX-License-Identifier: MIT
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 flight5bParse(_ context.Context, _ flightConn, state *State, cache *handshakeCache, cfg *handshakeConfig) (flightVal, *alert.Alert, error) {
_, msgs, ok := cache.fullPullMap(state.handshakeRecvSequence-1, state.cipherSuite,
handshakeCachePullRule{handshake.TypeFinished, cfg.initialEpoch + 1, false, false},
)
if !ok {
// No valid message received. Keep reading
return 0, nil, nil
}
if _, ok = msgs[handshake.TypeFinished].(*handshake.MessageFinished); !ok {
return 0, &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}, nil
}
// Other party may re-transmit the last flight. Keep state to be flight5b.
return flight5b, nil, nil
}
func flight5bGenerate(_ flightConn, state *State, cache *handshakeCache, cfg *handshakeConfig) ([]*packet, *alert.Alert, error) { //nolint:gocognit
var pkts []*packet
pkts = append(pkts,
&packet{
record: &recordlayer.RecordLayer{
Header: recordlayer.Header{
Version: protocol.Version1_2,
},
Content: &protocol.ChangeCipherSpec{},
},
})
if len(state.localVerifyData) == 0 {
plainText := cache.pullAndMerge(
handshakeCachePullRule{handshake.TypeClientHello, cfg.initialEpoch, true, false},
handshakeCachePullRule{handshake.TypeServerHello, cfg.initialEpoch, false, false},
handshakeCachePullRule{handshake.TypeFinished, cfg.initialEpoch + 1, false, false},
)
var err error
state.localVerifyData, err = prf.VerifyDataClient(state.masterSecret, plainText, state.cipherSuite.HashFunc())
if err != nil {
return nil, &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}, err
}
}
pkts = append(pkts,
&packet{
record: &recordlayer.RecordLayer{
Header: recordlayer.Header{
Version: protocol.Version1_2,
Epoch: 1,
},
Content: &handshake.Handshake{
Message: &handshake.MessageFinished{
VerifyData: state.localVerifyData,
},
},
},
shouldEncrypt: true,
resetLocalSequenceNumber: true,
})
return pkts, nil, nil
}

357
server/vendor/github.com/pion/dtls/v2/flight5handler.go generated vendored Normal file
View File

@@ -0,0 +1,357 @@
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
// SPDX-License-Identifier: MIT
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(_ context.Context, c flightConn, state *State, cache *handshakeCache, cfg *handshakeConfig) (flightVal, *alert.Alert, error) {
_, msgs, ok := cache.fullPullMap(state.handshakeRecvSequence, state.cipherSuite,
handshakeCachePullRule{handshake.TypeFinished, cfg.initialEpoch + 1, false, false},
)
if !ok {
// No valid message received. Keep reading
return 0, nil, nil
}
var finished *handshake.MessageFinished
if finished, ok = msgs[handshake.TypeFinished].(*handshake.MessageFinished); !ok {
return 0, &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}, nil
}
plainText := cache.pullAndMerge(
handshakeCachePullRule{handshake.TypeClientHello, cfg.initialEpoch, true, false},
handshakeCachePullRule{handshake.TypeServerHello, cfg.initialEpoch, false, false},
handshakeCachePullRule{handshake.TypeCertificate, cfg.initialEpoch, false, false},
handshakeCachePullRule{handshake.TypeServerKeyExchange, cfg.initialEpoch, false, false},
handshakeCachePullRule{handshake.TypeCertificateRequest, cfg.initialEpoch, false, false},
handshakeCachePullRule{handshake.TypeServerHelloDone, cfg.initialEpoch, false, false},
handshakeCachePullRule{handshake.TypeCertificate, cfg.initialEpoch, true, false},
handshakeCachePullRule{handshake.TypeClientKeyExchange, cfg.initialEpoch, true, false},
handshakeCachePullRule{handshake.TypeCertificateVerify, cfg.initialEpoch, true, false},
handshakeCachePullRule{handshake.TypeFinished, cfg.initialEpoch + 1, true, false},
)
expectedVerifyData, err := prf.VerifyDataServer(state.masterSecret, plainText, state.cipherSuite.HashFunc())
if err != nil {
return 0, &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}, err
}
if !bytes.Equal(expectedVerifyData, finished.VerifyData) {
return 0, &alert.Alert{Level: alert.Fatal, Description: alert.HandshakeFailure}, errVerifyDataMismatch
}
if len(state.SessionID) > 0 {
s := Session{
ID: state.SessionID,
Secret: state.masterSecret,
}
cfg.log.Tracef("[handshake] save new session: %x", s.ID)
if err := cfg.sessionStore.Set(c.sessionKey(), s); err != nil {
return 0, &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}, err
}
}
return flight5, nil, nil
}
func flight5Generate(c flightConn, state *State, cache *handshakeCache, cfg *handshakeConfig) ([]*packet, *alert.Alert, error) { //nolint:gocognit
var privateKey crypto.PrivateKey
var pkts []*packet
if state.remoteRequestedCertificate {
_, msgs, ok := cache.fullPullMap(state.handshakeRecvSequence-2, state.cipherSuite,
handshakeCachePullRule{handshake.TypeCertificateRequest, cfg.initialEpoch, false, false})
if !ok {
return nil, &alert.Alert{Level: alert.Fatal, Description: alert.HandshakeFailure}, errClientCertificateRequired
}
reqInfo := CertificateRequestInfo{}
if r, ok := msgs[handshake.TypeCertificateRequest].(*handshake.MessageCertificateRequest); ok {
reqInfo.AcceptableCAs = r.CertificateAuthoritiesNames
} else {
return nil, &alert.Alert{Level: alert.Fatal, Description: alert.HandshakeFailure}, errClientCertificateRequired
}
certificate, err := cfg.getClientCertificate(&reqInfo)
if err != nil {
return nil, &alert.Alert{Level: alert.Fatal, Description: alert.HandshakeFailure}, err
}
if certificate == nil {
return nil, &alert.Alert{Level: alert.Fatal, Description: alert.HandshakeFailure}, errNotAcceptableCertificateChain
}
if certificate.Certificate != nil {
privateKey = certificate.PrivateKey
}
pkts = append(pkts,
&packet{
record: &recordlayer.RecordLayer{
Header: recordlayer.Header{
Version: protocol.Version1_2,
},
Content: &handshake.Handshake{
Message: &handshake.MessageCertificate{
Certificate: certificate.Certificate,
},
},
},
})
}
clientKeyExchange := &handshake.MessageClientKeyExchange{}
if cfg.localPSKCallback == nil {
clientKeyExchange.PublicKey = state.localKeypair.PublicKey
} else {
clientKeyExchange.IdentityHint = cfg.localPSKIdentityHint
}
if state != nil && state.localKeypair != nil && len(state.localKeypair.PublicKey) > 0 {
clientKeyExchange.PublicKey = state.localKeypair.PublicKey
}
pkts = append(pkts,
&packet{
record: &recordlayer.RecordLayer{
Header: recordlayer.Header{
Version: protocol.Version1_2,
},
Content: &handshake.Handshake{
Message: clientKeyExchange,
},
},
})
serverKeyExchangeData := cache.pullAndMerge(
handshakeCachePullRule{handshake.TypeServerKeyExchange, cfg.initialEpoch, false, false},
)
serverKeyExchange := &handshake.MessageServerKeyExchange{}
// handshakeMessageServerKeyExchange is optional for PSK
if len(serverKeyExchangeData) == 0 {
alertPtr, err := handleServerKeyExchange(c, state, cfg, &handshake.MessageServerKeyExchange{})
if err != nil {
return nil, alertPtr, err
}
} else {
rawHandshake := &handshake.Handshake{
KeyExchangeAlgorithm: state.cipherSuite.KeyExchangeAlgorithm(),
}
err := rawHandshake.Unmarshal(serverKeyExchangeData)
if err != nil {
return nil, &alert.Alert{Level: alert.Fatal, Description: alert.UnexpectedMessage}, err
}
switch h := rawHandshake.Message.(type) {
case *handshake.MessageServerKeyExchange:
serverKeyExchange = h
default:
return nil, &alert.Alert{Level: alert.Fatal, Description: alert.UnexpectedMessage}, errInvalidContentType
}
}
// Append not-yet-sent packets
merged := []byte{}
seqPred := uint16(state.handshakeSendSequence)
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 && privateKey != nil {
plainText := append(cache.pullAndMerge(
handshakeCachePullRule{handshake.TypeClientHello, cfg.initialEpoch, true, false},
handshakeCachePullRule{handshake.TypeServerHello, cfg.initialEpoch, false, false},
handshakeCachePullRule{handshake.TypeCertificate, cfg.initialEpoch, false, false},
handshakeCachePullRule{handshake.TypeServerKeyExchange, cfg.initialEpoch, false, false},
handshakeCachePullRule{handshake.TypeCertificateRequest, cfg.initialEpoch, false, false},
handshakeCachePullRule{handshake.TypeServerHelloDone, cfg.initialEpoch, false, false},
handshakeCachePullRule{handshake.TypeCertificate, cfg.initialEpoch, true, false},
handshakeCachePullRule{handshake.TypeClientKeyExchange, cfg.initialEpoch, true, false},
), merged...)
// Find compatible signature scheme
signatureHashAlgo, err := signaturehash.SelectSignatureScheme(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 //nolint
}
clientRandom := state.localRandom.MarshalFixed()
serverRandom := state.remoteRandom.MarshalFixed()
var err error
if state.extendedMasterSecret {
var sessionHash []byte
sessionHash, err = cache.sessionHash(state.cipherSuite.HashFunc(), cfg.initialEpoch, sendingPlainText)
if err != nil {
return &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}, err
}
state.masterSecret, err = prf.ExtendedMasterSecret(state.preMasterSecret, sessionHash, state.cipherSuite.HashFunc())
if err != nil {
return &alert.Alert{Level: alert.Fatal, Description: alert.IllegalParameter}, err
}
} else {
state.masterSecret, err = prf.MasterSecret(state.preMasterSecret, clientRandom[:], serverRandom[:], state.cipherSuite.HashFunc())
if err != nil {
return &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}, err
}
}
if state.cipherSuite.AuthenticationType() == CipherSuiteAuthenticationTypeCertificate {
// 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 cfg.verifyConnection != nil {
if err = cfg.verifyConnection(state.clone()); 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 //nolint
}

View File

@@ -0,0 +1,85 @@
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
// SPDX-License-Identifier: MIT
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(_ context.Context, _ flightConn, state *State, cache *handshakeCache, cfg *handshakeConfig) (flightVal, *alert.Alert, error) {
_, msgs, ok := cache.fullPullMap(state.handshakeRecvSequence-1, state.cipherSuite,
handshakeCachePullRule{handshake.TypeFinished, cfg.initialEpoch + 1, true, false},
)
if !ok {
// No valid message received. Keep reading
return 0, nil, nil
}
if _, ok = msgs[handshake.TypeFinished].(*handshake.MessageFinished); !ok {
return 0, &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}, nil
}
// Other party may re-transmit the last flight. Keep state to be flight6.
return flight6, nil, nil
}
func flight6Generate(_ flightConn, state *State, cache *handshakeCache, cfg *handshakeConfig) ([]*packet, *alert.Alert, error) {
var pkts []*packet
pkts = append(pkts,
&packet{
record: &recordlayer.RecordLayer{
Header: recordlayer.Header{
Version: protocol.Version1_2,
},
Content: &protocol.ChangeCipherSpec{},
},
})
if len(state.localVerifyData) == 0 {
plainText := cache.pullAndMerge(
handshakeCachePullRule{handshake.TypeClientHello, cfg.initialEpoch, true, false},
handshakeCachePullRule{handshake.TypeServerHello, cfg.initialEpoch, false, false},
handshakeCachePullRule{handshake.TypeCertificate, cfg.initialEpoch, false, false},
handshakeCachePullRule{handshake.TypeServerKeyExchange, cfg.initialEpoch, false, false},
handshakeCachePullRule{handshake.TypeCertificateRequest, cfg.initialEpoch, false, false},
handshakeCachePullRule{handshake.TypeServerHelloDone, cfg.initialEpoch, false, false},
handshakeCachePullRule{handshake.TypeCertificate, cfg.initialEpoch, true, false},
handshakeCachePullRule{handshake.TypeClientKeyExchange, cfg.initialEpoch, true, false},
handshakeCachePullRule{handshake.TypeCertificateVerify, cfg.initialEpoch, true, false},
handshakeCachePullRule{handshake.TypeFinished, cfg.initialEpoch + 1, true, false},
)
var err error
state.localVerifyData, err = prf.VerifyDataServer(state.masterSecret, plainText, state.cipherSuite.HashFunc())
if err != nil {
return nil, &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}, err
}
}
pkts = append(pkts,
&packet{
record: &recordlayer.RecordLayer{
Header: recordlayer.Header{
Version: protocol.Version1_2,
Epoch: 1,
},
Content: &handshake.Handshake{
Message: &handshake.MessageFinished{
VerifyData: state.localVerifyData,
},
},
},
shouldEncrypt: true,
resetLocalSequenceNumber: true,
},
)
return pkts, nil, nil
}

68
server/vendor/github.com/pion/dtls/v2/flighthandler.go generated vendored Normal file
View File

@@ -0,0 +1,68 @@
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
// SPDX-License-Identifier: MIT
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 flight4b:
return flight4bParse, nil
case flight5:
return flight5Parse, nil
case flight5b:
return flight5bParse, nil
case flight6:
return flight6Parse, nil
default:
return nil, errInvalidFlight
}
}
func (f flightVal) getFlightGenerator() (gen flightGenerator, retransmit bool, err error) {
switch f {
case flight0:
return flight0Generate, true, nil
case flight1:
return flight1Generate, true, nil
case flight2:
// https://tools.ietf.org/html/rfc6347#section-3.2.1
// HelloVerifyRequests must not be retransmitted.
return flight2Generate, false, nil
case flight3:
return flight3Generate, true, nil
case flight4:
return flight4Generate, true, nil
case flight4b:
return flight4bGenerate, true, nil
case flight5:
return flight5Generate, true, nil
case flight5b:
return flight5bGenerate, true, nil
case flight6:
return flight6Generate, true, nil
default:
return nil, false, errInvalidFlight
}
}

View File

@@ -0,0 +1,132 @@
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
// SPDX-License-Identifier: MIT
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"
)
// 2 megabytes
const fragmentBufferMaxSize = 2000000
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{}}
}
// current total size of buffer
func (f *fragmentBuffer) size() int {
size := 0
for i := range f.cache {
for j := range f.cache[i] {
size += len(f.cache[i][j].data)
}
}
return size
}
// 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) {
if f.size()+len(buf) >= fragmentBufferMaxSize {
return false, errFragmentBufferOverflow
}
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 && f.handshakeHeader.FragmentLength != 0 {
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
}

View File

@@ -0,0 +1,172 @@
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
// SPDX-License-Identifier: MIT
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) {
h.mu.Lock()
defer h.mu.Unlock()
h.cache = append(h.cache, &handshakeCacheItem{
data: append([]byte{}, data...),
epoch: epoch,
messageSequence: messageSequence,
typ: typ,
isClient: isClient,
})
}
// returns a list handshakes that match the requested rules
// the list will contain null entries for rules that can't be satisfied
// multiple entries may match a rule, but only the last match is returned (ie ClientHello with cookies)
func (h *handshakeCache) pull(rules ...handshakeCachePullRule) []*handshakeCacheItem {
h.mu.Lock()
defer h.mu.Unlock()
out := make([]*handshakeCacheItem, len(rules))
for i, r := range rules {
for _, c := range h.cache {
if c.typ == r.typ && c.isClient == r.isClient && c.epoch == r.epoch {
switch {
case out[i] == nil:
out[i] = c
case out[i].messageSequence < c.messageSequence:
out[i] = c
}
}
}
}
return out
}
// fullPullMap pulls all handshakes between rules[0] to rules[len(rules)-1] as map.
func (h *handshakeCache) fullPullMap(startSeq int, cipherSuite CipherSuite, rules ...handshakeCachePullRule) (int, map[handshake.Type]handshake.Message, bool) {
h.mu.Lock()
defer h.mu.Unlock()
ci := make(map[handshake.Type]*handshakeCacheItem)
for _, 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
}
var keyExchangeAlgorithm CipherSuiteKeyExchangeAlgorithm
if cipherSuite != nil {
keyExchangeAlgorithm = cipherSuite.KeyExchangeAlgorithm()
}
rawHandshake := &handshake.Handshake{
KeyExchangeAlgorithm: keyExchangeAlgorithm,
}
if err := rawHandshake.Unmarshal(i.data); err != nil {
return startSeq, nil, false
}
if uint16(seq) != rawHandshake.Header.MessageSequence {
// 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
}

350
server/vendor/github.com/pion/dtls/v2/handshaker.go generated vendored Normal file
View File

@@ -0,0 +1,350 @@
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
// SPDX-License-Identifier: MIT
package dtls
import (
"context"
"crypto/tls"
"crypto/x509"
"fmt"
"io"
"sync"
"time"
"github.com/pion/dtls/v2/pkg/crypto/elliptic"
"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
supportedProtocols []string
clientAuth ClientAuthType // If we are a client should we request a client certificate
localCertificates []tls.Certificate
nameToCertificate map[string]*tls.Certificate
insecureSkipVerify bool
verifyPeerCertificate func(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error
verifyConnection func(*State) error
sessionStore SessionStore
rootCAs *x509.CertPool
clientCAs *x509.CertPool
retransmitInterval time.Duration
customCipherSuites func() []CipherSuite
ellipticCurves []elliptic.Curve
insecureSkipHelloVerify bool
onFlightState func(flightVal, handshakeState)
log logging.LeveledLogger
keyLogWriter io.Writer
localGetCertificate func(*ClientHelloInfo) (*tls.Certificate, error)
localGetClientCertificate func(*CertificateRequestInfo) (*tls.Certificate, error)
initialEpoch uint16
mu sync.Mutex
}
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
sessionKey() []byte
}
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
}
if nextFlight.isLastRecvFlight() && s.currentFlight == nextFlight {
return handshakeFinished, nil
}
<-retransmitTimer.C
// Retransmit last flight
return handshakeSending, nil
case <-ctx.Done():
return handshakeErrored, ctx.Err()
}
return handshakeFinished, nil
}

View File

@@ -0,0 +1,33 @@
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
// SPDX-License-Identifier: MIT
package ciphersuite
import (
"github.com/pion/dtls/v2/pkg/crypto/ciphersuite"
"github.com/pion/dtls/v2/pkg/crypto/clientcertificate"
)
// Aes128Ccm is a base class used by multiple AES-CCM Ciphers
type Aes128Ccm struct {
AesCcm
}
func newAes128Ccm(clientCertificateType clientcertificate.Type, id ID, psk bool, cryptoCCMTagLen ciphersuite.CCMTagLen, keyExchangeAlgorithm KeyExchangeAlgorithm, ecc bool) *Aes128Ccm {
return &Aes128Ccm{
AesCcm: AesCcm{
clientCertificateType: clientCertificateType,
id: id,
psk: psk,
cryptoCCMTagLen: cryptoCCMTagLen,
keyExchangeAlgorithm: keyExchangeAlgorithm,
ecc: ecc,
},
}
}
// Init initializes the internal Cipher with keying material
func (c *Aes128Ccm) Init(masterSecret, clientRandom, serverRandom []byte, isClient bool) error {
const prfKeyLen = 16
return c.AesCcm.Init(masterSecret, clientRandom, serverRandom, isClient, prfKeyLen)
}

View File

@@ -0,0 +1,33 @@
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
// SPDX-License-Identifier: MIT
package ciphersuite
import (
"github.com/pion/dtls/v2/pkg/crypto/ciphersuite"
"github.com/pion/dtls/v2/pkg/crypto/clientcertificate"
)
// Aes256Ccm is a base class used by multiple AES-CCM Ciphers
type Aes256Ccm struct {
AesCcm
}
func newAes256Ccm(clientCertificateType clientcertificate.Type, id ID, psk bool, cryptoCCMTagLen ciphersuite.CCMTagLen, keyExchangeAlgorithm KeyExchangeAlgorithm, ecc bool) *Aes256Ccm {
return &Aes256Ccm{
AesCcm: AesCcm{
clientCertificateType: clientCertificateType,
id: id,
psk: psk,
cryptoCCMTagLen: cryptoCCMTagLen,
keyExchangeAlgorithm: keyExchangeAlgorithm,
ecc: ecc,
},
}
}
// Init initializes the internal Cipher with keying material
func (c *Aes256Ccm) Init(masterSecret, clientRandom, serverRandom []byte, isClient bool) error {
const prfKeyLen = 32
return c.AesCcm.Init(masterSecret, clientRandom, serverRandom, isClient, prfKeyLen)
}

View File

@@ -0,0 +1,113 @@
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
// SPDX-License-Identifier: MIT
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"
)
// AesCcm is a base class used by multiple AES-CCM Ciphers
type AesCcm struct {
ccm atomic.Value // *cryptoCCM
clientCertificateType clientcertificate.Type
id ID
psk bool
keyExchangeAlgorithm KeyExchangeAlgorithm
cryptoCCMTagLen ciphersuite.CCMTagLen
ecc bool
}
// CertificateType returns what type of certificate this CipherSuite exchanges
func (c *AesCcm) CertificateType() clientcertificate.Type {
return c.clientCertificateType
}
// ID returns the ID of the CipherSuite
func (c *AesCcm) ID() ID {
return c.id
}
func (c *AesCcm) String() string {
return c.id.String()
}
// ECC uses Elliptic Curve Cryptography
func (c *AesCcm) ECC() bool {
return c.ecc
}
// KeyExchangeAlgorithm controls what key exchange algorithm is using during the handshake
func (c *AesCcm) KeyExchangeAlgorithm() KeyExchangeAlgorithm {
return c.keyExchangeAlgorithm
}
// HashFunc returns the hashing func for this CipherSuite
func (c *AesCcm) HashFunc() func() hash.Hash {
return sha256.New
}
// AuthenticationType controls what authentication method is using during the handshake
func (c *AesCcm) AuthenticationType() AuthenticationType {
if c.psk {
return AuthenticationTypePreSharedKey
}
return AuthenticationTypeCertificate
}
// IsInitialized returns if the CipherSuite has keying material and can
// encrypt/decrypt packets
func (c *AesCcm) IsInitialized() bool {
return c.ccm.Load() != nil
}
// Init initializes the internal Cipher with keying material
func (c *AesCcm) Init(masterSecret, clientRandom, serverRandom []byte, isClient bool, prfKeyLen int) error {
const (
prfMacLen = 0
prfIvLen = 4
)
keys, err := prf.GenerateEncryptionKeys(masterSecret, clientRandom, serverRandom, prfMacLen, prfKeyLen, prfIvLen, c.HashFunc())
if err != nil {
return err
}
var ccm *ciphersuite.CCM
if isClient {
ccm, err = ciphersuite.NewCCM(c.cryptoCCMTagLen, keys.ClientWriteKey, keys.ClientWriteIV, keys.ServerWriteKey, keys.ServerWriteIV)
} else {
ccm, err = ciphersuite.NewCCM(c.cryptoCCMTagLen, keys.ServerWriteKey, keys.ServerWriteIV, keys.ClientWriteKey, keys.ClientWriteIV)
}
c.ccm.Store(ccm)
return err
}
// Encrypt encrypts a single TLS RecordLayer
func (c *AesCcm) Encrypt(pkt *recordlayer.RecordLayer, raw []byte) ([]byte, error) {
cipherSuite, ok := c.ccm.Load().(*ciphersuite.CCM)
if !ok {
return nil, fmt.Errorf("%w, unable to encrypt", errCipherSuiteNotInit)
}
return cipherSuite.Encrypt(pkt, raw)
}
// Decrypt decrypts a single TLS RecordLayer
func (c *AesCcm) Decrypt(raw []byte) ([]byte, error) {
cipherSuite, ok := c.ccm.Load().(*ciphersuite.CCM)
if !ok {
return nil, fmt.Errorf("%w, unable to decrypt", errCipherSuiteNotInit)
}
return cipherSuite.Decrypt(raw)
}

View File

@@ -0,0 +1,98 @@
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
// SPDX-License-Identifier: MIT
// Package ciphersuite provides TLS Ciphers as registered with the IANA https://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml#tls-parameters-4
package ciphersuite
import (
"errors"
"fmt"
"github.com/pion/dtls/v2/internal/ciphersuite/types"
"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_256_CCM_8:
return "TLS_PSK_WITH_AES_256_CCM_8"
case TLS_PSK_WITH_AES_128_GCM_SHA256:
return "TLS_PSK_WITH_AES_128_GCM_SHA256"
case TLS_PSK_WITH_AES_128_CBC_SHA256:
return "TLS_PSK_WITH_AES_128_CBC_SHA256"
case TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384:
return "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384"
case TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384:
return "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384"
case TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256:
return "TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256"
default:
return fmt.Sprintf("unknown(%v)", uint16(i))
}
}
// Supported Cipher Suites
const (
// AES-128-CCM
TLS_ECDHE_ECDSA_WITH_AES_128_CCM ID = 0xc0ac //nolint:revive,stylecheck
TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8 ID = 0xc0ae //nolint:revive,stylecheck
// AES-128-GCM-SHA256
TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 ID = 0xc02b //nolint:revive,stylecheck
TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 ID = 0xc02f //nolint:revive,stylecheck
TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 ID = 0xc02c //nolint:revive,stylecheck
TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 ID = 0xc030 //nolint:revive,stylecheck
// AES-256-CBC-SHA
TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA ID = 0xc00a //nolint:revive,stylecheck
TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA ID = 0xc014 //nolint:revive,stylecheck
TLS_PSK_WITH_AES_128_CCM ID = 0xc0a4 //nolint:revive,stylecheck
TLS_PSK_WITH_AES_128_CCM_8 ID = 0xc0a8 //nolint:revive,stylecheck
TLS_PSK_WITH_AES_256_CCM_8 ID = 0xc0a9 //nolint:revive,stylecheck
TLS_PSK_WITH_AES_128_GCM_SHA256 ID = 0x00a8 //nolint:revive,stylecheck
TLS_PSK_WITH_AES_128_CBC_SHA256 ID = 0x00ae //nolint:revive,stylecheck
TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256 ID = 0xC037 //nolint:revive,stylecheck
)
// AuthenticationType controls what authentication method is using during the handshake
type AuthenticationType = types.AuthenticationType
// AuthenticationType Enums
const (
AuthenticationTypeCertificate AuthenticationType = types.AuthenticationTypeCertificate
AuthenticationTypePreSharedKey AuthenticationType = types.AuthenticationTypePreSharedKey
AuthenticationTypeAnonymous AuthenticationType = types.AuthenticationTypeAnonymous
)
// KeyExchangeAlgorithm controls what exchange algorithm was chosen.
type KeyExchangeAlgorithm = types.KeyExchangeAlgorithm
// KeyExchangeAlgorithm Bitmask
const (
KeyExchangeAlgorithmNone KeyExchangeAlgorithm = types.KeyExchangeAlgorithmNone
KeyExchangeAlgorithmPsk KeyExchangeAlgorithm = types.KeyExchangeAlgorithmPsk
KeyExchangeAlgorithmEcdhe KeyExchangeAlgorithm = types.KeyExchangeAlgorithmEcdhe
)

View File

@@ -0,0 +1,14 @@
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
// SPDX-License-Identifier: MIT
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, KeyExchangeAlgorithmEcdhe, true)
}

View File

@@ -0,0 +1,14 @@
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
// SPDX-License-Identifier: MIT
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, KeyExchangeAlgorithmEcdhe, true)
}

View File

@@ -0,0 +1,108 @@
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
// SPDX-License-Identifier: MIT
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
}
// KeyExchangeAlgorithm controls what key exchange algorithm is using during the handshake
func (c *TLSEcdheEcdsaWithAes128GcmSha256) KeyExchangeAlgorithm() KeyExchangeAlgorithm {
return KeyExchangeAlgorithmEcdhe
}
// ECC uses Elliptic Curve Cryptography
func (c *TLSEcdheEcdsaWithAes128GcmSha256) ECC() bool {
return true
}
// ID returns the ID of the CipherSuite
func (c *TLSEcdheEcdsaWithAes128GcmSha256) ID() ID {
return TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256
}
func (c *TLSEcdheEcdsaWithAes128GcmSha256) String() string {
return "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256"
}
// HashFunc returns the hashing func for this CipherSuite
func (c *TLSEcdheEcdsaWithAes128GcmSha256) HashFunc() func() hash.Hash {
return sha256.New
}
// AuthenticationType controls what authentication method is using during the handshake
func (c *TLSEcdheEcdsaWithAes128GcmSha256) AuthenticationType() AuthenticationType {
return AuthenticationTypeCertificate
}
// IsInitialized returns if the CipherSuite has keying material and can
// encrypt/decrypt packets
func (c *TLSEcdheEcdsaWithAes128GcmSha256) IsInitialized() bool {
return c.gcm.Load() != nil
}
func (c *TLSEcdheEcdsaWithAes128GcmSha256) init(masterSecret, clientRandom, serverRandom []byte, isClient bool, prfMacLen, prfKeyLen, prfIvLen int, hashFunc func() hash.Hash) error {
keys, err := prf.GenerateEncryptionKeys(masterSecret, clientRandom, serverRandom, prfMacLen, prfKeyLen, prfIvLen, hashFunc)
if err != nil {
return err
}
var gcm *ciphersuite.GCM
if isClient {
gcm, err = ciphersuite.NewGCM(keys.ClientWriteKey, keys.ClientWriteIV, keys.ServerWriteKey, keys.ServerWriteIV)
} else {
gcm, err = ciphersuite.NewGCM(keys.ServerWriteKey, keys.ServerWriteIV, keys.ClientWriteKey, keys.ClientWriteIV)
}
c.gcm.Store(gcm)
return err
}
// Init initializes the internal Cipher with keying material
func (c *TLSEcdheEcdsaWithAes128GcmSha256) Init(masterSecret, clientRandom, serverRandom []byte, isClient bool) error {
const (
prfMacLen = 0
prfKeyLen = 16
prfIvLen = 4
)
return c.init(masterSecret, clientRandom, serverRandom, isClient, prfMacLen, prfKeyLen, prfIvLen, c.HashFunc())
}
// Encrypt encrypts a single TLS RecordLayer
func (c *TLSEcdheEcdsaWithAes128GcmSha256) Encrypt(pkt *recordlayer.RecordLayer, raw []byte) ([]byte, error) {
cipherSuite, ok := c.gcm.Load().(*ciphersuite.GCM)
if !ok {
return nil, fmt.Errorf("%w, unable to encrypt", errCipherSuiteNotInit)
}
return cipherSuite.Encrypt(pkt, raw)
}
// Decrypt decrypts a single TLS RecordLayer
func (c *TLSEcdheEcdsaWithAes128GcmSha256) Decrypt(raw []byte) ([]byte, error) {
cipherSuite, ok := c.gcm.Load().(*ciphersuite.GCM)
if !ok {
return nil, fmt.Errorf("%w, unable to decrypt", errCipherSuiteNotInit)
}
return cipherSuite.Decrypt(raw)
}

View File

@@ -0,0 +1,114 @@
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
// SPDX-License-Identifier: MIT
package ciphersuite
import (
"crypto/sha1" //nolint: gosec,gci
"crypto/sha256"
"fmt"
"hash"
"sync/atomic"
"github.com/pion/dtls/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
}
// KeyExchangeAlgorithm controls what key exchange algorithm is using during the handshake
func (c *TLSEcdheEcdsaWithAes256CbcSha) KeyExchangeAlgorithm() KeyExchangeAlgorithm {
return KeyExchangeAlgorithmEcdhe
}
// ECC uses Elliptic Curve Cryptography
func (c *TLSEcdheEcdsaWithAes256CbcSha) ECC() bool {
return true
}
// ID returns the ID of the CipherSuite
func (c *TLSEcdheEcdsaWithAes256CbcSha) ID() ID {
return TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA
}
func (c *TLSEcdheEcdsaWithAes256CbcSha) String() string {
return "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA"
}
// HashFunc returns the hashing func for this CipherSuite
func (c *TLSEcdheEcdsaWithAes256CbcSha) HashFunc() func() hash.Hash {
return sha256.New
}
// AuthenticationType controls what authentication method is using during the handshake
func (c *TLSEcdheEcdsaWithAes256CbcSha) AuthenticationType() AuthenticationType {
return AuthenticationTypeCertificate
}
// IsInitialized returns if the CipherSuite has keying material and can
// encrypt/decrypt packets
func (c *TLSEcdheEcdsaWithAes256CbcSha) IsInitialized() bool {
return c.cbc.Load() != nil
}
// Init initializes the internal Cipher with keying material
func (c *TLSEcdheEcdsaWithAes256CbcSha) Init(masterSecret, clientRandom, serverRandom []byte, isClient bool) error {
const (
prfMacLen = 20
prfKeyLen = 32
prfIvLen = 16
)
keys, err := prf.GenerateEncryptionKeys(masterSecret, clientRandom, serverRandom, prfMacLen, prfKeyLen, prfIvLen, c.HashFunc())
if err != nil {
return err
}
var cbc *ciphersuite.CBC
if isClient {
cbc, err = ciphersuite.NewCBC(
keys.ClientWriteKey, keys.ClientWriteIV, keys.ClientMACKey,
keys.ServerWriteKey, keys.ServerWriteIV, keys.ServerMACKey,
sha1.New,
)
} else {
cbc, err = ciphersuite.NewCBC(
keys.ServerWriteKey, keys.ServerWriteIV, keys.ServerMACKey,
keys.ClientWriteKey, keys.ClientWriteIV, keys.ClientMACKey,
sha1.New,
)
}
c.cbc.Store(cbc)
return err
}
// Encrypt encrypts a single TLS RecordLayer
func (c *TLSEcdheEcdsaWithAes256CbcSha) Encrypt(pkt *recordlayer.RecordLayer, raw []byte) ([]byte, error) {
cipherSuite, ok := c.cbc.Load().(*ciphersuite.CBC)
if !ok {
return nil, fmt.Errorf("%w, unable to encrypt", errCipherSuiteNotInit)
}
return cipherSuite.Encrypt(pkt, raw)
}
// Decrypt decrypts a single TLS RecordLayer
func (c *TLSEcdheEcdsaWithAes256CbcSha) Decrypt(raw []byte) ([]byte, error) {
cipherSuite, ok := c.cbc.Load().(*ciphersuite.CBC)
if !ok {
return nil, fmt.Errorf("%w, unable to decrypt", errCipherSuiteNotInit)
}
return cipherSuite.Decrypt(raw)
}

View File

@@ -0,0 +1,39 @@
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
// SPDX-License-Identifier: MIT
package ciphersuite
import (
"crypto/sha512"
"hash"
)
// TLSEcdheEcdsaWithAes256GcmSha384 represents a TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 CipherSuite
type TLSEcdheEcdsaWithAes256GcmSha384 struct {
TLSEcdheEcdsaWithAes128GcmSha256
}
// ID returns the ID of the CipherSuite
func (c *TLSEcdheEcdsaWithAes256GcmSha384) ID() ID {
return TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384
}
func (c *TLSEcdheEcdsaWithAes256GcmSha384) String() string {
return "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384"
}
// HashFunc returns the hashing func for this CipherSuite
func (c *TLSEcdheEcdsaWithAes256GcmSha384) HashFunc() func() hash.Hash {
return sha512.New384
}
// Init initializes the internal Cipher with keying material
func (c *TLSEcdheEcdsaWithAes256GcmSha384) Init(masterSecret, clientRandom, serverRandom []byte, isClient bool) error {
const (
prfMacLen = 0
prfKeyLen = 32
prfIvLen = 4
)
return c.init(masterSecret, clientRandom, serverRandom, isClient, prfMacLen, prfKeyLen, prfIvLen, c.HashFunc())
}

View File

@@ -0,0 +1,118 @@
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
// SPDX-License-Identifier: MIT
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"
)
// TLSEcdhePskWithAes128CbcSha256 implements the TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256 CipherSuite
type TLSEcdhePskWithAes128CbcSha256 struct {
cbc atomic.Value // *cryptoCBC
}
// NewTLSEcdhePskWithAes128CbcSha256 creates TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256 cipher.
func NewTLSEcdhePskWithAes128CbcSha256() *TLSEcdhePskWithAes128CbcSha256 {
return &TLSEcdhePskWithAes128CbcSha256{}
}
// CertificateType returns what type of certificate this CipherSuite exchanges
func (c *TLSEcdhePskWithAes128CbcSha256) CertificateType() clientcertificate.Type {
return clientcertificate.Type(0)
}
// KeyExchangeAlgorithm controls what key exchange algorithm is using during the handshake
func (c *TLSEcdhePskWithAes128CbcSha256) KeyExchangeAlgorithm() KeyExchangeAlgorithm {
return (KeyExchangeAlgorithmPsk | KeyExchangeAlgorithmEcdhe)
}
// ECC uses Elliptic Curve Cryptography
func (c *TLSEcdhePskWithAes128CbcSha256) ECC() bool {
return true
}
// ID returns the ID of the CipherSuite
func (c *TLSEcdhePskWithAes128CbcSha256) ID() ID {
return TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256
}
func (c *TLSEcdhePskWithAes128CbcSha256) String() string {
return "TLS-ECDHE-PSK-WITH-AES-128-CBC-SHA256"
}
// HashFunc returns the hashing func for this CipherSuite
func (c *TLSEcdhePskWithAes128CbcSha256) HashFunc() func() hash.Hash {
return sha256.New
}
// AuthenticationType controls what authentication method is using during the handshake
func (c *TLSEcdhePskWithAes128CbcSha256) AuthenticationType() AuthenticationType {
return AuthenticationTypePreSharedKey
}
// IsInitialized returns if the CipherSuite has keying material and can
// encrypt/decrypt packets
func (c *TLSEcdhePskWithAes128CbcSha256) IsInitialized() bool {
return c.cbc.Load() != nil
}
// Init initializes the internal Cipher with keying material
func (c *TLSEcdhePskWithAes128CbcSha256) Init(masterSecret, clientRandom, serverRandom []byte, isClient bool) error {
const (
prfMacLen = 32
prfKeyLen = 16
prfIvLen = 16
)
keys, err := prf.GenerateEncryptionKeys(masterSecret, clientRandom, serverRandom, prfMacLen, prfKeyLen, prfIvLen, c.HashFunc())
if err != nil {
return err
}
var cbc *ciphersuite.CBC
if isClient {
cbc, err = ciphersuite.NewCBC(
keys.ClientWriteKey, keys.ClientWriteIV, keys.ClientMACKey,
keys.ServerWriteKey, keys.ServerWriteIV, keys.ServerMACKey,
c.HashFunc(),
)
} else {
cbc, err = ciphersuite.NewCBC(
keys.ServerWriteKey, keys.ServerWriteIV, keys.ServerMACKey,
keys.ClientWriteKey, keys.ClientWriteIV, keys.ClientMACKey,
c.HashFunc(),
)
}
c.cbc.Store(cbc)
return err
}
// Encrypt encrypts a single TLS RecordLayer
func (c *TLSEcdhePskWithAes128CbcSha256) Encrypt(pkt *recordlayer.RecordLayer, raw []byte) ([]byte, error) {
cipherSuite, ok := c.cbc.Load().(*ciphersuite.CBC)
if !ok { // !c.isInitialized()
return nil, fmt.Errorf("%w, unable to encrypt", errCipherSuiteNotInit)
}
return cipherSuite.Encrypt(pkt, raw)
}
// Decrypt decrypts a single TLS RecordLayer
func (c *TLSEcdhePskWithAes128CbcSha256) Decrypt(raw []byte) ([]byte, error) {
cipherSuite, ok := c.cbc.Load().(*ciphersuite.CBC)
if !ok { // !c.isInitialized()
return nil, fmt.Errorf("%w, unable to decrypt", errCipherSuiteNotInit)
}
return cipherSuite.Decrypt(raw)
}

View File

@@ -0,0 +1,25 @@
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
// SPDX-License-Identifier: MIT
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"
}

View File

@@ -0,0 +1,25 @@
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
// SPDX-License-Identifier: MIT
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"
}

View File

@@ -0,0 +1,25 @@
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
// SPDX-License-Identifier: MIT
package ciphersuite
import "github.com/pion/dtls/v2/pkg/crypto/clientcertificate"
// TLSEcdheRsaWithAes256GcmSha384 implements the TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 CipherSuite
type TLSEcdheRsaWithAes256GcmSha384 struct {
TLSEcdheEcdsaWithAes256GcmSha384
}
// CertificateType returns what type of certificate this CipherSuite exchanges
func (c *TLSEcdheRsaWithAes256GcmSha384) CertificateType() clientcertificate.Type {
return clientcertificate.RSASign
}
// ID returns the ID of the CipherSuite
func (c *TLSEcdheRsaWithAes256GcmSha384) ID() ID {
return TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
}
func (c *TLSEcdheRsaWithAes256GcmSha384) String() string {
return "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384"
}

View File

@@ -0,0 +1,113 @@
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
// SPDX-License-Identifier: MIT
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)
}
// KeyExchangeAlgorithm controls what key exchange algorithm is using during the handshake
func (c *TLSPskWithAes128CbcSha256) KeyExchangeAlgorithm() KeyExchangeAlgorithm {
return KeyExchangeAlgorithmPsk
}
// ECC uses Elliptic Curve Cryptography
func (c *TLSPskWithAes128CbcSha256) ECC() bool {
return false
}
// ID returns the ID of the CipherSuite
func (c *TLSPskWithAes128CbcSha256) ID() ID {
return TLS_PSK_WITH_AES_128_CBC_SHA256
}
func (c *TLSPskWithAes128CbcSha256) String() string {
return "TLS_PSK_WITH_AES_128_CBC_SHA256"
}
// HashFunc returns the hashing func for this CipherSuite
func (c *TLSPskWithAes128CbcSha256) HashFunc() func() hash.Hash {
return sha256.New
}
// AuthenticationType controls what authentication method is using during the handshake
func (c *TLSPskWithAes128CbcSha256) AuthenticationType() AuthenticationType {
return AuthenticationTypePreSharedKey
}
// IsInitialized returns if the CipherSuite has keying material and can
// encrypt/decrypt packets
func (c *TLSPskWithAes128CbcSha256) IsInitialized() bool {
return c.cbc.Load() != nil
}
// Init initializes the internal Cipher with keying material
func (c *TLSPskWithAes128CbcSha256) Init(masterSecret, clientRandom, serverRandom []byte, isClient bool) error {
const (
prfMacLen = 32
prfKeyLen = 16
prfIvLen = 16
)
keys, err := prf.GenerateEncryptionKeys(masterSecret, clientRandom, serverRandom, prfMacLen, prfKeyLen, prfIvLen, c.HashFunc())
if err != nil {
return err
}
var cbc *ciphersuite.CBC
if isClient {
cbc, err = ciphersuite.NewCBC(
keys.ClientWriteKey, keys.ClientWriteIV, keys.ClientMACKey,
keys.ServerWriteKey, keys.ServerWriteIV, keys.ServerMACKey,
c.HashFunc(),
)
} else {
cbc, err = ciphersuite.NewCBC(
keys.ServerWriteKey, keys.ServerWriteIV, keys.ServerMACKey,
keys.ClientWriteKey, keys.ClientWriteIV, keys.ClientMACKey,
c.HashFunc(),
)
}
c.cbc.Store(cbc)
return err
}
// Encrypt encrypts a single TLS RecordLayer
func (c *TLSPskWithAes128CbcSha256) Encrypt(pkt *recordlayer.RecordLayer, raw []byte) ([]byte, error) {
cipherSuite, ok := c.cbc.Load().(*ciphersuite.CBC)
if !ok {
return nil, fmt.Errorf("%w, unable to encrypt", errCipherSuiteNotInit)
}
return cipherSuite.Encrypt(pkt, raw)
}
// Decrypt decrypts a single TLS RecordLayer
func (c *TLSPskWithAes128CbcSha256) Decrypt(raw []byte) ([]byte, error) {
cipherSuite, ok := c.cbc.Load().(*ciphersuite.CBC)
if !ok {
return nil, fmt.Errorf("%w, unable to decrypt", errCipherSuiteNotInit)
}
return cipherSuite.Decrypt(raw)
}

View File

@@ -0,0 +1,14 @@
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
// SPDX-License-Identifier: MIT
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, KeyExchangeAlgorithmPsk, false)
}

View File

@@ -0,0 +1,14 @@
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
// SPDX-License-Identifier: MIT
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, KeyExchangeAlgorithmPsk, false)
}

View File

@@ -0,0 +1,35 @@
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
// SPDX-License-Identifier: MIT
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)
}
// KeyExchangeAlgorithm controls what key exchange algorithm is using during the handshake
func (c *TLSPskWithAes128GcmSha256) KeyExchangeAlgorithm() KeyExchangeAlgorithm {
return KeyExchangeAlgorithmPsk
}
// ID returns the ID of the CipherSuite
func (c *TLSPskWithAes128GcmSha256) ID() ID {
return TLS_PSK_WITH_AES_128_GCM_SHA256
}
func (c *TLSPskWithAes128GcmSha256) String() string {
return "TLS_PSK_WITH_AES_128_GCM_SHA256"
}
// AuthenticationType controls what authentication method is using during the handshake
func (c *TLSPskWithAes128GcmSha256) AuthenticationType() AuthenticationType {
return AuthenticationTypePreSharedKey
}

View File

@@ -0,0 +1,14 @@
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
// SPDX-License-Identifier: MIT
package ciphersuite
import (
"github.com/pion/dtls/v2/pkg/crypto/ciphersuite"
"github.com/pion/dtls/v2/pkg/crypto/clientcertificate"
)
// NewTLSPskWithAes256Ccm8 returns the TLS_PSK_WITH_AES_256_CCM_8 CipherSuite
func NewTLSPskWithAes256Ccm8() *Aes256Ccm {
return newAes256Ccm(clientcertificate.Type(0), TLS_PSK_WITH_AES_256_CCM_8, true, ciphersuite.CCMTagLength8, KeyExchangeAlgorithmPsk, false)
}

View File

@@ -0,0 +1,14 @@
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
// SPDX-License-Identifier: MIT
package types
// AuthenticationType controls what authentication method is using during the handshake
type AuthenticationType int
// AuthenticationType Enums
const (
AuthenticationTypeCertificate AuthenticationType = iota + 1
AuthenticationTypePreSharedKey
AuthenticationTypeAnonymous
)

View File

@@ -0,0 +1,20 @@
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
// SPDX-License-Identifier: MIT
// Package types provides types for TLS Ciphers
package types
// KeyExchangeAlgorithm controls what exchange algorithm was chosen.
type KeyExchangeAlgorithm int
// KeyExchangeAlgorithm Bitmask
const (
KeyExchangeAlgorithmNone KeyExchangeAlgorithm = 0
KeyExchangeAlgorithmPsk KeyExchangeAlgorithm = iota << 1
KeyExchangeAlgorithmEcdhe
)
// Has check if keyExchangeAlgorithm is supported.
func (a KeyExchangeAlgorithm) Has(v KeyExchangeAlgorithm) bool {
return (a & v) == v
}

View File

@@ -0,0 +1,48 @@
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
// SPDX-License-Identifier: MIT
// Package closer provides signaling channel for shutdown
package closer
import (
"context"
)
// Closer allows for each signaling a channel for shutdown
type Closer struct {
ctx context.Context
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()
}

View File

@@ -0,0 +1,42 @@
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
// SPDX-License-Identifier: MIT
// 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
}

83
server/vendor/github.com/pion/dtls/v2/listener.go generated vendored Normal file
View File

@@ -0,0 +1,83 @@
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
// SPDX-License-Identifier: MIT
package dtls
import (
"net"
"github.com/pion/dtls/v2/pkg/protocol"
"github.com/pion/dtls/v2/pkg/protocol/recordlayer"
"github.com/pion/transport/v2/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()
}

12
server/vendor/github.com/pion/dtls/v2/packet.go generated vendored Normal file
View File

@@ -0,0 +1,12 @@
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
// SPDX-License-Identifier: MIT
package dtls
import "github.com/pion/dtls/v2/pkg/protocol/recordlayer"
type packet struct {
record *recordlayer.RecordLayer
shouldEncrypt bool
resetLocalSequenceNumber bool
}

View File

@@ -0,0 +1,254 @@
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
// SPDX-License-Identifier: MIT
// Package ccm implements a CCM, Counter with CBC-MAC
// as per RFC 3610.
//
// See https://tools.ietf.org/html/rfc3610
//
// This code was lifted from https://github.com/bocajim/dtls/blob/a3300364a283fcb490d28a93d7fcfa7ba437fbbe/ccm/ccm.go
// and as such was not written by the Pions authors. Like Pions this
// code is licensed under MIT.
//
// A request for including CCM into the Go standard library
// can be found as issue #27484 on the https://github.com/golang/go/
// repository.
package ccm
import (
"crypto/cipher"
"crypto/subtle"
"encoding/binary"
"errors"
"math"
)
// ccm represents a Counter with CBC-MAC with a specific key.
type ccm struct {
b cipher.Block
M uint8
L uint8
}
const ccmBlockSize = 16
// CCM is a block cipher in Counter with CBC-MAC mode.
// Providing authenticated encryption with associated data via the cipher.AEAD interface.
type CCM interface {
cipher.AEAD
// MaxLength returns the maxium length of plaintext in calls to Seal.
// The maximum length of ciphertext in calls to Open is MaxLength()+Overhead().
// The maximum length is related to CCM's `L` parameter (15-noncesize) and
// is 1<<(8*L) - 1 (but also limited by the maxium size of an int).
MaxLength() int
}
var (
errInvalidBlockSize = errors.New("ccm: NewCCM requires 128-bit block cipher")
errInvalidTagSize = errors.New("ccm: tagsize must be 4, 6, 8, 10, 12, 14, or 16")
errInvalidNonceSize = errors.New("ccm: invalid nonce size")
)
// NewCCM returns the given 128-bit block cipher wrapped in CCM.
// The tagsize must be an even integer between 4 and 16 inclusive
// and is used as CCM's `M` parameter.
// The noncesize must be an integer between 7 and 13 inclusive,
// 15-noncesize is used as CCM's `L` parameter.
func NewCCM(b cipher.Block, tagsize, noncesize int) (CCM, error) {
if b.BlockSize() != ccmBlockSize {
return nil, errInvalidBlockSize
}
if tagsize < 4 || tagsize > 16 || tagsize&1 != 0 {
return nil, errInvalidTagSize
}
lensize := 15 - noncesize
if lensize < 2 || lensize > 8 {
return nil, errInvalidNonceSize
}
c := &ccm{b: b, M: uint8(tagsize), L: uint8(lensize)}
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
}

View File

@@ -0,0 +1,177 @@
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
// SPDX-License-Identifier: MIT
package ciphersuite
import ( //nolint:gci
"crypto/aes"
"crypto/cipher"
"crypto/hmac"
"crypto/rand"
"encoding/binary"
"hash"
"github.com/pion/dtls/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
}
writeCBC, ok := cipher.NewCBCEncrypter(writeBlock, localWriteIV).(cbcMode)
if !ok {
return nil, errFailedToCast
}
readCBC, ok := cipher.NewCBCDecrypter(readBlock, remoteWriteIV).(cbcMode)
if !ok {
return nil, errFailedToCast
}
return &CBC{
writeCBC: writeCBC,
writeMac: localMac,
readCBC: readCBC,
readMac: remoteMac,
h: 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
}

View File

@@ -0,0 +1,107 @@
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
// SPDX-License-Identifier: MIT
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) //nolint:errorlint
}
return append(in[:recordlayer.HeaderSize], out...), nil
}

View File

@@ -0,0 +1,76 @@
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
// SPDX-License-Identifier: MIT
// 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
errFailedToCast = &protocol.FatalError{Err: errors.New("failed to cast")} //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
}

View File

@@ -0,0 +1,103 @@
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
// SPDX-License-Identifier: MIT
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) //nolint:errorlint
}
return append(in[:recordlayer.HeaderSize], out...), nil
}

View File

@@ -0,0 +1,25 @@
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
// SPDX-License-Identifier: MIT
// Package clientcertificate provides all the support Client Certificate types
package clientcertificate
// Type is used to communicate what
// type of certificate is being transported
//
// https://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml#tls-parameters-2
type Type byte
// ClientCertificateType enums
const (
RSASign Type = 1
ECDSASign Type = 64
)
// Types returns all valid ClientCertificate Types
func Types() map[Type]bool {
return map[Type]bool{
RSASign: true,
ECDSASign: true,
}
}

View File

@@ -0,0 +1,115 @@
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
// SPDX-License-Identifier: MIT
// Package elliptic provides elliptic curve cryptography for DTLS
package elliptic
import (
"crypto/elliptic"
"crypto/rand"
"errors"
"fmt"
"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
)
func (c Curve) String() string {
switch c {
case P256:
return "P-256"
case P384:
return "P-384"
case X25519:
return "X25519"
}
return fmt.Sprintf("%#x", uint16(c))
}
// Curves returns all curves we implement
func Curves() map[Curve]bool {
return map[Curve]bool{
X25519: true,
P256: true,
P384: true,
}
}
// GenerateKeypair generates a keypair for the given Curve
func GenerateKeypair(c Curve) (*Keypair, error) {
switch c { //nolint:revive
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
}

View File

@@ -0,0 +1,53 @@
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
// SPDX-License-Identifier: MIT
// Package fingerprint provides a helper to create fingerprint string from certificate
package fingerprint
import (
"crypto"
"crypto/x509"
"errors"
"fmt"
)
var (
errHashUnavailable = errors.New("fingerprint: hash algorithm is not linked into the binary")
errInvalidFingerprintLength = errors.New("fingerprint: invalid fingerprint length")
)
// Fingerprint creates a fingerprint for a certificate using the specified hash algorithm
func Fingerprint(cert *x509.Certificate, algo crypto.Hash) (string, error) {
if !algo.Available() {
return "", errHashUnavailable
}
h := algo.New()
for i := 0; i < len(cert.Raw); {
n, _ := h.Write(cert.Raw[i:])
// Hash.Writer is specified to be never returning an error.
// https://golang.org/pkg/hash/#Hash
i += n
}
digest := []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
}

View File

@@ -0,0 +1,41 @@
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
// SPDX-License-Identifier: MIT
package fingerprint
import (
"crypto"
"errors"
"strings"
)
var errInvalidHashAlgorithm = errors.New("fingerprint: invalid hash algorithm")
func nameToHash() map[string]crypto.Hash {
return map[string]crypto.Hash{
"md5": crypto.MD5, // [RFC3279]
"sha-1": crypto.SHA1, // [RFC3279]
"sha-224": crypto.SHA224, // [RFC4055]
"sha-256": crypto.SHA256, // [RFC4055]
"sha-384": crypto.SHA384, // [RFC4055]
"sha-512": crypto.SHA512, // [RFC4055]
}
}
// HashFromString allows looking up a hash algorithm by it's string representation
func HashFromString(s string) (crypto.Hash, error) {
if h, ok := nameToHash()[strings.ToLower(s)]; ok {
return h, nil
}
return 0, errInvalidHashAlgorithm
}
// StringFromHash allows looking up a string representation of the crypto.Hash.
func StringFromHash(hash crypto.Hash) (string, error) {
for s, h := range nameToHash() {
if h == hash {
return s, nil
}
}
return "", errInvalidHashAlgorithm
}

View File

@@ -0,0 +1,129 @@
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
// SPDX-License-Identifier: MIT
// Package hash provides TLS HashAlgorithm as defined in TLS 1.2
package hash
import ( //nolint:gci
"crypto"
"crypto/md5" //nolint:gosec
"crypto/sha1" //nolint:gosec
"crypto/sha256"
"crypto/sha512"
)
// Algorithm is used to indicate the hash algorithm used
// https://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml#tls-parameters-18
type Algorithm uint16
// Supported hash algorithms
const (
None Algorithm = 0 // Blacklisted
MD5 Algorithm = 1 // Blacklisted
SHA1 Algorithm = 2 // Blacklisted
SHA224 Algorithm = 3
SHA256 Algorithm = 4
SHA384 Algorithm = 5
SHA512 Algorithm = 6
Ed25519 Algorithm = 8
)
// String makes hashAlgorithm printable
func (a Algorithm) String() string {
switch a {
case None:
return "none"
case MD5:
return "md5" // [RFC3279]
case SHA1:
return "sha-1" // [RFC3279]
case SHA224:
return "sha-224" // [RFC4055]
case SHA256:
return "sha-256" // [RFC4055]
case SHA384:
return "sha-384" // [RFC4055]
case SHA512:
return "sha-512" // [RFC4055]
case Ed25519:
return "null"
default:
return "unknown or unsupported hash algorithm"
}
}
// Digest performs a digest on the passed value
func (a Algorithm) Digest(b []byte) []byte {
switch a {
case None:
return nil
case MD5:
hash := md5.Sum(b) // #nosec
return hash[:]
case SHA1:
hash := sha1.Sum(b) // #nosec
return hash[:]
case SHA224:
hash := sha256.Sum224(b)
return hash[:]
case SHA256:
hash := sha256.Sum256(b)
return hash[:]
case SHA384:
hash := sha512.Sum384(b)
return hash[:]
case SHA512:
hash := sha512.Sum512(b)
return hash[:]
default:
return nil
}
}
// Insecure returns if the given HashAlgorithm is considered secure in DTLS 1.2
func (a Algorithm) Insecure() bool {
switch a {
case None, MD5, SHA1:
return true
default:
return false
}
}
// CryptoHash returns the crypto.Hash implementation for the given HashAlgorithm
func (a Algorithm) CryptoHash() crypto.Hash {
switch a {
case None:
return crypto.Hash(0)
case MD5:
return crypto.MD5
case SHA1:
return crypto.SHA1
case SHA224:
return crypto.SHA224
case SHA256:
return crypto.SHA256
case SHA384:
return crypto.SHA384
case SHA512:
return crypto.SHA512
case Ed25519:
return crypto.Hash(0)
default:
return crypto.Hash(0)
}
}
// Algorithms returns all the supported Hash Algorithms
func Algorithms() map[Algorithm]struct{} {
return map[Algorithm]struct{}{
None: {},
MD5: {},
SHA1: {},
SHA224: {},
SHA256: {},
SHA384: {},
SHA512: {},
Ed25519: {},
}
}

View File

@@ -0,0 +1,255 @@
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
// SPDX-License-Identifier: MIT
// 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
}
// EcdhePSKPreMasterSecret implements TLS 1.2 Premaster Secret generation given a psk, a keypair and a curve
//
// https://datatracker.ietf.org/doc/html/rfc5489#section-2
func EcdhePSKPreMasterSecret(psk, publicKey, privateKey []byte, curve elliptic.Curve) ([]byte, error) {
preMasterSecret, err := PreMasterSecret(publicKey, privateKey, curve)
if err != nil {
return nil, err
}
out := make([]byte, 2+len(preMasterSecret)+2+len(psk))
// write preMasterSecret length
offset := 0
binary.BigEndian.PutUint16(out[offset:], uint16(len(preMasterSecret)))
offset += 2
// write preMasterSecret
copy(out[offset:], preMasterSecret)
offset += len(preMasterSecret)
// write psk length
binary.BigEndian.PutUint16(out[offset:], uint16(len(psk)))
offset += 2
// write psk
copy(out[offset:], psk)
return out, nil
}
// PreMasterSecret implements TLS 1.2 Premaster Secret generation given a keypair and a curve
func PreMasterSecret(publicKey, privateKey []byte, curve elliptic.Curve) ([]byte, error) {
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)
}

View File

@@ -0,0 +1,27 @@
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
// SPDX-License-Identifier: MIT
// 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: {},
}
}

View File

@@ -0,0 +1,12 @@
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
// SPDX-License-Identifier: MIT
package signaturehash
import "errors"
var (
errNoAvailableSignatureSchemes = errors.New("connection can not be created, no SignatureScheme satisfy this Config")
errInvalidSignatureAlgorithm = errors.New("invalid signature algorithm")
errInvalidHashAlgorithm = errors.New("invalid hash algorithm")
)

View File

@@ -0,0 +1,96 @@
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
// SPDX-License-Identifier: MIT
// Package signaturehash provides the SignatureHashAlgorithm as defined in TLS 1.2
package signaturehash
import (
"crypto"
"crypto/ecdsa"
"crypto/ed25519"
"crypto/rsa"
"crypto/tls"
"fmt"
"github.com/pion/dtls/v2/pkg/crypto/hash"
"github.com/pion/dtls/v2/pkg/crypto/signature"
)
// Algorithm is a signature/hash algorithm pairs which may be used in
// digital signatures.
//
// https://tools.ietf.org/html/rfc5246#section-7.4.1.4.1
type Algorithm struct {
Hash hash.Algorithm
Signature signature.Algorithm
}
// Algorithms 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,
fmt.Errorf("SignatureScheme %04x: %w", ss, errInvalidSignatureAlgorithm)
}
h := hash.Algorithm(ss >> 8)
if _, ok := hash.Algorithms()[h]; !ok || (ok && h == hash.None) {
return nil, fmt.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
}

View File

@@ -0,0 +1,166 @@
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
// SPDX-License-Identifier: MIT
// Package alert implements TLS alert protocol https://tools.ietf.org/html/rfc5246#section-7.2
package alert
import (
"errors"
"fmt"
"github.com/pion/dtls/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
NoApplicationProtocol Description = 120
)
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"
case NoApplicationProtocol:
return "NoApplicationProtocol"
default:
return "Invalid alert description"
}
}
// Alert is one of the content types supported by the TLS record layer.
// Alert messages convey the severity of the message
// (warning or fatal) and a description of the alert. Alert messages
// with a level of fatal result in the immediate termination of the
// connection. In this case, other connections corresponding to the
// session may continue, but the session identifier MUST be invalidated,
// preventing the failed session from being used to establish new
// connections. Like other messages, alert messages are encrypted and
// compressed, as specified by the current connection state.
// https://tools.ietf.org/html/rfc5246#section-7.2
type Alert struct {
Level Level
Description Description
}
// ContentType returns the ContentType of this Content
func (a Alert) ContentType() protocol.ContentType {
return protocol.ContentTypeAlert
}
// Marshal returns the encoded alert
func (a *Alert) Marshal() ([]byte, error) {
return []byte{byte(a.Level), byte(a.Description)}, nil
}
// Unmarshal populates the alert from binary data
func (a *Alert) Unmarshal(data []byte) error {
if len(data) != 2 {
return errBufferTooSmall
}
a.Level = Level(data[0])
a.Description = Description(data[1])
return nil
}
func (a *Alert) String() string {
return fmt.Sprintf("Alert %s: %s", a.Level, a.Description)
}

View File

@@ -0,0 +1,29 @@
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
// SPDX-License-Identifier: MIT
package protocol
// ApplicationData messages are carried by the record layer and are
// fragmented, compressed, and encrypted based on the current connection
// state. The messages are treated as transparent data to the record
// layer.
// https://tools.ietf.org/html/rfc5246#section-10
type ApplicationData struct {
Data []byte
}
// ContentType returns the ContentType of this content
func (a ApplicationData) ContentType() ContentType {
return ContentTypeApplicationData
}
// Marshal encodes the ApplicationData to binary
func (a *ApplicationData) Marshal() ([]byte, error) {
return append([]byte{}, a.Data...), nil
}
// Unmarshal populates the ApplicationData from binary
func (a *ApplicationData) Unmarshal(data []byte) error {
a.Data = append([]byte{}, data...)
return nil
}

View File

@@ -0,0 +1,30 @@
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
// SPDX-License-Identifier: MIT
package protocol
// ChangeCipherSpec protocol exists to signal transitions in
// ciphering strategies. The protocol consists of a single message,
// which is encrypted and compressed under the current (not the pending)
// connection state. The message consists of a single byte of value 1.
// https://tools.ietf.org/html/rfc5246#section-7.1
type ChangeCipherSpec struct{}
// ContentType returns the ContentType of this content
func (c ChangeCipherSpec) ContentType() ContentType {
return ContentTypeChangeCipherSpec
}
// Marshal encodes the ChangeCipherSpec to binary
func (c *ChangeCipherSpec) Marshal() ([]byte, error) {
return []byte{0x01}, nil
}
// Unmarshal populates the ChangeCipherSpec from binary
func (c *ChangeCipherSpec) Unmarshal(data []byte) error {
if len(data) == 1 && data[0] == 0x01 {
return nil
}
return errInvalidCipherSpec
}

View File

@@ -0,0 +1,51 @@
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
// SPDX-License-Identifier: MIT
package protocol
// CompressionMethodID is the ID for a CompressionMethod
type CompressionMethodID byte
const (
compressionMethodNull CompressionMethodID = 0
)
// CompressionMethod represents a TLS Compression Method
type CompressionMethod struct {
ID CompressionMethodID
}
// CompressionMethods returns all supported CompressionMethods
func CompressionMethods() map[CompressionMethodID]*CompressionMethod {
return map[CompressionMethodID]*CompressionMethod{
compressionMethodNull: {ID: compressionMethodNull},
}
}
// DecodeCompressionMethods the given compression methods
func DecodeCompressionMethods(buf []byte) ([]*CompressionMethod, error) {
if len(buf) < 1 {
return nil, errBufferTooSmall
}
compressionMethodsCount := int(buf[0])
c := []*CompressionMethod{}
for i := 0; i < compressionMethodsCount; i++ {
if len(buf) <= i+1 {
return nil, errBufferTooSmall
}
id := CompressionMethodID(buf[i+1])
if compressionMethod, ok := CompressionMethods()[id]; ok {
c = append(c, compressionMethod)
}
}
return c, nil
}
// EncodeCompressionMethods the given compression methods
func EncodeCompressionMethods(c []*CompressionMethod) []byte {
out := []byte{byte(len(c))}
for i := len(c); i > 0; i-- {
out = append(out, byte(c[i-1].ID))
}
return out
}

View File

@@ -0,0 +1,24 @@
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
// SPDX-License-Identifier: MIT
package protocol
// ContentType represents the IANA Registered ContentTypes
//
// https://tools.ietf.org/html/rfc4346#section-6.2.1
type ContentType uint8
// ContentType enums
const (
ContentTypeChangeCipherSpec ContentType = 20
ContentTypeAlert ContentType = 21
ContentTypeHandshake ContentType = 22
ContentTypeApplicationData ContentType = 23
)
// Content is the top level distinguisher for a DTLS Datagram
type Content interface {
ContentType() ContentType
Marshal() ([]byte, error)
Unmarshal(data []byte) error
}

View File

@@ -0,0 +1,109 @@
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
// SPDX-License-Identifier: MIT
package protocol
import (
"errors"
"fmt"
"net"
)
var (
errBufferTooSmall = &TemporaryError{Err: errors.New("buffer is too small")} //nolint: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 {
var netErr net.Error
if errors.As(e.Err, &netErr) {
return netErr.Timeout()
}
return false
}
// Temporary implements net.Error.Temporary()
func (e *HandshakeError) Temporary() bool {
var netErr net.Error
if errors.As(e.Err, &netErr) {
return netErr.Temporary() //nolint
}
return false
}
// Unwrap implements Go1.13 error unwrapper.
func (e *HandshakeError) Unwrap() error { return e.Err }
func (e *HandshakeError) Error() string { return fmt.Sprintf("handshake error: %v", e.Err) }

View File

@@ -0,0 +1,80 @@
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
// SPDX-License-Identifier: MIT
package extension
import (
"golang.org/x/crypto/cryptobyte"
)
// ALPN is a TLS extension for application-layer protocol negotiation within
// the TLS handshake.
//
// https://tools.ietf.org/html/rfc7301
type ALPN struct {
ProtocolNameList []string
}
// TypeValue returns the extension TypeValue
func (a ALPN) TypeValue() TypeValue {
return ALPNTypeValue
}
// Marshal encodes the extension
func (a *ALPN) Marshal() ([]byte, error) {
var b cryptobyte.Builder
b.AddUint16(uint16(a.TypeValue()))
b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
for _, proto := range a.ProtocolNameList {
p := proto // Satisfy range scope lint
b.AddUint8LengthPrefixed(func(b *cryptobyte.Builder) {
b.AddBytes([]byte(p))
})
}
})
})
return b.Bytes()
}
// Unmarshal populates the extension from encoded data
func (a *ALPN) Unmarshal(data []byte) error {
val := cryptobyte.String(data)
var extension uint16
val.ReadUint16(&extension)
if TypeValue(extension) != a.TypeValue() {
return errInvalidExtensionType
}
var extData cryptobyte.String
val.ReadUint16LengthPrefixed(&extData)
var protoList cryptobyte.String
if !extData.ReadUint16LengthPrefixed(&protoList) || protoList.Empty() {
return ErrALPNInvalidFormat
}
for !protoList.Empty() {
var proto cryptobyte.String
if !protoList.ReadUint8LengthPrefixed(&proto) || proto.Empty() {
return ErrALPNInvalidFormat
}
a.ProtocolNameList = append(a.ProtocolNameList, string(proto))
}
return nil
}
// ALPNProtocolSelection negotiates a shared protocol according to #3.2 of rfc7301
func ALPNProtocolSelection(supportedProtocols, peerSupportedProtocols []string) (string, error) {
if len(supportedProtocols) == 0 || len(peerSupportedProtocols) == 0 {
return "", nil
}
for _, s := range supportedProtocols {
for _, c := range peerSupportedProtocols {
if s == c {
return s, nil
}
}
}
return "", errALPNNoAppProto
}

View File

@@ -0,0 +1,20 @@
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
// SPDX-License-Identifier: MIT
package extension
import (
"errors"
"github.com/pion/dtls/v2/pkg/protocol"
)
var (
// ErrALPNInvalidFormat is raised when the ALPN format is invalid
ErrALPNInvalidFormat = &protocol.FatalError{Err: errors.New("invalid alpn format")} //nolint:goerr113
errALPNNoAppProto = &protocol.FatalError{Err: errors.New("no application protocol")} //nolint:goerr113
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
)

View File

@@ -0,0 +1,102 @@
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
// SPDX-License-Identifier: MIT
// 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
ALPNTypeValue TypeValue = 16
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 ALPNTypeValue:
err = unmarshalAndAppend(buf[offset:], &ALPN{})
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
}

View File

@@ -0,0 +1,46 @@
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
// SPDX-License-Identifier: MIT
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
}

View File

@@ -0,0 +1,81 @@
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
// SPDX-License-Identifier: MIT
package extension
import (
"strings"
"golang.org/x/crypto/cryptobyte"
)
const serverNameTypeDNSHostName = 0
// ServerName allows the client to inform the server the specific
// name it wishes 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
}

View File

@@ -0,0 +1,32 @@
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
// SPDX-License-Identifier: MIT
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_AES256_CM_SHA1_80 SRTPProtectionProfile = 0x0003 // nolint
SRTP_AES256_CM_SHA1_32 SRTPProtectionProfile = 0x0004 // nolint
SRTP_NULL_HMAC_SHA1_80 SRTPProtectionProfile = 0x0005 // nolint
SRTP_NULL_HMAC_SHA1_32 SRTPProtectionProfile = 0x0006 // 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_AES256_CM_SHA1_80: true,
SRTP_AES256_CM_SHA1_32: true,
SRTP_NULL_HMAC_SHA1_80: true,
SRTP_NULL_HMAC_SHA1_32: true,
SRTP_AEAD_AES_128_GCM: true,
SRTP_AEAD_AES_256_GCM: true,
}
}

View File

@@ -0,0 +1,65 @@
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
// SPDX-License-Identifier: MIT
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
}

View File

@@ -0,0 +1,65 @@
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
// SPDX-License-Identifier: MIT
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
}

View File

@@ -0,0 +1,73 @@
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
// SPDX-License-Identifier: MIT
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
}

View File

@@ -0,0 +1,48 @@
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
// SPDX-License-Identifier: MIT
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
}

View File

@@ -0,0 +1,62 @@
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
// SPDX-License-Identifier: MIT
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
}

View File

@@ -0,0 +1,32 @@
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
// SPDX-License-Identifier: MIT
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
}

View File

@@ -0,0 +1,28 @@
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
// SPDX-License-Identifier: MIT
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
)

View File

@@ -0,0 +1,150 @@
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
// SPDX-License-Identifier: MIT
// Package handshake provides the DTLS wire protocol for handshakes
package handshake
import (
"github.com/pion/dtls/v2/internal/ciphersuite/types"
"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
KeyExchangeAlgorithm types.KeyExchangeAlgorithm
}
// 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{KeyExchangeAlgorithm: h.KeyExchangeAlgorithm}
case TypeCertificateRequest:
h.Message = &MessageCertificateRequest{}
case TypeServerHelloDone:
h.Message = &MessageServerHelloDone{}
case TypeClientKeyExchange:
h.Message = &MessageClientKeyExchange{KeyExchangeAlgorithm: h.KeyExchangeAlgorithm}
case TypeFinished:
h.Message = &MessageFinished{}
case TypeCertificateVerify:
h.Message = &MessageCertificateVerify{}
default:
return errNotImplemented
}
return h.Message.Unmarshal(data[HeaderLength:])
}

View File

@@ -0,0 +1,53 @@
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
// SPDX-License-Identifier: MIT
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
}

View File

@@ -0,0 +1,69 @@
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
// SPDX-License-Identifier: MIT
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
}

View File

@@ -0,0 +1,144 @@
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
// SPDX-License-Identifier: MIT
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
CertificateAuthoritiesNames [][]byte
}
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))
}
// Distinguished Names
casLength := 0
for _, ca := range m.CertificateAuthoritiesNames {
casLength += len(ca) + 2
}
out = append(out, []byte{0x00, 0x00}...)
binary.BigEndian.PutUint16(out[len(out)-2:], uint16(casLength))
if casLength > 0 {
for _, ca := range m.CertificateAuthoritiesNames {
out = append(out, []byte{0x00, 0x00}...)
binary.BigEndian.PutUint16(out[len(out)-2:], uint16(len(ca)))
out = append(out, ca...)
}
}
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})
}
offset += signatureHashAlgorithmsLength
if len(data) < offset+2 {
return errBufferTooSmall
}
casLength := int(binary.BigEndian.Uint16(data[offset:]))
offset += 2
if (offset + casLength) > len(data) {
return errBufferTooSmall
}
cas := make([]byte, casLength)
copy(cas, data[offset:offset+casLength])
m.CertificateAuthoritiesNames = nil
for len(cas) > 0 {
if len(cas) < 2 {
return errBufferTooSmall
}
caLen := binary.BigEndian.Uint16(cas)
cas = cas[2:]
if len(cas) < int(caLen) {
return errBufferTooSmall
}
m.CertificateAuthoritiesNames = append(m.CertificateAuthoritiesNames, cas[:caLen])
cas = cas[caLen:]
}
return nil
}

View File

@@ -0,0 +1,64 @@
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
// SPDX-License-Identifier: MIT
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
}

View File

@@ -0,0 +1,141 @@
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
// SPDX-License-Identifier: MIT
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
SessionID []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, byte(len(m.SessionID)))
out = append(out, m.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++
if len(data) <= currOffset {
return errBufferTooSmall
}
n := int(data[currOffset-1])
if len(data) <= currOffset+n {
return errBufferTooSmall
}
m.SessionID = append([]byte{}, data[currOffset:currOffset+n]...)
currOffset += len(m.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
}

View File

@@ -0,0 +1,81 @@
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
// SPDX-License-Identifier: MIT
package handshake
import (
"encoding/binary"
"github.com/pion/dtls/v2/internal/ciphersuite/types"
)
// 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
// for unmarshaling
KeyExchangeAlgorithm types.KeyExchangeAlgorithm
}
// Type returns the Handshake Type
func (m MessageClientKeyExchange) Type() Type {
return TypeClientKeyExchange
}
// Marshal encodes the Handshake
func (m *MessageClientKeyExchange) Marshal() (out []byte, err error) {
if m.IdentityHint == nil && m.PublicKey == nil {
return nil, errInvalidClientKeyExchange
}
if m.IdentityHint != nil {
out = append([]byte{0x00, 0x00}, m.IdentityHint...)
binary.BigEndian.PutUint16(out, uint16(len(out)-2))
}
if m.PublicKey != nil {
out = append(out, byte(len(m.PublicKey)))
out = append(out, m.PublicKey...)
}
return out, nil
}
// Unmarshal populates the message from encoded data
func (m *MessageClientKeyExchange) Unmarshal(data []byte) error {
switch {
case len(data) < 2:
return errBufferTooSmall
case m.KeyExchangeAlgorithm == types.KeyExchangeAlgorithmNone:
return errCipherSuiteUnset
}
offset := 0
if m.KeyExchangeAlgorithm.Has(types.KeyExchangeAlgorithmPsk) {
pskLength := int(binary.BigEndian.Uint16(data))
if pskLength > len(data)-2 {
return errBufferTooSmall
}
m.IdentityHint = append([]byte{}, data[2:pskLength+2]...)
offset += pskLength + 2
}
if m.KeyExchangeAlgorithm.Has(types.KeyExchangeAlgorithmEcdhe) {
publicKeyLength := int(data[offset])
if publicKeyLength > len(data)-1-offset {
return errBufferTooSmall
}
m.PublicKey = append([]byte{}, data[offset+1:]...)
}
return nil
}

View File

@@ -0,0 +1,30 @@
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
// SPDX-License-Identifier: MIT
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
}

View File

@@ -0,0 +1,65 @@
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
// SPDX-License-Identifier: MIT
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 := int(data[2])
if len(data) < cookieLength+3 {
return errBufferTooSmall
}
m.Cookie = make([]byte, cookieLength)
copy(m.Cookie, data[3:3+cookieLength])
return nil
}

View File

@@ -0,0 +1,122 @@
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
// SPDX-License-Identifier: MIT
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
SessionID []byte
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, byte(len(m.SessionID)))
out = append(out, m.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++
if len(data) <= currOffset {
return errBufferTooSmall
}
n := int(data[currOffset-1])
if len(data) <= currOffset+n {
return errBufferTooSmall
}
m.SessionID = append([]byte{}, data[currOffset:currOffset+n]...)
currOffset += len(m.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
}

Some files were not shown because too many files have changed in this diff Show More