+| Feature | Description |
+|------------------|-----------------------------------------------------|
+| 🪀 Easy to use | PTerm emphasizes ease of use, with [examples](#-examples) and consistent component design. |
+| 🤹♀️ Cross-Platform | PTerm works on various OS and terminals, including `Windows CMD`, `macOS iTerm2`, and in CI systems like `GitHub Actions`. |
+| 🧪 Well tested | A high test coverage and `28774` automated tests ensure PTerm's reliability. |
+| ✨ Consistent Colors | PTerm uses the [ANSI color scheme](https://en.wikipedia.org/wiki/ANSI_escape_code#3/4_bit) for uniformity and supports `TrueColor` for advanced terminals. |
+| 📚 Component system | PTerm's flexible `Printers` can be used individually or combined to generate beautiful console output. |
+| 🛠 Configurable | PTerm is ready to use without configuration but allows easy customization for unique terminal output. |
+| ✏ Documentation | Access comprehensive docs on [pkg.go.dev](https://pkg.go.dev/github.com/pterm/pterm#section-documentation) and view practical examples in the [examples section](#-examples). |
### Printers (Components)
-|Feature|Examples| - |Feature|Examples|
-|-------|--------|---|-----|--------|
-|Bar Charts|[Examples](https://github.com/pterm/pterm/tree/master/_examples/barchart)|-|RGB|[Examples](https://github.com/pterm/pterm/tree/master/_examples/coloring)|
-|BigText|[Examples](https://github.com/pterm/pterm/tree/master/_examples/bigtext)|-|Sections|[Examples](https://github.com/pterm/pterm/tree/master/_examples/section)|
-|Box|[Examples](https://github.com/pterm/pterm/tree/master/_examples/box)|-|Spinners|[Examples](https://github.com/pterm/pterm/tree/master/_examples/spinner)|
-|Bullet Lists|[Examples](https://github.com/pterm/pterm/tree/master/_examples/bulletlist)|-|Trees|[Examples](https://github.com/pterm/pterm/tree/master/_examples/tree)|
-|Centered|[Examples](https://github.com/pterm/pterm/tree/master/_examples/center)|-|Theming|[Examples](https://github.com/pterm/pterm/tree/master/_examples/theme)|
-|Colors|[Examples](https://github.com/pterm/pterm/tree/master/_examples/coloring)|-|Tables|[Examples](https://github.com/pterm/pterm/tree/master/_examples/table)|
-|Headers|[Examples](https://github.com/pterm/pterm/tree/master/_examples/header)|-|Styles|[Examples](https://github.com/pterm/pterm/tree/master/_examples/style)|
-|Panels|[Examples](https://github.com/pterm/pterm/tree/master/_examples/panel)|-|Area|[Examples](https://github.com/pterm/pterm/tree/master/_examples/area)|
-|Paragraphs|[Examples](https://github.com/pterm/pterm/tree/master/_examples/paragraph)|-|||
-|Prefixes|[Examples](https://github.com/pterm/pterm/tree/master/_examples/prefix)|-|||
-|Progress Bars|[Examples](https://github.com/pterm/pterm/tree/master/_examples/progressbar)|-|||
+
+| Feature | Feature | Feature | Feature | Feature |
+| :-------: | :-------: | :-------: | :-------: | :-------: |
+| Area
[(Examples)](https://github.com/pterm/pterm/tree/master/_examples/area) |Barchart
[(Examples)](https://github.com/pterm/pterm/tree/master/_examples/barchart) |Basictext
[(Examples)](https://github.com/pterm/pterm/tree/master/_examples/basictext) |Bigtext
[(Examples)](https://github.com/pterm/pterm/tree/master/_examples/bigtext) |Box
[(Examples)](https://github.com/pterm/pterm/tree/master/_examples/box) |
+| Bulletlist
[(Examples)](https://github.com/pterm/pterm/tree/master/_examples/bulletlist) |Center
[(Examples)](https://github.com/pterm/pterm/tree/master/_examples/center) |Coloring
[(Examples)](https://github.com/pterm/pterm/tree/master/_examples/coloring) |Demo
[(Examples)](https://github.com/pterm/pterm/tree/master/_examples/demo) |Header
[(Examples)](https://github.com/pterm/pterm/tree/master/_examples/header) |
+| Interactive confirm
[(Examples)](https://github.com/pterm/pterm/tree/master/_examples/interactive_confirm) |Interactive continue
[(Examples)](https://github.com/pterm/pterm/tree/master/_examples/interactive_continue) |Interactive multiselect
[(Examples)](https://github.com/pterm/pterm/tree/master/_examples/interactive_multiselect) |Interactive select
[(Examples)](https://github.com/pterm/pterm/tree/master/_examples/interactive_select) |Interactive textinput
[(Examples)](https://github.com/pterm/pterm/tree/master/_examples/interactive_textinput) |
+| Logger
[(Examples)](https://github.com/pterm/pterm/tree/master/_examples/logger) |Panel
[(Examples)](https://github.com/pterm/pterm/tree/master/_examples/panel) |Paragraph
[(Examples)](https://github.com/pterm/pterm/tree/master/_examples/paragraph) |Prefix
[(Examples)](https://github.com/pterm/pterm/tree/master/_examples/prefix) |Progressbar
[(Examples)](https://github.com/pterm/pterm/tree/master/_examples/progressbar) |
+| Section
[(Examples)](https://github.com/pterm/pterm/tree/master/_examples/section) |Spinner
[(Examples)](https://github.com/pterm/pterm/tree/master/_examples/spinner) |Style
[(Examples)](https://github.com/pterm/pterm/tree/master/_examples/style) |Table
[(Examples)](https://github.com/pterm/pterm/tree/master/_examples/table) |Theme
[(Examples)](https://github.com/pterm/pterm/tree/master/_examples/theme) |
+| Tree
[(Examples)](https://github.com/pterm/pterm/tree/master/_examples/tree) | | | | |
+
+
+
+
### 🦸♂️ Supporters
@@ -131,6 +130,70 @@ which features automatic website generation, automatic deployments, a custom CI-
+### area/center
+
+![Animation](https://raw.githubusercontent.com/pterm/pterm/master/_examples/area/center/animation.svg)
+
+
+
+SHOW SOURCE
+
+```go
+package main
+
+import (
+ "time"
+
+ "github.com/pterm/pterm"
+)
+
+func main() {
+ area, _ := pterm.DefaultArea.WithCenter().Start()
+
+ for i := 0; i < 5; i++ {
+ area.Update(pterm.Sprintf("Current count: %d\nAreas can update their content dynamically!", i))
+ time.Sleep(time.Second)
+ }
+
+ area.Stop()
+}
+
+```
+
+
+
+### area/default
+
+![Animation](https://raw.githubusercontent.com/pterm/pterm/master/_examples/area/default/animation.svg)
+
+
+
+SHOW SOURCE
+
+```go
+package main
+
+import (
+ "time"
+
+ "github.com/pterm/pterm"
+)
+
+func main() {
+ area, _ := pterm.DefaultArea.Start()
+
+ for i := 0; i < 5; i++ {
+ area.Update(pterm.Sprintf("Current count: %d\nAreas can update their content dynamically!", i))
+ time.Sleep(time.Second)
+ }
+
+ area.Stop()
+}
+
+```
+
+
+
### area/demo
![Animation](https://raw.githubusercontent.com/pterm/pterm/master/_examples/area/demo/animation.svg)
@@ -165,6 +228,205 @@ func main() {
+### area/dynamic-chart
+
+![Animation](https://raw.githubusercontent.com/pterm/pterm/master/_examples/area/dynamic-chart/animation.svg)
+
+
+
+SHOW SOURCE
+
+```go
+package main
+
+import (
+ "time"
+
+ "github.com/pterm/pterm"
+)
+
+func main() {
+ area, _ := pterm.DefaultArea.WithFullscreen().WithCenter().Start()
+ defer area.Stop()
+
+ for i := 0; i < 10; i++ {
+ barchart := pterm.DefaultBarChart.WithBars(dynamicBars(i))
+ content, _ := barchart.Srender()
+ area.Update(content)
+ time.Sleep(500 * time.Millisecond)
+ }
+}
+
+func dynamicBars(i int) pterm.Bars {
+ return pterm.Bars{
+ {Label: "A", Value: 10},
+ {Label: "B", Value: 20 * i},
+ {Label: "C", Value: 30},
+ {Label: "D", Value: 40 + i},
+ }
+}
+
+```
+
+
+
+### area/fullscreen
+
+![Animation](https://raw.githubusercontent.com/pterm/pterm/master/_examples/area/fullscreen/animation.svg)
+
+
+
+SHOW SOURCE
+
+```go
+package main
+
+import (
+ "time"
+
+ "github.com/pterm/pterm"
+)
+
+func main() {
+ area, _ := pterm.DefaultArea.WithFullscreen().Start()
+
+ for i := 0; i < 5; i++ {
+ area.Update(pterm.Sprintf("Current count: %d\nAreas can update their content dynamically!", i))
+ time.Sleep(time.Second)
+ }
+
+ area.Stop()
+}
+
+```
+
+
+
+### area/fullscreen-center
+
+![Animation](https://raw.githubusercontent.com/pterm/pterm/master/_examples/area/fullscreen-center/animation.svg)
+
+
+
+SHOW SOURCE
+
+```go
+package main
+
+import (
+ "time"
+
+ "github.com/pterm/pterm"
+)
+
+func main() {
+ area, _ := pterm.DefaultArea.WithFullscreen().WithCenter().Start()
+
+ for i := 0; i < 5; i++ {
+ area.Update(pterm.Sprintf("Current count: %d\nAreas can update their content dynamically!", i))
+ time.Sleep(time.Second)
+ }
+
+ area.Stop()
+}
+
+```
+
+
+
+### barchart/custom-height
+
+![Animation](https://raw.githubusercontent.com/pterm/pterm/master/_examples/barchart/custom-height/animation.svg)
+
+
+
+SHOW SOURCE
+
+```go
+package main
+
+import "github.com/pterm/pterm"
+
+func main() {
+ pterm.DefaultBarChart.WithBars([]pterm.Bar{
+ {Label: "A", Value: 10},
+ {Label: "B", Value: 20},
+ {Label: "C", Value: 30},
+ {Label: "D", Value: 40},
+ {Label: "E", Value: 50},
+ {Label: "F", Value: 40},
+ {Label: "G", Value: 30},
+ {Label: "H", Value: 20},
+ {Label: "I", Value: 10},
+ }).WithHeight(5).Render()
+}
+
+```
+
+
+
+### barchart/custom-width
+
+![Animation](https://raw.githubusercontent.com/pterm/pterm/master/_examples/barchart/custom-width/animation.svg)
+
+
+
+SHOW SOURCE
+
+```go
+package main
+
+import "github.com/pterm/pterm"
+
+func main() {
+ pterm.DefaultBarChart.WithBars([]pterm.Bar{
+ {Label: "A", Value: 10},
+ {Label: "B", Value: 20},
+ {Label: "C", Value: 30},
+ {Label: "D", Value: 40},
+ {Label: "E", Value: 50},
+ {Label: "F", Value: 40},
+ {Label: "G", Value: 30},
+ {Label: "H", Value: 20},
+ {Label: "I", Value: 10},
+ }).WithHorizontal().WithWidth(5).Render()
+}
+
+```
+
+
+
+### barchart/default
+
+![Animation](https://raw.githubusercontent.com/pterm/pterm/master/_examples/barchart/default/animation.svg)
+
+
+
+SHOW SOURCE
+
+```go
+package main
+
+import "github.com/pterm/pterm"
+
+func main() {
+ pterm.DefaultBarChart.WithBars([]pterm.Bar{
+ {Label: "A", Value: 10},
+ {Label: "B", Value: 20},
+ {Label: "C", Value: 30},
+ {Label: "D", Value: 40},
+ {Label: "E", Value: 50},
+ {Label: "F", Value: 40},
+ {Label: "G", Value: 30},
+ {Label: "H", Value: 20},
+ {Label: "I", Value: 10},
+ }).Render()
+}
+
+```
+
+
+
### barchart/demo
![Animation](https://raw.githubusercontent.com/pterm/pterm/master/_examples/barchart/demo/animation.svg)
@@ -205,6 +467,68 @@ func main() {
+### barchart/horizontal
+
+![Animation](https://raw.githubusercontent.com/pterm/pterm/master/_examples/barchart/horizontal/animation.svg)
+
+
+
+SHOW SOURCE
+
+```go
+package main
+
+import "github.com/pterm/pterm"
+
+func main() {
+ pterm.DefaultBarChart.WithBars([]pterm.Bar{
+ {Label: "A", Value: 10},
+ {Label: "B", Value: 20},
+ {Label: "C", Value: 30},
+ {Label: "D", Value: 40},
+ {Label: "E", Value: 50},
+ {Label: "F", Value: 40},
+ {Label: "G", Value: 30},
+ {Label: "H", Value: 20},
+ {Label: "I", Value: 10},
+ }).WithHorizontal().Render()
+}
+
+```
+
+
+
+### barchart/horizontal-show-value
+
+![Animation](https://raw.githubusercontent.com/pterm/pterm/master/_examples/barchart/horizontal-show-value/animation.svg)
+
+
+
+SHOW SOURCE
+
+```go
+package main
+
+import "github.com/pterm/pterm"
+
+func main() {
+ pterm.DefaultBarChart.WithBars([]pterm.Bar{
+ {Label: "A", Value: 10},
+ {Label: "B", Value: 20},
+ {Label: "C", Value: 30},
+ {Label: "D", Value: 40},
+ {Label: "E", Value: 50},
+ {Label: "F", Value: 40},
+ {Label: "G", Value: 30},
+ {Label: "H", Value: 20},
+ {Label: "I", Value: 10},
+ }).WithHorizontal().WithShowValue().Render()
+}
+
+```
+
+
+
### barchart/mixed-values
![Animation](https://raw.githubusercontent.com/pterm/pterm/master/_examples/barchart/mixed-values/animation.svg)
@@ -284,18 +608,164 @@ func main() {
},
}
- pterm.Info.Println("Chart example with negative only values (bars use 100% of chart area)")
- _ = pterm.DefaultBarChart.WithBars(negativeBars).WithShowValue().Render()
- _ = pterm.DefaultBarChart.WithHorizontal().WithBars(negativeBars).WithShowValue().Render()
+ pterm.Info.Println("Chart example with negative only values (bars use 100% of chart area)")
+ _ = pterm.DefaultBarChart.WithBars(negativeBars).WithShowValue().Render()
+ _ = pterm.DefaultBarChart.WithHorizontal().WithBars(negativeBars).WithShowValue().Render()
+}
+
+```
+
+
+
+### barchart/show-value
+
+![Animation](https://raw.githubusercontent.com/pterm/pterm/master/_examples/barchart/show-value/animation.svg)
+
+
+
+SHOW SOURCE
+
+```go
+package main
+
+import "github.com/pterm/pterm"
+
+func main() {
+ pterm.DefaultBarChart.WithBars([]pterm.Bar{
+ {Label: "A", Value: 10},
+ {Label: "B", Value: 20},
+ {Label: "C", Value: 30},
+ {Label: "D", Value: 40},
+ {Label: "E", Value: 50},
+ {Label: "F", Value: 40},
+ {Label: "G", Value: 30},
+ {Label: "H", Value: 20},
+ {Label: "I", Value: 10},
+ }).WithShowValue().Render()
+}
+
+```
+
+
+
+### basictext/demo
+
+![Animation](https://raw.githubusercontent.com/pterm/pterm/master/_examples/basictext/demo/animation.svg)
+
+
+
+SHOW SOURCE
+
+```go
+package main
+
+import "github.com/pterm/pterm"
+
+func main() {
+ // A BasicText printer is used to print text, without special formatting.
+ // As it implements the TextPrinter interface, you can use it in combination with other printers.
+ pterm.DefaultBasicText.Println("Default basic text printer.")
+ pterm.DefaultBasicText.Println("Can be used in any" + pterm.LightMagenta(" TextPrinter ") + "context.")
+ pterm.DefaultBasicText.Println("For example to resolve progressbars and spinners.")
+ // If you just want to print text, you should use this instead:
+ // pterm.Println("Hello, World!")
+}
+
+```
+
+
+
+### bigtext/colored
+
+![Animation](https://raw.githubusercontent.com/pterm/pterm/master/_examples/bigtext/colored/animation.svg)
+
+
+
+SHOW SOURCE
+
+```go
+package main
+
+import (
+ "github.com/pterm/pterm"
+ "github.com/pterm/pterm/putils"
+)
+
+func main() {
+ pterm.DefaultBigText.WithLetters(
+ putils.LettersFromStringWithStyle("P", pterm.FgCyan.ToStyle()),
+ putils.LettersFromStringWithStyle("Term", pterm.FgLightMagenta.ToStyle())).
+ Render()
+}
+
+```
+
+
+
+### bigtext/default
+
+![Animation](https://raw.githubusercontent.com/pterm/pterm/master/_examples/bigtext/default/animation.svg)
+
+
+
+SHOW SOURCE
+
+```go
+package main
+
+import (
+ "github.com/pterm/pterm"
+ "github.com/pterm/pterm/putils"
+)
+
+func main() {
+ pterm.DefaultBigText.WithLetters(putils.LettersFromString("PTerm")).Render()
+}
+
+```
+
+
+
+### bigtext/demo
+
+![Animation](https://raw.githubusercontent.com/pterm/pterm/master/_examples/bigtext/demo/animation.svg)
+
+
+
+SHOW SOURCE
+
+```go
+package main
+
+import (
+ "github.com/pterm/pterm"
+ "github.com/pterm/pterm/putils"
+)
+
+func main() {
+ // Print a large text with the LetterStyle from the standard theme.
+ // Useful for title screens.
+ pterm.DefaultBigText.WithLetters(putils.LettersFromString("PTerm")).Render()
+
+ // Print a large text with differently colored letters.
+ pterm.DefaultBigText.WithLetters(
+ putils.LettersFromStringWithStyle("P", pterm.FgCyan.ToStyle()),
+ putils.LettersFromStringWithStyle("Term", pterm.FgLightMagenta.ToStyle())).
+ Render()
+
+ // LettersFromStringWithRGB can be used to create a large text with a specific RGB color.
+ pterm.DefaultBigText.WithLetters(
+ putils.LettersFromStringWithRGB("PTerm", pterm.NewRGB(255, 215, 0))).
+ Render()
}
```
-### basictext/demo
+### box/custom-padding
-![Animation](https://raw.githubusercontent.com/pterm/pterm/master/_examples/basictext/demo/animation.svg)
+![Animation](https://raw.githubusercontent.com/pterm/pterm/master/_examples/box/custom-padding/animation.svg)
@@ -307,22 +777,21 @@ package main
import "github.com/pterm/pterm"
func main() {
- // A BasicText printer is used to print text, without special formatting.
- // As it implements the TextPrinter interface, you can use it in combination with other printers.
- pterm.DefaultBasicText.Println("Default basic text printer.")
- pterm.DefaultBasicText.Println("Can be used in any" + pterm.LightMagenta(" TextPrinter ") + "context.")
- pterm.DefaultBasicText.Println("For example to resolve progressbars and spinners.")
- // If you just want to print text, you should use this instead:
- // pterm.Println("Hello, World!")
+ pterm.DefaultBox.
+ WithRightPadding(10).
+ WithLeftPadding(10).
+ WithTopPadding(2).
+ WithBottomPadding(2).
+ Println("Hello, World!")
}
```
-### bigtext/demo
+### box/default
-![Animation](https://raw.githubusercontent.com/pterm/pterm/master/_examples/bigtext/demo/animation.svg)
+![Animation](https://raw.githubusercontent.com/pterm/pterm/master/_examples/box/default/animation.svg)
@@ -331,26 +800,10 @@ func main() {
```go
package main
-import (
- "github.com/pterm/pterm"
- "github.com/pterm/pterm/putils"
-)
+import "github.com/pterm/pterm"
func main() {
- // Print a large text with the LetterStyle from the standard theme.
- // Useful for title screens.
- pterm.DefaultBigText.WithLetters(putils.LettersFromString("PTerm")).Render()
-
- // Print a large text with differently colored letters.
- pterm.DefaultBigText.WithLetters(
- putils.LettersFromStringWithStyle("P", pterm.NewStyle(pterm.FgCyan)),
- putils.LettersFromStringWithStyle("Term", pterm.NewStyle(pterm.FgLightMagenta))).
- Render()
-
- // LettersFromStringWithRGB can be used to create a large text with a specific RGB color.
- pterm.DefaultBigText.WithLetters(
- putils.LettersFromStringWithRGB("PTerm", pterm.NewRGB(255, 215, 0))).
- Render()
+ pterm.DefaultBox.Println("Hello, World!")
}
```
@@ -389,6 +842,44 @@ func main() {
+### box/title
+
+![Animation](https://raw.githubusercontent.com/pterm/pterm/master/_examples/box/title/animation.svg)
+
+
+
+SHOW SOURCE
+
+```go
+package main
+
+import "github.com/pterm/pterm"
+
+func main() {
+ // Default titled bpx
+ paddedBox := pterm.DefaultBox.WithLeftPadding(4).WithRightPadding(4).WithTopPadding(1).WithBottomPadding(1)
+
+ title := pterm.LightRed("I'm a box!")
+
+ box1 := paddedBox.WithTitle(title).Sprint("Hello, World!\n 1")
+ box2 := paddedBox.WithTitle(title).WithTitleTopCenter().Sprint("Hello, World!\n 2")
+ box3 := paddedBox.WithTitle(title).WithTitleTopRight().Sprint("Hello, World!\n 3")
+ box4 := paddedBox.WithTitle(title).WithTitleBottomRight().Sprint("Hello, World!\n 4")
+ box5 := paddedBox.WithTitle(title).WithTitleBottomCenter().Sprint("Hello, World!\n 5")
+ box6 := paddedBox.WithTitle(title).WithTitleBottomLeft().Sprint("Hello, World!\n 6")
+ box7 := paddedBox.WithTitle(title).WithTitleTopLeft().Sprint("Hello, World!\n 7")
+
+ pterm.DefaultPanel.WithPanels([][]pterm.Panel{
+ {{box1}, {box2}, {box3}},
+ {{box4}, {box5}, {box6}},
+ {{box7}},
+ }).Render()
+}
+
+```
+
+
+
### bulletlist/customized
![Animation](https://raw.githubusercontent.com/pterm/pterm/master/_examples/bulletlist/customized/animation.svg)
@@ -589,6 +1080,87 @@ func main() {
+### coloring/fade-colors-rgb-style
+
+![Animation](https://raw.githubusercontent.com/pterm/pterm/master/_examples/coloring/fade-colors-rgb-style/animation.svg)
+
+
+
+SHOW SOURCE
+
+```go
+package main
+
+import (
+ "strings"
+
+ "github.com/pterm/pterm"
+)
+
+func main() {
+ white := pterm.NewRGB(255, 255, 255) // This RGB value is used as the gradients start point.
+ grey := pterm.NewRGB(128, 128, 128) // This RGB value is used as the gradients start point.
+ black := pterm.NewRGB(0, 0, 0) // This RGB value is used as the gradients start point.
+ red := pterm.NewRGB(255, 0, 0) // This RGB value is used as the gradients start point.
+ purple := pterm.NewRGB(255, 0, 255) // This RGB value is used as the gradients start point.
+ green := pterm.NewRGB(0, 255, 0) // This RGB value is used as the gradients start point.
+
+ str := "RGB colors only work in Terminals which support TrueColor."
+ strs := strings.Split(str, "")
+ var fadeInfo string // String which will be used to print.
+ for i := 0; i < len(str); i++ {
+ // Append faded letter to info string.
+ fadeInfo += pterm.NewRGBStyle(white.Fade(0, float32(len(str)), float32(i), purple), grey.Fade(0, float32(len(str)), float32(i), black)).Sprint(strs[i])
+ }
+
+ pterm.Info.Println(fadeInfo)
+
+ str = "The background and foreground colors can be customized individually."
+ strs = strings.Split(str, "")
+ var fade2 string // String which will be used to print info.
+ for i := 0; i < len(str); i++ {
+ // Append faded letter to info string.
+ fade2 += pterm.NewRGBStyle(black, purple.Fade(0, float32(len(str)), float32(i), red)).Sprint(strs[i])
+ }
+
+ pterm.Println(fade2)
+
+ str = "Styles can also be applied. For example: Bold or Italic."
+ strs = strings.Split(str, "")
+ var fade3 string // String which will be used to print.
+
+ bold := 0
+ boldStr := strings.Split("Bold", "")
+ italic := 0
+ italicStr := strings.Split("Italic", "")
+
+ for i := 0; i < len(str); i++ {
+ // Append faded letter to info string.
+ s := pterm.NewRGBStyle(white.Fade(0, float32(len(str)), float32(i), green), red.Fade(0, float32(len(str)), float32(i), black))
+
+ // if the next letters are "Bold", then add the style "Bold".
+ // else if the next letters are "Italic", then add the style "Italic".
+ if bold < len(boldStr) && i+len(boldStr) <= len(strs) {
+ if strings.Join(strs[i:i+len(boldStr)-bold], "") == strings.Join(boldStr[bold:], "") {
+ s = s.AddOptions(pterm.Bold)
+ bold++
+ }
+ } else if italic < len(italicStr) && i+len(italicStr)-italic < len(strs) {
+ if strings.Join(strs[i:i+len(italicStr)-italic], "") == strings.Join(italicStr[italic:], "") {
+ s = s.AddOptions(pterm.Italic)
+ italic++
+ }
+ }
+ fade3 += s.Sprint(strs[i])
+ }
+
+ pterm.Println(fade3)
+}
+
+```
+
+
+
### coloring/fade-multiple-colors
![Animation](https://raw.githubusercontent.com/pterm/pterm/master/_examples/coloring/fade-multiple-colors/animation.svg)
@@ -685,7 +1257,40 @@ func main() {
// NOTICE: This only works with terminals which support TrueColor.
pterm.NewRGB(178, 44, 199).Println("This text is printed with a custom RGB!")
pterm.NewRGB(15, 199, 209).Println("This text is printed with a custom RGB!")
- pterm.NewRGB(201, 144, 30).Println("This text is printed with a custom RGB!")
+ pterm.NewRGB(201, 144, 30, true).Println("This text is printed with a custom RGB background!")
+}
+
+```
+
+
+
+### coloring/print-color-rgb-style
+
+![Animation](https://raw.githubusercontent.com/pterm/pterm/master/_examples/coloring/print-color-rgb-style/animation.svg)
+
+
+
+SHOW SOURCE
+
+```go
+package main
+
+import (
+ "github.com/pterm/pterm"
+)
+
+func main() {
+ foregroundRGB := pterm.RGB{R: 187, G: 80, B: 0}
+ backgroundRGB := pterm.RGB{R: 0, G: 50, B: 123}
+
+ // Print string with a custom foreground and background RGB color.
+ pterm.NewRGBStyle(foregroundRGB, backgroundRGB).Println("This text is not styled.")
+
+ // Print string with a custom foreground and background RGB color and style bold.
+ pterm.NewRGBStyle(foregroundRGB, backgroundRGB).AddOptions(pterm.Bold).Println("This text is bold.")
+
+ // Print string with a custom foreground and background RGB color and style italic.
+ pterm.NewRGBStyle(foregroundRGB, backgroundRGB).AddOptions(pterm.Italic).Println("This text is italic.")
}
```
@@ -718,7 +1323,8 @@ import (
// Speed the demo up, by setting this flag.
// Usefull for debugging.
// Example:
-// go run main.go -speedup
+//
+// go run main.go -speedup
var speedup = flag.Bool("speedup", false, "Speed up the demo")
var skipIntro = flag.Bool("skip-intro", false, "Skips the intro")
var second = time.Second
@@ -735,6 +1341,31 @@ func main() {
clear()
}
+ showcase("Structured Logging", 5, func() {
+ logger := pterm.DefaultLogger.
+ WithLevel(pterm.LogLevelTrace)
+
+ logger.Trace("Doing not so important stuff", logger.Args("priority", "super low"))
+
+ time.Sleep(time.Second * 3)
+
+ interstingStuff := map[string]any{
+ "when were crayons invented": "1903",
+ "what is the meaning of life": 42,
+ "is this interesting": true,
+ }
+ logger.Debug("This might be interesting", logger.ArgsFromMap(interstingStuff))
+ time.Sleep(time.Second * 3)
+
+ logger.Info("That was actually interesting", logger.Args("such", "wow"))
+ time.Sleep(time.Second * 3)
+ logger.Warn("Oh no, I see an error coming to us!", logger.Args("speed", 88, "measures", "mph"))
+ time.Sleep(time.Second * 3)
+ logger.Error("Damn, here it is!", logger.Args("error", "something went wrong"))
+ time.Sleep(time.Second * 3)
+ logger.Info("But what's really cool is, that you can print very long logs, and PTerm will automatically wrap them for you! Say goodbye to text, that has weird line breaks!", logger.Args("very", "long"))
+ })
+
showcase("Progress bar", 2, func() {
pb, _ := pterm.DefaultProgressbar.WithTotal(len(pseudoProgramList)).WithTitle("Installing stuff").Start()
for i := 0; i < pb.Total; i++ {
@@ -793,28 +1424,6 @@ func main() {
pterm.DefaultCenter.Println(boxedTable)
})
- showcase("Default Prefix Printers", 5, func() {
- // Enable debug messages.
- pterm.EnableDebugMessages() // Temporarily set debug output to true, to display the debug printer.
-
- pterm.Debug.Println("Hello, World!") // Print Debug.
- time.Sleep(second / 2)
- pterm.Info.Println("Hello, World!") // Print Info.
- time.Sleep(second / 2)
- pterm.Success.Println("Hello, World!") // Print Success.
- time.Sleep(second / 2)
- pterm.Warning.Println("Hello, World!") // Print Warning.
- time.Sleep(second / 2)
- pterm.Error.Println("Errors show the filename and linenumber inside the terminal!") // Print Error.
- time.Sleep(second / 2)
- pterm.Info.WithShowLineNumber().Println("Other PrefixPrinters can do that too!") // Print Error.
- time.Sleep(second / 2)
- // Temporarily set Fatal to false, so that the CI won't panic.
- pterm.Fatal.WithFatal(false).Println("Hello, World!") // Print Fatal.
-
- pterm.DisableDebugMessages() // Disable debug output again.
- })
-
showcase("TrueColor Support", 7, func() {
from := pterm.NewRGB(0, 255, 255) // This RGB value is used as the gradients start point.
to := pterm.NewRGB(255, 0, 255) // This RGB value is used as the gradients first point.
@@ -830,24 +1439,6 @@ func main() {
pterm.DefaultCenter.WithCenterEachLineSeparately().Println(fadeInfo)
})
- showcase("Themes", 2, func() {
- pterm.Info.Println("You can change the color theme of PTerm easily to fit your needs!\nThis is the default one:")
- time.Sleep(second / 2)
- // Print every value of the default theme with its own style.
- v := reflect.ValueOf(pterm.ThemeDefault)
- typeOfS := v.Type()
-
- if typeOfS == reflect.TypeOf(pterm.Theme{}) {
- for i := 0; i < v.NumField(); i++ {
- field, ok := v.Field(i).Interface().(pterm.Style)
- if ok {
- field.Println(typeOfS.Field(i).Name)
- }
- time.Sleep(time.Millisecond * 250)
- }
- }
- })
-
showcase("Fully Customizale", 2, func() {
for i := 0; i < 4; i++ {
pterm.Println()
@@ -896,6 +1487,24 @@ func main() {
area.Stop()
})
+ showcase("Themes", 2, func() {
+ pterm.Info.Println("You can change the color theme of PTerm easily to fit your needs!\nThis is the default one:")
+ time.Sleep(second / 2)
+ // Print every value of the default theme with its own style.
+ v := reflect.ValueOf(pterm.ThemeDefault)
+ typeOfS := v.Type()
+
+ if typeOfS == reflect.TypeOf(pterm.Theme{}) {
+ for i := 0; i < v.NumField(); i++ {
+ field, ok := v.Field(i).Interface().(pterm.Style)
+ if ok {
+ field.Println(typeOfS.Field(i).Name)
+ }
+ time.Sleep(time.Millisecond * 250)
+ }
+ }
+ })
+
showcase("And much more!", 3, func() {
for i := 0; i < 4; i++ {
pterm.Println()
@@ -970,33 +1579,9 @@ func randomInt(min, max int) int {
-### header/demo
-
-![Animation](https://raw.githubusercontent.com/pterm/pterm/master/_examples/header/demo/animation.svg)
-
-
-
-SHOW SOURCE
-
-```go
-package main
-
-import "github.com/pterm/pterm"
-
-func main() {
- // Print a default header.
- pterm.DefaultHeader.Println("This is the default header!")
- pterm.Println() // spacer
- pterm.DefaultHeader.WithFullWidth().Println("This is a full-width header.")
-}
-
-```
-
-
-
-### header-custom/demo
+### header/custom
-![Animation](https://raw.githubusercontent.com/pterm/pterm/master/_examples/header-custom/demo/animation.svg)
+![Animation](https://raw.githubusercontent.com/pterm/pterm/master/_examples/header/custom/animation.svg)
@@ -1034,6 +1619,30 @@ func main() {
+### header/demo
+
+![Animation](https://raw.githubusercontent.com/pterm/pterm/master/_examples/header/demo/animation.svg)
+
+
+
+SHOW SOURCE
+
+```go
+package main
+
+import "github.com/pterm/pterm"
+
+func main() {
+ // Print a default header.
+ pterm.DefaultHeader.Println("This is the default header!")
+ pterm.Println() // spacer
+ pterm.DefaultHeader.WithFullWidth().Println("This is a full-width header.")
+}
+
+```
+
+
+
### interactive_confirm/demo
![Animation](https://raw.githubusercontent.com/pterm/pterm/master/_examples/interactive_confirm/demo/animation.svg)
@@ -1055,20 +1664,45 @@ func main() {
pterm.Info.Printfln("You answered: %s", boolToText(result))
}
-func boolToText(b bool) string {
- if b {
- return pterm.Green("Yes")
- }
- return pterm.Red("No")
+func boolToText(b bool) string {
+ if b {
+ return pterm.Green("Yes")
+ }
+ return pterm.Red("No")
+}
+
+```
+
+
+
+### interactive_continue/demo
+
+![Animation](https://raw.githubusercontent.com/pterm/pterm/master/_examples/interactive_continue/demo/animation.svg)
+
+
+
+SHOW SOURCE
+
+```go
+package main
+
+import (
+ "github.com/pterm/pterm"
+)
+
+func main() {
+ result, _ := pterm.DefaultInteractiveContinue.Show()
+ pterm.Println() // Blank line
+ pterm.Info.Printfln("You answered: %s", result)
}
```
-### interactive_continue/demo
+### interactive_multiselect/custom-checkmarks
-![Animation](https://raw.githubusercontent.com/pterm/pterm/master/_examples/interactive_continue/demo/animation.svg)
+![Animation](https://raw.githubusercontent.com/pterm/pterm/master/_examples/interactive_multiselect/custom-checkmarks/animation.svg)
@@ -1078,13 +1712,27 @@ func boolToText(b bool) string {
package main
import (
+ "fmt"
+
+ "atomicgo.dev/keyboard/keys"
+
"github.com/pterm/pterm"
)
func main() {
- result, _ := pterm.DefaultInteractiveContinue.Show()
- pterm.Println() // Blank line
- pterm.Info.Printfln("You answered: %s", result)
+ var options []string
+
+ for i := 0; i < 5; i++ {
+ options = append(options, fmt.Sprintf("Option %d", i))
+ }
+
+ printer := pterm.DefaultInteractiveMultiselect.WithOptions(options)
+ printer.Filter = false
+ printer.KeyConfirm = keys.Enter
+ printer.KeySelect = keys.Space
+ printer.Checkmark = &pterm.Checkmark{Checked: pterm.Green("+"), Unchecked: pterm.Red("-")}
+ selectedOptions, _ := printer.Show()
+ pterm.Info.Printfln("Selected options: %s", pterm.Green(selectedOptions))
}
```
@@ -1250,6 +1898,235 @@ func main() {
+### interactive_textinput/password
+
+![Animation](https://raw.githubusercontent.com/pterm/pterm/master/_examples/interactive_textinput/password/animation.svg)
+
+
+
+SHOW SOURCE
+
+```go
+package main
+
+import "github.com/pterm/pterm"
+
+func main() {
+ result, _ := pterm.DefaultInteractiveTextInput.WithMask("*").Show("Enter your password")
+
+ logger := pterm.DefaultLogger
+ logger.Info("Password received", logger.Args("password", result))
+}
+
+```
+
+
+
+### logger/custom-key-styles
+
+![Animation](https://raw.githubusercontent.com/pterm/pterm/master/_examples/logger/custom-key-styles/animation.svg)
+
+
+
+SHOW SOURCE
+
+```go
+package main
+
+import "github.com/pterm/pterm"
+
+func main() {
+ logger := pterm.DefaultLogger.WithLevel(pterm.LogLevelTrace) // Only show logs with a level of Trace or higher.
+
+ // Overwrite all key styles with a new map
+ logger = logger.WithKeyStyles(map[string]pterm.Style{
+ "priority": *pterm.NewStyle(pterm.FgRed),
+ })
+
+ // The priority key should now be red
+ logger.Info("The priority key should now be red", logger.Args("priority", "low", "foo", "bar"))
+
+ // Append a key style to the exisiting ones
+ logger.AppendKeyStyle("foo", *pterm.NewStyle(pterm.FgBlue))
+
+ // The foo key should now be blue
+ logger.Info("The foo key should now be blue", logger.Args("priority", "low", "foo", "bar"))
+}
+
+```
+
+
+
+### logger/default
+
+![Animation](https://raw.githubusercontent.com/pterm/pterm/master/_examples/logger/default/animation.svg)
+
+
+
+SHOW SOURCE
+
+```go
+package main
+
+import (
+ "github.com/pterm/pterm"
+ "time"
+)
+
+func main() {
+ logger := pterm.DefaultLogger.WithLevel(pterm.LogLevelTrace) // Only show logs with a level of Trace or higher.
+
+ logger.Trace("Doing not so important stuff", logger.Args("priority", "super low"))
+
+ // You can also use the `ArgsFromMap` function to create a `Args` object from a map.
+ interstingStuff := map[string]any{
+ "when were crayons invented": "1903",
+ "what is the meaning of life": 42,
+ "is this interesting": true,
+ }
+ logger.Debug("This might be interesting", logger.ArgsFromMap(interstingStuff))
+
+ logger.Info("That was actually interesting", logger.Args("such", "wow"))
+ logger.Warn("Oh no, I see an error coming to us!", logger.Args("speed", 88, "measures", "mph"))
+ logger.Error("Damn, here it is!", logger.Args("error", "something went wrong"))
+ logger.Info("But what's really cool is, that you can print very long logs, and PTerm will automatically wrap them for you! Say goodbye to text, that has weird line breaks!", logger.Args("very", "long"))
+ time.Sleep(time.Second * 2)
+ logger.Fatal("Oh no, this process is getting killed!", logger.Args("fatal", true))
+}
+
+```
+
+
+
+### logger/demo
+
+![Animation](https://raw.githubusercontent.com/pterm/pterm/master/_examples/logger/demo/animation.svg)
+
+
+
+SHOW SOURCE
+
+```go
+package main
+
+import (
+ "github.com/pterm/pterm"
+ "time"
+)
+
+func main() {
+ logger := pterm.DefaultLogger.
+ WithLevel(pterm.LogLevelTrace)
+
+ logger.Trace("Doing not so important stuff", logger.Args("priority", "super low"))
+
+ sleep()
+
+ interstingStuff := map[string]any{
+ "when were crayons invented": "1903",
+ "what is the meaning of life": 42,
+ "is this interesting": true,
+ }
+ logger.Debug("This might be interesting", logger.ArgsFromMap(interstingStuff))
+ sleep()
+
+ logger.Info("That was actually interesting", logger.Args("such", "wow"))
+ sleep()
+ logger.Warn("Oh no, I see an error coming to us!", logger.Args("speed", 88, "measures", "mph"))
+ sleep()
+ logger.Error("Damn, here it is!", logger.Args("error", "something went wrong"))
+ sleep()
+ logger.Info("But what's really cool is, that you can print very long logs, and PTerm will automatically wrap them for you! Say goodbye to text, that has weird line breaks!", logger.Args("very", "long"))
+ sleep()
+ logger.Fatal("Oh no, this process is getting killed!", logger.Args("fatal", true))
+}
+
+func sleep() {
+ time.Sleep(time.Second * 3)
+}
+
+```
+
+
+
+### logger/json
+
+![Animation](https://raw.githubusercontent.com/pterm/pterm/master/_examples/logger/json/animation.svg)
+
+
+
+SHOW SOURCE
+
+```go
+package main
+
+import "github.com/pterm/pterm"
+
+func main() {
+ logger := pterm.DefaultLogger.
+ WithLevel(pterm.LogLevelTrace). // Only show logs with a level of Trace or higher.
+ WithFormatter(pterm.LogFormatterJSON) // ! Make the logger print JSON logs.
+
+ logger.Trace("Doing not so important stuff", logger.Args("priority", "super low"))
+
+ // You can also use the `ArgsFromMap` function to create a `Args` object from a map.
+ interstingStuff := map[string]any{
+ "when were crayons invented": "1903",
+ "what is the meaning of life": 42,
+ "is this interesting": true,
+ }
+ logger.Debug("This might be interesting", logger.ArgsFromMap(interstingStuff))
+
+ logger.Info("That was actually interesting", logger.Args("such", "wow"))
+ logger.Warn("Oh no, I see an error coming to us!", logger.Args("speed", 88, "measures", "mph"))
+ logger.Error("Damn, here it is!", logger.Args("error", "something went wrong"))
+ logger.Info("But what's really cool is, that you can print very long logs, and PTerm will automatically wrap them for you! Say goodbye to text, that has weird line breaks!", logger.Args("very", "long"))
+ logger.Fatal("Oh no, this process is getting killed!", logger.Args("fatal", true))
+}
+
+```
+
+
+
+### logger/with-caller
+
+![Animation](https://raw.githubusercontent.com/pterm/pterm/master/_examples/logger/with-caller/animation.svg)
+
+
+
+SHOW SOURCE
+
+```go
+package main
+
+import "github.com/pterm/pterm"
+
+func main() {
+ logger := pterm.DefaultLogger.
+ WithLevel(pterm.LogLevelTrace). // Only show logs with a level of Trace or higher.
+ WithCaller() // ! Show the caller of the log function.
+
+ logger.Trace("Doing not so important stuff", logger.Args("priority", "super low"))
+
+ // You can also use the `ArgsFromMap` function to create a `Args` object from a map.
+ interstingStuff := map[string]any{
+ "when were crayons invented": "1903",
+ "what is the meaning of life": 42,
+ "is this interesting": true,
+ }
+ logger.Debug("This might be interesting", logger.ArgsFromMap(interstingStuff))
+
+ logger.Info("That was actually interesting", logger.Args("such", "wow"))
+ logger.Warn("Oh no, I see an error coming to us!", logger.Args("speed", 88, "measures", "mph"))
+ logger.Error("Damn, here it is!", logger.Args("error", "something went wrong"))
+ logger.Info("But what's really cool is, that you can print very long logs, and PTerm will automatically wrap them for you! Say goodbye to text, that has weird line breaks!", logger.Args("very", "long"))
+ logger.Fatal("Oh no, this process is getting killed!", logger.Args("fatal", true))
+}
+
+```
+
+
+
### panel/demo
![Animation](https://raw.githubusercontent.com/pterm/pterm/master/_examples/panel/demo/animation.svg)
@@ -1398,6 +2275,9 @@ func main() {
p, _ := pterm.DefaultProgressbar.WithTotal(len(fakeInstallList)).WithTitle("Downloading stuff").Start()
for i := 0; i < p.Total; i++ {
+ if i == 6 {
+ time.Sleep(time.Second * 3) // Simulate a slow download.
+ }
p.UpdateTitle("Downloading " + fakeInstallList[i]) // Update the title of the progressbar.
pterm.Success.Println("Downloading " + fakeInstallList[i]) // If a progressbar is running, each print will be printed above the progressbar.
p.Increment() // Increment the progressbar by one. Use Add(x int) to increment by a custom amount.
@@ -1530,6 +2410,35 @@ func main() {
+### table/boxed
+
+![Animation](https://raw.githubusercontent.com/pterm/pterm/master/_examples/table/boxed/animation.svg)
+
+
+
+SHOW SOURCE
+
+```go
+package main
+
+import "github.com/pterm/pterm"
+
+func main() {
+ // Create a fork of the default table, fill it with data and print it.
+ // Data can also be generated and inserted later.
+ pterm.DefaultTable.WithHasHeader().WithBoxed().WithData(pterm.TableData{
+ {"Firstname", "Lastname", "Email", "Note"},
+ {"Paul", "Dean", "augue@velitAliquam.co.uk", ""},
+ {"Callie", "Mckay", "nunc.sed@est.com", "这是一个测试, haha!"},
+ {"Libby", "Camacho", "lobortis@semper.com", "just a test, hey!"},
+ {"张", "小宝", "zhang@example.com", ""},
+ }).Render()
+}
+
+```
+
+
+
### table/demo
![Animation](https://raw.githubusercontent.com/pterm/pterm/master/_examples/table/demo/animation.svg)
@@ -1548,21 +2457,79 @@ func main() {
// Data can also be generated and inserted later.
pterm.DefaultTable.WithHasHeader().WithData(pterm.TableData{
{"Firstname", "Lastname", "Email", "Note"},
- {"Paul", "Dean", "nisi.dictum.augue@velitAliquam.co.uk", ""},
- {"Callie", "Mckay", "egestas.nunc.sed@est.com", "这是一个测试, haha!"},
- {"Libby", "Camacho", "aliquet.lobortis@semper.com", "just a test, hey!"},
+ {"Paul", "Dean", "augue@velitAliquam.co.uk", ""},
+ {"Callie", "Mckay", "nunc.sed@est.com", "这是一个测试, haha!"},
+ {"Libby", "Camacho", "lobortis@semper.com", "just a test, hey!"},
+ {"张", "小宝", "zhang@example.com", ""},
}).Render()
pterm.Println() // Blank line
- // Create a table with right alignment.
+ // Create a table with multiple lines in a row.
pterm.DefaultTable.WithHasHeader().WithData(pterm.TableData{
{"Firstname", "Lastname", "Email"},
- {"Paul", "Dean", "nisi.dictum.augue@velitAliquam.co.uk"},
- {"Callie", "Mckay", "egestas.nunc.sed@est.com"},
- {"Libby", "Camacho", "aliquet.lobortis@semper.com"},
+ {"Paul\n\nNewline", "Dean", "augue@velitAliquam.co.uk"},
+ {"Callie", "Mckay", "nunc.sed@est.com\nNewline"},
+ {"Libby", "Camacho", "lobortis@semper.com"},
+ {"张", "小宝", "zhang@example.com"},
+ }).Render()
+}
+
+```
+
+
+
+### table/multiple-lines
+
+![Animation](https://raw.githubusercontent.com/pterm/pterm/master/_examples/table/multiple-lines/animation.svg)
+
+
+
+SHOW SOURCE
+
+```go
+package main
+
+import "github.com/pterm/pterm"
+
+func main() {
+ // Create a table with multiple lines in a row and set a row separator.
+ pterm.DefaultTable.WithHasHeader().WithRowSeparator("-").WithHeaderRowSeparator("-").WithData(pterm.TableData{
+ {"Firstname", "Lastname", "Email"},
+ {"Paul\n\nNewline", "Dean", "augue@velitAliquam.co.uk"},
+ {"Callie", "Mckay", "nunc.sed@est.com\nNewline"},
+ {"Libby", "Camacho", "lobortis@semper.com"},
{"张", "小宝", "zhang@example.com"},
- }).WithRightAlignment().Render()
+ }).Render()
+}
+
+```
+
+
+
+### table/right-alignment
+
+![Animation](https://raw.githubusercontent.com/pterm/pterm/master/_examples/table/right-alignment/animation.svg)
+
+
+
+SHOW SOURCE
+
+```go
+package main
+
+import "github.com/pterm/pterm"
+
+func main() {
+ // Create a fork of the default table, fill it with data and print it.
+ // Data can also be generated and inserted later.
+ pterm.DefaultTable.WithHasHeader().WithRightAlignment().WithData(pterm.TableData{
+ {"Firstname", "Lastname", "Email", "Note"},
+ {"Paul", "Dean", "augue@velitAliquam.co.uk", ""},
+ {"Callie", "Mckay", "nunc.sed@est.com", "这是一个测试, haha!"},
+ {"Libby", "Camacho", "lobortis@semper.com", "just a test, hey!"},
+ {"张", "小宝", "zhang@example.com", ""},
+ }).Render()
}
```
@@ -1624,6 +2591,41 @@ func main() {
```go
package main
+import (
+ "github.com/pterm/pterm"
+)
+
+func main() {
+ tree := pterm.TreeNode{
+ Text: "Top node",
+ Children: []pterm.TreeNode{{
+ Text: "Child node",
+ Children: []pterm.TreeNode{
+ {Text: "Grandchild node"},
+ {Text: "Grandchild node"},
+ {Text: "Grandchild node"},
+ },
+ }},
+ }
+
+ pterm.DefaultTree.WithRoot(tree).Render()
+}
+
+```
+
+
+
+### tree/from-leveled-list
+
+![Animation](https://raw.githubusercontent.com/pterm/pterm/master/_examples/tree/from-leveled-list/animation.svg)
+
+
+
+SHOW SOURCE
+
+```go
+package main
+
import (
"github.com/pterm/pterm"
"github.com/pterm/pterm/putils"
@@ -1658,6 +2660,7 @@ func main() {
// Generate tree from LeveledList.
root := putils.TreeFromLeveledList(leveledList)
+ root.Text = "Computer"
// Render TreePrinter
pterm.DefaultTree.WithRoot(root).Render()
diff --git a/vendor/github.com/pterm/pterm/SECURITY.md b/vendor/github.com/pterm/pterm/SECURITY.md
new file mode 100644
index 0000000000..27611496b0
--- /dev/null
+++ b/vendor/github.com/pterm/pterm/SECURITY.md
@@ -0,0 +1,25 @@
+# PTerm Security Policy
+This security policy applies to the PTerm GitHub repository and outlines the process for reporting security issues and handling security incidents. The primary goal of this policy is to ensure the safety and integrity of the PTerm codebase and to minimize the impact of security incidents on our users.
+
+## 1. Overview
+PTerm is a command-line interface (CLI) tool library, and we believe the security risks associated with it are minimal. However, we recognize that vulnerabilities can still arise, and we are committed to addressing them promptly and transparently.
+
+## 2. Reporting Security Issues
+If you discover a security issue in PTerm, please follow these steps:
+
+Open a new issue in the PTerm GitHub repository, describing the security problem in detail.
+Do not disclose any sensitive information or exploit details in the issue, as PTerm is not considered to have any exploitable features.
+
+## 3. Vulnerable Dependencies
+If a dependency of PTerm is found to be vulnerable or infected and requires immediate updates, please follow these steps:
+
+1. Open a new issue in the PTerm GitHub repository, describing the vulnerable dependency and the need for an update.
+2. *Optional: Contact @MarvinJWendt directly via Twitter or Discord to alert them to the issue.*
+
+## 4. Incident Response
+Upon receiving a security report, the PTerm team will:
+
+1. Acknowledge receipt of the report and review the issue.
+2. Investigate the issue and determine the severity and impact.
+3. Develop and implement a fix or mitigation plan, as necessary.
+4. Update the PTerm repository and notify users, if applicable.
diff --git a/vendor/github.com/pterm/pterm/area_printer.go b/vendor/github.com/pterm/pterm/area_printer.go
index 721341c369..5f3e5b0407 100644
--- a/vendor/github.com/pterm/pterm/area_printer.go
+++ b/vendor/github.com/pterm/pterm/area_printer.go
@@ -125,8 +125,8 @@ func (p *AreaPrinter) GenericStop() (*LivePrinter, error) {
return &lp, nil
}
-// Wrapper function that clears the content of the Area.
-// Moves the cursor to the bottom of the terminal, clears n lines upwards from
+// Clear is a Wrapper function that clears the content of the Area
+// moves the cursor to the bottom of the terminal, clears n lines upwards from
// the current position and moves the cursor again.
func (p *AreaPrinter) Clear() {
p.area.Clear()
diff --git a/vendor/github.com/pterm/pterm/atoms.go b/vendor/github.com/pterm/pterm/atoms.go
index d2410923e4..bc11da24e9 100644
--- a/vendor/github.com/pterm/pterm/atoms.go
+++ b/vendor/github.com/pterm/pterm/atoms.go
@@ -1,5 +1,11 @@
package pterm
+// Checkmark is used in the interactive multiselect printer.
+type Checkmark struct {
+ Checked string
+ Unchecked string
+}
+
// Bars is used to display multiple Bar.
type Bars []Bar
diff --git a/vendor/github.com/pterm/pterm/color.go b/vendor/github.com/pterm/pterm/color.go
index 3626ee4752..89ad025b95 100644
--- a/vendor/github.com/pterm/pterm/color.go
+++ b/vendor/github.com/pterm/pterm/color.go
@@ -246,6 +246,11 @@ func (c Color) String() string {
return fmt.Sprintf("%d", c)
}
+// ToStyle converts the color to a style.
+func (c Color) ToStyle() *Style {
+ return &Style{c}
+}
+
// Style is a collection of colors.
// Can include foreground, background and styling (eg. Bold, Underscore, etc.) colors.
type Style []Color
@@ -271,6 +276,23 @@ func (s Style) Add(styles ...Style) Style {
return ret
}
+// RemoveColor removes the given colors from the Style.
+func (s Style) RemoveColor(colors ...Color) Style {
+ ret := s
+
+ for _, c := range colors {
+ // remove via index
+ for i := 0; i < len(ret); i++ {
+ if ret[i] == c {
+ ret = append(ret[:i], ret[i+1:]...)
+ i--
+ }
+ }
+ }
+
+ return ret
+}
+
// Sprint formats using the default formats for its operands and returns the resulting string.
// Spaces are added between operands when neither is a string.
// Input will be colored with the parent Style.
diff --git a/vendor/github.com/pterm/pterm/interactive_confirm_printer.go b/vendor/github.com/pterm/pterm/interactive_confirm_printer.go
index dc37e597fb..6760ce69ed 100644
--- a/vendor/github.com/pterm/pterm/interactive_confirm_printer.go
+++ b/vendor/github.com/pterm/pterm/interactive_confirm_printer.go
@@ -2,12 +2,12 @@ package pterm
import (
"fmt"
- "os"
"strings"
"atomicgo.dev/cursor"
"atomicgo.dev/keyboard"
"atomicgo.dev/keyboard/keys"
+ "github.com/pterm/pterm/internal"
)
var (
@@ -89,9 +89,15 @@ func (p InteractiveConfirmPrinter) WithSuffixStyle(style *Style) *InteractiveCon
// Show shows the confirm prompt.
//
// Example:
-// result, _ := pterm.DefaultInteractiveConfirm.Show("Are you sure?")
-// pterm.Println(result)
+//
+// result, _ := pterm.DefaultInteractiveConfirm.Show("Are you sure?")
+// pterm.Println(result)
func (p InteractiveConfirmPrinter) Show(text ...string) (bool, error) {
+ // should be the first defer statement to make sure it is executed last
+ // and all the needed cleanup can be done before
+ cancel, exit := internal.NewCancelationSignal()
+ defer exit()
+
var result bool
if len(text) == 0 || text[0] == "" {
@@ -101,6 +107,7 @@ func (p InteractiveConfirmPrinter) Show(text ...string) (bool, error) {
p.TextStyle.Print(text[0] + " " + p.getSuffix() + ": ")
y, n := p.getShortHandles()
+ var interrupted bool
err := keyboard.Listen(func(keyInfo keys.Key) (stop bool, err error) {
key := keyInfo.Code
char := strings.ToLower(keyInfo.String())
@@ -132,12 +139,15 @@ func (p InteractiveConfirmPrinter) Show(text ...string) (bool, error) {
result = p.DefaultValue
return true, nil
case keys.CtrlC:
- os.Exit(1)
+ cancel()
+ interrupted = true
return true, nil
}
return false, nil
})
- cursor.StartOfLine()
+ if !interrupted {
+ cursor.StartOfLine()
+ }
return result, err
}
diff --git a/vendor/github.com/pterm/pterm/interactive_continue_printer.go b/vendor/github.com/pterm/pterm/interactive_continue_printer.go
index 8f8f67de1b..d0f91f13e9 100644
--- a/vendor/github.com/pterm/pterm/interactive_continue_printer.go
+++ b/vendor/github.com/pterm/pterm/interactive_continue_printer.go
@@ -111,8 +111,9 @@ func (p InteractiveContinuePrinter) WithSuffixStyle(style *Style) *InteractiveCo
// Show shows the continue prompt.
//
// Example:
-// result, _ := pterm.DefaultInteractiveContinue.Show("Do you want to apply the changes?")
-// pterm.Println(result)
+//
+// result, _ := pterm.DefaultInteractiveContinue.Show("Do you want to apply the changes?")
+// pterm.Println(result)
func (p InteractiveContinuePrinter) Show(text ...string) (string, error) {
var result string
diff --git a/vendor/github.com/pterm/pterm/interactive_multiselect_printer.go b/vendor/github.com/pterm/pterm/interactive_multiselect_printer.go
index dc6be6b2ba..c80f9355b8 100644
--- a/vendor/github.com/pterm/pterm/interactive_multiselect_printer.go
+++ b/vendor/github.com/pterm/pterm/interactive_multiselect_printer.go
@@ -8,6 +8,7 @@ import (
"atomicgo.dev/keyboard"
"atomicgo.dev/keyboard/keys"
"github.com/lithammer/fuzzysearch/fuzzy"
+
"github.com/pterm/pterm/internal"
)
@@ -25,6 +26,7 @@ var (
Filter: true,
KeySelect: keys.Enter,
KeyConfirm: keys.Tab,
+ Checkmark: &ThemeDefault.Checkmark,
}
)
@@ -39,6 +41,7 @@ type InteractiveMultiselectPrinter struct {
Selector string
SelectorStyle *Style
Filter bool
+ Checkmark *Checkmark
selectedOption int
selectedOptions []int
@@ -95,6 +98,12 @@ func (p InteractiveMultiselectPrinter) WithKeyConfirm(keyConfirm keys.KeyCode) *
return &p
}
+// WithCheckmark sets the checkmark
+func (p InteractiveMultiselectPrinter) WithCheckmark(checkmark *Checkmark) *InteractiveMultiselectPrinter {
+ p.Checkmark = checkmark
+ return &p
+}
+
// Show shows the interactive multiselect menu and returns the selected entry.
func (p *InteractiveMultiselectPrinter) Show(text ...string) ([]string, error) {
// should be the first defer statement to make sure it is executed last
@@ -270,7 +279,7 @@ func (p *InteractiveMultiselectPrinter) Show(text ...string) ([]string, error) {
return false, nil
})
if err != nil {
- fmt.Println(err)
+ Error.Println(err)
return nil, fmt.Errorf("failed to start keyboard listener: %w", err)
}
@@ -345,9 +354,9 @@ func (p *InteractiveMultiselectPrinter) renderSelectMenu() string {
}
var checkmark string
if p.isSelected(option) {
- checkmark = fmt.Sprintf("[%s]", Green("✓"))
+ checkmark = fmt.Sprintf("[%s]", p.Checkmark.Checked)
} else {
- checkmark = fmt.Sprintf("[%s]", Red("✗"))
+ checkmark = fmt.Sprintf("[%s]", p.Checkmark.Unchecked)
}
if i == p.selectedOption {
content += Sprintf("%s %s %s\n", p.renderSelector(), checkmark, option)
diff --git a/vendor/github.com/pterm/pterm/interactive_select_printer.go b/vendor/github.com/pterm/pterm/interactive_select_printer.go
index 13e25f7b2a..0af1a0d6ae 100644
--- a/vendor/github.com/pterm/pterm/interactive_select_printer.go
+++ b/vendor/github.com/pterm/pterm/interactive_select_printer.go
@@ -2,6 +2,7 @@ package pterm
import (
"fmt"
+ "math"
"sort"
"atomicgo.dev/cursor"
@@ -106,9 +107,9 @@ func (p *InteractiveSelectPrinter) Show(text ...string) (string, error) {
for i, option := range p.Options {
if option == p.DefaultOption {
p.selectedOption = i
- if i > 0 {
- p.displayedOptionsStart = i - 1
- p.displayedOptionsEnd = i - 1 + maxHeight
+ if i > 0 && len(p.Options) > maxHeight {
+ p.displayedOptionsEnd = int(math.Min(float64(i-1+maxHeight), float64(len(p.Options))))
+ p.displayedOptionsStart = p.displayedOptionsEnd - maxHeight
} else {
p.displayedOptionsStart = 0
p.displayedOptionsEnd = maxHeight
@@ -235,7 +236,7 @@ func (p *InteractiveSelectPrinter) Show(text ...string) (string, error) {
return false, nil
})
if err != nil {
- fmt.Println(err)
+ Error.Println(err)
return "", fmt.Errorf("failed to start keyboard listener: %w", err)
}
@@ -244,7 +245,7 @@ func (p *InteractiveSelectPrinter) Show(text ...string) (string, error) {
func (p *InteractiveSelectPrinter) renderSelectMenu() string {
var content string
- content += Sprintf("%s %s: %s\n", p.text, ThemeDefault.SecondaryStyle.Sprint("[type to search]"), p.fuzzySearchString)
+ content += Sprintf("%s %s: %s\n", p.text, p.SelectorStyle.Sprint("[type to search]"), p.fuzzySearchString)
// find options that match fuzzy search string
rankedResults := fuzzy.RankFindFold(p.fuzzySearchString, p.Options)
@@ -274,9 +275,9 @@ func (p *InteractiveSelectPrinter) renderSelectMenu() string {
continue
}
if i == p.selectedOption {
- content += Sprintf("%s %s\n", p.renderSelector(), option)
+ content += Sprintf("%s %s\n", p.renderSelector(), p.OptionStyle.Sprint(option))
} else {
- content += Sprintf(" %s\n", option)
+ content += Sprintf(" %s\n", p.OptionStyle.Sprint(option))
}
}
diff --git a/vendor/github.com/pterm/pterm/interactive_textinput_printer.go b/vendor/github.com/pterm/pterm/interactive_textinput_printer.go
index e71745552b..512e24f975 100644
--- a/vendor/github.com/pterm/pterm/interactive_textinput_printer.go
+++ b/vendor/github.com/pterm/pterm/interactive_textinput_printer.go
@@ -15,6 +15,7 @@ var (
DefaultInteractiveTextInput = InteractiveTextInputPrinter{
DefaultText: "Input text",
TextStyle: &ThemeDefault.PrimaryStyle,
+ Mask: "",
}
)
@@ -23,6 +24,7 @@ type InteractiveTextInputPrinter struct {
TextStyle *Style
DefaultText string
MultiLine bool
+ Mask string
input []string
cursorXPos int
@@ -48,6 +50,12 @@ func (p InteractiveTextInputPrinter) WithMultiLine(multiLine ...bool) *Interacti
return &p
}
+// WithMask sets the mask.
+func (p InteractiveTextInputPrinter) WithMask(mask string) *InteractiveTextInputPrinter {
+ p.Mask = mask
+ return &p
+}
+
// Show shows the interactive select menu and returns the selected entry.
func (p InteractiveTextInputPrinter) Show(text ...string) (string, error) {
// should be the first defer statement to make sure it is executed last
@@ -200,6 +208,7 @@ func (p InteractiveTextInputPrinter) updateArea(area *AreaPrinter) string {
p.cursorYPos = 0
}
areaText := p.text
+
for i, s := range p.input {
if i < len(p.input)-1 {
areaText += s + "\n"
@@ -207,6 +216,11 @@ func (p InteractiveTextInputPrinter) updateArea(area *AreaPrinter) string {
areaText += s
}
}
+
+ if p.Mask != "" {
+ areaText = p.text + strings.Repeat(p.Mask, internal.GetStringMaxWidth(areaText)-internal.GetStringMaxWidth(p.text))
+ }
+
if p.cursorXPos+internal.GetStringMaxWidth(p.input[p.cursorYPos]) < 1 {
p.cursorXPos = -internal.GetStringMaxWidth(p.input[p.cursorYPos])
}
diff --git a/vendor/github.com/pterm/pterm/internal/max_text_width.go b/vendor/github.com/pterm/pterm/internal/max_text_width.go
index fe39475094..1d4f0ea31d 100644
--- a/vendor/github.com/pterm/pterm/internal/max_text_width.go
+++ b/vendor/github.com/pterm/pterm/internal/max_text_width.go
@@ -1,10 +1,9 @@
package internal
import (
- "strings"
-
"github.com/gookit/color"
"github.com/mattn/go-runewidth"
+ "strings"
)
// GetStringMaxWidth returns the maximum width of a string with multiple lines.
@@ -12,8 +11,9 @@ func GetStringMaxWidth(s string) int {
var max int
ss := strings.Split(s, "\n")
for _, s2 := range ss {
- if runewidth.StringWidth(color.ClearCode(s2)) > max {
- max = runewidth.StringWidth(color.ClearCode(s2))
+ s2WithoutColor := color.ClearCode(s2)
+ if runewidth.StringWidth(s2WithoutColor) > max {
+ max = runewidth.StringWidth(s2WithoutColor)
}
}
return max
diff --git a/vendor/github.com/pterm/pterm/logger.go b/vendor/github.com/pterm/pterm/logger.go
new file mode 100644
index 0000000000..4fed928349
--- /dev/null
+++ b/vendor/github.com/pterm/pterm/logger.go
@@ -0,0 +1,428 @@
+package pterm
+
+import (
+ "encoding/json"
+ "io"
+ "os"
+ "path/filepath"
+ "runtime"
+ "strings"
+ "sync"
+ "time"
+
+ "github.com/pterm/pterm/internal"
+)
+
+type LogLevel int
+
+// Style returns the style of the log level.
+func (l LogLevel) Style() Style {
+ baseStyle := NewStyle(Bold)
+ switch l {
+ case LogLevelTrace:
+ return baseStyle.Add(*FgCyan.ToStyle())
+ case LogLevelDebug:
+ return baseStyle.Add(*FgBlue.ToStyle())
+ case LogLevelInfo:
+ return baseStyle.Add(*FgGreen.ToStyle())
+ case LogLevelWarn:
+ return baseStyle.Add(*FgYellow.ToStyle())
+ case LogLevelError:
+ return baseStyle.Add(*FgRed.ToStyle())
+ case LogLevelFatal:
+ return baseStyle.Add(*FgRed.ToStyle())
+ case LogLevelPrint:
+ return baseStyle.Add(*FgWhite.ToStyle())
+ }
+
+ return baseStyle.Add(*FgWhite.ToStyle())
+}
+
+func (l LogLevel) String() string {
+ switch l {
+ case LogLevelDisabled:
+ return ""
+ case LogLevelTrace:
+ return "TRACE"
+ case LogLevelDebug:
+ return "DEBUG"
+ case LogLevelInfo:
+ return "INFO"
+ case LogLevelWarn:
+ return "WARN"
+ case LogLevelError:
+ return "ERROR"
+ case LogLevelFatal:
+ return "FATAL"
+ case LogLevelPrint:
+ return "PRINT"
+ }
+ return "Unknown"
+}
+
+const (
+ // LogLevelDisabled does never print.
+ LogLevelDisabled LogLevel = iota
+ // LogLevelTrace is the log level for traces.
+ LogLevelTrace
+ // LogLevelDebug is the log level for debug.
+ LogLevelDebug
+ // LogLevelInfo is the log level for info.
+ LogLevelInfo
+ // LogLevelWarn is the log level for warnings.
+ LogLevelWarn
+ // LogLevelError is the log level for errors.
+ LogLevelError
+ // LogLevelFatal is the log level for fatal errors.
+ LogLevelFatal
+ // LogLevelPrint is the log level for printing.
+ LogLevelPrint
+)
+
+// LogFormatter is the log formatter.
+// Can be either LogFormatterColorful or LogFormatterJSON.
+type LogFormatter int
+
+const (
+ // LogFormatterColorful is a colorful log formatter.
+ LogFormatterColorful LogFormatter = iota
+ // LogFormatterJSON is a JSON log formatter.
+ LogFormatterJSON
+)
+
+// DefaultLogger is the default logger.
+var DefaultLogger = Logger{
+ Formatter: LogFormatterColorful,
+ Writer: os.Stdout,
+ Level: LogLevelInfo,
+ ShowTime: true,
+ TimeFormat: "2006-01-02 15:04:05",
+ MaxWidth: 80,
+ KeyStyles: map[string]Style{
+ "error": *NewStyle(FgRed, Bold),
+ "err": *NewStyle(FgRed, Bold),
+ "caller": *NewStyle(FgGray, Bold),
+ },
+}
+
+// loggerMutex syncs all loggers, so that they don't print at the exact same time.
+var loggerMutex sync.Mutex
+
+type Logger struct {
+ // Formatter is the log formatter of the logger.
+ Formatter LogFormatter
+ // Writer is the writer of the logger.
+ Writer io.Writer
+ // Level is the log level of the logger.
+ Level LogLevel
+ // ShowCaller defines if the logger should print the caller.
+ ShowCaller bool
+ // CallerOffset defines the offset of the caller.
+ CallerOffset int
+ // ShowTime defines if the logger should print a timestamp.
+ ShowTime bool
+ // TimestampLayout defines the layout of the timestamp.
+ TimeFormat string
+ // KeyStyles defines the styles for specific keys.
+ KeyStyles map[string]Style
+ // MaxWidth defines the maximum width of the logger.
+ // If the text (including the arguments) is longer than the max width, it will be split into multiple lines.
+ MaxWidth int
+}
+
+// WithFormatter sets the log formatter of the logger.
+func (l Logger) WithFormatter(formatter LogFormatter) *Logger {
+ l.Formatter = formatter
+ return &l
+}
+
+// WithWriter sets the writer of the logger.
+func (l Logger) WithWriter(writer io.Writer) *Logger {
+ l.Writer = writer
+ return &l
+}
+
+// WithLevel sets the log level of the logger.
+func (l Logger) WithLevel(level LogLevel) *Logger {
+ l.Level = level
+ return &l
+}
+
+// WithCaller enables or disables the caller.
+func (l Logger) WithCaller(b ...bool) *Logger {
+ l.ShowCaller = internal.WithBoolean(b)
+ return &l
+}
+
+// WithCallerOffset sets the caller offset.
+func (l Logger) WithCallerOffset(offset int) *Logger {
+ l.CallerOffset = offset
+ return &l
+}
+
+// WithTime enables or disables the timestamp.
+func (l Logger) WithTime(b ...bool) *Logger {
+ l.ShowTime = internal.WithBoolean(b)
+ return &l
+}
+
+// WithTimeFormat sets the timestamp layout.
+func (l Logger) WithTimeFormat(format string) *Logger {
+ l.TimeFormat = format
+ return &l
+}
+
+// WithKeyStyles sets the style for a specific key.
+func (l Logger) WithKeyStyles(styles map[string]Style) *Logger {
+ l.KeyStyles = styles
+ return &l
+}
+
+// WithMaxWidth sets the maximum width of the logger.
+func (l Logger) WithMaxWidth(width int) *Logger {
+ l.MaxWidth = width
+ return &l
+}
+
+// AppendKeyStyles appends a style for a specific key.
+func (l Logger) AppendKeyStyles(styles map[string]Style) *Logger {
+ for k, v := range styles {
+ l.KeyStyles[k] = v
+ }
+ return &l
+}
+
+// AppendKeyStyle appends a style for a specific key.
+func (l Logger) AppendKeyStyle(key string, style Style) *Logger {
+ l.KeyStyles[key] = style
+ return &l
+}
+
+// CanPrint checks if the logger can print a specific log level.
+func (l Logger) CanPrint(level LogLevel) bool {
+ return l.Level <= level
+}
+
+// Args converts any arguments to a slice of LoggerArgument.
+func (l Logger) Args(args ...any) []LoggerArgument {
+ var loggerArgs []LoggerArgument
+
+ // args are in the format of: key, value, key, value, key, value, ...
+ for i := 0; i < len(args); i += 2 {
+ key := Sprint(args[i])
+ value := args[i+1]
+
+ loggerArgs = append(loggerArgs, LoggerArgument{
+ Key: key,
+ Value: value,
+ })
+ }
+
+ return loggerArgs
+}
+
+// ArgsFromMap converts a map to a slice of LoggerArgument.
+func (l Logger) ArgsFromMap(m map[string]any) []LoggerArgument {
+ var loggerArgs []LoggerArgument
+
+ for k, v := range m {
+ loggerArgs = append(loggerArgs, LoggerArgument{
+ Key: k,
+ Value: v,
+ })
+ }
+
+ return loggerArgs
+}
+
+func (l Logger) getCallerInfo() (path string, line int) {
+ if !l.ShowCaller {
+ return
+ }
+
+ _, path, line, _ = runtime.Caller(l.CallerOffset + 4)
+ _, callerBase, _, _ := runtime.Caller(0)
+ basepath := filepath.Dir(callerBase)
+ basepath = strings.ReplaceAll(basepath, "\\", "/")
+
+ path = strings.TrimPrefix(path, basepath)
+
+ return
+}
+
+func (l Logger) combineArgs(args ...[]LoggerArgument) []LoggerArgument {
+ var result []LoggerArgument
+
+ for _, arg := range args {
+ result = append(result, arg...)
+ }
+
+ return result
+}
+
+func (l Logger) print(level LogLevel, msg string, args []LoggerArgument) {
+ if l.Level > level {
+ return
+ }
+
+ var line string
+
+ switch l.Formatter {
+ case LogFormatterColorful:
+ line = l.renderColorful(level, msg, args)
+ case LogFormatterJSON:
+ line = l.renderJSON(level, msg, args)
+ }
+
+ loggerMutex.Lock()
+ defer loggerMutex.Unlock()
+
+ _, _ = l.Writer.Write([]byte(line + "\n"))
+}
+
+func (l Logger) renderColorful(level LogLevel, msg string, args []LoggerArgument) (result string) {
+ if l.ShowTime {
+ result += Gray(time.Now().Format(l.TimeFormat)) + " "
+ }
+
+ if GetTerminalWidth() > 0 && GetTerminalWidth() < l.MaxWidth {
+ l.MaxWidth = GetTerminalWidth()
+ }
+
+ var argumentsInNewLine bool
+
+ result += level.Style().Sprintf("%-5s", level.String()) + " "
+
+ // if msg is too long, wrap it to multiple lines with the same length
+ remainingWidth := l.MaxWidth - internal.GetStringMaxWidth(result)
+ if internal.GetStringMaxWidth(msg) > remainingWidth {
+ argumentsInNewLine = true
+ msg = DefaultParagraph.WithMaxWidth(remainingWidth).Sprint(msg)
+ padding := len(time.Now().Format(l.TimeFormat) + " ")
+ msg = strings.ReplaceAll(msg, "\n", "\n"+strings.Repeat(" ", padding)+" │ ")
+ }
+
+ result += msg
+
+ if l.ShowCaller {
+ path, line := l.getCallerInfo()
+ args = append(args, LoggerArgument{
+ Key: "caller",
+ Value: FgGray.Sprintf("%s:%d", path, line),
+ })
+ }
+
+ arguments := make([]string, len(args))
+
+ // add arguments
+ if len(args) > 0 {
+ for i, arg := range args {
+ if style, ok := l.KeyStyles[arg.Key]; ok {
+ arguments[i] = style.Sprintf("%s: ", arg.Key)
+ } else {
+ arguments[i] = level.Style().Sprintf("%s: ", arg.Key)
+ }
+
+ arguments[i] += Sprintf("%s", Sprint(arg.Value))
+ }
+ }
+
+ fullLine := result + " " + strings.Join(arguments, " ")
+
+ // if the full line is too long, wrap the arguments to multiple lines
+ if internal.GetStringMaxWidth(fullLine) > l.MaxWidth {
+ argumentsInNewLine = true
+ }
+
+ if !argumentsInNewLine {
+ result = fullLine
+ } else {
+ padding := 4
+ if l.ShowTime {
+ padding = len(time.Time{}.Format(l.TimeFormat)) + 3
+ }
+
+ for i, argument := range arguments {
+ var pipe string
+ if i < len(arguments)-1 {
+ pipe = "├"
+ } else {
+ pipe = "└"
+ }
+ result += "\n" + strings.Repeat(" ", padding) + pipe + " " + argument
+ }
+ }
+
+ return
+}
+
+func (l Logger) renderJSON(level LogLevel, msg string, args []LoggerArgument) string {
+ m := l.argsToMap(args)
+
+ m["level"] = level.String()
+ m["timestamp"] = time.Now().Format(l.TimeFormat)
+ m["msg"] = msg
+
+ if file, line := l.getCallerInfo(); file != "" {
+ m["caller"] = Sprintf("%s:%d", file, line)
+ }
+
+ b, _ := json.Marshal(m)
+ return string(b)
+}
+
+func (l Logger) argsToMap(args []LoggerArgument) map[string]any {
+ m := make(map[string]any)
+
+ for _, arg := range args {
+ m[arg.Key] = arg.Value
+ }
+
+ return m
+}
+
+// Trace prints a trace log.
+func (l Logger) Trace(msg string, args ...[]LoggerArgument) {
+ l.print(LogLevelTrace, msg, l.combineArgs(args...))
+}
+
+// Debug prints a debug log.
+func (l Logger) Debug(msg string, args ...[]LoggerArgument) {
+ l.print(LogLevelDebug, msg, l.combineArgs(args...))
+}
+
+// Info prints an info log.
+func (l Logger) Info(msg string, args ...[]LoggerArgument) {
+ l.print(LogLevelInfo, msg, l.combineArgs(args...))
+}
+
+// Warn prints a warning log.
+func (l Logger) Warn(msg string, args ...[]LoggerArgument) {
+ l.print(LogLevelWarn, msg, l.combineArgs(args...))
+}
+
+// Error prints an error log.
+func (l Logger) Error(msg string, args ...[]LoggerArgument) {
+ l.print(LogLevelError, msg, l.combineArgs(args...))
+}
+
+// Fatal prints a fatal log and exits the program.
+func (l Logger) Fatal(msg string, args ...[]LoggerArgument) {
+ l.print(LogLevelFatal, msg, l.combineArgs(args...))
+ if l.CanPrint(LogLevelFatal) {
+ os.Exit(1)
+ }
+}
+
+// Print prints a log.
+func (l Logger) Print(msg string, args ...[]LoggerArgument) {
+ l.print(LogLevelPrint, msg, l.combineArgs(args...))
+}
+
+// LoggerArgument is a key-value pair for a logger.
+type LoggerArgument struct {
+ // Key is the key of the argument.
+ Key string
+ // Value is the value of the argument.
+ Value any
+}
diff --git a/vendor/github.com/pterm/pterm/prefix_printer.go b/vendor/github.com/pterm/pterm/prefix_printer.go
index ea0180ceb8..6823c94c44 100644
--- a/vendor/github.com/pterm/pterm/prefix_printer.go
+++ b/vendor/github.com/pterm/pterm/prefix_printer.go
@@ -199,9 +199,8 @@ func (p *PrefixPrinter) Sprint(a ...interface{}) string {
}
}
- _, fileName, line, _ := runtime.Caller(3 + p.LineNumberOffset)
-
if p.ShowLineNumber {
+ _, fileName, line, _ := runtime.Caller(3 + p.LineNumberOffset)
ret += FgGray.Sprint("\n└ " + fmt.Sprintf("(%s:%d)\n", fileName, line))
newLine = false
}
@@ -248,7 +247,9 @@ func (p *PrefixPrinter) Print(a ...interface{}) *TextPrinter {
if p.Debugger && !PrintDebugMessages {
return &tp
}
+ p.LineNumberOffset--
Fprint(p.Writer, p.Sprint(a...))
+ p.LineNumberOffset++
checkFatal(p)
return &tp
}
@@ -286,7 +287,9 @@ func (p *PrefixPrinter) Printfln(format string, a ...interface{}) *TextPrinter {
if p.Debugger && !PrintDebugMessages {
return &tp
}
+ p.LineNumberOffset++
Fprint(p.Writer, p.Sprintfln(format, a...))
+ p.LineNumberOffset--
checkFatal(p)
return &tp
}
diff --git a/vendor/github.com/pterm/pterm/print.go b/vendor/github.com/pterm/pterm/print.go
index 6a2612d81d..a58e5fa6a7 100644
--- a/vendor/github.com/pterm/pterm/print.go
+++ b/vendor/github.com/pterm/pterm/print.go
@@ -149,9 +149,10 @@ func Fprintln(writer io.Writer, a ...interface{}) {
// Printo overrides the current line in a terminal.
// If the current line is empty, the text will be printed like with pterm.Print.
// Example:
-// pterm.Printo("Hello, World")
-// time.Sleep(time.Second)
-// pterm.Printo("Hello, Earth!")
+//
+// pterm.Printo("Hello, World")
+// time.Sleep(time.Second)
+// pterm.Printo("Hello, Earth!")
func Printo(a ...interface{}) {
if !Output {
return
diff --git a/vendor/github.com/pterm/pterm/progressbar_printer.go b/vendor/github.com/pterm/pterm/progressbar_printer.go
index d4cee2fabd..282adcda32 100644
--- a/vendor/github.com/pterm/pterm/progressbar_printer.go
+++ b/vendor/github.com/pterm/pterm/progressbar_printer.go
@@ -1,8 +1,11 @@
package pterm
import (
+ "atomicgo.dev/cursor"
+ "atomicgo.dev/schedule"
+ "fmt"
"io"
- "strconv"
+ "math"
"strings"
"time"
@@ -28,7 +31,7 @@ var (
ShowCount: true,
ShowPercentage: true,
ShowElapsedTime: true,
- BarFiller: " ",
+ BarFiller: Gray("█"),
MaxWidth: 80,
}
)
@@ -55,7 +58,8 @@ type ProgressbarPrinter struct {
IsActive bool
- startedAt time.Time
+ startedAt time.Time
+ rerenderTask *schedule.Task
Writer io.Writer
}
@@ -173,6 +177,9 @@ func (p *ProgressbarPrinter) UpdateTitle(title string) *ProgressbarPrinter {
// This is the update logic, renders the progressbar
func (p *ProgressbarPrinter) updateProgress() *ProgressbarPrinter {
+ if !p.IsActive {
+ return p
+ }
if p.TitleStyle == nil {
p.TitleStyle = NewStyle()
}
@@ -195,25 +202,20 @@ func (p *ProgressbarPrinter) updateProgress() *ProgressbarPrinter {
width = p.MaxWidth
}
- currentPercentage := int(internal.PercentageRound(float64(int64(p.Total)), float64(int64(p.Current))))
-
- decoratorCount := Gray("[") + LightWhite(p.Current) + Gray("/") + LightWhite(p.Total) + Gray("]")
-
- decoratorCurrentPercentage := color.RGB(NewRGB(255, 0, 0).Fade(0, float32(p.Total), float32(p.Current), NewRGB(0, 255, 0)).GetValues()).
- Sprint(strconv.Itoa(currentPercentage) + "%")
-
- decoratorTitle := p.TitleStyle.Sprint(p.Title)
-
if p.ShowTitle {
- before += decoratorTitle + " "
+ before += p.TitleStyle.Sprint(p.Title) + " "
}
if p.ShowCount {
- before += decoratorCount + " "
+ padding := 1 + int(math.Log10(float64(p.Total)))
+ before += Gray("[") + LightWhite(fmt.Sprintf("%0*d", padding, p.Current)) + Gray("/") + LightWhite(p.Total) + Gray("]") + " "
}
after += " "
if p.ShowPercentage {
+ currentPercentage := int(internal.PercentageRound(float64(int64(p.Total)), float64(int64(p.Current))))
+ decoratorCurrentPercentage := color.RGB(NewRGB(255, 0, 0).Fade(0, float32(p.Total), float32(p.Current), NewRGB(0, 255, 0)).GetValues()).
+ Sprintf("%3d%%", currentPercentage)
after += decoratorCurrentPercentage + " "
}
if p.ShowElapsedTime {
@@ -228,11 +230,9 @@ func (p *ProgressbarPrinter) updateProgress() *ProgressbarPrinter {
barFiller = strings.Repeat(p.BarFiller, barMaxLength-barCurrentLength)
}
- var bar string
+ bar := barFiller
if barCurrentLength > 0 {
- bar = p.BarStyle.Sprint(strings.Repeat(p.BarCharacter, barCurrentLength)+p.LastCharacter) + barFiller
- } else {
- bar = ""
+ bar = p.BarStyle.Sprint(strings.Repeat(p.BarCharacter, barCurrentLength)+p.LastCharacter) + bar
}
if !RawOutput {
@@ -250,28 +250,43 @@ func (p *ProgressbarPrinter) Add(count int) *ProgressbarPrinter {
p.Current += count
p.updateProgress()
- if p.Current == p.Total {
+ if p.Current >= p.Total {
p.Stop()
}
return p
}
// Start the ProgressbarPrinter.
-func (p ProgressbarPrinter) Start() (*ProgressbarPrinter, error) {
+func (p ProgressbarPrinter) Start(title ...interface{}) (*ProgressbarPrinter, error) {
+ cursor.Hide()
if RawOutput && p.ShowTitle {
Fprintln(p.Writer, p.Title)
}
p.IsActive = true
+ if len(title) != 0 {
+ p.Title = Sprint(title...)
+ }
ActiveProgressBarPrinters = append(ActiveProgressBarPrinters, &p)
p.startedAt = time.Now()
p.updateProgress()
+ if p.ShowElapsedTime {
+ p.rerenderTask = schedule.Every(time.Second, func() {
+ p.updateProgress()
+ })
+ }
+
return &p, nil
}
// Stop the ProgressbarPrinter.
func (p *ProgressbarPrinter) Stop() (*ProgressbarPrinter, error) {
+ if p.rerenderTask != nil && p.rerenderTask.IsActive() {
+ p.rerenderTask.Stop()
+ }
+ cursor.Show()
+
if !p.IsActive {
return p, nil
}
diff --git a/vendor/github.com/pterm/pterm/pterm.go b/vendor/github.com/pterm/pterm/pterm.go
index be3d9a5297..ac05f0fd60 100644
--- a/vendor/github.com/pterm/pterm/pterm.go
+++ b/vendor/github.com/pterm/pterm/pterm.go
@@ -6,7 +6,13 @@
// View the animated examples here: https://github.com/pterm/pterm#-examples
package pterm
-import "github.com/gookit/color"
+import (
+ "atomicgo.dev/cursor"
+ "github.com/gookit/color"
+ "os"
+ "os/signal"
+ "syscall"
+)
var (
// Output completely disables output from pterm if set to false. Can be used in CLI application quiet mode.
@@ -24,6 +30,16 @@ var (
func init() {
color.ForceColor()
+
+ // Make the cursor visible when the program stops
+ c := make(chan os.Signal, 1)
+ signal.Notify(c, os.Interrupt)
+ signal.Notify(c, syscall.SIGTERM)
+ go func() {
+ for range c {
+ cursor.Show()
+ }
+ }()
}
// EnableOutput enables the output of PTerm.
diff --git a/vendor/github.com/pterm/pterm/putils/tabledata_from_csv.go b/vendor/github.com/pterm/pterm/putils/tabledata_from_csv.go
index 46667b17b8..84b4d5070f 100644
--- a/vendor/github.com/pterm/pterm/putils/tabledata_from_csv.go
+++ b/vendor/github.com/pterm/pterm/putils/tabledata_from_csv.go
@@ -7,6 +7,7 @@ import (
// TableDataFromCSV converts CSV data into pterm.TableData.
//
// Usage:
+//
// pterm.DefaultTable.WithData(putils.TableDataFromCSV(csv)).Render()
func TableDataFromCSV(csv string) (td pterm.TableData) {
return TableDataFromSeparatedValues(csv, ",", "\n")
diff --git a/vendor/github.com/pterm/pterm/putils/tabledata_from_separated_values.go b/vendor/github.com/pterm/pterm/putils/tabledata_from_separated_values.go
index 632ca3079a..3919d169fe 100644
--- a/vendor/github.com/pterm/pterm/putils/tabledata_from_separated_values.go
+++ b/vendor/github.com/pterm/pterm/putils/tabledata_from_separated_values.go
@@ -9,6 +9,7 @@ import (
// TableDataFromSeparatedValues converts values, separated by separator, into pterm.TableData.
//
// Usage:
+//
// pterm.DefaultTable.WithData(putils.TableDataFromCSV(csv)).Render()
func TableDataFromSeparatedValues(text, valueSeparator, rowSeparator string) (td pterm.TableData) {
for _, line := range strings.Split(text, rowSeparator) {
diff --git a/vendor/github.com/pterm/pterm/putils/tabledata_from_tsv.go b/vendor/github.com/pterm/pterm/putils/tabledata_from_tsv.go
index 41d629120d..7c6a3db660 100644
--- a/vendor/github.com/pterm/pterm/putils/tabledata_from_tsv.go
+++ b/vendor/github.com/pterm/pterm/putils/tabledata_from_tsv.go
@@ -7,6 +7,7 @@ import (
// TableDataFromTSV converts TSV data into pterm.TableData.
//
// Usage:
+//
// pterm.DefaultTable.WithData(putils.TableDataFromTSV(tsv)).Render()
func TableDataFromTSV(csv string) (td pterm.TableData) {
return TableDataFromSeparatedValues(csv, "\t", "\n")
diff --git a/vendor/github.com/pterm/pterm/putils/tree_from_leveled_list.go b/vendor/github.com/pterm/pterm/putils/tree_from_leveled_list.go
index 59b684c126..6502b6c314 100644
--- a/vendor/github.com/pterm/pterm/putils/tree_from_leveled_list.go
+++ b/vendor/github.com/pterm/pterm/putils/tree_from_leveled_list.go
@@ -10,7 +10,6 @@ func TreeFromLeveledList(leveledListItems pterm.LeveledList) pterm.TreeNode {
root := &pterm.TreeNode{
Children: []pterm.TreeNode{},
- Text: leveledListItems[0].Text,
}
for i, record := range leveledListItems {
diff --git a/vendor/github.com/pterm/pterm/rgb.go b/vendor/github.com/pterm/pterm/rgb.go
index fee602a5b6..a35dd1a070 100644
--- a/vendor/github.com/pterm/pterm/rgb.go
+++ b/vendor/github.com/pterm/pterm/rgb.go
@@ -12,9 +12,137 @@ import (
// The name of the model comes from the initials of the three additive primary colors, red, green, and blue.
// https://en.wikipedia.org/wiki/RGB_color_model
type RGB struct {
- R uint8
- G uint8
- B uint8
+ R uint8
+ G uint8
+ B uint8
+ Background bool
+}
+
+type RGBStyle struct {
+ Options []Color
+ Foreground, Background RGB
+
+ hasBg bool
+}
+
+// NewRGBStyle returns a new RGBStyle.
+// The foreground color is required, the background color is optional.
+// The colors will be set as is, ignoring the RGB.Background property.
+func NewRGBStyle(foreground RGB, background ...RGB) RGBStyle {
+ var s RGBStyle
+ s.Foreground = foreground
+ if len(background) > 0 {
+ s.Background = background[0]
+ s.hasBg = true
+ }
+ return s
+}
+
+// AddOptions adds options to the RGBStyle.
+func (p RGBStyle) AddOptions(opts ...Color) RGBStyle {
+ p.Options = append(p.Options, opts...)
+ return p
+}
+
+// Print formats using the default formats for its operands and writes to standard output.
+// Spaces are added between operands when neither is a string.
+// It returns the number of bytes written and any write error encountered.
+func (p RGBStyle) Print(a ...interface{}) *TextPrinter {
+ Print(p.Sprint(a...))
+ tp := TextPrinter(p)
+ return &tp
+}
+
+// Println formats using the default formats for its operands and writes to standard output.
+// Spaces are always added between operands and a newline is appended.
+// It returns the number of bytes written and any write error encountered.
+func (p RGBStyle) Println(a ...interface{}) *TextPrinter {
+ Println(p.Sprint(a...))
+ tp := TextPrinter(p)
+ return &tp
+}
+
+// Printf formats according to a format specifier and writes to standard output.
+// It returns the number of bytes written and any write error encountered.
+func (p RGBStyle) Printf(format string, a ...interface{}) *TextPrinter {
+ Printf(format, p.Sprint(a...))
+ tp := TextPrinter(p)
+ return &tp
+}
+
+// Printfln formats according to a format specifier and writes to standard output.
+// Spaces are always added between operands and a newline is appended.
+// It returns the number of bytes written and any write error encountered.
+func (p RGBStyle) Printfln(format string, a ...interface{}) *TextPrinter {
+ Printf(format, p.Sprint(a...))
+ tp := TextPrinter(p)
+ return &tp
+}
+
+// PrintOnError prints every error which is not nil.
+// If every error is nil, nothing will be printed.
+// This can be used for simple error checking.
+func (p RGBStyle) PrintOnError(a ...interface{}) *TextPrinter {
+ for _, arg := range a {
+ if err, ok := arg.(error); ok {
+ if err != nil {
+ p.Println(err)
+ }
+ }
+ }
+
+ tp := TextPrinter(p)
+ return &tp
+}
+
+// PrintOnErrorf wraps every error which is not nil and prints it.
+// If every error is nil, nothing will be printed.
+// This can be used for simple error checking.
+func (p RGBStyle) PrintOnErrorf(format string, a ...interface{}) *TextPrinter {
+ for _, arg := range a {
+ if err, ok := arg.(error); ok {
+ if err != nil {
+ p.Println(fmt.Errorf(format, err))
+ }
+ }
+ }
+
+ tp := TextPrinter(p)
+ return &tp
+}
+
+// Sprint formats using the default formats for its operands and returns the resulting string.
+// Spaces are added between operands when neither is a string.
+func (p RGBStyle) Sprint(a ...interface{}) string {
+ var rgbStyle *color.RGBStyle
+ if !p.hasBg {
+ rgbStyle = color.NewRGBStyle(color.RGB(p.Foreground.R, p.Foreground.G, p.Foreground.B))
+ } else {
+ rgbStyle = color.NewRGBStyle(color.RGB(p.Foreground.R, p.Foreground.G, p.Foreground.B), color.RGB(p.Background.R, p.Background.G, p.Background.B))
+ }
+ if len(p.Options) > 0 {
+ for _, opt := range p.Options {
+ rgbStyle.AddOpts(color.Color(opt))
+ }
+ }
+ return rgbStyle.Sprint(a...)
+}
+
+// Sprintln formats using the default formats for its operands and returns the resulting string.
+// Spaces are always added between operands and a newline is appended.
+func (p RGBStyle) Sprintln(a ...interface{}) string {
+ return p.Sprint(a...) + "\n"
+}
+
+// Sprintf formats according to a format specifier and returns the resulting string.
+func (p RGBStyle) Sprintf(format string, a ...interface{}) string {
+ return fmt.Sprintf(format, p.Sprint(a...))
+}
+
+// Sprintfln formats according to a format specifier and returns the resulting string.
+// Spaces are always added between operands and a newline is appended.
+func (p RGBStyle) Sprintfln(format string, a ...interface{}) string {
+ return fmt.Sprintf(format, p.Sprint(a...)) + "\n"
}
// GetValues returns the RGB values separately.
@@ -23,12 +151,21 @@ func (p RGB) GetValues() (r, g, b uint8) {
}
// NewRGB returns a new RGB.
-func NewRGB(r, g, b uint8) RGB {
- return RGB{R: r, G: g, B: b}
+func NewRGB(r, g, b uint8, background ...bool) RGB {
+ var bg bool
+
+ if len(background) > 0 {
+ bg = background[0]
+ }
+
+ return RGB{R: r, G: g, B: b, Background: bg}
}
// Fade fades one RGB value (over other RGB values) to another RGB value, by giving the function a minimum, maximum and current value.
func (p RGB) Fade(min, max, current float32, end ...RGB) RGB {
+ if max == current {
+ return end[len(end)-1]
+ }
if min < 0 {
max -= min
current -= min
@@ -36,9 +173,10 @@ func (p RGB) Fade(min, max, current float32, end ...RGB) RGB {
}
if len(end) == 1 {
return RGB{
- R: uint8(internal.MapRangeToRange(min, max, float32(p.R), float32(end[0].R), current)),
- G: uint8(internal.MapRangeToRange(min, max, float32(p.G), float32(end[0].G), current)),
- B: uint8(internal.MapRangeToRange(min, max, float32(p.B), float32(end[0].B), current)),
+ R: uint8(internal.MapRangeToRange(min, max, float32(p.R), float32(end[0].R), current)),
+ G: uint8(internal.MapRangeToRange(min, max, float32(p.G), float32(end[0].G), current)),
+ B: uint8(internal.MapRangeToRange(min, max, float32(p.B), float32(end[0].B), current)),
+ Background: p.Background,
}
} else if len(end) > 1 {
f := (max - min) / float32(len(end))
@@ -60,7 +198,10 @@ func (p RGB) Fade(min, max, current float32, end ...RGB) RGB {
// Sprint formats using the default formats for its operands and returns the resulting string.
// Spaces are added between operands when neither is a string.
func (p RGB) Sprint(a ...interface{}) string {
- return color.RGB(p.R, p.G, p.B).Sprint(a...)
+ if p.Background {
+ return color.RGB(p.R, p.G, p.B, p.Background).Sprint(a...) + "\033[0m\033[K"
+ }
+ return color.RGB(p.R, p.G, p.B, p.Background).Sprint(a...)
}
// Sprintln formats using the default formats for its operands and returns the resulting string.
@@ -146,3 +287,11 @@ func (p RGB) PrintOnErrorf(format string, a ...interface{}) *TextPrinter {
tp := TextPrinter(p)
return &tp
}
+
+func (p RGB) ToRGBStyle() RGBStyle {
+ if p.Background {
+ return RGBStyle{Background: p}
+ }
+
+ return RGBStyle{Foreground: p}
+}
diff --git a/vendor/github.com/pterm/pterm/spinner_printer.go b/vendor/github.com/pterm/pterm/spinner_printer.go
index 86d9c6559b..82b84af7fb 100644
--- a/vendor/github.com/pterm/pterm/spinner_printer.go
+++ b/vendor/github.com/pterm/pterm/spinner_printer.go
@@ -140,7 +140,11 @@ func (s SpinnerPrinter) Start(text ...interface{}) (*SpinnerPrinter, error) {
go func() {
for s.IsActive {
for _, seq := range s.Sequence {
- if !s.IsActive || RawOutput {
+ if !s.IsActive {
+ continue
+ }
+ if RawOutput {
+ time.Sleep(s.Delay)
continue
}
diff --git a/vendor/github.com/pterm/pterm/table_printer.go b/vendor/github.com/pterm/pterm/table_printer.go
index 785fbf26cf..54e8965a42 100644
--- a/vendor/github.com/pterm/pterm/table_printer.go
+++ b/vendor/github.com/pterm/pterm/table_printer.go
@@ -5,7 +5,6 @@ import (
"io"
"strings"
- "github.com/mattn/go-runewidth"
"github.com/pterm/pterm/internal"
)
@@ -140,6 +139,23 @@ func (p TablePrinter) WithWriter(writer io.Writer) *TablePrinter {
return &p
}
+type table struct {
+ rows []row
+ maxColumnWidths []int
+}
+
+type row struct {
+ height int
+ cells []cell
+ columnWidths []int
+}
+
+type cell struct {
+ width int
+ height int
+ lines []string
+}
+
// Srender renders the TablePrinter as a string.
func (p TablePrinter) Srender() (string, error) {
if p.Style == nil {
@@ -158,70 +174,109 @@ func (p TablePrinter) Srender() (string, error) {
p.RowSeparatorStyle = NewStyle()
}
- var ret string
- maxColumnWidth := make(map[int]int)
+ var t table
+
+ // convert data to table and calculate values
+ for _, rRaw := range p.Data {
+ var r row
+ for _, cRaw := range rRaw {
+ var c cell
+ c.lines = strings.Split(cRaw, "\n")
+ c.height = len(c.lines)
+ for _, l := range c.lines {
+ if maxWidth := internal.GetStringMaxWidth(l); maxWidth > c.width {
+ c.width = maxWidth
+ }
+ }
+ r.cells = append(r.cells, c)
+ if c.height > r.height {
+ r.height = c.height
+ }
+ }
- for _, row := range p.Data {
- for ci, column := range row {
- columnLength := runewidth.StringWidth(RemoveColorFromString(column))
- if columnLength > maxColumnWidth[ci] {
- maxColumnWidth[ci] = columnLength
+ // set max column widths of table
+ for i, c := range r.cells {
+ if len(t.maxColumnWidths) <= i {
+ t.maxColumnWidths = append(t.maxColumnWidths, c.width)
+ } else if c.width > t.maxColumnWidths[i] {
+ t.maxColumnWidths[i] = c.width
}
}
+
+ t.rows = append(t.rows, r)
}
- for ri, row := range p.Data {
- rowWidth := 0
- for ci, column := range row {
- columnString := p.createColumnString(column, maxColumnWidth[ci])
- rowWidth += runewidth.StringWidth(RemoveColorFromString(columnString))
+ var maxRowWidth int
+ for _, r := range t.rows {
+ rowWidth := internal.GetStringMaxWidth(p.renderRow(t, r))
+ if rowWidth > maxRowWidth {
+ maxRowWidth = rowWidth
+ }
+ }
- if ci != len(row) && ci != 0 {
- ret += p.Style.Sprint(p.SeparatorStyle.Sprint(p.Separator))
- rowWidth += runewidth.StringWidth(RemoveColorFromString(p.SeparatorStyle.Sprint(p.Separator)))
- }
+ // render table
+ var s string
+
+ for i, r := range t.rows {
+ if i == 0 && p.HasHeader {
+ s += p.HeaderStyle.Sprint(p.renderRow(t, r))
- if p.HasHeader && ri == 0 {
- ret += p.Style.Sprint(p.HeaderStyle.Sprint(columnString))
- } else {
- ret += p.Style.Sprint(columnString)
+ if p.HeaderRowSeparator != "" {
+ s += strings.Repeat(p.HeaderRowSeparatorStyle.Sprint(p.HeaderRowSeparator), maxRowWidth) + "\n"
}
+ continue
}
- if p.HasHeader && ri == 0 && p.HeaderRowSeparator != "" {
- ret += p.createHeaderRowSeparatorString(rowWidth)
- }
+ s += p.renderRow(t, r)
- if ri != len(p.Data)-1 && ri != 0 && p.RowSeparator != "" {
- ret += p.createRowSeparatorString(rowWidth)
+ if p.RowSeparator != "" {
+ s += strings.Repeat(p.RowSeparatorStyle.Sprint(p.RowSeparator), maxRowWidth) + "\n"
}
-
- ret += "\n"
}
- ret = strings.TrimSuffix(ret, "\n")
-
if p.Boxed {
- ret = DefaultBox.Sprint(ret)
+ s = DefaultBox.Sprint(strings.TrimSuffix(s, "\n"))
}
- return ret, nil
+ return s, nil
}
-func (p TablePrinter) createColumnString(data string, maxColumnWidth int) string {
- columnLength := runewidth.StringWidth(RemoveColorFromString(data))
- if p.RightAlignment {
- return strings.Repeat(" ", maxColumnWidth-columnLength) + data
- }
- return data + strings.Repeat(" ", maxColumnWidth-columnLength)
-}
+// renderRow renders a row.
+// It merges the cells of a row into one string.
+// Each line of each cell is merged with the same line of the other cells.
+func (p TablePrinter) renderRow(t table, r row) string {
+ var s string
+
+ // merge lines of cells and add separator
+ // use the t.maxColumnWidths to add padding to the corresponding cell
+ // a newline in a cell should be in the same column as the original cell
+ for i := 0; i < r.height; i++ {
+ for j, c := range r.cells {
+ var currentLine string
+ if i < len(c.lines) {
+ currentLine = c.lines[i]
+ }
+ paddingForLine := t.maxColumnWidths[j] - internal.GetStringMaxWidth(currentLine)
-func (p TablePrinter) createHeaderRowSeparatorString(rowWidth int) string {
- return "\n" + p.Style.Sprint(p.HeaderRowSeparatorStyle.Sprint(strings.Repeat(p.HeaderRowSeparator, rowWidth)))
-}
+ if p.RightAlignment {
+ s += strings.Repeat(" ", paddingForLine)
+ }
+
+ if i < len(c.lines) {
+ s += c.lines[i]
+ }
+
+ if j < len(r.cells)-1 {
+ if p.LeftAlignment {
+ s += strings.Repeat(" ", paddingForLine)
+ }
+ s += p.SeparatorStyle.Sprint(p.Separator)
+ }
+ }
+ s += "\n"
+ }
-func (p TablePrinter) createRowSeparatorString(rowWidth int) string {
- return "\n" + p.Style.Sprint(p.RowSeparatorStyle.Sprint(strings.Repeat(p.RowSeparator, rowWidth)))
+ return s
}
// Render prints the TablePrinter to the terminal.
diff --git a/vendor/github.com/pterm/pterm/theme.go b/vendor/github.com/pterm/pterm/theme.go
index b02ac3e28e..22466ea719 100644
--- a/vendor/github.com/pterm/pterm/theme.go
+++ b/vendor/github.com/pterm/pterm/theme.go
@@ -43,6 +43,10 @@ var (
BarLabelStyle: Style{FgLightCyan},
BarStyle: Style{FgCyan},
TimerStyle: Style{FgGray},
+ Checkmark: Checkmark{
+ Checked: Green("✓"),
+ Unchecked: Red("✗"),
+ },
}
)
@@ -89,6 +93,7 @@ type Theme struct {
BoxTextStyle Style
BarLabelStyle Style
BarStyle Style
+ Checkmark Checkmark
}
// WithPrimaryStyle returns a new theme with overridden value.
diff --git a/vendor/github.com/pterm/pterm/tree_printer.go b/vendor/github.com/pterm/pterm/tree_printer.go
index f462fff48a..113f920255 100644
--- a/vendor/github.com/pterm/pterm/tree_printer.go
+++ b/vendor/github.com/pterm/pterm/tree_printer.go
@@ -122,7 +122,12 @@ func (p TreePrinter) Srender() (string, error) {
p.TextStyle = NewStyle()
}
- return walkOverTree(p.Root.Children, p, ""), nil
+ var result string
+ if p.Root.Text != "" {
+ result += p.TextStyle.Sprint(p.Root.Text) + "\n"
+ }
+ result += walkOverTree(p.Root.Children, p, "")
+ return result, nil
}
// walkOverTree is a recursive function,
diff --git a/vendor/github.com/quic-go/qtls-go1-18/LICENSE b/vendor/github.com/quic-go/qtls-go1-18/LICENSE
new file mode 100644
index 0000000000..6a66aea5ea
--- /dev/null
+++ b/vendor/github.com/quic-go/qtls-go1-18/LICENSE
@@ -0,0 +1,27 @@
+Copyright (c) 2009 The Go Authors. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with the
+distribution.
+ * Neither the name of Google Inc. nor the names of its
+contributors may be used to endorse or promote products derived from
+this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/vendor/github.com/quic-go/qtls-go1-18/README.md b/vendor/github.com/quic-go/qtls-go1-18/README.md
new file mode 100644
index 0000000000..c592c4ca13
--- /dev/null
+++ b/vendor/github.com/quic-go/qtls-go1-18/README.md
@@ -0,0 +1,6 @@
+# qtls
+
+[![Go Reference](https://pkg.go.dev/badge/github.com/quic-go/qtls-go1-18.svg)](https://pkg.go.dev/github.com/quic-go/qtls-go1-18)
+[![.github/workflows/go-test.yml](https://github.com/quic-go/qtls-go1-18/actions/workflows/go-test.yml/badge.svg)](https://github.com/quic-go/qtls-go1-18/actions/workflows/go-test.yml)
+
+This repository contains a modified version of the standard library's TLS implementation, modified for the QUIC protocol. It is used by [quic-go](https://github.com/lucas-clemente/quic-go).
diff --git a/vendor/github.com/quic-go/qtls-go1-18/alert.go b/vendor/github.com/quic-go/qtls-go1-18/alert.go
new file mode 100644
index 0000000000..3feac79be8
--- /dev/null
+++ b/vendor/github.com/quic-go/qtls-go1-18/alert.go
@@ -0,0 +1,102 @@
+// Copyright 2009 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package qtls
+
+import "strconv"
+
+type alert uint8
+
+// Alert is a TLS alert
+type Alert = alert
+
+const (
+ // alert level
+ alertLevelWarning = 1
+ alertLevelError = 2
+)
+
+const (
+ alertCloseNotify alert = 0
+ alertUnexpectedMessage alert = 10
+ alertBadRecordMAC alert = 20
+ alertDecryptionFailed alert = 21
+ alertRecordOverflow alert = 22
+ alertDecompressionFailure alert = 30
+ alertHandshakeFailure alert = 40
+ alertBadCertificate alert = 42
+ alertUnsupportedCertificate alert = 43
+ alertCertificateRevoked alert = 44
+ alertCertificateExpired alert = 45
+ alertCertificateUnknown alert = 46
+ alertIllegalParameter alert = 47
+ alertUnknownCA alert = 48
+ alertAccessDenied alert = 49
+ alertDecodeError alert = 50
+ alertDecryptError alert = 51
+ alertExportRestriction alert = 60
+ alertProtocolVersion alert = 70
+ alertInsufficientSecurity alert = 71
+ alertInternalError alert = 80
+ alertInappropriateFallback alert = 86
+ alertUserCanceled alert = 90
+ alertNoRenegotiation alert = 100
+ alertMissingExtension alert = 109
+ alertUnsupportedExtension alert = 110
+ alertCertificateUnobtainable alert = 111
+ alertUnrecognizedName alert = 112
+ alertBadCertificateStatusResponse alert = 113
+ alertBadCertificateHashValue alert = 114
+ alertUnknownPSKIdentity alert = 115
+ alertCertificateRequired alert = 116
+ alertNoApplicationProtocol alert = 120
+)
+
+var alertText = map[alert]string{
+ alertCloseNotify: "close notify",
+ alertUnexpectedMessage: "unexpected message",
+ alertBadRecordMAC: "bad record MAC",
+ alertDecryptionFailed: "decryption failed",
+ alertRecordOverflow: "record overflow",
+ alertDecompressionFailure: "decompression failure",
+ alertHandshakeFailure: "handshake failure",
+ alertBadCertificate: "bad certificate",
+ alertUnsupportedCertificate: "unsupported certificate",
+ alertCertificateRevoked: "revoked certificate",
+ alertCertificateExpired: "expired certificate",
+ alertCertificateUnknown: "unknown certificate",
+ alertIllegalParameter: "illegal parameter",
+ alertUnknownCA: "unknown certificate authority",
+ alertAccessDenied: "access denied",
+ alertDecodeError: "error decoding message",
+ alertDecryptError: "error decrypting message",
+ alertExportRestriction: "export restriction",
+ alertProtocolVersion: "protocol version not supported",
+ alertInsufficientSecurity: "insufficient security level",
+ alertInternalError: "internal error",
+ alertInappropriateFallback: "inappropriate fallback",
+ alertUserCanceled: "user canceled",
+ alertNoRenegotiation: "no renegotiation",
+ alertMissingExtension: "missing extension",
+ alertUnsupportedExtension: "unsupported extension",
+ alertCertificateUnobtainable: "certificate unobtainable",
+ alertUnrecognizedName: "unrecognized name",
+ alertBadCertificateStatusResponse: "bad certificate status response",
+ alertBadCertificateHashValue: "bad certificate hash value",
+ alertUnknownPSKIdentity: "unknown PSK identity",
+ alertCertificateRequired: "certificate required",
+ alertNoApplicationProtocol: "no application protocol",
+}
+
+func (e alert) String() string {
+ s, ok := alertText[e]
+ if ok {
+ return "tls: " + s
+ }
+ return "tls: alert(" + strconv.Itoa(int(e)) + ")"
+}
+
+func (e alert) Error() string {
+ return e.String()
+}
diff --git a/vendor/github.com/quic-go/qtls-go1-18/auth.go b/vendor/github.com/quic-go/qtls-go1-18/auth.go
new file mode 100644
index 0000000000..1ef675fd37
--- /dev/null
+++ b/vendor/github.com/quic-go/qtls-go1-18/auth.go
@@ -0,0 +1,289 @@
+// Copyright 2017 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package qtls
+
+import (
+ "bytes"
+ "crypto"
+ "crypto/ecdsa"
+ "crypto/ed25519"
+ "crypto/elliptic"
+ "crypto/rsa"
+ "errors"
+ "fmt"
+ "hash"
+ "io"
+)
+
+// verifyHandshakeSignature verifies a signature against pre-hashed
+// (if required) handshake contents.
+func verifyHandshakeSignature(sigType uint8, pubkey crypto.PublicKey, hashFunc crypto.Hash, signed, sig []byte) error {
+ switch sigType {
+ case signatureECDSA:
+ pubKey, ok := pubkey.(*ecdsa.PublicKey)
+ if !ok {
+ return fmt.Errorf("expected an ECDSA public key, got %T", pubkey)
+ }
+ if !ecdsa.VerifyASN1(pubKey, signed, sig) {
+ return errors.New("ECDSA verification failure")
+ }
+ case signatureEd25519:
+ pubKey, ok := pubkey.(ed25519.PublicKey)
+ if !ok {
+ return fmt.Errorf("expected an Ed25519 public key, got %T", pubkey)
+ }
+ if !ed25519.Verify(pubKey, signed, sig) {
+ return errors.New("Ed25519 verification failure")
+ }
+ case signaturePKCS1v15:
+ pubKey, ok := pubkey.(*rsa.PublicKey)
+ if !ok {
+ return fmt.Errorf("expected an RSA public key, got %T", pubkey)
+ }
+ if err := rsa.VerifyPKCS1v15(pubKey, hashFunc, signed, sig); err != nil {
+ return err
+ }
+ case signatureRSAPSS:
+ pubKey, ok := pubkey.(*rsa.PublicKey)
+ if !ok {
+ return fmt.Errorf("expected an RSA public key, got %T", pubkey)
+ }
+ signOpts := &rsa.PSSOptions{SaltLength: rsa.PSSSaltLengthEqualsHash}
+ if err := rsa.VerifyPSS(pubKey, hashFunc, signed, sig, signOpts); err != nil {
+ return err
+ }
+ default:
+ return errors.New("internal error: unknown signature type")
+ }
+ return nil
+}
+
+const (
+ serverSignatureContext = "TLS 1.3, server CertificateVerify\x00"
+ clientSignatureContext = "TLS 1.3, client CertificateVerify\x00"
+)
+
+var signaturePadding = []byte{
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+}
+
+// signedMessage returns the pre-hashed (if necessary) message to be signed by
+// certificate keys in TLS 1.3. See RFC 8446, Section 4.4.3.
+func signedMessage(sigHash crypto.Hash, context string, transcript hash.Hash) []byte {
+ if sigHash == directSigning {
+ b := &bytes.Buffer{}
+ b.Write(signaturePadding)
+ io.WriteString(b, context)
+ b.Write(transcript.Sum(nil))
+ return b.Bytes()
+ }
+ h := sigHash.New()
+ h.Write(signaturePadding)
+ io.WriteString(h, context)
+ h.Write(transcript.Sum(nil))
+ return h.Sum(nil)
+}
+
+// typeAndHashFromSignatureScheme returns the corresponding signature type and
+// crypto.Hash for a given TLS SignatureScheme.
+func typeAndHashFromSignatureScheme(signatureAlgorithm SignatureScheme) (sigType uint8, hash crypto.Hash, err error) {
+ switch signatureAlgorithm {
+ case PKCS1WithSHA1, PKCS1WithSHA256, PKCS1WithSHA384, PKCS1WithSHA512:
+ sigType = signaturePKCS1v15
+ case PSSWithSHA256, PSSWithSHA384, PSSWithSHA512:
+ sigType = signatureRSAPSS
+ case ECDSAWithSHA1, ECDSAWithP256AndSHA256, ECDSAWithP384AndSHA384, ECDSAWithP521AndSHA512:
+ sigType = signatureECDSA
+ case Ed25519:
+ sigType = signatureEd25519
+ default:
+ return 0, 0, fmt.Errorf("unsupported signature algorithm: %v", signatureAlgorithm)
+ }
+ switch signatureAlgorithm {
+ case PKCS1WithSHA1, ECDSAWithSHA1:
+ hash = crypto.SHA1
+ case PKCS1WithSHA256, PSSWithSHA256, ECDSAWithP256AndSHA256:
+ hash = crypto.SHA256
+ case PKCS1WithSHA384, PSSWithSHA384, ECDSAWithP384AndSHA384:
+ hash = crypto.SHA384
+ case PKCS1WithSHA512, PSSWithSHA512, ECDSAWithP521AndSHA512:
+ hash = crypto.SHA512
+ case Ed25519:
+ hash = directSigning
+ default:
+ return 0, 0, fmt.Errorf("unsupported signature algorithm: %v", signatureAlgorithm)
+ }
+ return sigType, hash, nil
+}
+
+// legacyTypeAndHashFromPublicKey returns the fixed signature type and crypto.Hash for
+// a given public key used with TLS 1.0 and 1.1, before the introduction of
+// signature algorithm negotiation.
+func legacyTypeAndHashFromPublicKey(pub crypto.PublicKey) (sigType uint8, hash crypto.Hash, err error) {
+ switch pub.(type) {
+ case *rsa.PublicKey:
+ return signaturePKCS1v15, crypto.MD5SHA1, nil
+ case *ecdsa.PublicKey:
+ return signatureECDSA, crypto.SHA1, nil
+ case ed25519.PublicKey:
+ // RFC 8422 specifies support for Ed25519 in TLS 1.0 and 1.1,
+ // but it requires holding on to a handshake transcript to do a
+ // full signature, and not even OpenSSL bothers with the
+ // complexity, so we can't even test it properly.
+ return 0, 0, fmt.Errorf("tls: Ed25519 public keys are not supported before TLS 1.2")
+ default:
+ return 0, 0, fmt.Errorf("tls: unsupported public key: %T", pub)
+ }
+}
+
+var rsaSignatureSchemes = []struct {
+ scheme SignatureScheme
+ minModulusBytes int
+ maxVersion uint16
+}{
+ // RSA-PSS is used with PSSSaltLengthEqualsHash, and requires
+ // emLen >= hLen + sLen + 2
+ {PSSWithSHA256, crypto.SHA256.Size()*2 + 2, VersionTLS13},
+ {PSSWithSHA384, crypto.SHA384.Size()*2 + 2, VersionTLS13},
+ {PSSWithSHA512, crypto.SHA512.Size()*2 + 2, VersionTLS13},
+ // PKCS #1 v1.5 uses prefixes from hashPrefixes in crypto/rsa, and requires
+ // emLen >= len(prefix) + hLen + 11
+ // TLS 1.3 dropped support for PKCS #1 v1.5 in favor of RSA-PSS.
+ {PKCS1WithSHA256, 19 + crypto.SHA256.Size() + 11, VersionTLS12},
+ {PKCS1WithSHA384, 19 + crypto.SHA384.Size() + 11, VersionTLS12},
+ {PKCS1WithSHA512, 19 + crypto.SHA512.Size() + 11, VersionTLS12},
+ {PKCS1WithSHA1, 15 + crypto.SHA1.Size() + 11, VersionTLS12},
+}
+
+// signatureSchemesForCertificate returns the list of supported SignatureSchemes
+// for a given certificate, based on the public key and the protocol version,
+// and optionally filtered by its explicit SupportedSignatureAlgorithms.
+//
+// This function must be kept in sync with supportedSignatureAlgorithms.
+func signatureSchemesForCertificate(version uint16, cert *Certificate) []SignatureScheme {
+ priv, ok := cert.PrivateKey.(crypto.Signer)
+ if !ok {
+ return nil
+ }
+
+ var sigAlgs []SignatureScheme
+ switch pub := priv.Public().(type) {
+ case *ecdsa.PublicKey:
+ if version != VersionTLS13 {
+ // In TLS 1.2 and earlier, ECDSA algorithms are not
+ // constrained to a single curve.
+ sigAlgs = []SignatureScheme{
+ ECDSAWithP256AndSHA256,
+ ECDSAWithP384AndSHA384,
+ ECDSAWithP521AndSHA512,
+ ECDSAWithSHA1,
+ }
+ break
+ }
+ switch pub.Curve {
+ case elliptic.P256():
+ sigAlgs = []SignatureScheme{ECDSAWithP256AndSHA256}
+ case elliptic.P384():
+ sigAlgs = []SignatureScheme{ECDSAWithP384AndSHA384}
+ case elliptic.P521():
+ sigAlgs = []SignatureScheme{ECDSAWithP521AndSHA512}
+ default:
+ return nil
+ }
+ case *rsa.PublicKey:
+ size := pub.Size()
+ sigAlgs = make([]SignatureScheme, 0, len(rsaSignatureSchemes))
+ for _, candidate := range rsaSignatureSchemes {
+ if size >= candidate.minModulusBytes && version <= candidate.maxVersion {
+ sigAlgs = append(sigAlgs, candidate.scheme)
+ }
+ }
+ case ed25519.PublicKey:
+ sigAlgs = []SignatureScheme{Ed25519}
+ default:
+ return nil
+ }
+
+ if cert.SupportedSignatureAlgorithms != nil {
+ var filteredSigAlgs []SignatureScheme
+ for _, sigAlg := range sigAlgs {
+ if isSupportedSignatureAlgorithm(sigAlg, cert.SupportedSignatureAlgorithms) {
+ filteredSigAlgs = append(filteredSigAlgs, sigAlg)
+ }
+ }
+ return filteredSigAlgs
+ }
+ return sigAlgs
+}
+
+// selectSignatureScheme picks a SignatureScheme from the peer's preference list
+// that works with the selected certificate. It's only called for protocol
+// versions that support signature algorithms, so TLS 1.2 and 1.3.
+func selectSignatureScheme(vers uint16, c *Certificate, peerAlgs []SignatureScheme) (SignatureScheme, error) {
+ supportedAlgs := signatureSchemesForCertificate(vers, c)
+ if len(supportedAlgs) == 0 {
+ return 0, unsupportedCertificateError(c)
+ }
+ if len(peerAlgs) == 0 && vers == VersionTLS12 {
+ // For TLS 1.2, if the client didn't send signature_algorithms then we
+ // can assume that it supports SHA1. See RFC 5246, Section 7.4.1.4.1.
+ peerAlgs = []SignatureScheme{PKCS1WithSHA1, ECDSAWithSHA1}
+ }
+ // Pick signature scheme in the peer's preference order, as our
+ // preference order is not configurable.
+ for _, preferredAlg := range peerAlgs {
+ if isSupportedSignatureAlgorithm(preferredAlg, supportedAlgs) {
+ return preferredAlg, nil
+ }
+ }
+ return 0, errors.New("tls: peer doesn't support any of the certificate's signature algorithms")
+}
+
+// unsupportedCertificateError returns a helpful error for certificates with
+// an unsupported private key.
+func unsupportedCertificateError(cert *Certificate) error {
+ switch cert.PrivateKey.(type) {
+ case rsa.PrivateKey, ecdsa.PrivateKey:
+ return fmt.Errorf("tls: unsupported certificate: private key is %T, expected *%T",
+ cert.PrivateKey, cert.PrivateKey)
+ case *ed25519.PrivateKey:
+ return fmt.Errorf("tls: unsupported certificate: private key is *ed25519.PrivateKey, expected ed25519.PrivateKey")
+ }
+
+ signer, ok := cert.PrivateKey.(crypto.Signer)
+ if !ok {
+ return fmt.Errorf("tls: certificate private key (%T) does not implement crypto.Signer",
+ cert.PrivateKey)
+ }
+
+ switch pub := signer.Public().(type) {
+ case *ecdsa.PublicKey:
+ switch pub.Curve {
+ case elliptic.P256():
+ case elliptic.P384():
+ case elliptic.P521():
+ default:
+ return fmt.Errorf("tls: unsupported certificate curve (%s)", pub.Curve.Params().Name)
+ }
+ case *rsa.PublicKey:
+ return fmt.Errorf("tls: certificate RSA key size too small for supported signature algorithms")
+ case ed25519.PublicKey:
+ default:
+ return fmt.Errorf("tls: unsupported certificate key (%T)", pub)
+ }
+
+ if cert.SupportedSignatureAlgorithms != nil {
+ return fmt.Errorf("tls: peer doesn't support the certificate custom signature algorithms")
+ }
+
+ return fmt.Errorf("tls: internal error: unsupported key (%T)", cert.PrivateKey)
+}
diff --git a/vendor/github.com/quic-go/qtls-go1-18/cipher_suites.go b/vendor/github.com/quic-go/qtls-go1-18/cipher_suites.go
new file mode 100644
index 0000000000..e0be514740
--- /dev/null
+++ b/vendor/github.com/quic-go/qtls-go1-18/cipher_suites.go
@@ -0,0 +1,691 @@
+// Copyright 2010 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package qtls
+
+import (
+ "crypto"
+ "crypto/aes"
+ "crypto/cipher"
+ "crypto/des"
+ "crypto/hmac"
+ "crypto/rc4"
+ "crypto/sha1"
+ "crypto/sha256"
+ "fmt"
+ "hash"
+
+ "golang.org/x/crypto/chacha20poly1305"
+)
+
+// CipherSuite is a TLS cipher suite. Note that most functions in this package
+// accept and expose cipher suite IDs instead of this type.
+type CipherSuite struct {
+ ID uint16
+ Name string
+
+ // Supported versions is the list of TLS protocol versions that can
+ // negotiate this cipher suite.
+ SupportedVersions []uint16
+
+ // Insecure is true if the cipher suite has known security issues
+ // due to its primitives, design, or implementation.
+ Insecure bool
+}
+
+var (
+ supportedUpToTLS12 = []uint16{VersionTLS10, VersionTLS11, VersionTLS12}
+ supportedOnlyTLS12 = []uint16{VersionTLS12}
+ supportedOnlyTLS13 = []uint16{VersionTLS13}
+)
+
+// CipherSuites returns a list of cipher suites currently implemented by this
+// package, excluding those with security issues, which are returned by
+// InsecureCipherSuites.
+//
+// The list is sorted by ID. Note that the default cipher suites selected by
+// this package might depend on logic that can't be captured by a static list,
+// and might not match those returned by this function.
+func CipherSuites() []*CipherSuite {
+ return []*CipherSuite{
+ {TLS_RSA_WITH_AES_128_CBC_SHA, "TLS_RSA_WITH_AES_128_CBC_SHA", supportedUpToTLS12, false},
+ {TLS_RSA_WITH_AES_256_CBC_SHA, "TLS_RSA_WITH_AES_256_CBC_SHA", supportedUpToTLS12, false},
+ {TLS_RSA_WITH_AES_128_GCM_SHA256, "TLS_RSA_WITH_AES_128_GCM_SHA256", supportedOnlyTLS12, false},
+ {TLS_RSA_WITH_AES_256_GCM_SHA384, "TLS_RSA_WITH_AES_256_GCM_SHA384", supportedOnlyTLS12, false},
+
+ {TLS_AES_128_GCM_SHA256, "TLS_AES_128_GCM_SHA256", supportedOnlyTLS13, false},
+ {TLS_AES_256_GCM_SHA384, "TLS_AES_256_GCM_SHA384", supportedOnlyTLS13, false},
+ {TLS_CHACHA20_POLY1305_SHA256, "TLS_CHACHA20_POLY1305_SHA256", supportedOnlyTLS13, false},
+
+ {TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA", supportedUpToTLS12, false},
+ {TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA", supportedUpToTLS12, false},
+ {TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA", supportedUpToTLS12, false},
+ {TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA", supportedUpToTLS12, false},
+ {TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256", supportedOnlyTLS12, false},
+ {TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384", supportedOnlyTLS12, false},
+ {TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256", supportedOnlyTLS12, false},
+ {TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384", supportedOnlyTLS12, false},
+ {TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256", supportedOnlyTLS12, false},
+ {TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256", supportedOnlyTLS12, false},
+ }
+}
+
+// InsecureCipherSuites returns a list of cipher suites currently implemented by
+// this package and which have security issues.
+//
+// Most applications should not use the cipher suites in this list, and should
+// only use those returned by CipherSuites.
+func InsecureCipherSuites() []*CipherSuite {
+ // This list includes RC4, CBC_SHA256, and 3DES cipher suites. See
+ // cipherSuitesPreferenceOrder for details.
+ return []*CipherSuite{
+ {TLS_RSA_WITH_RC4_128_SHA, "TLS_RSA_WITH_RC4_128_SHA", supportedUpToTLS12, true},
+ {TLS_RSA_WITH_3DES_EDE_CBC_SHA, "TLS_RSA_WITH_3DES_EDE_CBC_SHA", supportedUpToTLS12, true},
+ {TLS_RSA_WITH_AES_128_CBC_SHA256, "TLS_RSA_WITH_AES_128_CBC_SHA256", supportedOnlyTLS12, true},
+ {TLS_ECDHE_ECDSA_WITH_RC4_128_SHA, "TLS_ECDHE_ECDSA_WITH_RC4_128_SHA", supportedUpToTLS12, true},
+ {TLS_ECDHE_RSA_WITH_RC4_128_SHA, "TLS_ECDHE_RSA_WITH_RC4_128_SHA", supportedUpToTLS12, true},
+ {TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA, "TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA", supportedUpToTLS12, true},
+ {TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256, "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256", supportedOnlyTLS12, true},
+ {TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256, "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256", supportedOnlyTLS12, true},
+ }
+}
+
+// CipherSuiteName returns the standard name for the passed cipher suite ID
+// (e.g. "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256"), or a fallback representation
+// of the ID value if the cipher suite is not implemented by this package.
+func CipherSuiteName(id uint16) string {
+ for _, c := range CipherSuites() {
+ if c.ID == id {
+ return c.Name
+ }
+ }
+ for _, c := range InsecureCipherSuites() {
+ if c.ID == id {
+ return c.Name
+ }
+ }
+ return fmt.Sprintf("0x%04X", id)
+}
+
+const (
+ // suiteECDHE indicates that the cipher suite involves elliptic curve
+ // Diffie-Hellman. This means that it should only be selected when the
+ // client indicates that it supports ECC with a curve and point format
+ // that we're happy with.
+ suiteECDHE = 1 << iota
+ // suiteECSign indicates that the cipher suite involves an ECDSA or
+ // EdDSA signature and therefore may only be selected when the server's
+ // certificate is ECDSA or EdDSA. If this is not set then the cipher suite
+ // is RSA based.
+ suiteECSign
+ // suiteTLS12 indicates that the cipher suite should only be advertised
+ // and accepted when using TLS 1.2.
+ suiteTLS12
+ // suiteSHA384 indicates that the cipher suite uses SHA384 as the
+ // handshake hash.
+ suiteSHA384
+)
+
+// A cipherSuite is a TLS 1.0–1.2 cipher suite, and defines the key exchange
+// mechanism, as well as the cipher+MAC pair or the AEAD.
+type cipherSuite struct {
+ id uint16
+ // the lengths, in bytes, of the key material needed for each component.
+ keyLen int
+ macLen int
+ ivLen int
+ ka func(version uint16) keyAgreement
+ // flags is a bitmask of the suite* values, above.
+ flags int
+ cipher func(key, iv []byte, isRead bool) any
+ mac func(key []byte) hash.Hash
+ aead func(key, fixedNonce []byte) aead
+}
+
+var cipherSuites = []*cipherSuite{ // TODO: replace with a map, since the order doesn't matter.
+ {TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305, 32, 0, 12, ecdheRSAKA, suiteECDHE | suiteTLS12, nil, nil, aeadChaCha20Poly1305},
+ {TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305, 32, 0, 12, ecdheECDSAKA, suiteECDHE | suiteECSign | suiteTLS12, nil, nil, aeadChaCha20Poly1305},
+ {TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, 16, 0, 4, ecdheRSAKA, suiteECDHE | suiteTLS12, nil, nil, aeadAESGCM},
+ {TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, 16, 0, 4, ecdheECDSAKA, suiteECDHE | suiteECSign | suiteTLS12, nil, nil, aeadAESGCM},
+ {TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, 32, 0, 4, ecdheRSAKA, suiteECDHE | suiteTLS12 | suiteSHA384, nil, nil, aeadAESGCM},
+ {TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, 32, 0, 4, ecdheECDSAKA, suiteECDHE | suiteECSign | suiteTLS12 | suiteSHA384, nil, nil, aeadAESGCM},
+ {TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256, 16, 32, 16, ecdheRSAKA, suiteECDHE | suiteTLS12, cipherAES, macSHA256, nil},
+ {TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, 16, 20, 16, ecdheRSAKA, suiteECDHE, cipherAES, macSHA1, nil},
+ {TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256, 16, 32, 16, ecdheECDSAKA, suiteECDHE | suiteECSign | suiteTLS12, cipherAES, macSHA256, nil},
+ {TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, 16, 20, 16, ecdheECDSAKA, suiteECDHE | suiteECSign, cipherAES, macSHA1, nil},
+ {TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, 32, 20, 16, ecdheRSAKA, suiteECDHE, cipherAES, macSHA1, nil},
+ {TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, 32, 20, 16, ecdheECDSAKA, suiteECDHE | suiteECSign, cipherAES, macSHA1, nil},
+ {TLS_RSA_WITH_AES_128_GCM_SHA256, 16, 0, 4, rsaKA, suiteTLS12, nil, nil, aeadAESGCM},
+ {TLS_RSA_WITH_AES_256_GCM_SHA384, 32, 0, 4, rsaKA, suiteTLS12 | suiteSHA384, nil, nil, aeadAESGCM},
+ {TLS_RSA_WITH_AES_128_CBC_SHA256, 16, 32, 16, rsaKA, suiteTLS12, cipherAES, macSHA256, nil},
+ {TLS_RSA_WITH_AES_128_CBC_SHA, 16, 20, 16, rsaKA, 0, cipherAES, macSHA1, nil},
+ {TLS_RSA_WITH_AES_256_CBC_SHA, 32, 20, 16, rsaKA, 0, cipherAES, macSHA1, nil},
+ {TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA, 24, 20, 8, ecdheRSAKA, suiteECDHE, cipher3DES, macSHA1, nil},
+ {TLS_RSA_WITH_3DES_EDE_CBC_SHA, 24, 20, 8, rsaKA, 0, cipher3DES, macSHA1, nil},
+ {TLS_RSA_WITH_RC4_128_SHA, 16, 20, 0, rsaKA, 0, cipherRC4, macSHA1, nil},
+ {TLS_ECDHE_RSA_WITH_RC4_128_SHA, 16, 20, 0, ecdheRSAKA, suiteECDHE, cipherRC4, macSHA1, nil},
+ {TLS_ECDHE_ECDSA_WITH_RC4_128_SHA, 16, 20, 0, ecdheECDSAKA, suiteECDHE | suiteECSign, cipherRC4, macSHA1, nil},
+}
+
+// selectCipherSuite returns the first TLS 1.0–1.2 cipher suite from ids which
+// is also in supportedIDs and passes the ok filter.
+func selectCipherSuite(ids, supportedIDs []uint16, ok func(*cipherSuite) bool) *cipherSuite {
+ for _, id := range ids {
+ candidate := cipherSuiteByID(id)
+ if candidate == nil || !ok(candidate) {
+ continue
+ }
+
+ for _, suppID := range supportedIDs {
+ if id == suppID {
+ return candidate
+ }
+ }
+ }
+ return nil
+}
+
+// A cipherSuiteTLS13 defines only the pair of the AEAD algorithm and hash
+// algorithm to be used with HKDF. See RFC 8446, Appendix B.4.
+type cipherSuiteTLS13 struct {
+ id uint16
+ keyLen int
+ aead func(key, fixedNonce []byte) aead
+ hash crypto.Hash
+}
+
+type CipherSuiteTLS13 struct {
+ ID uint16
+ KeyLen int
+ Hash crypto.Hash
+ AEAD func(key, fixedNonce []byte) cipher.AEAD
+}
+
+func (c *CipherSuiteTLS13) IVLen() int {
+ return aeadNonceLength
+}
+
+var cipherSuitesTLS13 = []*cipherSuiteTLS13{ // TODO: replace with a map.
+ {TLS_AES_128_GCM_SHA256, 16, aeadAESGCMTLS13, crypto.SHA256},
+ {TLS_CHACHA20_POLY1305_SHA256, 32, aeadChaCha20Poly1305, crypto.SHA256},
+ {TLS_AES_256_GCM_SHA384, 32, aeadAESGCMTLS13, crypto.SHA384},
+}
+
+// cipherSuitesPreferenceOrder is the order in which we'll select (on the
+// server) or advertise (on the client) TLS 1.0–1.2 cipher suites.
+//
+// Cipher suites are filtered but not reordered based on the application and
+// peer's preferences, meaning we'll never select a suite lower in this list if
+// any higher one is available. This makes it more defensible to keep weaker
+// cipher suites enabled, especially on the server side where we get the last
+// word, since there are no known downgrade attacks on cipher suites selection.
+//
+// The list is sorted by applying the following priority rules, stopping at the
+// first (most important) applicable one:
+//
+// - Anything else comes before RC4
+//
+// RC4 has practically exploitable biases. See https://www.rc4nomore.com.
+//
+// - Anything else comes before CBC_SHA256
+//
+// SHA-256 variants of the CBC ciphersuites don't implement any Lucky13
+// countermeasures. See http://www.isg.rhul.ac.uk/tls/Lucky13.html and
+// https://www.imperialviolet.org/2013/02/04/luckythirteen.html.
+//
+// - Anything else comes before 3DES
+//
+// 3DES has 64-bit blocks, which makes it fundamentally susceptible to
+// birthday attacks. See https://sweet32.info.
+//
+// - ECDHE comes before anything else
+//
+// Once we got the broken stuff out of the way, the most important
+// property a cipher suite can have is forward secrecy. We don't
+// implement FFDHE, so that means ECDHE.
+//
+// - AEADs come before CBC ciphers
+//
+// Even with Lucky13 countermeasures, MAC-then-Encrypt CBC cipher suites
+// are fundamentally fragile, and suffered from an endless sequence of
+// padding oracle attacks. See https://eprint.iacr.org/2015/1129,
+// https://www.imperialviolet.org/2014/12/08/poodleagain.html, and
+// https://blog.cloudflare.com/yet-another-padding-oracle-in-openssl-cbc-ciphersuites/.
+//
+// - AES comes before ChaCha20
+//
+// When AES hardware is available, AES-128-GCM and AES-256-GCM are faster
+// than ChaCha20Poly1305.
+//
+// When AES hardware is not available, AES-128-GCM is one or more of: much
+// slower, way more complex, and less safe (because not constant time)
+// than ChaCha20Poly1305.
+//
+// We use this list if we think both peers have AES hardware, and
+// cipherSuitesPreferenceOrderNoAES otherwise.
+//
+// - AES-128 comes before AES-256
+//
+// The only potential advantages of AES-256 are better multi-target
+// margins, and hypothetical post-quantum properties. Neither apply to
+// TLS, and AES-256 is slower due to its four extra rounds (which don't
+// contribute to the advantages above).
+//
+// - ECDSA comes before RSA
+//
+// The relative order of ECDSA and RSA cipher suites doesn't matter,
+// as they depend on the certificate. Pick one to get a stable order.
+//
+var cipherSuitesPreferenceOrder = []uint16{
+ // AEADs w/ ECDHE
+ TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
+ TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
+ TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305, TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305,
+
+ // CBC w/ ECDHE
+ TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
+ TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
+
+ // AEADs w/o ECDHE
+ TLS_RSA_WITH_AES_128_GCM_SHA256,
+ TLS_RSA_WITH_AES_256_GCM_SHA384,
+
+ // CBC w/o ECDHE
+ TLS_RSA_WITH_AES_128_CBC_SHA,
+ TLS_RSA_WITH_AES_256_CBC_SHA,
+
+ // 3DES
+ TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA,
+ TLS_RSA_WITH_3DES_EDE_CBC_SHA,
+
+ // CBC_SHA256
+ TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256, TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256,
+ TLS_RSA_WITH_AES_128_CBC_SHA256,
+
+ // RC4
+ TLS_ECDHE_ECDSA_WITH_RC4_128_SHA, TLS_ECDHE_RSA_WITH_RC4_128_SHA,
+ TLS_RSA_WITH_RC4_128_SHA,
+}
+
+var cipherSuitesPreferenceOrderNoAES = []uint16{
+ // ChaCha20Poly1305
+ TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305, TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305,
+
+ // AES-GCM w/ ECDHE
+ TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
+ TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
+
+ // The rest of cipherSuitesPreferenceOrder.
+ TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
+ TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
+ TLS_RSA_WITH_AES_128_GCM_SHA256,
+ TLS_RSA_WITH_AES_256_GCM_SHA384,
+ TLS_RSA_WITH_AES_128_CBC_SHA,
+ TLS_RSA_WITH_AES_256_CBC_SHA,
+ TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA,
+ TLS_RSA_WITH_3DES_EDE_CBC_SHA,
+ TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256, TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256,
+ TLS_RSA_WITH_AES_128_CBC_SHA256,
+ TLS_ECDHE_ECDSA_WITH_RC4_128_SHA, TLS_ECDHE_RSA_WITH_RC4_128_SHA,
+ TLS_RSA_WITH_RC4_128_SHA,
+}
+
+// disabledCipherSuites are not used unless explicitly listed in
+// Config.CipherSuites. They MUST be at the end of cipherSuitesPreferenceOrder.
+var disabledCipherSuites = []uint16{
+ // CBC_SHA256
+ TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256, TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256,
+ TLS_RSA_WITH_AES_128_CBC_SHA256,
+
+ // RC4
+ TLS_ECDHE_ECDSA_WITH_RC4_128_SHA, TLS_ECDHE_RSA_WITH_RC4_128_SHA,
+ TLS_RSA_WITH_RC4_128_SHA,
+}
+
+var (
+ defaultCipherSuitesLen = len(cipherSuitesPreferenceOrder) - len(disabledCipherSuites)
+ defaultCipherSuites = cipherSuitesPreferenceOrder[:defaultCipherSuitesLen]
+)
+
+// defaultCipherSuitesTLS13 is also the preference order, since there are no
+// disabled by default TLS 1.3 cipher suites. The same AES vs ChaCha20 logic as
+// cipherSuitesPreferenceOrder applies.
+var defaultCipherSuitesTLS13 = []uint16{
+ TLS_AES_128_GCM_SHA256,
+ TLS_AES_256_GCM_SHA384,
+ TLS_CHACHA20_POLY1305_SHA256,
+}
+
+var defaultCipherSuitesTLS13NoAES = []uint16{
+ TLS_CHACHA20_POLY1305_SHA256,
+ TLS_AES_128_GCM_SHA256,
+ TLS_AES_256_GCM_SHA384,
+}
+
+var aesgcmCiphers = map[uint16]bool{
+ // TLS 1.2
+ TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256: true,
+ TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384: true,
+ TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256: true,
+ TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384: true,
+ // TLS 1.3
+ TLS_AES_128_GCM_SHA256: true,
+ TLS_AES_256_GCM_SHA384: true,
+}
+
+var nonAESGCMAEADCiphers = map[uint16]bool{
+ // TLS 1.2
+ TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305: true,
+ TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305: true,
+ // TLS 1.3
+ TLS_CHACHA20_POLY1305_SHA256: true,
+}
+
+// aesgcmPreferred returns whether the first known cipher in the preference list
+// is an AES-GCM cipher, implying the peer has hardware support for it.
+func aesgcmPreferred(ciphers []uint16) bool {
+ for _, cID := range ciphers {
+ if c := cipherSuiteByID(cID); c != nil {
+ return aesgcmCiphers[cID]
+ }
+ if c := cipherSuiteTLS13ByID(cID); c != nil {
+ return aesgcmCiphers[cID]
+ }
+ }
+ return false
+}
+
+func cipherRC4(key, iv []byte, isRead bool) any {
+ cipher, _ := rc4.NewCipher(key)
+ return cipher
+}
+
+func cipher3DES(key, iv []byte, isRead bool) any {
+ block, _ := des.NewTripleDESCipher(key)
+ if isRead {
+ return cipher.NewCBCDecrypter(block, iv)
+ }
+ return cipher.NewCBCEncrypter(block, iv)
+}
+
+func cipherAES(key, iv []byte, isRead bool) any {
+ block, _ := aes.NewCipher(key)
+ if isRead {
+ return cipher.NewCBCDecrypter(block, iv)
+ }
+ return cipher.NewCBCEncrypter(block, iv)
+}
+
+// macSHA1 returns a SHA-1 based constant time MAC.
+func macSHA1(key []byte) hash.Hash {
+ return hmac.New(newConstantTimeHash(sha1.New), key)
+}
+
+// macSHA256 returns a SHA-256 based MAC. This is only supported in TLS 1.2 and
+// is currently only used in disabled-by-default cipher suites.
+func macSHA256(key []byte) hash.Hash {
+ return hmac.New(sha256.New, key)
+}
+
+type aead interface {
+ cipher.AEAD
+
+ // explicitNonceLen returns the number of bytes of explicit nonce
+ // included in each record. This is eight for older AEADs and
+ // zero for modern ones.
+ explicitNonceLen() int
+}
+
+const (
+ aeadNonceLength = 12
+ noncePrefixLength = 4
+)
+
+// prefixNonceAEAD wraps an AEAD and prefixes a fixed portion of the nonce to
+// each call.
+type prefixNonceAEAD struct {
+ // nonce contains the fixed part of the nonce in the first four bytes.
+ nonce [aeadNonceLength]byte
+ aead cipher.AEAD
+}
+
+func (f *prefixNonceAEAD) NonceSize() int { return aeadNonceLength - noncePrefixLength }
+func (f *prefixNonceAEAD) Overhead() int { return f.aead.Overhead() }
+func (f *prefixNonceAEAD) explicitNonceLen() int { return f.NonceSize() }
+
+func (f *prefixNonceAEAD) Seal(out, nonce, plaintext, additionalData []byte) []byte {
+ copy(f.nonce[4:], nonce)
+ return f.aead.Seal(out, f.nonce[:], plaintext, additionalData)
+}
+
+func (f *prefixNonceAEAD) Open(out, nonce, ciphertext, additionalData []byte) ([]byte, error) {
+ copy(f.nonce[4:], nonce)
+ return f.aead.Open(out, f.nonce[:], ciphertext, additionalData)
+}
+
+// xoredNonceAEAD wraps an AEAD by XORing in a fixed pattern to the nonce
+// before each call.
+type xorNonceAEAD struct {
+ nonceMask [aeadNonceLength]byte
+ aead cipher.AEAD
+}
+
+func (f *xorNonceAEAD) NonceSize() int { return 8 } // 64-bit sequence number
+func (f *xorNonceAEAD) Overhead() int { return f.aead.Overhead() }
+func (f *xorNonceAEAD) explicitNonceLen() int { return 0 }
+
+func (f *xorNonceAEAD) Seal(out, nonce, plaintext, additionalData []byte) []byte {
+ for i, b := range nonce {
+ f.nonceMask[4+i] ^= b
+ }
+ result := f.aead.Seal(out, f.nonceMask[:], plaintext, additionalData)
+ for i, b := range nonce {
+ f.nonceMask[4+i] ^= b
+ }
+
+ return result
+}
+
+func (f *xorNonceAEAD) Open(out, nonce, ciphertext, additionalData []byte) ([]byte, error) {
+ for i, b := range nonce {
+ f.nonceMask[4+i] ^= b
+ }
+ result, err := f.aead.Open(out, f.nonceMask[:], ciphertext, additionalData)
+ for i, b := range nonce {
+ f.nonceMask[4+i] ^= b
+ }
+
+ return result, err
+}
+
+func aeadAESGCM(key, noncePrefix []byte) aead {
+ if len(noncePrefix) != noncePrefixLength {
+ panic("tls: internal error: wrong nonce length")
+ }
+ aes, err := aes.NewCipher(key)
+ if err != nil {
+ panic(err)
+ }
+ aead, err := cipher.NewGCM(aes)
+ if err != nil {
+ panic(err)
+ }
+
+ ret := &prefixNonceAEAD{aead: aead}
+ copy(ret.nonce[:], noncePrefix)
+ return ret
+}
+
+// AEADAESGCMTLS13 creates a new AES-GCM AEAD for TLS 1.3
+func AEADAESGCMTLS13(key, fixedNonce []byte) cipher.AEAD {
+ return aeadAESGCMTLS13(key, fixedNonce)
+}
+
+func aeadAESGCMTLS13(key, nonceMask []byte) aead {
+ if len(nonceMask) != aeadNonceLength {
+ panic("tls: internal error: wrong nonce length")
+ }
+ aes, err := aes.NewCipher(key)
+ if err != nil {
+ panic(err)
+ }
+ aead, err := cipher.NewGCM(aes)
+ if err != nil {
+ panic(err)
+ }
+
+ ret := &xorNonceAEAD{aead: aead}
+ copy(ret.nonceMask[:], nonceMask)
+ return ret
+}
+
+func aeadChaCha20Poly1305(key, nonceMask []byte) aead {
+ if len(nonceMask) != aeadNonceLength {
+ panic("tls: internal error: wrong nonce length")
+ }
+ aead, err := chacha20poly1305.New(key)
+ if err != nil {
+ panic(err)
+ }
+
+ ret := &xorNonceAEAD{aead: aead}
+ copy(ret.nonceMask[:], nonceMask)
+ return ret
+}
+
+type constantTimeHash interface {
+ hash.Hash
+ ConstantTimeSum(b []byte) []byte
+}
+
+// cthWrapper wraps any hash.Hash that implements ConstantTimeSum, and replaces
+// with that all calls to Sum. It's used to obtain a ConstantTimeSum-based HMAC.
+type cthWrapper struct {
+ h constantTimeHash
+}
+
+func (c *cthWrapper) Size() int { return c.h.Size() }
+func (c *cthWrapper) BlockSize() int { return c.h.BlockSize() }
+func (c *cthWrapper) Reset() { c.h.Reset() }
+func (c *cthWrapper) Write(p []byte) (int, error) { return c.h.Write(p) }
+func (c *cthWrapper) Sum(b []byte) []byte { return c.h.ConstantTimeSum(b) }
+
+func newConstantTimeHash(h func() hash.Hash) func() hash.Hash {
+ return func() hash.Hash {
+ return &cthWrapper{h().(constantTimeHash)}
+ }
+}
+
+// tls10MAC implements the TLS 1.0 MAC function. RFC 2246, Section 6.2.3.
+func tls10MAC(h hash.Hash, out, seq, header, data, extra []byte) []byte {
+ h.Reset()
+ h.Write(seq)
+ h.Write(header)
+ h.Write(data)
+ res := h.Sum(out)
+ if extra != nil {
+ h.Write(extra)
+ }
+ return res
+}
+
+func rsaKA(version uint16) keyAgreement {
+ return rsaKeyAgreement{}
+}
+
+func ecdheECDSAKA(version uint16) keyAgreement {
+ return &ecdheKeyAgreement{
+ isRSA: false,
+ version: version,
+ }
+}
+
+func ecdheRSAKA(version uint16) keyAgreement {
+ return &ecdheKeyAgreement{
+ isRSA: true,
+ version: version,
+ }
+}
+
+// mutualCipherSuite returns a cipherSuite given a list of supported
+// ciphersuites and the id requested by the peer.
+func mutualCipherSuite(have []uint16, want uint16) *cipherSuite {
+ for _, id := range have {
+ if id == want {
+ return cipherSuiteByID(id)
+ }
+ }
+ return nil
+}
+
+func cipherSuiteByID(id uint16) *cipherSuite {
+ for _, cipherSuite := range cipherSuites {
+ if cipherSuite.id == id {
+ return cipherSuite
+ }
+ }
+ return nil
+}
+
+func mutualCipherSuiteTLS13(have []uint16, want uint16) *cipherSuiteTLS13 {
+ for _, id := range have {
+ if id == want {
+ return cipherSuiteTLS13ByID(id)
+ }
+ }
+ return nil
+}
+
+func cipherSuiteTLS13ByID(id uint16) *cipherSuiteTLS13 {
+ for _, cipherSuite := range cipherSuitesTLS13 {
+ if cipherSuite.id == id {
+ return cipherSuite
+ }
+ }
+ return nil
+}
+
+// A list of cipher suite IDs that are, or have been, implemented by this
+// package.
+//
+// See https://www.iana.org/assignments/tls-parameters/tls-parameters.xml
+const (
+ // TLS 1.0 - 1.2 cipher suites.
+ TLS_RSA_WITH_RC4_128_SHA uint16 = 0x0005
+ TLS_RSA_WITH_3DES_EDE_CBC_SHA uint16 = 0x000a
+ TLS_RSA_WITH_AES_128_CBC_SHA uint16 = 0x002f
+ TLS_RSA_WITH_AES_256_CBC_SHA uint16 = 0x0035
+ TLS_RSA_WITH_AES_128_CBC_SHA256 uint16 = 0x003c
+ TLS_RSA_WITH_AES_128_GCM_SHA256 uint16 = 0x009c
+ TLS_RSA_WITH_AES_256_GCM_SHA384 uint16 = 0x009d
+ TLS_ECDHE_ECDSA_WITH_RC4_128_SHA uint16 = 0xc007
+ TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA uint16 = 0xc009
+ TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA uint16 = 0xc00a
+ TLS_ECDHE_RSA_WITH_RC4_128_SHA uint16 = 0xc011
+ TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA uint16 = 0xc012
+ TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA uint16 = 0xc013
+ TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA uint16 = 0xc014
+ TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256 uint16 = 0xc023
+ TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256 uint16 = 0xc027
+ TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 uint16 = 0xc02f
+ TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 uint16 = 0xc02b
+ TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 uint16 = 0xc030
+ TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 uint16 = 0xc02c
+ TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256 uint16 = 0xcca8
+ TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256 uint16 = 0xcca9
+
+ // TLS 1.3 cipher suites.
+ TLS_AES_128_GCM_SHA256 uint16 = 0x1301
+ TLS_AES_256_GCM_SHA384 uint16 = 0x1302
+ TLS_CHACHA20_POLY1305_SHA256 uint16 = 0x1303
+
+ // TLS_FALLBACK_SCSV isn't a standard cipher suite but an indicator
+ // that the client is doing version fallback. See RFC 7507.
+ TLS_FALLBACK_SCSV uint16 = 0x5600
+
+ // Legacy names for the corresponding cipher suites with the correct _SHA256
+ // suffix, retained for backward compatibility.
+ TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305 = TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256
+ TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305 = TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256
+)
diff --git a/vendor/github.com/quic-go/qtls-go1-18/common.go b/vendor/github.com/quic-go/qtls-go1-18/common.go
new file mode 100644
index 0000000000..3e4ced7e28
--- /dev/null
+++ b/vendor/github.com/quic-go/qtls-go1-18/common.go
@@ -0,0 +1,1508 @@
+// Copyright 2009 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package qtls
+
+import (
+ "bytes"
+ "container/list"
+ "context"
+ "crypto"
+ "crypto/ecdsa"
+ "crypto/ed25519"
+ "crypto/elliptic"
+ "crypto/rand"
+ "crypto/rsa"
+ "crypto/sha512"
+ "crypto/tls"
+ "crypto/x509"
+ "errors"
+ "fmt"
+ "io"
+ "net"
+ "strings"
+ "sync"
+ "time"
+)
+
+const (
+ VersionTLS10 = 0x0301
+ VersionTLS11 = 0x0302
+ VersionTLS12 = 0x0303
+ VersionTLS13 = 0x0304
+
+ // Deprecated: SSLv3 is cryptographically broken, and is no longer
+ // supported by this package. See golang.org/issue/32716.
+ VersionSSL30 = 0x0300
+)
+
+const (
+ maxPlaintext = 16384 // maximum plaintext payload length
+ maxCiphertext = 16384 + 2048 // maximum ciphertext payload length
+ maxCiphertextTLS13 = 16384 + 256 // maximum ciphertext length in TLS 1.3
+ recordHeaderLen = 5 // record header length
+ maxHandshake = 65536 // maximum handshake we support (protocol max is 16 MB)
+ maxUselessRecords = 16 // maximum number of consecutive non-advancing records
+)
+
+// TLS record types.
+type recordType uint8
+
+const (
+ recordTypeChangeCipherSpec recordType = 20
+ recordTypeAlert recordType = 21
+ recordTypeHandshake recordType = 22
+ recordTypeApplicationData recordType = 23
+)
+
+// TLS handshake message types.
+const (
+ typeHelloRequest uint8 = 0
+ typeClientHello uint8 = 1
+ typeServerHello uint8 = 2
+ typeNewSessionTicket uint8 = 4
+ typeEndOfEarlyData uint8 = 5
+ typeEncryptedExtensions uint8 = 8
+ typeCertificate uint8 = 11
+ typeServerKeyExchange uint8 = 12
+ typeCertificateRequest uint8 = 13
+ typeServerHelloDone uint8 = 14
+ typeCertificateVerify uint8 = 15
+ typeClientKeyExchange uint8 = 16
+ typeFinished uint8 = 20
+ typeCertificateStatus uint8 = 22
+ typeKeyUpdate uint8 = 24
+ typeNextProtocol uint8 = 67 // Not IANA assigned
+ typeMessageHash uint8 = 254 // synthetic message
+)
+
+// TLS compression types.
+const (
+ compressionNone uint8 = 0
+)
+
+type Extension struct {
+ Type uint16
+ Data []byte
+}
+
+// TLS extension numbers
+const (
+ extensionServerName uint16 = 0
+ extensionStatusRequest uint16 = 5
+ extensionSupportedCurves uint16 = 10 // supported_groups in TLS 1.3, see RFC 8446, Section 4.2.7
+ extensionSupportedPoints uint16 = 11
+ extensionSignatureAlgorithms uint16 = 13
+ extensionALPN uint16 = 16
+ extensionSCT uint16 = 18
+ extensionSessionTicket uint16 = 35
+ extensionPreSharedKey uint16 = 41
+ extensionEarlyData uint16 = 42
+ extensionSupportedVersions uint16 = 43
+ extensionCookie uint16 = 44
+ extensionPSKModes uint16 = 45
+ extensionCertificateAuthorities uint16 = 47
+ extensionSignatureAlgorithmsCert uint16 = 50
+ extensionKeyShare uint16 = 51
+ extensionRenegotiationInfo uint16 = 0xff01
+)
+
+// TLS signaling cipher suite values
+const (
+ scsvRenegotiation uint16 = 0x00ff
+)
+
+type EncryptionLevel uint8
+
+const (
+ EncryptionHandshake EncryptionLevel = iota
+ Encryption0RTT
+ EncryptionApplication
+)
+
+// CurveID is a tls.CurveID
+type CurveID = tls.CurveID
+
+const (
+ CurveP256 CurveID = 23
+ CurveP384 CurveID = 24
+ CurveP521 CurveID = 25
+ X25519 CurveID = 29
+)
+
+// TLS 1.3 Key Share. See RFC 8446, Section 4.2.8.
+type keyShare struct {
+ group CurveID
+ data []byte
+}
+
+// TLS 1.3 PSK Key Exchange Modes. See RFC 8446, Section 4.2.9.
+const (
+ pskModePlain uint8 = 0
+ pskModeDHE uint8 = 1
+)
+
+// TLS 1.3 PSK Identity. Can be a Session Ticket, or a reference to a saved
+// session. See RFC 8446, Section 4.2.11.
+type pskIdentity struct {
+ label []byte
+ obfuscatedTicketAge uint32
+}
+
+// TLS Elliptic Curve Point Formats
+// https://www.iana.org/assignments/tls-parameters/tls-parameters.xml#tls-parameters-9
+const (
+ pointFormatUncompressed uint8 = 0
+)
+
+// TLS CertificateStatusType (RFC 3546)
+const (
+ statusTypeOCSP uint8 = 1
+)
+
+// Certificate types (for certificateRequestMsg)
+const (
+ certTypeRSASign = 1
+ certTypeECDSASign = 64 // ECDSA or EdDSA keys, see RFC 8422, Section 3.
+)
+
+// Signature algorithms (for internal signaling use). Starting at 225 to avoid overlap with
+// TLS 1.2 codepoints (RFC 5246, Appendix A.4.1), with which these have nothing to do.
+const (
+ signaturePKCS1v15 uint8 = iota + 225
+ signatureRSAPSS
+ signatureECDSA
+ signatureEd25519
+)
+
+// directSigning is a standard Hash value that signals that no pre-hashing
+// should be performed, and that the input should be signed directly. It is the
+// hash function associated with the Ed25519 signature scheme.
+var directSigning crypto.Hash = 0
+
+// supportedSignatureAlgorithms contains the signature and hash algorithms that
+// the code advertises as supported in a TLS 1.2+ ClientHello and in a TLS 1.2+
+// CertificateRequest. The two fields are merged to match with TLS 1.3.
+// Note that in TLS 1.2, the ECDSA algorithms are not constrained to P-256, etc.
+var supportedSignatureAlgorithms = []SignatureScheme{
+ PSSWithSHA256,
+ ECDSAWithP256AndSHA256,
+ Ed25519,
+ PSSWithSHA384,
+ PSSWithSHA512,
+ PKCS1WithSHA256,
+ PKCS1WithSHA384,
+ PKCS1WithSHA512,
+ ECDSAWithP384AndSHA384,
+ ECDSAWithP521AndSHA512,
+ PKCS1WithSHA1,
+ ECDSAWithSHA1,
+}
+
+// helloRetryRequestRandom is set as the Random value of a ServerHello
+// to signal that the message is actually a HelloRetryRequest.
+var helloRetryRequestRandom = []byte{ // See RFC 8446, Section 4.1.3.
+ 0xCF, 0x21, 0xAD, 0x74, 0xE5, 0x9A, 0x61, 0x11,
+ 0xBE, 0x1D, 0x8C, 0x02, 0x1E, 0x65, 0xB8, 0x91,
+ 0xC2, 0xA2, 0x11, 0x16, 0x7A, 0xBB, 0x8C, 0x5E,
+ 0x07, 0x9E, 0x09, 0xE2, 0xC8, 0xA8, 0x33, 0x9C,
+}
+
+const (
+ // downgradeCanaryTLS12 or downgradeCanaryTLS11 is embedded in the server
+ // random as a downgrade protection if the server would be capable of
+ // negotiating a higher version. See RFC 8446, Section 4.1.3.
+ downgradeCanaryTLS12 = "DOWNGRD\x01"
+ downgradeCanaryTLS11 = "DOWNGRD\x00"
+)
+
+// testingOnlyForceDowngradeCanary is set in tests to force the server side to
+// include downgrade canaries even if it's using its highers supported version.
+var testingOnlyForceDowngradeCanary bool
+
+type ConnectionState = tls.ConnectionState
+
+// ConnectionState records basic TLS details about the connection.
+type connectionState struct {
+ // Version is the TLS version used by the connection (e.g. VersionTLS12).
+ Version uint16
+
+ // HandshakeComplete is true if the handshake has concluded.
+ HandshakeComplete bool
+
+ // DidResume is true if this connection was successfully resumed from a
+ // previous session with a session ticket or similar mechanism.
+ DidResume bool
+
+ // CipherSuite is the cipher suite negotiated for the connection (e.g.
+ // TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, TLS_AES_128_GCM_SHA256).
+ CipherSuite uint16
+
+ // NegotiatedProtocol is the application protocol negotiated with ALPN.
+ NegotiatedProtocol string
+
+ // NegotiatedProtocolIsMutual used to indicate a mutual NPN negotiation.
+ //
+ // Deprecated: this value is always true.
+ NegotiatedProtocolIsMutual bool
+
+ // ServerName is the value of the Server Name Indication extension sent by
+ // the client. It's available both on the server and on the client side.
+ ServerName string
+
+ // PeerCertificates are the parsed certificates sent by the peer, in the
+ // order in which they were sent. The first element is the leaf certificate
+ // that the connection is verified against.
+ //
+ // On the client side, it can't be empty. On the server side, it can be
+ // empty if Config.ClientAuth is not RequireAnyClientCert or
+ // RequireAndVerifyClientCert.
+ PeerCertificates []*x509.Certificate
+
+ // VerifiedChains is a list of one or more chains where the first element is
+ // PeerCertificates[0] and the last element is from Config.RootCAs (on the
+ // client side) or Config.ClientCAs (on the server side).
+ //
+ // On the client side, it's set if Config.InsecureSkipVerify is false. On
+ // the server side, it's set if Config.ClientAuth is VerifyClientCertIfGiven
+ // (and the peer provided a certificate) or RequireAndVerifyClientCert.
+ VerifiedChains [][]*x509.Certificate
+
+ // SignedCertificateTimestamps is a list of SCTs provided by the peer
+ // through the TLS handshake for the leaf certificate, if any.
+ SignedCertificateTimestamps [][]byte
+
+ // OCSPResponse is a stapled Online Certificate Status Protocol (OCSP)
+ // response provided by the peer for the leaf certificate, if any.
+ OCSPResponse []byte
+
+ // TLSUnique contains the "tls-unique" channel binding value (see RFC 5929,
+ // Section 3). This value will be nil for TLS 1.3 connections and for all
+ // resumed connections.
+ //
+ // Deprecated: there are conditions in which this value might not be unique
+ // to a connection. See the Security Considerations sections of RFC 5705 and
+ // RFC 7627, and https://mitls.org/pages/attacks/3SHAKE#channelbindings.
+ TLSUnique []byte
+
+ // ekm is a closure exposed via ExportKeyingMaterial.
+ ekm func(label string, context []byte, length int) ([]byte, error)
+}
+
+type ConnectionStateWith0RTT struct {
+ ConnectionState
+
+ Used0RTT bool // true if 0-RTT was both offered and accepted
+}
+
+// ClientAuthType is tls.ClientAuthType
+type ClientAuthType = tls.ClientAuthType
+
+const (
+ NoClientCert = tls.NoClientCert
+ RequestClientCert = tls.RequestClientCert
+ RequireAnyClientCert = tls.RequireAnyClientCert
+ VerifyClientCertIfGiven = tls.VerifyClientCertIfGiven
+ RequireAndVerifyClientCert = tls.RequireAndVerifyClientCert
+)
+
+// requiresClientCert reports whether the ClientAuthType requires a client
+// certificate to be provided.
+func requiresClientCert(c ClientAuthType) bool {
+ switch c {
+ case RequireAnyClientCert, RequireAndVerifyClientCert:
+ return true
+ default:
+ return false
+ }
+}
+
+// ClientSessionState contains the state needed by clients to resume TLS
+// sessions.
+type ClientSessionState = tls.ClientSessionState
+
+type clientSessionState struct {
+ sessionTicket []uint8 // Encrypted ticket used for session resumption with server
+ vers uint16 // TLS version negotiated for the session
+ cipherSuite uint16 // Ciphersuite negotiated for the session
+ masterSecret []byte // Full handshake MasterSecret, or TLS 1.3 resumption_master_secret
+ serverCertificates []*x509.Certificate // Certificate chain presented by the server
+ verifiedChains [][]*x509.Certificate // Certificate chains we built for verification
+ receivedAt time.Time // When the session ticket was received from the server
+ ocspResponse []byte // Stapled OCSP response presented by the server
+ scts [][]byte // SCTs presented by the server
+
+ // TLS 1.3 fields.
+ nonce []byte // Ticket nonce sent by the server, to derive PSK
+ useBy time.Time // Expiration of the ticket lifetime as set by the server
+ ageAdd uint32 // Random obfuscation factor for sending the ticket age
+}
+
+// ClientSessionCache is a cache of ClientSessionState objects that can be used
+// by a client to resume a TLS session with a given server. ClientSessionCache
+// implementations should expect to be called concurrently from different
+// goroutines. Up to TLS 1.2, only ticket-based resumption is supported, not
+// SessionID-based resumption. In TLS 1.3 they were merged into PSK modes, which
+// are supported via this interface.
+//
+//go:generate sh -c "mockgen -package qtls -destination mock_client_session_cache_test.go github.com/quic-go/qtls-go1-18 ClientSessionCache"
+type ClientSessionCache = tls.ClientSessionCache
+
+// SignatureScheme is a tls.SignatureScheme
+type SignatureScheme = tls.SignatureScheme
+
+const (
+ // RSASSA-PKCS1-v1_5 algorithms.
+ PKCS1WithSHA256 SignatureScheme = 0x0401
+ PKCS1WithSHA384 SignatureScheme = 0x0501
+ PKCS1WithSHA512 SignatureScheme = 0x0601
+
+ // RSASSA-PSS algorithms with public key OID rsaEncryption.
+ PSSWithSHA256 SignatureScheme = 0x0804
+ PSSWithSHA384 SignatureScheme = 0x0805
+ PSSWithSHA512 SignatureScheme = 0x0806
+
+ // ECDSA algorithms. Only constrained to a specific curve in TLS 1.3.
+ ECDSAWithP256AndSHA256 SignatureScheme = 0x0403
+ ECDSAWithP384AndSHA384 SignatureScheme = 0x0503
+ ECDSAWithP521AndSHA512 SignatureScheme = 0x0603
+
+ // EdDSA algorithms.
+ Ed25519 SignatureScheme = 0x0807
+
+ // Legacy signature and hash algorithms for TLS 1.2.
+ PKCS1WithSHA1 SignatureScheme = 0x0201
+ ECDSAWithSHA1 SignatureScheme = 0x0203
+)
+
+// ClientHelloInfo contains information from a ClientHello message in order to
+// guide application logic in the GetCertificate and GetConfigForClient callbacks.
+type ClientHelloInfo = tls.ClientHelloInfo
+
+type clientHelloInfo struct {
+ // CipherSuites lists the CipherSuites supported by the client (e.g.
+ // TLS_AES_128_GCM_SHA256, TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256).
+ CipherSuites []uint16
+
+ // 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
+
+ // SupportedCurves lists the elliptic curves supported by the client.
+ // SupportedCurves is set only if the Supported Elliptic Curves
+ // Extension is being used (see RFC 4492, Section 5.1.1).
+ SupportedCurves []CurveID
+
+ // SupportedPoints lists the point formats supported by the client.
+ // SupportedPoints is set only if the Supported Point Formats Extension
+ // is being used (see RFC 4492, Section 5.1.2).
+ SupportedPoints []uint8
+
+ // SignatureSchemes lists the signature and hash schemes that the client
+ // is willing to verify. SignatureSchemes is set only if the Signature
+ // Algorithms Extension is being used (see RFC 5246, Section 7.4.1.4.1).
+ SignatureSchemes []SignatureScheme
+
+ // SupportedProtos lists the application protocols supported by the client.
+ // SupportedProtos is set only if the Application-Layer Protocol
+ // Negotiation Extension is being used (see RFC 7301, Section 3.1).
+ //
+ // Servers can select a protocol by setting Config.NextProtos in a
+ // GetConfigForClient return value.
+ SupportedProtos []string
+
+ // SupportedVersions lists the TLS versions supported by the client.
+ // For TLS versions less than 1.3, this is extrapolated from the max
+ // version advertised by the client, so values other than the greatest
+ // might be rejected if used.
+ SupportedVersions []uint16
+
+ // Conn is the underlying net.Conn for the connection. Do not read
+ // from, or write to, this connection; that will cause the TLS
+ // connection to fail.
+ Conn net.Conn
+
+ // config is embedded by the GetCertificate or GetConfigForClient caller,
+ // for use with SupportsCertificate.
+ config *Config
+
+ // ctx is the context of the handshake that is in progress.
+ ctx context.Context
+}
+
+// Context returns the context of the handshake that is in progress.
+// This context is a child of the context passed to HandshakeContext,
+// if any, and is canceled when the handshake concludes.
+func (c *clientHelloInfo) Context() context.Context {
+ return c.ctx
+}
+
+// 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 = tls.CertificateRequestInfo
+
+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
+
+ // SignatureSchemes lists the signature schemes that the server is
+ // willing to verify.
+ SignatureSchemes []SignatureScheme
+
+ // Version is the TLS version that was negotiated for this connection.
+ Version uint16
+
+ // ctx is the context of the handshake that is in progress.
+ ctx context.Context
+}
+
+// Context returns the context of the handshake that is in progress.
+// This context is a child of the context passed to HandshakeContext,
+// if any, and is canceled when the handshake concludes.
+func (c *certificateRequestInfo) Context() context.Context {
+ return c.ctx
+}
+
+// RenegotiationSupport enumerates the different levels of support for TLS
+// renegotiation. TLS renegotiation is the act of performing subsequent
+// handshakes on a connection after the first. This significantly complicates
+// the state machine and has been the source of numerous, subtle security
+// issues. Initiating a renegotiation is not supported, but support for
+// accepting renegotiation requests may be enabled.
+//
+// Even when enabled, the server may not change its identity between handshakes
+// (i.e. the leaf certificate must be the same). Additionally, concurrent
+// handshake and application data flow is not permitted so renegotiation can
+// only be used with protocols that synchronise with the renegotiation, such as
+// HTTPS.
+//
+// Renegotiation is not defined in TLS 1.3.
+type RenegotiationSupport = tls.RenegotiationSupport
+
+const (
+ // RenegotiateNever disables renegotiation.
+ RenegotiateNever = tls.RenegotiateNever
+
+ // RenegotiateOnceAsClient allows a remote server to request
+ // renegotiation once per connection.
+ RenegotiateOnceAsClient = tls.RenegotiateOnceAsClient
+
+ // RenegotiateFreelyAsClient allows a remote server to repeatedly
+ // request renegotiation.
+ RenegotiateFreelyAsClient = tls.RenegotiateFreelyAsClient
+)
+
+// A Config structure is used to configure a TLS client or server.
+// After one has been passed to a TLS function it must not be
+// modified. A Config may be reused; the tls package will also not
+// modify it.
+type Config = tls.Config
+
+type config struct {
+ // Rand provides the source of entropy for nonces and RSA blinding.
+ // If Rand is nil, TLS uses the cryptographic random reader in package
+ // crypto/rand.
+ // The Reader must be safe for use by multiple goroutines.
+ Rand io.Reader
+
+ // Time returns the current time as the number of seconds since the epoch.
+ // If Time is nil, TLS uses time.Now.
+ Time func() time.Time
+
+ // Certificates contains one or more certificate chains to present to the
+ // other side of the connection. The first certificate compatible with the
+ // peer's requirements is selected automatically.
+ //
+ // Server configurations must set one of Certificates, GetCertificate or
+ // GetConfigForClient. Clients doing client-authentication may set either
+ // Certificates or GetClientCertificate.
+ //
+ // Note: if there are multiple Certificates, and they don't have the
+ // optional field Leaf set, certificate selection will incur a significant
+ // per-handshake performance cost.
+ Certificates []Certificate
+
+ // NameToCertificate maps from a certificate name to an element of
+ // Certificates. Note that a certificate name can be of the form
+ // '*.example.com' and so doesn't have to be a domain name as such.
+ //
+ // Deprecated: NameToCertificate only allows associating a single
+ // certificate with a given name. Leave this field nil to let the library
+ // select the first compatible chain from Certificates.
+ NameToCertificate map[string]*Certificate
+
+ // 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) (*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 may be called multiple times for the same
+ // connection if renegotiation occurs or if TLS 1.3 is in use.
+ GetClientCertificate func(*CertificateRequestInfo) (*Certificate, error)
+
+ // GetConfigForClient, if not nil, is called after a ClientHello is
+ // received from a client. It may return a non-nil Config in order to
+ // change the Config that will be used to handle this connection. If
+ // the returned Config is nil, the original Config will be used. The
+ // Config returned by this callback may not be subsequently modified.
+ //
+ // If GetConfigForClient is nil, the Config passed to Server() will be
+ // used for all connections.
+ //
+ // If SessionTicketKey was explicitly set on the returned Config, or if
+ // SetSessionTicketKeys was called on the returned Config, those keys will
+ // be used. Otherwise, the original Config keys will be used (and possibly
+ // rotated if they are automatically managed).
+ GetConfigForClient func(*ClientHelloInfo) (*Config, error)
+
+ // VerifyPeerCertificate, if not nil, is called after normal
+ // certificate verification by either a TLS client or server. It
+ // receives the raw ASN.1 certificates provided by the peer and also
+ // any verified chains that normal processing found. 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 argument will always be nil.
+ VerifyPeerCertificate func(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error
+
+ // VerifyConnection, if not nil, is called after normal certificate
+ // verification 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(ConnectionState) error
+
+ // RootCAs defines the set of root certificate authorities
+ // that clients use when verifying server certificates.
+ // If RootCAs is nil, TLS uses the host's root CA set.
+ RootCAs *x509.CertPool
+
+ // NextProtos is a list of supported application level protocols, in
+ // order of preference. If both peers support ALPN, the selected
+ // protocol will be one from this list, and the connection will fail
+ // if there is no mutually supported protocol. If NextProtos is empty
+ // or the peer doesn't support ALPN, the connection will succeed and
+ // ConnectionState.NegotiatedProtocol will be empty.
+ NextProtos []string
+
+ // ServerName is used to verify the hostname on the returned
+ // certificates unless InsecureSkipVerify is given. It is also included
+ // in the client's handshake to support virtual hosting unless it is
+ // an IP address.
+ ServerName string
+
+ // ClientAuth determines the server's policy for
+ // TLS Client Authentication. The default is NoClientCert.
+ ClientAuth ClientAuthType
+
+ // 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
+
+ // InsecureSkipVerify controls whether a client verifies the server's
+ // certificate chain and host name. If InsecureSkipVerify is true, crypto/tls
+ // accepts any certificate presented by the server and any host name in that
+ // certificate. In this mode, TLS is susceptible to machine-in-the-middle
+ // attacks unless custom verification is used. This should be used only for
+ // testing or in combination with VerifyConnection or VerifyPeerCertificate.
+ InsecureSkipVerify bool
+
+ // CipherSuites is a list of enabled TLS 1.0–1.2 cipher suites. The order of
+ // the list is ignored. Note that TLS 1.3 ciphersuites are not configurable.
+ //
+ // If CipherSuites is nil, a safe default list is used. The default cipher
+ // suites might change over time.
+ CipherSuites []uint16
+
+ // PreferServerCipherSuites is a legacy field and has no effect.
+ //
+ // It used to control whether the server would follow the client's or the
+ // server's preference. Servers now select the best mutually supported
+ // cipher suite based on logic that takes into account inferred client
+ // hardware, server hardware, and security.
+ //
+ // Deprecated: PreferServerCipherSuites is ignored.
+ PreferServerCipherSuites bool
+
+ // SessionTicketsDisabled may be set to true to disable session ticket and
+ // PSK (resumption) support. Note that on clients, session ticket support is
+ // also disabled if ClientSessionCache is nil.
+ SessionTicketsDisabled bool
+
+ // SessionTicketKey is used by TLS servers to provide session resumption.
+ // See RFC 5077 and the PSK mode of RFC 8446. If zero, it will be filled
+ // with random data before the first server handshake.
+ //
+ // Deprecated: if this field is left at zero, session ticket keys will be
+ // automatically rotated every day and dropped after seven days. For
+ // customizing the rotation schedule or synchronizing servers that are
+ // terminating connections for the same host, use SetSessionTicketKeys.
+ SessionTicketKey [32]byte
+
+ // ClientSessionCache is a cache of ClientSessionState entries for TLS
+ // session resumption. It is only used by clients.
+ ClientSessionCache ClientSessionCache
+
+ // MinVersion contains the minimum TLS version that is acceptable.
+ //
+ // By default, TLS 1.2 is currently used as the minimum when acting as a
+ // client, and TLS 1.0 when acting as a server. TLS 1.0 is the minimum
+ // supported by this package, both as a client and as a server.
+ //
+ // The client-side default can temporarily be reverted to TLS 1.0 by
+ // including the value "x509sha1=1" in the GODEBUG environment variable.
+ // Note that this option will be removed in Go 1.19 (but it will still be
+ // possible to set this field to VersionTLS10 explicitly).
+ MinVersion uint16
+
+ // MaxVersion contains the maximum TLS version that is acceptable.
+ //
+ // By default, the maximum version supported by this package is used,
+ // which is currently TLS 1.3.
+ MaxVersion uint16
+
+ // CurvePreferences contains the elliptic curves that will be used in
+ // an ECDHE handshake, in preference order. If empty, the default will
+ // be used. The client will use the first preference as the type for
+ // its key share in TLS 1.3. This may change in the future.
+ CurvePreferences []CurveID
+
+ // DynamicRecordSizingDisabled disables adaptive sizing of TLS records.
+ // When true, the largest possible TLS record size is always used. When
+ // false, the size of TLS records may be adjusted in an attempt to
+ // improve latency.
+ DynamicRecordSizingDisabled bool
+
+ // Renegotiation controls what types of renegotiation are supported.
+ // The default, none, is correct for the vast majority of applications.
+ Renegotiation RenegotiationSupport
+
+ // 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
+
+ // mutex protects sessionTicketKeys and autoSessionTicketKeys.
+ mutex sync.RWMutex
+ // sessionTicketKeys contains zero or more ticket keys. If set, it means the
+ // the keys were set with SessionTicketKey or SetSessionTicketKeys. The
+ // first key is used for new tickets and any subsequent keys can be used to
+ // decrypt old tickets. The slice contents are not protected by the mutex
+ // and are immutable.
+ sessionTicketKeys []ticketKey
+ // autoSessionTicketKeys is like sessionTicketKeys but is owned by the
+ // auto-rotation logic. See Config.ticketKeys.
+ autoSessionTicketKeys []ticketKey
+}
+
+// A RecordLayer handles encrypting and decrypting of TLS messages.
+type RecordLayer interface {
+ SetReadKey(encLevel EncryptionLevel, suite *CipherSuiteTLS13, trafficSecret []byte)
+ SetWriteKey(encLevel EncryptionLevel, suite *CipherSuiteTLS13, trafficSecret []byte)
+ ReadHandshakeMessage() ([]byte, error)
+ WriteRecord([]byte) (int, error)
+ SendAlert(uint8)
+}
+
+type ExtraConfig struct {
+ // GetExtensions, if not nil, is called before a message that allows
+ // sending of extensions is sent.
+ // Currently only implemented for the ClientHello message (for the client)
+ // and for the EncryptedExtensions message (for the server).
+ // Only valid for TLS 1.3.
+ GetExtensions func(handshakeMessageType uint8) []Extension
+
+ // ReceivedExtensions, if not nil, is called when a message that allows the
+ // inclusion of extensions is received.
+ // It is called with an empty slice of extensions, if the message didn't
+ // contain any extensions.
+ // Currently only implemented for the ClientHello message (sent by the
+ // client) and for the EncryptedExtensions message (sent by the server).
+ // Only valid for TLS 1.3.
+ ReceivedExtensions func(handshakeMessageType uint8, exts []Extension)
+
+ // AlternativeRecordLayer is used by QUIC
+ AlternativeRecordLayer RecordLayer
+
+ // Enforce the selection of a supported application protocol.
+ // Only works for TLS 1.3.
+ // If enabled, client and server have to agree on an application protocol.
+ // Otherwise, connection establishment fails.
+ EnforceNextProtoSelection bool
+
+ // If MaxEarlyData is greater than 0, the client will be allowed to send early
+ // data when resuming a session.
+ // Requires the AlternativeRecordLayer to be set.
+ //
+ // It has no meaning on the client.
+ MaxEarlyData uint32
+
+ // The Accept0RTT callback is called when the client offers 0-RTT.
+ // The server then has to decide if it wants to accept or reject 0-RTT.
+ // It is only used for servers.
+ Accept0RTT func(appData []byte) bool
+
+ // 0RTTRejected is called when the server rejectes 0-RTT.
+ // It is only used for clients.
+ Rejected0RTT func()
+
+ // If set, the client will export the 0-RTT key when resuming a session that
+ // allows sending of early data.
+ // Requires the AlternativeRecordLayer to be set.
+ //
+ // It has no meaning to the server.
+ Enable0RTT bool
+
+ // Is called when the client saves a session ticket to the session ticket.
+ // This gives the application the opportunity to save some data along with the ticket,
+ // which can be restored when the session ticket is used.
+ GetAppDataForSessionState func() []byte
+
+ // Is called when the client uses a session ticket.
+ // Restores the application data that was saved earlier on GetAppDataForSessionTicket.
+ SetAppDataFromSessionState func([]byte)
+}
+
+// Clone clones.
+func (c *ExtraConfig) Clone() *ExtraConfig {
+ return &ExtraConfig{
+ GetExtensions: c.GetExtensions,
+ ReceivedExtensions: c.ReceivedExtensions,
+ AlternativeRecordLayer: c.AlternativeRecordLayer,
+ EnforceNextProtoSelection: c.EnforceNextProtoSelection,
+ MaxEarlyData: c.MaxEarlyData,
+ Enable0RTT: c.Enable0RTT,
+ Accept0RTT: c.Accept0RTT,
+ Rejected0RTT: c.Rejected0RTT,
+ GetAppDataForSessionState: c.GetAppDataForSessionState,
+ SetAppDataFromSessionState: c.SetAppDataFromSessionState,
+ }
+}
+
+func (c *ExtraConfig) usesAlternativeRecordLayer() bool {
+ return c != nil && c.AlternativeRecordLayer != nil
+}
+
+const (
+ // ticketKeyNameLen is the number of bytes of identifier that is prepended to
+ // an encrypted session ticket in order to identify the key used to encrypt it.
+ ticketKeyNameLen = 16
+
+ // ticketKeyLifetime is how long a ticket key remains valid and can be used to
+ // resume a client connection.
+ ticketKeyLifetime = 7 * 24 * time.Hour // 7 days
+
+ // ticketKeyRotation is how often the server should rotate the session ticket key
+ // that is used for new tickets.
+ ticketKeyRotation = 24 * time.Hour
+)
+
+// ticketKey is the internal representation of a session ticket key.
+type ticketKey struct {
+ // keyName is an opaque byte string that serves to identify the session
+ // ticket key. It's exposed as plaintext in every session ticket.
+ keyName [ticketKeyNameLen]byte
+ aesKey [16]byte
+ hmacKey [16]byte
+ // created is the time at which this ticket key was created. See Config.ticketKeys.
+ created time.Time
+}
+
+// ticketKeyFromBytes converts from the external representation of a session
+// ticket key to a ticketKey. Externally, session ticket keys are 32 random
+// bytes and this function expands that into sufficient name and key material.
+func (c *config) ticketKeyFromBytes(b [32]byte) (key ticketKey) {
+ hashed := sha512.Sum512(b[:])
+ copy(key.keyName[:], hashed[:ticketKeyNameLen])
+ copy(key.aesKey[:], hashed[ticketKeyNameLen:ticketKeyNameLen+16])
+ copy(key.hmacKey[:], hashed[ticketKeyNameLen+16:ticketKeyNameLen+32])
+ key.created = c.time()
+ return key
+}
+
+// maxSessionTicketLifetime is the maximum allowed lifetime of a TLS 1.3 session
+// ticket, and the lifetime we set for tickets we send.
+const maxSessionTicketLifetime = 7 * 24 * time.Hour
+
+// Clone returns a shallow clone of c or nil if c is nil. It is safe to clone a Config that is
+// being used concurrently by a TLS client or server.
+func (c *config) Clone() *config {
+ if c == nil {
+ return nil
+ }
+ c.mutex.RLock()
+ defer c.mutex.RUnlock()
+ return &config{
+ Rand: c.Rand,
+ Time: c.Time,
+ Certificates: c.Certificates,
+ NameToCertificate: c.NameToCertificate,
+ GetCertificate: c.GetCertificate,
+ GetClientCertificate: c.GetClientCertificate,
+ GetConfigForClient: c.GetConfigForClient,
+ VerifyPeerCertificate: c.VerifyPeerCertificate,
+ VerifyConnection: c.VerifyConnection,
+ RootCAs: c.RootCAs,
+ NextProtos: c.NextProtos,
+ ServerName: c.ServerName,
+ ClientAuth: c.ClientAuth,
+ ClientCAs: c.ClientCAs,
+ InsecureSkipVerify: c.InsecureSkipVerify,
+ CipherSuites: c.CipherSuites,
+ PreferServerCipherSuites: c.PreferServerCipherSuites,
+ SessionTicketsDisabled: c.SessionTicketsDisabled,
+ SessionTicketKey: c.SessionTicketKey,
+ ClientSessionCache: c.ClientSessionCache,
+ MinVersion: c.MinVersion,
+ MaxVersion: c.MaxVersion,
+ CurvePreferences: c.CurvePreferences,
+ DynamicRecordSizingDisabled: c.DynamicRecordSizingDisabled,
+ Renegotiation: c.Renegotiation,
+ KeyLogWriter: c.KeyLogWriter,
+ sessionTicketKeys: c.sessionTicketKeys,
+ autoSessionTicketKeys: c.autoSessionTicketKeys,
+ }
+}
+
+// deprecatedSessionTicketKey is set as the prefix of SessionTicketKey if it was
+// randomized for backwards compatibility but is not in use.
+var deprecatedSessionTicketKey = []byte("DEPRECATED")
+
+// initLegacySessionTicketKeyRLocked ensures the legacy SessionTicketKey field is
+// randomized if empty, and that sessionTicketKeys is populated from it otherwise.
+func (c *config) initLegacySessionTicketKeyRLocked() {
+ // Don't write if SessionTicketKey is already defined as our deprecated string,
+ // or if it is defined by the user but sessionTicketKeys is already set.
+ if c.SessionTicketKey != [32]byte{} &&
+ (bytes.HasPrefix(c.SessionTicketKey[:], deprecatedSessionTicketKey) || len(c.sessionTicketKeys) > 0) {
+ return
+ }
+
+ // We need to write some data, so get an exclusive lock and re-check any conditions.
+ c.mutex.RUnlock()
+ defer c.mutex.RLock()
+ c.mutex.Lock()
+ defer c.mutex.Unlock()
+ if c.SessionTicketKey == [32]byte{} {
+ if _, err := io.ReadFull(c.rand(), c.SessionTicketKey[:]); err != nil {
+ panic(fmt.Sprintf("tls: unable to generate random session ticket key: %v", err))
+ }
+ // Write the deprecated prefix at the beginning so we know we created
+ // it. This key with the DEPRECATED prefix isn't used as an actual
+ // session ticket key, and is only randomized in case the application
+ // reuses it for some reason.
+ copy(c.SessionTicketKey[:], deprecatedSessionTicketKey)
+ } else if !bytes.HasPrefix(c.SessionTicketKey[:], deprecatedSessionTicketKey) && len(c.sessionTicketKeys) == 0 {
+ c.sessionTicketKeys = []ticketKey{c.ticketKeyFromBytes(c.SessionTicketKey)}
+ }
+
+}
+
+// ticketKeys returns the ticketKeys for this connection.
+// If configForClient has explicitly set keys, those will
+// be returned. Otherwise, the keys on c will be used and
+// may be rotated if auto-managed.
+// During rotation, any expired session ticket keys are deleted from
+// c.sessionTicketKeys. If the session ticket key that is currently
+// encrypting tickets (ie. the first ticketKey in c.sessionTicketKeys)
+// is not fresh, then a new session ticket key will be
+// created and prepended to c.sessionTicketKeys.
+func (c *config) ticketKeys(configForClient *config) []ticketKey {
+ // If the ConfigForClient callback returned a Config with explicitly set
+ // keys, use those, otherwise just use the original Config.
+ if configForClient != nil {
+ configForClient.mutex.RLock()
+ if configForClient.SessionTicketsDisabled {
+ return nil
+ }
+ configForClient.initLegacySessionTicketKeyRLocked()
+ if len(configForClient.sessionTicketKeys) != 0 {
+ ret := configForClient.sessionTicketKeys
+ configForClient.mutex.RUnlock()
+ return ret
+ }
+ configForClient.mutex.RUnlock()
+ }
+
+ c.mutex.RLock()
+ defer c.mutex.RUnlock()
+ if c.SessionTicketsDisabled {
+ return nil
+ }
+ c.initLegacySessionTicketKeyRLocked()
+ if len(c.sessionTicketKeys) != 0 {
+ return c.sessionTicketKeys
+ }
+ // Fast path for the common case where the key is fresh enough.
+ if len(c.autoSessionTicketKeys) > 0 && c.time().Sub(c.autoSessionTicketKeys[0].created) < ticketKeyRotation {
+ return c.autoSessionTicketKeys
+ }
+
+ // autoSessionTicketKeys are managed by auto-rotation.
+ c.mutex.RUnlock()
+ defer c.mutex.RLock()
+ c.mutex.Lock()
+ defer c.mutex.Unlock()
+ // Re-check the condition in case it changed since obtaining the new lock.
+ if len(c.autoSessionTicketKeys) == 0 || c.time().Sub(c.autoSessionTicketKeys[0].created) >= ticketKeyRotation {
+ var newKey [32]byte
+ if _, err := io.ReadFull(c.rand(), newKey[:]); err != nil {
+ panic(fmt.Sprintf("unable to generate random session ticket key: %v", err))
+ }
+ valid := make([]ticketKey, 0, len(c.autoSessionTicketKeys)+1)
+ valid = append(valid, c.ticketKeyFromBytes(newKey))
+ for _, k := range c.autoSessionTicketKeys {
+ // While rotating the current key, also remove any expired ones.
+ if c.time().Sub(k.created) < ticketKeyLifetime {
+ valid = append(valid, k)
+ }
+ }
+ c.autoSessionTicketKeys = valid
+ }
+ return c.autoSessionTicketKeys
+}
+
+// SetSessionTicketKeys updates the session ticket keys for a server.
+//
+// The first key will be used when creating new tickets, while all keys can be
+// used for decrypting tickets. It is safe to call this function while the
+// server is running in order to rotate the session ticket keys. The function
+// will panic if keys is empty.
+//
+// Calling this function will turn off automatic session ticket key rotation.
+//
+// If multiple servers are terminating connections for the same host they should
+// all have the same session ticket keys. If the session ticket keys leaks,
+// previously recorded and future TLS connections using those keys might be
+// compromised.
+func (c *config) SetSessionTicketKeys(keys [][32]byte) {
+ if len(keys) == 0 {
+ panic("tls: keys must have at least one key")
+ }
+
+ newKeys := make([]ticketKey, len(keys))
+ for i, bytes := range keys {
+ newKeys[i] = c.ticketKeyFromBytes(bytes)
+ }
+
+ c.mutex.Lock()
+ c.sessionTicketKeys = newKeys
+ c.mutex.Unlock()
+}
+
+func (c *config) rand() io.Reader {
+ r := c.Rand
+ if r == nil {
+ return rand.Reader
+ }
+ return r
+}
+
+func (c *config) time() time.Time {
+ t := c.Time
+ if t == nil {
+ t = time.Now
+ }
+ return t()
+}
+
+func (c *config) cipherSuites() []uint16 {
+ if c.CipherSuites != nil {
+ return c.CipherSuites
+ }
+ return defaultCipherSuites
+}
+
+var supportedVersions = []uint16{
+ VersionTLS13,
+ VersionTLS12,
+ VersionTLS11,
+ VersionTLS10,
+}
+
+// debugEnableTLS10 enables TLS 1.0. See issue 45428.
+// We don't care about TLS1.0 in qtls. Always disable it.
+var debugEnableTLS10 = false
+
+// roleClient and roleServer are meant to call supportedVersions and parents
+// with more readability at the callsite.
+const roleClient = true
+const roleServer = false
+
+func (c *config) supportedVersions(isClient bool) []uint16 {
+ versions := make([]uint16, 0, len(supportedVersions))
+ for _, v := range supportedVersions {
+ if (c == nil || c.MinVersion == 0) && !debugEnableTLS10 &&
+ isClient && v < VersionTLS12 {
+ continue
+ }
+ if c != nil && c.MinVersion != 0 && v < c.MinVersion {
+ continue
+ }
+ if c != nil && c.MaxVersion != 0 && v > c.MaxVersion {
+ continue
+ }
+ versions = append(versions, v)
+ }
+ return versions
+}
+
+func (c *config) maxSupportedVersion(isClient bool) uint16 {
+ supportedVersions := c.supportedVersions(isClient)
+ if len(supportedVersions) == 0 {
+ return 0
+ }
+ return supportedVersions[0]
+}
+
+// supportedVersionsFromMax returns a list of supported versions derived from a
+// legacy maximum version value. Note that only versions supported by this
+// library are returned. Any newer peer will use supportedVersions anyway.
+func supportedVersionsFromMax(maxVersion uint16) []uint16 {
+ versions := make([]uint16, 0, len(supportedVersions))
+ for _, v := range supportedVersions {
+ if v > maxVersion {
+ continue
+ }
+ versions = append(versions, v)
+ }
+ return versions
+}
+
+var defaultCurvePreferences = []CurveID{X25519, CurveP256, CurveP384, CurveP521}
+
+func (c *config) curvePreferences() []CurveID {
+ if c == nil || len(c.CurvePreferences) == 0 {
+ return defaultCurvePreferences
+ }
+ return c.CurvePreferences
+}
+
+func (c *config) supportsCurve(curve CurveID) bool {
+ for _, cc := range c.curvePreferences() {
+ if cc == curve {
+ return true
+ }
+ }
+ return false
+}
+
+// mutualVersion returns the protocol version to use given the advertised
+// versions of the peer. Priority is given to the peer preference order.
+func (c *config) mutualVersion(isClient bool, peerVersions []uint16) (uint16, bool) {
+ supportedVersions := c.supportedVersions(isClient)
+ for _, peerVersion := range peerVersions {
+ for _, v := range supportedVersions {
+ if v == peerVersion {
+ return v, true
+ }
+ }
+ }
+ return 0, false
+}
+
+var errNoCertificates = errors.New("tls: no certificates configured")
+
+// getCertificate returns the best certificate for the given ClientHelloInfo,
+// defaulting to the first element of c.Certificates.
+func (c *config) getCertificate(clientHello *ClientHelloInfo) (*Certificate, error) {
+ if c.GetCertificate != nil &&
+ (len(c.Certificates) == 0 || len(clientHello.ServerName) > 0) {
+ cert, err := c.GetCertificate(clientHello)
+ if cert != nil || err != nil {
+ return cert, err
+ }
+ }
+
+ if len(c.Certificates) == 0 {
+ return nil, errNoCertificates
+ }
+
+ if len(c.Certificates) == 1 {
+ // There's only one choice, so no point doing any work.
+ return &c.Certificates[0], nil
+ }
+
+ if c.NameToCertificate != nil {
+ name := strings.ToLower(clientHello.ServerName)
+ if cert, ok := c.NameToCertificate[name]; ok {
+ return cert, nil
+ }
+ if len(name) > 0 {
+ labels := strings.Split(name, ".")
+ labels[0] = "*"
+ wildcardName := strings.Join(labels, ".")
+ if cert, ok := c.NameToCertificate[wildcardName]; ok {
+ return cert, nil
+ }
+ }
+ }
+
+ for _, cert := range c.Certificates {
+ if err := clientHello.SupportsCertificate(&cert); err == nil {
+ return &cert, nil
+ }
+ }
+
+ // If nothing matches, return the first certificate.
+ return &c.Certificates[0], nil
+}
+
+// SupportsCertificate returns nil if the provided certificate is supported by
+// the client that sent the ClientHello. Otherwise, it returns an error
+// describing the reason for the incompatibility.
+//
+// If this ClientHelloInfo was passed to a GetConfigForClient or GetCertificate
+// callback, this method will take into account the associated Config. Note that
+// if GetConfigForClient returns a different Config, the change can't be
+// accounted for by this method.
+//
+// This function will call x509.ParseCertificate unless c.Leaf is set, which can
+// incur a significant performance cost.
+func (chi *clientHelloInfo) SupportsCertificate(c *Certificate) error {
+ // Note we don't currently support certificate_authorities nor
+ // signature_algorithms_cert, and don't check the algorithms of the
+ // signatures on the chain (which anyway are a SHOULD, see RFC 8446,
+ // Section 4.4.2.2).
+
+ config := chi.config
+ if config == nil {
+ config = &Config{}
+ }
+ conf := fromConfig(config)
+ vers, ok := conf.mutualVersion(roleServer, chi.SupportedVersions)
+ if !ok {
+ return errors.New("no mutually supported protocol versions")
+ }
+
+ // If the client specified the name they are trying to connect to, the
+ // certificate needs to be valid for it.
+ if chi.ServerName != "" {
+ x509Cert, err := leafCertificate(c)
+ if err != nil {
+ return fmt.Errorf("failed to parse certificate: %w", err)
+ }
+ if err := x509Cert.VerifyHostname(chi.ServerName); err != nil {
+ return fmt.Errorf("certificate is not valid for requested server name: %w", err)
+ }
+ }
+
+ // supportsRSAFallback returns nil if the certificate and connection support
+ // the static RSA key exchange, and unsupported otherwise. The logic for
+ // supporting static RSA is completely disjoint from the logic for
+ // supporting signed key exchanges, so we just check it as a fallback.
+ supportsRSAFallback := func(unsupported error) error {
+ // TLS 1.3 dropped support for the static RSA key exchange.
+ if vers == VersionTLS13 {
+ return unsupported
+ }
+ // The static RSA key exchange works by decrypting a challenge with the
+ // RSA private key, not by signing, so check the PrivateKey implements
+ // crypto.Decrypter, like *rsa.PrivateKey does.
+ if priv, ok := c.PrivateKey.(crypto.Decrypter); ok {
+ if _, ok := priv.Public().(*rsa.PublicKey); !ok {
+ return unsupported
+ }
+ } else {
+ return unsupported
+ }
+ // Finally, there needs to be a mutual cipher suite that uses the static
+ // RSA key exchange instead of ECDHE.
+ rsaCipherSuite := selectCipherSuite(chi.CipherSuites, conf.cipherSuites(), func(c *cipherSuite) bool {
+ if c.flags&suiteECDHE != 0 {
+ return false
+ }
+ if vers < VersionTLS12 && c.flags&suiteTLS12 != 0 {
+ return false
+ }
+ return true
+ })
+ if rsaCipherSuite == nil {
+ return unsupported
+ }
+ return nil
+ }
+
+ // If the client sent the signature_algorithms extension, ensure it supports
+ // schemes we can use with this certificate and TLS version.
+ if len(chi.SignatureSchemes) > 0 {
+ if _, err := selectSignatureScheme(vers, c, chi.SignatureSchemes); err != nil {
+ return supportsRSAFallback(err)
+ }
+ }
+
+ // In TLS 1.3 we are done because supported_groups is only relevant to the
+ // ECDHE computation, point format negotiation is removed, cipher suites are
+ // only relevant to the AEAD choice, and static RSA does not exist.
+ if vers == VersionTLS13 {
+ return nil
+ }
+
+ // The only signed key exchange we support is ECDHE.
+ if !supportsECDHE(conf, chi.SupportedCurves, chi.SupportedPoints) {
+ return supportsRSAFallback(errors.New("client doesn't support ECDHE, can only use legacy RSA key exchange"))
+ }
+
+ var ecdsaCipherSuite bool
+ if priv, ok := c.PrivateKey.(crypto.Signer); ok {
+ switch pub := priv.Public().(type) {
+ case *ecdsa.PublicKey:
+ var curve CurveID
+ switch pub.Curve {
+ case elliptic.P256():
+ curve = CurveP256
+ case elliptic.P384():
+ curve = CurveP384
+ case elliptic.P521():
+ curve = CurveP521
+ default:
+ return supportsRSAFallback(unsupportedCertificateError(c))
+ }
+ var curveOk bool
+ for _, c := range chi.SupportedCurves {
+ if c == curve && conf.supportsCurve(c) {
+ curveOk = true
+ break
+ }
+ }
+ if !curveOk {
+ return errors.New("client doesn't support certificate curve")
+ }
+ ecdsaCipherSuite = true
+ case ed25519.PublicKey:
+ if vers < VersionTLS12 || len(chi.SignatureSchemes) == 0 {
+ return errors.New("connection doesn't support Ed25519")
+ }
+ ecdsaCipherSuite = true
+ case *rsa.PublicKey:
+ default:
+ return supportsRSAFallback(unsupportedCertificateError(c))
+ }
+ } else {
+ return supportsRSAFallback(unsupportedCertificateError(c))
+ }
+
+ // Make sure that there is a mutually supported cipher suite that works with
+ // this certificate. Cipher suite selection will then apply the logic in
+ // reverse to pick it. See also serverHandshakeState.cipherSuiteOk.
+ cipherSuite := selectCipherSuite(chi.CipherSuites, conf.cipherSuites(), func(c *cipherSuite) bool {
+ if c.flags&suiteECDHE == 0 {
+ return false
+ }
+ if c.flags&suiteECSign != 0 {
+ if !ecdsaCipherSuite {
+ return false
+ }
+ } else {
+ if ecdsaCipherSuite {
+ return false
+ }
+ }
+ if vers < VersionTLS12 && c.flags&suiteTLS12 != 0 {
+ return false
+ }
+ return true
+ })
+ if cipherSuite == nil {
+ return supportsRSAFallback(errors.New("client doesn't support any cipher suites compatible with the certificate"))
+ }
+
+ return nil
+}
+
+// BuildNameToCertificate parses c.Certificates and builds c.NameToCertificate
+// from the CommonName and SubjectAlternateName fields of each of the leaf
+// certificates.
+//
+// Deprecated: NameToCertificate only allows associating a single certificate
+// with a given name. Leave that field nil to let the library select the first
+// compatible chain from Certificates.
+func (c *config) BuildNameToCertificate() {
+ c.NameToCertificate = make(map[string]*Certificate)
+ for i := range c.Certificates {
+ cert := &c.Certificates[i]
+ x509Cert, err := leafCertificate(cert)
+ if err != nil {
+ continue
+ }
+ // If SANs are *not* present, some clients will consider the certificate
+ // valid for the name in the Common Name.
+ if x509Cert.Subject.CommonName != "" && len(x509Cert.DNSNames) == 0 {
+ c.NameToCertificate[x509Cert.Subject.CommonName] = cert
+ }
+ for _, san := range x509Cert.DNSNames {
+ c.NameToCertificate[san] = cert
+ }
+ }
+}
+
+const (
+ keyLogLabelTLS12 = "CLIENT_RANDOM"
+ keyLogLabelEarlyTraffic = "CLIENT_EARLY_TRAFFIC_SECRET"
+ keyLogLabelClientHandshake = "CLIENT_HANDSHAKE_TRAFFIC_SECRET"
+ keyLogLabelServerHandshake = "SERVER_HANDSHAKE_TRAFFIC_SECRET"
+ keyLogLabelClientTraffic = "CLIENT_TRAFFIC_SECRET_0"
+ keyLogLabelServerTraffic = "SERVER_TRAFFIC_SECRET_0"
+)
+
+func (c *config) writeKeyLog(label string, clientRandom, secret []byte) error {
+ if c.KeyLogWriter == nil {
+ return nil
+ }
+
+ logLine := []byte(fmt.Sprintf("%s %x %x\n", label, clientRandom, secret))
+
+ writerMutex.Lock()
+ _, err := c.KeyLogWriter.Write(logLine)
+ writerMutex.Unlock()
+
+ return err
+}
+
+// writerMutex protects all KeyLogWriters globally. It is rarely enabled,
+// and is only for debugging, so a global mutex saves space.
+var writerMutex sync.Mutex
+
+// A Certificate is a chain of one or more certificates, leaf first.
+type Certificate = tls.Certificate
+
+// leaf returns the parsed leaf certificate, either from c.Leaf or by parsing
+// the corresponding c.Certificate[0].
+func leafCertificate(c *Certificate) (*x509.Certificate, error) {
+ if c.Leaf != nil {
+ return c.Leaf, nil
+ }
+ return x509.ParseCertificate(c.Certificate[0])
+}
+
+type handshakeMessage interface {
+ marshal() []byte
+ unmarshal([]byte) bool
+}
+
+// lruSessionCache is a ClientSessionCache implementation that uses an LRU
+// caching strategy.
+type lruSessionCache struct {
+ sync.Mutex
+
+ m map[string]*list.Element
+ q *list.List
+ capacity int
+}
+
+type lruSessionCacheEntry struct {
+ sessionKey string
+ state *ClientSessionState
+}
+
+// NewLRUClientSessionCache returns a ClientSessionCache with the given
+// capacity that uses an LRU strategy. If capacity is < 1, a default capacity
+// is used instead.
+func NewLRUClientSessionCache(capacity int) ClientSessionCache {
+ const defaultSessionCacheCapacity = 64
+
+ if capacity < 1 {
+ capacity = defaultSessionCacheCapacity
+ }
+ return &lruSessionCache{
+ m: make(map[string]*list.Element),
+ q: list.New(),
+ capacity: capacity,
+ }
+}
+
+// Put adds the provided (sessionKey, cs) pair to the cache. If cs is nil, the entry
+// corresponding to sessionKey is removed from the cache instead.
+func (c *lruSessionCache) Put(sessionKey string, cs *ClientSessionState) {
+ c.Lock()
+ defer c.Unlock()
+
+ if elem, ok := c.m[sessionKey]; ok {
+ if cs == nil {
+ c.q.Remove(elem)
+ delete(c.m, sessionKey)
+ } else {
+ entry := elem.Value.(*lruSessionCacheEntry)
+ entry.state = cs
+ c.q.MoveToFront(elem)
+ }
+ return
+ }
+
+ if c.q.Len() < c.capacity {
+ entry := &lruSessionCacheEntry{sessionKey, cs}
+ c.m[sessionKey] = c.q.PushFront(entry)
+ return
+ }
+
+ elem := c.q.Back()
+ entry := elem.Value.(*lruSessionCacheEntry)
+ delete(c.m, entry.sessionKey)
+ entry.sessionKey = sessionKey
+ entry.state = cs
+ c.q.MoveToFront(elem)
+ c.m[sessionKey] = elem
+}
+
+// Get returns the ClientSessionState value associated with a given key. It
+// returns (nil, false) if no value is found.
+func (c *lruSessionCache) Get(sessionKey string) (*ClientSessionState, bool) {
+ c.Lock()
+ defer c.Unlock()
+
+ if elem, ok := c.m[sessionKey]; ok {
+ c.q.MoveToFront(elem)
+ return elem.Value.(*lruSessionCacheEntry).state, true
+ }
+ return nil, false
+}
+
+var emptyConfig Config
+
+func defaultConfig() *Config {
+ return &emptyConfig
+}
+
+func unexpectedMessageError(wanted, got any) error {
+ return fmt.Errorf("tls: received unexpected handshake message of type %T when waiting for %T", got, wanted)
+}
+
+func isSupportedSignatureAlgorithm(sigAlg SignatureScheme, supportedSignatureAlgorithms []SignatureScheme) bool {
+ for _, s := range supportedSignatureAlgorithms {
+ if s == sigAlg {
+ return true
+ }
+ }
+ return false
+}
diff --git a/vendor/github.com/quic-go/qtls-go1-18/conn.go b/vendor/github.com/quic-go/qtls-go1-18/conn.go
new file mode 100644
index 0000000000..2b8c7307e9
--- /dev/null
+++ b/vendor/github.com/quic-go/qtls-go1-18/conn.go
@@ -0,0 +1,1617 @@
+// Copyright 2010 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// TLS low level connection and record layer
+
+package qtls
+
+import (
+ "bytes"
+ "context"
+ "crypto/cipher"
+ "crypto/subtle"
+ "crypto/x509"
+ "errors"
+ "fmt"
+ "hash"
+ "io"
+ "net"
+ "sync"
+ "sync/atomic"
+ "time"
+)
+
+// A Conn represents a secured connection.
+// It implements the net.Conn interface.
+type Conn struct {
+ // constant
+ conn net.Conn
+ isClient bool
+ handshakeFn func(context.Context) error // (*Conn).clientHandshake or serverHandshake
+
+ // handshakeStatus is 1 if the connection is currently transferring
+ // application data (i.e. is not currently processing a handshake).
+ // handshakeStatus == 1 implies handshakeErr == nil.
+ // This field is only to be accessed with sync/atomic.
+ handshakeStatus uint32
+ // constant after handshake; protected by handshakeMutex
+ handshakeMutex sync.Mutex
+ handshakeErr error // error resulting from handshake
+ vers uint16 // TLS version
+ haveVers bool // version has been negotiated
+ config *config // configuration passed to constructor
+ // handshakes counts the number of handshakes performed on the
+ // connection so far. If renegotiation is disabled then this is either
+ // zero or one.
+ extraConfig *ExtraConfig
+
+ handshakes int
+ didResume bool // whether this connection was a session resumption
+ cipherSuite uint16
+ ocspResponse []byte // stapled OCSP response
+ scts [][]byte // signed certificate timestamps from server
+ peerCertificates []*x509.Certificate
+ // verifiedChains contains the certificate chains that we built, as
+ // opposed to the ones presented by the server.
+ verifiedChains [][]*x509.Certificate
+ // serverName contains the server name indicated by the client, if any.
+ serverName string
+ // secureRenegotiation is true if the server echoed the secure
+ // renegotiation extension. (This is meaningless as a server because
+ // renegotiation is not supported in that case.)
+ secureRenegotiation bool
+ // ekm is a closure for exporting keying material.
+ ekm func(label string, context []byte, length int) ([]byte, error)
+ // For the client:
+ // resumptionSecret is the resumption_master_secret for handling
+ // NewSessionTicket messages. nil if config.SessionTicketsDisabled.
+ // For the server:
+ // resumptionSecret is the resumption_master_secret for generating
+ // NewSessionTicket messages. Only used when the alternative record
+ // layer is set. nil if config.SessionTicketsDisabled.
+ resumptionSecret []byte
+
+ // ticketKeys is the set of active session ticket keys for this
+ // connection. The first one is used to encrypt new tickets and
+ // all are tried to decrypt tickets.
+ ticketKeys []ticketKey
+
+ // clientFinishedIsFirst is true if the client sent the first Finished
+ // message during the most recent handshake. This is recorded because
+ // the first transmitted Finished message is the tls-unique
+ // channel-binding value.
+ clientFinishedIsFirst bool
+
+ // closeNotifyErr is any error from sending the alertCloseNotify record.
+ closeNotifyErr error
+ // closeNotifySent is true if the Conn attempted to send an
+ // alertCloseNotify record.
+ closeNotifySent bool
+
+ // clientFinished and serverFinished contain the Finished message sent
+ // by the client or server in the most recent handshake. This is
+ // retained to support the renegotiation extension and tls-unique
+ // channel-binding.
+ clientFinished [12]byte
+ serverFinished [12]byte
+
+ // clientProtocol is the negotiated ALPN protocol.
+ clientProtocol string
+
+ // input/output
+ in, out halfConn
+ rawInput bytes.Buffer // raw input, starting with a record header
+ input bytes.Reader // application data waiting to be read, from rawInput.Next
+ hand bytes.Buffer // handshake data waiting to be read
+ buffering bool // whether records are buffered in sendBuf
+ sendBuf []byte // a buffer of records waiting to be sent
+
+ // bytesSent counts the bytes of application data sent.
+ // packetsSent counts packets.
+ bytesSent int64
+ packetsSent int64
+
+ // retryCount counts the number of consecutive non-advancing records
+ // received by Conn.readRecord. That is, records that neither advance the
+ // handshake, nor deliver application data. Protected by in.Mutex.
+ retryCount int
+
+ // activeCall is an atomic int32; the low bit is whether Close has
+ // been called. the rest of the bits are the number of goroutines
+ // in Conn.Write.
+ activeCall int32
+
+ used0RTT bool
+
+ tmp [16]byte
+
+ connStateMutex sync.Mutex
+ connState ConnectionStateWith0RTT
+}
+
+// Access to net.Conn methods.
+// Cannot just embed net.Conn because that would
+// export the struct field too.
+
+// LocalAddr returns the local network address.
+func (c *Conn) LocalAddr() net.Addr {
+ return c.conn.LocalAddr()
+}
+
+// RemoteAddr returns the remote network address.
+func (c *Conn) RemoteAddr() net.Addr {
+ return c.conn.RemoteAddr()
+}
+
+// SetDeadline sets the read and write deadlines associated with the connection.
+// A zero value for t means Read and Write will not time out.
+// After a Write has timed out, the TLS state is corrupt and all future writes will return the same error.
+func (c *Conn) SetDeadline(t time.Time) error {
+ return c.conn.SetDeadline(t)
+}
+
+// SetReadDeadline sets the read deadline on the underlying connection.
+// A zero value for t means Read will not time out.
+func (c *Conn) SetReadDeadline(t time.Time) error {
+ return c.conn.SetReadDeadline(t)
+}
+
+// SetWriteDeadline sets the write deadline on the underlying connection.
+// A zero value for t means Write will not time out.
+// After a Write has timed out, the TLS state is corrupt and all future writes will return the same error.
+func (c *Conn) SetWriteDeadline(t time.Time) error {
+ return c.conn.SetWriteDeadline(t)
+}
+
+// NetConn returns the underlying connection that is wrapped by c.
+// Note that writing to or reading from this connection directly will corrupt the
+// TLS session.
+func (c *Conn) NetConn() net.Conn {
+ return c.conn
+}
+
+// A halfConn represents one direction of the record layer
+// connection, either sending or receiving.
+type halfConn struct {
+ sync.Mutex
+
+ err error // first permanent error
+ version uint16 // protocol version
+ cipher any // cipher algorithm
+ mac hash.Hash
+ seq [8]byte // 64-bit sequence number
+
+ scratchBuf [13]byte // to avoid allocs; interface method args escape
+
+ nextCipher any // next encryption state
+ nextMac hash.Hash // next MAC algorithm
+
+ trafficSecret []byte // current TLS 1.3 traffic secret
+
+ setKeyCallback func(encLevel EncryptionLevel, suite *CipherSuiteTLS13, trafficSecret []byte)
+}
+
+type permanentError struct {
+ err net.Error
+}
+
+func (e *permanentError) Error() string { return e.err.Error() }
+func (e *permanentError) Unwrap() error { return e.err }
+func (e *permanentError) Timeout() bool { return e.err.Timeout() }
+func (e *permanentError) Temporary() bool { return false }
+
+func (hc *halfConn) setErrorLocked(err error) error {
+ if e, ok := err.(net.Error); ok {
+ hc.err = &permanentError{err: e}
+ } else {
+ hc.err = err
+ }
+ return hc.err
+}
+
+// prepareCipherSpec sets the encryption and MAC states
+// that a subsequent changeCipherSpec will use.
+func (hc *halfConn) prepareCipherSpec(version uint16, cipher any, mac hash.Hash) {
+ hc.version = version
+ hc.nextCipher = cipher
+ hc.nextMac = mac
+}
+
+// changeCipherSpec changes the encryption and MAC states
+// to the ones previously passed to prepareCipherSpec.
+func (hc *halfConn) changeCipherSpec() error {
+ if hc.nextCipher == nil || hc.version == VersionTLS13 {
+ return alertInternalError
+ }
+ hc.cipher = hc.nextCipher
+ hc.mac = hc.nextMac
+ hc.nextCipher = nil
+ hc.nextMac = nil
+ for i := range hc.seq {
+ hc.seq[i] = 0
+ }
+ return nil
+}
+
+func (hc *halfConn) exportKey(encLevel EncryptionLevel, suite *cipherSuiteTLS13, trafficSecret []byte) {
+ if hc.setKeyCallback != nil {
+ s := &CipherSuiteTLS13{
+ ID: suite.id,
+ KeyLen: suite.keyLen,
+ Hash: suite.hash,
+ AEAD: func(key, fixedNonce []byte) cipher.AEAD { return suite.aead(key, fixedNonce) },
+ }
+ hc.setKeyCallback(encLevel, s, trafficSecret)
+ }
+}
+
+func (hc *halfConn) setTrafficSecret(suite *cipherSuiteTLS13, secret []byte) {
+ hc.trafficSecret = secret
+ key, iv := suite.trafficKey(secret)
+ hc.cipher = suite.aead(key, iv)
+ for i := range hc.seq {
+ hc.seq[i] = 0
+ }
+}
+
+// incSeq increments the sequence number.
+func (hc *halfConn) incSeq() {
+ for i := 7; i >= 0; i-- {
+ hc.seq[i]++
+ if hc.seq[i] != 0 {
+ return
+ }
+ }
+
+ // Not allowed to let sequence number wrap.
+ // Instead, must renegotiate before it does.
+ // Not likely enough to bother.
+ panic("TLS: sequence number wraparound")
+}
+
+// explicitNonceLen returns the number of bytes of explicit nonce or IV included
+// in each record. Explicit nonces are present only in CBC modes after TLS 1.0
+// and in certain AEAD modes in TLS 1.2.
+func (hc *halfConn) explicitNonceLen() int {
+ if hc.cipher == nil {
+ return 0
+ }
+
+ switch c := hc.cipher.(type) {
+ case cipher.Stream:
+ return 0
+ case aead:
+ return c.explicitNonceLen()
+ case cbcMode:
+ // TLS 1.1 introduced a per-record explicit IV to fix the BEAST attack.
+ if hc.version >= VersionTLS11 {
+ return c.BlockSize()
+ }
+ return 0
+ default:
+ panic("unknown cipher type")
+ }
+}
+
+// extractPadding 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.
+func extractPadding(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)
+
+ // Zero the padding length on error. This ensures any unchecked bytes
+ // are included in the MAC. Otherwise, an attacker that could
+ // distinguish MAC failures from padding failures could mount an attack
+ // similar to POODLE in SSL 3.0: given a good ciphertext that uses a
+ // full block's worth of padding, replace the final block with another
+ // block. If the MAC check passed but the padding check failed, the
+ // last byte of that block decrypted to the block size.
+ //
+ // See also macAndPaddingGood logic below.
+ paddingLen &= good
+
+ toRemove = int(paddingLen) + 1
+ return
+}
+
+func roundUp(a, b int) int {
+ return a + (b-a%b)%b
+}
+
+// cbcMode is an interface for block ciphers using cipher block chaining.
+type cbcMode interface {
+ cipher.BlockMode
+ SetIV([]byte)
+}
+
+// decrypt authenticates and decrypts the record if protection is active at
+// this stage. The returned plaintext might overlap with the input.
+func (hc *halfConn) decrypt(record []byte) ([]byte, recordType, error) {
+ var plaintext []byte
+ typ := recordType(record[0])
+ payload := record[recordHeaderLen:]
+
+ // In TLS 1.3, change_cipher_spec messages are to be ignored without being
+ // decrypted. See RFC 8446, Appendix D.4.
+ if hc.version == VersionTLS13 && typ == recordTypeChangeCipherSpec {
+ return payload, typ, nil
+ }
+
+ paddingGood := byte(255)
+ paddingLen := 0
+
+ explicitNonceLen := hc.explicitNonceLen()
+
+ if hc.cipher != nil {
+ switch c := hc.cipher.(type) {
+ case cipher.Stream:
+ c.XORKeyStream(payload, payload)
+ case aead:
+ if len(payload) < explicitNonceLen {
+ return nil, 0, alertBadRecordMAC
+ }
+ nonce := payload[:explicitNonceLen]
+ if len(nonce) == 0 {
+ nonce = hc.seq[:]
+ }
+ payload = payload[explicitNonceLen:]
+
+ var additionalData []byte
+ if hc.version == VersionTLS13 {
+ additionalData = record[:recordHeaderLen]
+ } else {
+ additionalData = append(hc.scratchBuf[:0], hc.seq[:]...)
+ additionalData = append(additionalData, record[:3]...)
+ n := len(payload) - c.Overhead()
+ additionalData = append(additionalData, byte(n>>8), byte(n))
+ }
+
+ var err error
+ plaintext, err = c.Open(payload[:0], nonce, payload, additionalData)
+ if err != nil {
+ return nil, 0, alertBadRecordMAC
+ }
+ case cbcMode:
+ blockSize := c.BlockSize()
+ minPayload := explicitNonceLen + roundUp(hc.mac.Size()+1, blockSize)
+ if len(payload)%blockSize != 0 || len(payload) < minPayload {
+ return nil, 0, alertBadRecordMAC
+ }
+
+ if explicitNonceLen > 0 {
+ c.SetIV(payload[:explicitNonceLen])
+ payload = payload[explicitNonceLen:]
+ }
+ c.CryptBlocks(payload, payload)
+
+ // In a limited attempt to protect against CBC padding oracles like
+ // Lucky13, the data past paddingLen (which is secret) is passed to
+ // the MAC function as extra data, to be fed into the HMAC after
+ // computing the digest. This makes the MAC roughly constant time as
+ // long as the digest computation is constant time and does not
+ // affect the subsequent write, modulo cache effects.
+ paddingLen, paddingGood = extractPadding(payload)
+ default:
+ panic("unknown cipher type")
+ }
+
+ if hc.version == VersionTLS13 {
+ if typ != recordTypeApplicationData {
+ return nil, 0, alertUnexpectedMessage
+ }
+ if len(plaintext) > maxPlaintext+1 {
+ return nil, 0, alertRecordOverflow
+ }
+ // Remove padding and find the ContentType scanning from the end.
+ for i := len(plaintext) - 1; i >= 0; i-- {
+ if plaintext[i] != 0 {
+ typ = recordType(plaintext[i])
+ plaintext = plaintext[:i]
+ break
+ }
+ if i == 0 {
+ return nil, 0, alertUnexpectedMessage
+ }
+ }
+ }
+ } else {
+ plaintext = payload
+ }
+
+ if hc.mac != nil {
+ macSize := hc.mac.Size()
+ if len(payload) < macSize {
+ return nil, 0, alertBadRecordMAC
+ }
+
+ n := len(payload) - macSize - paddingLen
+ n = subtle.ConstantTimeSelect(int(uint32(n)>>31), 0, n) // if n < 0 { n = 0 }
+ record[3] = byte(n >> 8)
+ record[4] = byte(n)
+ remoteMAC := payload[n : n+macSize]
+ localMAC := tls10MAC(hc.mac, hc.scratchBuf[:0], hc.seq[:], record[:recordHeaderLen], payload[:n], payload[n+macSize:])
+
+ // This is equivalent to checking the MACs and paddingGood
+ // separately, but in constant-time to prevent distinguishing
+ // padding failures from MAC failures. Depending on what value
+ // of paddingLen was returned on bad padding, distinguishing
+ // bad MAC from bad padding can lead to an attack.
+ //
+ // See also the logic at the end of extractPadding.
+ macAndPaddingGood := subtle.ConstantTimeCompare(localMAC, remoteMAC) & int(paddingGood)
+ if macAndPaddingGood != 1 {
+ return nil, 0, alertBadRecordMAC
+ }
+
+ plaintext = payload[:n]
+ }
+
+ hc.incSeq()
+ return plaintext, typ, nil
+}
+
+func (c *Conn) setAlternativeRecordLayer() {
+ if c.extraConfig != nil && c.extraConfig.AlternativeRecordLayer != nil {
+ c.in.setKeyCallback = c.extraConfig.AlternativeRecordLayer.SetReadKey
+ c.out.setKeyCallback = c.extraConfig.AlternativeRecordLayer.SetWriteKey
+ }
+}
+
+// sliceForAppend extends the input slice by n bytes. head is the full extended
+// slice, while tail is the appended part. If the original slice has sufficient
+// capacity no allocation is performed.
+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
+}
+
+// encrypt encrypts payload, adding the appropriate nonce and/or MAC, and
+// appends it to record, which must already contain the record header.
+func (hc *halfConn) encrypt(record, payload []byte, rand io.Reader) ([]byte, error) {
+ if hc.cipher == nil {
+ return append(record, payload...), nil
+ }
+
+ var explicitNonce []byte
+ if explicitNonceLen := hc.explicitNonceLen(); explicitNonceLen > 0 {
+ record, explicitNonce = sliceForAppend(record, explicitNonceLen)
+ if _, isCBC := hc.cipher.(cbcMode); !isCBC && explicitNonceLen < 16 {
+ // The AES-GCM construction in TLS has an explicit nonce so that the
+ // nonce can be random. However, the nonce is only 8 bytes which is
+ // too small for a secure, random nonce. Therefore we use the
+ // sequence number as the nonce. The 3DES-CBC construction also has
+ // an 8 bytes nonce but its nonces must be unpredictable (see RFC
+ // 5246, Appendix F.3), forcing us to use randomness. That's not
+ // 3DES' biggest problem anyway because the birthday bound on block
+ // collision is reached first due to its similarly small block size
+ // (see the Sweet32 attack).
+ copy(explicitNonce, hc.seq[:])
+ } else {
+ if _, err := io.ReadFull(rand, explicitNonce); err != nil {
+ return nil, err
+ }
+ }
+ }
+
+ var dst []byte
+ switch c := hc.cipher.(type) {
+ case cipher.Stream:
+ mac := tls10MAC(hc.mac, hc.scratchBuf[:0], hc.seq[:], record[:recordHeaderLen], payload, nil)
+ record, dst = sliceForAppend(record, len(payload)+len(mac))
+ c.XORKeyStream(dst[:len(payload)], payload)
+ c.XORKeyStream(dst[len(payload):], mac)
+ case aead:
+ nonce := explicitNonce
+ if len(nonce) == 0 {
+ nonce = hc.seq[:]
+ }
+
+ if hc.version == VersionTLS13 {
+ record = append(record, payload...)
+
+ // Encrypt the actual ContentType and replace the plaintext one.
+ record = append(record, record[0])
+ record[0] = byte(recordTypeApplicationData)
+
+ n := len(payload) + 1 + c.Overhead()
+ record[3] = byte(n >> 8)
+ record[4] = byte(n)
+
+ record = c.Seal(record[:recordHeaderLen],
+ nonce, record[recordHeaderLen:], record[:recordHeaderLen])
+ } else {
+ additionalData := append(hc.scratchBuf[:0], hc.seq[:]...)
+ additionalData = append(additionalData, record[:recordHeaderLen]...)
+ record = c.Seal(record, nonce, payload, additionalData)
+ }
+ case cbcMode:
+ mac := tls10MAC(hc.mac, hc.scratchBuf[:0], hc.seq[:], record[:recordHeaderLen], payload, nil)
+ blockSize := c.BlockSize()
+ plaintextLen := len(payload) + len(mac)
+ paddingLen := blockSize - plaintextLen%blockSize
+ record, dst = sliceForAppend(record, plaintextLen+paddingLen)
+ copy(dst, payload)
+ copy(dst[len(payload):], mac)
+ for i := plaintextLen; i < len(dst); i++ {
+ dst[i] = byte(paddingLen - 1)
+ }
+ if len(explicitNonce) > 0 {
+ c.SetIV(explicitNonce)
+ }
+ c.CryptBlocks(dst, dst)
+ default:
+ panic("unknown cipher type")
+ }
+
+ // Update length to include nonce, MAC and any block padding needed.
+ n := len(record) - recordHeaderLen
+ record[3] = byte(n >> 8)
+ record[4] = byte(n)
+ hc.incSeq()
+
+ return record, nil
+}
+
+// RecordHeaderError is returned when a TLS record header is invalid.
+type RecordHeaderError struct {
+ // Msg contains a human readable string that describes the error.
+ Msg string
+ // RecordHeader contains the five bytes of TLS record header that
+ // triggered the error.
+ RecordHeader [5]byte
+ // Conn provides the underlying net.Conn in the case that a client
+ // sent an initial handshake that didn't look like TLS.
+ // It is nil if there's already been a handshake or a TLS alert has
+ // been written to the connection.
+ Conn net.Conn
+}
+
+func (e RecordHeaderError) Error() string { return "tls: " + e.Msg }
+
+func (c *Conn) newRecordHeaderError(conn net.Conn, msg string) (err RecordHeaderError) {
+ err.Msg = msg
+ err.Conn = conn
+ copy(err.RecordHeader[:], c.rawInput.Bytes())
+ return err
+}
+
+func (c *Conn) readRecord() error {
+ return c.readRecordOrCCS(false)
+}
+
+func (c *Conn) readChangeCipherSpec() error {
+ return c.readRecordOrCCS(true)
+}
+
+// readRecordOrCCS reads one or more TLS records from the connection and
+// updates the record layer state. Some invariants:
+// * c.in must be locked
+// * c.input must be empty
+// During the handshake one and only one of the following will happen:
+// - c.hand grows
+// - c.in.changeCipherSpec is called
+// - an error is returned
+// After the handshake one and only one of the following will happen:
+// - c.hand grows
+// - c.input is set
+// - an error is returned
+func (c *Conn) readRecordOrCCS(expectChangeCipherSpec bool) error {
+ if c.in.err != nil {
+ return c.in.err
+ }
+ handshakeComplete := c.handshakeComplete()
+
+ // This function modifies c.rawInput, which owns the c.input memory.
+ if c.input.Len() != 0 {
+ return c.in.setErrorLocked(errors.New("tls: internal error: attempted to read record with pending application data"))
+ }
+ c.input.Reset(nil)
+
+ // Read header, payload.
+ if err := c.readFromUntil(c.conn, recordHeaderLen); err != nil {
+ // RFC 8446, Section 6.1 suggests that EOF without an alertCloseNotify
+ // is an error, but popular web sites seem to do this, so we accept it
+ // if and only if at the record boundary.
+ if err == io.ErrUnexpectedEOF && c.rawInput.Len() == 0 {
+ err = io.EOF
+ }
+ if e, ok := err.(net.Error); !ok || !e.Temporary() {
+ c.in.setErrorLocked(err)
+ }
+ return err
+ }
+ hdr := c.rawInput.Bytes()[:recordHeaderLen]
+ typ := recordType(hdr[0])
+
+ // No valid TLS record has a type of 0x80, however SSLv2 handshakes
+ // start with a uint16 length where the MSB is set and the first record
+ // is always < 256 bytes long. Therefore typ == 0x80 strongly suggests
+ // an SSLv2 client.
+ if !handshakeComplete && typ == 0x80 {
+ c.sendAlert(alertProtocolVersion)
+ return c.in.setErrorLocked(c.newRecordHeaderError(nil, "unsupported SSLv2 handshake received"))
+ }
+
+ vers := uint16(hdr[1])<<8 | uint16(hdr[2])
+ n := int(hdr[3])<<8 | int(hdr[4])
+ if c.haveVers && c.vers != VersionTLS13 && vers != c.vers {
+ c.sendAlert(alertProtocolVersion)
+ msg := fmt.Sprintf("received record with version %x when expecting version %x", vers, c.vers)
+ return c.in.setErrorLocked(c.newRecordHeaderError(nil, msg))
+ }
+ if !c.haveVers {
+ // First message, be extra suspicious: this might not be a TLS
+ // client. Bail out before reading a full 'body', if possible.
+ // The current max version is 3.3 so if the version is >= 16.0,
+ // it's probably not real.
+ if (typ != recordTypeAlert && typ != recordTypeHandshake) || vers >= 0x1000 {
+ return c.in.setErrorLocked(c.newRecordHeaderError(c.conn, "first record does not look like a TLS handshake"))
+ }
+ }
+ if c.vers == VersionTLS13 && n > maxCiphertextTLS13 || n > maxCiphertext {
+ c.sendAlert(alertRecordOverflow)
+ msg := fmt.Sprintf("oversized record received with length %d", n)
+ return c.in.setErrorLocked(c.newRecordHeaderError(nil, msg))
+ }
+ if err := c.readFromUntil(c.conn, recordHeaderLen+n); err != nil {
+ if e, ok := err.(net.Error); !ok || !e.Temporary() {
+ c.in.setErrorLocked(err)
+ }
+ return err
+ }
+
+ // Process message.
+ record := c.rawInput.Next(recordHeaderLen + n)
+ data, typ, err := c.in.decrypt(record)
+ if err != nil {
+ return c.in.setErrorLocked(c.sendAlert(err.(alert)))
+ }
+ if len(data) > maxPlaintext {
+ return c.in.setErrorLocked(c.sendAlert(alertRecordOverflow))
+ }
+
+ // Application Data messages are always protected.
+ if c.in.cipher == nil && typ == recordTypeApplicationData {
+ return c.in.setErrorLocked(c.sendAlert(alertUnexpectedMessage))
+ }
+
+ if typ != recordTypeAlert && typ != recordTypeChangeCipherSpec && len(data) > 0 {
+ // This is a state-advancing message: reset the retry count.
+ c.retryCount = 0
+ }
+
+ // Handshake messages MUST NOT be interleaved with other record types in TLS 1.3.
+ if c.vers == VersionTLS13 && typ != recordTypeHandshake && c.hand.Len() > 0 {
+ return c.in.setErrorLocked(c.sendAlert(alertUnexpectedMessage))
+ }
+
+ switch typ {
+ default:
+ return c.in.setErrorLocked(c.sendAlert(alertUnexpectedMessage))
+
+ case recordTypeAlert:
+ if len(data) != 2 {
+ return c.in.setErrorLocked(c.sendAlert(alertUnexpectedMessage))
+ }
+ if alert(data[1]) == alertCloseNotify {
+ return c.in.setErrorLocked(io.EOF)
+ }
+ if c.vers == VersionTLS13 {
+ return c.in.setErrorLocked(&net.OpError{Op: "remote error", Err: alert(data[1])})
+ }
+ switch data[0] {
+ case alertLevelWarning:
+ // Drop the record on the floor and retry.
+ return c.retryReadRecord(expectChangeCipherSpec)
+ case alertLevelError:
+ return c.in.setErrorLocked(&net.OpError{Op: "remote error", Err: alert(data[1])})
+ default:
+ return c.in.setErrorLocked(c.sendAlert(alertUnexpectedMessage))
+ }
+
+ case recordTypeChangeCipherSpec:
+ if len(data) != 1 || data[0] != 1 {
+ return c.in.setErrorLocked(c.sendAlert(alertDecodeError))
+ }
+ // Handshake messages are not allowed to fragment across the CCS.
+ if c.hand.Len() > 0 {
+ return c.in.setErrorLocked(c.sendAlert(alertUnexpectedMessage))
+ }
+ // In TLS 1.3, change_cipher_spec records are ignored until the
+ // Finished. See RFC 8446, Appendix D.4. Note that according to Section
+ // 5, a server can send a ChangeCipherSpec before its ServerHello, when
+ // c.vers is still unset. That's not useful though and suspicious if the
+ // server then selects a lower protocol version, so don't allow that.
+ if c.vers == VersionTLS13 {
+ return c.retryReadRecord(expectChangeCipherSpec)
+ }
+ if !expectChangeCipherSpec {
+ return c.in.setErrorLocked(c.sendAlert(alertUnexpectedMessage))
+ }
+ if err := c.in.changeCipherSpec(); err != nil {
+ return c.in.setErrorLocked(c.sendAlert(err.(alert)))
+ }
+
+ case recordTypeApplicationData:
+ if !handshakeComplete || expectChangeCipherSpec {
+ return c.in.setErrorLocked(c.sendAlert(alertUnexpectedMessage))
+ }
+ // Some OpenSSL servers send empty records in order to randomize the
+ // CBC IV. Ignore a limited number of empty records.
+ if len(data) == 0 {
+ return c.retryReadRecord(expectChangeCipherSpec)
+ }
+ // Note that data is owned by c.rawInput, following the Next call above,
+ // to avoid copying the plaintext. This is safe because c.rawInput is
+ // not read from or written to until c.input is drained.
+ c.input.Reset(data)
+
+ case recordTypeHandshake:
+ if len(data) == 0 || expectChangeCipherSpec {
+ return c.in.setErrorLocked(c.sendAlert(alertUnexpectedMessage))
+ }
+ c.hand.Write(data)
+ }
+
+ return nil
+}
+
+// retryReadRecord recurses into readRecordOrCCS to drop a non-advancing record, like
+// a warning alert, empty application_data, or a change_cipher_spec in TLS 1.3.
+func (c *Conn) retryReadRecord(expectChangeCipherSpec bool) error {
+ c.retryCount++
+ if c.retryCount > maxUselessRecords {
+ c.sendAlert(alertUnexpectedMessage)
+ return c.in.setErrorLocked(errors.New("tls: too many ignored records"))
+ }
+ return c.readRecordOrCCS(expectChangeCipherSpec)
+}
+
+// atLeastReader reads from R, stopping with EOF once at least N bytes have been
+// read. It is different from an io.LimitedReader in that it doesn't cut short
+// the last Read call, and in that it considers an early EOF an error.
+type atLeastReader struct {
+ R io.Reader
+ N int64
+}
+
+func (r *atLeastReader) Read(p []byte) (int, error) {
+ if r.N <= 0 {
+ return 0, io.EOF
+ }
+ n, err := r.R.Read(p)
+ r.N -= int64(n) // won't underflow unless len(p) >= n > 9223372036854775809
+ if r.N > 0 && err == io.EOF {
+ return n, io.ErrUnexpectedEOF
+ }
+ if r.N <= 0 && err == nil {
+ return n, io.EOF
+ }
+ return n, err
+}
+
+// readFromUntil reads from r into c.rawInput until c.rawInput contains
+// at least n bytes or else returns an error.
+func (c *Conn) readFromUntil(r io.Reader, n int) error {
+ if c.rawInput.Len() >= n {
+ return nil
+ }
+ needs := n - c.rawInput.Len()
+ // There might be extra input waiting on the wire. Make a best effort
+ // attempt to fetch it so that it can be used in (*Conn).Read to
+ // "predict" closeNotify alerts.
+ c.rawInput.Grow(needs + bytes.MinRead)
+ _, err := c.rawInput.ReadFrom(&atLeastReader{r, int64(needs)})
+ return err
+}
+
+// sendAlert sends a TLS alert message.
+func (c *Conn) sendAlertLocked(err alert) error {
+ switch err {
+ case alertNoRenegotiation, alertCloseNotify:
+ c.tmp[0] = alertLevelWarning
+ default:
+ c.tmp[0] = alertLevelError
+ }
+ c.tmp[1] = byte(err)
+
+ _, writeErr := c.writeRecordLocked(recordTypeAlert, c.tmp[0:2])
+ if err == alertCloseNotify {
+ // closeNotify is a special case in that it isn't an error.
+ return writeErr
+ }
+
+ return c.out.setErrorLocked(&net.OpError{Op: "local error", Err: err})
+}
+
+// sendAlert sends a TLS alert message.
+func (c *Conn) sendAlert(err alert) error {
+ if c.extraConfig != nil && c.extraConfig.AlternativeRecordLayer != nil {
+ c.extraConfig.AlternativeRecordLayer.SendAlert(uint8(err))
+ return &net.OpError{Op: "local error", Err: err}
+ }
+
+ c.out.Lock()
+ defer c.out.Unlock()
+ return c.sendAlertLocked(err)
+}
+
+const (
+ // tcpMSSEstimate is a conservative estimate of the TCP maximum segment
+ // size (MSS). A constant is used, rather than querying the kernel for
+ // the actual MSS, to avoid complexity. The value here is the IPv6
+ // minimum MTU (1280 bytes) minus the overhead of an IPv6 header (40
+ // bytes) and a TCP header with timestamps (32 bytes).
+ tcpMSSEstimate = 1208
+
+ // recordSizeBoostThreshold is the number of bytes of application data
+ // sent after which the TLS record size will be increased to the
+ // maximum.
+ recordSizeBoostThreshold = 128 * 1024
+)
+
+// maxPayloadSizeForWrite returns the maximum TLS payload size to use for the
+// next application data record. There is the following trade-off:
+//
+// - For latency-sensitive applications, such as web browsing, each TLS
+// record should fit in one TCP segment.
+// - For throughput-sensitive applications, such as large file transfers,
+// larger TLS records better amortize framing and encryption overheads.
+//
+// A simple heuristic that works well in practice is to use small records for
+// the first 1MB of data, then use larger records for subsequent data, and
+// reset back to smaller records after the connection becomes idle. See "High
+// Performance Web Networking", Chapter 4, or:
+// https://www.igvita.com/2013/10/24/optimizing-tls-record-size-and-buffering-latency/
+//
+// In the interests of simplicity and determinism, this code does not attempt
+// to reset the record size once the connection is idle, however.
+func (c *Conn) maxPayloadSizeForWrite(typ recordType) int {
+ if c.config.DynamicRecordSizingDisabled || typ != recordTypeApplicationData {
+ return maxPlaintext
+ }
+
+ if c.bytesSent >= recordSizeBoostThreshold {
+ return maxPlaintext
+ }
+
+ // Subtract TLS overheads to get the maximum payload size.
+ payloadBytes := tcpMSSEstimate - recordHeaderLen - c.out.explicitNonceLen()
+ if c.out.cipher != nil {
+ switch ciph := c.out.cipher.(type) {
+ case cipher.Stream:
+ payloadBytes -= c.out.mac.Size()
+ case cipher.AEAD:
+ payloadBytes -= ciph.Overhead()
+ case cbcMode:
+ blockSize := ciph.BlockSize()
+ // The payload must fit in a multiple of blockSize, with
+ // room for at least one padding byte.
+ payloadBytes = (payloadBytes & ^(blockSize - 1)) - 1
+ // The MAC is appended before padding so affects the
+ // payload size directly.
+ payloadBytes -= c.out.mac.Size()
+ default:
+ panic("unknown cipher type")
+ }
+ }
+ if c.vers == VersionTLS13 {
+ payloadBytes-- // encrypted ContentType
+ }
+
+ // Allow packet growth in arithmetic progression up to max.
+ pkt := c.packetsSent
+ c.packetsSent++
+ if pkt > 1000 {
+ return maxPlaintext // avoid overflow in multiply below
+ }
+
+ n := payloadBytes * int(pkt+1)
+ if n > maxPlaintext {
+ n = maxPlaintext
+ }
+ return n
+}
+
+func (c *Conn) write(data []byte) (int, error) {
+ if c.buffering {
+ c.sendBuf = append(c.sendBuf, data...)
+ return len(data), nil
+ }
+
+ n, err := c.conn.Write(data)
+ c.bytesSent += int64(n)
+ return n, err
+}
+
+func (c *Conn) flush() (int, error) {
+ if len(c.sendBuf) == 0 {
+ return 0, nil
+ }
+
+ n, err := c.conn.Write(c.sendBuf)
+ c.bytesSent += int64(n)
+ c.sendBuf = nil
+ c.buffering = false
+ return n, err
+}
+
+// outBufPool pools the record-sized scratch buffers used by writeRecordLocked.
+var outBufPool = sync.Pool{
+ New: func() any {
+ return new([]byte)
+ },
+}
+
+// writeRecordLocked writes a TLS record with the given type and payload to the
+// connection and updates the record layer state.
+func (c *Conn) writeRecordLocked(typ recordType, data []byte) (int, error) {
+ outBufPtr := outBufPool.Get().(*[]byte)
+ outBuf := *outBufPtr
+ defer func() {
+ // You might be tempted to simplify this by just passing &outBuf to Put,
+ // but that would make the local copy of the outBuf slice header escape
+ // to the heap, causing an allocation. Instead, we keep around the
+ // pointer to the slice header returned by Get, which is already on the
+ // heap, and overwrite and return that.
+ *outBufPtr = outBuf
+ outBufPool.Put(outBufPtr)
+ }()
+
+ var n int
+ for len(data) > 0 {
+ m := len(data)
+ if maxPayload := c.maxPayloadSizeForWrite(typ); m > maxPayload {
+ m = maxPayload
+ }
+
+ _, outBuf = sliceForAppend(outBuf[:0], recordHeaderLen)
+ outBuf[0] = byte(typ)
+ vers := c.vers
+ if vers == 0 {
+ // Some TLS servers fail if the record version is
+ // greater than TLS 1.0 for the initial ClientHello.
+ vers = VersionTLS10
+ } else if vers == VersionTLS13 {
+ // TLS 1.3 froze the record layer version to 1.2.
+ // See RFC 8446, Section 5.1.
+ vers = VersionTLS12
+ }
+ outBuf[1] = byte(vers >> 8)
+ outBuf[2] = byte(vers)
+ outBuf[3] = byte(m >> 8)
+ outBuf[4] = byte(m)
+
+ var err error
+ outBuf, err = c.out.encrypt(outBuf, data[:m], c.config.rand())
+ if err != nil {
+ return n, err
+ }
+ if _, err := c.write(outBuf); err != nil {
+ return n, err
+ }
+ n += m
+ data = data[m:]
+ }
+
+ if typ == recordTypeChangeCipherSpec && c.vers != VersionTLS13 {
+ if err := c.out.changeCipherSpec(); err != nil {
+ return n, c.sendAlertLocked(err.(alert))
+ }
+ }
+
+ return n, nil
+}
+
+// writeRecord writes a TLS record with the given type and payload to the
+// connection and updates the record layer state.
+func (c *Conn) writeRecord(typ recordType, data []byte) (int, error) {
+ if c.extraConfig != nil && c.extraConfig.AlternativeRecordLayer != nil {
+ if typ == recordTypeChangeCipherSpec {
+ return len(data), nil
+ }
+ return c.extraConfig.AlternativeRecordLayer.WriteRecord(data)
+ }
+
+ c.out.Lock()
+ defer c.out.Unlock()
+
+ return c.writeRecordLocked(typ, data)
+}
+
+// readHandshake reads the next handshake message from
+// the record layer.
+func (c *Conn) readHandshake() (any, error) {
+ var data []byte
+ if c.extraConfig != nil && c.extraConfig.AlternativeRecordLayer != nil {
+ var err error
+ data, err = c.extraConfig.AlternativeRecordLayer.ReadHandshakeMessage()
+ if err != nil {
+ return nil, err
+ }
+ } else {
+ for c.hand.Len() < 4 {
+ if err := c.readRecord(); err != nil {
+ return nil, err
+ }
+ }
+
+ data = c.hand.Bytes()
+ n := int(data[1])<<16 | int(data[2])<<8 | int(data[3])
+ if n > maxHandshake {
+ c.sendAlertLocked(alertInternalError)
+ return nil, c.in.setErrorLocked(fmt.Errorf("tls: handshake message of length %d bytes exceeds maximum of %d bytes", n, maxHandshake))
+ }
+ for c.hand.Len() < 4+n {
+ if err := c.readRecord(); err != nil {
+ return nil, err
+ }
+ }
+ data = c.hand.Next(4 + n)
+ }
+ var m handshakeMessage
+ switch data[0] {
+ case typeHelloRequest:
+ m = new(helloRequestMsg)
+ case typeClientHello:
+ m = new(clientHelloMsg)
+ case typeServerHello:
+ m = new(serverHelloMsg)
+ case typeNewSessionTicket:
+ if c.vers == VersionTLS13 {
+ m = new(newSessionTicketMsgTLS13)
+ } else {
+ m = new(newSessionTicketMsg)
+ }
+ case typeCertificate:
+ if c.vers == VersionTLS13 {
+ m = new(certificateMsgTLS13)
+ } else {
+ m = new(certificateMsg)
+ }
+ case typeCertificateRequest:
+ if c.vers == VersionTLS13 {
+ m = new(certificateRequestMsgTLS13)
+ } else {
+ m = &certificateRequestMsg{
+ hasSignatureAlgorithm: c.vers >= VersionTLS12,
+ }
+ }
+ case typeCertificateStatus:
+ m = new(certificateStatusMsg)
+ case typeServerKeyExchange:
+ m = new(serverKeyExchangeMsg)
+ case typeServerHelloDone:
+ m = new(serverHelloDoneMsg)
+ case typeClientKeyExchange:
+ m = new(clientKeyExchangeMsg)
+ case typeCertificateVerify:
+ m = &certificateVerifyMsg{
+ hasSignatureAlgorithm: c.vers >= VersionTLS12,
+ }
+ case typeFinished:
+ m = new(finishedMsg)
+ case typeEncryptedExtensions:
+ m = new(encryptedExtensionsMsg)
+ case typeEndOfEarlyData:
+ m = new(endOfEarlyDataMsg)
+ case typeKeyUpdate:
+ m = new(keyUpdateMsg)
+ default:
+ return nil, c.in.setErrorLocked(c.sendAlert(alertUnexpectedMessage))
+ }
+
+ // The handshake message unmarshalers
+ // expect to be able to keep references to data,
+ // so pass in a fresh copy that won't be overwritten.
+ data = append([]byte(nil), data...)
+
+ if !m.unmarshal(data) {
+ return nil, c.in.setErrorLocked(c.sendAlert(alertUnexpectedMessage))
+ }
+ return m, nil
+}
+
+var (
+ errShutdown = errors.New("tls: protocol is shutdown")
+)
+
+// Write writes data to the connection.
+//
+// As Write calls Handshake, in order to prevent indefinite blocking a deadline
+// must be set for both Read and Write before Write is called when the handshake
+// has not yet completed. See SetDeadline, SetReadDeadline, and
+// SetWriteDeadline.
+func (c *Conn) Write(b []byte) (int, error) {
+ // interlock with Close below
+ for {
+ x := atomic.LoadInt32(&c.activeCall)
+ if x&1 != 0 {
+ return 0, net.ErrClosed
+ }
+ if atomic.CompareAndSwapInt32(&c.activeCall, x, x+2) {
+ break
+ }
+ }
+ defer atomic.AddInt32(&c.activeCall, -2)
+
+ if err := c.Handshake(); err != nil {
+ return 0, err
+ }
+
+ c.out.Lock()
+ defer c.out.Unlock()
+
+ if err := c.out.err; err != nil {
+ return 0, err
+ }
+
+ if !c.handshakeComplete() {
+ return 0, alertInternalError
+ }
+
+ if c.closeNotifySent {
+ return 0, errShutdown
+ }
+
+ // TLS 1.0 is susceptible to a chosen-plaintext
+ // attack when using block mode ciphers due to predictable IVs.
+ // This can be prevented by splitting each Application Data
+ // record into two records, effectively randomizing the IV.
+ //
+ // https://www.openssl.org/~bodo/tls-cbc.txt
+ // https://bugzilla.mozilla.org/show_bug.cgi?id=665814
+ // https://www.imperialviolet.org/2012/01/15/beastfollowup.html
+
+ var m int
+ if len(b) > 1 && c.vers == VersionTLS10 {
+ if _, ok := c.out.cipher.(cipher.BlockMode); ok {
+ n, err := c.writeRecordLocked(recordTypeApplicationData, b[:1])
+ if err != nil {
+ return n, c.out.setErrorLocked(err)
+ }
+ m, b = 1, b[1:]
+ }
+ }
+
+ n, err := c.writeRecordLocked(recordTypeApplicationData, b)
+ return n + m, c.out.setErrorLocked(err)
+}
+
+// handleRenegotiation processes a HelloRequest handshake message.
+func (c *Conn) handleRenegotiation() error {
+ if c.vers == VersionTLS13 {
+ return errors.New("tls: internal error: unexpected renegotiation")
+ }
+
+ msg, err := c.readHandshake()
+ if err != nil {
+ return err
+ }
+
+ helloReq, ok := msg.(*helloRequestMsg)
+ if !ok {
+ c.sendAlert(alertUnexpectedMessage)
+ return unexpectedMessageError(helloReq, msg)
+ }
+
+ if !c.isClient {
+ return c.sendAlert(alertNoRenegotiation)
+ }
+
+ switch c.config.Renegotiation {
+ case RenegotiateNever:
+ return c.sendAlert(alertNoRenegotiation)
+ case RenegotiateOnceAsClient:
+ if c.handshakes > 1 {
+ return c.sendAlert(alertNoRenegotiation)
+ }
+ case RenegotiateFreelyAsClient:
+ // Ok.
+ default:
+ c.sendAlert(alertInternalError)
+ return errors.New("tls: unknown Renegotiation value")
+ }
+
+ c.handshakeMutex.Lock()
+ defer c.handshakeMutex.Unlock()
+
+ atomic.StoreUint32(&c.handshakeStatus, 0)
+ if c.handshakeErr = c.clientHandshake(context.Background()); c.handshakeErr == nil {
+ c.handshakes++
+ }
+ return c.handshakeErr
+}
+
+func (c *Conn) HandlePostHandshakeMessage() error {
+ return c.handlePostHandshakeMessage()
+}
+
+// handlePostHandshakeMessage processes a handshake message arrived after the
+// handshake is complete. Up to TLS 1.2, it indicates the start of a renegotiation.
+func (c *Conn) handlePostHandshakeMessage() error {
+ if c.vers != VersionTLS13 {
+ return c.handleRenegotiation()
+ }
+
+ msg, err := c.readHandshake()
+ if err != nil {
+ return err
+ }
+
+ c.retryCount++
+ if c.retryCount > maxUselessRecords {
+ c.sendAlert(alertUnexpectedMessage)
+ return c.in.setErrorLocked(errors.New("tls: too many non-advancing records"))
+ }
+
+ switch msg := msg.(type) {
+ case *newSessionTicketMsgTLS13:
+ return c.handleNewSessionTicket(msg)
+ case *keyUpdateMsg:
+ return c.handleKeyUpdate(msg)
+ default:
+ c.sendAlert(alertUnexpectedMessage)
+ return fmt.Errorf("tls: received unexpected handshake message of type %T", msg)
+ }
+}
+
+func (c *Conn) handleKeyUpdate(keyUpdate *keyUpdateMsg) error {
+ cipherSuite := cipherSuiteTLS13ByID(c.cipherSuite)
+ if cipherSuite == nil {
+ return c.in.setErrorLocked(c.sendAlert(alertInternalError))
+ }
+
+ newSecret := cipherSuite.nextTrafficSecret(c.in.trafficSecret)
+ c.in.setTrafficSecret(cipherSuite, newSecret)
+
+ if keyUpdate.updateRequested {
+ c.out.Lock()
+ defer c.out.Unlock()
+
+ msg := &keyUpdateMsg{}
+ _, err := c.writeRecordLocked(recordTypeHandshake, msg.marshal())
+ if err != nil {
+ // Surface the error at the next write.
+ c.out.setErrorLocked(err)
+ return nil
+ }
+
+ newSecret := cipherSuite.nextTrafficSecret(c.out.trafficSecret)
+ c.out.setTrafficSecret(cipherSuite, newSecret)
+ }
+
+ return nil
+}
+
+// Read reads data from the connection.
+//
+// As Read calls Handshake, in order to prevent indefinite blocking a deadline
+// must be set for both Read and Write before Read is called when the handshake
+// has not yet completed. See SetDeadline, SetReadDeadline, and
+// SetWriteDeadline.
+func (c *Conn) Read(b []byte) (int, error) {
+ if err := c.Handshake(); err != nil {
+ return 0, err
+ }
+ if len(b) == 0 {
+ // Put this after Handshake, in case people were calling
+ // Read(nil) for the side effect of the Handshake.
+ return 0, nil
+ }
+
+ c.in.Lock()
+ defer c.in.Unlock()
+
+ for c.input.Len() == 0 {
+ if err := c.readRecord(); err != nil {
+ return 0, err
+ }
+ for c.hand.Len() > 0 {
+ if err := c.handlePostHandshakeMessage(); err != nil {
+ return 0, err
+ }
+ }
+ }
+
+ n, _ := c.input.Read(b)
+
+ // If a close-notify alert is waiting, read it so that we can return (n,
+ // EOF) instead of (n, nil), to signal to the HTTP response reading
+ // goroutine that the connection is now closed. This eliminates a race
+ // where the HTTP response reading goroutine would otherwise not observe
+ // the EOF until its next read, by which time a client goroutine might
+ // have already tried to reuse the HTTP connection for a new request.
+ // See https://golang.org/cl/76400046 and https://golang.org/issue/3514
+ if n != 0 && c.input.Len() == 0 && c.rawInput.Len() > 0 &&
+ recordType(c.rawInput.Bytes()[0]) == recordTypeAlert {
+ if err := c.readRecord(); err != nil {
+ return n, err // will be io.EOF on closeNotify
+ }
+ }
+
+ return n, nil
+}
+
+// Close closes the connection.
+func (c *Conn) Close() error {
+ // Interlock with Conn.Write above.
+ var x int32
+ for {
+ x = atomic.LoadInt32(&c.activeCall)
+ if x&1 != 0 {
+ return net.ErrClosed
+ }
+ if atomic.CompareAndSwapInt32(&c.activeCall, x, x|1) {
+ break
+ }
+ }
+ if x != 0 {
+ // io.Writer and io.Closer should not be used concurrently.
+ // If Close is called while a Write is currently in-flight,
+ // interpret that as a sign that this Close is really just
+ // being used to break the Write and/or clean up resources and
+ // avoid sending the alertCloseNotify, which may block
+ // waiting on handshakeMutex or the c.out mutex.
+ return c.conn.Close()
+ }
+
+ var alertErr error
+ if c.handshakeComplete() {
+ if err := c.closeNotify(); err != nil {
+ alertErr = fmt.Errorf("tls: failed to send closeNotify alert (but connection was closed anyway): %w", err)
+ }
+ }
+
+ if err := c.conn.Close(); err != nil {
+ return err
+ }
+ return alertErr
+}
+
+var errEarlyCloseWrite = errors.New("tls: CloseWrite called before handshake complete")
+
+// CloseWrite shuts down the writing side of the connection. It should only be
+// called once the handshake has completed and does not call CloseWrite on the
+// underlying connection. Most callers should just use Close.
+func (c *Conn) CloseWrite() error {
+ if !c.handshakeComplete() {
+ return errEarlyCloseWrite
+ }
+
+ return c.closeNotify()
+}
+
+func (c *Conn) closeNotify() error {
+ c.out.Lock()
+ defer c.out.Unlock()
+
+ if !c.closeNotifySent {
+ // Set a Write Deadline to prevent possibly blocking forever.
+ c.SetWriteDeadline(time.Now().Add(time.Second * 5))
+ c.closeNotifyErr = c.sendAlertLocked(alertCloseNotify)
+ c.closeNotifySent = true
+ // Any subsequent writes will fail.
+ c.SetWriteDeadline(time.Now())
+ }
+ return c.closeNotifyErr
+}
+
+// Handshake runs the client or server handshake
+// protocol if it has not yet been run.
+//
+// Most uses of this package need not call Handshake explicitly: the
+// first Read or Write will call it automatically.
+//
+// For control over canceling or setting a timeout on a handshake, use
+// HandshakeContext or the Dialer's DialContext method instead.
+func (c *Conn) Handshake() error {
+ return c.HandshakeContext(context.Background())
+}
+
+// HandshakeContext runs the client or server handshake
+// protocol if it has not yet been run.
+//
+// The provided Context must be non-nil. If the context is canceled before
+// the handshake is complete, the handshake is interrupted and an error is returned.
+// Once the handshake has completed, cancellation of the context will not affect the
+// connection.
+//
+// Most uses of this package need not call HandshakeContext explicitly: the
+// first Read or Write will call it automatically.
+func (c *Conn) HandshakeContext(ctx context.Context) error {
+ // Delegate to unexported method for named return
+ // without confusing documented signature.
+ return c.handshakeContext(ctx)
+}
+
+func (c *Conn) handshakeContext(ctx context.Context) (ret error) {
+ // Fast sync/atomic-based exit if there is no handshake in flight and the
+ // last one succeeded without an error. Avoids the expensive context setup
+ // and mutex for most Read and Write calls.
+ if c.handshakeComplete() {
+ return nil
+ }
+
+ handshakeCtx, cancel := context.WithCancel(ctx)
+ // Note: defer this before starting the "interrupter" goroutine
+ // so that we can tell the difference between the input being canceled and
+ // this cancellation. In the former case, we need to close the connection.
+ defer cancel()
+
+ // Start the "interrupter" goroutine, if this context might be canceled.
+ // (The background context cannot).
+ //
+ // The interrupter goroutine waits for the input context to be done and
+ // closes the connection if this happens before the function returns.
+ if ctx.Done() != nil {
+ done := make(chan struct{})
+ interruptRes := make(chan error, 1)
+ defer func() {
+ close(done)
+ if ctxErr := <-interruptRes; ctxErr != nil {
+ // Return context error to user.
+ ret = ctxErr
+ }
+ }()
+ go func() {
+ select {
+ case <-handshakeCtx.Done():
+ // Close the connection, discarding the error
+ _ = c.conn.Close()
+ interruptRes <- handshakeCtx.Err()
+ case <-done:
+ interruptRes <- nil
+ }
+ }()
+ }
+
+ c.handshakeMutex.Lock()
+ defer c.handshakeMutex.Unlock()
+
+ if err := c.handshakeErr; err != nil {
+ return err
+ }
+ if c.handshakeComplete() {
+ return nil
+ }
+
+ c.in.Lock()
+ defer c.in.Unlock()
+
+ c.handshakeErr = c.handshakeFn(handshakeCtx)
+ if c.handshakeErr == nil {
+ c.handshakes++
+ } else {
+ // If an error occurred during the handshake try to flush the
+ // alert that might be left in the buffer.
+ c.flush()
+ }
+
+ if c.handshakeErr == nil && !c.handshakeComplete() {
+ c.handshakeErr = errors.New("tls: internal error: handshake should have had a result")
+ }
+ if c.handshakeErr != nil && c.handshakeComplete() {
+ panic("tls: internal error: handshake returned an error but is marked successful")
+ }
+
+ return c.handshakeErr
+}
+
+// ConnectionState returns basic TLS details about the connection.
+func (c *Conn) ConnectionState() ConnectionState {
+ c.connStateMutex.Lock()
+ defer c.connStateMutex.Unlock()
+ return c.connState.ConnectionState
+}
+
+// ConnectionStateWith0RTT returns basic TLS details (incl. 0-RTT status) about the connection.
+func (c *Conn) ConnectionStateWith0RTT() ConnectionStateWith0RTT {
+ c.connStateMutex.Lock()
+ defer c.connStateMutex.Unlock()
+ return c.connState
+}
+
+func (c *Conn) connectionStateLocked() ConnectionState {
+ var state connectionState
+ state.HandshakeComplete = c.handshakeComplete()
+ state.Version = c.vers
+ state.NegotiatedProtocol = c.clientProtocol
+ state.DidResume = c.didResume
+ state.NegotiatedProtocolIsMutual = true
+ state.ServerName = c.serverName
+ state.CipherSuite = c.cipherSuite
+ state.PeerCertificates = c.peerCertificates
+ state.VerifiedChains = c.verifiedChains
+ state.SignedCertificateTimestamps = c.scts
+ state.OCSPResponse = c.ocspResponse
+ if !c.didResume && c.vers != VersionTLS13 {
+ if c.clientFinishedIsFirst {
+ state.TLSUnique = c.clientFinished[:]
+ } else {
+ state.TLSUnique = c.serverFinished[:]
+ }
+ }
+ if c.config.Renegotiation != RenegotiateNever {
+ state.ekm = noExportedKeyingMaterial
+ } else {
+ state.ekm = c.ekm
+ }
+ return toConnectionState(state)
+}
+
+func (c *Conn) updateConnectionState() {
+ c.connStateMutex.Lock()
+ defer c.connStateMutex.Unlock()
+ c.connState = ConnectionStateWith0RTT{
+ Used0RTT: c.used0RTT,
+ ConnectionState: c.connectionStateLocked(),
+ }
+}
+
+// OCSPResponse returns the stapled OCSP response from the TLS server, if
+// any. (Only valid for client connections.)
+func (c *Conn) OCSPResponse() []byte {
+ c.handshakeMutex.Lock()
+ defer c.handshakeMutex.Unlock()
+
+ return c.ocspResponse
+}
+
+// VerifyHostname checks that the peer certificate chain is valid for
+// connecting to host. If so, it returns nil; if not, it returns an error
+// describing the problem.
+func (c *Conn) VerifyHostname(host string) error {
+ c.handshakeMutex.Lock()
+ defer c.handshakeMutex.Unlock()
+ if !c.isClient {
+ return errors.New("tls: VerifyHostname called on TLS server connection")
+ }
+ if !c.handshakeComplete() {
+ return errors.New("tls: handshake has not yet been performed")
+ }
+ if len(c.verifiedChains) == 0 {
+ return errors.New("tls: handshake did not verify certificate chain")
+ }
+ return c.peerCertificates[0].VerifyHostname(host)
+}
+
+func (c *Conn) handshakeComplete() bool {
+ return atomic.LoadUint32(&c.handshakeStatus) == 1
+}
diff --git a/vendor/github.com/quic-go/qtls-go1-18/cpu.go b/vendor/github.com/quic-go/qtls-go1-18/cpu.go
new file mode 100644
index 0000000000..1219450879
--- /dev/null
+++ b/vendor/github.com/quic-go/qtls-go1-18/cpu.go
@@ -0,0 +1,22 @@
+//go:build !js
+// +build !js
+
+package qtls
+
+import (
+ "runtime"
+
+ "golang.org/x/sys/cpu"
+)
+
+var (
+ hasGCMAsmAMD64 = cpu.X86.HasAES && cpu.X86.HasPCLMULQDQ
+ hasGCMAsmARM64 = cpu.ARM64.HasAES && cpu.ARM64.HasPMULL
+ // Keep in sync with crypto/aes/cipher_s390x.go.
+ hasGCMAsmS390X = cpu.S390X.HasAES && cpu.S390X.HasAESCBC && cpu.S390X.HasAESCTR &&
+ (cpu.S390X.HasGHASH || cpu.S390X.HasAESGCM)
+
+ hasAESGCMHardwareSupport = runtime.GOARCH == "amd64" && hasGCMAsmAMD64 ||
+ runtime.GOARCH == "arm64" && hasGCMAsmARM64 ||
+ runtime.GOARCH == "s390x" && hasGCMAsmS390X
+)
diff --git a/vendor/github.com/quic-go/qtls-go1-18/cpu_other.go b/vendor/github.com/quic-go/qtls-go1-18/cpu_other.go
new file mode 100644
index 0000000000..33f7d21942
--- /dev/null
+++ b/vendor/github.com/quic-go/qtls-go1-18/cpu_other.go
@@ -0,0 +1,12 @@
+//go:build js
+// +build js
+
+package qtls
+
+var (
+ hasGCMAsmAMD64 = false
+ hasGCMAsmARM64 = false
+ hasGCMAsmS390X = false
+
+ hasAESGCMHardwareSupport = false
+)
diff --git a/vendor/github.com/quic-go/qtls-go1-18/handshake_client.go b/vendor/github.com/quic-go/qtls-go1-18/handshake_client.go
new file mode 100644
index 0000000000..a2a0eaea8c
--- /dev/null
+++ b/vendor/github.com/quic-go/qtls-go1-18/handshake_client.go
@@ -0,0 +1,1112 @@
+// Copyright 2009 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package qtls
+
+import (
+ "bytes"
+ "context"
+ "crypto"
+ "crypto/ecdsa"
+ "crypto/ed25519"
+ "crypto/rsa"
+ "crypto/subtle"
+ "crypto/x509"
+ "errors"
+ "fmt"
+ "hash"
+ "io"
+ "net"
+ "strings"
+ "sync/atomic"
+ "time"
+
+ "golang.org/x/crypto/cryptobyte"
+)
+
+const clientSessionStateVersion = 1
+
+type clientHandshakeState struct {
+ c *Conn
+ ctx context.Context
+ serverHello *serverHelloMsg
+ hello *clientHelloMsg
+ suite *cipherSuite
+ finishedHash finishedHash
+ masterSecret []byte
+ session *clientSessionState
+}
+
+func (c *Conn) makeClientHello() (*clientHelloMsg, ecdheParameters, error) {
+ config := c.config
+ if len(config.ServerName) == 0 && !config.InsecureSkipVerify {
+ return nil, nil, errors.New("tls: either ServerName or InsecureSkipVerify must be specified in the tls.Config")
+ }
+
+ nextProtosLength := 0
+ for _, proto := range config.NextProtos {
+ if l := len(proto); l == 0 || l > 255 {
+ return nil, nil, errors.New("tls: invalid NextProtos value")
+ } else {
+ nextProtosLength += 1 + l
+ }
+ }
+ if nextProtosLength > 0xffff {
+ return nil, nil, errors.New("tls: NextProtos values too large")
+ }
+
+ var supportedVersions []uint16
+ var clientHelloVersion uint16
+ if c.extraConfig.usesAlternativeRecordLayer() {
+ if config.maxSupportedVersion(roleClient) < VersionTLS13 {
+ return nil, nil, errors.New("tls: MaxVersion prevents QUIC from using TLS 1.3")
+ }
+ // Only offer TLS 1.3 when QUIC is used.
+ supportedVersions = []uint16{VersionTLS13}
+ clientHelloVersion = VersionTLS13
+ } else {
+ supportedVersions = config.supportedVersions(roleClient)
+ if len(supportedVersions) == 0 {
+ return nil, nil, errors.New("tls: no supported versions satisfy MinVersion and MaxVersion")
+ }
+ clientHelloVersion = config.maxSupportedVersion(roleClient)
+ }
+
+ // The version at the beginning of the ClientHello was capped at TLS 1.2
+ // for compatibility reasons. The supported_versions extension is used
+ // to negotiate versions now. See RFC 8446, Section 4.2.1.
+ if clientHelloVersion > VersionTLS12 {
+ clientHelloVersion = VersionTLS12
+ }
+
+ hello := &clientHelloMsg{
+ vers: clientHelloVersion,
+ compressionMethods: []uint8{compressionNone},
+ random: make([]byte, 32),
+ ocspStapling: true,
+ scts: true,
+ serverName: hostnameInSNI(config.ServerName),
+ supportedCurves: config.curvePreferences(),
+ supportedPoints: []uint8{pointFormatUncompressed},
+ secureRenegotiationSupported: true,
+ alpnProtocols: config.NextProtos,
+ supportedVersions: supportedVersions,
+ }
+
+ if c.handshakes > 0 {
+ hello.secureRenegotiation = c.clientFinished[:]
+ }
+
+ preferenceOrder := cipherSuitesPreferenceOrder
+ if !hasAESGCMHardwareSupport {
+ preferenceOrder = cipherSuitesPreferenceOrderNoAES
+ }
+ configCipherSuites := config.cipherSuites()
+ hello.cipherSuites = make([]uint16, 0, len(configCipherSuites))
+
+ for _, suiteId := range preferenceOrder {
+ suite := mutualCipherSuite(configCipherSuites, suiteId)
+ if suite == nil {
+ continue
+ }
+ // Don't advertise TLS 1.2-only cipher suites unless
+ // we're attempting TLS 1.2.
+ if hello.vers < VersionTLS12 && suite.flags&suiteTLS12 != 0 {
+ continue
+ }
+ hello.cipherSuites = append(hello.cipherSuites, suiteId)
+ }
+
+ _, err := io.ReadFull(config.rand(), hello.random)
+ if err != nil {
+ return nil, nil, errors.New("tls: short read from Rand: " + err.Error())
+ }
+
+ // A random session ID is used to detect when the server accepted a ticket
+ // and is resuming a session (see RFC 5077). In TLS 1.3, it's always set as
+ // a compatibility measure (see RFC 8446, Section 4.1.2).
+ if c.extraConfig == nil || c.extraConfig.AlternativeRecordLayer == nil {
+ hello.sessionId = make([]byte, 32)
+ if _, err := io.ReadFull(config.rand(), hello.sessionId); err != nil {
+ return nil, nil, errors.New("tls: short read from Rand: " + err.Error())
+ }
+ }
+
+ if hello.vers >= VersionTLS12 {
+ hello.supportedSignatureAlgorithms = supportedSignatureAlgorithms
+ }
+
+ var params ecdheParameters
+ if hello.supportedVersions[0] == VersionTLS13 {
+ var suites []uint16
+ for _, suiteID := range configCipherSuites {
+ for _, suite := range cipherSuitesTLS13 {
+ if suite.id == suiteID {
+ suites = append(suites, suiteID)
+ }
+ }
+ }
+ if len(suites) > 0 {
+ hello.cipherSuites = suites
+ } else {
+ if hasAESGCMHardwareSupport {
+ hello.cipherSuites = append(hello.cipherSuites, defaultCipherSuitesTLS13...)
+ } else {
+ hello.cipherSuites = append(hello.cipherSuites, defaultCipherSuitesTLS13NoAES...)
+ }
+ }
+
+ curveID := config.curvePreferences()[0]
+ if _, ok := curveForCurveID(curveID); curveID != X25519 && !ok {
+ return nil, nil, errors.New("tls: CurvePreferences includes unsupported curve")
+ }
+ params, err = generateECDHEParameters(config.rand(), curveID)
+ if err != nil {
+ return nil, nil, err
+ }
+ hello.keyShares = []keyShare{{group: curveID, data: params.PublicKey()}}
+ }
+
+ if hello.supportedVersions[0] == VersionTLS13 && c.extraConfig != nil && c.extraConfig.GetExtensions != nil {
+ hello.additionalExtensions = c.extraConfig.GetExtensions(typeClientHello)
+ }
+
+ return hello, params, nil
+}
+
+func (c *Conn) clientHandshake(ctx context.Context) (err error) {
+ if c.config == nil {
+ c.config = fromConfig(defaultConfig())
+ }
+ c.setAlternativeRecordLayer()
+
+ // This may be a renegotiation handshake, in which case some fields
+ // need to be reset.
+ c.didResume = false
+
+ hello, ecdheParams, err := c.makeClientHello()
+ if err != nil {
+ return err
+ }
+ c.serverName = hello.serverName
+
+ cacheKey, session, earlySecret, binderKey := c.loadSession(hello)
+ if cacheKey != "" && session != nil {
+ var deletedTicket bool
+ if session.vers == VersionTLS13 && hello.earlyData && c.extraConfig != nil && c.extraConfig.Enable0RTT {
+ // don't reuse a session ticket that enabled 0-RTT
+ c.config.ClientSessionCache.Put(cacheKey, nil)
+ deletedTicket = true
+
+ if suite := cipherSuiteTLS13ByID(session.cipherSuite); suite != nil {
+ h := suite.hash.New()
+ h.Write(hello.marshal())
+ clientEarlySecret := suite.deriveSecret(earlySecret, "c e traffic", h)
+ c.out.exportKey(Encryption0RTT, suite, clientEarlySecret)
+ if err := c.config.writeKeyLog(keyLogLabelEarlyTraffic, hello.random, clientEarlySecret); err != nil {
+ c.sendAlert(alertInternalError)
+ return err
+ }
+ }
+ }
+ if !deletedTicket {
+ defer func() {
+ // If we got a handshake failure when resuming a session, throw away
+ // the session ticket. See RFC 5077, Section 3.2.
+ //
+ // RFC 8446 makes no mention of dropping tickets on failure, but it
+ // does require servers to abort on invalid binders, so we need to
+ // delete tickets to recover from a corrupted PSK.
+ if err != nil {
+ c.config.ClientSessionCache.Put(cacheKey, nil)
+ }
+ }()
+ }
+ }
+
+ if _, err := c.writeRecord(recordTypeHandshake, hello.marshal()); err != nil {
+ return err
+ }
+
+ msg, err := c.readHandshake()
+ if err != nil {
+ return err
+ }
+
+ serverHello, ok := msg.(*serverHelloMsg)
+ if !ok {
+ c.sendAlert(alertUnexpectedMessage)
+ return unexpectedMessageError(serverHello, msg)
+ }
+
+ if err := c.pickTLSVersion(serverHello); err != nil {
+ return err
+ }
+
+ // If we are negotiating a protocol version that's lower than what we
+ // support, check for the server downgrade canaries.
+ // See RFC 8446, Section 4.1.3.
+ maxVers := c.config.maxSupportedVersion(roleClient)
+ tls12Downgrade := string(serverHello.random[24:]) == downgradeCanaryTLS12
+ tls11Downgrade := string(serverHello.random[24:]) == downgradeCanaryTLS11
+ if maxVers == VersionTLS13 && c.vers <= VersionTLS12 && (tls12Downgrade || tls11Downgrade) ||
+ maxVers == VersionTLS12 && c.vers <= VersionTLS11 && tls11Downgrade {
+ c.sendAlert(alertIllegalParameter)
+ return errors.New("tls: downgrade attempt detected, possibly due to a MitM attack or a broken middlebox")
+ }
+
+ if c.vers == VersionTLS13 {
+ hs := &clientHandshakeStateTLS13{
+ c: c,
+ ctx: ctx,
+ serverHello: serverHello,
+ hello: hello,
+ ecdheParams: ecdheParams,
+ session: session,
+ earlySecret: earlySecret,
+ binderKey: binderKey,
+ }
+
+ // In TLS 1.3, session tickets are delivered after the handshake.
+ return hs.handshake()
+ }
+
+ hs := &clientHandshakeState{
+ c: c,
+ ctx: ctx,
+ serverHello: serverHello,
+ hello: hello,
+ session: session,
+ }
+
+ if err := hs.handshake(); err != nil {
+ return err
+ }
+
+ // If we had a successful handshake and hs.session is different from
+ // the one already cached - cache a new one.
+ if cacheKey != "" && hs.session != nil && session != hs.session {
+ c.config.ClientSessionCache.Put(cacheKey, toClientSessionState(hs.session))
+ }
+
+ c.updateConnectionState()
+ return nil
+}
+
+// extract the app data saved in the session.nonce,
+// and set the session.nonce to the actual nonce value
+func (c *Conn) decodeSessionState(session *clientSessionState) (uint32 /* max early data */, []byte /* app data */, bool /* ok */) {
+ s := cryptobyte.String(session.nonce)
+ var version uint16
+ if !s.ReadUint16(&version) {
+ return 0, nil, false
+ }
+ if version != clientSessionStateVersion {
+ return 0, nil, false
+ }
+ var maxEarlyData uint32
+ if !s.ReadUint32(&maxEarlyData) {
+ return 0, nil, false
+ }
+ var appData []byte
+ if !readUint16LengthPrefixed(&s, &appData) {
+ return 0, nil, false
+ }
+ var nonce []byte
+ if !readUint16LengthPrefixed(&s, &nonce) {
+ return 0, nil, false
+ }
+ session.nonce = nonce
+ return maxEarlyData, appData, true
+}
+
+func (c *Conn) loadSession(hello *clientHelloMsg) (cacheKey string,
+ session *clientSessionState, earlySecret, binderKey []byte) {
+ if c.config.SessionTicketsDisabled || c.config.ClientSessionCache == nil {
+ return "", nil, nil, nil
+ }
+
+ hello.ticketSupported = true
+
+ if hello.supportedVersions[0] == VersionTLS13 {
+ // Require DHE on resumption as it guarantees forward secrecy against
+ // compromise of the session ticket key. See RFC 8446, Section 4.2.9.
+ hello.pskModes = []uint8{pskModeDHE}
+ }
+
+ // Session resumption is not allowed if renegotiating because
+ // renegotiation is primarily used to allow a client to send a client
+ // certificate, which would be skipped if session resumption occurred.
+ if c.handshakes != 0 {
+ return "", nil, nil, nil
+ }
+
+ // Try to resume a previously negotiated TLS session, if available.
+ cacheKey = clientSessionCacheKey(c.conn.RemoteAddr(), c.config)
+ sess, ok := c.config.ClientSessionCache.Get(cacheKey)
+ if !ok || sess == nil {
+ return cacheKey, nil, nil, nil
+ }
+ session = fromClientSessionState(sess)
+
+ var appData []byte
+ var maxEarlyData uint32
+ if session.vers == VersionTLS13 {
+ var ok bool
+ maxEarlyData, appData, ok = c.decodeSessionState(session)
+ if !ok { // delete it, if parsing failed
+ c.config.ClientSessionCache.Put(cacheKey, nil)
+ return cacheKey, nil, nil, nil
+ }
+ }
+
+ // Check that version used for the previous session is still valid.
+ versOk := false
+ for _, v := range hello.supportedVersions {
+ if v == session.vers {
+ versOk = true
+ break
+ }
+ }
+ if !versOk {
+ return cacheKey, nil, nil, nil
+ }
+
+ // Check that the cached server certificate is not expired, and that it's
+ // valid for the ServerName. This should be ensured by the cache key, but
+ // protect the application from a faulty ClientSessionCache implementation.
+ if !c.config.InsecureSkipVerify {
+ if len(session.verifiedChains) == 0 {
+ // The original connection had InsecureSkipVerify, while this doesn't.
+ return cacheKey, nil, nil, nil
+ }
+ serverCert := session.serverCertificates[0]
+ if c.config.time().After(serverCert.NotAfter) {
+ // Expired certificate, delete the entry.
+ c.config.ClientSessionCache.Put(cacheKey, nil)
+ return cacheKey, nil, nil, nil
+ }
+ if err := serverCert.VerifyHostname(c.config.ServerName); err != nil {
+ return cacheKey, nil, nil, nil
+ }
+ }
+
+ if session.vers != VersionTLS13 {
+ // In TLS 1.2 the cipher suite must match the resumed session. Ensure we
+ // are still offering it.
+ if mutualCipherSuite(hello.cipherSuites, session.cipherSuite) == nil {
+ return cacheKey, nil, nil, nil
+ }
+
+ hello.sessionTicket = session.sessionTicket
+ return
+ }
+
+ // Check that the session ticket is not expired.
+ if c.config.time().After(session.useBy) {
+ c.config.ClientSessionCache.Put(cacheKey, nil)
+ return cacheKey, nil, nil, nil
+ }
+
+ // In TLS 1.3 the KDF hash must match the resumed session. Ensure we
+ // offer at least one cipher suite with that hash.
+ cipherSuite := cipherSuiteTLS13ByID(session.cipherSuite)
+ if cipherSuite == nil {
+ return cacheKey, nil, nil, nil
+ }
+ cipherSuiteOk := false
+ for _, offeredID := range hello.cipherSuites {
+ offeredSuite := cipherSuiteTLS13ByID(offeredID)
+ if offeredSuite != nil && offeredSuite.hash == cipherSuite.hash {
+ cipherSuiteOk = true
+ break
+ }
+ }
+ if !cipherSuiteOk {
+ return cacheKey, nil, nil, nil
+ }
+
+ // Set the pre_shared_key extension. See RFC 8446, Section 4.2.11.1.
+ ticketAge := uint32(c.config.time().Sub(session.receivedAt) / time.Millisecond)
+ identity := pskIdentity{
+ label: session.sessionTicket,
+ obfuscatedTicketAge: ticketAge + session.ageAdd,
+ }
+ hello.pskIdentities = []pskIdentity{identity}
+ hello.pskBinders = [][]byte{make([]byte, cipherSuite.hash.Size())}
+
+ // Compute the PSK binders. See RFC 8446, Section 4.2.11.2.
+ psk := cipherSuite.expandLabel(session.masterSecret, "resumption",
+ session.nonce, cipherSuite.hash.Size())
+ earlySecret = cipherSuite.extract(psk, nil)
+ binderKey = cipherSuite.deriveSecret(earlySecret, resumptionBinderLabel, nil)
+ if c.extraConfig != nil {
+ hello.earlyData = c.extraConfig.Enable0RTT && maxEarlyData > 0
+ }
+ transcript := cipherSuite.hash.New()
+ transcript.Write(hello.marshalWithoutBinders())
+ pskBinders := [][]byte{cipherSuite.finishedHash(binderKey, transcript)}
+ hello.updateBinders(pskBinders)
+
+ if session.vers == VersionTLS13 && c.extraConfig != nil && c.extraConfig.SetAppDataFromSessionState != nil {
+ c.extraConfig.SetAppDataFromSessionState(appData)
+ }
+ return
+}
+
+func (c *Conn) pickTLSVersion(serverHello *serverHelloMsg) error {
+ peerVersion := serverHello.vers
+ if serverHello.supportedVersion != 0 {
+ peerVersion = serverHello.supportedVersion
+ }
+
+ vers, ok := c.config.mutualVersion(roleClient, []uint16{peerVersion})
+ if !ok {
+ c.sendAlert(alertProtocolVersion)
+ return fmt.Errorf("tls: server selected unsupported protocol version %x", peerVersion)
+ }
+
+ c.vers = vers
+ c.haveVers = true
+ c.in.version = vers
+ c.out.version = vers
+
+ return nil
+}
+
+// Does the handshake, either a full one or resumes old session. Requires hs.c,
+// hs.hello, hs.serverHello, and, optionally, hs.session to be set.
+func (hs *clientHandshakeState) handshake() error {
+ c := hs.c
+
+ isResume, err := hs.processServerHello()
+ if err != nil {
+ return err
+ }
+
+ hs.finishedHash = newFinishedHash(c.vers, hs.suite)
+
+ // No signatures of the handshake are needed in a resumption.
+ // Otherwise, in a full handshake, if we don't have any certificates
+ // configured then we will never send a CertificateVerify message and
+ // thus no signatures are needed in that case either.
+ if isResume || (len(c.config.Certificates) == 0 && c.config.GetClientCertificate == nil) {
+ hs.finishedHash.discardHandshakeBuffer()
+ }
+
+ hs.finishedHash.Write(hs.hello.marshal())
+ hs.finishedHash.Write(hs.serverHello.marshal())
+
+ c.buffering = true
+ c.didResume = isResume
+ if isResume {
+ if err := hs.establishKeys(); err != nil {
+ return err
+ }
+ if err := hs.readSessionTicket(); err != nil {
+ return err
+ }
+ if err := hs.readFinished(c.serverFinished[:]); err != nil {
+ return err
+ }
+ c.clientFinishedIsFirst = false
+ // Make sure the connection is still being verified whether or not this
+ // is a resumption. Resumptions currently don't reverify certificates so
+ // they don't call verifyServerCertificate. See Issue 31641.
+ if c.config.VerifyConnection != nil {
+ if err := c.config.VerifyConnection(c.connectionStateLocked()); err != nil {
+ c.sendAlert(alertBadCertificate)
+ return err
+ }
+ }
+ if err := hs.sendFinished(c.clientFinished[:]); err != nil {
+ return err
+ }
+ if _, err := c.flush(); err != nil {
+ return err
+ }
+ } else {
+ if err := hs.doFullHandshake(); err != nil {
+ return err
+ }
+ if err := hs.establishKeys(); err != nil {
+ return err
+ }
+ if err := hs.sendFinished(c.clientFinished[:]); err != nil {
+ return err
+ }
+ if _, err := c.flush(); err != nil {
+ return err
+ }
+ c.clientFinishedIsFirst = true
+ if err := hs.readSessionTicket(); err != nil {
+ return err
+ }
+ if err := hs.readFinished(c.serverFinished[:]); err != nil {
+ return err
+ }
+ }
+
+ c.ekm = ekmFromMasterSecret(c.vers, hs.suite, hs.masterSecret, hs.hello.random, hs.serverHello.random)
+ atomic.StoreUint32(&c.handshakeStatus, 1)
+
+ return nil
+}
+
+func (hs *clientHandshakeState) pickCipherSuite() error {
+ if hs.suite = mutualCipherSuite(hs.hello.cipherSuites, hs.serverHello.cipherSuite); hs.suite == nil {
+ hs.c.sendAlert(alertHandshakeFailure)
+ return errors.New("tls: server chose an unconfigured cipher suite")
+ }
+
+ hs.c.cipherSuite = hs.suite.id
+ return nil
+}
+
+func (hs *clientHandshakeState) doFullHandshake() error {
+ c := hs.c
+
+ msg, err := c.readHandshake()
+ if err != nil {
+ return err
+ }
+ certMsg, ok := msg.(*certificateMsg)
+ if !ok || len(certMsg.certificates) == 0 {
+ c.sendAlert(alertUnexpectedMessage)
+ return unexpectedMessageError(certMsg, msg)
+ }
+ hs.finishedHash.Write(certMsg.marshal())
+
+ msg, err = c.readHandshake()
+ if err != nil {
+ return err
+ }
+
+ cs, ok := msg.(*certificateStatusMsg)
+ if ok {
+ // RFC4366 on Certificate Status Request:
+ // The server MAY return a "certificate_status" message.
+
+ if !hs.serverHello.ocspStapling {
+ // If a server returns a "CertificateStatus" message, then the
+ // server MUST have included an extension of type "status_request"
+ // with empty "extension_data" in the extended server hello.
+
+ c.sendAlert(alertUnexpectedMessage)
+ return errors.New("tls: received unexpected CertificateStatus message")
+ }
+ hs.finishedHash.Write(cs.marshal())
+
+ c.ocspResponse = cs.response
+
+ msg, err = c.readHandshake()
+ if err != nil {
+ return err
+ }
+ }
+
+ if c.handshakes == 0 {
+ // If this is the first handshake on a connection, process and
+ // (optionally) verify the server's certificates.
+ if err := c.verifyServerCertificate(certMsg.certificates); err != nil {
+ return err
+ }
+ } else {
+ // This is a renegotiation handshake. We require that the
+ // server's identity (i.e. leaf certificate) is unchanged and
+ // thus any previous trust decision is still valid.
+ //
+ // See https://mitls.org/pages/attacks/3SHAKE for the
+ // motivation behind this requirement.
+ if !bytes.Equal(c.peerCertificates[0].Raw, certMsg.certificates[0]) {
+ c.sendAlert(alertBadCertificate)
+ return errors.New("tls: server's identity changed during renegotiation")
+ }
+ }
+
+ keyAgreement := hs.suite.ka(c.vers)
+
+ skx, ok := msg.(*serverKeyExchangeMsg)
+ if ok {
+ hs.finishedHash.Write(skx.marshal())
+ err = keyAgreement.processServerKeyExchange(c.config, hs.hello, hs.serverHello, c.peerCertificates[0], skx)
+ if err != nil {
+ c.sendAlert(alertUnexpectedMessage)
+ return err
+ }
+
+ msg, err = c.readHandshake()
+ if err != nil {
+ return err
+ }
+ }
+
+ var chainToSend *Certificate
+ var certRequested bool
+ certReq, ok := msg.(*certificateRequestMsg)
+ if ok {
+ certRequested = true
+ hs.finishedHash.Write(certReq.marshal())
+
+ cri := certificateRequestInfoFromMsg(hs.ctx, c.vers, certReq)
+ if chainToSend, err = c.getClientCertificate(cri); err != nil {
+ c.sendAlert(alertInternalError)
+ return err
+ }
+
+ msg, err = c.readHandshake()
+ if err != nil {
+ return err
+ }
+ }
+
+ shd, ok := msg.(*serverHelloDoneMsg)
+ if !ok {
+ c.sendAlert(alertUnexpectedMessage)
+ return unexpectedMessageError(shd, msg)
+ }
+ hs.finishedHash.Write(shd.marshal())
+
+ // If the server requested a certificate then we have to send a
+ // Certificate message, even if it's empty because we don't have a
+ // certificate to send.
+ if certRequested {
+ certMsg = new(certificateMsg)
+ certMsg.certificates = chainToSend.Certificate
+ hs.finishedHash.Write(certMsg.marshal())
+ if _, err := c.writeRecord(recordTypeHandshake, certMsg.marshal()); err != nil {
+ return err
+ }
+ }
+
+ preMasterSecret, ckx, err := keyAgreement.generateClientKeyExchange(c.config, hs.hello, c.peerCertificates[0])
+ if err != nil {
+ c.sendAlert(alertInternalError)
+ return err
+ }
+ if ckx != nil {
+ hs.finishedHash.Write(ckx.marshal())
+ if _, err := c.writeRecord(recordTypeHandshake, ckx.marshal()); err != nil {
+ return err
+ }
+ }
+
+ if chainToSend != nil && len(chainToSend.Certificate) > 0 {
+ certVerify := &certificateVerifyMsg{}
+
+ key, ok := chainToSend.PrivateKey.(crypto.Signer)
+ if !ok {
+ c.sendAlert(alertInternalError)
+ return fmt.Errorf("tls: client certificate private key of type %T does not implement crypto.Signer", chainToSend.PrivateKey)
+ }
+
+ var sigType uint8
+ var sigHash crypto.Hash
+ if c.vers >= VersionTLS12 {
+ signatureAlgorithm, err := selectSignatureScheme(c.vers, chainToSend, certReq.supportedSignatureAlgorithms)
+ if err != nil {
+ c.sendAlert(alertIllegalParameter)
+ return err
+ }
+ sigType, sigHash, err = typeAndHashFromSignatureScheme(signatureAlgorithm)
+ if err != nil {
+ return c.sendAlert(alertInternalError)
+ }
+ certVerify.hasSignatureAlgorithm = true
+ certVerify.signatureAlgorithm = signatureAlgorithm
+ } else {
+ sigType, sigHash, err = legacyTypeAndHashFromPublicKey(key.Public())
+ if err != nil {
+ c.sendAlert(alertIllegalParameter)
+ return err
+ }
+ }
+
+ signed := hs.finishedHash.hashForClientCertificate(sigType, sigHash, hs.masterSecret)
+ signOpts := crypto.SignerOpts(sigHash)
+ if sigType == signatureRSAPSS {
+ signOpts = &rsa.PSSOptions{SaltLength: rsa.PSSSaltLengthEqualsHash, Hash: sigHash}
+ }
+ certVerify.signature, err = key.Sign(c.config.rand(), signed, signOpts)
+ if err != nil {
+ c.sendAlert(alertInternalError)
+ return err
+ }
+
+ hs.finishedHash.Write(certVerify.marshal())
+ if _, err := c.writeRecord(recordTypeHandshake, certVerify.marshal()); err != nil {
+ return err
+ }
+ }
+
+ hs.masterSecret = masterFromPreMasterSecret(c.vers, hs.suite, preMasterSecret, hs.hello.random, hs.serverHello.random)
+ if err := c.config.writeKeyLog(keyLogLabelTLS12, hs.hello.random, hs.masterSecret); err != nil {
+ c.sendAlert(alertInternalError)
+ return errors.New("tls: failed to write to key log: " + err.Error())
+ }
+
+ hs.finishedHash.discardHandshakeBuffer()
+
+ return nil
+}
+
+func (hs *clientHandshakeState) establishKeys() error {
+ c := hs.c
+
+ clientMAC, serverMAC, clientKey, serverKey, clientIV, serverIV :=
+ keysFromMasterSecret(c.vers, hs.suite, hs.masterSecret, hs.hello.random, hs.serverHello.random, hs.suite.macLen, hs.suite.keyLen, hs.suite.ivLen)
+ var clientCipher, serverCipher any
+ var clientHash, serverHash hash.Hash
+ if hs.suite.cipher != nil {
+ clientCipher = hs.suite.cipher(clientKey, clientIV, false /* not for reading */)
+ clientHash = hs.suite.mac(clientMAC)
+ serverCipher = hs.suite.cipher(serverKey, serverIV, true /* for reading */)
+ serverHash = hs.suite.mac(serverMAC)
+ } else {
+ clientCipher = hs.suite.aead(clientKey, clientIV)
+ serverCipher = hs.suite.aead(serverKey, serverIV)
+ }
+
+ c.in.prepareCipherSpec(c.vers, serverCipher, serverHash)
+ c.out.prepareCipherSpec(c.vers, clientCipher, clientHash)
+ return nil
+}
+
+func (hs *clientHandshakeState) serverResumedSession() bool {
+ // If the server responded with the same sessionId then it means the
+ // sessionTicket is being used to resume a TLS session.
+ return hs.session != nil && hs.hello.sessionId != nil &&
+ bytes.Equal(hs.serverHello.sessionId, hs.hello.sessionId)
+}
+
+func (hs *clientHandshakeState) processServerHello() (bool, error) {
+ c := hs.c
+
+ if err := hs.pickCipherSuite(); err != nil {
+ return false, err
+ }
+
+ if hs.serverHello.compressionMethod != compressionNone {
+ c.sendAlert(alertUnexpectedMessage)
+ return false, errors.New("tls: server selected unsupported compression format")
+ }
+
+ if c.handshakes == 0 && hs.serverHello.secureRenegotiationSupported {
+ c.secureRenegotiation = true
+ if len(hs.serverHello.secureRenegotiation) != 0 {
+ c.sendAlert(alertHandshakeFailure)
+ return false, errors.New("tls: initial handshake had non-empty renegotiation extension")
+ }
+ }
+
+ if c.handshakes > 0 && c.secureRenegotiation {
+ var expectedSecureRenegotiation [24]byte
+ copy(expectedSecureRenegotiation[:], c.clientFinished[:])
+ copy(expectedSecureRenegotiation[12:], c.serverFinished[:])
+ if !bytes.Equal(hs.serverHello.secureRenegotiation, expectedSecureRenegotiation[:]) {
+ c.sendAlert(alertHandshakeFailure)
+ return false, errors.New("tls: incorrect renegotiation extension contents")
+ }
+ }
+
+ if err := checkALPN(hs.hello.alpnProtocols, hs.serverHello.alpnProtocol); err != nil {
+ c.sendAlert(alertUnsupportedExtension)
+ return false, err
+ }
+ c.clientProtocol = hs.serverHello.alpnProtocol
+
+ c.scts = hs.serverHello.scts
+
+ if !hs.serverResumedSession() {
+ return false, nil
+ }
+
+ if hs.session.vers != c.vers {
+ c.sendAlert(alertHandshakeFailure)
+ return false, errors.New("tls: server resumed a session with a different version")
+ }
+
+ if hs.session.cipherSuite != hs.suite.id {
+ c.sendAlert(alertHandshakeFailure)
+ return false, errors.New("tls: server resumed a session with a different cipher suite")
+ }
+
+ // Restore masterSecret, peerCerts, and ocspResponse from previous state
+ hs.masterSecret = hs.session.masterSecret
+ c.peerCertificates = hs.session.serverCertificates
+ c.verifiedChains = hs.session.verifiedChains
+ c.ocspResponse = hs.session.ocspResponse
+ // Let the ServerHello SCTs override the session SCTs from the original
+ // connection, if any are provided
+ if len(c.scts) == 0 && len(hs.session.scts) != 0 {
+ c.scts = hs.session.scts
+ }
+
+ return true, nil
+}
+
+// checkALPN ensure that the server's choice of ALPN protocol is compatible with
+// the protocols that we advertised in the Client Hello.
+func checkALPN(clientProtos []string, serverProto string) error {
+ if serverProto == "" {
+ return nil
+ }
+ if len(clientProtos) == 0 {
+ return errors.New("tls: server advertised unrequested ALPN extension")
+ }
+ for _, proto := range clientProtos {
+ if proto == serverProto {
+ return nil
+ }
+ }
+ return errors.New("tls: server selected unadvertised ALPN protocol")
+}
+
+func (hs *clientHandshakeState) readFinished(out []byte) error {
+ c := hs.c
+
+ if err := c.readChangeCipherSpec(); err != nil {
+ return err
+ }
+
+ msg, err := c.readHandshake()
+ if err != nil {
+ return err
+ }
+ serverFinished, ok := msg.(*finishedMsg)
+ if !ok {
+ c.sendAlert(alertUnexpectedMessage)
+ return unexpectedMessageError(serverFinished, msg)
+ }
+
+ verify := hs.finishedHash.serverSum(hs.masterSecret)
+ if len(verify) != len(serverFinished.verifyData) ||
+ subtle.ConstantTimeCompare(verify, serverFinished.verifyData) != 1 {
+ c.sendAlert(alertHandshakeFailure)
+ return errors.New("tls: server's Finished message was incorrect")
+ }
+ hs.finishedHash.Write(serverFinished.marshal())
+ copy(out, verify)
+ return nil
+}
+
+func (hs *clientHandshakeState) readSessionTicket() error {
+ if !hs.serverHello.ticketSupported {
+ return nil
+ }
+
+ c := hs.c
+ msg, err := c.readHandshake()
+ if err != nil {
+ return err
+ }
+ sessionTicketMsg, ok := msg.(*newSessionTicketMsg)
+ if !ok {
+ c.sendAlert(alertUnexpectedMessage)
+ return unexpectedMessageError(sessionTicketMsg, msg)
+ }
+ hs.finishedHash.Write(sessionTicketMsg.marshal())
+
+ hs.session = &clientSessionState{
+ sessionTicket: sessionTicketMsg.ticket,
+ vers: c.vers,
+ cipherSuite: hs.suite.id,
+ masterSecret: hs.masterSecret,
+ serverCertificates: c.peerCertificates,
+ verifiedChains: c.verifiedChains,
+ receivedAt: c.config.time(),
+ ocspResponse: c.ocspResponse,
+ scts: c.scts,
+ }
+
+ return nil
+}
+
+func (hs *clientHandshakeState) sendFinished(out []byte) error {
+ c := hs.c
+
+ if _, err := c.writeRecord(recordTypeChangeCipherSpec, []byte{1}); err != nil {
+ return err
+ }
+
+ finished := new(finishedMsg)
+ finished.verifyData = hs.finishedHash.clientSum(hs.masterSecret)
+ hs.finishedHash.Write(finished.marshal())
+ if _, err := c.writeRecord(recordTypeHandshake, finished.marshal()); err != nil {
+ return err
+ }
+ copy(out, finished.verifyData)
+ return nil
+}
+
+// verifyServerCertificate parses and verifies the provided chain, setting
+// c.verifiedChains and c.peerCertificates or sending the appropriate alert.
+func (c *Conn) verifyServerCertificate(certificates [][]byte) error {
+ certs := make([]*x509.Certificate, len(certificates))
+ for i, asn1Data := range certificates {
+ cert, err := x509.ParseCertificate(asn1Data)
+ if err != nil {
+ c.sendAlert(alertBadCertificate)
+ return errors.New("tls: failed to parse certificate from server: " + err.Error())
+ }
+ certs[i] = cert
+ }
+
+ if !c.config.InsecureSkipVerify {
+ opts := x509.VerifyOptions{
+ Roots: c.config.RootCAs,
+ CurrentTime: c.config.time(),
+ DNSName: c.config.ServerName,
+ Intermediates: x509.NewCertPool(),
+ }
+ for _, cert := range certs[1:] {
+ opts.Intermediates.AddCert(cert)
+ }
+ var err error
+ c.verifiedChains, err = certs[0].Verify(opts)
+ if err != nil {
+ c.sendAlert(alertBadCertificate)
+ return err
+ }
+ }
+
+ switch certs[0].PublicKey.(type) {
+ case *rsa.PublicKey, *ecdsa.PublicKey, ed25519.PublicKey:
+ break
+ default:
+ c.sendAlert(alertUnsupportedCertificate)
+ return fmt.Errorf("tls: server's certificate contains an unsupported type of public key: %T", certs[0].PublicKey)
+ }
+
+ c.peerCertificates = certs
+
+ if c.config.VerifyPeerCertificate != nil {
+ if err := c.config.VerifyPeerCertificate(certificates, c.verifiedChains); err != nil {
+ c.sendAlert(alertBadCertificate)
+ return err
+ }
+ }
+
+ if c.config.VerifyConnection != nil {
+ if err := c.config.VerifyConnection(c.connectionStateLocked()); err != nil {
+ c.sendAlert(alertBadCertificate)
+ return err
+ }
+ }
+
+ return nil
+}
+
+// certificateRequestInfoFromMsg generates a CertificateRequestInfo from a TLS
+// <= 1.2 CertificateRequest, making an effort to fill in missing information.
+func certificateRequestInfoFromMsg(ctx context.Context, vers uint16, certReq *certificateRequestMsg) *CertificateRequestInfo {
+ cri := &certificateRequestInfo{
+ AcceptableCAs: certReq.certificateAuthorities,
+ Version: vers,
+ ctx: ctx,
+ }
+
+ var rsaAvail, ecAvail bool
+ for _, certType := range certReq.certificateTypes {
+ switch certType {
+ case certTypeRSASign:
+ rsaAvail = true
+ case certTypeECDSASign:
+ ecAvail = true
+ }
+ }
+
+ if !certReq.hasSignatureAlgorithm {
+ // Prior to TLS 1.2, signature schemes did not exist. In this case we
+ // make up a list based on the acceptable certificate types, to help
+ // GetClientCertificate and SupportsCertificate select the right certificate.
+ // The hash part of the SignatureScheme is a lie here, because
+ // TLS 1.0 and 1.1 always use MD5+SHA1 for RSA and SHA1 for ECDSA.
+ switch {
+ case rsaAvail && ecAvail:
+ cri.SignatureSchemes = []SignatureScheme{
+ ECDSAWithP256AndSHA256, ECDSAWithP384AndSHA384, ECDSAWithP521AndSHA512,
+ PKCS1WithSHA256, PKCS1WithSHA384, PKCS1WithSHA512, PKCS1WithSHA1,
+ }
+ case rsaAvail:
+ cri.SignatureSchemes = []SignatureScheme{
+ PKCS1WithSHA256, PKCS1WithSHA384, PKCS1WithSHA512, PKCS1WithSHA1,
+ }
+ case ecAvail:
+ cri.SignatureSchemes = []SignatureScheme{
+ ECDSAWithP256AndSHA256, ECDSAWithP384AndSHA384, ECDSAWithP521AndSHA512,
+ }
+ }
+ return toCertificateRequestInfo(cri)
+ }
+
+ // Filter the signature schemes based on the certificate types.
+ // See RFC 5246, Section 7.4.4 (where it calls this "somewhat complicated").
+ cri.SignatureSchemes = make([]SignatureScheme, 0, len(certReq.supportedSignatureAlgorithms))
+ for _, sigScheme := range certReq.supportedSignatureAlgorithms {
+ sigType, _, err := typeAndHashFromSignatureScheme(sigScheme)
+ if err != nil {
+ continue
+ }
+ switch sigType {
+ case signatureECDSA, signatureEd25519:
+ if ecAvail {
+ cri.SignatureSchemes = append(cri.SignatureSchemes, sigScheme)
+ }
+ case signatureRSAPSS, signaturePKCS1v15:
+ if rsaAvail {
+ cri.SignatureSchemes = append(cri.SignatureSchemes, sigScheme)
+ }
+ }
+ }
+
+ return toCertificateRequestInfo(cri)
+}
+
+func (c *Conn) getClientCertificate(cri *CertificateRequestInfo) (*Certificate, error) {
+ if c.config.GetClientCertificate != nil {
+ return c.config.GetClientCertificate(cri)
+ }
+
+ for _, chain := range c.config.Certificates {
+ if err := cri.SupportsCertificate(&chain); err != nil {
+ continue
+ }
+ return &chain, nil
+ }
+
+ // No acceptable certificate found. Don't send a certificate.
+ return new(Certificate), nil
+}
+
+const clientSessionCacheKeyPrefix = "qtls-"
+
+// clientSessionCacheKey returns a key used to cache sessionTickets that could
+// be used to resume previously negotiated TLS sessions with a server.
+func clientSessionCacheKey(serverAddr net.Addr, config *config) string {
+ if len(config.ServerName) > 0 {
+ return clientSessionCacheKeyPrefix + config.ServerName
+ }
+ return clientSessionCacheKeyPrefix + serverAddr.String()
+}
+
+// hostnameInSNI converts name into an appropriate hostname for SNI.
+// Literal IP addresses and absolute FQDNs are not permitted as SNI values.
+// See RFC 6066, Section 3.
+func hostnameInSNI(name string) string {
+ host := name
+ if len(host) > 0 && host[0] == '[' && host[len(host)-1] == ']' {
+ host = host[1 : len(host)-1]
+ }
+ if i := strings.LastIndex(host, "%"); i > 0 {
+ host = host[:i]
+ }
+ if net.ParseIP(host) != nil {
+ return ""
+ }
+ for len(name) > 0 && name[len(name)-1] == '.' {
+ name = name[:len(name)-1]
+ }
+ return name
+}
diff --git a/vendor/github.com/quic-go/qtls-go1-18/handshake_client_tls13.go b/vendor/github.com/quic-go/qtls-go1-18/handshake_client_tls13.go
new file mode 100644
index 0000000000..09d602d0b2
--- /dev/null
+++ b/vendor/github.com/quic-go/qtls-go1-18/handshake_client_tls13.go
@@ -0,0 +1,734 @@
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package qtls
+
+import (
+ "bytes"
+ "context"
+ "crypto"
+ "crypto/hmac"
+ "crypto/rsa"
+ "encoding/binary"
+ "errors"
+ "hash"
+ "sync/atomic"
+ "time"
+
+ "golang.org/x/crypto/cryptobyte"
+)
+
+type clientHandshakeStateTLS13 struct {
+ c *Conn
+ ctx context.Context
+ serverHello *serverHelloMsg
+ hello *clientHelloMsg
+ ecdheParams ecdheParameters
+
+ session *clientSessionState
+ earlySecret []byte
+ binderKey []byte
+
+ certReq *certificateRequestMsgTLS13
+ usingPSK bool
+ sentDummyCCS bool
+ suite *cipherSuiteTLS13
+ transcript hash.Hash
+ masterSecret []byte
+ trafficSecret []byte // client_application_traffic_secret_0
+}
+
+// handshake requires hs.c, hs.hello, hs.serverHello, hs.ecdheParams, and,
+// optionally, hs.session, hs.earlySecret and hs.binderKey to be set.
+func (hs *clientHandshakeStateTLS13) handshake() error {
+ c := hs.c
+
+ // The server must not select TLS 1.3 in a renegotiation. See RFC 8446,
+ // sections 4.1.2 and 4.1.3.
+ if c.handshakes > 0 {
+ c.sendAlert(alertProtocolVersion)
+ return errors.New("tls: server selected TLS 1.3 in a renegotiation")
+ }
+
+ // Consistency check on the presence of a keyShare and its parameters.
+ if hs.ecdheParams == nil || len(hs.hello.keyShares) != 1 {
+ return c.sendAlert(alertInternalError)
+ }
+
+ if err := hs.checkServerHelloOrHRR(); err != nil {
+ return err
+ }
+
+ hs.transcript = hs.suite.hash.New()
+ hs.transcript.Write(hs.hello.marshal())
+
+ if bytes.Equal(hs.serverHello.random, helloRetryRequestRandom) {
+ if err := hs.sendDummyChangeCipherSpec(); err != nil {
+ return err
+ }
+ if err := hs.processHelloRetryRequest(); err != nil {
+ return err
+ }
+ }
+
+ hs.transcript.Write(hs.serverHello.marshal())
+
+ c.buffering = true
+ if err := hs.processServerHello(); err != nil {
+ return err
+ }
+ c.updateConnectionState()
+ if err := hs.sendDummyChangeCipherSpec(); err != nil {
+ return err
+ }
+ if err := hs.establishHandshakeKeys(); err != nil {
+ return err
+ }
+ if err := hs.readServerParameters(); err != nil {
+ return err
+ }
+ if err := hs.readServerCertificate(); err != nil {
+ return err
+ }
+ c.updateConnectionState()
+ if err := hs.readServerFinished(); err != nil {
+ return err
+ }
+ if err := hs.sendClientCertificate(); err != nil {
+ return err
+ }
+ if err := hs.sendClientFinished(); err != nil {
+ return err
+ }
+ if _, err := c.flush(); err != nil {
+ return err
+ }
+
+ atomic.StoreUint32(&c.handshakeStatus, 1)
+ c.updateConnectionState()
+ return nil
+}
+
+// checkServerHelloOrHRR does validity checks that apply to both ServerHello and
+// HelloRetryRequest messages. It sets hs.suite.
+func (hs *clientHandshakeStateTLS13) checkServerHelloOrHRR() error {
+ c := hs.c
+
+ if hs.serverHello.supportedVersion == 0 {
+ c.sendAlert(alertMissingExtension)
+ return errors.New("tls: server selected TLS 1.3 using the legacy version field")
+ }
+
+ if hs.serverHello.supportedVersion != VersionTLS13 {
+ c.sendAlert(alertIllegalParameter)
+ return errors.New("tls: server selected an invalid version after a HelloRetryRequest")
+ }
+
+ if hs.serverHello.vers != VersionTLS12 {
+ c.sendAlert(alertIllegalParameter)
+ return errors.New("tls: server sent an incorrect legacy version")
+ }
+
+ if hs.serverHello.ocspStapling ||
+ hs.serverHello.ticketSupported ||
+ hs.serverHello.secureRenegotiationSupported ||
+ len(hs.serverHello.secureRenegotiation) != 0 ||
+ len(hs.serverHello.alpnProtocol) != 0 ||
+ len(hs.serverHello.scts) != 0 {
+ c.sendAlert(alertUnsupportedExtension)
+ return errors.New("tls: server sent a ServerHello extension forbidden in TLS 1.3")
+ }
+
+ if !bytes.Equal(hs.hello.sessionId, hs.serverHello.sessionId) {
+ c.sendAlert(alertIllegalParameter)
+ return errors.New("tls: server did not echo the legacy session ID")
+ }
+
+ if hs.serverHello.compressionMethod != compressionNone {
+ c.sendAlert(alertIllegalParameter)
+ return errors.New("tls: server selected unsupported compression format")
+ }
+
+ selectedSuite := mutualCipherSuiteTLS13(hs.hello.cipherSuites, hs.serverHello.cipherSuite)
+ if hs.suite != nil && selectedSuite != hs.suite {
+ c.sendAlert(alertIllegalParameter)
+ return errors.New("tls: server changed cipher suite after a HelloRetryRequest")
+ }
+ if selectedSuite == nil {
+ c.sendAlert(alertIllegalParameter)
+ return errors.New("tls: server chose an unconfigured cipher suite")
+ }
+ hs.suite = selectedSuite
+ c.cipherSuite = hs.suite.id
+
+ return nil
+}
+
+// sendDummyChangeCipherSpec sends a ChangeCipherSpec record for compatibility
+// with middleboxes that didn't implement TLS correctly. See RFC 8446, Appendix D.4.
+func (hs *clientHandshakeStateTLS13) sendDummyChangeCipherSpec() error {
+ if hs.sentDummyCCS {
+ return nil
+ }
+ hs.sentDummyCCS = true
+
+ _, err := hs.c.writeRecord(recordTypeChangeCipherSpec, []byte{1})
+ return err
+}
+
+// processHelloRetryRequest handles the HRR in hs.serverHello, modifies and
+// resends hs.hello, and reads the new ServerHello into hs.serverHello.
+func (hs *clientHandshakeStateTLS13) processHelloRetryRequest() error {
+ c := hs.c
+
+ // The first ClientHello gets double-hashed into the transcript upon a
+ // HelloRetryRequest. (The idea is that the server might offload transcript
+ // storage to the client in the cookie.) See RFC 8446, Section 4.4.1.
+ chHash := hs.transcript.Sum(nil)
+ hs.transcript.Reset()
+ hs.transcript.Write([]byte{typeMessageHash, 0, 0, uint8(len(chHash))})
+ hs.transcript.Write(chHash)
+ hs.transcript.Write(hs.serverHello.marshal())
+
+ // The only HelloRetryRequest extensions we support are key_share and
+ // cookie, and clients must abort the handshake if the HRR would not result
+ // in any change in the ClientHello.
+ if hs.serverHello.selectedGroup == 0 && hs.serverHello.cookie == nil {
+ c.sendAlert(alertIllegalParameter)
+ return errors.New("tls: server sent an unnecessary HelloRetryRequest message")
+ }
+
+ if hs.serverHello.cookie != nil {
+ hs.hello.cookie = hs.serverHello.cookie
+ }
+
+ if hs.serverHello.serverShare.group != 0 {
+ c.sendAlert(alertDecodeError)
+ return errors.New("tls: received malformed key_share extension")
+ }
+
+ // If the server sent a key_share extension selecting a group, ensure it's
+ // a group we advertised but did not send a key share for, and send a key
+ // share for it this time.
+ if curveID := hs.serverHello.selectedGroup; curveID != 0 {
+ curveOK := false
+ for _, id := range hs.hello.supportedCurves {
+ if id == curveID {
+ curveOK = true
+ break
+ }
+ }
+ if !curveOK {
+ c.sendAlert(alertIllegalParameter)
+ return errors.New("tls: server selected unsupported group")
+ }
+ if hs.ecdheParams.CurveID() == curveID {
+ c.sendAlert(alertIllegalParameter)
+ return errors.New("tls: server sent an unnecessary HelloRetryRequest key_share")
+ }
+ if _, ok := curveForCurveID(curveID); curveID != X25519 && !ok {
+ c.sendAlert(alertInternalError)
+ return errors.New("tls: CurvePreferences includes unsupported curve")
+ }
+ params, err := generateECDHEParameters(c.config.rand(), curveID)
+ if err != nil {
+ c.sendAlert(alertInternalError)
+ return err
+ }
+ hs.ecdheParams = params
+ hs.hello.keyShares = []keyShare{{group: curveID, data: params.PublicKey()}}
+ }
+
+ hs.hello.raw = nil
+ if len(hs.hello.pskIdentities) > 0 {
+ pskSuite := cipherSuiteTLS13ByID(hs.session.cipherSuite)
+ if pskSuite == nil {
+ return c.sendAlert(alertInternalError)
+ }
+ if pskSuite.hash == hs.suite.hash {
+ // Update binders and obfuscated_ticket_age.
+ ticketAge := uint32(c.config.time().Sub(hs.session.receivedAt) / time.Millisecond)
+ hs.hello.pskIdentities[0].obfuscatedTicketAge = ticketAge + hs.session.ageAdd
+
+ transcript := hs.suite.hash.New()
+ transcript.Write([]byte{typeMessageHash, 0, 0, uint8(len(chHash))})
+ transcript.Write(chHash)
+ transcript.Write(hs.serverHello.marshal())
+ transcript.Write(hs.hello.marshalWithoutBinders())
+ pskBinders := [][]byte{hs.suite.finishedHash(hs.binderKey, transcript)}
+ hs.hello.updateBinders(pskBinders)
+ } else {
+ // Server selected a cipher suite incompatible with the PSK.
+ hs.hello.pskIdentities = nil
+ hs.hello.pskBinders = nil
+ }
+ }
+
+ if hs.hello.earlyData && c.extraConfig != nil && c.extraConfig.Rejected0RTT != nil {
+ c.extraConfig.Rejected0RTT()
+ }
+ hs.hello.earlyData = false // disable 0-RTT
+
+ hs.transcript.Write(hs.hello.marshal())
+ if _, err := c.writeRecord(recordTypeHandshake, hs.hello.marshal()); err != nil {
+ return err
+ }
+
+ msg, err := c.readHandshake()
+ if err != nil {
+ return err
+ }
+
+ serverHello, ok := msg.(*serverHelloMsg)
+ if !ok {
+ c.sendAlert(alertUnexpectedMessage)
+ return unexpectedMessageError(serverHello, msg)
+ }
+ hs.serverHello = serverHello
+
+ if err := hs.checkServerHelloOrHRR(); err != nil {
+ return err
+ }
+
+ return nil
+}
+
+func (hs *clientHandshakeStateTLS13) processServerHello() error {
+ c := hs.c
+
+ if bytes.Equal(hs.serverHello.random, helloRetryRequestRandom) {
+ c.sendAlert(alertUnexpectedMessage)
+ return errors.New("tls: server sent two HelloRetryRequest messages")
+ }
+
+ if len(hs.serverHello.cookie) != 0 {
+ c.sendAlert(alertUnsupportedExtension)
+ return errors.New("tls: server sent a cookie in a normal ServerHello")
+ }
+
+ if hs.serverHello.selectedGroup != 0 {
+ c.sendAlert(alertDecodeError)
+ return errors.New("tls: malformed key_share extension")
+ }
+
+ if hs.serverHello.serverShare.group == 0 {
+ c.sendAlert(alertIllegalParameter)
+ return errors.New("tls: server did not send a key share")
+ }
+ if hs.serverHello.serverShare.group != hs.ecdheParams.CurveID() {
+ c.sendAlert(alertIllegalParameter)
+ return errors.New("tls: server selected unsupported group")
+ }
+
+ if !hs.serverHello.selectedIdentityPresent {
+ return nil
+ }
+
+ if int(hs.serverHello.selectedIdentity) >= len(hs.hello.pskIdentities) {
+ c.sendAlert(alertIllegalParameter)
+ return errors.New("tls: server selected an invalid PSK")
+ }
+
+ if len(hs.hello.pskIdentities) != 1 || hs.session == nil {
+ return c.sendAlert(alertInternalError)
+ }
+ pskSuite := cipherSuiteTLS13ByID(hs.session.cipherSuite)
+ if pskSuite == nil {
+ return c.sendAlert(alertInternalError)
+ }
+ if pskSuite.hash != hs.suite.hash {
+ c.sendAlert(alertIllegalParameter)
+ return errors.New("tls: server selected an invalid PSK and cipher suite pair")
+ }
+
+ hs.usingPSK = true
+ c.didResume = true
+ c.peerCertificates = hs.session.serverCertificates
+ c.verifiedChains = hs.session.verifiedChains
+ c.ocspResponse = hs.session.ocspResponse
+ c.scts = hs.session.scts
+ return nil
+}
+
+func (hs *clientHandshakeStateTLS13) establishHandshakeKeys() error {
+ c := hs.c
+
+ sharedKey := hs.ecdheParams.SharedKey(hs.serverHello.serverShare.data)
+ if sharedKey == nil {
+ c.sendAlert(alertIllegalParameter)
+ return errors.New("tls: invalid server key share")
+ }
+
+ earlySecret := hs.earlySecret
+ if !hs.usingPSK {
+ earlySecret = hs.suite.extract(nil, nil)
+ }
+ handshakeSecret := hs.suite.extract(sharedKey,
+ hs.suite.deriveSecret(earlySecret, "derived", nil))
+
+ clientSecret := hs.suite.deriveSecret(handshakeSecret,
+ clientHandshakeTrafficLabel, hs.transcript)
+ c.out.exportKey(EncryptionHandshake, hs.suite, clientSecret)
+ c.out.setTrafficSecret(hs.suite, clientSecret)
+ serverSecret := hs.suite.deriveSecret(handshakeSecret,
+ serverHandshakeTrafficLabel, hs.transcript)
+ c.in.exportKey(EncryptionHandshake, hs.suite, serverSecret)
+ c.in.setTrafficSecret(hs.suite, serverSecret)
+
+ err := c.config.writeKeyLog(keyLogLabelClientHandshake, hs.hello.random, clientSecret)
+ if err != nil {
+ c.sendAlert(alertInternalError)
+ return err
+ }
+ err = c.config.writeKeyLog(keyLogLabelServerHandshake, hs.hello.random, serverSecret)
+ if err != nil {
+ c.sendAlert(alertInternalError)
+ return err
+ }
+
+ hs.masterSecret = hs.suite.extract(nil,
+ hs.suite.deriveSecret(handshakeSecret, "derived", nil))
+
+ return nil
+}
+
+func (hs *clientHandshakeStateTLS13) readServerParameters() error {
+ c := hs.c
+
+ msg, err := c.readHandshake()
+ if err != nil {
+ return err
+ }
+
+ encryptedExtensions, ok := msg.(*encryptedExtensionsMsg)
+ if !ok {
+ c.sendAlert(alertUnexpectedMessage)
+ return unexpectedMessageError(encryptedExtensions, msg)
+ }
+ // Notify the caller if 0-RTT was rejected.
+ if !encryptedExtensions.earlyData && hs.hello.earlyData && c.extraConfig != nil && c.extraConfig.Rejected0RTT != nil {
+ c.extraConfig.Rejected0RTT()
+ }
+ c.used0RTT = encryptedExtensions.earlyData
+ if hs.c.extraConfig != nil && hs.c.extraConfig.ReceivedExtensions != nil {
+ hs.c.extraConfig.ReceivedExtensions(typeEncryptedExtensions, encryptedExtensions.additionalExtensions)
+ }
+ hs.transcript.Write(encryptedExtensions.marshal())
+
+ if err := checkALPN(hs.hello.alpnProtocols, encryptedExtensions.alpnProtocol); err != nil {
+ c.sendAlert(alertUnsupportedExtension)
+ return err
+ }
+ c.clientProtocol = encryptedExtensions.alpnProtocol
+
+ if c.extraConfig != nil && c.extraConfig.EnforceNextProtoSelection {
+ if len(encryptedExtensions.alpnProtocol) == 0 {
+ // the server didn't select an ALPN
+ c.sendAlert(alertNoApplicationProtocol)
+ return errors.New("ALPN negotiation failed. Server didn't offer any protocols")
+ }
+ }
+ return nil
+}
+
+func (hs *clientHandshakeStateTLS13) readServerCertificate() error {
+ c := hs.c
+
+ // Either a PSK or a certificate is always used, but not both.
+ // See RFC 8446, Section 4.1.1.
+ if hs.usingPSK {
+ // Make sure the connection is still being verified whether or not this
+ // is a resumption. Resumptions currently don't reverify certificates so
+ // they don't call verifyServerCertificate. See Issue 31641.
+ if c.config.VerifyConnection != nil {
+ if err := c.config.VerifyConnection(c.connectionStateLocked()); err != nil {
+ c.sendAlert(alertBadCertificate)
+ return err
+ }
+ }
+ return nil
+ }
+
+ msg, err := c.readHandshake()
+ if err != nil {
+ return err
+ }
+
+ certReq, ok := msg.(*certificateRequestMsgTLS13)
+ if ok {
+ hs.transcript.Write(certReq.marshal())
+
+ hs.certReq = certReq
+
+ msg, err = c.readHandshake()
+ if err != nil {
+ return err
+ }
+ }
+
+ certMsg, ok := msg.(*certificateMsgTLS13)
+ if !ok {
+ c.sendAlert(alertUnexpectedMessage)
+ return unexpectedMessageError(certMsg, msg)
+ }
+ if len(certMsg.certificate.Certificate) == 0 {
+ c.sendAlert(alertDecodeError)
+ return errors.New("tls: received empty certificates message")
+ }
+ hs.transcript.Write(certMsg.marshal())
+
+ c.scts = certMsg.certificate.SignedCertificateTimestamps
+ c.ocspResponse = certMsg.certificate.OCSPStaple
+
+ if err := c.verifyServerCertificate(certMsg.certificate.Certificate); err != nil {
+ return err
+ }
+
+ msg, err = c.readHandshake()
+ if err != nil {
+ return err
+ }
+
+ certVerify, ok := msg.(*certificateVerifyMsg)
+ if !ok {
+ c.sendAlert(alertUnexpectedMessage)
+ return unexpectedMessageError(certVerify, msg)
+ }
+
+ // See RFC 8446, Section 4.4.3.
+ if !isSupportedSignatureAlgorithm(certVerify.signatureAlgorithm, supportedSignatureAlgorithms) {
+ c.sendAlert(alertIllegalParameter)
+ return errors.New("tls: certificate used with invalid signature algorithm")
+ }
+ sigType, sigHash, err := typeAndHashFromSignatureScheme(certVerify.signatureAlgorithm)
+ if err != nil {
+ return c.sendAlert(alertInternalError)
+ }
+ if sigType == signaturePKCS1v15 || sigHash == crypto.SHA1 {
+ c.sendAlert(alertIllegalParameter)
+ return errors.New("tls: certificate used with invalid signature algorithm")
+ }
+ signed := signedMessage(sigHash, serverSignatureContext, hs.transcript)
+ if err := verifyHandshakeSignature(sigType, c.peerCertificates[0].PublicKey,
+ sigHash, signed, certVerify.signature); err != nil {
+ c.sendAlert(alertDecryptError)
+ return errors.New("tls: invalid signature by the server certificate: " + err.Error())
+ }
+
+ hs.transcript.Write(certVerify.marshal())
+
+ return nil
+}
+
+func (hs *clientHandshakeStateTLS13) readServerFinished() error {
+ c := hs.c
+
+ msg, err := c.readHandshake()
+ if err != nil {
+ return err
+ }
+
+ finished, ok := msg.(*finishedMsg)
+ if !ok {
+ c.sendAlert(alertUnexpectedMessage)
+ return unexpectedMessageError(finished, msg)
+ }
+
+ expectedMAC := hs.suite.finishedHash(c.in.trafficSecret, hs.transcript)
+ if !hmac.Equal(expectedMAC, finished.verifyData) {
+ c.sendAlert(alertDecryptError)
+ return errors.New("tls: invalid server finished hash")
+ }
+
+ hs.transcript.Write(finished.marshal())
+
+ // Derive secrets that take context through the server Finished.
+
+ hs.trafficSecret = hs.suite.deriveSecret(hs.masterSecret,
+ clientApplicationTrafficLabel, hs.transcript)
+ serverSecret := hs.suite.deriveSecret(hs.masterSecret,
+ serverApplicationTrafficLabel, hs.transcript)
+ c.in.exportKey(EncryptionApplication, hs.suite, serverSecret)
+ c.in.setTrafficSecret(hs.suite, serverSecret)
+
+ err = c.config.writeKeyLog(keyLogLabelClientTraffic, hs.hello.random, hs.trafficSecret)
+ if err != nil {
+ c.sendAlert(alertInternalError)
+ return err
+ }
+ err = c.config.writeKeyLog(keyLogLabelServerTraffic, hs.hello.random, serverSecret)
+ if err != nil {
+ c.sendAlert(alertInternalError)
+ return err
+ }
+
+ c.ekm = hs.suite.exportKeyingMaterial(hs.masterSecret, hs.transcript)
+
+ return nil
+}
+
+func (hs *clientHandshakeStateTLS13) sendClientCertificate() error {
+ c := hs.c
+
+ if hs.certReq == nil {
+ return nil
+ }
+
+ cert, err := c.getClientCertificate(toCertificateRequestInfo(&certificateRequestInfo{
+ AcceptableCAs: hs.certReq.certificateAuthorities,
+ SignatureSchemes: hs.certReq.supportedSignatureAlgorithms,
+ Version: c.vers,
+ ctx: hs.ctx,
+ }))
+ if err != nil {
+ return err
+ }
+
+ certMsg := new(certificateMsgTLS13)
+
+ certMsg.certificate = *cert
+ certMsg.scts = hs.certReq.scts && len(cert.SignedCertificateTimestamps) > 0
+ certMsg.ocspStapling = hs.certReq.ocspStapling && len(cert.OCSPStaple) > 0
+
+ hs.transcript.Write(certMsg.marshal())
+ if _, err := c.writeRecord(recordTypeHandshake, certMsg.marshal()); err != nil {
+ return err
+ }
+
+ // If we sent an empty certificate message, skip the CertificateVerify.
+ if len(cert.Certificate) == 0 {
+ return nil
+ }
+
+ certVerifyMsg := new(certificateVerifyMsg)
+ certVerifyMsg.hasSignatureAlgorithm = true
+
+ certVerifyMsg.signatureAlgorithm, err = selectSignatureScheme(c.vers, cert, hs.certReq.supportedSignatureAlgorithms)
+ if err != nil {
+ // getClientCertificate returned a certificate incompatible with the
+ // CertificateRequestInfo supported signature algorithms.
+ c.sendAlert(alertHandshakeFailure)
+ return err
+ }
+
+ sigType, sigHash, err := typeAndHashFromSignatureScheme(certVerifyMsg.signatureAlgorithm)
+ if err != nil {
+ return c.sendAlert(alertInternalError)
+ }
+
+ signed := signedMessage(sigHash, clientSignatureContext, hs.transcript)
+ signOpts := crypto.SignerOpts(sigHash)
+ if sigType == signatureRSAPSS {
+ signOpts = &rsa.PSSOptions{SaltLength: rsa.PSSSaltLengthEqualsHash, Hash: sigHash}
+ }
+ sig, err := cert.PrivateKey.(crypto.Signer).Sign(c.config.rand(), signed, signOpts)
+ if err != nil {
+ c.sendAlert(alertInternalError)
+ return errors.New("tls: failed to sign handshake: " + err.Error())
+ }
+ certVerifyMsg.signature = sig
+
+ hs.transcript.Write(certVerifyMsg.marshal())
+ if _, err := c.writeRecord(recordTypeHandshake, certVerifyMsg.marshal()); err != nil {
+ return err
+ }
+
+ return nil
+}
+
+func (hs *clientHandshakeStateTLS13) sendClientFinished() error {
+ c := hs.c
+
+ finished := &finishedMsg{
+ verifyData: hs.suite.finishedHash(c.out.trafficSecret, hs.transcript),
+ }
+
+ hs.transcript.Write(finished.marshal())
+ if _, err := c.writeRecord(recordTypeHandshake, finished.marshal()); err != nil {
+ return err
+ }
+
+ c.out.exportKey(EncryptionApplication, hs.suite, hs.trafficSecret)
+ c.out.setTrafficSecret(hs.suite, hs.trafficSecret)
+
+ if !c.config.SessionTicketsDisabled && c.config.ClientSessionCache != nil {
+ c.resumptionSecret = hs.suite.deriveSecret(hs.masterSecret,
+ resumptionLabel, hs.transcript)
+ }
+
+ return nil
+}
+
+func (c *Conn) handleNewSessionTicket(msg *newSessionTicketMsgTLS13) error {
+ if !c.isClient {
+ c.sendAlert(alertUnexpectedMessage)
+ return errors.New("tls: received new session ticket from a client")
+ }
+
+ if c.config.SessionTicketsDisabled || c.config.ClientSessionCache == nil {
+ return nil
+ }
+
+ // See RFC 8446, Section 4.6.1.
+ if msg.lifetime == 0 {
+ return nil
+ }
+ lifetime := time.Duration(msg.lifetime) * time.Second
+ if lifetime > maxSessionTicketLifetime {
+ c.sendAlert(alertIllegalParameter)
+ return errors.New("tls: received a session ticket with invalid lifetime")
+ }
+
+ cipherSuite := cipherSuiteTLS13ByID(c.cipherSuite)
+ if cipherSuite == nil || c.resumptionSecret == nil {
+ return c.sendAlert(alertInternalError)
+ }
+
+ // We need to save the max_early_data_size that the server sent us, in order
+ // to decide if we're going to try 0-RTT with this ticket.
+ // However, at the same time, the qtls.ClientSessionTicket needs to be equal to
+ // the tls.ClientSessionTicket, so we can't just add a new field to the struct.
+ // We therefore abuse the nonce field (which is a byte slice)
+ nonceWithEarlyData := make([]byte, len(msg.nonce)+4)
+ binary.BigEndian.PutUint32(nonceWithEarlyData, msg.maxEarlyData)
+ copy(nonceWithEarlyData[4:], msg.nonce)
+
+ var appData []byte
+ if c.extraConfig != nil && c.extraConfig.GetAppDataForSessionState != nil {
+ appData = c.extraConfig.GetAppDataForSessionState()
+ }
+ var b cryptobyte.Builder
+ b.AddUint16(clientSessionStateVersion) // revision
+ b.AddUint32(msg.maxEarlyData)
+ b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
+ b.AddBytes(appData)
+ })
+ b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
+ b.AddBytes(msg.nonce)
+ })
+
+ // Save the resumption_master_secret and nonce instead of deriving the PSK
+ // to do the least amount of work on NewSessionTicket messages before we
+ // know if the ticket will be used. Forward secrecy of resumed connections
+ // is guaranteed by the requirement for pskModeDHE.
+ session := &clientSessionState{
+ sessionTicket: msg.label,
+ vers: c.vers,
+ cipherSuite: c.cipherSuite,
+ masterSecret: c.resumptionSecret,
+ serverCertificates: c.peerCertificates,
+ verifiedChains: c.verifiedChains,
+ receivedAt: c.config.time(),
+ nonce: b.BytesOrPanic(),
+ useBy: c.config.time().Add(lifetime),
+ ageAdd: msg.ageAdd,
+ ocspResponse: c.ocspResponse,
+ scts: c.scts,
+ }
+
+ cacheKey := clientSessionCacheKey(c.conn.RemoteAddr(), c.config)
+ c.config.ClientSessionCache.Put(cacheKey, toClientSessionState(session))
+
+ return nil
+}
diff --git a/vendor/github.com/quic-go/qtls-go1-18/handshake_messages.go b/vendor/github.com/quic-go/qtls-go1-18/handshake_messages.go
new file mode 100644
index 0000000000..5f87d4b81b
--- /dev/null
+++ b/vendor/github.com/quic-go/qtls-go1-18/handshake_messages.go
@@ -0,0 +1,1831 @@
+// Copyright 2009 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package qtls
+
+import (
+ "fmt"
+ "strings"
+
+ "golang.org/x/crypto/cryptobyte"
+)
+
+// The marshalingFunction type is an adapter to allow the use of ordinary
+// functions as cryptobyte.MarshalingValue.
+type marshalingFunction func(b *cryptobyte.Builder) error
+
+func (f marshalingFunction) Marshal(b *cryptobyte.Builder) error {
+ return f(b)
+}
+
+// addBytesWithLength appends a sequence of bytes to the cryptobyte.Builder. If
+// the length of the sequence is not the value specified, it produces an error.
+func addBytesWithLength(b *cryptobyte.Builder, v []byte, n int) {
+ b.AddValue(marshalingFunction(func(b *cryptobyte.Builder) error {
+ if len(v) != n {
+ return fmt.Errorf("invalid value length: expected %d, got %d", n, len(v))
+ }
+ b.AddBytes(v)
+ return nil
+ }))
+}
+
+// addUint64 appends a big-endian, 64-bit value to the cryptobyte.Builder.
+func addUint64(b *cryptobyte.Builder, v uint64) {
+ b.AddUint32(uint32(v >> 32))
+ b.AddUint32(uint32(v))
+}
+
+// readUint64 decodes a big-endian, 64-bit value into out and advances over it.
+// It reports whether the read was successful.
+func readUint64(s *cryptobyte.String, out *uint64) bool {
+ var hi, lo uint32
+ if !s.ReadUint32(&hi) || !s.ReadUint32(&lo) {
+ return false
+ }
+ *out = uint64(hi)<<32 | uint64(lo)
+ return true
+}
+
+// readUint8LengthPrefixed acts like s.ReadUint8LengthPrefixed, but targets a
+// []byte instead of a cryptobyte.String.
+func readUint8LengthPrefixed(s *cryptobyte.String, out *[]byte) bool {
+ return s.ReadUint8LengthPrefixed((*cryptobyte.String)(out))
+}
+
+// readUint16LengthPrefixed acts like s.ReadUint16LengthPrefixed, but targets a
+// []byte instead of a cryptobyte.String.
+func readUint16LengthPrefixed(s *cryptobyte.String, out *[]byte) bool {
+ return s.ReadUint16LengthPrefixed((*cryptobyte.String)(out))
+}
+
+// readUint24LengthPrefixed acts like s.ReadUint24LengthPrefixed, but targets a
+// []byte instead of a cryptobyte.String.
+func readUint24LengthPrefixed(s *cryptobyte.String, out *[]byte) bool {
+ return s.ReadUint24LengthPrefixed((*cryptobyte.String)(out))
+}
+
+type clientHelloMsg struct {
+ raw []byte
+ vers uint16
+ random []byte
+ sessionId []byte
+ cipherSuites []uint16
+ compressionMethods []uint8
+ serverName string
+ ocspStapling bool
+ supportedCurves []CurveID
+ supportedPoints []uint8
+ ticketSupported bool
+ sessionTicket []uint8
+ supportedSignatureAlgorithms []SignatureScheme
+ supportedSignatureAlgorithmsCert []SignatureScheme
+ secureRenegotiationSupported bool
+ secureRenegotiation []byte
+ alpnProtocols []string
+ scts bool
+ supportedVersions []uint16
+ cookie []byte
+ keyShares []keyShare
+ earlyData bool
+ pskModes []uint8
+ pskIdentities []pskIdentity
+ pskBinders [][]byte
+ additionalExtensions []Extension
+}
+
+func (m *clientHelloMsg) marshal() []byte {
+ if m.raw != nil {
+ return m.raw
+ }
+
+ var b cryptobyte.Builder
+ b.AddUint8(typeClientHello)
+ b.AddUint24LengthPrefixed(func(b *cryptobyte.Builder) {
+ b.AddUint16(m.vers)
+ addBytesWithLength(b, m.random, 32)
+ b.AddUint8LengthPrefixed(func(b *cryptobyte.Builder) {
+ b.AddBytes(m.sessionId)
+ })
+ b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
+ for _, suite := range m.cipherSuites {
+ b.AddUint16(suite)
+ }
+ })
+ b.AddUint8LengthPrefixed(func(b *cryptobyte.Builder) {
+ b.AddBytes(m.compressionMethods)
+ })
+
+ // If extensions aren't present, omit them.
+ var extensionsPresent bool
+ bWithoutExtensions := *b
+
+ b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
+ if len(m.serverName) > 0 {
+ // RFC 6066, Section 3
+ b.AddUint16(extensionServerName)
+ b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
+ b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
+ b.AddUint8(0) // name_type = host_name
+ b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
+ b.AddBytes([]byte(m.serverName))
+ })
+ })
+ })
+ }
+ if m.ocspStapling {
+ // RFC 4366, Section 3.6
+ b.AddUint16(extensionStatusRequest)
+ b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
+ b.AddUint8(1) // status_type = ocsp
+ b.AddUint16(0) // empty responder_id_list
+ b.AddUint16(0) // empty request_extensions
+ })
+ }
+ if len(m.supportedCurves) > 0 {
+ // RFC 4492, sections 5.1.1 and RFC 8446, Section 4.2.7
+ b.AddUint16(extensionSupportedCurves)
+ b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
+ b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
+ for _, curve := range m.supportedCurves {
+ b.AddUint16(uint16(curve))
+ }
+ })
+ })
+ }
+ if len(m.supportedPoints) > 0 {
+ // RFC 4492, Section 5.1.2
+ b.AddUint16(extensionSupportedPoints)
+ b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
+ b.AddUint8LengthPrefixed(func(b *cryptobyte.Builder) {
+ b.AddBytes(m.supportedPoints)
+ })
+ })
+ }
+ if m.ticketSupported {
+ // RFC 5077, Section 3.2
+ b.AddUint16(extensionSessionTicket)
+ b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
+ b.AddBytes(m.sessionTicket)
+ })
+ }
+ if len(m.supportedSignatureAlgorithms) > 0 {
+ // RFC 5246, Section 7.4.1.4.1
+ b.AddUint16(extensionSignatureAlgorithms)
+ b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
+ b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
+ for _, sigAlgo := range m.supportedSignatureAlgorithms {
+ b.AddUint16(uint16(sigAlgo))
+ }
+ })
+ })
+ }
+ if len(m.supportedSignatureAlgorithmsCert) > 0 {
+ // RFC 8446, Section 4.2.3
+ b.AddUint16(extensionSignatureAlgorithmsCert)
+ b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
+ b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
+ for _, sigAlgo := range m.supportedSignatureAlgorithmsCert {
+ b.AddUint16(uint16(sigAlgo))
+ }
+ })
+ })
+ }
+ if m.secureRenegotiationSupported {
+ // RFC 5746, Section 3.2
+ b.AddUint16(extensionRenegotiationInfo)
+ b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
+ b.AddUint8LengthPrefixed(func(b *cryptobyte.Builder) {
+ b.AddBytes(m.secureRenegotiation)
+ })
+ })
+ }
+ if len(m.alpnProtocols) > 0 {
+ // RFC 7301, Section 3.1
+ b.AddUint16(extensionALPN)
+ b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
+ b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
+ for _, proto := range m.alpnProtocols {
+ b.AddUint8LengthPrefixed(func(b *cryptobyte.Builder) {
+ b.AddBytes([]byte(proto))
+ })
+ }
+ })
+ })
+ }
+ if m.scts {
+ // RFC 6962, Section 3.3.1
+ b.AddUint16(extensionSCT)
+ b.AddUint16(0) // empty extension_data
+ }
+ if len(m.supportedVersions) > 0 {
+ // RFC 8446, Section 4.2.1
+ b.AddUint16(extensionSupportedVersions)
+ b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
+ b.AddUint8LengthPrefixed(func(b *cryptobyte.Builder) {
+ for _, vers := range m.supportedVersions {
+ b.AddUint16(vers)
+ }
+ })
+ })
+ }
+ if len(m.cookie) > 0 {
+ // RFC 8446, Section 4.2.2
+ b.AddUint16(extensionCookie)
+ b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
+ b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
+ b.AddBytes(m.cookie)
+ })
+ })
+ }
+ if len(m.keyShares) > 0 {
+ // RFC 8446, Section 4.2.8
+ b.AddUint16(extensionKeyShare)
+ b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
+ b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
+ for _, ks := range m.keyShares {
+ b.AddUint16(uint16(ks.group))
+ b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
+ b.AddBytes(ks.data)
+ })
+ }
+ })
+ })
+ }
+ if m.earlyData {
+ // RFC 8446, Section 4.2.10
+ b.AddUint16(extensionEarlyData)
+ b.AddUint16(0) // empty extension_data
+ }
+ if len(m.pskModes) > 0 {
+ // RFC 8446, Section 4.2.9
+ b.AddUint16(extensionPSKModes)
+ b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
+ b.AddUint8LengthPrefixed(func(b *cryptobyte.Builder) {
+ b.AddBytes(m.pskModes)
+ })
+ })
+ }
+ for _, ext := range m.additionalExtensions {
+ b.AddUint16(ext.Type)
+ b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
+ b.AddBytes(ext.Data)
+ })
+ }
+ if len(m.pskIdentities) > 0 { // pre_shared_key must be the last extension
+ // RFC 8446, Section 4.2.11
+ b.AddUint16(extensionPreSharedKey)
+ b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
+ b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
+ for _, psk := range m.pskIdentities {
+ b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
+ b.AddBytes(psk.label)
+ })
+ b.AddUint32(psk.obfuscatedTicketAge)
+ }
+ })
+ b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
+ for _, binder := range m.pskBinders {
+ b.AddUint8LengthPrefixed(func(b *cryptobyte.Builder) {
+ b.AddBytes(binder)
+ })
+ }
+ })
+ })
+ }
+
+ extensionsPresent = len(b.BytesOrPanic()) > 2
+ })
+
+ if !extensionsPresent {
+ *b = bWithoutExtensions
+ }
+ })
+
+ m.raw = b.BytesOrPanic()
+ return m.raw
+}
+
+// marshalWithoutBinders returns the ClientHello through the
+// PreSharedKeyExtension.identities field, according to RFC 8446, Section
+// 4.2.11.2. Note that m.pskBinders must be set to slices of the correct length.
+func (m *clientHelloMsg) marshalWithoutBinders() []byte {
+ bindersLen := 2 // uint16 length prefix
+ for _, binder := range m.pskBinders {
+ bindersLen += 1 // uint8 length prefix
+ bindersLen += len(binder)
+ }
+
+ fullMessage := m.marshal()
+ return fullMessage[:len(fullMessage)-bindersLen]
+}
+
+// updateBinders updates the m.pskBinders field, if necessary updating the
+// cached marshaled representation. The supplied binders must have the same
+// length as the current m.pskBinders.
+func (m *clientHelloMsg) updateBinders(pskBinders [][]byte) {
+ if len(pskBinders) != len(m.pskBinders) {
+ panic("tls: internal error: pskBinders length mismatch")
+ }
+ for i := range m.pskBinders {
+ if len(pskBinders[i]) != len(m.pskBinders[i]) {
+ panic("tls: internal error: pskBinders length mismatch")
+ }
+ }
+ m.pskBinders = pskBinders
+ if m.raw != nil {
+ lenWithoutBinders := len(m.marshalWithoutBinders())
+ b := cryptobyte.NewFixedBuilder(m.raw[:lenWithoutBinders])
+ b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
+ for _, binder := range m.pskBinders {
+ b.AddUint8LengthPrefixed(func(b *cryptobyte.Builder) {
+ b.AddBytes(binder)
+ })
+ }
+ })
+ if out, err := b.Bytes(); err != nil || len(out) != len(m.raw) {
+ panic("tls: internal error: failed to update binders")
+ }
+ }
+}
+
+func (m *clientHelloMsg) unmarshal(data []byte) bool {
+ *m = clientHelloMsg{raw: data}
+ s := cryptobyte.String(data)
+
+ if !s.Skip(4) || // message type and uint24 length field
+ !s.ReadUint16(&m.vers) || !s.ReadBytes(&m.random, 32) ||
+ !readUint8LengthPrefixed(&s, &m.sessionId) {
+ return false
+ }
+
+ var cipherSuites cryptobyte.String
+ if !s.ReadUint16LengthPrefixed(&cipherSuites) {
+ return false
+ }
+ m.cipherSuites = []uint16{}
+ m.secureRenegotiationSupported = false
+ for !cipherSuites.Empty() {
+ var suite uint16
+ if !cipherSuites.ReadUint16(&suite) {
+ return false
+ }
+ if suite == scsvRenegotiation {
+ m.secureRenegotiationSupported = true
+ }
+ m.cipherSuites = append(m.cipherSuites, suite)
+ }
+
+ if !readUint8LengthPrefixed(&s, &m.compressionMethods) {
+ return false
+ }
+
+ if s.Empty() {
+ // ClientHello is optionally followed by extension data
+ return true
+ }
+
+ var extensions cryptobyte.String
+ if !s.ReadUint16LengthPrefixed(&extensions) || !s.Empty() {
+ return false
+ }
+
+ for !extensions.Empty() {
+ var ext uint16
+ var extData cryptobyte.String
+ if !extensions.ReadUint16(&ext) ||
+ !extensions.ReadUint16LengthPrefixed(&extData) {
+ return false
+ }
+
+ switch ext {
+ case extensionServerName:
+ // RFC 6066, Section 3
+ var nameList cryptobyte.String
+ if !extData.ReadUint16LengthPrefixed(&nameList) || nameList.Empty() {
+ return false
+ }
+ for !nameList.Empty() {
+ var nameType uint8
+ var serverName cryptobyte.String
+ if !nameList.ReadUint8(&nameType) ||
+ !nameList.ReadUint16LengthPrefixed(&serverName) ||
+ serverName.Empty() {
+ return false
+ }
+ if nameType != 0 {
+ continue
+ }
+ if len(m.serverName) != 0 {
+ // Multiple names of the same name_type are prohibited.
+ return false
+ }
+ m.serverName = string(serverName)
+ // An SNI value may not include a trailing dot.
+ if strings.HasSuffix(m.serverName, ".") {
+ return false
+ }
+ }
+ case extensionStatusRequest:
+ // RFC 4366, Section 3.6
+ var statusType uint8
+ var ignored cryptobyte.String
+ if !extData.ReadUint8(&statusType) ||
+ !extData.ReadUint16LengthPrefixed(&ignored) ||
+ !extData.ReadUint16LengthPrefixed(&ignored) {
+ return false
+ }
+ m.ocspStapling = statusType == statusTypeOCSP
+ case extensionSupportedCurves:
+ // RFC 4492, sections 5.1.1 and RFC 8446, Section 4.2.7
+ var curves cryptobyte.String
+ if !extData.ReadUint16LengthPrefixed(&curves) || curves.Empty() {
+ return false
+ }
+ for !curves.Empty() {
+ var curve uint16
+ if !curves.ReadUint16(&curve) {
+ return false
+ }
+ m.supportedCurves = append(m.supportedCurves, CurveID(curve))
+ }
+ case extensionSupportedPoints:
+ // RFC 4492, Section 5.1.2
+ if !readUint8LengthPrefixed(&extData, &m.supportedPoints) ||
+ len(m.supportedPoints) == 0 {
+ return false
+ }
+ case extensionSessionTicket:
+ // RFC 5077, Section 3.2
+ m.ticketSupported = true
+ extData.ReadBytes(&m.sessionTicket, len(extData))
+ case extensionSignatureAlgorithms:
+ // RFC 5246, Section 7.4.1.4.1
+ var sigAndAlgs cryptobyte.String
+ if !extData.ReadUint16LengthPrefixed(&sigAndAlgs) || sigAndAlgs.Empty() {
+ return false
+ }
+ for !sigAndAlgs.Empty() {
+ var sigAndAlg uint16
+ if !sigAndAlgs.ReadUint16(&sigAndAlg) {
+ return false
+ }
+ m.supportedSignatureAlgorithms = append(
+ m.supportedSignatureAlgorithms, SignatureScheme(sigAndAlg))
+ }
+ case extensionSignatureAlgorithmsCert:
+ // RFC 8446, Section 4.2.3
+ var sigAndAlgs cryptobyte.String
+ if !extData.ReadUint16LengthPrefixed(&sigAndAlgs) || sigAndAlgs.Empty() {
+ return false
+ }
+ for !sigAndAlgs.Empty() {
+ var sigAndAlg uint16
+ if !sigAndAlgs.ReadUint16(&sigAndAlg) {
+ return false
+ }
+ m.supportedSignatureAlgorithmsCert = append(
+ m.supportedSignatureAlgorithmsCert, SignatureScheme(sigAndAlg))
+ }
+ case extensionRenegotiationInfo:
+ // RFC 5746, Section 3.2
+ if !readUint8LengthPrefixed(&extData, &m.secureRenegotiation) {
+ return false
+ }
+ m.secureRenegotiationSupported = true
+ case extensionALPN:
+ // RFC 7301, Section 3.1
+ var protoList cryptobyte.String
+ if !extData.ReadUint16LengthPrefixed(&protoList) || protoList.Empty() {
+ return false
+ }
+ for !protoList.Empty() {
+ var proto cryptobyte.String
+ if !protoList.ReadUint8LengthPrefixed(&proto) || proto.Empty() {
+ return false
+ }
+ m.alpnProtocols = append(m.alpnProtocols, string(proto))
+ }
+ case extensionSCT:
+ // RFC 6962, Section 3.3.1
+ m.scts = true
+ case extensionSupportedVersions:
+ // RFC 8446, Section 4.2.1
+ var versList cryptobyte.String
+ if !extData.ReadUint8LengthPrefixed(&versList) || versList.Empty() {
+ return false
+ }
+ for !versList.Empty() {
+ var vers uint16
+ if !versList.ReadUint16(&vers) {
+ return false
+ }
+ m.supportedVersions = append(m.supportedVersions, vers)
+ }
+ case extensionCookie:
+ // RFC 8446, Section 4.2.2
+ if !readUint16LengthPrefixed(&extData, &m.cookie) ||
+ len(m.cookie) == 0 {
+ return false
+ }
+ case extensionKeyShare:
+ // RFC 8446, Section 4.2.8
+ var clientShares cryptobyte.String
+ if !extData.ReadUint16LengthPrefixed(&clientShares) {
+ return false
+ }
+ for !clientShares.Empty() {
+ var ks keyShare
+ if !clientShares.ReadUint16((*uint16)(&ks.group)) ||
+ !readUint16LengthPrefixed(&clientShares, &ks.data) ||
+ len(ks.data) == 0 {
+ return false
+ }
+ m.keyShares = append(m.keyShares, ks)
+ }
+ case extensionEarlyData:
+ // RFC 8446, Section 4.2.10
+ m.earlyData = true
+ case extensionPSKModes:
+ // RFC 8446, Section 4.2.9
+ if !readUint8LengthPrefixed(&extData, &m.pskModes) {
+ return false
+ }
+ case extensionPreSharedKey:
+ // RFC 8446, Section 4.2.11
+ if !extensions.Empty() {
+ return false // pre_shared_key must be the last extension
+ }
+ var identities cryptobyte.String
+ if !extData.ReadUint16LengthPrefixed(&identities) || identities.Empty() {
+ return false
+ }
+ for !identities.Empty() {
+ var psk pskIdentity
+ if !readUint16LengthPrefixed(&identities, &psk.label) ||
+ !identities.ReadUint32(&psk.obfuscatedTicketAge) ||
+ len(psk.label) == 0 {
+ return false
+ }
+ m.pskIdentities = append(m.pskIdentities, psk)
+ }
+ var binders cryptobyte.String
+ if !extData.ReadUint16LengthPrefixed(&binders) || binders.Empty() {
+ return false
+ }
+ for !binders.Empty() {
+ var binder []byte
+ if !readUint8LengthPrefixed(&binders, &binder) ||
+ len(binder) == 0 {
+ return false
+ }
+ m.pskBinders = append(m.pskBinders, binder)
+ }
+ default:
+ m.additionalExtensions = append(m.additionalExtensions, Extension{Type: ext, Data: extData})
+ continue
+ }
+
+ if !extData.Empty() {
+ return false
+ }
+ }
+
+ return true
+}
+
+type serverHelloMsg struct {
+ raw []byte
+ vers uint16
+ random []byte
+ sessionId []byte
+ cipherSuite uint16
+ compressionMethod uint8
+ ocspStapling bool
+ ticketSupported bool
+ secureRenegotiationSupported bool
+ secureRenegotiation []byte
+ alpnProtocol string
+ scts [][]byte
+ supportedVersion uint16
+ serverShare keyShare
+ selectedIdentityPresent bool
+ selectedIdentity uint16
+ supportedPoints []uint8
+
+ // HelloRetryRequest extensions
+ cookie []byte
+ selectedGroup CurveID
+}
+
+func (m *serverHelloMsg) marshal() []byte {
+ if m.raw != nil {
+ return m.raw
+ }
+
+ var b cryptobyte.Builder
+ b.AddUint8(typeServerHello)
+ b.AddUint24LengthPrefixed(func(b *cryptobyte.Builder) {
+ b.AddUint16(m.vers)
+ addBytesWithLength(b, m.random, 32)
+ b.AddUint8LengthPrefixed(func(b *cryptobyte.Builder) {
+ b.AddBytes(m.sessionId)
+ })
+ b.AddUint16(m.cipherSuite)
+ b.AddUint8(m.compressionMethod)
+
+ // If extensions aren't present, omit them.
+ var extensionsPresent bool
+ bWithoutExtensions := *b
+
+ b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
+ if m.ocspStapling {
+ b.AddUint16(extensionStatusRequest)
+ b.AddUint16(0) // empty extension_data
+ }
+ if m.ticketSupported {
+ b.AddUint16(extensionSessionTicket)
+ b.AddUint16(0) // empty extension_data
+ }
+ if m.secureRenegotiationSupported {
+ b.AddUint16(extensionRenegotiationInfo)
+ b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
+ b.AddUint8LengthPrefixed(func(b *cryptobyte.Builder) {
+ b.AddBytes(m.secureRenegotiation)
+ })
+ })
+ }
+ if len(m.alpnProtocol) > 0 {
+ b.AddUint16(extensionALPN)
+ b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
+ b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
+ b.AddUint8LengthPrefixed(func(b *cryptobyte.Builder) {
+ b.AddBytes([]byte(m.alpnProtocol))
+ })
+ })
+ })
+ }
+ if len(m.scts) > 0 {
+ b.AddUint16(extensionSCT)
+ b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
+ b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
+ for _, sct := range m.scts {
+ b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
+ b.AddBytes(sct)
+ })
+ }
+ })
+ })
+ }
+ if m.supportedVersion != 0 {
+ b.AddUint16(extensionSupportedVersions)
+ b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
+ b.AddUint16(m.supportedVersion)
+ })
+ }
+ if m.serverShare.group != 0 {
+ b.AddUint16(extensionKeyShare)
+ b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
+ b.AddUint16(uint16(m.serverShare.group))
+ b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
+ b.AddBytes(m.serverShare.data)
+ })
+ })
+ }
+ if m.selectedIdentityPresent {
+ b.AddUint16(extensionPreSharedKey)
+ b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
+ b.AddUint16(m.selectedIdentity)
+ })
+ }
+
+ if len(m.cookie) > 0 {
+ b.AddUint16(extensionCookie)
+ b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
+ b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
+ b.AddBytes(m.cookie)
+ })
+ })
+ }
+ if m.selectedGroup != 0 {
+ b.AddUint16(extensionKeyShare)
+ b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
+ b.AddUint16(uint16(m.selectedGroup))
+ })
+ }
+ if len(m.supportedPoints) > 0 {
+ b.AddUint16(extensionSupportedPoints)
+ b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
+ b.AddUint8LengthPrefixed(func(b *cryptobyte.Builder) {
+ b.AddBytes(m.supportedPoints)
+ })
+ })
+ }
+
+ extensionsPresent = len(b.BytesOrPanic()) > 2
+ })
+
+ if !extensionsPresent {
+ *b = bWithoutExtensions
+ }
+ })
+
+ m.raw = b.BytesOrPanic()
+ return m.raw
+}
+
+func (m *serverHelloMsg) unmarshal(data []byte) bool {
+ *m = serverHelloMsg{raw: data}
+ s := cryptobyte.String(data)
+
+ if !s.Skip(4) || // message type and uint24 length field
+ !s.ReadUint16(&m.vers) || !s.ReadBytes(&m.random, 32) ||
+ !readUint8LengthPrefixed(&s, &m.sessionId) ||
+ !s.ReadUint16(&m.cipherSuite) ||
+ !s.ReadUint8(&m.compressionMethod) {
+ return false
+ }
+
+ if s.Empty() {
+ // ServerHello is optionally followed by extension data
+ return true
+ }
+
+ var extensions cryptobyte.String
+ if !s.ReadUint16LengthPrefixed(&extensions) || !s.Empty() {
+ return false
+ }
+
+ for !extensions.Empty() {
+ var extension uint16
+ var extData cryptobyte.String
+ if !extensions.ReadUint16(&extension) ||
+ !extensions.ReadUint16LengthPrefixed(&extData) {
+ return false
+ }
+
+ switch extension {
+ case extensionStatusRequest:
+ m.ocspStapling = true
+ case extensionSessionTicket:
+ m.ticketSupported = true
+ case extensionRenegotiationInfo:
+ if !readUint8LengthPrefixed(&extData, &m.secureRenegotiation) {
+ return false
+ }
+ m.secureRenegotiationSupported = true
+ case extensionALPN:
+ var protoList cryptobyte.String
+ if !extData.ReadUint16LengthPrefixed(&protoList) || protoList.Empty() {
+ return false
+ }
+ var proto cryptobyte.String
+ if !protoList.ReadUint8LengthPrefixed(&proto) ||
+ proto.Empty() || !protoList.Empty() {
+ return false
+ }
+ m.alpnProtocol = string(proto)
+ case extensionSCT:
+ var sctList cryptobyte.String
+ if !extData.ReadUint16LengthPrefixed(&sctList) || sctList.Empty() {
+ return false
+ }
+ for !sctList.Empty() {
+ var sct []byte
+ if !readUint16LengthPrefixed(&sctList, &sct) ||
+ len(sct) == 0 {
+ return false
+ }
+ m.scts = append(m.scts, sct)
+ }
+ case extensionSupportedVersions:
+ if !extData.ReadUint16(&m.supportedVersion) {
+ return false
+ }
+ case extensionCookie:
+ if !readUint16LengthPrefixed(&extData, &m.cookie) ||
+ len(m.cookie) == 0 {
+ return false
+ }
+ case extensionKeyShare:
+ // This extension has different formats in SH and HRR, accept either
+ // and let the handshake logic decide. See RFC 8446, Section 4.2.8.
+ if len(extData) == 2 {
+ if !extData.ReadUint16((*uint16)(&m.selectedGroup)) {
+ return false
+ }
+ } else {
+ if !extData.ReadUint16((*uint16)(&m.serverShare.group)) ||
+ !readUint16LengthPrefixed(&extData, &m.serverShare.data) {
+ return false
+ }
+ }
+ case extensionPreSharedKey:
+ m.selectedIdentityPresent = true
+ if !extData.ReadUint16(&m.selectedIdentity) {
+ return false
+ }
+ case extensionSupportedPoints:
+ // RFC 4492, Section 5.1.2
+ if !readUint8LengthPrefixed(&extData, &m.supportedPoints) ||
+ len(m.supportedPoints) == 0 {
+ return false
+ }
+ default:
+ // Ignore unknown extensions.
+ continue
+ }
+
+ if !extData.Empty() {
+ return false
+ }
+ }
+
+ return true
+}
+
+type encryptedExtensionsMsg struct {
+ raw []byte
+ alpnProtocol string
+ earlyData bool
+
+ additionalExtensions []Extension
+}
+
+func (m *encryptedExtensionsMsg) marshal() []byte {
+ if m.raw != nil {
+ return m.raw
+ }
+
+ var b cryptobyte.Builder
+ b.AddUint8(typeEncryptedExtensions)
+ b.AddUint24LengthPrefixed(func(b *cryptobyte.Builder) {
+ b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
+ if len(m.alpnProtocol) > 0 {
+ b.AddUint16(extensionALPN)
+ b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
+ b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
+ b.AddUint8LengthPrefixed(func(b *cryptobyte.Builder) {
+ b.AddBytes([]byte(m.alpnProtocol))
+ })
+ })
+ })
+ }
+ if m.earlyData {
+ // RFC 8446, Section 4.2.10
+ b.AddUint16(extensionEarlyData)
+ b.AddUint16(0) // empty extension_data
+ }
+ for _, ext := range m.additionalExtensions {
+ b.AddUint16(ext.Type)
+ b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
+ b.AddBytes(ext.Data)
+ })
+ }
+ })
+ })
+
+ m.raw = b.BytesOrPanic()
+ return m.raw
+}
+
+func (m *encryptedExtensionsMsg) unmarshal(data []byte) bool {
+ *m = encryptedExtensionsMsg{raw: data}
+ s := cryptobyte.String(data)
+
+ var extensions cryptobyte.String
+ if !s.Skip(4) || // message type and uint24 length field
+ !s.ReadUint16LengthPrefixed(&extensions) || !s.Empty() {
+ return false
+ }
+
+ for !extensions.Empty() {
+ var ext uint16
+ var extData cryptobyte.String
+ if !extensions.ReadUint16(&ext) ||
+ !extensions.ReadUint16LengthPrefixed(&extData) {
+ return false
+ }
+
+ switch ext {
+ case extensionALPN:
+ var protoList cryptobyte.String
+ if !extData.ReadUint16LengthPrefixed(&protoList) || protoList.Empty() {
+ return false
+ }
+ var proto cryptobyte.String
+ if !protoList.ReadUint8LengthPrefixed(&proto) ||
+ proto.Empty() || !protoList.Empty() {
+ return false
+ }
+ m.alpnProtocol = string(proto)
+ case extensionEarlyData:
+ m.earlyData = true
+ default:
+ m.additionalExtensions = append(m.additionalExtensions, Extension{Type: ext, Data: extData})
+ continue
+ }
+
+ if !extData.Empty() {
+ return false
+ }
+ }
+
+ return true
+}
+
+type endOfEarlyDataMsg struct{}
+
+func (m *endOfEarlyDataMsg) marshal() []byte {
+ x := make([]byte, 4)
+ x[0] = typeEndOfEarlyData
+ return x
+}
+
+func (m *endOfEarlyDataMsg) unmarshal(data []byte) bool {
+ return len(data) == 4
+}
+
+type keyUpdateMsg struct {
+ raw []byte
+ updateRequested bool
+}
+
+func (m *keyUpdateMsg) marshal() []byte {
+ if m.raw != nil {
+ return m.raw
+ }
+
+ var b cryptobyte.Builder
+ b.AddUint8(typeKeyUpdate)
+ b.AddUint24LengthPrefixed(func(b *cryptobyte.Builder) {
+ if m.updateRequested {
+ b.AddUint8(1)
+ } else {
+ b.AddUint8(0)
+ }
+ })
+
+ m.raw = b.BytesOrPanic()
+ return m.raw
+}
+
+func (m *keyUpdateMsg) unmarshal(data []byte) bool {
+ m.raw = data
+ s := cryptobyte.String(data)
+
+ var updateRequested uint8
+ if !s.Skip(4) || // message type and uint24 length field
+ !s.ReadUint8(&updateRequested) || !s.Empty() {
+ return false
+ }
+ switch updateRequested {
+ case 0:
+ m.updateRequested = false
+ case 1:
+ m.updateRequested = true
+ default:
+ return false
+ }
+ return true
+}
+
+type newSessionTicketMsgTLS13 struct {
+ raw []byte
+ lifetime uint32
+ ageAdd uint32
+ nonce []byte
+ label []byte
+ maxEarlyData uint32
+}
+
+func (m *newSessionTicketMsgTLS13) marshal() []byte {
+ if m.raw != nil {
+ return m.raw
+ }
+
+ var b cryptobyte.Builder
+ b.AddUint8(typeNewSessionTicket)
+ b.AddUint24LengthPrefixed(func(b *cryptobyte.Builder) {
+ b.AddUint32(m.lifetime)
+ b.AddUint32(m.ageAdd)
+ b.AddUint8LengthPrefixed(func(b *cryptobyte.Builder) {
+ b.AddBytes(m.nonce)
+ })
+ b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
+ b.AddBytes(m.label)
+ })
+
+ b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
+ if m.maxEarlyData > 0 {
+ b.AddUint16(extensionEarlyData)
+ b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
+ b.AddUint32(m.maxEarlyData)
+ })
+ }
+ })
+ })
+
+ m.raw = b.BytesOrPanic()
+ return m.raw
+}
+
+func (m *newSessionTicketMsgTLS13) unmarshal(data []byte) bool {
+ *m = newSessionTicketMsgTLS13{raw: data}
+ s := cryptobyte.String(data)
+
+ var extensions cryptobyte.String
+ if !s.Skip(4) || // message type and uint24 length field
+ !s.ReadUint32(&m.lifetime) ||
+ !s.ReadUint32(&m.ageAdd) ||
+ !readUint8LengthPrefixed(&s, &m.nonce) ||
+ !readUint16LengthPrefixed(&s, &m.label) ||
+ !s.ReadUint16LengthPrefixed(&extensions) ||
+ !s.Empty() {
+ return false
+ }
+
+ for !extensions.Empty() {
+ var extension uint16
+ var extData cryptobyte.String
+ if !extensions.ReadUint16(&extension) ||
+ !extensions.ReadUint16LengthPrefixed(&extData) {
+ return false
+ }
+
+ switch extension {
+ case extensionEarlyData:
+ if !extData.ReadUint32(&m.maxEarlyData) {
+ return false
+ }
+ default:
+ // Ignore unknown extensions.
+ continue
+ }
+
+ if !extData.Empty() {
+ return false
+ }
+ }
+
+ return true
+}
+
+type certificateRequestMsgTLS13 struct {
+ raw []byte
+ ocspStapling bool
+ scts bool
+ supportedSignatureAlgorithms []SignatureScheme
+ supportedSignatureAlgorithmsCert []SignatureScheme
+ certificateAuthorities [][]byte
+}
+
+func (m *certificateRequestMsgTLS13) marshal() []byte {
+ if m.raw != nil {
+ return m.raw
+ }
+
+ var b cryptobyte.Builder
+ b.AddUint8(typeCertificateRequest)
+ b.AddUint24LengthPrefixed(func(b *cryptobyte.Builder) {
+ // certificate_request_context (SHALL be zero length unless used for
+ // post-handshake authentication)
+ b.AddUint8(0)
+
+ b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
+ if m.ocspStapling {
+ b.AddUint16(extensionStatusRequest)
+ b.AddUint16(0) // empty extension_data
+ }
+ if m.scts {
+ // RFC 8446, Section 4.4.2.1 makes no mention of
+ // signed_certificate_timestamp in CertificateRequest, but
+ // "Extensions in the Certificate message from the client MUST
+ // correspond to extensions in the CertificateRequest message
+ // from the server." and it appears in the table in Section 4.2.
+ b.AddUint16(extensionSCT)
+ b.AddUint16(0) // empty extension_data
+ }
+ if len(m.supportedSignatureAlgorithms) > 0 {
+ b.AddUint16(extensionSignatureAlgorithms)
+ b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
+ b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
+ for _, sigAlgo := range m.supportedSignatureAlgorithms {
+ b.AddUint16(uint16(sigAlgo))
+ }
+ })
+ })
+ }
+ if len(m.supportedSignatureAlgorithmsCert) > 0 {
+ b.AddUint16(extensionSignatureAlgorithmsCert)
+ b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
+ b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
+ for _, sigAlgo := range m.supportedSignatureAlgorithmsCert {
+ b.AddUint16(uint16(sigAlgo))
+ }
+ })
+ })
+ }
+ if len(m.certificateAuthorities) > 0 {
+ b.AddUint16(extensionCertificateAuthorities)
+ b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
+ b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
+ for _, ca := range m.certificateAuthorities {
+ b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
+ b.AddBytes(ca)
+ })
+ }
+ })
+ })
+ }
+ })
+ })
+
+ m.raw = b.BytesOrPanic()
+ return m.raw
+}
+
+func (m *certificateRequestMsgTLS13) unmarshal(data []byte) bool {
+ *m = certificateRequestMsgTLS13{raw: data}
+ s := cryptobyte.String(data)
+
+ var context, extensions cryptobyte.String
+ if !s.Skip(4) || // message type and uint24 length field
+ !s.ReadUint8LengthPrefixed(&context) || !context.Empty() ||
+ !s.ReadUint16LengthPrefixed(&extensions) ||
+ !s.Empty() {
+ return false
+ }
+
+ for !extensions.Empty() {
+ var extension uint16
+ var extData cryptobyte.String
+ if !extensions.ReadUint16(&extension) ||
+ !extensions.ReadUint16LengthPrefixed(&extData) {
+ return false
+ }
+
+ switch extension {
+ case extensionStatusRequest:
+ m.ocspStapling = true
+ case extensionSCT:
+ m.scts = true
+ case extensionSignatureAlgorithms:
+ var sigAndAlgs cryptobyte.String
+ if !extData.ReadUint16LengthPrefixed(&sigAndAlgs) || sigAndAlgs.Empty() {
+ return false
+ }
+ for !sigAndAlgs.Empty() {
+ var sigAndAlg uint16
+ if !sigAndAlgs.ReadUint16(&sigAndAlg) {
+ return false
+ }
+ m.supportedSignatureAlgorithms = append(
+ m.supportedSignatureAlgorithms, SignatureScheme(sigAndAlg))
+ }
+ case extensionSignatureAlgorithmsCert:
+ var sigAndAlgs cryptobyte.String
+ if !extData.ReadUint16LengthPrefixed(&sigAndAlgs) || sigAndAlgs.Empty() {
+ return false
+ }
+ for !sigAndAlgs.Empty() {
+ var sigAndAlg uint16
+ if !sigAndAlgs.ReadUint16(&sigAndAlg) {
+ return false
+ }
+ m.supportedSignatureAlgorithmsCert = append(
+ m.supportedSignatureAlgorithmsCert, SignatureScheme(sigAndAlg))
+ }
+ case extensionCertificateAuthorities:
+ var auths cryptobyte.String
+ if !extData.ReadUint16LengthPrefixed(&auths) || auths.Empty() {
+ return false
+ }
+ for !auths.Empty() {
+ var ca []byte
+ if !readUint16LengthPrefixed(&auths, &ca) || len(ca) == 0 {
+ return false
+ }
+ m.certificateAuthorities = append(m.certificateAuthorities, ca)
+ }
+ default:
+ // Ignore unknown extensions.
+ continue
+ }
+
+ if !extData.Empty() {
+ return false
+ }
+ }
+
+ return true
+}
+
+type certificateMsg struct {
+ raw []byte
+ certificates [][]byte
+}
+
+func (m *certificateMsg) marshal() (x []byte) {
+ if m.raw != nil {
+ return m.raw
+ }
+
+ var i int
+ for _, slice := range m.certificates {
+ i += len(slice)
+ }
+
+ length := 3 + 3*len(m.certificates) + i
+ x = make([]byte, 4+length)
+ x[0] = typeCertificate
+ x[1] = uint8(length >> 16)
+ x[2] = uint8(length >> 8)
+ x[3] = uint8(length)
+
+ certificateOctets := length - 3
+ x[4] = uint8(certificateOctets >> 16)
+ x[5] = uint8(certificateOctets >> 8)
+ x[6] = uint8(certificateOctets)
+
+ y := x[7:]
+ for _, slice := range m.certificates {
+ y[0] = uint8(len(slice) >> 16)
+ y[1] = uint8(len(slice) >> 8)
+ y[2] = uint8(len(slice))
+ copy(y[3:], slice)
+ y = y[3+len(slice):]
+ }
+
+ m.raw = x
+ return
+}
+
+func (m *certificateMsg) unmarshal(data []byte) bool {
+ if len(data) < 7 {
+ return false
+ }
+
+ m.raw = data
+ certsLen := uint32(data[4])<<16 | uint32(data[5])<<8 | uint32(data[6])
+ if uint32(len(data)) != certsLen+7 {
+ return false
+ }
+
+ numCerts := 0
+ d := data[7:]
+ for certsLen > 0 {
+ if len(d) < 4 {
+ return false
+ }
+ certLen := uint32(d[0])<<16 | uint32(d[1])<<8 | uint32(d[2])
+ if uint32(len(d)) < 3+certLen {
+ return false
+ }
+ d = d[3+certLen:]
+ certsLen -= 3 + certLen
+ numCerts++
+ }
+
+ m.certificates = make([][]byte, numCerts)
+ d = data[7:]
+ for i := 0; i < numCerts; i++ {
+ certLen := uint32(d[0])<<16 | uint32(d[1])<<8 | uint32(d[2])
+ m.certificates[i] = d[3 : 3+certLen]
+ d = d[3+certLen:]
+ }
+
+ return true
+}
+
+type certificateMsgTLS13 struct {
+ raw []byte
+ certificate Certificate
+ ocspStapling bool
+ scts bool
+}
+
+func (m *certificateMsgTLS13) marshal() []byte {
+ if m.raw != nil {
+ return m.raw
+ }
+
+ var b cryptobyte.Builder
+ b.AddUint8(typeCertificate)
+ b.AddUint24LengthPrefixed(func(b *cryptobyte.Builder) {
+ b.AddUint8(0) // certificate_request_context
+
+ certificate := m.certificate
+ if !m.ocspStapling {
+ certificate.OCSPStaple = nil
+ }
+ if !m.scts {
+ certificate.SignedCertificateTimestamps = nil
+ }
+ marshalCertificate(b, certificate)
+ })
+
+ m.raw = b.BytesOrPanic()
+ return m.raw
+}
+
+func marshalCertificate(b *cryptobyte.Builder, certificate Certificate) {
+ b.AddUint24LengthPrefixed(func(b *cryptobyte.Builder) {
+ for i, cert := range certificate.Certificate {
+ b.AddUint24LengthPrefixed(func(b *cryptobyte.Builder) {
+ b.AddBytes(cert)
+ })
+ b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
+ if i > 0 {
+ // This library only supports OCSP and SCT for leaf certificates.
+ return
+ }
+ if certificate.OCSPStaple != nil {
+ b.AddUint16(extensionStatusRequest)
+ b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
+ b.AddUint8(statusTypeOCSP)
+ b.AddUint24LengthPrefixed(func(b *cryptobyte.Builder) {
+ b.AddBytes(certificate.OCSPStaple)
+ })
+ })
+ }
+ if certificate.SignedCertificateTimestamps != nil {
+ b.AddUint16(extensionSCT)
+ b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
+ b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
+ for _, sct := range certificate.SignedCertificateTimestamps {
+ b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
+ b.AddBytes(sct)
+ })
+ }
+ })
+ })
+ }
+ })
+ }
+ })
+}
+
+func (m *certificateMsgTLS13) unmarshal(data []byte) bool {
+ *m = certificateMsgTLS13{raw: data}
+ s := cryptobyte.String(data)
+
+ var context cryptobyte.String
+ if !s.Skip(4) || // message type and uint24 length field
+ !s.ReadUint8LengthPrefixed(&context) || !context.Empty() ||
+ !unmarshalCertificate(&s, &m.certificate) ||
+ !s.Empty() {
+ return false
+ }
+
+ m.scts = m.certificate.SignedCertificateTimestamps != nil
+ m.ocspStapling = m.certificate.OCSPStaple != nil
+
+ return true
+}
+
+func unmarshalCertificate(s *cryptobyte.String, certificate *Certificate) bool {
+ var certList cryptobyte.String
+ if !s.ReadUint24LengthPrefixed(&certList) {
+ return false
+ }
+ for !certList.Empty() {
+ var cert []byte
+ var extensions cryptobyte.String
+ if !readUint24LengthPrefixed(&certList, &cert) ||
+ !certList.ReadUint16LengthPrefixed(&extensions) {
+ return false
+ }
+ certificate.Certificate = append(certificate.Certificate, cert)
+ for !extensions.Empty() {
+ var extension uint16
+ var extData cryptobyte.String
+ if !extensions.ReadUint16(&extension) ||
+ !extensions.ReadUint16LengthPrefixed(&extData) {
+ return false
+ }
+ if len(certificate.Certificate) > 1 {
+ // This library only supports OCSP and SCT for leaf certificates.
+ continue
+ }
+
+ switch extension {
+ case extensionStatusRequest:
+ var statusType uint8
+ if !extData.ReadUint8(&statusType) || statusType != statusTypeOCSP ||
+ !readUint24LengthPrefixed(&extData, &certificate.OCSPStaple) ||
+ len(certificate.OCSPStaple) == 0 {
+ return false
+ }
+ case extensionSCT:
+ var sctList cryptobyte.String
+ if !extData.ReadUint16LengthPrefixed(&sctList) || sctList.Empty() {
+ return false
+ }
+ for !sctList.Empty() {
+ var sct []byte
+ if !readUint16LengthPrefixed(&sctList, &sct) ||
+ len(sct) == 0 {
+ return false
+ }
+ certificate.SignedCertificateTimestamps = append(
+ certificate.SignedCertificateTimestamps, sct)
+ }
+ default:
+ // Ignore unknown extensions.
+ continue
+ }
+
+ if !extData.Empty() {
+ return false
+ }
+ }
+ }
+ return true
+}
+
+type serverKeyExchangeMsg struct {
+ raw []byte
+ key []byte
+}
+
+func (m *serverKeyExchangeMsg) marshal() []byte {
+ if m.raw != nil {
+ return m.raw
+ }
+ length := len(m.key)
+ x := make([]byte, length+4)
+ x[0] = typeServerKeyExchange
+ x[1] = uint8(length >> 16)
+ x[2] = uint8(length >> 8)
+ x[3] = uint8(length)
+ copy(x[4:], m.key)
+
+ m.raw = x
+ return x
+}
+
+func (m *serverKeyExchangeMsg) unmarshal(data []byte) bool {
+ m.raw = data
+ if len(data) < 4 {
+ return false
+ }
+ m.key = data[4:]
+ return true
+}
+
+type certificateStatusMsg struct {
+ raw []byte
+ response []byte
+}
+
+func (m *certificateStatusMsg) marshal() []byte {
+ if m.raw != nil {
+ return m.raw
+ }
+
+ var b cryptobyte.Builder
+ b.AddUint8(typeCertificateStatus)
+ b.AddUint24LengthPrefixed(func(b *cryptobyte.Builder) {
+ b.AddUint8(statusTypeOCSP)
+ b.AddUint24LengthPrefixed(func(b *cryptobyte.Builder) {
+ b.AddBytes(m.response)
+ })
+ })
+
+ m.raw = b.BytesOrPanic()
+ return m.raw
+}
+
+func (m *certificateStatusMsg) unmarshal(data []byte) bool {
+ m.raw = data
+ s := cryptobyte.String(data)
+
+ var statusType uint8
+ if !s.Skip(4) || // message type and uint24 length field
+ !s.ReadUint8(&statusType) || statusType != statusTypeOCSP ||
+ !readUint24LengthPrefixed(&s, &m.response) ||
+ len(m.response) == 0 || !s.Empty() {
+ return false
+ }
+ return true
+}
+
+type serverHelloDoneMsg struct{}
+
+func (m *serverHelloDoneMsg) marshal() []byte {
+ x := make([]byte, 4)
+ x[0] = typeServerHelloDone
+ return x
+}
+
+func (m *serverHelloDoneMsg) unmarshal(data []byte) bool {
+ return len(data) == 4
+}
+
+type clientKeyExchangeMsg struct {
+ raw []byte
+ ciphertext []byte
+}
+
+func (m *clientKeyExchangeMsg) marshal() []byte {
+ if m.raw != nil {
+ return m.raw
+ }
+ length := len(m.ciphertext)
+ x := make([]byte, length+4)
+ x[0] = typeClientKeyExchange
+ x[1] = uint8(length >> 16)
+ x[2] = uint8(length >> 8)
+ x[3] = uint8(length)
+ copy(x[4:], m.ciphertext)
+
+ m.raw = x
+ return x
+}
+
+func (m *clientKeyExchangeMsg) unmarshal(data []byte) bool {
+ m.raw = data
+ if len(data) < 4 {
+ return false
+ }
+ l := int(data[1])<<16 | int(data[2])<<8 | int(data[3])
+ if l != len(data)-4 {
+ return false
+ }
+ m.ciphertext = data[4:]
+ return true
+}
+
+type finishedMsg struct {
+ raw []byte
+ verifyData []byte
+}
+
+func (m *finishedMsg) marshal() []byte {
+ if m.raw != nil {
+ return m.raw
+ }
+
+ var b cryptobyte.Builder
+ b.AddUint8(typeFinished)
+ b.AddUint24LengthPrefixed(func(b *cryptobyte.Builder) {
+ b.AddBytes(m.verifyData)
+ })
+
+ m.raw = b.BytesOrPanic()
+ return m.raw
+}
+
+func (m *finishedMsg) unmarshal(data []byte) bool {
+ m.raw = data
+ s := cryptobyte.String(data)
+ return s.Skip(1) &&
+ readUint24LengthPrefixed(&s, &m.verifyData) &&
+ s.Empty()
+}
+
+type certificateRequestMsg struct {
+ raw []byte
+ // hasSignatureAlgorithm indicates whether this message includes a list of
+ // supported signature algorithms. This change was introduced with TLS 1.2.
+ hasSignatureAlgorithm bool
+
+ certificateTypes []byte
+ supportedSignatureAlgorithms []SignatureScheme
+ certificateAuthorities [][]byte
+}
+
+func (m *certificateRequestMsg) marshal() (x []byte) {
+ if m.raw != nil {
+ return m.raw
+ }
+
+ // See RFC 4346, Section 7.4.4.
+ length := 1 + len(m.certificateTypes) + 2
+ casLength := 0
+ for _, ca := range m.certificateAuthorities {
+ casLength += 2 + len(ca)
+ }
+ length += casLength
+
+ if m.hasSignatureAlgorithm {
+ length += 2 + 2*len(m.supportedSignatureAlgorithms)
+ }
+
+ x = make([]byte, 4+length)
+ x[0] = typeCertificateRequest
+ x[1] = uint8(length >> 16)
+ x[2] = uint8(length >> 8)
+ x[3] = uint8(length)
+
+ x[4] = uint8(len(m.certificateTypes))
+
+ copy(x[5:], m.certificateTypes)
+ y := x[5+len(m.certificateTypes):]
+
+ if m.hasSignatureAlgorithm {
+ n := len(m.supportedSignatureAlgorithms) * 2
+ y[0] = uint8(n >> 8)
+ y[1] = uint8(n)
+ y = y[2:]
+ for _, sigAlgo := range m.supportedSignatureAlgorithms {
+ y[0] = uint8(sigAlgo >> 8)
+ y[1] = uint8(sigAlgo)
+ y = y[2:]
+ }
+ }
+
+ y[0] = uint8(casLength >> 8)
+ y[1] = uint8(casLength)
+ y = y[2:]
+ for _, ca := range m.certificateAuthorities {
+ y[0] = uint8(len(ca) >> 8)
+ y[1] = uint8(len(ca))
+ y = y[2:]
+ copy(y, ca)
+ y = y[len(ca):]
+ }
+
+ m.raw = x
+ return
+}
+
+func (m *certificateRequestMsg) unmarshal(data []byte) bool {
+ m.raw = data
+
+ if len(data) < 5 {
+ return false
+ }
+
+ length := uint32(data[1])<<16 | uint32(data[2])<<8 | uint32(data[3])
+ if uint32(len(data))-4 != length {
+ return false
+ }
+
+ numCertTypes := int(data[4])
+ data = data[5:]
+ if numCertTypes == 0 || len(data) <= numCertTypes {
+ return false
+ }
+
+ m.certificateTypes = make([]byte, numCertTypes)
+ if copy(m.certificateTypes, data) != numCertTypes {
+ return false
+ }
+
+ data = data[numCertTypes:]
+
+ if m.hasSignatureAlgorithm {
+ if len(data) < 2 {
+ return false
+ }
+ sigAndHashLen := uint16(data[0])<<8 | uint16(data[1])
+ data = data[2:]
+ if sigAndHashLen&1 != 0 {
+ return false
+ }
+ if len(data) < int(sigAndHashLen) {
+ return false
+ }
+ numSigAlgos := sigAndHashLen / 2
+ m.supportedSignatureAlgorithms = make([]SignatureScheme, numSigAlgos)
+ for i := range m.supportedSignatureAlgorithms {
+ m.supportedSignatureAlgorithms[i] = SignatureScheme(data[0])<<8 | SignatureScheme(data[1])
+ data = data[2:]
+ }
+ }
+
+ if len(data) < 2 {
+ return false
+ }
+ casLength := uint16(data[0])<<8 | uint16(data[1])
+ data = data[2:]
+ if len(data) < int(casLength) {
+ return false
+ }
+ cas := make([]byte, casLength)
+ copy(cas, data)
+ data = data[casLength:]
+
+ m.certificateAuthorities = nil
+ for len(cas) > 0 {
+ if len(cas) < 2 {
+ return false
+ }
+ caLen := uint16(cas[0])<<8 | uint16(cas[1])
+ cas = cas[2:]
+
+ if len(cas) < int(caLen) {
+ return false
+ }
+
+ m.certificateAuthorities = append(m.certificateAuthorities, cas[:caLen])
+ cas = cas[caLen:]
+ }
+
+ return len(data) == 0
+}
+
+type certificateVerifyMsg struct {
+ raw []byte
+ hasSignatureAlgorithm bool // format change introduced in TLS 1.2
+ signatureAlgorithm SignatureScheme
+ signature []byte
+}
+
+func (m *certificateVerifyMsg) marshal() (x []byte) {
+ if m.raw != nil {
+ return m.raw
+ }
+
+ var b cryptobyte.Builder
+ b.AddUint8(typeCertificateVerify)
+ b.AddUint24LengthPrefixed(func(b *cryptobyte.Builder) {
+ if m.hasSignatureAlgorithm {
+ b.AddUint16(uint16(m.signatureAlgorithm))
+ }
+ b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
+ b.AddBytes(m.signature)
+ })
+ })
+
+ m.raw = b.BytesOrPanic()
+ return m.raw
+}
+
+func (m *certificateVerifyMsg) unmarshal(data []byte) bool {
+ m.raw = data
+ s := cryptobyte.String(data)
+
+ if !s.Skip(4) { // message type and uint24 length field
+ return false
+ }
+ if m.hasSignatureAlgorithm {
+ if !s.ReadUint16((*uint16)(&m.signatureAlgorithm)) {
+ return false
+ }
+ }
+ return readUint16LengthPrefixed(&s, &m.signature) && s.Empty()
+}
+
+type newSessionTicketMsg struct {
+ raw []byte
+ ticket []byte
+}
+
+func (m *newSessionTicketMsg) marshal() (x []byte) {
+ if m.raw != nil {
+ return m.raw
+ }
+
+ // See RFC 5077, Section 3.3.
+ ticketLen := len(m.ticket)
+ length := 2 + 4 + ticketLen
+ x = make([]byte, 4+length)
+ x[0] = typeNewSessionTicket
+ x[1] = uint8(length >> 16)
+ x[2] = uint8(length >> 8)
+ x[3] = uint8(length)
+ x[8] = uint8(ticketLen >> 8)
+ x[9] = uint8(ticketLen)
+ copy(x[10:], m.ticket)
+
+ m.raw = x
+
+ return
+}
+
+func (m *newSessionTicketMsg) unmarshal(data []byte) bool {
+ m.raw = data
+
+ if len(data) < 10 {
+ return false
+ }
+
+ length := uint32(data[1])<<16 | uint32(data[2])<<8 | uint32(data[3])
+ if uint32(len(data))-4 != length {
+ return false
+ }
+
+ ticketLen := int(data[8])<<8 + int(data[9])
+ if len(data)-10 != ticketLen {
+ return false
+ }
+
+ m.ticket = data[10:]
+
+ return true
+}
+
+type helloRequestMsg struct {
+}
+
+func (*helloRequestMsg) marshal() []byte {
+ return []byte{typeHelloRequest, 0, 0, 0}
+}
+
+func (*helloRequestMsg) unmarshal(data []byte) bool {
+ return len(data) == 4
+}
diff --git a/vendor/github.com/quic-go/qtls-go1-18/handshake_server.go b/vendor/github.com/quic-go/qtls-go1-18/handshake_server.go
new file mode 100644
index 0000000000..a6519d7f58
--- /dev/null
+++ b/vendor/github.com/quic-go/qtls-go1-18/handshake_server.go
@@ -0,0 +1,913 @@
+// Copyright 2009 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package qtls
+
+import (
+ "context"
+ "crypto"
+ "crypto/ecdsa"
+ "crypto/ed25519"
+ "crypto/rsa"
+ "crypto/subtle"
+ "crypto/x509"
+ "errors"
+ "fmt"
+ "hash"
+ "io"
+ "sync/atomic"
+ "time"
+)
+
+// serverHandshakeState contains details of a server handshake in progress.
+// It's discarded once the handshake has completed.
+type serverHandshakeState struct {
+ c *Conn
+ ctx context.Context
+ clientHello *clientHelloMsg
+ hello *serverHelloMsg
+ suite *cipherSuite
+ ecdheOk bool
+ ecSignOk bool
+ rsaDecryptOk bool
+ rsaSignOk bool
+ sessionState *sessionState
+ finishedHash finishedHash
+ masterSecret []byte
+ cert *Certificate
+}
+
+// serverHandshake performs a TLS handshake as a server.
+func (c *Conn) serverHandshake(ctx context.Context) error {
+ c.setAlternativeRecordLayer()
+
+ clientHello, err := c.readClientHello(ctx)
+ if err != nil {
+ return err
+ }
+
+ if c.vers == VersionTLS13 {
+ hs := serverHandshakeStateTLS13{
+ c: c,
+ ctx: ctx,
+ clientHello: clientHello,
+ }
+ return hs.handshake()
+ } else if c.extraConfig.usesAlternativeRecordLayer() {
+ // This should already have been caught by the check that the ClientHello doesn't
+ // offer any (supported) versions older than TLS 1.3.
+ // Check again to make sure we can't be tricked into using an older version.
+ c.sendAlert(alertProtocolVersion)
+ return errors.New("tls: negotiated TLS < 1.3 when using QUIC")
+ }
+
+ hs := serverHandshakeState{
+ c: c,
+ ctx: ctx,
+ clientHello: clientHello,
+ }
+ return hs.handshake()
+}
+
+func (hs *serverHandshakeState) handshake() error {
+ c := hs.c
+
+ if err := hs.processClientHello(); err != nil {
+ return err
+ }
+
+ // For an overview of TLS handshaking, see RFC 5246, Section 7.3.
+ c.buffering = true
+ if hs.checkForResumption() {
+ // The client has included a session ticket and so we do an abbreviated handshake.
+ c.didResume = true
+ if err := hs.doResumeHandshake(); err != nil {
+ return err
+ }
+ if err := hs.establishKeys(); err != nil {
+ return err
+ }
+ if err := hs.sendSessionTicket(); err != nil {
+ return err
+ }
+ if err := hs.sendFinished(c.serverFinished[:]); err != nil {
+ return err
+ }
+ if _, err := c.flush(); err != nil {
+ return err
+ }
+ c.clientFinishedIsFirst = false
+ if err := hs.readFinished(nil); err != nil {
+ return err
+ }
+ } else {
+ // The client didn't include a session ticket, or it wasn't
+ // valid so we do a full handshake.
+ if err := hs.pickCipherSuite(); err != nil {
+ return err
+ }
+ if err := hs.doFullHandshake(); err != nil {
+ return err
+ }
+ if err := hs.establishKeys(); err != nil {
+ return err
+ }
+ if err := hs.readFinished(c.clientFinished[:]); err != nil {
+ return err
+ }
+ c.clientFinishedIsFirst = true
+ c.buffering = true
+ if err := hs.sendSessionTicket(); err != nil {
+ return err
+ }
+ if err := hs.sendFinished(nil); err != nil {
+ return err
+ }
+ if _, err := c.flush(); err != nil {
+ return err
+ }
+ }
+
+ c.ekm = ekmFromMasterSecret(c.vers, hs.suite, hs.masterSecret, hs.clientHello.random, hs.hello.random)
+ atomic.StoreUint32(&c.handshakeStatus, 1)
+
+ c.updateConnectionState()
+ return nil
+}
+
+// readClientHello reads a ClientHello message and selects the protocol version.
+func (c *Conn) readClientHello(ctx context.Context) (*clientHelloMsg, error) {
+ msg, err := c.readHandshake()
+ if err != nil {
+ return nil, err
+ }
+ clientHello, ok := msg.(*clientHelloMsg)
+ if !ok {
+ c.sendAlert(alertUnexpectedMessage)
+ return nil, unexpectedMessageError(clientHello, msg)
+ }
+
+ var configForClient *config
+ originalConfig := c.config
+ if c.config.GetConfigForClient != nil {
+ chi := newClientHelloInfo(ctx, c, clientHello)
+ if cfc, err := c.config.GetConfigForClient(chi); err != nil {
+ c.sendAlert(alertInternalError)
+ return nil, err
+ } else if cfc != nil {
+ configForClient = fromConfig(cfc)
+ c.config = configForClient
+ }
+ }
+ c.ticketKeys = originalConfig.ticketKeys(configForClient)
+
+ clientVersions := clientHello.supportedVersions
+ if len(clientHello.supportedVersions) == 0 {
+ clientVersions = supportedVersionsFromMax(clientHello.vers)
+ }
+ if c.extraConfig.usesAlternativeRecordLayer() {
+ // In QUIC, the client MUST NOT offer any old TLS versions.
+ // Here, we can only check that none of the other supported versions of this library
+ // (TLS 1.0 - TLS 1.2) is offered. We don't check for any SSL versions here.
+ for _, ver := range clientVersions {
+ if ver == VersionTLS13 {
+ continue
+ }
+ for _, v := range supportedVersions {
+ if ver == v {
+ c.sendAlert(alertProtocolVersion)
+ return nil, fmt.Errorf("tls: client offered old TLS version %#x", ver)
+ }
+ }
+ }
+ // Make the config we're using allows us to use TLS 1.3.
+ if c.config.maxSupportedVersion(roleServer) < VersionTLS13 {
+ c.sendAlert(alertInternalError)
+ return nil, errors.New("tls: MaxVersion prevents QUIC from using TLS 1.3")
+ }
+ }
+ c.vers, ok = c.config.mutualVersion(roleServer, clientVersions)
+ if !ok {
+ c.sendAlert(alertProtocolVersion)
+ return nil, fmt.Errorf("tls: client offered only unsupported versions: %x", clientVersions)
+ }
+ c.haveVers = true
+ c.in.version = c.vers
+ c.out.version = c.vers
+
+ return clientHello, nil
+}
+
+func (hs *serverHandshakeState) processClientHello() error {
+ c := hs.c
+
+ hs.hello = new(serverHelloMsg)
+ hs.hello.vers = c.vers
+
+ foundCompression := false
+ // We only support null compression, so check that the client offered it.
+ for _, compression := range hs.clientHello.compressionMethods {
+ if compression == compressionNone {
+ foundCompression = true
+ break
+ }
+ }
+
+ if !foundCompression {
+ c.sendAlert(alertHandshakeFailure)
+ return errors.New("tls: client does not support uncompressed connections")
+ }
+
+ hs.hello.random = make([]byte, 32)
+ serverRandom := hs.hello.random
+ // Downgrade protection canaries. See RFC 8446, Section 4.1.3.
+ maxVers := c.config.maxSupportedVersion(roleServer)
+ if maxVers >= VersionTLS12 && c.vers < maxVers || testingOnlyForceDowngradeCanary {
+ if c.vers == VersionTLS12 {
+ copy(serverRandom[24:], downgradeCanaryTLS12)
+ } else {
+ copy(serverRandom[24:], downgradeCanaryTLS11)
+ }
+ serverRandom = serverRandom[:24]
+ }
+ _, err := io.ReadFull(c.config.rand(), serverRandom)
+ if err != nil {
+ c.sendAlert(alertInternalError)
+ return err
+ }
+
+ if len(hs.clientHello.secureRenegotiation) != 0 {
+ c.sendAlert(alertHandshakeFailure)
+ return errors.New("tls: initial handshake had non-empty renegotiation extension")
+ }
+
+ hs.hello.secureRenegotiationSupported = hs.clientHello.secureRenegotiationSupported
+ hs.hello.compressionMethod = compressionNone
+ if len(hs.clientHello.serverName) > 0 {
+ c.serverName = hs.clientHello.serverName
+ }
+
+ selectedProto, err := negotiateALPN(c.config.NextProtos, hs.clientHello.alpnProtocols)
+ if err != nil {
+ c.sendAlert(alertNoApplicationProtocol)
+ return err
+ }
+ hs.hello.alpnProtocol = selectedProto
+ c.clientProtocol = selectedProto
+
+ hs.cert, err = c.config.getCertificate(newClientHelloInfo(hs.ctx, c, hs.clientHello))
+ if err != nil {
+ if err == errNoCertificates {
+ c.sendAlert(alertUnrecognizedName)
+ } else {
+ c.sendAlert(alertInternalError)
+ }
+ return err
+ }
+ if hs.clientHello.scts {
+ hs.hello.scts = hs.cert.SignedCertificateTimestamps
+ }
+
+ hs.ecdheOk = supportsECDHE(c.config, hs.clientHello.supportedCurves, hs.clientHello.supportedPoints)
+
+ if hs.ecdheOk && len(hs.clientHello.supportedPoints) > 0 {
+ // Although omitting the ec_point_formats extension is permitted, some
+ // old OpenSSL version will refuse to handshake if not present.
+ //
+ // Per RFC 4492, section 5.1.2, implementations MUST support the
+ // uncompressed point format. See golang.org/issue/31943.
+ hs.hello.supportedPoints = []uint8{pointFormatUncompressed}
+ }
+
+ if priv, ok := hs.cert.PrivateKey.(crypto.Signer); ok {
+ switch priv.Public().(type) {
+ case *ecdsa.PublicKey:
+ hs.ecSignOk = true
+ case ed25519.PublicKey:
+ hs.ecSignOk = true
+ case *rsa.PublicKey:
+ hs.rsaSignOk = true
+ default:
+ c.sendAlert(alertInternalError)
+ return fmt.Errorf("tls: unsupported signing key type (%T)", priv.Public())
+ }
+ }
+ if priv, ok := hs.cert.PrivateKey.(crypto.Decrypter); ok {
+ switch priv.Public().(type) {
+ case *rsa.PublicKey:
+ hs.rsaDecryptOk = true
+ default:
+ c.sendAlert(alertInternalError)
+ return fmt.Errorf("tls: unsupported decryption key type (%T)", priv.Public())
+ }
+ }
+
+ return nil
+}
+
+// negotiateALPN picks a shared ALPN protocol that both sides support in server
+// preference order. If ALPN is not configured or the peer doesn't support it,
+// it returns "" and no error.
+func negotiateALPN(serverProtos, clientProtos []string) (string, error) {
+ if len(serverProtos) == 0 || len(clientProtos) == 0 {
+ return "", nil
+ }
+ var http11fallback bool
+ for _, s := range serverProtos {
+ for _, c := range clientProtos {
+ if s == c {
+ return s, nil
+ }
+ if s == "h2" && c == "http/1.1" {
+ http11fallback = true
+ }
+ }
+ }
+ // As a special case, let http/1.1 clients connect to h2 servers as if they
+ // didn't support ALPN. We used not to enforce protocol overlap, so over
+ // time a number of HTTP servers were configured with only "h2", but
+ // expected to accept connections from "http/1.1" clients. See Issue 46310.
+ if http11fallback {
+ return "", nil
+ }
+ return "", fmt.Errorf("tls: client requested unsupported application protocols (%s)", clientProtos)
+}
+
+// supportsECDHE returns whether ECDHE key exchanges can be used with this
+// pre-TLS 1.3 client.
+func supportsECDHE(c *config, supportedCurves []CurveID, supportedPoints []uint8) bool {
+ supportsCurve := false
+ for _, curve := range supportedCurves {
+ if c.supportsCurve(curve) {
+ supportsCurve = true
+ break
+ }
+ }
+
+ supportsPointFormat := false
+ for _, pointFormat := range supportedPoints {
+ if pointFormat == pointFormatUncompressed {
+ supportsPointFormat = true
+ break
+ }
+ }
+ // Per RFC 8422, Section 5.1.2, if the Supported Point Formats extension is
+ // missing, uncompressed points are supported. If supportedPoints is empty,
+ // the extension must be missing, as an empty extension body is rejected by
+ // the parser. See https://go.dev/issue/49126.
+ if len(supportedPoints) == 0 {
+ supportsPointFormat = true
+ }
+
+ return supportsCurve && supportsPointFormat
+}
+
+func (hs *serverHandshakeState) pickCipherSuite() error {
+ c := hs.c
+
+ preferenceOrder := cipherSuitesPreferenceOrder
+ if !hasAESGCMHardwareSupport || !aesgcmPreferred(hs.clientHello.cipherSuites) {
+ preferenceOrder = cipherSuitesPreferenceOrderNoAES
+ }
+
+ configCipherSuites := c.config.cipherSuites()
+ preferenceList := make([]uint16, 0, len(configCipherSuites))
+ for _, suiteID := range preferenceOrder {
+ for _, id := range configCipherSuites {
+ if id == suiteID {
+ preferenceList = append(preferenceList, id)
+ break
+ }
+ }
+ }
+
+ hs.suite = selectCipherSuite(preferenceList, hs.clientHello.cipherSuites, hs.cipherSuiteOk)
+ if hs.suite == nil {
+ c.sendAlert(alertHandshakeFailure)
+ return errors.New("tls: no cipher suite supported by both client and server")
+ }
+ c.cipherSuite = hs.suite.id
+
+ for _, id := range hs.clientHello.cipherSuites {
+ if id == TLS_FALLBACK_SCSV {
+ // The client is doing a fallback connection. See RFC 7507.
+ if hs.clientHello.vers < c.config.maxSupportedVersion(roleServer) {
+ c.sendAlert(alertInappropriateFallback)
+ return errors.New("tls: client using inappropriate protocol fallback")
+ }
+ break
+ }
+ }
+
+ return nil
+}
+
+func (hs *serverHandshakeState) cipherSuiteOk(c *cipherSuite) bool {
+ if c.flags&suiteECDHE != 0 {
+ if !hs.ecdheOk {
+ return false
+ }
+ if c.flags&suiteECSign != 0 {
+ if !hs.ecSignOk {
+ return false
+ }
+ } else if !hs.rsaSignOk {
+ return false
+ }
+ } else if !hs.rsaDecryptOk {
+ return false
+ }
+ if hs.c.vers < VersionTLS12 && c.flags&suiteTLS12 != 0 {
+ return false
+ }
+ return true
+}
+
+// checkForResumption reports whether we should perform resumption on this connection.
+func (hs *serverHandshakeState) checkForResumption() bool {
+ c := hs.c
+
+ if c.config.SessionTicketsDisabled {
+ return false
+ }
+
+ plaintext, usedOldKey := c.decryptTicket(hs.clientHello.sessionTicket)
+ if plaintext == nil {
+ return false
+ }
+ hs.sessionState = &sessionState{usedOldKey: usedOldKey}
+ ok := hs.sessionState.unmarshal(plaintext)
+ if !ok {
+ return false
+ }
+
+ createdAt := time.Unix(int64(hs.sessionState.createdAt), 0)
+ if c.config.time().Sub(createdAt) > maxSessionTicketLifetime {
+ return false
+ }
+
+ // Never resume a session for a different TLS version.
+ if c.vers != hs.sessionState.vers {
+ return false
+ }
+
+ cipherSuiteOk := false
+ // Check that the client is still offering the ciphersuite in the session.
+ for _, id := range hs.clientHello.cipherSuites {
+ if id == hs.sessionState.cipherSuite {
+ cipherSuiteOk = true
+ break
+ }
+ }
+ if !cipherSuiteOk {
+ return false
+ }
+
+ // Check that we also support the ciphersuite from the session.
+ hs.suite = selectCipherSuite([]uint16{hs.sessionState.cipherSuite},
+ c.config.cipherSuites(), hs.cipherSuiteOk)
+ if hs.suite == nil {
+ return false
+ }
+
+ sessionHasClientCerts := len(hs.sessionState.certificates) != 0
+ needClientCerts := requiresClientCert(c.config.ClientAuth)
+ if needClientCerts && !sessionHasClientCerts {
+ return false
+ }
+ if sessionHasClientCerts && c.config.ClientAuth == NoClientCert {
+ return false
+ }
+
+ return true
+}
+
+func (hs *serverHandshakeState) doResumeHandshake() error {
+ c := hs.c
+
+ hs.hello.cipherSuite = hs.suite.id
+ c.cipherSuite = hs.suite.id
+ // We echo the client's session ID in the ServerHello to let it know
+ // that we're doing a resumption.
+ hs.hello.sessionId = hs.clientHello.sessionId
+ hs.hello.ticketSupported = hs.sessionState.usedOldKey
+ hs.finishedHash = newFinishedHash(c.vers, hs.suite)
+ hs.finishedHash.discardHandshakeBuffer()
+ hs.finishedHash.Write(hs.clientHello.marshal())
+ hs.finishedHash.Write(hs.hello.marshal())
+ if _, err := c.writeRecord(recordTypeHandshake, hs.hello.marshal()); err != nil {
+ return err
+ }
+
+ if err := c.processCertsFromClient(Certificate{
+ Certificate: hs.sessionState.certificates,
+ }); err != nil {
+ return err
+ }
+
+ if c.config.VerifyConnection != nil {
+ if err := c.config.VerifyConnection(c.connectionStateLocked()); err != nil {
+ c.sendAlert(alertBadCertificate)
+ return err
+ }
+ }
+
+ hs.masterSecret = hs.sessionState.masterSecret
+
+ return nil
+}
+
+func (hs *serverHandshakeState) doFullHandshake() error {
+ c := hs.c
+
+ if hs.clientHello.ocspStapling && len(hs.cert.OCSPStaple) > 0 {
+ hs.hello.ocspStapling = true
+ }
+
+ hs.hello.ticketSupported = hs.clientHello.ticketSupported && !c.config.SessionTicketsDisabled
+ hs.hello.cipherSuite = hs.suite.id
+
+ hs.finishedHash = newFinishedHash(hs.c.vers, hs.suite)
+ if c.config.ClientAuth == NoClientCert {
+ // No need to keep a full record of the handshake if client
+ // certificates won't be used.
+ hs.finishedHash.discardHandshakeBuffer()
+ }
+ hs.finishedHash.Write(hs.clientHello.marshal())
+ hs.finishedHash.Write(hs.hello.marshal())
+ if _, err := c.writeRecord(recordTypeHandshake, hs.hello.marshal()); err != nil {
+ return err
+ }
+
+ certMsg := new(certificateMsg)
+ certMsg.certificates = hs.cert.Certificate
+ hs.finishedHash.Write(certMsg.marshal())
+ if _, err := c.writeRecord(recordTypeHandshake, certMsg.marshal()); err != nil {
+ return err
+ }
+
+ if hs.hello.ocspStapling {
+ certStatus := new(certificateStatusMsg)
+ certStatus.response = hs.cert.OCSPStaple
+ hs.finishedHash.Write(certStatus.marshal())
+ if _, err := c.writeRecord(recordTypeHandshake, certStatus.marshal()); err != nil {
+ return err
+ }
+ }
+
+ keyAgreement := hs.suite.ka(c.vers)
+ skx, err := keyAgreement.generateServerKeyExchange(c.config, hs.cert, hs.clientHello, hs.hello)
+ if err != nil {
+ c.sendAlert(alertHandshakeFailure)
+ return err
+ }
+ if skx != nil {
+ hs.finishedHash.Write(skx.marshal())
+ if _, err := c.writeRecord(recordTypeHandshake, skx.marshal()); err != nil {
+ return err
+ }
+ }
+
+ var certReq *certificateRequestMsg
+ if c.config.ClientAuth >= RequestClientCert {
+ // Request a client certificate
+ certReq = new(certificateRequestMsg)
+ certReq.certificateTypes = []byte{
+ byte(certTypeRSASign),
+ byte(certTypeECDSASign),
+ }
+ if c.vers >= VersionTLS12 {
+ certReq.hasSignatureAlgorithm = true
+ certReq.supportedSignatureAlgorithms = supportedSignatureAlgorithms
+ }
+
+ // 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.
+ if c.config.ClientCAs != nil {
+ certReq.certificateAuthorities = c.config.ClientCAs.Subjects()
+ }
+ hs.finishedHash.Write(certReq.marshal())
+ if _, err := c.writeRecord(recordTypeHandshake, certReq.marshal()); err != nil {
+ return err
+ }
+ }
+
+ helloDone := new(serverHelloDoneMsg)
+ hs.finishedHash.Write(helloDone.marshal())
+ if _, err := c.writeRecord(recordTypeHandshake, helloDone.marshal()); err != nil {
+ return err
+ }
+
+ if _, err := c.flush(); err != nil {
+ return err
+ }
+
+ var pub crypto.PublicKey // public key for client auth, if any
+
+ msg, err := c.readHandshake()
+ if err != nil {
+ return err
+ }
+
+ // If we requested a client certificate, then the client must send a
+ // certificate message, even if it's empty.
+ if c.config.ClientAuth >= RequestClientCert {
+ certMsg, ok := msg.(*certificateMsg)
+ if !ok {
+ c.sendAlert(alertUnexpectedMessage)
+ return unexpectedMessageError(certMsg, msg)
+ }
+ hs.finishedHash.Write(certMsg.marshal())
+
+ if err := c.processCertsFromClient(Certificate{
+ Certificate: certMsg.certificates,
+ }); err != nil {
+ return err
+ }
+ if len(certMsg.certificates) != 0 {
+ pub = c.peerCertificates[0].PublicKey
+ }
+
+ msg, err = c.readHandshake()
+ if err != nil {
+ return err
+ }
+ }
+ if c.config.VerifyConnection != nil {
+ if err := c.config.VerifyConnection(c.connectionStateLocked()); err != nil {
+ c.sendAlert(alertBadCertificate)
+ return err
+ }
+ }
+
+ // Get client key exchange
+ ckx, ok := msg.(*clientKeyExchangeMsg)
+ if !ok {
+ c.sendAlert(alertUnexpectedMessage)
+ return unexpectedMessageError(ckx, msg)
+ }
+ hs.finishedHash.Write(ckx.marshal())
+
+ preMasterSecret, err := keyAgreement.processClientKeyExchange(c.config, hs.cert, ckx, c.vers)
+ if err != nil {
+ c.sendAlert(alertHandshakeFailure)
+ return err
+ }
+ hs.masterSecret = masterFromPreMasterSecret(c.vers, hs.suite, preMasterSecret, hs.clientHello.random, hs.hello.random)
+ if err := c.config.writeKeyLog(keyLogLabelTLS12, hs.clientHello.random, hs.masterSecret); err != nil {
+ c.sendAlert(alertInternalError)
+ return err
+ }
+
+ // If we received a client cert in response to our certificate request message,
+ // the client will send us a certificateVerifyMsg immediately after the
+ // clientKeyExchangeMsg. This message is a digest of all preceding
+ // handshake-layer messages that is signed using the private key corresponding
+ // to the client's certificate. This allows us to verify that the client is in
+ // possession of the private key of the certificate.
+ if len(c.peerCertificates) > 0 {
+ msg, err = c.readHandshake()
+ if err != nil {
+ return err
+ }
+ certVerify, ok := msg.(*certificateVerifyMsg)
+ if !ok {
+ c.sendAlert(alertUnexpectedMessage)
+ return unexpectedMessageError(certVerify, msg)
+ }
+
+ var sigType uint8
+ var sigHash crypto.Hash
+ if c.vers >= VersionTLS12 {
+ if !isSupportedSignatureAlgorithm(certVerify.signatureAlgorithm, certReq.supportedSignatureAlgorithms) {
+ c.sendAlert(alertIllegalParameter)
+ return errors.New("tls: client certificate used with invalid signature algorithm")
+ }
+ sigType, sigHash, err = typeAndHashFromSignatureScheme(certVerify.signatureAlgorithm)
+ if err != nil {
+ return c.sendAlert(alertInternalError)
+ }
+ } else {
+ sigType, sigHash, err = legacyTypeAndHashFromPublicKey(pub)
+ if err != nil {
+ c.sendAlert(alertIllegalParameter)
+ return err
+ }
+ }
+
+ signed := hs.finishedHash.hashForClientCertificate(sigType, sigHash, hs.masterSecret)
+ if err := verifyHandshakeSignature(sigType, pub, sigHash, signed, certVerify.signature); err != nil {
+ c.sendAlert(alertDecryptError)
+ return errors.New("tls: invalid signature by the client certificate: " + err.Error())
+ }
+
+ hs.finishedHash.Write(certVerify.marshal())
+ }
+
+ hs.finishedHash.discardHandshakeBuffer()
+
+ return nil
+}
+
+func (hs *serverHandshakeState) establishKeys() error {
+ c := hs.c
+
+ clientMAC, serverMAC, clientKey, serverKey, clientIV, serverIV :=
+ keysFromMasterSecret(c.vers, hs.suite, hs.masterSecret, hs.clientHello.random, hs.hello.random, hs.suite.macLen, hs.suite.keyLen, hs.suite.ivLen)
+
+ var clientCipher, serverCipher any
+ var clientHash, serverHash hash.Hash
+
+ if hs.suite.aead == nil {
+ clientCipher = hs.suite.cipher(clientKey, clientIV, true /* for reading */)
+ clientHash = hs.suite.mac(clientMAC)
+ serverCipher = hs.suite.cipher(serverKey, serverIV, false /* not for reading */)
+ serverHash = hs.suite.mac(serverMAC)
+ } else {
+ clientCipher = hs.suite.aead(clientKey, clientIV)
+ serverCipher = hs.suite.aead(serverKey, serverIV)
+ }
+
+ c.in.prepareCipherSpec(c.vers, clientCipher, clientHash)
+ c.out.prepareCipherSpec(c.vers, serverCipher, serverHash)
+
+ return nil
+}
+
+func (hs *serverHandshakeState) readFinished(out []byte) error {
+ c := hs.c
+
+ if err := c.readChangeCipherSpec(); err != nil {
+ return err
+ }
+
+ msg, err := c.readHandshake()
+ if err != nil {
+ return err
+ }
+ clientFinished, ok := msg.(*finishedMsg)
+ if !ok {
+ c.sendAlert(alertUnexpectedMessage)
+ return unexpectedMessageError(clientFinished, msg)
+ }
+
+ verify := hs.finishedHash.clientSum(hs.masterSecret)
+ if len(verify) != len(clientFinished.verifyData) ||
+ subtle.ConstantTimeCompare(verify, clientFinished.verifyData) != 1 {
+ c.sendAlert(alertHandshakeFailure)
+ return errors.New("tls: client's Finished message is incorrect")
+ }
+
+ hs.finishedHash.Write(clientFinished.marshal())
+ copy(out, verify)
+ return nil
+}
+
+func (hs *serverHandshakeState) sendSessionTicket() error {
+ // ticketSupported is set in a resumption handshake if the
+ // ticket from the client was encrypted with an old session
+ // ticket key and thus a refreshed ticket should be sent.
+ if !hs.hello.ticketSupported {
+ return nil
+ }
+
+ c := hs.c
+ m := new(newSessionTicketMsg)
+
+ createdAt := uint64(c.config.time().Unix())
+ if hs.sessionState != nil {
+ // If this is re-wrapping an old key, then keep
+ // the original time it was created.
+ createdAt = hs.sessionState.createdAt
+ }
+
+ var certsFromClient [][]byte
+ for _, cert := range c.peerCertificates {
+ certsFromClient = append(certsFromClient, cert.Raw)
+ }
+ state := sessionState{
+ vers: c.vers,
+ cipherSuite: hs.suite.id,
+ createdAt: createdAt,
+ masterSecret: hs.masterSecret,
+ certificates: certsFromClient,
+ }
+ var err error
+ m.ticket, err = c.encryptTicket(state.marshal())
+ if err != nil {
+ return err
+ }
+
+ hs.finishedHash.Write(m.marshal())
+ if _, err := c.writeRecord(recordTypeHandshake, m.marshal()); err != nil {
+ return err
+ }
+
+ return nil
+}
+
+func (hs *serverHandshakeState) sendFinished(out []byte) error {
+ c := hs.c
+
+ if _, err := c.writeRecord(recordTypeChangeCipherSpec, []byte{1}); err != nil {
+ return err
+ }
+
+ finished := new(finishedMsg)
+ finished.verifyData = hs.finishedHash.serverSum(hs.masterSecret)
+ hs.finishedHash.Write(finished.marshal())
+ if _, err := c.writeRecord(recordTypeHandshake, finished.marshal()); err != nil {
+ return err
+ }
+
+ copy(out, finished.verifyData)
+
+ return nil
+}
+
+// processCertsFromClient takes a chain of client certificates either from a
+// Certificates message or from a sessionState and verifies them. It returns
+// the public key of the leaf certificate.
+func (c *Conn) processCertsFromClient(certificate Certificate) error {
+ certificates := certificate.Certificate
+ certs := make([]*x509.Certificate, len(certificates))
+ var err error
+ for i, asn1Data := range certificates {
+ if certs[i], err = x509.ParseCertificate(asn1Data); err != nil {
+ c.sendAlert(alertBadCertificate)
+ return errors.New("tls: failed to parse client certificate: " + err.Error())
+ }
+ }
+
+ if len(certs) == 0 && requiresClientCert(c.config.ClientAuth) {
+ c.sendAlert(alertBadCertificate)
+ return errors.New("tls: client didn't provide a certificate")
+ }
+
+ if c.config.ClientAuth >= VerifyClientCertIfGiven && len(certs) > 0 {
+ opts := x509.VerifyOptions{
+ Roots: c.config.ClientCAs,
+ CurrentTime: c.config.time(),
+ Intermediates: x509.NewCertPool(),
+ KeyUsages: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth},
+ }
+
+ for _, cert := range certs[1:] {
+ opts.Intermediates.AddCert(cert)
+ }
+
+ chains, err := certs[0].Verify(opts)
+ if err != nil {
+ c.sendAlert(alertBadCertificate)
+ return errors.New("tls: failed to verify client certificate: " + err.Error())
+ }
+
+ c.verifiedChains = chains
+ }
+
+ c.peerCertificates = certs
+ c.ocspResponse = certificate.OCSPStaple
+ c.scts = certificate.SignedCertificateTimestamps
+
+ if len(certs) > 0 {
+ switch certs[0].PublicKey.(type) {
+ case *ecdsa.PublicKey, *rsa.PublicKey, ed25519.PublicKey:
+ default:
+ c.sendAlert(alertUnsupportedCertificate)
+ return fmt.Errorf("tls: client certificate contains an unsupported public key of type %T", certs[0].PublicKey)
+ }
+ }
+
+ if c.config.VerifyPeerCertificate != nil {
+ if err := c.config.VerifyPeerCertificate(certificates, c.verifiedChains); err != nil {
+ c.sendAlert(alertBadCertificate)
+ return err
+ }
+ }
+
+ return nil
+}
+
+func newClientHelloInfo(ctx context.Context, c *Conn, clientHello *clientHelloMsg) *ClientHelloInfo {
+ supportedVersions := clientHello.supportedVersions
+ if len(clientHello.supportedVersions) == 0 {
+ supportedVersions = supportedVersionsFromMax(clientHello.vers)
+ }
+
+ return toClientHelloInfo(&clientHelloInfo{
+ CipherSuites: clientHello.cipherSuites,
+ ServerName: clientHello.serverName,
+ SupportedCurves: clientHello.supportedCurves,
+ SupportedPoints: clientHello.supportedPoints,
+ SignatureSchemes: clientHello.supportedSignatureAlgorithms,
+ SupportedProtos: clientHello.alpnProtocols,
+ SupportedVersions: supportedVersions,
+ Conn: c.conn,
+ config: toConfig(c.config),
+ ctx: ctx,
+ })
+}
diff --git a/vendor/github.com/quic-go/qtls-go1-18/handshake_server_tls13.go b/vendor/github.com/quic-go/qtls-go1-18/handshake_server_tls13.go
new file mode 100644
index 0000000000..7ce09c37b9
--- /dev/null
+++ b/vendor/github.com/quic-go/qtls-go1-18/handshake_server_tls13.go
@@ -0,0 +1,898 @@
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package qtls
+
+import (
+ "bytes"
+ "context"
+ "crypto"
+ "crypto/hmac"
+ "crypto/rsa"
+ "errors"
+ "hash"
+ "io"
+ "sync/atomic"
+ "time"
+)
+
+// maxClientPSKIdentities is the number of client PSK identities the server will
+// attempt to validate. It will ignore the rest not to let cheap ClientHello
+// messages cause too much work in session ticket decryption attempts.
+const maxClientPSKIdentities = 5
+
+type serverHandshakeStateTLS13 struct {
+ c *Conn
+ ctx context.Context
+ clientHello *clientHelloMsg
+ hello *serverHelloMsg
+ alpnNegotiationErr error
+ encryptedExtensions *encryptedExtensionsMsg
+ sentDummyCCS bool
+ usingPSK bool
+ suite *cipherSuiteTLS13
+ cert *Certificate
+ sigAlg SignatureScheme
+ earlySecret []byte
+ sharedKey []byte
+ handshakeSecret []byte
+ masterSecret []byte
+ trafficSecret []byte // client_application_traffic_secret_0
+ transcript hash.Hash
+ clientFinished []byte
+}
+
+func (hs *serverHandshakeStateTLS13) handshake() error {
+ c := hs.c
+
+ // For an overview of the TLS 1.3 handshake, see RFC 8446, Section 2.
+ if err := hs.processClientHello(); err != nil {
+ return err
+ }
+ if err := hs.checkForResumption(); err != nil {
+ return err
+ }
+ c.updateConnectionState()
+ if err := hs.pickCertificate(); err != nil {
+ return err
+ }
+ c.buffering = true
+ if err := hs.sendServerParameters(); err != nil {
+ return err
+ }
+ if err := hs.sendServerCertificate(); err != nil {
+ return err
+ }
+ if err := hs.sendServerFinished(); err != nil {
+ return err
+ }
+ // Note that at this point we could start sending application data without
+ // waiting for the client's second flight, but the application might not
+ // expect the lack of replay protection of the ClientHello parameters.
+ if _, err := c.flush(); err != nil {
+ return err
+ }
+ if err := hs.readClientCertificate(); err != nil {
+ return err
+ }
+ c.updateConnectionState()
+ if err := hs.readClientFinished(); err != nil {
+ return err
+ }
+
+ atomic.StoreUint32(&c.handshakeStatus, 1)
+ c.updateConnectionState()
+ return nil
+}
+
+func (hs *serverHandshakeStateTLS13) processClientHello() error {
+ c := hs.c
+
+ hs.hello = new(serverHelloMsg)
+ hs.encryptedExtensions = new(encryptedExtensionsMsg)
+
+ // TLS 1.3 froze the ServerHello.legacy_version field, and uses
+ // supported_versions instead. See RFC 8446, sections 4.1.3 and 4.2.1.
+ hs.hello.vers = VersionTLS12
+ hs.hello.supportedVersion = c.vers
+
+ if len(hs.clientHello.supportedVersions) == 0 {
+ c.sendAlert(alertIllegalParameter)
+ return errors.New("tls: client used the legacy version field to negotiate TLS 1.3")
+ }
+
+ // Abort if the client is doing a fallback and landing lower than what we
+ // support. See RFC 7507, which however does not specify the interaction
+ // with supported_versions. The only difference is that with
+ // supported_versions a client has a chance to attempt a [TLS 1.2, TLS 1.4]
+ // handshake in case TLS 1.3 is broken but 1.2 is not. Alas, in that case,
+ // it will have to drop the TLS_FALLBACK_SCSV protection if it falls back to
+ // TLS 1.2, because a TLS 1.3 server would abort here. The situation before
+ // supported_versions was not better because there was just no way to do a
+ // TLS 1.4 handshake without risking the server selecting TLS 1.3.
+ for _, id := range hs.clientHello.cipherSuites {
+ if id == TLS_FALLBACK_SCSV {
+ // Use c.vers instead of max(supported_versions) because an attacker
+ // could defeat this by adding an arbitrary high version otherwise.
+ if c.vers < c.config.maxSupportedVersion(roleServer) {
+ c.sendAlert(alertInappropriateFallback)
+ return errors.New("tls: client using inappropriate protocol fallback")
+ }
+ break
+ }
+ }
+
+ if len(hs.clientHello.compressionMethods) != 1 ||
+ hs.clientHello.compressionMethods[0] != compressionNone {
+ c.sendAlert(alertIllegalParameter)
+ return errors.New("tls: TLS 1.3 client supports illegal compression methods")
+ }
+
+ hs.hello.random = make([]byte, 32)
+ if _, err := io.ReadFull(c.config.rand(), hs.hello.random); err != nil {
+ c.sendAlert(alertInternalError)
+ return err
+ }
+
+ if len(hs.clientHello.secureRenegotiation) != 0 {
+ c.sendAlert(alertHandshakeFailure)
+ return errors.New("tls: initial handshake had non-empty renegotiation extension")
+ }
+
+ hs.hello.sessionId = hs.clientHello.sessionId
+ hs.hello.compressionMethod = compressionNone
+
+ if hs.suite == nil {
+ var preferenceList []uint16
+ for _, suiteID := range c.config.CipherSuites {
+ for _, suite := range cipherSuitesTLS13 {
+ if suite.id == suiteID {
+ preferenceList = append(preferenceList, suiteID)
+ break
+ }
+ }
+ }
+ if len(preferenceList) == 0 {
+ preferenceList = defaultCipherSuitesTLS13
+ if !hasAESGCMHardwareSupport || !aesgcmPreferred(hs.clientHello.cipherSuites) {
+ preferenceList = defaultCipherSuitesTLS13NoAES
+ }
+ }
+ for _, suiteID := range preferenceList {
+ hs.suite = mutualCipherSuiteTLS13(hs.clientHello.cipherSuites, suiteID)
+ if hs.suite != nil {
+ break
+ }
+ }
+ }
+ if hs.suite == nil {
+ c.sendAlert(alertHandshakeFailure)
+ return errors.New("tls: no cipher suite supported by both client and server")
+ }
+ c.cipherSuite = hs.suite.id
+ hs.hello.cipherSuite = hs.suite.id
+ hs.transcript = hs.suite.hash.New()
+
+ // Pick the ECDHE group in server preference order, but give priority to
+ // groups with a key share, to avoid a HelloRetryRequest round-trip.
+ var selectedGroup CurveID
+ var clientKeyShare *keyShare
+GroupSelection:
+ for _, preferredGroup := range c.config.curvePreferences() {
+ for _, ks := range hs.clientHello.keyShares {
+ if ks.group == preferredGroup {
+ selectedGroup = ks.group
+ clientKeyShare = &ks
+ break GroupSelection
+ }
+ }
+ if selectedGroup != 0 {
+ continue
+ }
+ for _, group := range hs.clientHello.supportedCurves {
+ if group == preferredGroup {
+ selectedGroup = group
+ break
+ }
+ }
+ }
+ if selectedGroup == 0 {
+ c.sendAlert(alertHandshakeFailure)
+ return errors.New("tls: no ECDHE curve supported by both client and server")
+ }
+ if clientKeyShare == nil {
+ if err := hs.doHelloRetryRequest(selectedGroup); err != nil {
+ return err
+ }
+ clientKeyShare = &hs.clientHello.keyShares[0]
+ }
+
+ if _, ok := curveForCurveID(selectedGroup); selectedGroup != X25519 && !ok {
+ c.sendAlert(alertInternalError)
+ return errors.New("tls: CurvePreferences includes unsupported curve")
+ }
+ params, err := generateECDHEParameters(c.config.rand(), selectedGroup)
+ if err != nil {
+ c.sendAlert(alertInternalError)
+ return err
+ }
+ hs.hello.serverShare = keyShare{group: selectedGroup, data: params.PublicKey()}
+ hs.sharedKey = params.SharedKey(clientKeyShare.data)
+ if hs.sharedKey == nil {
+ c.sendAlert(alertIllegalParameter)
+ return errors.New("tls: invalid client key share")
+ }
+
+ c.serverName = hs.clientHello.serverName
+
+ if c.extraConfig != nil && c.extraConfig.ReceivedExtensions != nil {
+ c.extraConfig.ReceivedExtensions(typeClientHello, hs.clientHello.additionalExtensions)
+ }
+
+ selectedProto, err := negotiateALPN(c.config.NextProtos, hs.clientHello.alpnProtocols)
+ if err != nil {
+ hs.alpnNegotiationErr = err
+ }
+ hs.encryptedExtensions.alpnProtocol = selectedProto
+ c.clientProtocol = selectedProto
+
+ return nil
+}
+
+func (hs *serverHandshakeStateTLS13) checkForResumption() error {
+ c := hs.c
+
+ if c.config.SessionTicketsDisabled {
+ return nil
+ }
+
+ modeOK := false
+ for _, mode := range hs.clientHello.pskModes {
+ if mode == pskModeDHE {
+ modeOK = true
+ break
+ }
+ }
+ if !modeOK {
+ return nil
+ }
+
+ if len(hs.clientHello.pskIdentities) != len(hs.clientHello.pskBinders) {
+ c.sendAlert(alertIllegalParameter)
+ return errors.New("tls: invalid or missing PSK binders")
+ }
+ if len(hs.clientHello.pskIdentities) == 0 {
+ return nil
+ }
+
+ for i, identity := range hs.clientHello.pskIdentities {
+ if i >= maxClientPSKIdentities {
+ break
+ }
+
+ plaintext, _ := c.decryptTicket(identity.label)
+ if plaintext == nil {
+ continue
+ }
+ sessionState := new(sessionStateTLS13)
+ if ok := sessionState.unmarshal(plaintext); !ok {
+ continue
+ }
+
+ if hs.clientHello.earlyData {
+ if sessionState.maxEarlyData == 0 {
+ c.sendAlert(alertUnsupportedExtension)
+ return errors.New("tls: client sent unexpected early data")
+ }
+
+ if hs.alpnNegotiationErr == nil && sessionState.alpn == c.clientProtocol &&
+ c.extraConfig != nil && c.extraConfig.MaxEarlyData > 0 &&
+ c.extraConfig.Accept0RTT != nil && c.extraConfig.Accept0RTT(sessionState.appData) {
+ hs.encryptedExtensions.earlyData = true
+ c.used0RTT = true
+ }
+ }
+
+ createdAt := time.Unix(int64(sessionState.createdAt), 0)
+ if c.config.time().Sub(createdAt) > maxSessionTicketLifetime {
+ continue
+ }
+
+ // We don't check the obfuscated ticket age because it's affected by
+ // clock skew and it's only a freshness signal useful for shrinking the
+ // window for replay attacks, which don't affect us as we don't do 0-RTT.
+
+ pskSuite := cipherSuiteTLS13ByID(sessionState.cipherSuite)
+ if pskSuite == nil || pskSuite.hash != hs.suite.hash {
+ continue
+ }
+
+ // PSK connections don't re-establish client certificates, but carry
+ // them over in the session ticket. Ensure the presence of client certs
+ // in the ticket is consistent with the configured requirements.
+ sessionHasClientCerts := len(sessionState.certificate.Certificate) != 0
+ needClientCerts := requiresClientCert(c.config.ClientAuth)
+ if needClientCerts && !sessionHasClientCerts {
+ continue
+ }
+ if sessionHasClientCerts && c.config.ClientAuth == NoClientCert {
+ continue
+ }
+
+ psk := hs.suite.expandLabel(sessionState.resumptionSecret, "resumption",
+ nil, hs.suite.hash.Size())
+ hs.earlySecret = hs.suite.extract(psk, nil)
+ binderKey := hs.suite.deriveSecret(hs.earlySecret, resumptionBinderLabel, nil)
+ // Clone the transcript in case a HelloRetryRequest was recorded.
+ transcript := cloneHash(hs.transcript, hs.suite.hash)
+ if transcript == nil {
+ c.sendAlert(alertInternalError)
+ return errors.New("tls: internal error: failed to clone hash")
+ }
+ transcript.Write(hs.clientHello.marshalWithoutBinders())
+ pskBinder := hs.suite.finishedHash(binderKey, transcript)
+ if !hmac.Equal(hs.clientHello.pskBinders[i], pskBinder) {
+ c.sendAlert(alertDecryptError)
+ return errors.New("tls: invalid PSK binder")
+ }
+
+ c.didResume = true
+ if err := c.processCertsFromClient(sessionState.certificate); err != nil {
+ return err
+ }
+
+ h := cloneHash(hs.transcript, hs.suite.hash)
+ h.Write(hs.clientHello.marshal())
+ if hs.encryptedExtensions.earlyData {
+ clientEarlySecret := hs.suite.deriveSecret(hs.earlySecret, "c e traffic", h)
+ c.in.exportKey(Encryption0RTT, hs.suite, clientEarlySecret)
+ if err := c.config.writeKeyLog(keyLogLabelEarlyTraffic, hs.clientHello.random, clientEarlySecret); err != nil {
+ c.sendAlert(alertInternalError)
+ return err
+ }
+ }
+
+ hs.hello.selectedIdentityPresent = true
+ hs.hello.selectedIdentity = uint16(i)
+ hs.usingPSK = true
+ return nil
+ }
+
+ return nil
+}
+
+// cloneHash uses the encoding.BinaryMarshaler and encoding.BinaryUnmarshaler
+// interfaces implemented by standard library hashes to clone the state of in
+// to a new instance of h. It returns nil if the operation fails.
+func cloneHash(in hash.Hash, h crypto.Hash) hash.Hash {
+ // Recreate the interface to avoid importing encoding.
+ type binaryMarshaler interface {
+ MarshalBinary() (data []byte, err error)
+ UnmarshalBinary(data []byte) error
+ }
+ marshaler, ok := in.(binaryMarshaler)
+ if !ok {
+ return nil
+ }
+ state, err := marshaler.MarshalBinary()
+ if err != nil {
+ return nil
+ }
+ out := h.New()
+ unmarshaler, ok := out.(binaryMarshaler)
+ if !ok {
+ return nil
+ }
+ if err := unmarshaler.UnmarshalBinary(state); err != nil {
+ return nil
+ }
+ return out
+}
+
+func (hs *serverHandshakeStateTLS13) pickCertificate() error {
+ c := hs.c
+
+ // Only one of PSK and certificates are used at a time.
+ if hs.usingPSK {
+ return nil
+ }
+
+ // signature_algorithms is required in TLS 1.3. See RFC 8446, Section 4.2.3.
+ if len(hs.clientHello.supportedSignatureAlgorithms) == 0 {
+ return c.sendAlert(alertMissingExtension)
+ }
+
+ certificate, err := c.config.getCertificate(newClientHelloInfo(hs.ctx, c, hs.clientHello))
+ if err != nil {
+ if err == errNoCertificates {
+ c.sendAlert(alertUnrecognizedName)
+ } else {
+ c.sendAlert(alertInternalError)
+ }
+ return err
+ }
+ hs.sigAlg, err = selectSignatureScheme(c.vers, certificate, hs.clientHello.supportedSignatureAlgorithms)
+ if err != nil {
+ // getCertificate returned a certificate that is unsupported or
+ // incompatible with the client's signature algorithms.
+ c.sendAlert(alertHandshakeFailure)
+ return err
+ }
+ hs.cert = certificate
+
+ return nil
+}
+
+// sendDummyChangeCipherSpec sends a ChangeCipherSpec record for compatibility
+// with middleboxes that didn't implement TLS correctly. See RFC 8446, Appendix D.4.
+func (hs *serverHandshakeStateTLS13) sendDummyChangeCipherSpec() error {
+ if hs.sentDummyCCS {
+ return nil
+ }
+ hs.sentDummyCCS = true
+
+ _, err := hs.c.writeRecord(recordTypeChangeCipherSpec, []byte{1})
+ return err
+}
+
+func (hs *serverHandshakeStateTLS13) doHelloRetryRequest(selectedGroup CurveID) error {
+ c := hs.c
+
+ // The first ClientHello gets double-hashed into the transcript upon a
+ // HelloRetryRequest. See RFC 8446, Section 4.4.1.
+ hs.transcript.Write(hs.clientHello.marshal())
+ chHash := hs.transcript.Sum(nil)
+ hs.transcript.Reset()
+ hs.transcript.Write([]byte{typeMessageHash, 0, 0, uint8(len(chHash))})
+ hs.transcript.Write(chHash)
+
+ helloRetryRequest := &serverHelloMsg{
+ vers: hs.hello.vers,
+ random: helloRetryRequestRandom,
+ sessionId: hs.hello.sessionId,
+ cipherSuite: hs.hello.cipherSuite,
+ compressionMethod: hs.hello.compressionMethod,
+ supportedVersion: hs.hello.supportedVersion,
+ selectedGroup: selectedGroup,
+ }
+
+ hs.transcript.Write(helloRetryRequest.marshal())
+ if _, err := c.writeRecord(recordTypeHandshake, helloRetryRequest.marshal()); err != nil {
+ return err
+ }
+
+ if err := hs.sendDummyChangeCipherSpec(); err != nil {
+ return err
+ }
+
+ msg, err := c.readHandshake()
+ if err != nil {
+ return err
+ }
+
+ clientHello, ok := msg.(*clientHelloMsg)
+ if !ok {
+ c.sendAlert(alertUnexpectedMessage)
+ return unexpectedMessageError(clientHello, msg)
+ }
+
+ if len(clientHello.keyShares) != 1 || clientHello.keyShares[0].group != selectedGroup {
+ c.sendAlert(alertIllegalParameter)
+ return errors.New("tls: client sent invalid key share in second ClientHello")
+ }
+
+ if clientHello.earlyData {
+ c.sendAlert(alertIllegalParameter)
+ return errors.New("tls: client indicated early data in second ClientHello")
+ }
+
+ if illegalClientHelloChange(clientHello, hs.clientHello) {
+ c.sendAlert(alertIllegalParameter)
+ return errors.New("tls: client illegally modified second ClientHello")
+ }
+
+ if clientHello.earlyData {
+ c.sendAlert(alertIllegalParameter)
+ return errors.New("tls: client offered 0-RTT data in second ClientHello")
+ }
+
+ hs.clientHello = clientHello
+ return nil
+}
+
+// illegalClientHelloChange reports whether the two ClientHello messages are
+// different, with the exception of the changes allowed before and after a
+// HelloRetryRequest. See RFC 8446, Section 4.1.2.
+func illegalClientHelloChange(ch, ch1 *clientHelloMsg) bool {
+ if len(ch.supportedVersions) != len(ch1.supportedVersions) ||
+ len(ch.cipherSuites) != len(ch1.cipherSuites) ||
+ len(ch.supportedCurves) != len(ch1.supportedCurves) ||
+ len(ch.supportedSignatureAlgorithms) != len(ch1.supportedSignatureAlgorithms) ||
+ len(ch.supportedSignatureAlgorithmsCert) != len(ch1.supportedSignatureAlgorithmsCert) ||
+ len(ch.alpnProtocols) != len(ch1.alpnProtocols) {
+ return true
+ }
+ for i := range ch.supportedVersions {
+ if ch.supportedVersions[i] != ch1.supportedVersions[i] {
+ return true
+ }
+ }
+ for i := range ch.cipherSuites {
+ if ch.cipherSuites[i] != ch1.cipherSuites[i] {
+ return true
+ }
+ }
+ for i := range ch.supportedCurves {
+ if ch.supportedCurves[i] != ch1.supportedCurves[i] {
+ return true
+ }
+ }
+ for i := range ch.supportedSignatureAlgorithms {
+ if ch.supportedSignatureAlgorithms[i] != ch1.supportedSignatureAlgorithms[i] {
+ return true
+ }
+ }
+ for i := range ch.supportedSignatureAlgorithmsCert {
+ if ch.supportedSignatureAlgorithmsCert[i] != ch1.supportedSignatureAlgorithmsCert[i] {
+ return true
+ }
+ }
+ for i := range ch.alpnProtocols {
+ if ch.alpnProtocols[i] != ch1.alpnProtocols[i] {
+ return true
+ }
+ }
+ return ch.vers != ch1.vers ||
+ !bytes.Equal(ch.random, ch1.random) ||
+ !bytes.Equal(ch.sessionId, ch1.sessionId) ||
+ !bytes.Equal(ch.compressionMethods, ch1.compressionMethods) ||
+ ch.serverName != ch1.serverName ||
+ ch.ocspStapling != ch1.ocspStapling ||
+ !bytes.Equal(ch.supportedPoints, ch1.supportedPoints) ||
+ ch.ticketSupported != ch1.ticketSupported ||
+ !bytes.Equal(ch.sessionTicket, ch1.sessionTicket) ||
+ ch.secureRenegotiationSupported != ch1.secureRenegotiationSupported ||
+ !bytes.Equal(ch.secureRenegotiation, ch1.secureRenegotiation) ||
+ ch.scts != ch1.scts ||
+ !bytes.Equal(ch.cookie, ch1.cookie) ||
+ !bytes.Equal(ch.pskModes, ch1.pskModes)
+}
+
+func (hs *serverHandshakeStateTLS13) sendServerParameters() error {
+ c := hs.c
+
+ hs.transcript.Write(hs.clientHello.marshal())
+ hs.transcript.Write(hs.hello.marshal())
+ if _, err := c.writeRecord(recordTypeHandshake, hs.hello.marshal()); err != nil {
+ return err
+ }
+
+ if err := hs.sendDummyChangeCipherSpec(); err != nil {
+ return err
+ }
+
+ earlySecret := hs.earlySecret
+ if earlySecret == nil {
+ earlySecret = hs.suite.extract(nil, nil)
+ }
+ hs.handshakeSecret = hs.suite.extract(hs.sharedKey,
+ hs.suite.deriveSecret(earlySecret, "derived", nil))
+
+ clientSecret := hs.suite.deriveSecret(hs.handshakeSecret,
+ clientHandshakeTrafficLabel, hs.transcript)
+ c.in.exportKey(EncryptionHandshake, hs.suite, clientSecret)
+ c.in.setTrafficSecret(hs.suite, clientSecret)
+ serverSecret := hs.suite.deriveSecret(hs.handshakeSecret,
+ serverHandshakeTrafficLabel, hs.transcript)
+ c.out.exportKey(EncryptionHandshake, hs.suite, serverSecret)
+ c.out.setTrafficSecret(hs.suite, serverSecret)
+
+ err := c.config.writeKeyLog(keyLogLabelClientHandshake, hs.clientHello.random, clientSecret)
+ if err != nil {
+ c.sendAlert(alertInternalError)
+ return err
+ }
+ err = c.config.writeKeyLog(keyLogLabelServerHandshake, hs.clientHello.random, serverSecret)
+ if err != nil {
+ c.sendAlert(alertInternalError)
+ return err
+ }
+
+ if hs.alpnNegotiationErr != nil {
+ c.sendAlert(alertNoApplicationProtocol)
+ return hs.alpnNegotiationErr
+ }
+ if hs.c.extraConfig != nil && hs.c.extraConfig.GetExtensions != nil {
+ hs.encryptedExtensions.additionalExtensions = hs.c.extraConfig.GetExtensions(typeEncryptedExtensions)
+ }
+
+ hs.transcript.Write(hs.encryptedExtensions.marshal())
+ if _, err := c.writeRecord(recordTypeHandshake, hs.encryptedExtensions.marshal()); err != nil {
+ return err
+ }
+
+ return nil
+}
+
+func (hs *serverHandshakeStateTLS13) requestClientCert() bool {
+ return hs.c.config.ClientAuth >= RequestClientCert && !hs.usingPSK
+}
+
+func (hs *serverHandshakeStateTLS13) sendServerCertificate() error {
+ c := hs.c
+
+ // Only one of PSK and certificates are used at a time.
+ if hs.usingPSK {
+ return nil
+ }
+
+ if hs.requestClientCert() {
+ // Request a client certificate
+ certReq := new(certificateRequestMsgTLS13)
+ certReq.ocspStapling = true
+ certReq.scts = true
+ certReq.supportedSignatureAlgorithms = supportedSignatureAlgorithms
+ if c.config.ClientCAs != nil {
+ certReq.certificateAuthorities = c.config.ClientCAs.Subjects()
+ }
+
+ hs.transcript.Write(certReq.marshal())
+ if _, err := c.writeRecord(recordTypeHandshake, certReq.marshal()); err != nil {
+ return err
+ }
+ }
+
+ certMsg := new(certificateMsgTLS13)
+
+ certMsg.certificate = *hs.cert
+ certMsg.scts = hs.clientHello.scts && len(hs.cert.SignedCertificateTimestamps) > 0
+ certMsg.ocspStapling = hs.clientHello.ocspStapling && len(hs.cert.OCSPStaple) > 0
+
+ hs.transcript.Write(certMsg.marshal())
+ if _, err := c.writeRecord(recordTypeHandshake, certMsg.marshal()); err != nil {
+ return err
+ }
+
+ certVerifyMsg := new(certificateVerifyMsg)
+ certVerifyMsg.hasSignatureAlgorithm = true
+ certVerifyMsg.signatureAlgorithm = hs.sigAlg
+
+ sigType, sigHash, err := typeAndHashFromSignatureScheme(hs.sigAlg)
+ if err != nil {
+ return c.sendAlert(alertInternalError)
+ }
+
+ signed := signedMessage(sigHash, serverSignatureContext, hs.transcript)
+ signOpts := crypto.SignerOpts(sigHash)
+ if sigType == signatureRSAPSS {
+ signOpts = &rsa.PSSOptions{SaltLength: rsa.PSSSaltLengthEqualsHash, Hash: sigHash}
+ }
+ sig, err := hs.cert.PrivateKey.(crypto.Signer).Sign(c.config.rand(), signed, signOpts)
+ if err != nil {
+ public := hs.cert.PrivateKey.(crypto.Signer).Public()
+ if rsaKey, ok := public.(*rsa.PublicKey); ok && sigType == signatureRSAPSS &&
+ rsaKey.N.BitLen()/8 < sigHash.Size()*2+2 { // key too small for RSA-PSS
+ c.sendAlert(alertHandshakeFailure)
+ } else {
+ c.sendAlert(alertInternalError)
+ }
+ return errors.New("tls: failed to sign handshake: " + err.Error())
+ }
+ certVerifyMsg.signature = sig
+
+ hs.transcript.Write(certVerifyMsg.marshal())
+ if _, err := c.writeRecord(recordTypeHandshake, certVerifyMsg.marshal()); err != nil {
+ return err
+ }
+
+ return nil
+}
+
+func (hs *serverHandshakeStateTLS13) sendServerFinished() error {
+ c := hs.c
+
+ finished := &finishedMsg{
+ verifyData: hs.suite.finishedHash(c.out.trafficSecret, hs.transcript),
+ }
+
+ hs.transcript.Write(finished.marshal())
+ if _, err := c.writeRecord(recordTypeHandshake, finished.marshal()); err != nil {
+ return err
+ }
+
+ // Derive secrets that take context through the server Finished.
+
+ hs.masterSecret = hs.suite.extract(nil,
+ hs.suite.deriveSecret(hs.handshakeSecret, "derived", nil))
+
+ hs.trafficSecret = hs.suite.deriveSecret(hs.masterSecret,
+ clientApplicationTrafficLabel, hs.transcript)
+ serverSecret := hs.suite.deriveSecret(hs.masterSecret,
+ serverApplicationTrafficLabel, hs.transcript)
+ c.out.exportKey(EncryptionApplication, hs.suite, serverSecret)
+ c.out.setTrafficSecret(hs.suite, serverSecret)
+
+ err := c.config.writeKeyLog(keyLogLabelClientTraffic, hs.clientHello.random, hs.trafficSecret)
+ if err != nil {
+ c.sendAlert(alertInternalError)
+ return err
+ }
+ err = c.config.writeKeyLog(keyLogLabelServerTraffic, hs.clientHello.random, serverSecret)
+ if err != nil {
+ c.sendAlert(alertInternalError)
+ return err
+ }
+
+ c.ekm = hs.suite.exportKeyingMaterial(hs.masterSecret, hs.transcript)
+
+ // If we did not request client certificates, at this point we can
+ // precompute the client finished and roll the transcript forward to send
+ // session tickets in our first flight.
+ if !hs.requestClientCert() {
+ if err := hs.sendSessionTickets(); err != nil {
+ return err
+ }
+ }
+
+ return nil
+}
+
+func (hs *serverHandshakeStateTLS13) shouldSendSessionTickets() bool {
+ if hs.c.config.SessionTicketsDisabled {
+ return false
+ }
+
+ // Don't send tickets the client wouldn't use. See RFC 8446, Section 4.2.9.
+ for _, pskMode := range hs.clientHello.pskModes {
+ if pskMode == pskModeDHE {
+ return true
+ }
+ }
+ return false
+}
+
+func (hs *serverHandshakeStateTLS13) sendSessionTickets() error {
+ c := hs.c
+
+ hs.clientFinished = hs.suite.finishedHash(c.in.trafficSecret, hs.transcript)
+ finishedMsg := &finishedMsg{
+ verifyData: hs.clientFinished,
+ }
+ hs.transcript.Write(finishedMsg.marshal())
+
+ if !hs.shouldSendSessionTickets() {
+ return nil
+ }
+
+ c.resumptionSecret = hs.suite.deriveSecret(hs.masterSecret,
+ resumptionLabel, hs.transcript)
+
+ // Don't send session tickets when the alternative record layer is set.
+ // Instead, save the resumption secret on the Conn.
+ // Session tickets can then be generated by calling Conn.GetSessionTicket().
+ if hs.c.extraConfig != nil && hs.c.extraConfig.AlternativeRecordLayer != nil {
+ return nil
+ }
+
+ m, err := hs.c.getSessionTicketMsg(nil)
+ if err != nil {
+ return err
+ }
+
+ if _, err := c.writeRecord(recordTypeHandshake, m.marshal()); err != nil {
+ return err
+ }
+
+ return nil
+}
+
+func (hs *serverHandshakeStateTLS13) readClientCertificate() error {
+ c := hs.c
+
+ if !hs.requestClientCert() {
+ // Make sure the connection is still being verified whether or not
+ // the server requested a client certificate.
+ if c.config.VerifyConnection != nil {
+ if err := c.config.VerifyConnection(c.connectionStateLocked()); err != nil {
+ c.sendAlert(alertBadCertificate)
+ return err
+ }
+ }
+ return nil
+ }
+
+ // If we requested a client certificate, then the client must send a
+ // certificate message. If it's empty, no CertificateVerify is sent.
+
+ msg, err := c.readHandshake()
+ if err != nil {
+ return err
+ }
+
+ certMsg, ok := msg.(*certificateMsgTLS13)
+ if !ok {
+ c.sendAlert(alertUnexpectedMessage)
+ return unexpectedMessageError(certMsg, msg)
+ }
+ hs.transcript.Write(certMsg.marshal())
+
+ if err := c.processCertsFromClient(certMsg.certificate); err != nil {
+ return err
+ }
+
+ if c.config.VerifyConnection != nil {
+ if err := c.config.VerifyConnection(c.connectionStateLocked()); err != nil {
+ c.sendAlert(alertBadCertificate)
+ return err
+ }
+ }
+
+ if len(certMsg.certificate.Certificate) != 0 {
+ msg, err = c.readHandshake()
+ if err != nil {
+ return err
+ }
+
+ certVerify, ok := msg.(*certificateVerifyMsg)
+ if !ok {
+ c.sendAlert(alertUnexpectedMessage)
+ return unexpectedMessageError(certVerify, msg)
+ }
+
+ // See RFC 8446, Section 4.4.3.
+ if !isSupportedSignatureAlgorithm(certVerify.signatureAlgorithm, supportedSignatureAlgorithms) {
+ c.sendAlert(alertIllegalParameter)
+ return errors.New("tls: client certificate used with invalid signature algorithm")
+ }
+ sigType, sigHash, err := typeAndHashFromSignatureScheme(certVerify.signatureAlgorithm)
+ if err != nil {
+ return c.sendAlert(alertInternalError)
+ }
+ if sigType == signaturePKCS1v15 || sigHash == crypto.SHA1 {
+ c.sendAlert(alertIllegalParameter)
+ return errors.New("tls: client certificate used with invalid signature algorithm")
+ }
+ signed := signedMessage(sigHash, clientSignatureContext, hs.transcript)
+ if err := verifyHandshakeSignature(sigType, c.peerCertificates[0].PublicKey,
+ sigHash, signed, certVerify.signature); err != nil {
+ c.sendAlert(alertDecryptError)
+ return errors.New("tls: invalid signature by the client certificate: " + err.Error())
+ }
+
+ hs.transcript.Write(certVerify.marshal())
+ }
+
+ // If we waited until the client certificates to send session tickets, we
+ // are ready to do it now.
+ if err := hs.sendSessionTickets(); err != nil {
+ return err
+ }
+
+ return nil
+}
+
+func (hs *serverHandshakeStateTLS13) readClientFinished() error {
+ c := hs.c
+
+ msg, err := c.readHandshake()
+ if err != nil {
+ return err
+ }
+
+ finished, ok := msg.(*finishedMsg)
+ if !ok {
+ c.sendAlert(alertUnexpectedMessage)
+ return unexpectedMessageError(finished, msg)
+ }
+
+ if !hmac.Equal(hs.clientFinished, finished.verifyData) {
+ c.sendAlert(alertDecryptError)
+ return errors.New("tls: invalid client finished hash")
+ }
+
+ c.in.exportKey(EncryptionApplication, hs.suite, hs.trafficSecret)
+ c.in.setTrafficSecret(hs.suite, hs.trafficSecret)
+
+ return nil
+}
diff --git a/vendor/github.com/quic-go/qtls-go1-18/key_agreement.go b/vendor/github.com/quic-go/qtls-go1-18/key_agreement.go
new file mode 100644
index 0000000000..453a8dcf08
--- /dev/null
+++ b/vendor/github.com/quic-go/qtls-go1-18/key_agreement.go
@@ -0,0 +1,357 @@
+// Copyright 2010 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package qtls
+
+import (
+ "crypto"
+ "crypto/md5"
+ "crypto/rsa"
+ "crypto/sha1"
+ "crypto/x509"
+ "errors"
+ "fmt"
+ "io"
+)
+
+// a keyAgreement implements the client and server side of a TLS key agreement
+// protocol by generating and processing key exchange messages.
+type keyAgreement interface {
+ // On the server side, the first two methods are called in order.
+
+ // In the case that the key agreement protocol doesn't use a
+ // ServerKeyExchange message, generateServerKeyExchange can return nil,
+ // nil.
+ generateServerKeyExchange(*config, *Certificate, *clientHelloMsg, *serverHelloMsg) (*serverKeyExchangeMsg, error)
+ processClientKeyExchange(*config, *Certificate, *clientKeyExchangeMsg, uint16) ([]byte, error)
+
+ // On the client side, the next two methods are called in order.
+
+ // This method may not be called if the server doesn't send a
+ // ServerKeyExchange message.
+ processServerKeyExchange(*config, *clientHelloMsg, *serverHelloMsg, *x509.Certificate, *serverKeyExchangeMsg) error
+ generateClientKeyExchange(*config, *clientHelloMsg, *x509.Certificate) ([]byte, *clientKeyExchangeMsg, error)
+}
+
+var errClientKeyExchange = errors.New("tls: invalid ClientKeyExchange message")
+var errServerKeyExchange = errors.New("tls: invalid ServerKeyExchange message")
+
+// rsaKeyAgreement implements the standard TLS key agreement where the client
+// encrypts the pre-master secret to the server's public key.
+type rsaKeyAgreement struct{}
+
+func (ka rsaKeyAgreement) generateServerKeyExchange(config *config, cert *Certificate, clientHello *clientHelloMsg, hello *serverHelloMsg) (*serverKeyExchangeMsg, error) {
+ return nil, nil
+}
+
+func (ka rsaKeyAgreement) processClientKeyExchange(config *config, cert *Certificate, ckx *clientKeyExchangeMsg, version uint16) ([]byte, error) {
+ if len(ckx.ciphertext) < 2 {
+ return nil, errClientKeyExchange
+ }
+ ciphertextLen := int(ckx.ciphertext[0])<<8 | int(ckx.ciphertext[1])
+ if ciphertextLen != len(ckx.ciphertext)-2 {
+ return nil, errClientKeyExchange
+ }
+ ciphertext := ckx.ciphertext[2:]
+
+ priv, ok := cert.PrivateKey.(crypto.Decrypter)
+ if !ok {
+ return nil, errors.New("tls: certificate private key does not implement crypto.Decrypter")
+ }
+ // Perform constant time RSA PKCS #1 v1.5 decryption
+ preMasterSecret, err := priv.Decrypt(config.rand(), ciphertext, &rsa.PKCS1v15DecryptOptions{SessionKeyLen: 48})
+ if err != nil {
+ return nil, err
+ }
+ // We don't check the version number in the premaster secret. For one,
+ // by checking it, we would leak information about the validity of the
+ // encrypted pre-master secret. Secondly, it provides only a small
+ // benefit against a downgrade attack and some implementations send the
+ // wrong version anyway. See the discussion at the end of section
+ // 7.4.7.1 of RFC 4346.
+ return preMasterSecret, nil
+}
+
+func (ka rsaKeyAgreement) processServerKeyExchange(config *config, clientHello *clientHelloMsg, serverHello *serverHelloMsg, cert *x509.Certificate, skx *serverKeyExchangeMsg) error {
+ return errors.New("tls: unexpected ServerKeyExchange")
+}
+
+func (ka rsaKeyAgreement) generateClientKeyExchange(config *config, clientHello *clientHelloMsg, cert *x509.Certificate) ([]byte, *clientKeyExchangeMsg, error) {
+ preMasterSecret := make([]byte, 48)
+ preMasterSecret[0] = byte(clientHello.vers >> 8)
+ preMasterSecret[1] = byte(clientHello.vers)
+ _, err := io.ReadFull(config.rand(), preMasterSecret[2:])
+ if err != nil {
+ return nil, nil, err
+ }
+
+ rsaKey, ok := cert.PublicKey.(*rsa.PublicKey)
+ if !ok {
+ return nil, nil, errors.New("tls: server certificate contains incorrect key type for selected ciphersuite")
+ }
+ encrypted, err := rsa.EncryptPKCS1v15(config.rand(), rsaKey, preMasterSecret)
+ if err != nil {
+ return nil, nil, err
+ }
+ ckx := new(clientKeyExchangeMsg)
+ ckx.ciphertext = make([]byte, len(encrypted)+2)
+ ckx.ciphertext[0] = byte(len(encrypted) >> 8)
+ ckx.ciphertext[1] = byte(len(encrypted))
+ copy(ckx.ciphertext[2:], encrypted)
+ return preMasterSecret, ckx, nil
+}
+
+// sha1Hash calculates a SHA1 hash over the given byte slices.
+func sha1Hash(slices [][]byte) []byte {
+ hsha1 := sha1.New()
+ for _, slice := range slices {
+ hsha1.Write(slice)
+ }
+ return hsha1.Sum(nil)
+}
+
+// md5SHA1Hash implements TLS 1.0's hybrid hash function which consists of the
+// concatenation of an MD5 and SHA1 hash.
+func md5SHA1Hash(slices [][]byte) []byte {
+ md5sha1 := make([]byte, md5.Size+sha1.Size)
+ hmd5 := md5.New()
+ for _, slice := range slices {
+ hmd5.Write(slice)
+ }
+ copy(md5sha1, hmd5.Sum(nil))
+ copy(md5sha1[md5.Size:], sha1Hash(slices))
+ return md5sha1
+}
+
+// hashForServerKeyExchange hashes the given slices and returns their digest
+// using the given hash function (for >= TLS 1.2) or using a default based on
+// the sigType (for earlier TLS versions). For Ed25519 signatures, which don't
+// do pre-hashing, it returns the concatenation of the slices.
+func hashForServerKeyExchange(sigType uint8, hashFunc crypto.Hash, version uint16, slices ...[]byte) []byte {
+ if sigType == signatureEd25519 {
+ var signed []byte
+ for _, slice := range slices {
+ signed = append(signed, slice...)
+ }
+ return signed
+ }
+ if version >= VersionTLS12 {
+ h := hashFunc.New()
+ for _, slice := range slices {
+ h.Write(slice)
+ }
+ digest := h.Sum(nil)
+ return digest
+ }
+ if sigType == signatureECDSA {
+ return sha1Hash(slices)
+ }
+ return md5SHA1Hash(slices)
+}
+
+// ecdheKeyAgreement implements a TLS key agreement where the server
+// generates an ephemeral EC public/private key pair and signs it. The
+// pre-master secret is then calculated using ECDH. The signature may
+// be ECDSA, Ed25519 or RSA.
+type ecdheKeyAgreement struct {
+ version uint16
+ isRSA bool
+ params ecdheParameters
+
+ // ckx and preMasterSecret are generated in processServerKeyExchange
+ // and returned in generateClientKeyExchange.
+ ckx *clientKeyExchangeMsg
+ preMasterSecret []byte
+}
+
+func (ka *ecdheKeyAgreement) generateServerKeyExchange(config *config, cert *Certificate, clientHello *clientHelloMsg, hello *serverHelloMsg) (*serverKeyExchangeMsg, error) {
+ var curveID CurveID
+ for _, c := range clientHello.supportedCurves {
+ if config.supportsCurve(c) {
+ curveID = c
+ break
+ }
+ }
+
+ if curveID == 0 {
+ return nil, errors.New("tls: no supported elliptic curves offered")
+ }
+ if _, ok := curveForCurveID(curveID); curveID != X25519 && !ok {
+ return nil, errors.New("tls: CurvePreferences includes unsupported curve")
+ }
+
+ params, err := generateECDHEParameters(config.rand(), curveID)
+ if err != nil {
+ return nil, err
+ }
+ ka.params = params
+
+ // See RFC 4492, Section 5.4.
+ ecdhePublic := params.PublicKey()
+ serverECDHEParams := make([]byte, 1+2+1+len(ecdhePublic))
+ serverECDHEParams[0] = 3 // named curve
+ serverECDHEParams[1] = byte(curveID >> 8)
+ serverECDHEParams[2] = byte(curveID)
+ serverECDHEParams[3] = byte(len(ecdhePublic))
+ copy(serverECDHEParams[4:], ecdhePublic)
+
+ priv, ok := cert.PrivateKey.(crypto.Signer)
+ if !ok {
+ return nil, fmt.Errorf("tls: certificate private key of type %T does not implement crypto.Signer", cert.PrivateKey)
+ }
+
+ var signatureAlgorithm SignatureScheme
+ var sigType uint8
+ var sigHash crypto.Hash
+ if ka.version >= VersionTLS12 {
+ signatureAlgorithm, err = selectSignatureScheme(ka.version, cert, clientHello.supportedSignatureAlgorithms)
+ if err != nil {
+ return nil, err
+ }
+ sigType, sigHash, err = typeAndHashFromSignatureScheme(signatureAlgorithm)
+ if err != nil {
+ return nil, err
+ }
+ } else {
+ sigType, sigHash, err = legacyTypeAndHashFromPublicKey(priv.Public())
+ if err != nil {
+ return nil, err
+ }
+ }
+ if (sigType == signaturePKCS1v15 || sigType == signatureRSAPSS) != ka.isRSA {
+ return nil, errors.New("tls: certificate cannot be used with the selected cipher suite")
+ }
+
+ signed := hashForServerKeyExchange(sigType, sigHash, ka.version, clientHello.random, hello.random, serverECDHEParams)
+
+ signOpts := crypto.SignerOpts(sigHash)
+ if sigType == signatureRSAPSS {
+ signOpts = &rsa.PSSOptions{SaltLength: rsa.PSSSaltLengthEqualsHash, Hash: sigHash}
+ }
+ sig, err := priv.Sign(config.rand(), signed, signOpts)
+ if err != nil {
+ return nil, errors.New("tls: failed to sign ECDHE parameters: " + err.Error())
+ }
+
+ skx := new(serverKeyExchangeMsg)
+ sigAndHashLen := 0
+ if ka.version >= VersionTLS12 {
+ sigAndHashLen = 2
+ }
+ skx.key = make([]byte, len(serverECDHEParams)+sigAndHashLen+2+len(sig))
+ copy(skx.key, serverECDHEParams)
+ k := skx.key[len(serverECDHEParams):]
+ if ka.version >= VersionTLS12 {
+ k[0] = byte(signatureAlgorithm >> 8)
+ k[1] = byte(signatureAlgorithm)
+ k = k[2:]
+ }
+ k[0] = byte(len(sig) >> 8)
+ k[1] = byte(len(sig))
+ copy(k[2:], sig)
+
+ return skx, nil
+}
+
+func (ka *ecdheKeyAgreement) processClientKeyExchange(config *config, cert *Certificate, ckx *clientKeyExchangeMsg, version uint16) ([]byte, error) {
+ if len(ckx.ciphertext) == 0 || int(ckx.ciphertext[0]) != len(ckx.ciphertext)-1 {
+ return nil, errClientKeyExchange
+ }
+
+ preMasterSecret := ka.params.SharedKey(ckx.ciphertext[1:])
+ if preMasterSecret == nil {
+ return nil, errClientKeyExchange
+ }
+
+ return preMasterSecret, nil
+}
+
+func (ka *ecdheKeyAgreement) processServerKeyExchange(config *config, clientHello *clientHelloMsg, serverHello *serverHelloMsg, cert *x509.Certificate, skx *serverKeyExchangeMsg) error {
+ if len(skx.key) < 4 {
+ return errServerKeyExchange
+ }
+ if skx.key[0] != 3 { // named curve
+ return errors.New("tls: server selected unsupported curve")
+ }
+ curveID := CurveID(skx.key[1])<<8 | CurveID(skx.key[2])
+
+ publicLen := int(skx.key[3])
+ if publicLen+4 > len(skx.key) {
+ return errServerKeyExchange
+ }
+ serverECDHEParams := skx.key[:4+publicLen]
+ publicKey := serverECDHEParams[4:]
+
+ sig := skx.key[4+publicLen:]
+ if len(sig) < 2 {
+ return errServerKeyExchange
+ }
+
+ if _, ok := curveForCurveID(curveID); curveID != X25519 && !ok {
+ return errors.New("tls: server selected unsupported curve")
+ }
+
+ params, err := generateECDHEParameters(config.rand(), curveID)
+ if err != nil {
+ return err
+ }
+ ka.params = params
+
+ ka.preMasterSecret = params.SharedKey(publicKey)
+ if ka.preMasterSecret == nil {
+ return errServerKeyExchange
+ }
+
+ ourPublicKey := params.PublicKey()
+ ka.ckx = new(clientKeyExchangeMsg)
+ ka.ckx.ciphertext = make([]byte, 1+len(ourPublicKey))
+ ka.ckx.ciphertext[0] = byte(len(ourPublicKey))
+ copy(ka.ckx.ciphertext[1:], ourPublicKey)
+
+ var sigType uint8
+ var sigHash crypto.Hash
+ if ka.version >= VersionTLS12 {
+ signatureAlgorithm := SignatureScheme(sig[0])<<8 | SignatureScheme(sig[1])
+ sig = sig[2:]
+ if len(sig) < 2 {
+ return errServerKeyExchange
+ }
+
+ if !isSupportedSignatureAlgorithm(signatureAlgorithm, clientHello.supportedSignatureAlgorithms) {
+ return errors.New("tls: certificate used with invalid signature algorithm")
+ }
+ sigType, sigHash, err = typeAndHashFromSignatureScheme(signatureAlgorithm)
+ if err != nil {
+ return err
+ }
+ } else {
+ sigType, sigHash, err = legacyTypeAndHashFromPublicKey(cert.PublicKey)
+ if err != nil {
+ return err
+ }
+ }
+ if (sigType == signaturePKCS1v15 || sigType == signatureRSAPSS) != ka.isRSA {
+ return errServerKeyExchange
+ }
+
+ sigLen := int(sig[0])<<8 | int(sig[1])
+ if sigLen+2 != len(sig) {
+ return errServerKeyExchange
+ }
+ sig = sig[2:]
+
+ signed := hashForServerKeyExchange(sigType, sigHash, ka.version, clientHello.random, serverHello.random, serverECDHEParams)
+ if err := verifyHandshakeSignature(sigType, cert.PublicKey, sigHash, signed, sig); err != nil {
+ return errors.New("tls: invalid signature by the server certificate: " + err.Error())
+ }
+ return nil
+}
+
+func (ka *ecdheKeyAgreement) generateClientKeyExchange(config *config, clientHello *clientHelloMsg, cert *x509.Certificate) ([]byte, *clientKeyExchangeMsg, error) {
+ if ka.ckx == nil {
+ return nil, nil, errors.New("tls: missing ServerKeyExchange message")
+ }
+
+ return ka.preMasterSecret, ka.ckx, nil
+}
diff --git a/vendor/github.com/quic-go/qtls-go1-18/key_schedule.go b/vendor/github.com/quic-go/qtls-go1-18/key_schedule.go
new file mode 100644
index 0000000000..da13904a6e
--- /dev/null
+++ b/vendor/github.com/quic-go/qtls-go1-18/key_schedule.go
@@ -0,0 +1,199 @@
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package qtls
+
+import (
+ "crypto/elliptic"
+ "crypto/hmac"
+ "errors"
+ "hash"
+ "io"
+ "math/big"
+
+ "golang.org/x/crypto/cryptobyte"
+ "golang.org/x/crypto/curve25519"
+ "golang.org/x/crypto/hkdf"
+)
+
+// This file contains the functions necessary to compute the TLS 1.3 key
+// schedule. See RFC 8446, Section 7.
+
+const (
+ resumptionBinderLabel = "res binder"
+ clientHandshakeTrafficLabel = "c hs traffic"
+ serverHandshakeTrafficLabel = "s hs traffic"
+ clientApplicationTrafficLabel = "c ap traffic"
+ serverApplicationTrafficLabel = "s ap traffic"
+ exporterLabel = "exp master"
+ resumptionLabel = "res master"
+ trafficUpdateLabel = "traffic upd"
+)
+
+// expandLabel implements HKDF-Expand-Label from RFC 8446, Section 7.1.
+func (c *cipherSuiteTLS13) expandLabel(secret []byte, label string, context []byte, length int) []byte {
+ var hkdfLabel cryptobyte.Builder
+ hkdfLabel.AddUint16(uint16(length))
+ hkdfLabel.AddUint8LengthPrefixed(func(b *cryptobyte.Builder) {
+ b.AddBytes([]byte("tls13 "))
+ b.AddBytes([]byte(label))
+ })
+ hkdfLabel.AddUint8LengthPrefixed(func(b *cryptobyte.Builder) {
+ b.AddBytes(context)
+ })
+ out := make([]byte, length)
+ n, err := hkdf.Expand(c.hash.New, secret, hkdfLabel.BytesOrPanic()).Read(out)
+ if err != nil || n != length {
+ panic("tls: HKDF-Expand-Label invocation failed unexpectedly")
+ }
+ return out
+}
+
+// deriveSecret implements Derive-Secret from RFC 8446, Section 7.1.
+func (c *cipherSuiteTLS13) deriveSecret(secret []byte, label string, transcript hash.Hash) []byte {
+ if transcript == nil {
+ transcript = c.hash.New()
+ }
+ return c.expandLabel(secret, label, transcript.Sum(nil), c.hash.Size())
+}
+
+// extract implements HKDF-Extract with the cipher suite hash.
+func (c *cipherSuiteTLS13) extract(newSecret, currentSecret []byte) []byte {
+ if newSecret == nil {
+ newSecret = make([]byte, c.hash.Size())
+ }
+ return hkdf.Extract(c.hash.New, newSecret, currentSecret)
+}
+
+// nextTrafficSecret generates the next traffic secret, given the current one,
+// according to RFC 8446, Section 7.2.
+func (c *cipherSuiteTLS13) nextTrafficSecret(trafficSecret []byte) []byte {
+ return c.expandLabel(trafficSecret, trafficUpdateLabel, nil, c.hash.Size())
+}
+
+// trafficKey generates traffic keys according to RFC 8446, Section 7.3.
+func (c *cipherSuiteTLS13) trafficKey(trafficSecret []byte) (key, iv []byte) {
+ key = c.expandLabel(trafficSecret, "key", nil, c.keyLen)
+ iv = c.expandLabel(trafficSecret, "iv", nil, aeadNonceLength)
+ return
+}
+
+// finishedHash generates the Finished verify_data or PskBinderEntry according
+// to RFC 8446, Section 4.4.4. See sections 4.4 and 4.2.11.2 for the baseKey
+// selection.
+func (c *cipherSuiteTLS13) finishedHash(baseKey []byte, transcript hash.Hash) []byte {
+ finishedKey := c.expandLabel(baseKey, "finished", nil, c.hash.Size())
+ verifyData := hmac.New(c.hash.New, finishedKey)
+ verifyData.Write(transcript.Sum(nil))
+ return verifyData.Sum(nil)
+}
+
+// exportKeyingMaterial implements RFC5705 exporters for TLS 1.3 according to
+// RFC 8446, Section 7.5.
+func (c *cipherSuiteTLS13) exportKeyingMaterial(masterSecret []byte, transcript hash.Hash) func(string, []byte, int) ([]byte, error) {
+ expMasterSecret := c.deriveSecret(masterSecret, exporterLabel, transcript)
+ return func(label string, context []byte, length int) ([]byte, error) {
+ secret := c.deriveSecret(expMasterSecret, label, nil)
+ h := c.hash.New()
+ h.Write(context)
+ return c.expandLabel(secret, "exporter", h.Sum(nil), length), nil
+ }
+}
+
+// ecdheParameters implements Diffie-Hellman with either NIST curves or X25519,
+// according to RFC 8446, Section 4.2.8.2.
+type ecdheParameters interface {
+ CurveID() CurveID
+ PublicKey() []byte
+ SharedKey(peerPublicKey []byte) []byte
+}
+
+func generateECDHEParameters(rand io.Reader, curveID CurveID) (ecdheParameters, error) {
+ if curveID == X25519 {
+ privateKey := make([]byte, curve25519.ScalarSize)
+ if _, err := io.ReadFull(rand, privateKey); err != nil {
+ return nil, err
+ }
+ publicKey, err := curve25519.X25519(privateKey, curve25519.Basepoint)
+ if err != nil {
+ return nil, err
+ }
+ return &x25519Parameters{privateKey: privateKey, publicKey: publicKey}, nil
+ }
+
+ curve, ok := curveForCurveID(curveID)
+ if !ok {
+ return nil, errors.New("tls: internal error: unsupported curve")
+ }
+
+ p := &nistParameters{curveID: curveID}
+ var err error
+ p.privateKey, p.x, p.y, err = elliptic.GenerateKey(curve, rand)
+ if err != nil {
+ return nil, err
+ }
+ return p, nil
+}
+
+func curveForCurveID(id CurveID) (elliptic.Curve, bool) {
+ switch id {
+ case CurveP256:
+ return elliptic.P256(), true
+ case CurveP384:
+ return elliptic.P384(), true
+ case CurveP521:
+ return elliptic.P521(), true
+ default:
+ return nil, false
+ }
+}
+
+type nistParameters struct {
+ privateKey []byte
+ x, y *big.Int // public key
+ curveID CurveID
+}
+
+func (p *nistParameters) CurveID() CurveID {
+ return p.curveID
+}
+
+func (p *nistParameters) PublicKey() []byte {
+ curve, _ := curveForCurveID(p.curveID)
+ return elliptic.Marshal(curve, p.x, p.y)
+}
+
+func (p *nistParameters) SharedKey(peerPublicKey []byte) []byte {
+ curve, _ := curveForCurveID(p.curveID)
+ // Unmarshal also checks whether the given point is on the curve.
+ x, y := elliptic.Unmarshal(curve, peerPublicKey)
+ if x == nil {
+ return nil
+ }
+
+ xShared, _ := curve.ScalarMult(x, y, p.privateKey)
+ sharedKey := make([]byte, (curve.Params().BitSize+7)/8)
+ return xShared.FillBytes(sharedKey)
+}
+
+type x25519Parameters struct {
+ privateKey []byte
+ publicKey []byte
+}
+
+func (p *x25519Parameters) CurveID() CurveID {
+ return X25519
+}
+
+func (p *x25519Parameters) PublicKey() []byte {
+ return p.publicKey[:]
+}
+
+func (p *x25519Parameters) SharedKey(peerPublicKey []byte) []byte {
+ sharedKey, err := curve25519.X25519(p.privateKey, peerPublicKey)
+ if err != nil {
+ return nil
+ }
+ return sharedKey
+}
diff --git a/vendor/github.com/quic-go/qtls-go1-18/prf.go b/vendor/github.com/quic-go/qtls-go1-18/prf.go
new file mode 100644
index 0000000000..9eb0221a0c
--- /dev/null
+++ b/vendor/github.com/quic-go/qtls-go1-18/prf.go
@@ -0,0 +1,283 @@
+// Copyright 2009 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package qtls
+
+import (
+ "crypto"
+ "crypto/hmac"
+ "crypto/md5"
+ "crypto/sha1"
+ "crypto/sha256"
+ "crypto/sha512"
+ "errors"
+ "fmt"
+ "hash"
+)
+
+// Split a premaster secret in two as specified in RFC 4346, Section 5.
+func splitPreMasterSecret(secret []byte) (s1, s2 []byte) {
+ s1 = secret[0 : (len(secret)+1)/2]
+ s2 = secret[len(secret)/2:]
+ return
+}
+
+// pHash implements the P_hash function, as defined in RFC 4346, Section 5.
+func pHash(result, secret, seed []byte, hash func() hash.Hash) {
+ h := hmac.New(hash, secret)
+ h.Write(seed)
+ a := h.Sum(nil)
+
+ j := 0
+ for j < len(result) {
+ h.Reset()
+ h.Write(a)
+ h.Write(seed)
+ b := h.Sum(nil)
+ copy(result[j:], b)
+ j += len(b)
+
+ h.Reset()
+ h.Write(a)
+ a = h.Sum(nil)
+ }
+}
+
+// prf10 implements the TLS 1.0 pseudo-random function, as defined in RFC 2246, Section 5.
+func prf10(result, secret, label, seed []byte) {
+ hashSHA1 := sha1.New
+ hashMD5 := md5.New
+
+ labelAndSeed := make([]byte, len(label)+len(seed))
+ copy(labelAndSeed, label)
+ copy(labelAndSeed[len(label):], seed)
+
+ s1, s2 := splitPreMasterSecret(secret)
+ pHash(result, s1, labelAndSeed, hashMD5)
+ result2 := make([]byte, len(result))
+ pHash(result2, s2, labelAndSeed, hashSHA1)
+
+ for i, b := range result2 {
+ result[i] ^= b
+ }
+}
+
+// prf12 implements the TLS 1.2 pseudo-random function, as defined in RFC 5246, Section 5.
+func prf12(hashFunc func() hash.Hash) func(result, secret, label, seed []byte) {
+ return func(result, secret, label, seed []byte) {
+ labelAndSeed := make([]byte, len(label)+len(seed))
+ copy(labelAndSeed, label)
+ copy(labelAndSeed[len(label):], seed)
+
+ pHash(result, secret, labelAndSeed, hashFunc)
+ }
+}
+
+const (
+ masterSecretLength = 48 // Length of a master secret in TLS 1.1.
+ finishedVerifyLength = 12 // Length of verify_data in a Finished message.
+)
+
+var masterSecretLabel = []byte("master secret")
+var keyExpansionLabel = []byte("key expansion")
+var clientFinishedLabel = []byte("client finished")
+var serverFinishedLabel = []byte("server finished")
+
+func prfAndHashForVersion(version uint16, suite *cipherSuite) (func(result, secret, label, seed []byte), crypto.Hash) {
+ switch version {
+ case VersionTLS10, VersionTLS11:
+ return prf10, crypto.Hash(0)
+ case VersionTLS12:
+ if suite.flags&suiteSHA384 != 0 {
+ return prf12(sha512.New384), crypto.SHA384
+ }
+ return prf12(sha256.New), crypto.SHA256
+ default:
+ panic("unknown version")
+ }
+}
+
+func prfForVersion(version uint16, suite *cipherSuite) func(result, secret, label, seed []byte) {
+ prf, _ := prfAndHashForVersion(version, suite)
+ return prf
+}
+
+// masterFromPreMasterSecret generates the master secret from the pre-master
+// secret. See RFC 5246, Section 8.1.
+func masterFromPreMasterSecret(version uint16, suite *cipherSuite, preMasterSecret, clientRandom, serverRandom []byte) []byte {
+ seed := make([]byte, 0, len(clientRandom)+len(serverRandom))
+ seed = append(seed, clientRandom...)
+ seed = append(seed, serverRandom...)
+
+ masterSecret := make([]byte, masterSecretLength)
+ prfForVersion(version, suite)(masterSecret, preMasterSecret, masterSecretLabel, seed)
+ return masterSecret
+}
+
+// keysFromMasterSecret generates the connection keys from the master
+// secret, given the lengths of the MAC key, cipher key and IV, as defined in
+// RFC 2246, Section 6.3.
+func keysFromMasterSecret(version uint16, suite *cipherSuite, masterSecret, clientRandom, serverRandom []byte, macLen, keyLen, ivLen int) (clientMAC, serverMAC, clientKey, serverKey, clientIV, serverIV []byte) {
+ seed := make([]byte, 0, len(serverRandom)+len(clientRandom))
+ seed = append(seed, serverRandom...)
+ seed = append(seed, clientRandom...)
+
+ n := 2*macLen + 2*keyLen + 2*ivLen
+ keyMaterial := make([]byte, n)
+ prfForVersion(version, suite)(keyMaterial, masterSecret, keyExpansionLabel, seed)
+ clientMAC = keyMaterial[:macLen]
+ keyMaterial = keyMaterial[macLen:]
+ serverMAC = keyMaterial[:macLen]
+ keyMaterial = keyMaterial[macLen:]
+ clientKey = keyMaterial[:keyLen]
+ keyMaterial = keyMaterial[keyLen:]
+ serverKey = keyMaterial[:keyLen]
+ keyMaterial = keyMaterial[keyLen:]
+ clientIV = keyMaterial[:ivLen]
+ keyMaterial = keyMaterial[ivLen:]
+ serverIV = keyMaterial[:ivLen]
+ return
+}
+
+func newFinishedHash(version uint16, cipherSuite *cipherSuite) finishedHash {
+ var buffer []byte
+ if version >= VersionTLS12 {
+ buffer = []byte{}
+ }
+
+ prf, hash := prfAndHashForVersion(version, cipherSuite)
+ if hash != 0 {
+ return finishedHash{hash.New(), hash.New(), nil, nil, buffer, version, prf}
+ }
+
+ return finishedHash{sha1.New(), sha1.New(), md5.New(), md5.New(), buffer, version, prf}
+}
+
+// A finishedHash calculates the hash of a set of handshake messages suitable
+// for including in a Finished message.
+type finishedHash struct {
+ client hash.Hash
+ server hash.Hash
+
+ // Prior to TLS 1.2, an additional MD5 hash is required.
+ clientMD5 hash.Hash
+ serverMD5 hash.Hash
+
+ // In TLS 1.2, a full buffer is sadly required.
+ buffer []byte
+
+ version uint16
+ prf func(result, secret, label, seed []byte)
+}
+
+func (h *finishedHash) Write(msg []byte) (n int, err error) {
+ h.client.Write(msg)
+ h.server.Write(msg)
+
+ if h.version < VersionTLS12 {
+ h.clientMD5.Write(msg)
+ h.serverMD5.Write(msg)
+ }
+
+ if h.buffer != nil {
+ h.buffer = append(h.buffer, msg...)
+ }
+
+ return len(msg), nil
+}
+
+func (h finishedHash) Sum() []byte {
+ if h.version >= VersionTLS12 {
+ return h.client.Sum(nil)
+ }
+
+ out := make([]byte, 0, md5.Size+sha1.Size)
+ out = h.clientMD5.Sum(out)
+ return h.client.Sum(out)
+}
+
+// clientSum returns the contents of the verify_data member of a client's
+// Finished message.
+func (h finishedHash) clientSum(masterSecret []byte) []byte {
+ out := make([]byte, finishedVerifyLength)
+ h.prf(out, masterSecret, clientFinishedLabel, h.Sum())
+ return out
+}
+
+// serverSum returns the contents of the verify_data member of a server's
+// Finished message.
+func (h finishedHash) serverSum(masterSecret []byte) []byte {
+ out := make([]byte, finishedVerifyLength)
+ h.prf(out, masterSecret, serverFinishedLabel, h.Sum())
+ return out
+}
+
+// hashForClientCertificate returns the handshake messages so far, pre-hashed if
+// necessary, suitable for signing by a TLS client certificate.
+func (h finishedHash) hashForClientCertificate(sigType uint8, hashAlg crypto.Hash, masterSecret []byte) []byte {
+ if (h.version >= VersionTLS12 || sigType == signatureEd25519) && h.buffer == nil {
+ panic("tls: handshake hash for a client certificate requested after discarding the handshake buffer")
+ }
+
+ if sigType == signatureEd25519 {
+ return h.buffer
+ }
+
+ if h.version >= VersionTLS12 {
+ hash := hashAlg.New()
+ hash.Write(h.buffer)
+ return hash.Sum(nil)
+ }
+
+ if sigType == signatureECDSA {
+ return h.server.Sum(nil)
+ }
+
+ return h.Sum()
+}
+
+// discardHandshakeBuffer is called when there is no more need to
+// buffer the entirety of the handshake messages.
+func (h *finishedHash) discardHandshakeBuffer() {
+ h.buffer = nil
+}
+
+// noExportedKeyingMaterial is used as a value of
+// ConnectionState.ekm when renegotiation is enabled and thus
+// we wish to fail all key-material export requests.
+func noExportedKeyingMaterial(label string, context []byte, length int) ([]byte, error) {
+ return nil, errors.New("crypto/tls: ExportKeyingMaterial is unavailable when renegotiation is enabled")
+}
+
+// ekmFromMasterSecret generates exported keying material as defined in RFC 5705.
+func ekmFromMasterSecret(version uint16, suite *cipherSuite, masterSecret, clientRandom, serverRandom []byte) func(string, []byte, int) ([]byte, error) {
+ return func(label string, context []byte, length int) ([]byte, error) {
+ switch label {
+ case "client finished", "server finished", "master secret", "key expansion":
+ // These values are reserved and may not be used.
+ return nil, fmt.Errorf("crypto/tls: reserved ExportKeyingMaterial label: %s", label)
+ }
+
+ seedLen := len(serverRandom) + len(clientRandom)
+ if context != nil {
+ seedLen += 2 + len(context)
+ }
+ seed := make([]byte, 0, seedLen)
+
+ seed = append(seed, clientRandom...)
+ seed = append(seed, serverRandom...)
+
+ if context != nil {
+ if len(context) >= 1<<16 {
+ return nil, fmt.Errorf("crypto/tls: ExportKeyingMaterial context too long")
+ }
+ seed = append(seed, byte(len(context)>>8), byte(len(context)))
+ seed = append(seed, context...)
+ }
+
+ keyMaterial := make([]byte, length)
+ prfForVersion(version, suite)(keyMaterial, masterSecret, []byte(label), seed)
+ return keyMaterial, nil
+ }
+}
diff --git a/vendor/github.com/quic-go/qtls-go1-18/ticket.go b/vendor/github.com/quic-go/qtls-go1-18/ticket.go
new file mode 100644
index 0000000000..81e8a52eac
--- /dev/null
+++ b/vendor/github.com/quic-go/qtls-go1-18/ticket.go
@@ -0,0 +1,274 @@
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package qtls
+
+import (
+ "bytes"
+ "crypto/aes"
+ "crypto/cipher"
+ "crypto/hmac"
+ "crypto/sha256"
+ "crypto/subtle"
+ "encoding/binary"
+ "errors"
+ "io"
+ "time"
+
+ "golang.org/x/crypto/cryptobyte"
+)
+
+// sessionState contains the information that is serialized into a session
+// ticket in order to later resume a connection.
+type sessionState struct {
+ vers uint16
+ cipherSuite uint16
+ createdAt uint64
+ masterSecret []byte // opaque master_secret<1..2^16-1>;
+ // struct { opaque certificate<1..2^24-1> } Certificate;
+ certificates [][]byte // Certificate certificate_list<0..2^24-1>;
+
+ // usedOldKey is true if the ticket from which this session came from
+ // was encrypted with an older key and thus should be refreshed.
+ usedOldKey bool
+}
+
+func (m *sessionState) marshal() []byte {
+ var b cryptobyte.Builder
+ b.AddUint16(m.vers)
+ b.AddUint16(m.cipherSuite)
+ addUint64(&b, m.createdAt)
+ b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
+ b.AddBytes(m.masterSecret)
+ })
+ b.AddUint24LengthPrefixed(func(b *cryptobyte.Builder) {
+ for _, cert := range m.certificates {
+ b.AddUint24LengthPrefixed(func(b *cryptobyte.Builder) {
+ b.AddBytes(cert)
+ })
+ }
+ })
+ return b.BytesOrPanic()
+}
+
+func (m *sessionState) unmarshal(data []byte) bool {
+ *m = sessionState{usedOldKey: m.usedOldKey}
+ s := cryptobyte.String(data)
+ if ok := s.ReadUint16(&m.vers) &&
+ s.ReadUint16(&m.cipherSuite) &&
+ readUint64(&s, &m.createdAt) &&
+ readUint16LengthPrefixed(&s, &m.masterSecret) &&
+ len(m.masterSecret) != 0; !ok {
+ return false
+ }
+ var certList cryptobyte.String
+ if !s.ReadUint24LengthPrefixed(&certList) {
+ return false
+ }
+ for !certList.Empty() {
+ var cert []byte
+ if !readUint24LengthPrefixed(&certList, &cert) {
+ return false
+ }
+ m.certificates = append(m.certificates, cert)
+ }
+ return s.Empty()
+}
+
+// sessionStateTLS13 is the content of a TLS 1.3 session ticket. Its first
+// version (revision = 0) doesn't carry any of the information needed for 0-RTT
+// validation and the nonce is always empty.
+// version (revision = 1) carries the max_early_data_size sent in the ticket.
+// version (revision = 2) carries the ALPN sent in the ticket.
+type sessionStateTLS13 struct {
+ // uint8 version = 0x0304;
+ // uint8 revision = 2;
+ cipherSuite uint16
+ createdAt uint64
+ resumptionSecret []byte // opaque resumption_master_secret<1..2^8-1>;
+ certificate Certificate // CertificateEntry certificate_list<0..2^24-1>;
+ maxEarlyData uint32
+ alpn string
+
+ appData []byte
+}
+
+func (m *sessionStateTLS13) marshal() []byte {
+ var b cryptobyte.Builder
+ b.AddUint16(VersionTLS13)
+ b.AddUint8(2) // revision
+ b.AddUint16(m.cipherSuite)
+ addUint64(&b, m.createdAt)
+ b.AddUint8LengthPrefixed(func(b *cryptobyte.Builder) {
+ b.AddBytes(m.resumptionSecret)
+ })
+ marshalCertificate(&b, m.certificate)
+ b.AddUint32(m.maxEarlyData)
+ b.AddUint8LengthPrefixed(func(b *cryptobyte.Builder) {
+ b.AddBytes([]byte(m.alpn))
+ })
+ b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
+ b.AddBytes(m.appData)
+ })
+ return b.BytesOrPanic()
+}
+
+func (m *sessionStateTLS13) unmarshal(data []byte) bool {
+ *m = sessionStateTLS13{}
+ s := cryptobyte.String(data)
+ var version uint16
+ var revision uint8
+ var alpn []byte
+ ret := s.ReadUint16(&version) &&
+ version == VersionTLS13 &&
+ s.ReadUint8(&revision) &&
+ revision == 2 &&
+ s.ReadUint16(&m.cipherSuite) &&
+ readUint64(&s, &m.createdAt) &&
+ readUint8LengthPrefixed(&s, &m.resumptionSecret) &&
+ len(m.resumptionSecret) != 0 &&
+ unmarshalCertificate(&s, &m.certificate) &&
+ s.ReadUint32(&m.maxEarlyData) &&
+ readUint8LengthPrefixed(&s, &alpn) &&
+ readUint16LengthPrefixed(&s, &m.appData) &&
+ s.Empty()
+ m.alpn = string(alpn)
+ return ret
+}
+
+func (c *Conn) encryptTicket(state []byte) ([]byte, error) {
+ if len(c.ticketKeys) == 0 {
+ return nil, errors.New("tls: internal error: session ticket keys unavailable")
+ }
+
+ encrypted := make([]byte, ticketKeyNameLen+aes.BlockSize+len(state)+sha256.Size)
+ keyName := encrypted[:ticketKeyNameLen]
+ iv := encrypted[ticketKeyNameLen : ticketKeyNameLen+aes.BlockSize]
+ macBytes := encrypted[len(encrypted)-sha256.Size:]
+
+ if _, err := io.ReadFull(c.config.rand(), iv); err != nil {
+ return nil, err
+ }
+ key := c.ticketKeys[0]
+ copy(keyName, key.keyName[:])
+ block, err := aes.NewCipher(key.aesKey[:])
+ if err != nil {
+ return nil, errors.New("tls: failed to create cipher while encrypting ticket: " + err.Error())
+ }
+ cipher.NewCTR(block, iv).XORKeyStream(encrypted[ticketKeyNameLen+aes.BlockSize:], state)
+
+ mac := hmac.New(sha256.New, key.hmacKey[:])
+ mac.Write(encrypted[:len(encrypted)-sha256.Size])
+ mac.Sum(macBytes[:0])
+
+ return encrypted, nil
+}
+
+func (c *Conn) decryptTicket(encrypted []byte) (plaintext []byte, usedOldKey bool) {
+ if len(encrypted) < ticketKeyNameLen+aes.BlockSize+sha256.Size {
+ return nil, false
+ }
+
+ keyName := encrypted[:ticketKeyNameLen]
+ iv := encrypted[ticketKeyNameLen : ticketKeyNameLen+aes.BlockSize]
+ macBytes := encrypted[len(encrypted)-sha256.Size:]
+ ciphertext := encrypted[ticketKeyNameLen+aes.BlockSize : len(encrypted)-sha256.Size]
+
+ keyIndex := -1
+ for i, candidateKey := range c.ticketKeys {
+ if bytes.Equal(keyName, candidateKey.keyName[:]) {
+ keyIndex = i
+ break
+ }
+ }
+ if keyIndex == -1 {
+ return nil, false
+ }
+ key := &c.ticketKeys[keyIndex]
+
+ mac := hmac.New(sha256.New, key.hmacKey[:])
+ mac.Write(encrypted[:len(encrypted)-sha256.Size])
+ expected := mac.Sum(nil)
+
+ if subtle.ConstantTimeCompare(macBytes, expected) != 1 {
+ return nil, false
+ }
+
+ block, err := aes.NewCipher(key.aesKey[:])
+ if err != nil {
+ return nil, false
+ }
+ plaintext = make([]byte, len(ciphertext))
+ cipher.NewCTR(block, iv).XORKeyStream(plaintext, ciphertext)
+
+ return plaintext, keyIndex > 0
+}
+
+func (c *Conn) getSessionTicketMsg(appData []byte) (*newSessionTicketMsgTLS13, error) {
+ m := new(newSessionTicketMsgTLS13)
+
+ var certsFromClient [][]byte
+ for _, cert := range c.peerCertificates {
+ certsFromClient = append(certsFromClient, cert.Raw)
+ }
+ state := sessionStateTLS13{
+ cipherSuite: c.cipherSuite,
+ createdAt: uint64(c.config.time().Unix()),
+ resumptionSecret: c.resumptionSecret,
+ certificate: Certificate{
+ Certificate: certsFromClient,
+ OCSPStaple: c.ocspResponse,
+ SignedCertificateTimestamps: c.scts,
+ },
+ appData: appData,
+ alpn: c.clientProtocol,
+ }
+ if c.extraConfig != nil {
+ state.maxEarlyData = c.extraConfig.MaxEarlyData
+ }
+ var err error
+ m.label, err = c.encryptTicket(state.marshal())
+ if err != nil {
+ return nil, err
+ }
+ m.lifetime = uint32(maxSessionTicketLifetime / time.Second)
+
+ // ticket_age_add is a random 32-bit value. See RFC 8446, section 4.6.1
+ // The value is not stored anywhere; we never need to check the ticket age
+ // because 0-RTT is not supported.
+ ageAdd := make([]byte, 4)
+ _, err = c.config.rand().Read(ageAdd)
+ if err != nil {
+ return nil, err
+ }
+ m.ageAdd = binary.LittleEndian.Uint32(ageAdd)
+
+ // ticket_nonce, which must be unique per connection, is always left at
+ // zero because we only ever send one ticket per connection.
+
+ if c.extraConfig != nil {
+ m.maxEarlyData = c.extraConfig.MaxEarlyData
+ }
+ return m, nil
+}
+
+// GetSessionTicket generates a new session ticket.
+// It should only be called after the handshake completes.
+// It can only be used for servers, and only if the alternative record layer is set.
+// The ticket may be nil if config.SessionTicketsDisabled is set,
+// or if the client isn't able to receive session tickets.
+func (c *Conn) GetSessionTicket(appData []byte) ([]byte, error) {
+ if c.isClient || !c.handshakeComplete() || c.extraConfig == nil || c.extraConfig.AlternativeRecordLayer == nil {
+ return nil, errors.New("GetSessionTicket is only valid for servers after completion of the handshake, and if an alternative record layer is set.")
+ }
+ if c.config.SessionTicketsDisabled {
+ return nil, nil
+ }
+
+ m, err := c.getSessionTicketMsg(appData)
+ if err != nil {
+ return nil, err
+ }
+ return m.marshal(), nil
+}
diff --git a/vendor/github.com/quic-go/qtls-go1-18/tls.go b/vendor/github.com/quic-go/qtls-go1-18/tls.go
new file mode 100644
index 0000000000..42207c235f
--- /dev/null
+++ b/vendor/github.com/quic-go/qtls-go1-18/tls.go
@@ -0,0 +1,362 @@
+// Copyright 2009 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// package qtls partially implements TLS 1.2, as specified in RFC 5246,
+// and TLS 1.3, as specified in RFC 8446.
+package qtls
+
+// BUG(agl): The crypto/tls package only implements some countermeasures
+// against Lucky13 attacks on CBC-mode encryption, and only on SHA1
+// variants. See http://www.isg.rhul.ac.uk/tls/TLStiming.pdf and
+// https://www.imperialviolet.org/2013/02/04/luckythirteen.html.
+
+import (
+ "bytes"
+ "context"
+ "crypto"
+ "crypto/ecdsa"
+ "crypto/ed25519"
+ "crypto/rsa"
+ "crypto/x509"
+ "encoding/pem"
+ "errors"
+ "fmt"
+ "net"
+ "os"
+ "strings"
+)
+
+// Server returns a new TLS server side connection
+// using conn as the underlying transport.
+// The configuration config must be non-nil and must include
+// at least one certificate or else set GetCertificate.
+func Server(conn net.Conn, config *Config, extraConfig *ExtraConfig) *Conn {
+ c := &Conn{
+ conn: conn,
+ config: fromConfig(config),
+ extraConfig: extraConfig,
+ }
+ c.handshakeFn = c.serverHandshake
+ return c
+}
+
+// Client returns a new TLS client side connection
+// using conn as the underlying transport.
+// The config cannot be nil: users must set either ServerName or
+// InsecureSkipVerify in the config.
+func Client(conn net.Conn, config *Config, extraConfig *ExtraConfig) *Conn {
+ c := &Conn{
+ conn: conn,
+ config: fromConfig(config),
+ extraConfig: extraConfig,
+ isClient: true,
+ }
+ c.handshakeFn = c.clientHandshake
+ return c
+}
+
+// A listener implements a network listener (net.Listener) for TLS connections.
+type listener struct {
+ net.Listener
+ config *Config
+ extraConfig *ExtraConfig
+}
+
+// Accept waits for and returns the next incoming TLS connection.
+// The returned connection is of type *Conn.
+func (l *listener) Accept() (net.Conn, error) {
+ c, err := l.Listener.Accept()
+ if err != nil {
+ return nil, err
+ }
+ return Server(c, l.config, l.extraConfig), nil
+}
+
+// NewListener creates a Listener which accepts connections from an inner
+// Listener and wraps each connection with Server.
+// The configuration config must be non-nil and must include
+// at least one certificate or else set GetCertificate.
+func NewListener(inner net.Listener, config *Config, extraConfig *ExtraConfig) net.Listener {
+ l := new(listener)
+ l.Listener = inner
+ l.config = config
+ l.extraConfig = extraConfig
+ return l
+}
+
+// Listen creates a TLS listener accepting connections on the
+// given network address using net.Listen.
+// The configuration config must be non-nil and must include
+// at least one certificate or else set GetCertificate.
+func Listen(network, laddr string, config *Config, extraConfig *ExtraConfig) (net.Listener, error) {
+ if config == nil || len(config.Certificates) == 0 &&
+ config.GetCertificate == nil && config.GetConfigForClient == nil {
+ return nil, errors.New("tls: neither Certificates, GetCertificate, nor GetConfigForClient set in Config")
+ }
+ l, err := net.Listen(network, laddr)
+ if err != nil {
+ return nil, err
+ }
+ return NewListener(l, config, extraConfig), nil
+}
+
+type timeoutError struct{}
+
+func (timeoutError) Error() string { return "tls: DialWithDialer timed out" }
+func (timeoutError) Timeout() bool { return true }
+func (timeoutError) Temporary() bool { return true }
+
+// DialWithDialer connects to the given network address using dialer.Dial and
+// then initiates a TLS handshake, returning the resulting TLS connection. Any
+// timeout or deadline given in the dialer apply to connection and TLS
+// handshake as a whole.
+//
+// DialWithDialer interprets a nil configuration as equivalent to the zero
+// configuration; see the documentation of Config for the defaults.
+//
+// DialWithDialer uses context.Background internally; to specify the context,
+// use Dialer.DialContext with NetDialer set to the desired dialer.
+func DialWithDialer(dialer *net.Dialer, network, addr string, config *Config, extraConfig *ExtraConfig) (*Conn, error) {
+ return dial(context.Background(), dialer, network, addr, config, extraConfig)
+}
+
+func dial(ctx context.Context, netDialer *net.Dialer, network, addr string, config *Config, extraConfig *ExtraConfig) (*Conn, error) {
+ if netDialer.Timeout != 0 {
+ var cancel context.CancelFunc
+ ctx, cancel = context.WithTimeout(ctx, netDialer.Timeout)
+ defer cancel()
+ }
+
+ if !netDialer.Deadline.IsZero() {
+ var cancel context.CancelFunc
+ ctx, cancel = context.WithDeadline(ctx, netDialer.Deadline)
+ defer cancel()
+ }
+
+ rawConn, err := netDialer.DialContext(ctx, network, addr)
+ if err != nil {
+ return nil, err
+ }
+
+ colonPos := strings.LastIndex(addr, ":")
+ if colonPos == -1 {
+ colonPos = len(addr)
+ }
+ hostname := addr[:colonPos]
+
+ if config == nil {
+ config = defaultConfig()
+ }
+ // If no ServerName is set, infer the ServerName
+ // from the hostname we're connecting to.
+ if config.ServerName == "" {
+ // Make a copy to avoid polluting argument or default.
+ c := config.Clone()
+ c.ServerName = hostname
+ config = c
+ }
+
+ conn := Client(rawConn, config, extraConfig)
+ if err := conn.HandshakeContext(ctx); err != nil {
+ rawConn.Close()
+ return nil, err
+ }
+ return conn, nil
+}
+
+// Dial connects to the given network address using net.Dial
+// and then initiates a TLS handshake, returning the resulting
+// TLS connection.
+// Dial interprets a nil configuration as equivalent to
+// the zero configuration; see the documentation of Config
+// for the defaults.
+func Dial(network, addr string, config *Config, extraConfig *ExtraConfig) (*Conn, error) {
+ return DialWithDialer(new(net.Dialer), network, addr, config, extraConfig)
+}
+
+// Dialer dials TLS connections given a configuration and a Dialer for the
+// underlying connection.
+type Dialer struct {
+ // NetDialer is the optional dialer to use for the TLS connections'
+ // underlying TCP connections.
+ // A nil NetDialer is equivalent to the net.Dialer zero value.
+ NetDialer *net.Dialer
+
+ // Config is the TLS configuration to use for new connections.
+ // A nil configuration is equivalent to the zero
+ // configuration; see the documentation of Config for the
+ // defaults.
+ Config *Config
+
+ ExtraConfig *ExtraConfig
+}
+
+// Dial connects to the given network address and initiates a TLS
+// handshake, returning the resulting TLS connection.
+//
+// The returned Conn, if any, will always be of type *Conn.
+//
+// Dial uses context.Background internally; to specify the context,
+// use DialContext.
+func (d *Dialer) Dial(network, addr string) (net.Conn, error) {
+ return d.DialContext(context.Background(), network, addr)
+}
+
+func (d *Dialer) netDialer() *net.Dialer {
+ if d.NetDialer != nil {
+ return d.NetDialer
+ }
+ return new(net.Dialer)
+}
+
+// DialContext connects to the given network address and initiates a TLS
+// handshake, returning the resulting TLS connection.
+//
+// The provided Context must be non-nil. If the context expires before
+// the connection is complete, an error is returned. Once successfully
+// connected, any expiration of the context will not affect the
+// connection.
+//
+// The returned Conn, if any, will always be of type *Conn.
+func (d *Dialer) DialContext(ctx context.Context, network, addr string) (net.Conn, error) {
+ c, err := dial(ctx, d.netDialer(), network, addr, d.Config, d.ExtraConfig)
+ if err != nil {
+ // Don't return c (a typed nil) in an interface.
+ return nil, err
+ }
+ return c, nil
+}
+
+// LoadX509KeyPair reads and parses a public/private key pair from a pair
+// of files. The files must contain PEM encoded data. The certificate file
+// may contain intermediate certificates following the leaf certificate to
+// form a certificate chain. On successful return, Certificate.Leaf will
+// be nil because the parsed form of the certificate is not retained.
+func LoadX509KeyPair(certFile, keyFile string) (Certificate, error) {
+ certPEMBlock, err := os.ReadFile(certFile)
+ if err != nil {
+ return Certificate{}, err
+ }
+ keyPEMBlock, err := os.ReadFile(keyFile)
+ if err != nil {
+ return Certificate{}, err
+ }
+ return X509KeyPair(certPEMBlock, keyPEMBlock)
+}
+
+// X509KeyPair parses a public/private key pair from a pair of
+// PEM encoded data. On successful return, Certificate.Leaf will be nil because
+// the parsed form of the certificate is not retained.
+func X509KeyPair(certPEMBlock, keyPEMBlock []byte) (Certificate, error) {
+ fail := func(err error) (Certificate, error) { return Certificate{}, err }
+
+ var cert Certificate
+ var skippedBlockTypes []string
+ for {
+ var certDERBlock *pem.Block
+ certDERBlock, certPEMBlock = pem.Decode(certPEMBlock)
+ if certDERBlock == nil {
+ break
+ }
+ if certDERBlock.Type == "CERTIFICATE" {
+ cert.Certificate = append(cert.Certificate, certDERBlock.Bytes)
+ } else {
+ skippedBlockTypes = append(skippedBlockTypes, certDERBlock.Type)
+ }
+ }
+
+ if len(cert.Certificate) == 0 {
+ if len(skippedBlockTypes) == 0 {
+ return fail(errors.New("tls: failed to find any PEM data in certificate input"))
+ }
+ if len(skippedBlockTypes) == 1 && strings.HasSuffix(skippedBlockTypes[0], "PRIVATE KEY") {
+ return fail(errors.New("tls: failed to find certificate PEM data in certificate input, but did find a private key; PEM inputs may have been switched"))
+ }
+ return fail(fmt.Errorf("tls: failed to find \"CERTIFICATE\" PEM block in certificate input after skipping PEM blocks of the following types: %v", skippedBlockTypes))
+ }
+
+ skippedBlockTypes = skippedBlockTypes[:0]
+ var keyDERBlock *pem.Block
+ for {
+ keyDERBlock, keyPEMBlock = pem.Decode(keyPEMBlock)
+ if keyDERBlock == nil {
+ if len(skippedBlockTypes) == 0 {
+ return fail(errors.New("tls: failed to find any PEM data in key input"))
+ }
+ if len(skippedBlockTypes) == 1 && skippedBlockTypes[0] == "CERTIFICATE" {
+ return fail(errors.New("tls: found a certificate rather than a key in the PEM for the private key"))
+ }
+ return fail(fmt.Errorf("tls: failed to find PEM block with type ending in \"PRIVATE KEY\" in key input after skipping PEM blocks of the following types: %v", skippedBlockTypes))
+ }
+ if keyDERBlock.Type == "PRIVATE KEY" || strings.HasSuffix(keyDERBlock.Type, " PRIVATE KEY") {
+ break
+ }
+ skippedBlockTypes = append(skippedBlockTypes, keyDERBlock.Type)
+ }
+
+ // We don't need to parse the public key for TLS, but we so do anyway
+ // to check that it looks sane and matches the private key.
+ x509Cert, err := x509.ParseCertificate(cert.Certificate[0])
+ if err != nil {
+ return fail(err)
+ }
+
+ cert.PrivateKey, err = parsePrivateKey(keyDERBlock.Bytes)
+ if err != nil {
+ return fail(err)
+ }
+
+ switch pub := x509Cert.PublicKey.(type) {
+ case *rsa.PublicKey:
+ priv, ok := cert.PrivateKey.(*rsa.PrivateKey)
+ if !ok {
+ return fail(errors.New("tls: private key type does not match public key type"))
+ }
+ if pub.N.Cmp(priv.N) != 0 {
+ return fail(errors.New("tls: private key does not match public key"))
+ }
+ case *ecdsa.PublicKey:
+ priv, ok := cert.PrivateKey.(*ecdsa.PrivateKey)
+ if !ok {
+ return fail(errors.New("tls: private key type does not match public key type"))
+ }
+ if pub.X.Cmp(priv.X) != 0 || pub.Y.Cmp(priv.Y) != 0 {
+ return fail(errors.New("tls: private key does not match public key"))
+ }
+ case ed25519.PublicKey:
+ priv, ok := cert.PrivateKey.(ed25519.PrivateKey)
+ if !ok {
+ return fail(errors.New("tls: private key type does not match public key type"))
+ }
+ if !bytes.Equal(priv.Public().(ed25519.PublicKey), pub) {
+ return fail(errors.New("tls: private key does not match public key"))
+ }
+ default:
+ return fail(errors.New("tls: unknown public key algorithm"))
+ }
+
+ return cert, nil
+}
+
+// Attempt to parse the given private key DER block. OpenSSL 0.9.8 generates
+// PKCS #1 private keys by default, while OpenSSL 1.0.0 generates PKCS #8 keys.
+// OpenSSL ecparam generates SEC1 EC private keys for ECDSA. We try all three.
+func parsePrivateKey(der []byte) (crypto.PrivateKey, error) {
+ if key, err := x509.ParsePKCS1PrivateKey(der); err == nil {
+ return key, nil
+ }
+ if key, err := x509.ParsePKCS8PrivateKey(der); err == nil {
+ switch key := key.(type) {
+ case *rsa.PrivateKey, *ecdsa.PrivateKey, ed25519.PrivateKey:
+ return key, nil
+ default:
+ return nil, errors.New("tls: found unknown private key type in PKCS#8 wrapping")
+ }
+ }
+ if key, err := x509.ParseECPrivateKey(der); err == nil {
+ return key, nil
+ }
+
+ return nil, errors.New("tls: failed to parse private key")
+}
diff --git a/vendor/github.com/quic-go/qtls-go1-18/unsafe.go b/vendor/github.com/quic-go/qtls-go1-18/unsafe.go
new file mode 100644
index 0000000000..55fa01b3d6
--- /dev/null
+++ b/vendor/github.com/quic-go/qtls-go1-18/unsafe.go
@@ -0,0 +1,96 @@
+package qtls
+
+import (
+ "crypto/tls"
+ "reflect"
+ "unsafe"
+)
+
+func init() {
+ if !structsEqual(&tls.ConnectionState{}, &connectionState{}) {
+ panic("qtls.ConnectionState doesn't match")
+ }
+ if !structsEqual(&tls.ClientSessionState{}, &clientSessionState{}) {
+ panic("qtls.ClientSessionState doesn't match")
+ }
+ if !structsEqual(&tls.CertificateRequestInfo{}, &certificateRequestInfo{}) {
+ panic("qtls.CertificateRequestInfo doesn't match")
+ }
+ if !structsEqual(&tls.Config{}, &config{}) {
+ panic("qtls.Config doesn't match")
+ }
+ if !structsEqual(&tls.ClientHelloInfo{}, &clientHelloInfo{}) {
+ panic("qtls.ClientHelloInfo doesn't match")
+ }
+}
+
+func toConnectionState(c connectionState) ConnectionState {
+ return *(*ConnectionState)(unsafe.Pointer(&c))
+}
+
+func toClientSessionState(s *clientSessionState) *ClientSessionState {
+ return (*ClientSessionState)(unsafe.Pointer(s))
+}
+
+func fromClientSessionState(s *ClientSessionState) *clientSessionState {
+ return (*clientSessionState)(unsafe.Pointer(s))
+}
+
+func toCertificateRequestInfo(i *certificateRequestInfo) *CertificateRequestInfo {
+ return (*CertificateRequestInfo)(unsafe.Pointer(i))
+}
+
+func toConfig(c *config) *Config {
+ return (*Config)(unsafe.Pointer(c))
+}
+
+func fromConfig(c *Config) *config {
+ return (*config)(unsafe.Pointer(c))
+}
+
+func toClientHelloInfo(chi *clientHelloInfo) *ClientHelloInfo {
+ return (*ClientHelloInfo)(unsafe.Pointer(chi))
+}
+
+func structsEqual(a, b interface{}) bool {
+ return compare(reflect.ValueOf(a), reflect.ValueOf(b))
+}
+
+func compare(a, b reflect.Value) bool {
+ sa := a.Elem()
+ sb := b.Elem()
+ if sa.NumField() != sb.NumField() {
+ return false
+ }
+ for i := 0; i < sa.NumField(); i++ {
+ fa := sa.Type().Field(i)
+ fb := sb.Type().Field(i)
+ if !reflect.DeepEqual(fa.Index, fb.Index) || fa.Name != fb.Name || fa.Anonymous != fb.Anonymous || fa.Offset != fb.Offset || !reflect.DeepEqual(fa.Type, fb.Type) {
+ if fa.Type.Kind() != fb.Type.Kind() {
+ return false
+ }
+ if fa.Type.Kind() == reflect.Slice {
+ if !compareStruct(fa.Type.Elem(), fb.Type.Elem()) {
+ return false
+ }
+ continue
+ }
+ return false
+ }
+ }
+ return true
+}
+
+func compareStruct(a, b reflect.Type) bool {
+ if a.NumField() != b.NumField() {
+ return false
+ }
+ for i := 0; i < a.NumField(); i++ {
+ fa := a.Field(i)
+ fb := b.Field(i)
+ if !reflect.DeepEqual(fa.Index, fb.Index) || fa.Name != fb.Name || fa.Anonymous != fb.Anonymous || fa.Offset != fb.Offset || !reflect.DeepEqual(fa.Type, fb.Type) {
+ return false
+ }
+ }
+ return true
+}
diff --git a/vendor/github.com/quic-go/qtls-go1-19/LICENSE b/vendor/github.com/quic-go/qtls-go1-19/LICENSE
new file mode 100644
index 0000000000..6a66aea5ea
--- /dev/null
+++ b/vendor/github.com/quic-go/qtls-go1-19/LICENSE
@@ -0,0 +1,27 @@
+Copyright (c) 2009 The Go Authors. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with the
+distribution.
+ * Neither the name of Google Inc. nor the names of its
+contributors may be used to endorse or promote products derived from
+this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/vendor/github.com/quic-go/qtls-go1-19/README.md b/vendor/github.com/quic-go/qtls-go1-19/README.md
new file mode 100644
index 0000000000..bf41f1c5f1
--- /dev/null
+++ b/vendor/github.com/quic-go/qtls-go1-19/README.md
@@ -0,0 +1,6 @@
+# qtls
+
+[![Go Reference](https://pkg.go.dev/badge/github.com/quic-go/qtls-go1-19.svg)](https://pkg.go.dev/github.com/quic-go/qtls-go1-19)
+[![.github/workflows/go-test.yml](https://github.com/quic-go/qtls-go1-19/actions/workflows/go-test.yml/badge.svg)](https://github.com/quic-go/qtls-go1-19/actions/workflows/go-test.yml)
+
+This repository contains a modified version of the standard library's TLS implementation, modified for the QUIC protocol. It is used by [quic-go](https://github.com/lucas-clemente/quic-go).
diff --git a/vendor/github.com/quic-go/qtls-go1-19/alert.go b/vendor/github.com/quic-go/qtls-go1-19/alert.go
new file mode 100644
index 0000000000..3feac79be8
--- /dev/null
+++ b/vendor/github.com/quic-go/qtls-go1-19/alert.go
@@ -0,0 +1,102 @@
+// Copyright 2009 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package qtls
+
+import "strconv"
+
+type alert uint8
+
+// Alert is a TLS alert
+type Alert = alert
+
+const (
+ // alert level
+ alertLevelWarning = 1
+ alertLevelError = 2
+)
+
+const (
+ alertCloseNotify alert = 0
+ alertUnexpectedMessage alert = 10
+ alertBadRecordMAC alert = 20
+ alertDecryptionFailed alert = 21
+ alertRecordOverflow alert = 22
+ alertDecompressionFailure alert = 30
+ alertHandshakeFailure alert = 40
+ alertBadCertificate alert = 42
+ alertUnsupportedCertificate alert = 43
+ alertCertificateRevoked alert = 44
+ alertCertificateExpired alert = 45
+ alertCertificateUnknown alert = 46
+ alertIllegalParameter alert = 47
+ alertUnknownCA alert = 48
+ alertAccessDenied alert = 49
+ alertDecodeError alert = 50
+ alertDecryptError alert = 51
+ alertExportRestriction alert = 60
+ alertProtocolVersion alert = 70
+ alertInsufficientSecurity alert = 71
+ alertInternalError alert = 80
+ alertInappropriateFallback alert = 86
+ alertUserCanceled alert = 90
+ alertNoRenegotiation alert = 100
+ alertMissingExtension alert = 109
+ alertUnsupportedExtension alert = 110
+ alertCertificateUnobtainable alert = 111
+ alertUnrecognizedName alert = 112
+ alertBadCertificateStatusResponse alert = 113
+ alertBadCertificateHashValue alert = 114
+ alertUnknownPSKIdentity alert = 115
+ alertCertificateRequired alert = 116
+ alertNoApplicationProtocol alert = 120
+)
+
+var alertText = map[alert]string{
+ alertCloseNotify: "close notify",
+ alertUnexpectedMessage: "unexpected message",
+ alertBadRecordMAC: "bad record MAC",
+ alertDecryptionFailed: "decryption failed",
+ alertRecordOverflow: "record overflow",
+ alertDecompressionFailure: "decompression failure",
+ alertHandshakeFailure: "handshake failure",
+ alertBadCertificate: "bad certificate",
+ alertUnsupportedCertificate: "unsupported certificate",
+ alertCertificateRevoked: "revoked certificate",
+ alertCertificateExpired: "expired certificate",
+ alertCertificateUnknown: "unknown certificate",
+ alertIllegalParameter: "illegal parameter",
+ alertUnknownCA: "unknown certificate authority",
+ alertAccessDenied: "access denied",
+ alertDecodeError: "error decoding message",
+ alertDecryptError: "error decrypting message",
+ alertExportRestriction: "export restriction",
+ alertProtocolVersion: "protocol version not supported",
+ alertInsufficientSecurity: "insufficient security level",
+ alertInternalError: "internal error",
+ alertInappropriateFallback: "inappropriate fallback",
+ alertUserCanceled: "user canceled",
+ alertNoRenegotiation: "no renegotiation",
+ alertMissingExtension: "missing extension",
+ alertUnsupportedExtension: "unsupported extension",
+ alertCertificateUnobtainable: "certificate unobtainable",
+ alertUnrecognizedName: "unrecognized name",
+ alertBadCertificateStatusResponse: "bad certificate status response",
+ alertBadCertificateHashValue: "bad certificate hash value",
+ alertUnknownPSKIdentity: "unknown PSK identity",
+ alertCertificateRequired: "certificate required",
+ alertNoApplicationProtocol: "no application protocol",
+}
+
+func (e alert) String() string {
+ s, ok := alertText[e]
+ if ok {
+ return "tls: " + s
+ }
+ return "tls: alert(" + strconv.Itoa(int(e)) + ")"
+}
+
+func (e alert) Error() string {
+ return e.String()
+}
diff --git a/vendor/github.com/quic-go/qtls-go1-19/auth.go b/vendor/github.com/quic-go/qtls-go1-19/auth.go
new file mode 100644
index 0000000000..effc9aced8
--- /dev/null
+++ b/vendor/github.com/quic-go/qtls-go1-19/auth.go
@@ -0,0 +1,293 @@
+// Copyright 2017 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package qtls
+
+import (
+ "bytes"
+ "crypto"
+ "crypto/ecdsa"
+ "crypto/ed25519"
+ "crypto/elliptic"
+ "crypto/rsa"
+ "errors"
+ "fmt"
+ "hash"
+ "io"
+)
+
+// verifyHandshakeSignature verifies a signature against pre-hashed
+// (if required) handshake contents.
+func verifyHandshakeSignature(sigType uint8, pubkey crypto.PublicKey, hashFunc crypto.Hash, signed, sig []byte) error {
+ switch sigType {
+ case signatureECDSA:
+ pubKey, ok := pubkey.(*ecdsa.PublicKey)
+ if !ok {
+ return fmt.Errorf("expected an ECDSA public key, got %T", pubkey)
+ }
+ if !ecdsa.VerifyASN1(pubKey, signed, sig) {
+ return errors.New("ECDSA verification failure")
+ }
+ case signatureEd25519:
+ pubKey, ok := pubkey.(ed25519.PublicKey)
+ if !ok {
+ return fmt.Errorf("expected an Ed25519 public key, got %T", pubkey)
+ }
+ if !ed25519.Verify(pubKey, signed, sig) {
+ return errors.New("Ed25519 verification failure")
+ }
+ case signaturePKCS1v15:
+ pubKey, ok := pubkey.(*rsa.PublicKey)
+ if !ok {
+ return fmt.Errorf("expected an RSA public key, got %T", pubkey)
+ }
+ if err := rsa.VerifyPKCS1v15(pubKey, hashFunc, signed, sig); err != nil {
+ return err
+ }
+ case signatureRSAPSS:
+ pubKey, ok := pubkey.(*rsa.PublicKey)
+ if !ok {
+ return fmt.Errorf("expected an RSA public key, got %T", pubkey)
+ }
+ signOpts := &rsa.PSSOptions{SaltLength: rsa.PSSSaltLengthEqualsHash}
+ if err := rsa.VerifyPSS(pubKey, hashFunc, signed, sig, signOpts); err != nil {
+ return err
+ }
+ default:
+ return errors.New("internal error: unknown signature type")
+ }
+ return nil
+}
+
+const (
+ serverSignatureContext = "TLS 1.3, server CertificateVerify\x00"
+ clientSignatureContext = "TLS 1.3, client CertificateVerify\x00"
+)
+
+var signaturePadding = []byte{
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+}
+
+// signedMessage returns the pre-hashed (if necessary) message to be signed by
+// certificate keys in TLS 1.3. See RFC 8446, Section 4.4.3.
+func signedMessage(sigHash crypto.Hash, context string, transcript hash.Hash) []byte {
+ if sigHash == directSigning {
+ b := &bytes.Buffer{}
+ b.Write(signaturePadding)
+ io.WriteString(b, context)
+ b.Write(transcript.Sum(nil))
+ return b.Bytes()
+ }
+ h := sigHash.New()
+ h.Write(signaturePadding)
+ io.WriteString(h, context)
+ h.Write(transcript.Sum(nil))
+ return h.Sum(nil)
+}
+
+// typeAndHashFromSignatureScheme returns the corresponding signature type and
+// crypto.Hash for a given TLS SignatureScheme.
+func typeAndHashFromSignatureScheme(signatureAlgorithm SignatureScheme) (sigType uint8, hash crypto.Hash, err error) {
+ switch signatureAlgorithm {
+ case PKCS1WithSHA1, PKCS1WithSHA256, PKCS1WithSHA384, PKCS1WithSHA512:
+ sigType = signaturePKCS1v15
+ case PSSWithSHA256, PSSWithSHA384, PSSWithSHA512:
+ sigType = signatureRSAPSS
+ case ECDSAWithSHA1, ECDSAWithP256AndSHA256, ECDSAWithP384AndSHA384, ECDSAWithP521AndSHA512:
+ sigType = signatureECDSA
+ case Ed25519:
+ sigType = signatureEd25519
+ default:
+ return 0, 0, fmt.Errorf("unsupported signature algorithm: %v", signatureAlgorithm)
+ }
+ switch signatureAlgorithm {
+ case PKCS1WithSHA1, ECDSAWithSHA1:
+ hash = crypto.SHA1
+ case PKCS1WithSHA256, PSSWithSHA256, ECDSAWithP256AndSHA256:
+ hash = crypto.SHA256
+ case PKCS1WithSHA384, PSSWithSHA384, ECDSAWithP384AndSHA384:
+ hash = crypto.SHA384
+ case PKCS1WithSHA512, PSSWithSHA512, ECDSAWithP521AndSHA512:
+ hash = crypto.SHA512
+ case Ed25519:
+ hash = directSigning
+ default:
+ return 0, 0, fmt.Errorf("unsupported signature algorithm: %v", signatureAlgorithm)
+ }
+ return sigType, hash, nil
+}
+
+// legacyTypeAndHashFromPublicKey returns the fixed signature type and crypto.Hash for
+// a given public key used with TLS 1.0 and 1.1, before the introduction of
+// signature algorithm negotiation.
+func legacyTypeAndHashFromPublicKey(pub crypto.PublicKey) (sigType uint8, hash crypto.Hash, err error) {
+ switch pub.(type) {
+ case *rsa.PublicKey:
+ return signaturePKCS1v15, crypto.MD5SHA1, nil
+ case *ecdsa.PublicKey:
+ return signatureECDSA, crypto.SHA1, nil
+ case ed25519.PublicKey:
+ // RFC 8422 specifies support for Ed25519 in TLS 1.0 and 1.1,
+ // but it requires holding on to a handshake transcript to do a
+ // full signature, and not even OpenSSL bothers with the
+ // complexity, so we can't even test it properly.
+ return 0, 0, fmt.Errorf("tls: Ed25519 public keys are not supported before TLS 1.2")
+ default:
+ return 0, 0, fmt.Errorf("tls: unsupported public key: %T", pub)
+ }
+}
+
+var rsaSignatureSchemes = []struct {
+ scheme SignatureScheme
+ minModulusBytes int
+ maxVersion uint16
+}{
+ // RSA-PSS is used with PSSSaltLengthEqualsHash, and requires
+ // emLen >= hLen + sLen + 2
+ {PSSWithSHA256, crypto.SHA256.Size()*2 + 2, VersionTLS13},
+ {PSSWithSHA384, crypto.SHA384.Size()*2 + 2, VersionTLS13},
+ {PSSWithSHA512, crypto.SHA512.Size()*2 + 2, VersionTLS13},
+ // PKCS #1 v1.5 uses prefixes from hashPrefixes in crypto/rsa, and requires
+ // emLen >= len(prefix) + hLen + 11
+ // TLS 1.3 dropped support for PKCS #1 v1.5 in favor of RSA-PSS.
+ {PKCS1WithSHA256, 19 + crypto.SHA256.Size() + 11, VersionTLS12},
+ {PKCS1WithSHA384, 19 + crypto.SHA384.Size() + 11, VersionTLS12},
+ {PKCS1WithSHA512, 19 + crypto.SHA512.Size() + 11, VersionTLS12},
+ {PKCS1WithSHA1, 15 + crypto.SHA1.Size() + 11, VersionTLS12},
+}
+
+// signatureSchemesForCertificate returns the list of supported SignatureSchemes
+// for a given certificate, based on the public key and the protocol version,
+// and optionally filtered by its explicit SupportedSignatureAlgorithms.
+//
+// This function must be kept in sync with supportedSignatureAlgorithms.
+// FIPS filtering is applied in the caller, selectSignatureScheme.
+func signatureSchemesForCertificate(version uint16, cert *Certificate) []SignatureScheme {
+ priv, ok := cert.PrivateKey.(crypto.Signer)
+ if !ok {
+ return nil
+ }
+
+ var sigAlgs []SignatureScheme
+ switch pub := priv.Public().(type) {
+ case *ecdsa.PublicKey:
+ if version != VersionTLS13 {
+ // In TLS 1.2 and earlier, ECDSA algorithms are not
+ // constrained to a single curve.
+ sigAlgs = []SignatureScheme{
+ ECDSAWithP256AndSHA256,
+ ECDSAWithP384AndSHA384,
+ ECDSAWithP521AndSHA512,
+ ECDSAWithSHA1,
+ }
+ break
+ }
+ switch pub.Curve {
+ case elliptic.P256():
+ sigAlgs = []SignatureScheme{ECDSAWithP256AndSHA256}
+ case elliptic.P384():
+ sigAlgs = []SignatureScheme{ECDSAWithP384AndSHA384}
+ case elliptic.P521():
+ sigAlgs = []SignatureScheme{ECDSAWithP521AndSHA512}
+ default:
+ return nil
+ }
+ case *rsa.PublicKey:
+ size := pub.Size()
+ sigAlgs = make([]SignatureScheme, 0, len(rsaSignatureSchemes))
+ for _, candidate := range rsaSignatureSchemes {
+ if size >= candidate.minModulusBytes && version <= candidate.maxVersion {
+ sigAlgs = append(sigAlgs, candidate.scheme)
+ }
+ }
+ case ed25519.PublicKey:
+ sigAlgs = []SignatureScheme{Ed25519}
+ default:
+ return nil
+ }
+
+ if cert.SupportedSignatureAlgorithms != nil {
+ var filteredSigAlgs []SignatureScheme
+ for _, sigAlg := range sigAlgs {
+ if isSupportedSignatureAlgorithm(sigAlg, cert.SupportedSignatureAlgorithms) {
+ filteredSigAlgs = append(filteredSigAlgs, sigAlg)
+ }
+ }
+ return filteredSigAlgs
+ }
+ return sigAlgs
+}
+
+// selectSignatureScheme picks a SignatureScheme from the peer's preference list
+// that works with the selected certificate. It's only called for protocol
+// versions that support signature algorithms, so TLS 1.2 and 1.3.
+func selectSignatureScheme(vers uint16, c *Certificate, peerAlgs []SignatureScheme) (SignatureScheme, error) {
+ supportedAlgs := signatureSchemesForCertificate(vers, c)
+ if len(supportedAlgs) == 0 {
+ return 0, unsupportedCertificateError(c)
+ }
+ if len(peerAlgs) == 0 && vers == VersionTLS12 {
+ // For TLS 1.2, if the client didn't send signature_algorithms then we
+ // can assume that it supports SHA1. See RFC 5246, Section 7.4.1.4.1.
+ peerAlgs = []SignatureScheme{PKCS1WithSHA1, ECDSAWithSHA1}
+ }
+ // Pick signature scheme in the peer's preference order, as our
+ // preference order is not configurable.
+ for _, preferredAlg := range peerAlgs {
+ if needFIPS() && !isSupportedSignatureAlgorithm(preferredAlg, fipsSupportedSignatureAlgorithms) {
+ continue
+ }
+ if isSupportedSignatureAlgorithm(preferredAlg, supportedAlgs) {
+ return preferredAlg, nil
+ }
+ }
+ return 0, errors.New("tls: peer doesn't support any of the certificate's signature algorithms")
+}
+
+// unsupportedCertificateError returns a helpful error for certificates with
+// an unsupported private key.
+func unsupportedCertificateError(cert *Certificate) error {
+ switch cert.PrivateKey.(type) {
+ case rsa.PrivateKey, ecdsa.PrivateKey:
+ return fmt.Errorf("tls: unsupported certificate: private key is %T, expected *%T",
+ cert.PrivateKey, cert.PrivateKey)
+ case *ed25519.PrivateKey:
+ return fmt.Errorf("tls: unsupported certificate: private key is *ed25519.PrivateKey, expected ed25519.PrivateKey")
+ }
+
+ signer, ok := cert.PrivateKey.(crypto.Signer)
+ if !ok {
+ return fmt.Errorf("tls: certificate private key (%T) does not implement crypto.Signer",
+ cert.PrivateKey)
+ }
+
+ switch pub := signer.Public().(type) {
+ case *ecdsa.PublicKey:
+ switch pub.Curve {
+ case elliptic.P256():
+ case elliptic.P384():
+ case elliptic.P521():
+ default:
+ return fmt.Errorf("tls: unsupported certificate curve (%s)", pub.Curve.Params().Name)
+ }
+ case *rsa.PublicKey:
+ return fmt.Errorf("tls: certificate RSA key size too small for supported signature algorithms")
+ case ed25519.PublicKey:
+ default:
+ return fmt.Errorf("tls: unsupported certificate key (%T)", pub)
+ }
+
+ if cert.SupportedSignatureAlgorithms != nil {
+ return fmt.Errorf("tls: peer doesn't support the certificate custom signature algorithms")
+ }
+
+ return fmt.Errorf("tls: internal error: unsupported key (%T)", cert.PrivateKey)
+}
diff --git a/vendor/github.com/quic-go/qtls-go1-19/cipher_suites.go b/vendor/github.com/quic-go/qtls-go1-19/cipher_suites.go
new file mode 100644
index 0000000000..56dd454360
--- /dev/null
+++ b/vendor/github.com/quic-go/qtls-go1-19/cipher_suites.go
@@ -0,0 +1,693 @@
+// Copyright 2010 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package qtls
+
+import (
+ "crypto"
+ "crypto/aes"
+ "crypto/cipher"
+ "crypto/des"
+ "crypto/hmac"
+ "crypto/rc4"
+ "crypto/sha1"
+ "crypto/sha256"
+ "fmt"
+ "hash"
+
+ "golang.org/x/crypto/chacha20poly1305"
+)
+
+// CipherSuite is a TLS cipher suite. Note that most functions in this package
+// accept and expose cipher suite IDs instead of this type.
+type CipherSuite struct {
+ ID uint16
+ Name string
+
+ // Supported versions is the list of TLS protocol versions that can
+ // negotiate this cipher suite.
+ SupportedVersions []uint16
+
+ // Insecure is true if the cipher suite has known security issues
+ // due to its primitives, design, or implementation.
+ Insecure bool
+}
+
+var (
+ supportedUpToTLS12 = []uint16{VersionTLS10, VersionTLS11, VersionTLS12}
+ supportedOnlyTLS12 = []uint16{VersionTLS12}
+ supportedOnlyTLS13 = []uint16{VersionTLS13}
+)
+
+// CipherSuites returns a list of cipher suites currently implemented by this
+// package, excluding those with security issues, which are returned by
+// InsecureCipherSuites.
+//
+// The list is sorted by ID. Note that the default cipher suites selected by
+// this package might depend on logic that can't be captured by a static list,
+// and might not match those returned by this function.
+func CipherSuites() []*CipherSuite {
+ return []*CipherSuite{
+ {TLS_RSA_WITH_AES_128_CBC_SHA, "TLS_RSA_WITH_AES_128_CBC_SHA", supportedUpToTLS12, false},
+ {TLS_RSA_WITH_AES_256_CBC_SHA, "TLS_RSA_WITH_AES_256_CBC_SHA", supportedUpToTLS12, false},
+ {TLS_RSA_WITH_AES_128_GCM_SHA256, "TLS_RSA_WITH_AES_128_GCM_SHA256", supportedOnlyTLS12, false},
+ {TLS_RSA_WITH_AES_256_GCM_SHA384, "TLS_RSA_WITH_AES_256_GCM_SHA384", supportedOnlyTLS12, false},
+
+ {TLS_AES_128_GCM_SHA256, "TLS_AES_128_GCM_SHA256", supportedOnlyTLS13, false},
+ {TLS_AES_256_GCM_SHA384, "TLS_AES_256_GCM_SHA384", supportedOnlyTLS13, false},
+ {TLS_CHACHA20_POLY1305_SHA256, "TLS_CHACHA20_POLY1305_SHA256", supportedOnlyTLS13, false},
+
+ {TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA", supportedUpToTLS12, false},
+ {TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA", supportedUpToTLS12, false},
+ {TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA", supportedUpToTLS12, false},
+ {TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA", supportedUpToTLS12, false},
+ {TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256", supportedOnlyTLS12, false},
+ {TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384", supportedOnlyTLS12, false},
+ {TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256", supportedOnlyTLS12, false},
+ {TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384", supportedOnlyTLS12, false},
+ {TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256", supportedOnlyTLS12, false},
+ {TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256", supportedOnlyTLS12, false},
+ }
+}
+
+// InsecureCipherSuites returns a list of cipher suites currently implemented by
+// this package and which have security issues.
+//
+// Most applications should not use the cipher suites in this list, and should
+// only use those returned by CipherSuites.
+func InsecureCipherSuites() []*CipherSuite {
+ // This list includes RC4, CBC_SHA256, and 3DES cipher suites. See
+ // cipherSuitesPreferenceOrder for details.
+ return []*CipherSuite{
+ {TLS_RSA_WITH_RC4_128_SHA, "TLS_RSA_WITH_RC4_128_SHA", supportedUpToTLS12, true},
+ {TLS_RSA_WITH_3DES_EDE_CBC_SHA, "TLS_RSA_WITH_3DES_EDE_CBC_SHA", supportedUpToTLS12, true},
+ {TLS_RSA_WITH_AES_128_CBC_SHA256, "TLS_RSA_WITH_AES_128_CBC_SHA256", supportedOnlyTLS12, true},
+ {TLS_ECDHE_ECDSA_WITH_RC4_128_SHA, "TLS_ECDHE_ECDSA_WITH_RC4_128_SHA", supportedUpToTLS12, true},
+ {TLS_ECDHE_RSA_WITH_RC4_128_SHA, "TLS_ECDHE_RSA_WITH_RC4_128_SHA", supportedUpToTLS12, true},
+ {TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA, "TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA", supportedUpToTLS12, true},
+ {TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256, "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256", supportedOnlyTLS12, true},
+ {TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256, "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256", supportedOnlyTLS12, true},
+ }
+}
+
+// CipherSuiteName returns the standard name for the passed cipher suite ID
+// (e.g. "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256"), or a fallback representation
+// of the ID value if the cipher suite is not implemented by this package.
+func CipherSuiteName(id uint16) string {
+ for _, c := range CipherSuites() {
+ if c.ID == id {
+ return c.Name
+ }
+ }
+ for _, c := range InsecureCipherSuites() {
+ if c.ID == id {
+ return c.Name
+ }
+ }
+ return fmt.Sprintf("0x%04X", id)
+}
+
+const (
+ // suiteECDHE indicates that the cipher suite involves elliptic curve
+ // Diffie-Hellman. This means that it should only be selected when the
+ // client indicates that it supports ECC with a curve and point format
+ // that we're happy with.
+ suiteECDHE = 1 << iota
+ // suiteECSign indicates that the cipher suite involves an ECDSA or
+ // EdDSA signature and therefore may only be selected when the server's
+ // certificate is ECDSA or EdDSA. If this is not set then the cipher suite
+ // is RSA based.
+ suiteECSign
+ // suiteTLS12 indicates that the cipher suite should only be advertised
+ // and accepted when using TLS 1.2.
+ suiteTLS12
+ // suiteSHA384 indicates that the cipher suite uses SHA384 as the
+ // handshake hash.
+ suiteSHA384
+)
+
+// A cipherSuite is a TLS 1.0–1.2 cipher suite, and defines the key exchange
+// mechanism, as well as the cipher+MAC pair or the AEAD.
+type cipherSuite struct {
+ id uint16
+ // the lengths, in bytes, of the key material needed for each component.
+ keyLen int
+ macLen int
+ ivLen int
+ ka func(version uint16) keyAgreement
+ // flags is a bitmask of the suite* values, above.
+ flags int
+ cipher func(key, iv []byte, isRead bool) any
+ mac func(key []byte) hash.Hash
+ aead func(key, fixedNonce []byte) aead
+}
+
+var cipherSuites = []*cipherSuite{ // TODO: replace with a map, since the order doesn't matter.
+ {TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305, 32, 0, 12, ecdheRSAKA, suiteECDHE | suiteTLS12, nil, nil, aeadChaCha20Poly1305},
+ {TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305, 32, 0, 12, ecdheECDSAKA, suiteECDHE | suiteECSign | suiteTLS12, nil, nil, aeadChaCha20Poly1305},
+ {TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, 16, 0, 4, ecdheRSAKA, suiteECDHE | suiteTLS12, nil, nil, aeadAESGCM},
+ {TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, 16, 0, 4, ecdheECDSAKA, suiteECDHE | suiteECSign | suiteTLS12, nil, nil, aeadAESGCM},
+ {TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, 32, 0, 4, ecdheRSAKA, suiteECDHE | suiteTLS12 | suiteSHA384, nil, nil, aeadAESGCM},
+ {TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, 32, 0, 4, ecdheECDSAKA, suiteECDHE | suiteECSign | suiteTLS12 | suiteSHA384, nil, nil, aeadAESGCM},
+ {TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256, 16, 32, 16, ecdheRSAKA, suiteECDHE | suiteTLS12, cipherAES, macSHA256, nil},
+ {TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, 16, 20, 16, ecdheRSAKA, suiteECDHE, cipherAES, macSHA1, nil},
+ {TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256, 16, 32, 16, ecdheECDSAKA, suiteECDHE | suiteECSign | suiteTLS12, cipherAES, macSHA256, nil},
+ {TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, 16, 20, 16, ecdheECDSAKA, suiteECDHE | suiteECSign, cipherAES, macSHA1, nil},
+ {TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, 32, 20, 16, ecdheRSAKA, suiteECDHE, cipherAES, macSHA1, nil},
+ {TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, 32, 20, 16, ecdheECDSAKA, suiteECDHE | suiteECSign, cipherAES, macSHA1, nil},
+ {TLS_RSA_WITH_AES_128_GCM_SHA256, 16, 0, 4, rsaKA, suiteTLS12, nil, nil, aeadAESGCM},
+ {TLS_RSA_WITH_AES_256_GCM_SHA384, 32, 0, 4, rsaKA, suiteTLS12 | suiteSHA384, nil, nil, aeadAESGCM},
+ {TLS_RSA_WITH_AES_128_CBC_SHA256, 16, 32, 16, rsaKA, suiteTLS12, cipherAES, macSHA256, nil},
+ {TLS_RSA_WITH_AES_128_CBC_SHA, 16, 20, 16, rsaKA, 0, cipherAES, macSHA1, nil},
+ {TLS_RSA_WITH_AES_256_CBC_SHA, 32, 20, 16, rsaKA, 0, cipherAES, macSHA1, nil},
+ {TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA, 24, 20, 8, ecdheRSAKA, suiteECDHE, cipher3DES, macSHA1, nil},
+ {TLS_RSA_WITH_3DES_EDE_CBC_SHA, 24, 20, 8, rsaKA, 0, cipher3DES, macSHA1, nil},
+ {TLS_RSA_WITH_RC4_128_SHA, 16, 20, 0, rsaKA, 0, cipherRC4, macSHA1, nil},
+ {TLS_ECDHE_RSA_WITH_RC4_128_SHA, 16, 20, 0, ecdheRSAKA, suiteECDHE, cipherRC4, macSHA1, nil},
+ {TLS_ECDHE_ECDSA_WITH_RC4_128_SHA, 16, 20, 0, ecdheECDSAKA, suiteECDHE | suiteECSign, cipherRC4, macSHA1, nil},
+}
+
+// selectCipherSuite returns the first TLS 1.0–1.2 cipher suite from ids which
+// is also in supportedIDs and passes the ok filter.
+func selectCipherSuite(ids, supportedIDs []uint16, ok func(*cipherSuite) bool) *cipherSuite {
+ for _, id := range ids {
+ candidate := cipherSuiteByID(id)
+ if candidate == nil || !ok(candidate) {
+ continue
+ }
+
+ for _, suppID := range supportedIDs {
+ if id == suppID {
+ return candidate
+ }
+ }
+ }
+ return nil
+}
+
+// A cipherSuiteTLS13 defines only the pair of the AEAD algorithm and hash
+// algorithm to be used with HKDF. See RFC 8446, Appendix B.4.
+type cipherSuiteTLS13 struct {
+ id uint16
+ keyLen int
+ aead func(key, fixedNonce []byte) aead
+ hash crypto.Hash
+}
+
+type CipherSuiteTLS13 struct {
+ ID uint16
+ KeyLen int
+ Hash crypto.Hash
+ AEAD func(key, fixedNonce []byte) cipher.AEAD
+}
+
+func (c *CipherSuiteTLS13) IVLen() int {
+ return aeadNonceLength
+}
+
+var cipherSuitesTLS13 = []*cipherSuiteTLS13{ // TODO: replace with a map.
+ {TLS_AES_128_GCM_SHA256, 16, aeadAESGCMTLS13, crypto.SHA256},
+ {TLS_CHACHA20_POLY1305_SHA256, 32, aeadChaCha20Poly1305, crypto.SHA256},
+ {TLS_AES_256_GCM_SHA384, 32, aeadAESGCMTLS13, crypto.SHA384},
+}
+
+// cipherSuitesPreferenceOrder is the order in which we'll select (on the
+// server) or advertise (on the client) TLS 1.0–1.2 cipher suites.
+//
+// Cipher suites are filtered but not reordered based on the application and
+// peer's preferences, meaning we'll never select a suite lower in this list if
+// any higher one is available. This makes it more defensible to keep weaker
+// cipher suites enabled, especially on the server side where we get the last
+// word, since there are no known downgrade attacks on cipher suites selection.
+//
+// The list is sorted by applying the following priority rules, stopping at the
+// first (most important) applicable one:
+//
+// - Anything else comes before RC4
+//
+// RC4 has practically exploitable biases. See https://www.rc4nomore.com.
+//
+// - Anything else comes before CBC_SHA256
+//
+// SHA-256 variants of the CBC ciphersuites don't implement any Lucky13
+// countermeasures. See http://www.isg.rhul.ac.uk/tls/Lucky13.html and
+// https://www.imperialviolet.org/2013/02/04/luckythirteen.html.
+//
+// - Anything else comes before 3DES
+//
+// 3DES has 64-bit blocks, which makes it fundamentally susceptible to
+// birthday attacks. See https://sweet32.info.
+//
+// - ECDHE comes before anything else
+//
+// Once we got the broken stuff out of the way, the most important
+// property a cipher suite can have is forward secrecy. We don't
+// implement FFDHE, so that means ECDHE.
+//
+// - AEADs come before CBC ciphers
+//
+// Even with Lucky13 countermeasures, MAC-then-Encrypt CBC cipher suites
+// are fundamentally fragile, and suffered from an endless sequence of
+// padding oracle attacks. See https://eprint.iacr.org/2015/1129,
+// https://www.imperialviolet.org/2014/12/08/poodleagain.html, and
+// https://blog.cloudflare.com/yet-another-padding-oracle-in-openssl-cbc-ciphersuites/.
+//
+// - AES comes before ChaCha20
+//
+// When AES hardware is available, AES-128-GCM and AES-256-GCM are faster
+// than ChaCha20Poly1305.
+//
+// When AES hardware is not available, AES-128-GCM is one or more of: much
+// slower, way more complex, and less safe (because not constant time)
+// than ChaCha20Poly1305.
+//
+// We use this list if we think both peers have AES hardware, and
+// cipherSuitesPreferenceOrderNoAES otherwise.
+//
+// - AES-128 comes before AES-256
+//
+// The only potential advantages of AES-256 are better multi-target
+// margins, and hypothetical post-quantum properties. Neither apply to
+// TLS, and AES-256 is slower due to its four extra rounds (which don't
+// contribute to the advantages above).
+//
+// - ECDSA comes before RSA
+//
+// The relative order of ECDSA and RSA cipher suites doesn't matter,
+// as they depend on the certificate. Pick one to get a stable order.
+var cipherSuitesPreferenceOrder = []uint16{
+ // AEADs w/ ECDHE
+ TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
+ TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
+ TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305, TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305,
+
+ // CBC w/ ECDHE
+ TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
+ TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
+
+ // AEADs w/o ECDHE
+ TLS_RSA_WITH_AES_128_GCM_SHA256,
+ TLS_RSA_WITH_AES_256_GCM_SHA384,
+
+ // CBC w/o ECDHE
+ TLS_RSA_WITH_AES_128_CBC_SHA,
+ TLS_RSA_WITH_AES_256_CBC_SHA,
+
+ // 3DES
+ TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA,
+ TLS_RSA_WITH_3DES_EDE_CBC_SHA,
+
+ // CBC_SHA256
+ TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256, TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256,
+ TLS_RSA_WITH_AES_128_CBC_SHA256,
+
+ // RC4
+ TLS_ECDHE_ECDSA_WITH_RC4_128_SHA, TLS_ECDHE_RSA_WITH_RC4_128_SHA,
+ TLS_RSA_WITH_RC4_128_SHA,
+}
+
+var cipherSuitesPreferenceOrderNoAES = []uint16{
+ // ChaCha20Poly1305
+ TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305, TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305,
+
+ // AES-GCM w/ ECDHE
+ TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
+ TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
+
+ // The rest of cipherSuitesPreferenceOrder.
+ TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
+ TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
+ TLS_RSA_WITH_AES_128_GCM_SHA256,
+ TLS_RSA_WITH_AES_256_GCM_SHA384,
+ TLS_RSA_WITH_AES_128_CBC_SHA,
+ TLS_RSA_WITH_AES_256_CBC_SHA,
+ TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA,
+ TLS_RSA_WITH_3DES_EDE_CBC_SHA,
+ TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256, TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256,
+ TLS_RSA_WITH_AES_128_CBC_SHA256,
+ TLS_ECDHE_ECDSA_WITH_RC4_128_SHA, TLS_ECDHE_RSA_WITH_RC4_128_SHA,
+ TLS_RSA_WITH_RC4_128_SHA,
+}
+
+// disabledCipherSuites are not used unless explicitly listed in
+// Config.CipherSuites. They MUST be at the end of cipherSuitesPreferenceOrder.
+var disabledCipherSuites = []uint16{
+ // CBC_SHA256
+ TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256, TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256,
+ TLS_RSA_WITH_AES_128_CBC_SHA256,
+
+ // RC4
+ TLS_ECDHE_ECDSA_WITH_RC4_128_SHA, TLS_ECDHE_RSA_WITH_RC4_128_SHA,
+ TLS_RSA_WITH_RC4_128_SHA,
+}
+
+var (
+ defaultCipherSuitesLen = len(cipherSuitesPreferenceOrder) - len(disabledCipherSuites)
+ defaultCipherSuites = cipherSuitesPreferenceOrder[:defaultCipherSuitesLen]
+)
+
+// defaultCipherSuitesTLS13 is also the preference order, since there are no
+// disabled by default TLS 1.3 cipher suites. The same AES vs ChaCha20 logic as
+// cipherSuitesPreferenceOrder applies.
+var defaultCipherSuitesTLS13 = []uint16{
+ TLS_AES_128_GCM_SHA256,
+ TLS_AES_256_GCM_SHA384,
+ TLS_CHACHA20_POLY1305_SHA256,
+}
+
+var defaultCipherSuitesTLS13NoAES = []uint16{
+ TLS_CHACHA20_POLY1305_SHA256,
+ TLS_AES_128_GCM_SHA256,
+ TLS_AES_256_GCM_SHA384,
+}
+
+var aesgcmCiphers = map[uint16]bool{
+ // TLS 1.2
+ TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256: true,
+ TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384: true,
+ TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256: true,
+ TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384: true,
+ // TLS 1.3
+ TLS_AES_128_GCM_SHA256: true,
+ TLS_AES_256_GCM_SHA384: true,
+}
+
+var nonAESGCMAEADCiphers = map[uint16]bool{
+ // TLS 1.2
+ TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305: true,
+ TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305: true,
+ // TLS 1.3
+ TLS_CHACHA20_POLY1305_SHA256: true,
+}
+
+// aesgcmPreferred returns whether the first known cipher in the preference list
+// is an AES-GCM cipher, implying the peer has hardware support for it.
+func aesgcmPreferred(ciphers []uint16) bool {
+ for _, cID := range ciphers {
+ if c := cipherSuiteByID(cID); c != nil {
+ return aesgcmCiphers[cID]
+ }
+ if c := cipherSuiteTLS13ByID(cID); c != nil {
+ return aesgcmCiphers[cID]
+ }
+ }
+ return false
+}
+
+func cipherRC4(key, iv []byte, isRead bool) any {
+ cipher, _ := rc4.NewCipher(key)
+ return cipher
+}
+
+func cipher3DES(key, iv []byte, isRead bool) any {
+ block, _ := des.NewTripleDESCipher(key)
+ if isRead {
+ return cipher.NewCBCDecrypter(block, iv)
+ }
+ return cipher.NewCBCEncrypter(block, iv)
+}
+
+func cipherAES(key, iv []byte, isRead bool) any {
+ block, _ := aes.NewCipher(key)
+ if isRead {
+ return cipher.NewCBCDecrypter(block, iv)
+ }
+ return cipher.NewCBCEncrypter(block, iv)
+}
+
+// macSHA1 returns a SHA-1 based constant time MAC.
+func macSHA1(key []byte) hash.Hash {
+ h := sha1.New
+ h = newConstantTimeHash(h)
+ return hmac.New(h, key)
+}
+
+// macSHA256 returns a SHA-256 based MAC. This is only supported in TLS 1.2 and
+// is currently only used in disabled-by-default cipher suites.
+func macSHA256(key []byte) hash.Hash {
+ return hmac.New(sha256.New, key)
+}
+
+type aead interface {
+ cipher.AEAD
+
+ // explicitNonceLen returns the number of bytes of explicit nonce
+ // included in each record. This is eight for older AEADs and
+ // zero for modern ones.
+ explicitNonceLen() int
+}
+
+const (
+ aeadNonceLength = 12
+ noncePrefixLength = 4
+)
+
+// prefixNonceAEAD wraps an AEAD and prefixes a fixed portion of the nonce to
+// each call.
+type prefixNonceAEAD struct {
+ // nonce contains the fixed part of the nonce in the first four bytes.
+ nonce [aeadNonceLength]byte
+ aead cipher.AEAD
+}
+
+func (f *prefixNonceAEAD) NonceSize() int { return aeadNonceLength - noncePrefixLength }
+func (f *prefixNonceAEAD) Overhead() int { return f.aead.Overhead() }
+func (f *prefixNonceAEAD) explicitNonceLen() int { return f.NonceSize() }
+
+func (f *prefixNonceAEAD) Seal(out, nonce, plaintext, additionalData []byte) []byte {
+ copy(f.nonce[4:], nonce)
+ return f.aead.Seal(out, f.nonce[:], plaintext, additionalData)
+}
+
+func (f *prefixNonceAEAD) Open(out, nonce, ciphertext, additionalData []byte) ([]byte, error) {
+ copy(f.nonce[4:], nonce)
+ return f.aead.Open(out, f.nonce[:], ciphertext, additionalData)
+}
+
+// xoredNonceAEAD wraps an AEAD by XORing in a fixed pattern to the nonce
+// before each call.
+type xorNonceAEAD struct {
+ nonceMask [aeadNonceLength]byte
+ aead cipher.AEAD
+}
+
+func (f *xorNonceAEAD) NonceSize() int { return 8 } // 64-bit sequence number
+func (f *xorNonceAEAD) Overhead() int { return f.aead.Overhead() }
+func (f *xorNonceAEAD) explicitNonceLen() int { return 0 }
+
+func (f *xorNonceAEAD) Seal(out, nonce, plaintext, additionalData []byte) []byte {
+ for i, b := range nonce {
+ f.nonceMask[4+i] ^= b
+ }
+ result := f.aead.Seal(out, f.nonceMask[:], plaintext, additionalData)
+ for i, b := range nonce {
+ f.nonceMask[4+i] ^= b
+ }
+
+ return result
+}
+
+func (f *xorNonceAEAD) Open(out, nonce, ciphertext, additionalData []byte) ([]byte, error) {
+ for i, b := range nonce {
+ f.nonceMask[4+i] ^= b
+ }
+ result, err := f.aead.Open(out, f.nonceMask[:], ciphertext, additionalData)
+ for i, b := range nonce {
+ f.nonceMask[4+i] ^= b
+ }
+
+ return result, err
+}
+
+func aeadAESGCM(key, noncePrefix []byte) aead {
+ if len(noncePrefix) != noncePrefixLength {
+ panic("tls: internal error: wrong nonce length")
+ }
+ aes, err := aes.NewCipher(key)
+ if err != nil {
+ panic(err)
+ }
+ var aead cipher.AEAD
+ aead, err = cipher.NewGCM(aes)
+ if err != nil {
+ panic(err)
+ }
+
+ ret := &prefixNonceAEAD{aead: aead}
+ copy(ret.nonce[:], noncePrefix)
+ return ret
+}
+
+// AEADAESGCMTLS13 creates a new AES-GCM AEAD for TLS 1.3
+func AEADAESGCMTLS13(key, fixedNonce []byte) cipher.AEAD {
+ return aeadAESGCMTLS13(key, fixedNonce)
+}
+
+func aeadAESGCMTLS13(key, nonceMask []byte) aead {
+ if len(nonceMask) != aeadNonceLength {
+ panic("tls: internal error: wrong nonce length")
+ }
+ aes, err := aes.NewCipher(key)
+ if err != nil {
+ panic(err)
+ }
+ aead, err := cipher.NewGCM(aes)
+ if err != nil {
+ panic(err)
+ }
+
+ ret := &xorNonceAEAD{aead: aead}
+ copy(ret.nonceMask[:], nonceMask)
+ return ret
+}
+
+func aeadChaCha20Poly1305(key, nonceMask []byte) aead {
+ if len(nonceMask) != aeadNonceLength {
+ panic("tls: internal error: wrong nonce length")
+ }
+ aead, err := chacha20poly1305.New(key)
+ if err != nil {
+ panic(err)
+ }
+
+ ret := &xorNonceAEAD{aead: aead}
+ copy(ret.nonceMask[:], nonceMask)
+ return ret
+}
+
+type constantTimeHash interface {
+ hash.Hash
+ ConstantTimeSum(b []byte) []byte
+}
+
+// cthWrapper wraps any hash.Hash that implements ConstantTimeSum, and replaces
+// with that all calls to Sum. It's used to obtain a ConstantTimeSum-based HMAC.
+type cthWrapper struct {
+ h constantTimeHash
+}
+
+func (c *cthWrapper) Size() int { return c.h.Size() }
+func (c *cthWrapper) BlockSize() int { return c.h.BlockSize() }
+func (c *cthWrapper) Reset() { c.h.Reset() }
+func (c *cthWrapper) Write(p []byte) (int, error) { return c.h.Write(p) }
+func (c *cthWrapper) Sum(b []byte) []byte { return c.h.ConstantTimeSum(b) }
+
+func newConstantTimeHash(h func() hash.Hash) func() hash.Hash {
+ return func() hash.Hash {
+ return &cthWrapper{h().(constantTimeHash)}
+ }
+}
+
+// tls10MAC implements the TLS 1.0 MAC function. RFC 2246, Section 6.2.3.
+func tls10MAC(h hash.Hash, out, seq, header, data, extra []byte) []byte {
+ h.Reset()
+ h.Write(seq)
+ h.Write(header)
+ h.Write(data)
+ res := h.Sum(out)
+ if extra != nil {
+ h.Write(extra)
+ }
+ return res
+}
+
+func rsaKA(version uint16) keyAgreement {
+ return rsaKeyAgreement{}
+}
+
+func ecdheECDSAKA(version uint16) keyAgreement {
+ return &ecdheKeyAgreement{
+ isRSA: false,
+ version: version,
+ }
+}
+
+func ecdheRSAKA(version uint16) keyAgreement {
+ return &ecdheKeyAgreement{
+ isRSA: true,
+ version: version,
+ }
+}
+
+// mutualCipherSuite returns a cipherSuite given a list of supported
+// ciphersuites and the id requested by the peer.
+func mutualCipherSuite(have []uint16, want uint16) *cipherSuite {
+ for _, id := range have {
+ if id == want {
+ return cipherSuiteByID(id)
+ }
+ }
+ return nil
+}
+
+func cipherSuiteByID(id uint16) *cipherSuite {
+ for _, cipherSuite := range cipherSuites {
+ if cipherSuite.id == id {
+ return cipherSuite
+ }
+ }
+ return nil
+}
+
+func mutualCipherSuiteTLS13(have []uint16, want uint16) *cipherSuiteTLS13 {
+ for _, id := range have {
+ if id == want {
+ return cipherSuiteTLS13ByID(id)
+ }
+ }
+ return nil
+}
+
+func cipherSuiteTLS13ByID(id uint16) *cipherSuiteTLS13 {
+ for _, cipherSuite := range cipherSuitesTLS13 {
+ if cipherSuite.id == id {
+ return cipherSuite
+ }
+ }
+ return nil
+}
+
+// A list of cipher suite IDs that are, or have been, implemented by this
+// package.
+//
+// See https://www.iana.org/assignments/tls-parameters/tls-parameters.xml
+const (
+ // TLS 1.0 - 1.2 cipher suites.
+ TLS_RSA_WITH_RC4_128_SHA uint16 = 0x0005
+ TLS_RSA_WITH_3DES_EDE_CBC_SHA uint16 = 0x000a
+ TLS_RSA_WITH_AES_128_CBC_SHA uint16 = 0x002f
+ TLS_RSA_WITH_AES_256_CBC_SHA uint16 = 0x0035
+ TLS_RSA_WITH_AES_128_CBC_SHA256 uint16 = 0x003c
+ TLS_RSA_WITH_AES_128_GCM_SHA256 uint16 = 0x009c
+ TLS_RSA_WITH_AES_256_GCM_SHA384 uint16 = 0x009d
+ TLS_ECDHE_ECDSA_WITH_RC4_128_SHA uint16 = 0xc007
+ TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA uint16 = 0xc009
+ TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA uint16 = 0xc00a
+ TLS_ECDHE_RSA_WITH_RC4_128_SHA uint16 = 0xc011
+ TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA uint16 = 0xc012
+ TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA uint16 = 0xc013
+ TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA uint16 = 0xc014
+ TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256 uint16 = 0xc023
+ TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256 uint16 = 0xc027
+ TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 uint16 = 0xc02f
+ TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 uint16 = 0xc02b
+ TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 uint16 = 0xc030
+ TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 uint16 = 0xc02c
+ TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256 uint16 = 0xcca8
+ TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256 uint16 = 0xcca9
+
+ // TLS 1.3 cipher suites.
+ TLS_AES_128_GCM_SHA256 uint16 = 0x1301
+ TLS_AES_256_GCM_SHA384 uint16 = 0x1302
+ TLS_CHACHA20_POLY1305_SHA256 uint16 = 0x1303
+
+ // TLS_FALLBACK_SCSV isn't a standard cipher suite but an indicator
+ // that the client is doing version fallback. See RFC 7507.
+ TLS_FALLBACK_SCSV uint16 = 0x5600
+
+ // Legacy names for the corresponding cipher suites with the correct _SHA256
+ // suffix, retained for backward compatibility.
+ TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305 = TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256
+ TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305 = TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256
+)
diff --git a/vendor/github.com/quic-go/qtls-go1-19/common.go b/vendor/github.com/quic-go/qtls-go1-19/common.go
new file mode 100644
index 0000000000..6be26dced8
--- /dev/null
+++ b/vendor/github.com/quic-go/qtls-go1-19/common.go
@@ -0,0 +1,1513 @@
+// Copyright 2009 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package qtls
+
+import (
+ "bytes"
+ "container/list"
+ "context"
+ "crypto"
+ "crypto/ecdsa"
+ "crypto/ed25519"
+ "crypto/elliptic"
+ "crypto/rand"
+ "crypto/rsa"
+ "crypto/sha512"
+ "crypto/tls"
+ "crypto/x509"
+ "errors"
+ "fmt"
+ "io"
+ "net"
+ "strings"
+ "sync"
+ "time"
+)
+
+const (
+ VersionTLS10 = 0x0301
+ VersionTLS11 = 0x0302
+ VersionTLS12 = 0x0303
+ VersionTLS13 = 0x0304
+
+ // Deprecated: SSLv3 is cryptographically broken, and is no longer
+ // supported by this package. See golang.org/issue/32716.
+ VersionSSL30 = 0x0300
+)
+
+const (
+ maxPlaintext = 16384 // maximum plaintext payload length
+ maxCiphertext = 16384 + 2048 // maximum ciphertext payload length
+ maxCiphertextTLS13 = 16384 + 256 // maximum ciphertext length in TLS 1.3
+ recordHeaderLen = 5 // record header length
+ maxHandshake = 65536 // maximum handshake we support (protocol max is 16 MB)
+ maxUselessRecords = 16 // maximum number of consecutive non-advancing records
+)
+
+// TLS record types.
+type recordType uint8
+
+const (
+ recordTypeChangeCipherSpec recordType = 20
+ recordTypeAlert recordType = 21
+ recordTypeHandshake recordType = 22
+ recordTypeApplicationData recordType = 23
+)
+
+// TLS handshake message types.
+const (
+ typeHelloRequest uint8 = 0
+ typeClientHello uint8 = 1
+ typeServerHello uint8 = 2
+ typeNewSessionTicket uint8 = 4
+ typeEndOfEarlyData uint8 = 5
+ typeEncryptedExtensions uint8 = 8
+ typeCertificate uint8 = 11
+ typeServerKeyExchange uint8 = 12
+ typeCertificateRequest uint8 = 13
+ typeServerHelloDone uint8 = 14
+ typeCertificateVerify uint8 = 15
+ typeClientKeyExchange uint8 = 16
+ typeFinished uint8 = 20
+ typeCertificateStatus uint8 = 22
+ typeKeyUpdate uint8 = 24
+ typeNextProtocol uint8 = 67 // Not IANA assigned
+ typeMessageHash uint8 = 254 // synthetic message
+)
+
+// TLS compression types.
+const (
+ compressionNone uint8 = 0
+)
+
+type Extension struct {
+ Type uint16
+ Data []byte
+}
+
+// TLS extension numbers
+const (
+ extensionServerName uint16 = 0
+ extensionStatusRequest uint16 = 5
+ extensionSupportedCurves uint16 = 10 // supported_groups in TLS 1.3, see RFC 8446, Section 4.2.7
+ extensionSupportedPoints uint16 = 11
+ extensionSignatureAlgorithms uint16 = 13
+ extensionALPN uint16 = 16
+ extensionSCT uint16 = 18
+ extensionSessionTicket uint16 = 35
+ extensionPreSharedKey uint16 = 41
+ extensionEarlyData uint16 = 42
+ extensionSupportedVersions uint16 = 43
+ extensionCookie uint16 = 44
+ extensionPSKModes uint16 = 45
+ extensionCertificateAuthorities uint16 = 47
+ extensionSignatureAlgorithmsCert uint16 = 50
+ extensionKeyShare uint16 = 51
+ extensionRenegotiationInfo uint16 = 0xff01
+)
+
+// TLS signaling cipher suite values
+const (
+ scsvRenegotiation uint16 = 0x00ff
+)
+
+type EncryptionLevel uint8
+
+const (
+ EncryptionHandshake EncryptionLevel = iota
+ Encryption0RTT
+ EncryptionApplication
+)
+
+// CurveID is a tls.CurveID
+type CurveID = tls.CurveID
+
+const (
+ CurveP256 CurveID = 23
+ CurveP384 CurveID = 24
+ CurveP521 CurveID = 25
+ X25519 CurveID = 29
+)
+
+// TLS 1.3 Key Share. See RFC 8446, Section 4.2.8.
+type keyShare struct {
+ group CurveID
+ data []byte
+}
+
+// TLS 1.3 PSK Key Exchange Modes. See RFC 8446, Section 4.2.9.
+const (
+ pskModePlain uint8 = 0
+ pskModeDHE uint8 = 1
+)
+
+// TLS 1.3 PSK Identity. Can be a Session Ticket, or a reference to a saved
+// session. See RFC 8446, Section 4.2.11.
+type pskIdentity struct {
+ label []byte
+ obfuscatedTicketAge uint32
+}
+
+// TLS Elliptic Curve Point Formats
+// https://www.iana.org/assignments/tls-parameters/tls-parameters.xml#tls-parameters-9
+const (
+ pointFormatUncompressed uint8 = 0
+)
+
+// TLS CertificateStatusType (RFC 3546)
+const (
+ statusTypeOCSP uint8 = 1
+)
+
+// Certificate types (for certificateRequestMsg)
+const (
+ certTypeRSASign = 1
+ certTypeECDSASign = 64 // ECDSA or EdDSA keys, see RFC 8422, Section 3.
+)
+
+// Signature algorithms (for internal signaling use). Starting at 225 to avoid overlap with
+// TLS 1.2 codepoints (RFC 5246, Appendix A.4.1), with which these have nothing to do.
+const (
+ signaturePKCS1v15 uint8 = iota + 225
+ signatureRSAPSS
+ signatureECDSA
+ signatureEd25519
+)
+
+// directSigning is a standard Hash value that signals that no pre-hashing
+// should be performed, and that the input should be signed directly. It is the
+// hash function associated with the Ed25519 signature scheme.
+var directSigning crypto.Hash = 0
+
+// defaultSupportedSignatureAlgorithms contains the signature and hash algorithms that
+// the code advertises as supported in a TLS 1.2+ ClientHello and in a TLS 1.2+
+// CertificateRequest. The two fields are merged to match with TLS 1.3.
+// Note that in TLS 1.2, the ECDSA algorithms are not constrained to P-256, etc.
+var defaultSupportedSignatureAlgorithms = []SignatureScheme{
+ PSSWithSHA256,
+ ECDSAWithP256AndSHA256,
+ Ed25519,
+ PSSWithSHA384,
+ PSSWithSHA512,
+ PKCS1WithSHA256,
+ PKCS1WithSHA384,
+ PKCS1WithSHA512,
+ ECDSAWithP384AndSHA384,
+ ECDSAWithP521AndSHA512,
+ PKCS1WithSHA1,
+ ECDSAWithSHA1,
+}
+
+// helloRetryRequestRandom is set as the Random value of a ServerHello
+// to signal that the message is actually a HelloRetryRequest.
+var helloRetryRequestRandom = []byte{ // See RFC 8446, Section 4.1.3.
+ 0xCF, 0x21, 0xAD, 0x74, 0xE5, 0x9A, 0x61, 0x11,
+ 0xBE, 0x1D, 0x8C, 0x02, 0x1E, 0x65, 0xB8, 0x91,
+ 0xC2, 0xA2, 0x11, 0x16, 0x7A, 0xBB, 0x8C, 0x5E,
+ 0x07, 0x9E, 0x09, 0xE2, 0xC8, 0xA8, 0x33, 0x9C,
+}
+
+const (
+ // downgradeCanaryTLS12 or downgradeCanaryTLS11 is embedded in the server
+ // random as a downgrade protection if the server would be capable of
+ // negotiating a higher version. See RFC 8446, Section 4.1.3.
+ downgradeCanaryTLS12 = "DOWNGRD\x01"
+ downgradeCanaryTLS11 = "DOWNGRD\x00"
+)
+
+// testingOnlyForceDowngradeCanary is set in tests to force the server side to
+// include downgrade canaries even if it's using its highers supported version.
+var testingOnlyForceDowngradeCanary bool
+
+type ConnectionState = tls.ConnectionState
+
+// ConnectionState records basic TLS details about the connection.
+type connectionState struct {
+ // Version is the TLS version used by the connection (e.g. VersionTLS12).
+ Version uint16
+
+ // HandshakeComplete is true if the handshake has concluded.
+ HandshakeComplete bool
+
+ // DidResume is true if this connection was successfully resumed from a
+ // previous session with a session ticket or similar mechanism.
+ DidResume bool
+
+ // CipherSuite is the cipher suite negotiated for the connection (e.g.
+ // TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, TLS_AES_128_GCM_SHA256).
+ CipherSuite uint16
+
+ // NegotiatedProtocol is the application protocol negotiated with ALPN.
+ NegotiatedProtocol string
+
+ // NegotiatedProtocolIsMutual used to indicate a mutual NPN negotiation.
+ //
+ // Deprecated: this value is always true.
+ NegotiatedProtocolIsMutual bool
+
+ // ServerName is the value of the Server Name Indication extension sent by
+ // the client. It's available both on the server and on the client side.
+ ServerName string
+
+ // PeerCertificates are the parsed certificates sent by the peer, in the
+ // order in which they were sent. The first element is the leaf certificate
+ // that the connection is verified against.
+ //
+ // On the client side, it can't be empty. On the server side, it can be
+ // empty if Config.ClientAuth is not RequireAnyClientCert or
+ // RequireAndVerifyClientCert.
+ PeerCertificates []*x509.Certificate
+
+ // VerifiedChains is a list of one or more chains where the first element is
+ // PeerCertificates[0] and the last element is from Config.RootCAs (on the
+ // client side) or Config.ClientCAs (on the server side).
+ //
+ // On the client side, it's set if Config.InsecureSkipVerify is false. On
+ // the server side, it's set if Config.ClientAuth is VerifyClientCertIfGiven
+ // (and the peer provided a certificate) or RequireAndVerifyClientCert.
+ VerifiedChains [][]*x509.Certificate
+
+ // SignedCertificateTimestamps is a list of SCTs provided by the peer
+ // through the TLS handshake for the leaf certificate, if any.
+ SignedCertificateTimestamps [][]byte
+
+ // OCSPResponse is a stapled Online Certificate Status Protocol (OCSP)
+ // response provided by the peer for the leaf certificate, if any.
+ OCSPResponse []byte
+
+ // TLSUnique contains the "tls-unique" channel binding value (see RFC 5929,
+ // Section 3). This value will be nil for TLS 1.3 connections and for all
+ // resumed connections.
+ //
+ // Deprecated: there are conditions in which this value might not be unique
+ // to a connection. See the Security Considerations sections of RFC 5705 and
+ // RFC 7627, and https://mitls.org/pages/attacks/3SHAKE#channelbindings.
+ TLSUnique []byte
+
+ // ekm is a closure exposed via ExportKeyingMaterial.
+ ekm func(label string, context []byte, length int) ([]byte, error)
+}
+
+type ConnectionStateWith0RTT struct {
+ ConnectionState
+
+ Used0RTT bool // true if 0-RTT was both offered and accepted
+}
+
+// ClientAuthType is tls.ClientAuthType
+type ClientAuthType = tls.ClientAuthType
+
+const (
+ NoClientCert = tls.NoClientCert
+ RequestClientCert = tls.RequestClientCert
+ RequireAnyClientCert = tls.RequireAnyClientCert
+ VerifyClientCertIfGiven = tls.VerifyClientCertIfGiven
+ RequireAndVerifyClientCert = tls.RequireAndVerifyClientCert
+)
+
+// requiresClientCert reports whether the ClientAuthType requires a client
+// certificate to be provided.
+func requiresClientCert(c ClientAuthType) bool {
+ switch c {
+ case RequireAnyClientCert, RequireAndVerifyClientCert:
+ return true
+ default:
+ return false
+ }
+}
+
+// ClientSessionState contains the state needed by clients to resume TLS
+// sessions.
+type ClientSessionState = tls.ClientSessionState
+
+type clientSessionState struct {
+ sessionTicket []uint8 // Encrypted ticket used for session resumption with server
+ vers uint16 // TLS version negotiated for the session
+ cipherSuite uint16 // Ciphersuite negotiated for the session
+ masterSecret []byte // Full handshake MasterSecret, or TLS 1.3 resumption_master_secret
+ serverCertificates []*x509.Certificate // Certificate chain presented by the server
+ verifiedChains [][]*x509.Certificate // Certificate chains we built for verification
+ receivedAt time.Time // When the session ticket was received from the server
+ ocspResponse []byte // Stapled OCSP response presented by the server
+ scts [][]byte // SCTs presented by the server
+
+ // TLS 1.3 fields.
+ nonce []byte // Ticket nonce sent by the server, to derive PSK
+ useBy time.Time // Expiration of the ticket lifetime as set by the server
+ ageAdd uint32 // Random obfuscation factor for sending the ticket age
+}
+
+// ClientSessionCache is a cache of ClientSessionState objects that can be used
+// by a client to resume a TLS session with a given server. ClientSessionCache
+// implementations should expect to be called concurrently from different
+// goroutines. Up to TLS 1.2, only ticket-based resumption is supported, not
+// SessionID-based resumption. In TLS 1.3 they were merged into PSK modes, which
+// are supported via this interface.
+//
+//go:generate sh -c "mockgen -package qtls -destination mock_client_session_cache_test.go github.com/quic-go/qtls-go1-19 ClientSessionCache"
+type ClientSessionCache = tls.ClientSessionCache
+
+// SignatureScheme is a tls.SignatureScheme
+type SignatureScheme = tls.SignatureScheme
+
+const (
+ // RSASSA-PKCS1-v1_5 algorithms.
+ PKCS1WithSHA256 SignatureScheme = 0x0401
+ PKCS1WithSHA384 SignatureScheme = 0x0501
+ PKCS1WithSHA512 SignatureScheme = 0x0601
+
+ // RSASSA-PSS algorithms with public key OID rsaEncryption.
+ PSSWithSHA256 SignatureScheme = 0x0804
+ PSSWithSHA384 SignatureScheme = 0x0805
+ PSSWithSHA512 SignatureScheme = 0x0806
+
+ // ECDSA algorithms. Only constrained to a specific curve in TLS 1.3.
+ ECDSAWithP256AndSHA256 SignatureScheme = 0x0403
+ ECDSAWithP384AndSHA384 SignatureScheme = 0x0503
+ ECDSAWithP521AndSHA512 SignatureScheme = 0x0603
+
+ // EdDSA algorithms.
+ Ed25519 SignatureScheme = 0x0807
+
+ // Legacy signature and hash algorithms for TLS 1.2.
+ PKCS1WithSHA1 SignatureScheme = 0x0201
+ ECDSAWithSHA1 SignatureScheme = 0x0203
+)
+
+// ClientHelloInfo contains information from a ClientHello message in order to
+// guide application logic in the GetCertificate and GetConfigForClient callbacks.
+type ClientHelloInfo = tls.ClientHelloInfo
+
+type clientHelloInfo struct {
+ // CipherSuites lists the CipherSuites supported by the client (e.g.
+ // TLS_AES_128_GCM_SHA256, TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256).
+ CipherSuites []uint16
+
+ // 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
+
+ // SupportedCurves lists the elliptic curves supported by the client.
+ // SupportedCurves is set only if the Supported Elliptic Curves
+ // Extension is being used (see RFC 4492, Section 5.1.1).
+ SupportedCurves []CurveID
+
+ // SupportedPoints lists the point formats supported by the client.
+ // SupportedPoints is set only if the Supported Point Formats Extension
+ // is being used (see RFC 4492, Section 5.1.2).
+ SupportedPoints []uint8
+
+ // SignatureSchemes lists the signature and hash schemes that the client
+ // is willing to verify. SignatureSchemes is set only if the Signature
+ // Algorithms Extension is being used (see RFC 5246, Section 7.4.1.4.1).
+ SignatureSchemes []SignatureScheme
+
+ // SupportedProtos lists the application protocols supported by the client.
+ // SupportedProtos is set only if the Application-Layer Protocol
+ // Negotiation Extension is being used (see RFC 7301, Section 3.1).
+ //
+ // Servers can select a protocol by setting Config.NextProtos in a
+ // GetConfigForClient return value.
+ SupportedProtos []string
+
+ // SupportedVersions lists the TLS versions supported by the client.
+ // For TLS versions less than 1.3, this is extrapolated from the max
+ // version advertised by the client, so values other than the greatest
+ // might be rejected if used.
+ SupportedVersions []uint16
+
+ // Conn is the underlying net.Conn for the connection. Do not read
+ // from, or write to, this connection; that will cause the TLS
+ // connection to fail.
+ Conn net.Conn
+
+ // config is embedded by the GetCertificate or GetConfigForClient caller,
+ // for use with SupportsCertificate.
+ config *Config
+
+ // ctx is the context of the handshake that is in progress.
+ ctx context.Context
+}
+
+// Context returns the context of the handshake that is in progress.
+// This context is a child of the context passed to HandshakeContext,
+// if any, and is canceled when the handshake concludes.
+func (c *clientHelloInfo) Context() context.Context {
+ return c.ctx
+}
+
+// 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 = tls.CertificateRequestInfo
+
+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
+
+ // SignatureSchemes lists the signature schemes that the server is
+ // willing to verify.
+ SignatureSchemes []SignatureScheme
+
+ // Version is the TLS version that was negotiated for this connection.
+ Version uint16
+
+ // ctx is the context of the handshake that is in progress.
+ ctx context.Context
+}
+
+// Context returns the context of the handshake that is in progress.
+// This context is a child of the context passed to HandshakeContext,
+// if any, and is canceled when the handshake concludes.
+func (c *certificateRequestInfo) Context() context.Context {
+ return c.ctx
+}
+
+// RenegotiationSupport enumerates the different levels of support for TLS
+// renegotiation. TLS renegotiation is the act of performing subsequent
+// handshakes on a connection after the first. This significantly complicates
+// the state machine and has been the source of numerous, subtle security
+// issues. Initiating a renegotiation is not supported, but support for
+// accepting renegotiation requests may be enabled.
+//
+// Even when enabled, the server may not change its identity between handshakes
+// (i.e. the leaf certificate must be the same). Additionally, concurrent
+// handshake and application data flow is not permitted so renegotiation can
+// only be used with protocols that synchronise with the renegotiation, such as
+// HTTPS.
+//
+// Renegotiation is not defined in TLS 1.3.
+type RenegotiationSupport = tls.RenegotiationSupport
+
+const (
+ // RenegotiateNever disables renegotiation.
+ RenegotiateNever = tls.RenegotiateNever
+
+ // RenegotiateOnceAsClient allows a remote server to request
+ // renegotiation once per connection.
+ RenegotiateOnceAsClient = tls.RenegotiateOnceAsClient
+
+ // RenegotiateFreelyAsClient allows a remote server to repeatedly
+ // request renegotiation.
+ RenegotiateFreelyAsClient = tls.RenegotiateFreelyAsClient
+)
+
+// A Config structure is used to configure a TLS client or server.
+// After one has been passed to a TLS function it must not be
+// modified. A Config may be reused; the tls package will also not
+// modify it.
+type Config = tls.Config
+
+type config struct {
+ // Rand provides the source of entropy for nonces and RSA blinding.
+ // If Rand is nil, TLS uses the cryptographic random reader in package
+ // crypto/rand.
+ // The Reader must be safe for use by multiple goroutines.
+ Rand io.Reader
+
+ // Time returns the current time as the number of seconds since the epoch.
+ // If Time is nil, TLS uses time.Now.
+ Time func() time.Time
+
+ // Certificates contains one or more certificate chains to present to the
+ // other side of the connection. The first certificate compatible with the
+ // peer's requirements is selected automatically.
+ //
+ // Server configurations must set one of Certificates, GetCertificate or
+ // GetConfigForClient. Clients doing client-authentication may set either
+ // Certificates or GetClientCertificate.
+ //
+ // Note: if there are multiple Certificates, and they don't have the
+ // optional field Leaf set, certificate selection will incur a significant
+ // per-handshake performance cost.
+ Certificates []Certificate
+
+ // NameToCertificate maps from a certificate name to an element of
+ // Certificates. Note that a certificate name can be of the form
+ // '*.example.com' and so doesn't have to be a domain name as such.
+ //
+ // Deprecated: NameToCertificate only allows associating a single
+ // certificate with a given name. Leave this field nil to let the library
+ // select the first compatible chain from Certificates.
+ NameToCertificate map[string]*Certificate
+
+ // 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) (*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 may be called multiple times for the same
+ // connection if renegotiation occurs or if TLS 1.3 is in use.
+ GetClientCertificate func(*CertificateRequestInfo) (*Certificate, error)
+
+ // GetConfigForClient, if not nil, is called after a ClientHello is
+ // received from a client. It may return a non-nil Config in order to
+ // change the Config that will be used to handle this connection. If
+ // the returned Config is nil, the original Config will be used. The
+ // Config returned by this callback may not be subsequently modified.
+ //
+ // If GetConfigForClient is nil, the Config passed to Server() will be
+ // used for all connections.
+ //
+ // If SessionTicketKey was explicitly set on the returned Config, or if
+ // SetSessionTicketKeys was called on the returned Config, those keys will
+ // be used. Otherwise, the original Config keys will be used (and possibly
+ // rotated if they are automatically managed).
+ GetConfigForClient func(*ClientHelloInfo) (*Config, error)
+
+ // VerifyPeerCertificate, if not nil, is called after normal
+ // certificate verification by either a TLS client or server. It
+ // receives the raw ASN.1 certificates provided by the peer and also
+ // any verified chains that normal processing found. 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 argument will always be nil.
+ VerifyPeerCertificate func(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error
+
+ // VerifyConnection, if not nil, is called after normal certificate
+ // verification 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(ConnectionState) error
+
+ // RootCAs defines the set of root certificate authorities
+ // that clients use when verifying server certificates.
+ // If RootCAs is nil, TLS uses the host's root CA set.
+ RootCAs *x509.CertPool
+
+ // NextProtos is a list of supported application level protocols, in
+ // order of preference. If both peers support ALPN, the selected
+ // protocol will be one from this list, and the connection will fail
+ // if there is no mutually supported protocol. If NextProtos is empty
+ // or the peer doesn't support ALPN, the connection will succeed and
+ // ConnectionState.NegotiatedProtocol will be empty.
+ NextProtos []string
+
+ // ServerName is used to verify the hostname on the returned
+ // certificates unless InsecureSkipVerify is given. It is also included
+ // in the client's handshake to support virtual hosting unless it is
+ // an IP address.
+ ServerName string
+
+ // ClientAuth determines the server's policy for
+ // TLS Client Authentication. The default is NoClientCert.
+ ClientAuth ClientAuthType
+
+ // 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
+
+ // InsecureSkipVerify controls whether a client verifies the server's
+ // certificate chain and host name. If InsecureSkipVerify is true, crypto/tls
+ // accepts any certificate presented by the server and any host name in that
+ // certificate. In this mode, TLS is susceptible to machine-in-the-middle
+ // attacks unless custom verification is used. This should be used only for
+ // testing or in combination with VerifyConnection or VerifyPeerCertificate.
+ InsecureSkipVerify bool
+
+ // CipherSuites is a list of enabled TLS 1.0–1.2 cipher suites. The order of
+ // the list is ignored. Note that TLS 1.3 ciphersuites are not configurable.
+ //
+ // If CipherSuites is nil, a safe default list is used. The default cipher
+ // suites might change over time.
+ CipherSuites []uint16
+
+ // PreferServerCipherSuites is a legacy field and has no effect.
+ //
+ // It used to control whether the server would follow the client's or the
+ // server's preference. Servers now select the best mutually supported
+ // cipher suite based on logic that takes into account inferred client
+ // hardware, server hardware, and security.
+ //
+ // Deprecated: PreferServerCipherSuites is ignored.
+ PreferServerCipherSuites bool
+
+ // SessionTicketsDisabled may be set to true to disable session ticket and
+ // PSK (resumption) support. Note that on clients, session ticket support is
+ // also disabled if ClientSessionCache is nil.
+ SessionTicketsDisabled bool
+
+ // SessionTicketKey is used by TLS servers to provide session resumption.
+ // See RFC 5077 and the PSK mode of RFC 8446. If zero, it will be filled
+ // with random data before the first server handshake.
+ //
+ // Deprecated: if this field is left at zero, session ticket keys will be
+ // automatically rotated every day and dropped after seven days. For
+ // customizing the rotation schedule or synchronizing servers that are
+ // terminating connections for the same host, use SetSessionTicketKeys.
+ SessionTicketKey [32]byte
+
+ // ClientSessionCache is a cache of ClientSessionState entries for TLS
+ // session resumption. It is only used by clients.
+ ClientSessionCache ClientSessionCache
+
+ // MinVersion contains the minimum TLS version that is acceptable.
+ //
+ // By default, TLS 1.2 is currently used as the minimum when acting as a
+ // client, and TLS 1.0 when acting as a server. TLS 1.0 is the minimum
+ // supported by this package, both as a client and as a server.
+ //
+ // The client-side default can temporarily be reverted to TLS 1.0 by
+ // including the value "x509sha1=1" in the GODEBUG environment variable.
+ // Note that this option will be removed in Go 1.19 (but it will still be
+ // possible to set this field to VersionTLS10 explicitly).
+ MinVersion uint16
+
+ // MaxVersion contains the maximum TLS version that is acceptable.
+ //
+ // By default, the maximum version supported by this package is used,
+ // which is currently TLS 1.3.
+ MaxVersion uint16
+
+ // CurvePreferences contains the elliptic curves that will be used in
+ // an ECDHE handshake, in preference order. If empty, the default will
+ // be used. The client will use the first preference as the type for
+ // its key share in TLS 1.3. This may change in the future.
+ CurvePreferences []CurveID
+
+ // DynamicRecordSizingDisabled disables adaptive sizing of TLS records.
+ // When true, the largest possible TLS record size is always used. When
+ // false, the size of TLS records may be adjusted in an attempt to
+ // improve latency.
+ DynamicRecordSizingDisabled bool
+
+ // Renegotiation controls what types of renegotiation are supported.
+ // The default, none, is correct for the vast majority of applications.
+ Renegotiation RenegotiationSupport
+
+ // 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
+
+ // mutex protects sessionTicketKeys and autoSessionTicketKeys.
+ mutex sync.RWMutex
+ // sessionTicketKeys contains zero or more ticket keys. If set, it means the
+ // the keys were set with SessionTicketKey or SetSessionTicketKeys. The
+ // first key is used for new tickets and any subsequent keys can be used to
+ // decrypt old tickets. The slice contents are not protected by the mutex
+ // and are immutable.
+ sessionTicketKeys []ticketKey
+ // autoSessionTicketKeys is like sessionTicketKeys but is owned by the
+ // auto-rotation logic. See Config.ticketKeys.
+ autoSessionTicketKeys []ticketKey
+}
+
+// A RecordLayer handles encrypting and decrypting of TLS messages.
+type RecordLayer interface {
+ SetReadKey(encLevel EncryptionLevel, suite *CipherSuiteTLS13, trafficSecret []byte)
+ SetWriteKey(encLevel EncryptionLevel, suite *CipherSuiteTLS13, trafficSecret []byte)
+ ReadHandshakeMessage() ([]byte, error)
+ WriteRecord([]byte) (int, error)
+ SendAlert(uint8)
+}
+
+type ExtraConfig struct {
+ // GetExtensions, if not nil, is called before a message that allows
+ // sending of extensions is sent.
+ // Currently only implemented for the ClientHello message (for the client)
+ // and for the EncryptedExtensions message (for the server).
+ // Only valid for TLS 1.3.
+ GetExtensions func(handshakeMessageType uint8) []Extension
+
+ // ReceivedExtensions, if not nil, is called when a message that allows the
+ // inclusion of extensions is received.
+ // It is called with an empty slice of extensions, if the message didn't
+ // contain any extensions.
+ // Currently only implemented for the ClientHello message (sent by the
+ // client) and for the EncryptedExtensions message (sent by the server).
+ // Only valid for TLS 1.3.
+ ReceivedExtensions func(handshakeMessageType uint8, exts []Extension)
+
+ // AlternativeRecordLayer is used by QUIC
+ AlternativeRecordLayer RecordLayer
+
+ // Enforce the selection of a supported application protocol.
+ // Only works for TLS 1.3.
+ // If enabled, client and server have to agree on an application protocol.
+ // Otherwise, connection establishment fails.
+ EnforceNextProtoSelection bool
+
+ // If MaxEarlyData is greater than 0, the client will be allowed to send early
+ // data when resuming a session.
+ // Requires the AlternativeRecordLayer to be set.
+ //
+ // It has no meaning on the client.
+ MaxEarlyData uint32
+
+ // The Accept0RTT callback is called when the client offers 0-RTT.
+ // The server then has to decide if it wants to accept or reject 0-RTT.
+ // It is only used for servers.
+ Accept0RTT func(appData []byte) bool
+
+ // 0RTTRejected is called when the server rejectes 0-RTT.
+ // It is only used for clients.
+ Rejected0RTT func()
+
+ // If set, the client will export the 0-RTT key when resuming a session that
+ // allows sending of early data.
+ // Requires the AlternativeRecordLayer to be set.
+ //
+ // It has no meaning to the server.
+ Enable0RTT bool
+
+ // Is called when the client saves a session ticket to the session ticket.
+ // This gives the application the opportunity to save some data along with the ticket,
+ // which can be restored when the session ticket is used.
+ GetAppDataForSessionState func() []byte
+
+ // Is called when the client uses a session ticket.
+ // Restores the application data that was saved earlier on GetAppDataForSessionTicket.
+ SetAppDataFromSessionState func([]byte)
+}
+
+// Clone clones.
+func (c *ExtraConfig) Clone() *ExtraConfig {
+ return &ExtraConfig{
+ GetExtensions: c.GetExtensions,
+ ReceivedExtensions: c.ReceivedExtensions,
+ AlternativeRecordLayer: c.AlternativeRecordLayer,
+ EnforceNextProtoSelection: c.EnforceNextProtoSelection,
+ MaxEarlyData: c.MaxEarlyData,
+ Enable0RTT: c.Enable0RTT,
+ Accept0RTT: c.Accept0RTT,
+ Rejected0RTT: c.Rejected0RTT,
+ GetAppDataForSessionState: c.GetAppDataForSessionState,
+ SetAppDataFromSessionState: c.SetAppDataFromSessionState,
+ }
+}
+
+func (c *ExtraConfig) usesAlternativeRecordLayer() bool {
+ return c != nil && c.AlternativeRecordLayer != nil
+}
+
+const (
+ // ticketKeyNameLen is the number of bytes of identifier that is prepended to
+ // an encrypted session ticket in order to identify the key used to encrypt it.
+ ticketKeyNameLen = 16
+
+ // ticketKeyLifetime is how long a ticket key remains valid and can be used to
+ // resume a client connection.
+ ticketKeyLifetime = 7 * 24 * time.Hour // 7 days
+
+ // ticketKeyRotation is how often the server should rotate the session ticket key
+ // that is used for new tickets.
+ ticketKeyRotation = 24 * time.Hour
+)
+
+// ticketKey is the internal representation of a session ticket key.
+type ticketKey struct {
+ // keyName is an opaque byte string that serves to identify the session
+ // ticket key. It's exposed as plaintext in every session ticket.
+ keyName [ticketKeyNameLen]byte
+ aesKey [16]byte
+ hmacKey [16]byte
+ // created is the time at which this ticket key was created. See Config.ticketKeys.
+ created time.Time
+}
+
+// ticketKeyFromBytes converts from the external representation of a session
+// ticket key to a ticketKey. Externally, session ticket keys are 32 random
+// bytes and this function expands that into sufficient name and key material.
+func (c *config) ticketKeyFromBytes(b [32]byte) (key ticketKey) {
+ hashed := sha512.Sum512(b[:])
+ copy(key.keyName[:], hashed[:ticketKeyNameLen])
+ copy(key.aesKey[:], hashed[ticketKeyNameLen:ticketKeyNameLen+16])
+ copy(key.hmacKey[:], hashed[ticketKeyNameLen+16:ticketKeyNameLen+32])
+ key.created = c.time()
+ return key
+}
+
+// maxSessionTicketLifetime is the maximum allowed lifetime of a TLS 1.3 session
+// ticket, and the lifetime we set for tickets we send.
+const maxSessionTicketLifetime = 7 * 24 * time.Hour
+
+// Clone returns a shallow clone of c or nil if c is nil. It is safe to clone a Config that is
+// being used concurrently by a TLS client or server.
+func (c *config) Clone() *config {
+ if c == nil {
+ return nil
+ }
+ c.mutex.RLock()
+ defer c.mutex.RUnlock()
+ return &config{
+ Rand: c.Rand,
+ Time: c.Time,
+ Certificates: c.Certificates,
+ NameToCertificate: c.NameToCertificate,
+ GetCertificate: c.GetCertificate,
+ GetClientCertificate: c.GetClientCertificate,
+ GetConfigForClient: c.GetConfigForClient,
+ VerifyPeerCertificate: c.VerifyPeerCertificate,
+ VerifyConnection: c.VerifyConnection,
+ RootCAs: c.RootCAs,
+ NextProtos: c.NextProtos,
+ ServerName: c.ServerName,
+ ClientAuth: c.ClientAuth,
+ ClientCAs: c.ClientCAs,
+ InsecureSkipVerify: c.InsecureSkipVerify,
+ CipherSuites: c.CipherSuites,
+ PreferServerCipherSuites: c.PreferServerCipherSuites,
+ SessionTicketsDisabled: c.SessionTicketsDisabled,
+ SessionTicketKey: c.SessionTicketKey,
+ ClientSessionCache: c.ClientSessionCache,
+ MinVersion: c.MinVersion,
+ MaxVersion: c.MaxVersion,
+ CurvePreferences: c.CurvePreferences,
+ DynamicRecordSizingDisabled: c.DynamicRecordSizingDisabled,
+ Renegotiation: c.Renegotiation,
+ KeyLogWriter: c.KeyLogWriter,
+ sessionTicketKeys: c.sessionTicketKeys,
+ autoSessionTicketKeys: c.autoSessionTicketKeys,
+ }
+}
+
+// deprecatedSessionTicketKey is set as the prefix of SessionTicketKey if it was
+// randomized for backwards compatibility but is not in use.
+var deprecatedSessionTicketKey = []byte("DEPRECATED")
+
+// initLegacySessionTicketKeyRLocked ensures the legacy SessionTicketKey field is
+// randomized if empty, and that sessionTicketKeys is populated from it otherwise.
+func (c *config) initLegacySessionTicketKeyRLocked() {
+ // Don't write if SessionTicketKey is already defined as our deprecated string,
+ // or if it is defined by the user but sessionTicketKeys is already set.
+ if c.SessionTicketKey != [32]byte{} &&
+ (bytes.HasPrefix(c.SessionTicketKey[:], deprecatedSessionTicketKey) || len(c.sessionTicketKeys) > 0) {
+ return
+ }
+
+ // We need to write some data, so get an exclusive lock and re-check any conditions.
+ c.mutex.RUnlock()
+ defer c.mutex.RLock()
+ c.mutex.Lock()
+ defer c.mutex.Unlock()
+ if c.SessionTicketKey == [32]byte{} {
+ if _, err := io.ReadFull(c.rand(), c.SessionTicketKey[:]); err != nil {
+ panic(fmt.Sprintf("tls: unable to generate random session ticket key: %v", err))
+ }
+ // Write the deprecated prefix at the beginning so we know we created
+ // it. This key with the DEPRECATED prefix isn't used as an actual
+ // session ticket key, and is only randomized in case the application
+ // reuses it for some reason.
+ copy(c.SessionTicketKey[:], deprecatedSessionTicketKey)
+ } else if !bytes.HasPrefix(c.SessionTicketKey[:], deprecatedSessionTicketKey) && len(c.sessionTicketKeys) == 0 {
+ c.sessionTicketKeys = []ticketKey{c.ticketKeyFromBytes(c.SessionTicketKey)}
+ }
+
+}
+
+// ticketKeys returns the ticketKeys for this connection.
+// If configForClient has explicitly set keys, those will
+// be returned. Otherwise, the keys on c will be used and
+// may be rotated if auto-managed.
+// During rotation, any expired session ticket keys are deleted from
+// c.sessionTicketKeys. If the session ticket key that is currently
+// encrypting tickets (ie. the first ticketKey in c.sessionTicketKeys)
+// is not fresh, then a new session ticket key will be
+// created and prepended to c.sessionTicketKeys.
+func (c *config) ticketKeys(configForClient *config) []ticketKey {
+ // If the ConfigForClient callback returned a Config with explicitly set
+ // keys, use those, otherwise just use the original Config.
+ if configForClient != nil {
+ configForClient.mutex.RLock()
+ if configForClient.SessionTicketsDisabled {
+ return nil
+ }
+ configForClient.initLegacySessionTicketKeyRLocked()
+ if len(configForClient.sessionTicketKeys) != 0 {
+ ret := configForClient.sessionTicketKeys
+ configForClient.mutex.RUnlock()
+ return ret
+ }
+ configForClient.mutex.RUnlock()
+ }
+
+ c.mutex.RLock()
+ defer c.mutex.RUnlock()
+ if c.SessionTicketsDisabled {
+ return nil
+ }
+ c.initLegacySessionTicketKeyRLocked()
+ if len(c.sessionTicketKeys) != 0 {
+ return c.sessionTicketKeys
+ }
+ // Fast path for the common case where the key is fresh enough.
+ if len(c.autoSessionTicketKeys) > 0 && c.time().Sub(c.autoSessionTicketKeys[0].created) < ticketKeyRotation {
+ return c.autoSessionTicketKeys
+ }
+
+ // autoSessionTicketKeys are managed by auto-rotation.
+ c.mutex.RUnlock()
+ defer c.mutex.RLock()
+ c.mutex.Lock()
+ defer c.mutex.Unlock()
+ // Re-check the condition in case it changed since obtaining the new lock.
+ if len(c.autoSessionTicketKeys) == 0 || c.time().Sub(c.autoSessionTicketKeys[0].created) >= ticketKeyRotation {
+ var newKey [32]byte
+ if _, err := io.ReadFull(c.rand(), newKey[:]); err != nil {
+ panic(fmt.Sprintf("unable to generate random session ticket key: %v", err))
+ }
+ valid := make([]ticketKey, 0, len(c.autoSessionTicketKeys)+1)
+ valid = append(valid, c.ticketKeyFromBytes(newKey))
+ for _, k := range c.autoSessionTicketKeys {
+ // While rotating the current key, also remove any expired ones.
+ if c.time().Sub(k.created) < ticketKeyLifetime {
+ valid = append(valid, k)
+ }
+ }
+ c.autoSessionTicketKeys = valid
+ }
+ return c.autoSessionTicketKeys
+}
+
+// SetSessionTicketKeys updates the session ticket keys for a server.
+//
+// The first key will be used when creating new tickets, while all keys can be
+// used for decrypting tickets. It is safe to call this function while the
+// server is running in order to rotate the session ticket keys. The function
+// will panic if keys is empty.
+//
+// Calling this function will turn off automatic session ticket key rotation.
+//
+// If multiple servers are terminating connections for the same host they should
+// all have the same session ticket keys. If the session ticket keys leaks,
+// previously recorded and future TLS connections using those keys might be
+// compromised.
+func (c *config) SetSessionTicketKeys(keys [][32]byte) {
+ if len(keys) == 0 {
+ panic("tls: keys must have at least one key")
+ }
+
+ newKeys := make([]ticketKey, len(keys))
+ for i, bytes := range keys {
+ newKeys[i] = c.ticketKeyFromBytes(bytes)
+ }
+
+ c.mutex.Lock()
+ c.sessionTicketKeys = newKeys
+ c.mutex.Unlock()
+}
+
+func (c *config) rand() io.Reader {
+ r := c.Rand
+ if r == nil {
+ return rand.Reader
+ }
+ return r
+}
+
+func (c *config) time() time.Time {
+ t := c.Time
+ if t == nil {
+ t = time.Now
+ }
+ return t()
+}
+
+func (c *config) cipherSuites() []uint16 {
+ if needFIPS() {
+ return fipsCipherSuites(c)
+ }
+ if c.CipherSuites != nil {
+ return c.CipherSuites
+ }
+ return defaultCipherSuites
+}
+
+var supportedVersions = []uint16{
+ VersionTLS13,
+ VersionTLS12,
+ VersionTLS11,
+ VersionTLS10,
+}
+
+// roleClient and roleServer are meant to call supportedVersions and parents
+// with more readability at the callsite.
+const roleClient = true
+const roleServer = false
+
+func (c *config) supportedVersions(isClient bool) []uint16 {
+ versions := make([]uint16, 0, len(supportedVersions))
+ for _, v := range supportedVersions {
+ if needFIPS() && (v < fipsMinVersion(c) || v > fipsMaxVersion(c)) {
+ continue
+ }
+ if (c == nil || c.MinVersion == 0) &&
+ isClient && v < VersionTLS12 {
+ continue
+ }
+ if c != nil && c.MinVersion != 0 && v < c.MinVersion {
+ continue
+ }
+ if c != nil && c.MaxVersion != 0 && v > c.MaxVersion {
+ continue
+ }
+ versions = append(versions, v)
+ }
+ return versions
+}
+
+func (c *config) maxSupportedVersion(isClient bool) uint16 {
+ supportedVersions := c.supportedVersions(isClient)
+ if len(supportedVersions) == 0 {
+ return 0
+ }
+ return supportedVersions[0]
+}
+
+// supportedVersionsFromMax returns a list of supported versions derived from a
+// legacy maximum version value. Note that only versions supported by this
+// library are returned. Any newer peer will use supportedVersions anyway.
+func supportedVersionsFromMax(maxVersion uint16) []uint16 {
+ versions := make([]uint16, 0, len(supportedVersions))
+ for _, v := range supportedVersions {
+ if v > maxVersion {
+ continue
+ }
+ versions = append(versions, v)
+ }
+ return versions
+}
+
+var defaultCurvePreferences = []CurveID{X25519, CurveP256, CurveP384, CurveP521}
+
+func (c *config) curvePreferences() []CurveID {
+ if needFIPS() {
+ return fipsCurvePreferences(c)
+ }
+ if c == nil || len(c.CurvePreferences) == 0 {
+ return defaultCurvePreferences
+ }
+ return c.CurvePreferences
+}
+
+func (c *config) supportsCurve(curve CurveID) bool {
+ for _, cc := range c.curvePreferences() {
+ if cc == curve {
+ return true
+ }
+ }
+ return false
+}
+
+// mutualVersion returns the protocol version to use given the advertised
+// versions of the peer. Priority is given to the peer preference order.
+func (c *config) mutualVersion(isClient bool, peerVersions []uint16) (uint16, bool) {
+ supportedVersions := c.supportedVersions(isClient)
+ for _, peerVersion := range peerVersions {
+ for _, v := range supportedVersions {
+ if v == peerVersion {
+ return v, true
+ }
+ }
+ }
+ return 0, false
+}
+
+var errNoCertificates = errors.New("tls: no certificates configured")
+
+// getCertificate returns the best certificate for the given ClientHelloInfo,
+// defaulting to the first element of c.Certificates.
+func (c *config) getCertificate(clientHello *ClientHelloInfo) (*Certificate, error) {
+ if c.GetCertificate != nil &&
+ (len(c.Certificates) == 0 || len(clientHello.ServerName) > 0) {
+ cert, err := c.GetCertificate(clientHello)
+ if cert != nil || err != nil {
+ return cert, err
+ }
+ }
+
+ if len(c.Certificates) == 0 {
+ return nil, errNoCertificates
+ }
+
+ if len(c.Certificates) == 1 {
+ // There's only one choice, so no point doing any work.
+ return &c.Certificates[0], nil
+ }
+
+ if c.NameToCertificate != nil {
+ name := strings.ToLower(clientHello.ServerName)
+ if cert, ok := c.NameToCertificate[name]; ok {
+ return cert, nil
+ }
+ if len(name) > 0 {
+ labels := strings.Split(name, ".")
+ labels[0] = "*"
+ wildcardName := strings.Join(labels, ".")
+ if cert, ok := c.NameToCertificate[wildcardName]; ok {
+ return cert, nil
+ }
+ }
+ }
+
+ for _, cert := range c.Certificates {
+ if err := clientHello.SupportsCertificate(&cert); err == nil {
+ return &cert, nil
+ }
+ }
+
+ // If nothing matches, return the first certificate.
+ return &c.Certificates[0], nil
+}
+
+// SupportsCertificate returns nil if the provided certificate is supported by
+// the client that sent the ClientHello. Otherwise, it returns an error
+// describing the reason for the incompatibility.
+//
+// If this ClientHelloInfo was passed to a GetConfigForClient or GetCertificate
+// callback, this method will take into account the associated Config. Note that
+// if GetConfigForClient returns a different Config, the change can't be
+// accounted for by this method.
+//
+// This function will call x509.ParseCertificate unless c.Leaf is set, which can
+// incur a significant performance cost.
+func (chi *clientHelloInfo) SupportsCertificate(c *Certificate) error {
+ // Note we don't currently support certificate_authorities nor
+ // signature_algorithms_cert, and don't check the algorithms of the
+ // signatures on the chain (which anyway are a SHOULD, see RFC 8446,
+ // Section 4.4.2.2).
+
+ config := chi.config
+ if config == nil {
+ config = &Config{}
+ }
+ conf := fromConfig(config)
+ vers, ok := conf.mutualVersion(roleServer, chi.SupportedVersions)
+ if !ok {
+ return errors.New("no mutually supported protocol versions")
+ }
+
+ // If the client specified the name they are trying to connect to, the
+ // certificate needs to be valid for it.
+ if chi.ServerName != "" {
+ x509Cert, err := leafCertificate(c)
+ if err != nil {
+ return fmt.Errorf("failed to parse certificate: %w", err)
+ }
+ if err := x509Cert.VerifyHostname(chi.ServerName); err != nil {
+ return fmt.Errorf("certificate is not valid for requested server name: %w", err)
+ }
+ }
+
+ // supportsRSAFallback returns nil if the certificate and connection support
+ // the static RSA key exchange, and unsupported otherwise. The logic for
+ // supporting static RSA is completely disjoint from the logic for
+ // supporting signed key exchanges, so we just check it as a fallback.
+ supportsRSAFallback := func(unsupported error) error {
+ // TLS 1.3 dropped support for the static RSA key exchange.
+ if vers == VersionTLS13 {
+ return unsupported
+ }
+ // The static RSA key exchange works by decrypting a challenge with the
+ // RSA private key, not by signing, so check the PrivateKey implements
+ // crypto.Decrypter, like *rsa.PrivateKey does.
+ if priv, ok := c.PrivateKey.(crypto.Decrypter); ok {
+ if _, ok := priv.Public().(*rsa.PublicKey); !ok {
+ return unsupported
+ }
+ } else {
+ return unsupported
+ }
+ // Finally, there needs to be a mutual cipher suite that uses the static
+ // RSA key exchange instead of ECDHE.
+ rsaCipherSuite := selectCipherSuite(chi.CipherSuites, conf.cipherSuites(), func(c *cipherSuite) bool {
+ if c.flags&suiteECDHE != 0 {
+ return false
+ }
+ if vers < VersionTLS12 && c.flags&suiteTLS12 != 0 {
+ return false
+ }
+ return true
+ })
+ if rsaCipherSuite == nil {
+ return unsupported
+ }
+ return nil
+ }
+
+ // If the client sent the signature_algorithms extension, ensure it supports
+ // schemes we can use with this certificate and TLS version.
+ if len(chi.SignatureSchemes) > 0 {
+ if _, err := selectSignatureScheme(vers, c, chi.SignatureSchemes); err != nil {
+ return supportsRSAFallback(err)
+ }
+ }
+
+ // In TLS 1.3 we are done because supported_groups is only relevant to the
+ // ECDHE computation, point format negotiation is removed, cipher suites are
+ // only relevant to the AEAD choice, and static RSA does not exist.
+ if vers == VersionTLS13 {
+ return nil
+ }
+
+ // The only signed key exchange we support is ECDHE.
+ if !supportsECDHE(conf, chi.SupportedCurves, chi.SupportedPoints) {
+ return supportsRSAFallback(errors.New("client doesn't support ECDHE, can only use legacy RSA key exchange"))
+ }
+
+ var ecdsaCipherSuite bool
+ if priv, ok := c.PrivateKey.(crypto.Signer); ok {
+ switch pub := priv.Public().(type) {
+ case *ecdsa.PublicKey:
+ var curve CurveID
+ switch pub.Curve {
+ case elliptic.P256():
+ curve = CurveP256
+ case elliptic.P384():
+ curve = CurveP384
+ case elliptic.P521():
+ curve = CurveP521
+ default:
+ return supportsRSAFallback(unsupportedCertificateError(c))
+ }
+ var curveOk bool
+ for _, c := range chi.SupportedCurves {
+ if c == curve && conf.supportsCurve(c) {
+ curveOk = true
+ break
+ }
+ }
+ if !curveOk {
+ return errors.New("client doesn't support certificate curve")
+ }
+ ecdsaCipherSuite = true
+ case ed25519.PublicKey:
+ if vers < VersionTLS12 || len(chi.SignatureSchemes) == 0 {
+ return errors.New("connection doesn't support Ed25519")
+ }
+ ecdsaCipherSuite = true
+ case *rsa.PublicKey:
+ default:
+ return supportsRSAFallback(unsupportedCertificateError(c))
+ }
+ } else {
+ return supportsRSAFallback(unsupportedCertificateError(c))
+ }
+
+ // Make sure that there is a mutually supported cipher suite that works with
+ // this certificate. Cipher suite selection will then apply the logic in
+ // reverse to pick it. See also serverHandshakeState.cipherSuiteOk.
+ cipherSuite := selectCipherSuite(chi.CipherSuites, conf.cipherSuites(), func(c *cipherSuite) bool {
+ if c.flags&suiteECDHE == 0 {
+ return false
+ }
+ if c.flags&suiteECSign != 0 {
+ if !ecdsaCipherSuite {
+ return false
+ }
+ } else {
+ if ecdsaCipherSuite {
+ return false
+ }
+ }
+ if vers < VersionTLS12 && c.flags&suiteTLS12 != 0 {
+ return false
+ }
+ return true
+ })
+ if cipherSuite == nil {
+ return supportsRSAFallback(errors.New("client doesn't support any cipher suites compatible with the certificate"))
+ }
+
+ return nil
+}
+
+// BuildNameToCertificate parses c.Certificates and builds c.NameToCertificate
+// from the CommonName and SubjectAlternateName fields of each of the leaf
+// certificates.
+//
+// Deprecated: NameToCertificate only allows associating a single certificate
+// with a given name. Leave that field nil to let the library select the first
+// compatible chain from Certificates.
+func (c *config) BuildNameToCertificate() {
+ c.NameToCertificate = make(map[string]*Certificate)
+ for i := range c.Certificates {
+ cert := &c.Certificates[i]
+ x509Cert, err := leafCertificate(cert)
+ if err != nil {
+ continue
+ }
+ // If SANs are *not* present, some clients will consider the certificate
+ // valid for the name in the Common Name.
+ if x509Cert.Subject.CommonName != "" && len(x509Cert.DNSNames) == 0 {
+ c.NameToCertificate[x509Cert.Subject.CommonName] = cert
+ }
+ for _, san := range x509Cert.DNSNames {
+ c.NameToCertificate[san] = cert
+ }
+ }
+}
+
+const (
+ keyLogLabelTLS12 = "CLIENT_RANDOM"
+ keyLogLabelEarlyTraffic = "CLIENT_EARLY_TRAFFIC_SECRET"
+ keyLogLabelClientHandshake = "CLIENT_HANDSHAKE_TRAFFIC_SECRET"
+ keyLogLabelServerHandshake = "SERVER_HANDSHAKE_TRAFFIC_SECRET"
+ keyLogLabelClientTraffic = "CLIENT_TRAFFIC_SECRET_0"
+ keyLogLabelServerTraffic = "SERVER_TRAFFIC_SECRET_0"
+)
+
+func (c *config) writeKeyLog(label string, clientRandom, secret []byte) error {
+ if c.KeyLogWriter == nil {
+ return nil
+ }
+
+ logLine := []byte(fmt.Sprintf("%s %x %x\n", label, clientRandom, secret))
+
+ writerMutex.Lock()
+ _, err := c.KeyLogWriter.Write(logLine)
+ writerMutex.Unlock()
+
+ return err
+}
+
+// writerMutex protects all KeyLogWriters globally. It is rarely enabled,
+// and is only for debugging, so a global mutex saves space.
+var writerMutex sync.Mutex
+
+// A Certificate is a chain of one or more certificates, leaf first.
+type Certificate = tls.Certificate
+
+// leaf returns the parsed leaf certificate, either from c.Leaf or by parsing
+// the corresponding c.Certificate[0].
+func leafCertificate(c *Certificate) (*x509.Certificate, error) {
+ if c.Leaf != nil {
+ return c.Leaf, nil
+ }
+ return x509.ParseCertificate(c.Certificate[0])
+}
+
+type handshakeMessage interface {
+ marshal() []byte
+ unmarshal([]byte) bool
+}
+
+// lruSessionCache is a ClientSessionCache implementation that uses an LRU
+// caching strategy.
+type lruSessionCache struct {
+ sync.Mutex
+
+ m map[string]*list.Element
+ q *list.List
+ capacity int
+}
+
+type lruSessionCacheEntry struct {
+ sessionKey string
+ state *ClientSessionState
+}
+
+// NewLRUClientSessionCache returns a ClientSessionCache with the given
+// capacity that uses an LRU strategy. If capacity is < 1, a default capacity
+// is used instead.
+func NewLRUClientSessionCache(capacity int) ClientSessionCache {
+ const defaultSessionCacheCapacity = 64
+
+ if capacity < 1 {
+ capacity = defaultSessionCacheCapacity
+ }
+ return &lruSessionCache{
+ m: make(map[string]*list.Element),
+ q: list.New(),
+ capacity: capacity,
+ }
+}
+
+// Put adds the provided (sessionKey, cs) pair to the cache. If cs is nil, the entry
+// corresponding to sessionKey is removed from the cache instead.
+func (c *lruSessionCache) Put(sessionKey string, cs *ClientSessionState) {
+ c.Lock()
+ defer c.Unlock()
+
+ if elem, ok := c.m[sessionKey]; ok {
+ if cs == nil {
+ c.q.Remove(elem)
+ delete(c.m, sessionKey)
+ } else {
+ entry := elem.Value.(*lruSessionCacheEntry)
+ entry.state = cs
+ c.q.MoveToFront(elem)
+ }
+ return
+ }
+
+ if c.q.Len() < c.capacity {
+ entry := &lruSessionCacheEntry{sessionKey, cs}
+ c.m[sessionKey] = c.q.PushFront(entry)
+ return
+ }
+
+ elem := c.q.Back()
+ entry := elem.Value.(*lruSessionCacheEntry)
+ delete(c.m, entry.sessionKey)
+ entry.sessionKey = sessionKey
+ entry.state = cs
+ c.q.MoveToFront(elem)
+ c.m[sessionKey] = elem
+}
+
+// Get returns the ClientSessionState value associated with a given key. It
+// returns (nil, false) if no value is found.
+func (c *lruSessionCache) Get(sessionKey string) (*ClientSessionState, bool) {
+ c.Lock()
+ defer c.Unlock()
+
+ if elem, ok := c.m[sessionKey]; ok {
+ c.q.MoveToFront(elem)
+ return elem.Value.(*lruSessionCacheEntry).state, true
+ }
+ return nil, false
+}
+
+var emptyConfig Config
+
+func defaultConfig() *Config {
+ return &emptyConfig
+}
+
+func unexpectedMessageError(wanted, got any) error {
+ return fmt.Errorf("tls: received unexpected handshake message of type %T when waiting for %T", got, wanted)
+}
+
+func isSupportedSignatureAlgorithm(sigAlg SignatureScheme, supportedSignatureAlgorithms []SignatureScheme) bool {
+ for _, s := range supportedSignatureAlgorithms {
+ if s == sigAlg {
+ return true
+ }
+ }
+ return false
+}
diff --git a/vendor/github.com/quic-go/qtls-go1-19/conn.go b/vendor/github.com/quic-go/qtls-go1-19/conn.go
new file mode 100644
index 0000000000..5a17f7a14f
--- /dev/null
+++ b/vendor/github.com/quic-go/qtls-go1-19/conn.go
@@ -0,0 +1,1619 @@
+// Copyright 2010 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// TLS low level connection and record layer
+
+package qtls
+
+import (
+ "bytes"
+ "context"
+ "crypto/cipher"
+ "crypto/subtle"
+ "crypto/x509"
+ "errors"
+ "fmt"
+ "hash"
+ "io"
+ "net"
+ "sync"
+ "sync/atomic"
+ "time"
+)
+
+// A Conn represents a secured connection.
+// It implements the net.Conn interface.
+type Conn struct {
+ // constant
+ conn net.Conn
+ isClient bool
+ handshakeFn func(context.Context) error // (*Conn).clientHandshake or serverHandshake
+
+ // handshakeStatus is 1 if the connection is currently transferring
+ // application data (i.e. is not currently processing a handshake).
+ // handshakeStatus == 1 implies handshakeErr == nil.
+ // This field is only to be accessed with sync/atomic.
+ handshakeStatus uint32
+ // constant after handshake; protected by handshakeMutex
+ handshakeMutex sync.Mutex
+ handshakeErr error // error resulting from handshake
+ vers uint16 // TLS version
+ haveVers bool // version has been negotiated
+ config *config // configuration passed to constructor
+ // handshakes counts the number of handshakes performed on the
+ // connection so far. If renegotiation is disabled then this is either
+ // zero or one.
+ extraConfig *ExtraConfig
+
+ handshakes int
+ didResume bool // whether this connection was a session resumption
+ cipherSuite uint16
+ ocspResponse []byte // stapled OCSP response
+ scts [][]byte // signed certificate timestamps from server
+ peerCertificates []*x509.Certificate
+ // verifiedChains contains the certificate chains that we built, as
+ // opposed to the ones presented by the server.
+ verifiedChains [][]*x509.Certificate
+ // serverName contains the server name indicated by the client, if any.
+ serverName string
+ // secureRenegotiation is true if the server echoed the secure
+ // renegotiation extension. (This is meaningless as a server because
+ // renegotiation is not supported in that case.)
+ secureRenegotiation bool
+ // ekm is a closure for exporting keying material.
+ ekm func(label string, context []byte, length int) ([]byte, error)
+ // For the client:
+ // resumptionSecret is the resumption_master_secret for handling
+ // NewSessionTicket messages. nil if config.SessionTicketsDisabled.
+ // For the server:
+ // resumptionSecret is the resumption_master_secret for generating
+ // NewSessionTicket messages. Only used when the alternative record
+ // layer is set. nil if config.SessionTicketsDisabled.
+ resumptionSecret []byte
+
+ // ticketKeys is the set of active session ticket keys for this
+ // connection. The first one is used to encrypt new tickets and
+ // all are tried to decrypt tickets.
+ ticketKeys []ticketKey
+
+ // clientFinishedIsFirst is true if the client sent the first Finished
+ // message during the most recent handshake. This is recorded because
+ // the first transmitted Finished message is the tls-unique
+ // channel-binding value.
+ clientFinishedIsFirst bool
+
+ // closeNotifyErr is any error from sending the alertCloseNotify record.
+ closeNotifyErr error
+ // closeNotifySent is true if the Conn attempted to send an
+ // alertCloseNotify record.
+ closeNotifySent bool
+
+ // clientFinished and serverFinished contain the Finished message sent
+ // by the client or server in the most recent handshake. This is
+ // retained to support the renegotiation extension and tls-unique
+ // channel-binding.
+ clientFinished [12]byte
+ serverFinished [12]byte
+
+ // clientProtocol is the negotiated ALPN protocol.
+ clientProtocol string
+
+ // input/output
+ in, out halfConn
+ rawInput bytes.Buffer // raw input, starting with a record header
+ input bytes.Reader // application data waiting to be read, from rawInput.Next
+ hand bytes.Buffer // handshake data waiting to be read
+ buffering bool // whether records are buffered in sendBuf
+ sendBuf []byte // a buffer of records waiting to be sent
+
+ // bytesSent counts the bytes of application data sent.
+ // packetsSent counts packets.
+ bytesSent int64
+ packetsSent int64
+
+ // retryCount counts the number of consecutive non-advancing records
+ // received by Conn.readRecord. That is, records that neither advance the
+ // handshake, nor deliver application data. Protected by in.Mutex.
+ retryCount int
+
+ // activeCall is an atomic int32; the low bit is whether Close has
+ // been called. the rest of the bits are the number of goroutines
+ // in Conn.Write.
+ activeCall int32
+
+ used0RTT bool
+
+ tmp [16]byte
+
+ connStateMutex sync.Mutex
+ connState ConnectionStateWith0RTT
+}
+
+// Access to net.Conn methods.
+// Cannot just embed net.Conn because that would
+// export the struct field too.
+
+// LocalAddr returns the local network address.
+func (c *Conn) LocalAddr() net.Addr {
+ return c.conn.LocalAddr()
+}
+
+// RemoteAddr returns the remote network address.
+func (c *Conn) RemoteAddr() net.Addr {
+ return c.conn.RemoteAddr()
+}
+
+// SetDeadline sets the read and write deadlines associated with the connection.
+// A zero value for t means Read and Write will not time out.
+// After a Write has timed out, the TLS state is corrupt and all future writes will return the same error.
+func (c *Conn) SetDeadline(t time.Time) error {
+ return c.conn.SetDeadline(t)
+}
+
+// SetReadDeadline sets the read deadline on the underlying connection.
+// A zero value for t means Read will not time out.
+func (c *Conn) SetReadDeadline(t time.Time) error {
+ return c.conn.SetReadDeadline(t)
+}
+
+// SetWriteDeadline sets the write deadline on the underlying connection.
+// A zero value for t means Write will not time out.
+// After a Write has timed out, the TLS state is corrupt and all future writes will return the same error.
+func (c *Conn) SetWriteDeadline(t time.Time) error {
+ return c.conn.SetWriteDeadline(t)
+}
+
+// NetConn returns the underlying connection that is wrapped by c.
+// Note that writing to or reading from this connection directly will corrupt the
+// TLS session.
+func (c *Conn) NetConn() net.Conn {
+ return c.conn
+}
+
+// A halfConn represents one direction of the record layer
+// connection, either sending or receiving.
+type halfConn struct {
+ sync.Mutex
+
+ err error // first permanent error
+ version uint16 // protocol version
+ cipher any // cipher algorithm
+ mac hash.Hash
+ seq [8]byte // 64-bit sequence number
+
+ scratchBuf [13]byte // to avoid allocs; interface method args escape
+
+ nextCipher any // next encryption state
+ nextMac hash.Hash // next MAC algorithm
+
+ trafficSecret []byte // current TLS 1.3 traffic secret
+
+ setKeyCallback func(encLevel EncryptionLevel, suite *CipherSuiteTLS13, trafficSecret []byte)
+}
+
+type permanentError struct {
+ err net.Error
+}
+
+func (e *permanentError) Error() string { return e.err.Error() }
+func (e *permanentError) Unwrap() error { return e.err }
+func (e *permanentError) Timeout() bool { return e.err.Timeout() }
+func (e *permanentError) Temporary() bool { return false }
+
+func (hc *halfConn) setErrorLocked(err error) error {
+ if e, ok := err.(net.Error); ok {
+ hc.err = &permanentError{err: e}
+ } else {
+ hc.err = err
+ }
+ return hc.err
+}
+
+// prepareCipherSpec sets the encryption and MAC states
+// that a subsequent changeCipherSpec will use.
+func (hc *halfConn) prepareCipherSpec(version uint16, cipher any, mac hash.Hash) {
+ hc.version = version
+ hc.nextCipher = cipher
+ hc.nextMac = mac
+}
+
+// changeCipherSpec changes the encryption and MAC states
+// to the ones previously passed to prepareCipherSpec.
+func (hc *halfConn) changeCipherSpec() error {
+ if hc.nextCipher == nil || hc.version == VersionTLS13 {
+ return alertInternalError
+ }
+ hc.cipher = hc.nextCipher
+ hc.mac = hc.nextMac
+ hc.nextCipher = nil
+ hc.nextMac = nil
+ for i := range hc.seq {
+ hc.seq[i] = 0
+ }
+ return nil
+}
+
+func (hc *halfConn) exportKey(encLevel EncryptionLevel, suite *cipherSuiteTLS13, trafficSecret []byte) {
+ if hc.setKeyCallback != nil {
+ s := &CipherSuiteTLS13{
+ ID: suite.id,
+ KeyLen: suite.keyLen,
+ Hash: suite.hash,
+ AEAD: func(key, fixedNonce []byte) cipher.AEAD { return suite.aead(key, fixedNonce) },
+ }
+ hc.setKeyCallback(encLevel, s, trafficSecret)
+ }
+}
+
+func (hc *halfConn) setTrafficSecret(suite *cipherSuiteTLS13, secret []byte) {
+ hc.trafficSecret = secret
+ key, iv := suite.trafficKey(secret)
+ hc.cipher = suite.aead(key, iv)
+ for i := range hc.seq {
+ hc.seq[i] = 0
+ }
+}
+
+// incSeq increments the sequence number.
+func (hc *halfConn) incSeq() {
+ for i := 7; i >= 0; i-- {
+ hc.seq[i]++
+ if hc.seq[i] != 0 {
+ return
+ }
+ }
+
+ // Not allowed to let sequence number wrap.
+ // Instead, must renegotiate before it does.
+ // Not likely enough to bother.
+ panic("TLS: sequence number wraparound")
+}
+
+// explicitNonceLen returns the number of bytes of explicit nonce or IV included
+// in each record. Explicit nonces are present only in CBC modes after TLS 1.0
+// and in certain AEAD modes in TLS 1.2.
+func (hc *halfConn) explicitNonceLen() int {
+ if hc.cipher == nil {
+ return 0
+ }
+
+ switch c := hc.cipher.(type) {
+ case cipher.Stream:
+ return 0
+ case aead:
+ return c.explicitNonceLen()
+ case cbcMode:
+ // TLS 1.1 introduced a per-record explicit IV to fix the BEAST attack.
+ if hc.version >= VersionTLS11 {
+ return c.BlockSize()
+ }
+ return 0
+ default:
+ panic("unknown cipher type")
+ }
+}
+
+// extractPadding 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.
+func extractPadding(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)
+
+ // Zero the padding length on error. This ensures any unchecked bytes
+ // are included in the MAC. Otherwise, an attacker that could
+ // distinguish MAC failures from padding failures could mount an attack
+ // similar to POODLE in SSL 3.0: given a good ciphertext that uses a
+ // full block's worth of padding, replace the final block with another
+ // block. If the MAC check passed but the padding check failed, the
+ // last byte of that block decrypted to the block size.
+ //
+ // See also macAndPaddingGood logic below.
+ paddingLen &= good
+
+ toRemove = int(paddingLen) + 1
+ return
+}
+
+func roundUp(a, b int) int {
+ return a + (b-a%b)%b
+}
+
+// cbcMode is an interface for block ciphers using cipher block chaining.
+type cbcMode interface {
+ cipher.BlockMode
+ SetIV([]byte)
+}
+
+// decrypt authenticates and decrypts the record if protection is active at
+// this stage. The returned plaintext might overlap with the input.
+func (hc *halfConn) decrypt(record []byte) ([]byte, recordType, error) {
+ var plaintext []byte
+ typ := recordType(record[0])
+ payload := record[recordHeaderLen:]
+
+ // In TLS 1.3, change_cipher_spec messages are to be ignored without being
+ // decrypted. See RFC 8446, Appendix D.4.
+ if hc.version == VersionTLS13 && typ == recordTypeChangeCipherSpec {
+ return payload, typ, nil
+ }
+
+ paddingGood := byte(255)
+ paddingLen := 0
+
+ explicitNonceLen := hc.explicitNonceLen()
+
+ if hc.cipher != nil {
+ switch c := hc.cipher.(type) {
+ case cipher.Stream:
+ c.XORKeyStream(payload, payload)
+ case aead:
+ if len(payload) < explicitNonceLen {
+ return nil, 0, alertBadRecordMAC
+ }
+ nonce := payload[:explicitNonceLen]
+ if len(nonce) == 0 {
+ nonce = hc.seq[:]
+ }
+ payload = payload[explicitNonceLen:]
+
+ var additionalData []byte
+ if hc.version == VersionTLS13 {
+ additionalData = record[:recordHeaderLen]
+ } else {
+ additionalData = append(hc.scratchBuf[:0], hc.seq[:]...)
+ additionalData = append(additionalData, record[:3]...)
+ n := len(payload) - c.Overhead()
+ additionalData = append(additionalData, byte(n>>8), byte(n))
+ }
+
+ var err error
+ plaintext, err = c.Open(payload[:0], nonce, payload, additionalData)
+ if err != nil {
+ return nil, 0, alertBadRecordMAC
+ }
+ case cbcMode:
+ blockSize := c.BlockSize()
+ minPayload := explicitNonceLen + roundUp(hc.mac.Size()+1, blockSize)
+ if len(payload)%blockSize != 0 || len(payload) < minPayload {
+ return nil, 0, alertBadRecordMAC
+ }
+
+ if explicitNonceLen > 0 {
+ c.SetIV(payload[:explicitNonceLen])
+ payload = payload[explicitNonceLen:]
+ }
+ c.CryptBlocks(payload, payload)
+
+ // In a limited attempt to protect against CBC padding oracles like
+ // Lucky13, the data past paddingLen (which is secret) is passed to
+ // the MAC function as extra data, to be fed into the HMAC after
+ // computing the digest. This makes the MAC roughly constant time as
+ // long as the digest computation is constant time and does not
+ // affect the subsequent write, modulo cache effects.
+ paddingLen, paddingGood = extractPadding(payload)
+ default:
+ panic("unknown cipher type")
+ }
+
+ if hc.version == VersionTLS13 {
+ if typ != recordTypeApplicationData {
+ return nil, 0, alertUnexpectedMessage
+ }
+ if len(plaintext) > maxPlaintext+1 {
+ return nil, 0, alertRecordOverflow
+ }
+ // Remove padding and find the ContentType scanning from the end.
+ for i := len(plaintext) - 1; i >= 0; i-- {
+ if plaintext[i] != 0 {
+ typ = recordType(plaintext[i])
+ plaintext = plaintext[:i]
+ break
+ }
+ if i == 0 {
+ return nil, 0, alertUnexpectedMessage
+ }
+ }
+ }
+ } else {
+ plaintext = payload
+ }
+
+ if hc.mac != nil {
+ macSize := hc.mac.Size()
+ if len(payload) < macSize {
+ return nil, 0, alertBadRecordMAC
+ }
+
+ n := len(payload) - macSize - paddingLen
+ n = subtle.ConstantTimeSelect(int(uint32(n)>>31), 0, n) // if n < 0 { n = 0 }
+ record[3] = byte(n >> 8)
+ record[4] = byte(n)
+ remoteMAC := payload[n : n+macSize]
+ localMAC := tls10MAC(hc.mac, hc.scratchBuf[:0], hc.seq[:], record[:recordHeaderLen], payload[:n], payload[n+macSize:])
+
+ // This is equivalent to checking the MACs and paddingGood
+ // separately, but in constant-time to prevent distinguishing
+ // padding failures from MAC failures. Depending on what value
+ // of paddingLen was returned on bad padding, distinguishing
+ // bad MAC from bad padding can lead to an attack.
+ //
+ // See also the logic at the end of extractPadding.
+ macAndPaddingGood := subtle.ConstantTimeCompare(localMAC, remoteMAC) & int(paddingGood)
+ if macAndPaddingGood != 1 {
+ return nil, 0, alertBadRecordMAC
+ }
+
+ plaintext = payload[:n]
+ }
+
+ hc.incSeq()
+ return plaintext, typ, nil
+}
+
+func (c *Conn) setAlternativeRecordLayer() {
+ if c.extraConfig != nil && c.extraConfig.AlternativeRecordLayer != nil {
+ c.in.setKeyCallback = c.extraConfig.AlternativeRecordLayer.SetReadKey
+ c.out.setKeyCallback = c.extraConfig.AlternativeRecordLayer.SetWriteKey
+ }
+}
+
+// sliceForAppend extends the input slice by n bytes. head is the full extended
+// slice, while tail is the appended part. If the original slice has sufficient
+// capacity no allocation is performed.
+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
+}
+
+// encrypt encrypts payload, adding the appropriate nonce and/or MAC, and
+// appends it to record, which must already contain the record header.
+func (hc *halfConn) encrypt(record, payload []byte, rand io.Reader) ([]byte, error) {
+ if hc.cipher == nil {
+ return append(record, payload...), nil
+ }
+
+ var explicitNonce []byte
+ if explicitNonceLen := hc.explicitNonceLen(); explicitNonceLen > 0 {
+ record, explicitNonce = sliceForAppend(record, explicitNonceLen)
+ if _, isCBC := hc.cipher.(cbcMode); !isCBC && explicitNonceLen < 16 {
+ // The AES-GCM construction in TLS has an explicit nonce so that the
+ // nonce can be random. However, the nonce is only 8 bytes which is
+ // too small for a secure, random nonce. Therefore we use the
+ // sequence number as the nonce. The 3DES-CBC construction also has
+ // an 8 bytes nonce but its nonces must be unpredictable (see RFC
+ // 5246, Appendix F.3), forcing us to use randomness. That's not
+ // 3DES' biggest problem anyway because the birthday bound on block
+ // collision is reached first due to its similarly small block size
+ // (see the Sweet32 attack).
+ copy(explicitNonce, hc.seq[:])
+ } else {
+ if _, err := io.ReadFull(rand, explicitNonce); err != nil {
+ return nil, err
+ }
+ }
+ }
+
+ var dst []byte
+ switch c := hc.cipher.(type) {
+ case cipher.Stream:
+ mac := tls10MAC(hc.mac, hc.scratchBuf[:0], hc.seq[:], record[:recordHeaderLen], payload, nil)
+ record, dst = sliceForAppend(record, len(payload)+len(mac))
+ c.XORKeyStream(dst[:len(payload)], payload)
+ c.XORKeyStream(dst[len(payload):], mac)
+ case aead:
+ nonce := explicitNonce
+ if len(nonce) == 0 {
+ nonce = hc.seq[:]
+ }
+
+ if hc.version == VersionTLS13 {
+ record = append(record, payload...)
+
+ // Encrypt the actual ContentType and replace the plaintext one.
+ record = append(record, record[0])
+ record[0] = byte(recordTypeApplicationData)
+
+ n := len(payload) + 1 + c.Overhead()
+ record[3] = byte(n >> 8)
+ record[4] = byte(n)
+
+ record = c.Seal(record[:recordHeaderLen],
+ nonce, record[recordHeaderLen:], record[:recordHeaderLen])
+ } else {
+ additionalData := append(hc.scratchBuf[:0], hc.seq[:]...)
+ additionalData = append(additionalData, record[:recordHeaderLen]...)
+ record = c.Seal(record, nonce, payload, additionalData)
+ }
+ case cbcMode:
+ mac := tls10MAC(hc.mac, hc.scratchBuf[:0], hc.seq[:], record[:recordHeaderLen], payload, nil)
+ blockSize := c.BlockSize()
+ plaintextLen := len(payload) + len(mac)
+ paddingLen := blockSize - plaintextLen%blockSize
+ record, dst = sliceForAppend(record, plaintextLen+paddingLen)
+ copy(dst, payload)
+ copy(dst[len(payload):], mac)
+ for i := plaintextLen; i < len(dst); i++ {
+ dst[i] = byte(paddingLen - 1)
+ }
+ if len(explicitNonce) > 0 {
+ c.SetIV(explicitNonce)
+ }
+ c.CryptBlocks(dst, dst)
+ default:
+ panic("unknown cipher type")
+ }
+
+ // Update length to include nonce, MAC and any block padding needed.
+ n := len(record) - recordHeaderLen
+ record[3] = byte(n >> 8)
+ record[4] = byte(n)
+ hc.incSeq()
+
+ return record, nil
+}
+
+// RecordHeaderError is returned when a TLS record header is invalid.
+type RecordHeaderError struct {
+ // Msg contains a human readable string that describes the error.
+ Msg string
+ // RecordHeader contains the five bytes of TLS record header that
+ // triggered the error.
+ RecordHeader [5]byte
+ // Conn provides the underlying net.Conn in the case that a client
+ // sent an initial handshake that didn't look like TLS.
+ // It is nil if there's already been a handshake or a TLS alert has
+ // been written to the connection.
+ Conn net.Conn
+}
+
+func (e RecordHeaderError) Error() string { return "tls: " + e.Msg }
+
+func (c *Conn) newRecordHeaderError(conn net.Conn, msg string) (err RecordHeaderError) {
+ err.Msg = msg
+ err.Conn = conn
+ copy(err.RecordHeader[:], c.rawInput.Bytes())
+ return err
+}
+
+func (c *Conn) readRecord() error {
+ return c.readRecordOrCCS(false)
+}
+
+func (c *Conn) readChangeCipherSpec() error {
+ return c.readRecordOrCCS(true)
+}
+
+// readRecordOrCCS reads one or more TLS records from the connection and
+// updates the record layer state. Some invariants:
+// - c.in must be locked
+// - c.input must be empty
+//
+// During the handshake one and only one of the following will happen:
+// - c.hand grows
+// - c.in.changeCipherSpec is called
+// - an error is returned
+//
+// After the handshake one and only one of the following will happen:
+// - c.hand grows
+// - c.input is set
+// - an error is returned
+func (c *Conn) readRecordOrCCS(expectChangeCipherSpec bool) error {
+ if c.in.err != nil {
+ return c.in.err
+ }
+ handshakeComplete := c.handshakeComplete()
+
+ // This function modifies c.rawInput, which owns the c.input memory.
+ if c.input.Len() != 0 {
+ return c.in.setErrorLocked(errors.New("tls: internal error: attempted to read record with pending application data"))
+ }
+ c.input.Reset(nil)
+
+ // Read header, payload.
+ if err := c.readFromUntil(c.conn, recordHeaderLen); err != nil {
+ // RFC 8446, Section 6.1 suggests that EOF without an alertCloseNotify
+ // is an error, but popular web sites seem to do this, so we accept it
+ // if and only if at the record boundary.
+ if err == io.ErrUnexpectedEOF && c.rawInput.Len() == 0 {
+ err = io.EOF
+ }
+ if e, ok := err.(net.Error); !ok || !e.Temporary() {
+ c.in.setErrorLocked(err)
+ }
+ return err
+ }
+ hdr := c.rawInput.Bytes()[:recordHeaderLen]
+ typ := recordType(hdr[0])
+
+ // No valid TLS record has a type of 0x80, however SSLv2 handshakes
+ // start with a uint16 length where the MSB is set and the first record
+ // is always < 256 bytes long. Therefore typ == 0x80 strongly suggests
+ // an SSLv2 client.
+ if !handshakeComplete && typ == 0x80 {
+ c.sendAlert(alertProtocolVersion)
+ return c.in.setErrorLocked(c.newRecordHeaderError(nil, "unsupported SSLv2 handshake received"))
+ }
+
+ vers := uint16(hdr[1])<<8 | uint16(hdr[2])
+ n := int(hdr[3])<<8 | int(hdr[4])
+ if c.haveVers && c.vers != VersionTLS13 && vers != c.vers {
+ c.sendAlert(alertProtocolVersion)
+ msg := fmt.Sprintf("received record with version %x when expecting version %x", vers, c.vers)
+ return c.in.setErrorLocked(c.newRecordHeaderError(nil, msg))
+ }
+ if !c.haveVers {
+ // First message, be extra suspicious: this might not be a TLS
+ // client. Bail out before reading a full 'body', if possible.
+ // The current max version is 3.3 so if the version is >= 16.0,
+ // it's probably not real.
+ if (typ != recordTypeAlert && typ != recordTypeHandshake) || vers >= 0x1000 {
+ return c.in.setErrorLocked(c.newRecordHeaderError(c.conn, "first record does not look like a TLS handshake"))
+ }
+ }
+ if c.vers == VersionTLS13 && n > maxCiphertextTLS13 || n > maxCiphertext {
+ c.sendAlert(alertRecordOverflow)
+ msg := fmt.Sprintf("oversized record received with length %d", n)
+ return c.in.setErrorLocked(c.newRecordHeaderError(nil, msg))
+ }
+ if err := c.readFromUntil(c.conn, recordHeaderLen+n); err != nil {
+ if e, ok := err.(net.Error); !ok || !e.Temporary() {
+ c.in.setErrorLocked(err)
+ }
+ return err
+ }
+
+ // Process message.
+ record := c.rawInput.Next(recordHeaderLen + n)
+ data, typ, err := c.in.decrypt(record)
+ if err != nil {
+ return c.in.setErrorLocked(c.sendAlert(err.(alert)))
+ }
+ if len(data) > maxPlaintext {
+ return c.in.setErrorLocked(c.sendAlert(alertRecordOverflow))
+ }
+
+ // Application Data messages are always protected.
+ if c.in.cipher == nil && typ == recordTypeApplicationData {
+ return c.in.setErrorLocked(c.sendAlert(alertUnexpectedMessage))
+ }
+
+ if typ != recordTypeAlert && typ != recordTypeChangeCipherSpec && len(data) > 0 {
+ // This is a state-advancing message: reset the retry count.
+ c.retryCount = 0
+ }
+
+ // Handshake messages MUST NOT be interleaved with other record types in TLS 1.3.
+ if c.vers == VersionTLS13 && typ != recordTypeHandshake && c.hand.Len() > 0 {
+ return c.in.setErrorLocked(c.sendAlert(alertUnexpectedMessage))
+ }
+
+ switch typ {
+ default:
+ return c.in.setErrorLocked(c.sendAlert(alertUnexpectedMessage))
+
+ case recordTypeAlert:
+ if len(data) != 2 {
+ return c.in.setErrorLocked(c.sendAlert(alertUnexpectedMessage))
+ }
+ if alert(data[1]) == alertCloseNotify {
+ return c.in.setErrorLocked(io.EOF)
+ }
+ if c.vers == VersionTLS13 {
+ return c.in.setErrorLocked(&net.OpError{Op: "remote error", Err: alert(data[1])})
+ }
+ switch data[0] {
+ case alertLevelWarning:
+ // Drop the record on the floor and retry.
+ return c.retryReadRecord(expectChangeCipherSpec)
+ case alertLevelError:
+ return c.in.setErrorLocked(&net.OpError{Op: "remote error", Err: alert(data[1])})
+ default:
+ return c.in.setErrorLocked(c.sendAlert(alertUnexpectedMessage))
+ }
+
+ case recordTypeChangeCipherSpec:
+ if len(data) != 1 || data[0] != 1 {
+ return c.in.setErrorLocked(c.sendAlert(alertDecodeError))
+ }
+ // Handshake messages are not allowed to fragment across the CCS.
+ if c.hand.Len() > 0 {
+ return c.in.setErrorLocked(c.sendAlert(alertUnexpectedMessage))
+ }
+ // In TLS 1.3, change_cipher_spec records are ignored until the
+ // Finished. See RFC 8446, Appendix D.4. Note that according to Section
+ // 5, a server can send a ChangeCipherSpec before its ServerHello, when
+ // c.vers is still unset. That's not useful though and suspicious if the
+ // server then selects a lower protocol version, so don't allow that.
+ if c.vers == VersionTLS13 {
+ return c.retryReadRecord(expectChangeCipherSpec)
+ }
+ if !expectChangeCipherSpec {
+ return c.in.setErrorLocked(c.sendAlert(alertUnexpectedMessage))
+ }
+ if err := c.in.changeCipherSpec(); err != nil {
+ return c.in.setErrorLocked(c.sendAlert(err.(alert)))
+ }
+
+ case recordTypeApplicationData:
+ if !handshakeComplete || expectChangeCipherSpec {
+ return c.in.setErrorLocked(c.sendAlert(alertUnexpectedMessage))
+ }
+ // Some OpenSSL servers send empty records in order to randomize the
+ // CBC IV. Ignore a limited number of empty records.
+ if len(data) == 0 {
+ return c.retryReadRecord(expectChangeCipherSpec)
+ }
+ // Note that data is owned by c.rawInput, following the Next call above,
+ // to avoid copying the plaintext. This is safe because c.rawInput is
+ // not read from or written to until c.input is drained.
+ c.input.Reset(data)
+
+ case recordTypeHandshake:
+ if len(data) == 0 || expectChangeCipherSpec {
+ return c.in.setErrorLocked(c.sendAlert(alertUnexpectedMessage))
+ }
+ c.hand.Write(data)
+ }
+
+ return nil
+}
+
+// retryReadRecord recurs into readRecordOrCCS to drop a non-advancing record, like
+// a warning alert, empty application_data, or a change_cipher_spec in TLS 1.3.
+func (c *Conn) retryReadRecord(expectChangeCipherSpec bool) error {
+ c.retryCount++
+ if c.retryCount > maxUselessRecords {
+ c.sendAlert(alertUnexpectedMessage)
+ return c.in.setErrorLocked(errors.New("tls: too many ignored records"))
+ }
+ return c.readRecordOrCCS(expectChangeCipherSpec)
+}
+
+// atLeastReader reads from R, stopping with EOF once at least N bytes have been
+// read. It is different from an io.LimitedReader in that it doesn't cut short
+// the last Read call, and in that it considers an early EOF an error.
+type atLeastReader struct {
+ R io.Reader
+ N int64
+}
+
+func (r *atLeastReader) Read(p []byte) (int, error) {
+ if r.N <= 0 {
+ return 0, io.EOF
+ }
+ n, err := r.R.Read(p)
+ r.N -= int64(n) // won't underflow unless len(p) >= n > 9223372036854775809
+ if r.N > 0 && err == io.EOF {
+ return n, io.ErrUnexpectedEOF
+ }
+ if r.N <= 0 && err == nil {
+ return n, io.EOF
+ }
+ return n, err
+}
+
+// readFromUntil reads from r into c.rawInput until c.rawInput contains
+// at least n bytes or else returns an error.
+func (c *Conn) readFromUntil(r io.Reader, n int) error {
+ if c.rawInput.Len() >= n {
+ return nil
+ }
+ needs := n - c.rawInput.Len()
+ // There might be extra input waiting on the wire. Make a best effort
+ // attempt to fetch it so that it can be used in (*Conn).Read to
+ // "predict" closeNotify alerts.
+ c.rawInput.Grow(needs + bytes.MinRead)
+ _, err := c.rawInput.ReadFrom(&atLeastReader{r, int64(needs)})
+ return err
+}
+
+// sendAlert sends a TLS alert message.
+func (c *Conn) sendAlertLocked(err alert) error {
+ switch err {
+ case alertNoRenegotiation, alertCloseNotify:
+ c.tmp[0] = alertLevelWarning
+ default:
+ c.tmp[0] = alertLevelError
+ }
+ c.tmp[1] = byte(err)
+
+ _, writeErr := c.writeRecordLocked(recordTypeAlert, c.tmp[0:2])
+ if err == alertCloseNotify {
+ // closeNotify is a special case in that it isn't an error.
+ return writeErr
+ }
+
+ return c.out.setErrorLocked(&net.OpError{Op: "local error", Err: err})
+}
+
+// sendAlert sends a TLS alert message.
+func (c *Conn) sendAlert(err alert) error {
+ if c.extraConfig != nil && c.extraConfig.AlternativeRecordLayer != nil {
+ c.extraConfig.AlternativeRecordLayer.SendAlert(uint8(err))
+ return &net.OpError{Op: "local error", Err: err}
+ }
+
+ c.out.Lock()
+ defer c.out.Unlock()
+ return c.sendAlertLocked(err)
+}
+
+const (
+ // tcpMSSEstimate is a conservative estimate of the TCP maximum segment
+ // size (MSS). A constant is used, rather than querying the kernel for
+ // the actual MSS, to avoid complexity. The value here is the IPv6
+ // minimum MTU (1280 bytes) minus the overhead of an IPv6 header (40
+ // bytes) and a TCP header with timestamps (32 bytes).
+ tcpMSSEstimate = 1208
+
+ // recordSizeBoostThreshold is the number of bytes of application data
+ // sent after which the TLS record size will be increased to the
+ // maximum.
+ recordSizeBoostThreshold = 128 * 1024
+)
+
+// maxPayloadSizeForWrite returns the maximum TLS payload size to use for the
+// next application data record. There is the following trade-off:
+//
+// - For latency-sensitive applications, such as web browsing, each TLS
+// record should fit in one TCP segment.
+// - For throughput-sensitive applications, such as large file transfers,
+// larger TLS records better amortize framing and encryption overheads.
+//
+// A simple heuristic that works well in practice is to use small records for
+// the first 1MB of data, then use larger records for subsequent data, and
+// reset back to smaller records after the connection becomes idle. See "High
+// Performance Web Networking", Chapter 4, or:
+// https://www.igvita.com/2013/10/24/optimizing-tls-record-size-and-buffering-latency/
+//
+// In the interests of simplicity and determinism, this code does not attempt
+// to reset the record size once the connection is idle, however.
+func (c *Conn) maxPayloadSizeForWrite(typ recordType) int {
+ if c.config.DynamicRecordSizingDisabled || typ != recordTypeApplicationData {
+ return maxPlaintext
+ }
+
+ if c.bytesSent >= recordSizeBoostThreshold {
+ return maxPlaintext
+ }
+
+ // Subtract TLS overheads to get the maximum payload size.
+ payloadBytes := tcpMSSEstimate - recordHeaderLen - c.out.explicitNonceLen()
+ if c.out.cipher != nil {
+ switch ciph := c.out.cipher.(type) {
+ case cipher.Stream:
+ payloadBytes -= c.out.mac.Size()
+ case cipher.AEAD:
+ payloadBytes -= ciph.Overhead()
+ case cbcMode:
+ blockSize := ciph.BlockSize()
+ // The payload must fit in a multiple of blockSize, with
+ // room for at least one padding byte.
+ payloadBytes = (payloadBytes & ^(blockSize - 1)) - 1
+ // The MAC is appended before padding so affects the
+ // payload size directly.
+ payloadBytes -= c.out.mac.Size()
+ default:
+ panic("unknown cipher type")
+ }
+ }
+ if c.vers == VersionTLS13 {
+ payloadBytes-- // encrypted ContentType
+ }
+
+ // Allow packet growth in arithmetic progression up to max.
+ pkt := c.packetsSent
+ c.packetsSent++
+ if pkt > 1000 {
+ return maxPlaintext // avoid overflow in multiply below
+ }
+
+ n := payloadBytes * int(pkt+1)
+ if n > maxPlaintext {
+ n = maxPlaintext
+ }
+ return n
+}
+
+func (c *Conn) write(data []byte) (int, error) {
+ if c.buffering {
+ c.sendBuf = append(c.sendBuf, data...)
+ return len(data), nil
+ }
+
+ n, err := c.conn.Write(data)
+ c.bytesSent += int64(n)
+ return n, err
+}
+
+func (c *Conn) flush() (int, error) {
+ if len(c.sendBuf) == 0 {
+ return 0, nil
+ }
+
+ n, err := c.conn.Write(c.sendBuf)
+ c.bytesSent += int64(n)
+ c.sendBuf = nil
+ c.buffering = false
+ return n, err
+}
+
+// outBufPool pools the record-sized scratch buffers used by writeRecordLocked.
+var outBufPool = sync.Pool{
+ New: func() any {
+ return new([]byte)
+ },
+}
+
+// writeRecordLocked writes a TLS record with the given type and payload to the
+// connection and updates the record layer state.
+func (c *Conn) writeRecordLocked(typ recordType, data []byte) (int, error) {
+ outBufPtr := outBufPool.Get().(*[]byte)
+ outBuf := *outBufPtr
+ defer func() {
+ // You might be tempted to simplify this by just passing &outBuf to Put,
+ // but that would make the local copy of the outBuf slice header escape
+ // to the heap, causing an allocation. Instead, we keep around the
+ // pointer to the slice header returned by Get, which is already on the
+ // heap, and overwrite and return that.
+ *outBufPtr = outBuf
+ outBufPool.Put(outBufPtr)
+ }()
+
+ var n int
+ for len(data) > 0 {
+ m := len(data)
+ if maxPayload := c.maxPayloadSizeForWrite(typ); m > maxPayload {
+ m = maxPayload
+ }
+
+ _, outBuf = sliceForAppend(outBuf[:0], recordHeaderLen)
+ outBuf[0] = byte(typ)
+ vers := c.vers
+ if vers == 0 {
+ // Some TLS servers fail if the record version is
+ // greater than TLS 1.0 for the initial ClientHello.
+ vers = VersionTLS10
+ } else if vers == VersionTLS13 {
+ // TLS 1.3 froze the record layer version to 1.2.
+ // See RFC 8446, Section 5.1.
+ vers = VersionTLS12
+ }
+ outBuf[1] = byte(vers >> 8)
+ outBuf[2] = byte(vers)
+ outBuf[3] = byte(m >> 8)
+ outBuf[4] = byte(m)
+
+ var err error
+ outBuf, err = c.out.encrypt(outBuf, data[:m], c.config.rand())
+ if err != nil {
+ return n, err
+ }
+ if _, err := c.write(outBuf); err != nil {
+ return n, err
+ }
+ n += m
+ data = data[m:]
+ }
+
+ if typ == recordTypeChangeCipherSpec && c.vers != VersionTLS13 {
+ if err := c.out.changeCipherSpec(); err != nil {
+ return n, c.sendAlertLocked(err.(alert))
+ }
+ }
+
+ return n, nil
+}
+
+// writeRecord writes a TLS record with the given type and payload to the
+// connection and updates the record layer state.
+func (c *Conn) writeRecord(typ recordType, data []byte) (int, error) {
+ if c.extraConfig != nil && c.extraConfig.AlternativeRecordLayer != nil {
+ if typ == recordTypeChangeCipherSpec {
+ return len(data), nil
+ }
+ return c.extraConfig.AlternativeRecordLayer.WriteRecord(data)
+ }
+
+ c.out.Lock()
+ defer c.out.Unlock()
+
+ return c.writeRecordLocked(typ, data)
+}
+
+// readHandshake reads the next handshake message from
+// the record layer.
+func (c *Conn) readHandshake() (any, error) {
+ var data []byte
+ if c.extraConfig != nil && c.extraConfig.AlternativeRecordLayer != nil {
+ var err error
+ data, err = c.extraConfig.AlternativeRecordLayer.ReadHandshakeMessage()
+ if err != nil {
+ return nil, err
+ }
+ } else {
+ for c.hand.Len() < 4 {
+ if err := c.readRecord(); err != nil {
+ return nil, err
+ }
+ }
+
+ data = c.hand.Bytes()
+ n := int(data[1])<<16 | int(data[2])<<8 | int(data[3])
+ if n > maxHandshake {
+ c.sendAlertLocked(alertInternalError)
+ return nil, c.in.setErrorLocked(fmt.Errorf("tls: handshake message of length %d bytes exceeds maximum of %d bytes", n, maxHandshake))
+ }
+ for c.hand.Len() < 4+n {
+ if err := c.readRecord(); err != nil {
+ return nil, err
+ }
+ }
+ data = c.hand.Next(4 + n)
+ }
+ var m handshakeMessage
+ switch data[0] {
+ case typeHelloRequest:
+ m = new(helloRequestMsg)
+ case typeClientHello:
+ m = new(clientHelloMsg)
+ case typeServerHello:
+ m = new(serverHelloMsg)
+ case typeNewSessionTicket:
+ if c.vers == VersionTLS13 {
+ m = new(newSessionTicketMsgTLS13)
+ } else {
+ m = new(newSessionTicketMsg)
+ }
+ case typeCertificate:
+ if c.vers == VersionTLS13 {
+ m = new(certificateMsgTLS13)
+ } else {
+ m = new(certificateMsg)
+ }
+ case typeCertificateRequest:
+ if c.vers == VersionTLS13 {
+ m = new(certificateRequestMsgTLS13)
+ } else {
+ m = &certificateRequestMsg{
+ hasSignatureAlgorithm: c.vers >= VersionTLS12,
+ }
+ }
+ case typeCertificateStatus:
+ m = new(certificateStatusMsg)
+ case typeServerKeyExchange:
+ m = new(serverKeyExchangeMsg)
+ case typeServerHelloDone:
+ m = new(serverHelloDoneMsg)
+ case typeClientKeyExchange:
+ m = new(clientKeyExchangeMsg)
+ case typeCertificateVerify:
+ m = &certificateVerifyMsg{
+ hasSignatureAlgorithm: c.vers >= VersionTLS12,
+ }
+ case typeFinished:
+ m = new(finishedMsg)
+ case typeEncryptedExtensions:
+ m = new(encryptedExtensionsMsg)
+ case typeEndOfEarlyData:
+ m = new(endOfEarlyDataMsg)
+ case typeKeyUpdate:
+ m = new(keyUpdateMsg)
+ default:
+ return nil, c.in.setErrorLocked(c.sendAlert(alertUnexpectedMessage))
+ }
+
+ // The handshake message unmarshalers
+ // expect to be able to keep references to data,
+ // so pass in a fresh copy that won't be overwritten.
+ data = append([]byte(nil), data...)
+
+ if !m.unmarshal(data) {
+ return nil, c.in.setErrorLocked(c.sendAlert(alertUnexpectedMessage))
+ }
+ return m, nil
+}
+
+var (
+ errShutdown = errors.New("tls: protocol is shutdown")
+)
+
+// Write writes data to the connection.
+//
+// As Write calls Handshake, in order to prevent indefinite blocking a deadline
+// must be set for both Read and Write before Write is called when the handshake
+// has not yet completed. See SetDeadline, SetReadDeadline, and
+// SetWriteDeadline.
+func (c *Conn) Write(b []byte) (int, error) {
+ // interlock with Close below
+ for {
+ x := atomic.LoadInt32(&c.activeCall)
+ if x&1 != 0 {
+ return 0, net.ErrClosed
+ }
+ if atomic.CompareAndSwapInt32(&c.activeCall, x, x+2) {
+ break
+ }
+ }
+ defer atomic.AddInt32(&c.activeCall, -2)
+
+ if err := c.Handshake(); err != nil {
+ return 0, err
+ }
+
+ c.out.Lock()
+ defer c.out.Unlock()
+
+ if err := c.out.err; err != nil {
+ return 0, err
+ }
+
+ if !c.handshakeComplete() {
+ return 0, alertInternalError
+ }
+
+ if c.closeNotifySent {
+ return 0, errShutdown
+ }
+
+ // TLS 1.0 is susceptible to a chosen-plaintext
+ // attack when using block mode ciphers due to predictable IVs.
+ // This can be prevented by splitting each Application Data
+ // record into two records, effectively randomizing the IV.
+ //
+ // https://www.openssl.org/~bodo/tls-cbc.txt
+ // https://bugzilla.mozilla.org/show_bug.cgi?id=665814
+ // https://www.imperialviolet.org/2012/01/15/beastfollowup.html
+
+ var m int
+ if len(b) > 1 && c.vers == VersionTLS10 {
+ if _, ok := c.out.cipher.(cipher.BlockMode); ok {
+ n, err := c.writeRecordLocked(recordTypeApplicationData, b[:1])
+ if err != nil {
+ return n, c.out.setErrorLocked(err)
+ }
+ m, b = 1, b[1:]
+ }
+ }
+
+ n, err := c.writeRecordLocked(recordTypeApplicationData, b)
+ return n + m, c.out.setErrorLocked(err)
+}
+
+// handleRenegotiation processes a HelloRequest handshake message.
+func (c *Conn) handleRenegotiation() error {
+ if c.vers == VersionTLS13 {
+ return errors.New("tls: internal error: unexpected renegotiation")
+ }
+
+ msg, err := c.readHandshake()
+ if err != nil {
+ return err
+ }
+
+ helloReq, ok := msg.(*helloRequestMsg)
+ if !ok {
+ c.sendAlert(alertUnexpectedMessage)
+ return unexpectedMessageError(helloReq, msg)
+ }
+
+ if !c.isClient {
+ return c.sendAlert(alertNoRenegotiation)
+ }
+
+ switch c.config.Renegotiation {
+ case RenegotiateNever:
+ return c.sendAlert(alertNoRenegotiation)
+ case RenegotiateOnceAsClient:
+ if c.handshakes > 1 {
+ return c.sendAlert(alertNoRenegotiation)
+ }
+ case RenegotiateFreelyAsClient:
+ // Ok.
+ default:
+ c.sendAlert(alertInternalError)
+ return errors.New("tls: unknown Renegotiation value")
+ }
+
+ c.handshakeMutex.Lock()
+ defer c.handshakeMutex.Unlock()
+
+ atomic.StoreUint32(&c.handshakeStatus, 0)
+ if c.handshakeErr = c.clientHandshake(context.Background()); c.handshakeErr == nil {
+ c.handshakes++
+ }
+ return c.handshakeErr
+}
+
+func (c *Conn) HandlePostHandshakeMessage() error {
+ return c.handlePostHandshakeMessage()
+}
+
+// handlePostHandshakeMessage processes a handshake message arrived after the
+// handshake is complete. Up to TLS 1.2, it indicates the start of a renegotiation.
+func (c *Conn) handlePostHandshakeMessage() error {
+ if c.vers != VersionTLS13 {
+ return c.handleRenegotiation()
+ }
+
+ msg, err := c.readHandshake()
+ if err != nil {
+ return err
+ }
+
+ c.retryCount++
+ if c.retryCount > maxUselessRecords {
+ c.sendAlert(alertUnexpectedMessage)
+ return c.in.setErrorLocked(errors.New("tls: too many non-advancing records"))
+ }
+
+ switch msg := msg.(type) {
+ case *newSessionTicketMsgTLS13:
+ return c.handleNewSessionTicket(msg)
+ case *keyUpdateMsg:
+ return c.handleKeyUpdate(msg)
+ default:
+ c.sendAlert(alertUnexpectedMessage)
+ return fmt.Errorf("tls: received unexpected handshake message of type %T", msg)
+ }
+}
+
+func (c *Conn) handleKeyUpdate(keyUpdate *keyUpdateMsg) error {
+ cipherSuite := cipherSuiteTLS13ByID(c.cipherSuite)
+ if cipherSuite == nil {
+ return c.in.setErrorLocked(c.sendAlert(alertInternalError))
+ }
+
+ newSecret := cipherSuite.nextTrafficSecret(c.in.trafficSecret)
+ c.in.setTrafficSecret(cipherSuite, newSecret)
+
+ if keyUpdate.updateRequested {
+ c.out.Lock()
+ defer c.out.Unlock()
+
+ msg := &keyUpdateMsg{}
+ _, err := c.writeRecordLocked(recordTypeHandshake, msg.marshal())
+ if err != nil {
+ // Surface the error at the next write.
+ c.out.setErrorLocked(err)
+ return nil
+ }
+
+ newSecret := cipherSuite.nextTrafficSecret(c.out.trafficSecret)
+ c.out.setTrafficSecret(cipherSuite, newSecret)
+ }
+
+ return nil
+}
+
+// Read reads data from the connection.
+//
+// As Read calls Handshake, in order to prevent indefinite blocking a deadline
+// must be set for both Read and Write before Read is called when the handshake
+// has not yet completed. See SetDeadline, SetReadDeadline, and
+// SetWriteDeadline.
+func (c *Conn) Read(b []byte) (int, error) {
+ if err := c.Handshake(); err != nil {
+ return 0, err
+ }
+ if len(b) == 0 {
+ // Put this after Handshake, in case people were calling
+ // Read(nil) for the side effect of the Handshake.
+ return 0, nil
+ }
+
+ c.in.Lock()
+ defer c.in.Unlock()
+
+ for c.input.Len() == 0 {
+ if err := c.readRecord(); err != nil {
+ return 0, err
+ }
+ for c.hand.Len() > 0 {
+ if err := c.handlePostHandshakeMessage(); err != nil {
+ return 0, err
+ }
+ }
+ }
+
+ n, _ := c.input.Read(b)
+
+ // If a close-notify alert is waiting, read it so that we can return (n,
+ // EOF) instead of (n, nil), to signal to the HTTP response reading
+ // goroutine that the connection is now closed. This eliminates a race
+ // where the HTTP response reading goroutine would otherwise not observe
+ // the EOF until its next read, by which time a client goroutine might
+ // have already tried to reuse the HTTP connection for a new request.
+ // See https://golang.org/cl/76400046 and https://golang.org/issue/3514
+ if n != 0 && c.input.Len() == 0 && c.rawInput.Len() > 0 &&
+ recordType(c.rawInput.Bytes()[0]) == recordTypeAlert {
+ if err := c.readRecord(); err != nil {
+ return n, err // will be io.EOF on closeNotify
+ }
+ }
+
+ return n, nil
+}
+
+// Close closes the connection.
+func (c *Conn) Close() error {
+ // Interlock with Conn.Write above.
+ var x int32
+ for {
+ x = atomic.LoadInt32(&c.activeCall)
+ if x&1 != 0 {
+ return net.ErrClosed
+ }
+ if atomic.CompareAndSwapInt32(&c.activeCall, x, x|1) {
+ break
+ }
+ }
+ if x != 0 {
+ // io.Writer and io.Closer should not be used concurrently.
+ // If Close is called while a Write is currently in-flight,
+ // interpret that as a sign that this Close is really just
+ // being used to break the Write and/or clean up resources and
+ // avoid sending the alertCloseNotify, which may block
+ // waiting on handshakeMutex or the c.out mutex.
+ return c.conn.Close()
+ }
+
+ var alertErr error
+ if c.handshakeComplete() {
+ if err := c.closeNotify(); err != nil {
+ alertErr = fmt.Errorf("tls: failed to send closeNotify alert (but connection was closed anyway): %w", err)
+ }
+ }
+
+ if err := c.conn.Close(); err != nil {
+ return err
+ }
+ return alertErr
+}
+
+var errEarlyCloseWrite = errors.New("tls: CloseWrite called before handshake complete")
+
+// CloseWrite shuts down the writing side of the connection. It should only be
+// called once the handshake has completed and does not call CloseWrite on the
+// underlying connection. Most callers should just use Close.
+func (c *Conn) CloseWrite() error {
+ if !c.handshakeComplete() {
+ return errEarlyCloseWrite
+ }
+
+ return c.closeNotify()
+}
+
+func (c *Conn) closeNotify() error {
+ c.out.Lock()
+ defer c.out.Unlock()
+
+ if !c.closeNotifySent {
+ // Set a Write Deadline to prevent possibly blocking forever.
+ c.SetWriteDeadline(time.Now().Add(time.Second * 5))
+ c.closeNotifyErr = c.sendAlertLocked(alertCloseNotify)
+ c.closeNotifySent = true
+ // Any subsequent writes will fail.
+ c.SetWriteDeadline(time.Now())
+ }
+ return c.closeNotifyErr
+}
+
+// Handshake runs the client or server handshake
+// protocol if it has not yet been run.
+//
+// Most uses of this package need not call Handshake explicitly: the
+// first Read or Write will call it automatically.
+//
+// For control over canceling or setting a timeout on a handshake, use
+// HandshakeContext or the Dialer's DialContext method instead.
+func (c *Conn) Handshake() error {
+ return c.HandshakeContext(context.Background())
+}
+
+// HandshakeContext runs the client or server handshake
+// protocol if it has not yet been run.
+//
+// The provided Context must be non-nil. If the context is canceled before
+// the handshake is complete, the handshake is interrupted and an error is returned.
+// Once the handshake has completed, cancellation of the context will not affect the
+// connection.
+//
+// Most uses of this package need not call HandshakeContext explicitly: the
+// first Read or Write will call it automatically.
+func (c *Conn) HandshakeContext(ctx context.Context) error {
+ // Delegate to unexported method for named return
+ // without confusing documented signature.
+ return c.handshakeContext(ctx)
+}
+
+func (c *Conn) handshakeContext(ctx context.Context) (ret error) {
+ // Fast sync/atomic-based exit if there is no handshake in flight and the
+ // last one succeeded without an error. Avoids the expensive context setup
+ // and mutex for most Read and Write calls.
+ if c.handshakeComplete() {
+ return nil
+ }
+
+ handshakeCtx, cancel := context.WithCancel(ctx)
+ // Note: defer this before starting the "interrupter" goroutine
+ // so that we can tell the difference between the input being canceled and
+ // this cancellation. In the former case, we need to close the connection.
+ defer cancel()
+
+ // Start the "interrupter" goroutine, if this context might be canceled.
+ // (The background context cannot).
+ //
+ // The interrupter goroutine waits for the input context to be done and
+ // closes the connection if this happens before the function returns.
+ if ctx.Done() != nil {
+ done := make(chan struct{})
+ interruptRes := make(chan error, 1)
+ defer func() {
+ close(done)
+ if ctxErr := <-interruptRes; ctxErr != nil {
+ // Return context error to user.
+ ret = ctxErr
+ }
+ }()
+ go func() {
+ select {
+ case <-handshakeCtx.Done():
+ // Close the connection, discarding the error
+ _ = c.conn.Close()
+ interruptRes <- handshakeCtx.Err()
+ case <-done:
+ interruptRes <- nil
+ }
+ }()
+ }
+
+ c.handshakeMutex.Lock()
+ defer c.handshakeMutex.Unlock()
+
+ if err := c.handshakeErr; err != nil {
+ return err
+ }
+ if c.handshakeComplete() {
+ return nil
+ }
+
+ c.in.Lock()
+ defer c.in.Unlock()
+
+ c.handshakeErr = c.handshakeFn(handshakeCtx)
+ if c.handshakeErr == nil {
+ c.handshakes++
+ } else {
+ // If an error occurred during the handshake try to flush the
+ // alert that might be left in the buffer.
+ c.flush()
+ }
+
+ if c.handshakeErr == nil && !c.handshakeComplete() {
+ c.handshakeErr = errors.New("tls: internal error: handshake should have had a result")
+ }
+ if c.handshakeErr != nil && c.handshakeComplete() {
+ panic("tls: internal error: handshake returned an error but is marked successful")
+ }
+
+ return c.handshakeErr
+}
+
+// ConnectionState returns basic TLS details about the connection.
+func (c *Conn) ConnectionState() ConnectionState {
+ c.connStateMutex.Lock()
+ defer c.connStateMutex.Unlock()
+ return c.connState.ConnectionState
+}
+
+// ConnectionStateWith0RTT returns basic TLS details (incl. 0-RTT status) about the connection.
+func (c *Conn) ConnectionStateWith0RTT() ConnectionStateWith0RTT {
+ c.connStateMutex.Lock()
+ defer c.connStateMutex.Unlock()
+ return c.connState
+}
+
+func (c *Conn) connectionStateLocked() ConnectionState {
+ var state connectionState
+ state.HandshakeComplete = c.handshakeComplete()
+ state.Version = c.vers
+ state.NegotiatedProtocol = c.clientProtocol
+ state.DidResume = c.didResume
+ state.NegotiatedProtocolIsMutual = true
+ state.ServerName = c.serverName
+ state.CipherSuite = c.cipherSuite
+ state.PeerCertificates = c.peerCertificates
+ state.VerifiedChains = c.verifiedChains
+ state.SignedCertificateTimestamps = c.scts
+ state.OCSPResponse = c.ocspResponse
+ if !c.didResume && c.vers != VersionTLS13 {
+ if c.clientFinishedIsFirst {
+ state.TLSUnique = c.clientFinished[:]
+ } else {
+ state.TLSUnique = c.serverFinished[:]
+ }
+ }
+ if c.config.Renegotiation != RenegotiateNever {
+ state.ekm = noExportedKeyingMaterial
+ } else {
+ state.ekm = c.ekm
+ }
+ return toConnectionState(state)
+}
+
+func (c *Conn) updateConnectionState() {
+ c.connStateMutex.Lock()
+ defer c.connStateMutex.Unlock()
+ c.connState = ConnectionStateWith0RTT{
+ Used0RTT: c.used0RTT,
+ ConnectionState: c.connectionStateLocked(),
+ }
+}
+
+// OCSPResponse returns the stapled OCSP response from the TLS server, if
+// any. (Only valid for client connections.)
+func (c *Conn) OCSPResponse() []byte {
+ c.handshakeMutex.Lock()
+ defer c.handshakeMutex.Unlock()
+
+ return c.ocspResponse
+}
+
+// VerifyHostname checks that the peer certificate chain is valid for
+// connecting to host. If so, it returns nil; if not, it returns an error
+// describing the problem.
+func (c *Conn) VerifyHostname(host string) error {
+ c.handshakeMutex.Lock()
+ defer c.handshakeMutex.Unlock()
+ if !c.isClient {
+ return errors.New("tls: VerifyHostname called on TLS server connection")
+ }
+ if !c.handshakeComplete() {
+ return errors.New("tls: handshake has not yet been performed")
+ }
+ if len(c.verifiedChains) == 0 {
+ return errors.New("tls: handshake did not verify certificate chain")
+ }
+ return c.peerCertificates[0].VerifyHostname(host)
+}
+
+func (c *Conn) handshakeComplete() bool {
+ return atomic.LoadUint32(&c.handshakeStatus) == 1
+}
diff --git a/vendor/github.com/quic-go/qtls-go1-19/cpu.go b/vendor/github.com/quic-go/qtls-go1-19/cpu.go
new file mode 100644
index 0000000000..1219450879
--- /dev/null
+++ b/vendor/github.com/quic-go/qtls-go1-19/cpu.go
@@ -0,0 +1,22 @@
+//go:build !js
+// +build !js
+
+package qtls
+
+import (
+ "runtime"
+
+ "golang.org/x/sys/cpu"
+)
+
+var (
+ hasGCMAsmAMD64 = cpu.X86.HasAES && cpu.X86.HasPCLMULQDQ
+ hasGCMAsmARM64 = cpu.ARM64.HasAES && cpu.ARM64.HasPMULL
+ // Keep in sync with crypto/aes/cipher_s390x.go.
+ hasGCMAsmS390X = cpu.S390X.HasAES && cpu.S390X.HasAESCBC && cpu.S390X.HasAESCTR &&
+ (cpu.S390X.HasGHASH || cpu.S390X.HasAESGCM)
+
+ hasAESGCMHardwareSupport = runtime.GOARCH == "amd64" && hasGCMAsmAMD64 ||
+ runtime.GOARCH == "arm64" && hasGCMAsmARM64 ||
+ runtime.GOARCH == "s390x" && hasGCMAsmS390X
+)
diff --git a/vendor/github.com/quic-go/qtls-go1-19/cpu_other.go b/vendor/github.com/quic-go/qtls-go1-19/cpu_other.go
new file mode 100644
index 0000000000..33f7d21942
--- /dev/null
+++ b/vendor/github.com/quic-go/qtls-go1-19/cpu_other.go
@@ -0,0 +1,12 @@
+//go:build js
+// +build js
+
+package qtls
+
+var (
+ hasGCMAsmAMD64 = false
+ hasGCMAsmARM64 = false
+ hasGCMAsmS390X = false
+
+ hasAESGCMHardwareSupport = false
+)
diff --git a/vendor/github.com/quic-go/qtls-go1-19/handshake_client.go b/vendor/github.com/quic-go/qtls-go1-19/handshake_client.go
new file mode 100644
index 0000000000..d373b88684
--- /dev/null
+++ b/vendor/github.com/quic-go/qtls-go1-19/handshake_client.go
@@ -0,0 +1,1118 @@
+// Copyright 2009 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package qtls
+
+import (
+ "bytes"
+ "context"
+ "crypto"
+ "crypto/ecdsa"
+ "crypto/ed25519"
+ "crypto/rsa"
+ "crypto/subtle"
+ "crypto/x509"
+ "errors"
+ "fmt"
+ "hash"
+ "io"
+ "net"
+ "strings"
+ "sync/atomic"
+ "time"
+
+ "golang.org/x/crypto/cryptobyte"
+)
+
+const clientSessionStateVersion = 1
+
+type clientHandshakeState struct {
+ c *Conn
+ ctx context.Context
+ serverHello *serverHelloMsg
+ hello *clientHelloMsg
+ suite *cipherSuite
+ finishedHash finishedHash
+ masterSecret []byte
+ session *clientSessionState
+}
+
+var testingOnlyForceClientHelloSignatureAlgorithms []SignatureScheme
+
+func (c *Conn) makeClientHello() (*clientHelloMsg, ecdheParameters, error) {
+ config := c.config
+ if len(config.ServerName) == 0 && !config.InsecureSkipVerify {
+ return nil, nil, errors.New("tls: either ServerName or InsecureSkipVerify must be specified in the tls.Config")
+ }
+
+ nextProtosLength := 0
+ for _, proto := range config.NextProtos {
+ if l := len(proto); l == 0 || l > 255 {
+ return nil, nil, errors.New("tls: invalid NextProtos value")
+ } else {
+ nextProtosLength += 1 + l
+ }
+ }
+ if nextProtosLength > 0xffff {
+ return nil, nil, errors.New("tls: NextProtos values too large")
+ }
+
+ var supportedVersions []uint16
+ var clientHelloVersion uint16
+ if c.extraConfig.usesAlternativeRecordLayer() {
+ if config.maxSupportedVersion(roleClient) < VersionTLS13 {
+ return nil, nil, errors.New("tls: MaxVersion prevents QUIC from using TLS 1.3")
+ }
+ // Only offer TLS 1.3 when QUIC is used.
+ supportedVersions = []uint16{VersionTLS13}
+ clientHelloVersion = VersionTLS13
+ } else {
+ supportedVersions = config.supportedVersions(roleClient)
+ if len(supportedVersions) == 0 {
+ return nil, nil, errors.New("tls: no supported versions satisfy MinVersion and MaxVersion")
+ }
+ clientHelloVersion = config.maxSupportedVersion(roleClient)
+ }
+
+ // The version at the beginning of the ClientHello was capped at TLS 1.2
+ // for compatibility reasons. The supported_versions extension is used
+ // to negotiate versions now. See RFC 8446, Section 4.2.1.
+ if clientHelloVersion > VersionTLS12 {
+ clientHelloVersion = VersionTLS12
+ }
+
+ hello := &clientHelloMsg{
+ vers: clientHelloVersion,
+ compressionMethods: []uint8{compressionNone},
+ random: make([]byte, 32),
+ ocspStapling: true,
+ scts: true,
+ serverName: hostnameInSNI(config.ServerName),
+ supportedCurves: config.curvePreferences(),
+ supportedPoints: []uint8{pointFormatUncompressed},
+ secureRenegotiationSupported: true,
+ alpnProtocols: config.NextProtos,
+ supportedVersions: supportedVersions,
+ }
+
+ if c.handshakes > 0 {
+ hello.secureRenegotiation = c.clientFinished[:]
+ }
+
+ preferenceOrder := cipherSuitesPreferenceOrder
+ if !hasAESGCMHardwareSupport {
+ preferenceOrder = cipherSuitesPreferenceOrderNoAES
+ }
+ configCipherSuites := config.cipherSuites()
+ hello.cipherSuites = make([]uint16, 0, len(configCipherSuites))
+
+ for _, suiteId := range preferenceOrder {
+ suite := mutualCipherSuite(configCipherSuites, suiteId)
+ if suite == nil {
+ continue
+ }
+ // Don't advertise TLS 1.2-only cipher suites unless
+ // we're attempting TLS 1.2.
+ if hello.vers < VersionTLS12 && suite.flags&suiteTLS12 != 0 {
+ continue
+ }
+ hello.cipherSuites = append(hello.cipherSuites, suiteId)
+ }
+
+ _, err := io.ReadFull(config.rand(), hello.random)
+ if err != nil {
+ return nil, nil, errors.New("tls: short read from Rand: " + err.Error())
+ }
+
+ // A random session ID is used to detect when the server accepted a ticket
+ // and is resuming a session (see RFC 5077). In TLS 1.3, it's always set as
+ // a compatibility measure (see RFC 8446, Section 4.1.2).
+ if c.extraConfig == nil || c.extraConfig.AlternativeRecordLayer == nil {
+ hello.sessionId = make([]byte, 32)
+ if _, err := io.ReadFull(config.rand(), hello.sessionId); err != nil {
+ return nil, nil, errors.New("tls: short read from Rand: " + err.Error())
+ }
+ }
+
+ if hello.vers >= VersionTLS12 {
+ hello.supportedSignatureAlgorithms = supportedSignatureAlgorithms()
+ }
+ if testingOnlyForceClientHelloSignatureAlgorithms != nil {
+ hello.supportedSignatureAlgorithms = testingOnlyForceClientHelloSignatureAlgorithms
+ }
+
+ var params ecdheParameters
+ if hello.supportedVersions[0] == VersionTLS13 {
+ var suites []uint16
+ for _, suiteID := range configCipherSuites {
+ for _, suite := range cipherSuitesTLS13 {
+ if suite.id == suiteID {
+ suites = append(suites, suiteID)
+ }
+ }
+ }
+ if len(suites) > 0 {
+ hello.cipherSuites = suites
+ } else {
+ if hasAESGCMHardwareSupport {
+ hello.cipherSuites = append(hello.cipherSuites, defaultCipherSuitesTLS13...)
+ } else {
+ hello.cipherSuites = append(hello.cipherSuites, defaultCipherSuitesTLS13NoAES...)
+ }
+ }
+
+ curveID := config.curvePreferences()[0]
+ if _, ok := curveForCurveID(curveID); curveID != X25519 && !ok {
+ return nil, nil, errors.New("tls: CurvePreferences includes unsupported curve")
+ }
+ params, err = generateECDHEParameters(config.rand(), curveID)
+ if err != nil {
+ return nil, nil, err
+ }
+ hello.keyShares = []keyShare{{group: curveID, data: params.PublicKey()}}
+ }
+
+ if hello.supportedVersions[0] == VersionTLS13 && c.extraConfig != nil && c.extraConfig.GetExtensions != nil {
+ hello.additionalExtensions = c.extraConfig.GetExtensions(typeClientHello)
+ }
+
+ return hello, params, nil
+}
+
+func (c *Conn) clientHandshake(ctx context.Context) (err error) {
+ if c.config == nil {
+ c.config = fromConfig(defaultConfig())
+ }
+ c.setAlternativeRecordLayer()
+
+ // This may be a renegotiation handshake, in which case some fields
+ // need to be reset.
+ c.didResume = false
+
+ hello, ecdheParams, err := c.makeClientHello()
+ if err != nil {
+ return err
+ }
+ c.serverName = hello.serverName
+
+ cacheKey, session, earlySecret, binderKey := c.loadSession(hello)
+ if cacheKey != "" && session != nil {
+ var deletedTicket bool
+ if session.vers == VersionTLS13 && hello.earlyData && c.extraConfig != nil && c.extraConfig.Enable0RTT {
+ // don't reuse a session ticket that enabled 0-RTT
+ c.config.ClientSessionCache.Put(cacheKey, nil)
+ deletedTicket = true
+
+ if suite := cipherSuiteTLS13ByID(session.cipherSuite); suite != nil {
+ h := suite.hash.New()
+ h.Write(hello.marshal())
+ clientEarlySecret := suite.deriveSecret(earlySecret, "c e traffic", h)
+ c.out.exportKey(Encryption0RTT, suite, clientEarlySecret)
+ if err := c.config.writeKeyLog(keyLogLabelEarlyTraffic, hello.random, clientEarlySecret); err != nil {
+ c.sendAlert(alertInternalError)
+ return err
+ }
+ }
+ }
+ if !deletedTicket {
+ defer func() {
+ // If we got a handshake failure when resuming a session, throw away
+ // the session ticket. See RFC 5077, Section 3.2.
+ //
+ // RFC 8446 makes no mention of dropping tickets on failure, but it
+ // does require servers to abort on invalid binders, so we need to
+ // delete tickets to recover from a corrupted PSK.
+ if err != nil {
+ c.config.ClientSessionCache.Put(cacheKey, nil)
+ }
+ }()
+ }
+ }
+
+ if _, err := c.writeRecord(recordTypeHandshake, hello.marshal()); err != nil {
+ return err
+ }
+
+ msg, err := c.readHandshake()
+ if err != nil {
+ return err
+ }
+
+ serverHello, ok := msg.(*serverHelloMsg)
+ if !ok {
+ c.sendAlert(alertUnexpectedMessage)
+ return unexpectedMessageError(serverHello, msg)
+ }
+
+ if err := c.pickTLSVersion(serverHello); err != nil {
+ return err
+ }
+
+ // If we are negotiating a protocol version that's lower than what we
+ // support, check for the server downgrade canaries.
+ // See RFC 8446, Section 4.1.3.
+ maxVers := c.config.maxSupportedVersion(roleClient)
+ tls12Downgrade := string(serverHello.random[24:]) == downgradeCanaryTLS12
+ tls11Downgrade := string(serverHello.random[24:]) == downgradeCanaryTLS11
+ if maxVers == VersionTLS13 && c.vers <= VersionTLS12 && (tls12Downgrade || tls11Downgrade) ||
+ maxVers == VersionTLS12 && c.vers <= VersionTLS11 && tls11Downgrade {
+ c.sendAlert(alertIllegalParameter)
+ return errors.New("tls: downgrade attempt detected, possibly due to a MitM attack or a broken middlebox")
+ }
+
+ if c.vers == VersionTLS13 {
+ hs := &clientHandshakeStateTLS13{
+ c: c,
+ ctx: ctx,
+ serverHello: serverHello,
+ hello: hello,
+ ecdheParams: ecdheParams,
+ session: session,
+ earlySecret: earlySecret,
+ binderKey: binderKey,
+ }
+
+ // In TLS 1.3, session tickets are delivered after the handshake.
+ return hs.handshake()
+ }
+
+ hs := &clientHandshakeState{
+ c: c,
+ ctx: ctx,
+ serverHello: serverHello,
+ hello: hello,
+ session: session,
+ }
+
+ if err := hs.handshake(); err != nil {
+ return err
+ }
+
+ // If we had a successful handshake and hs.session is different from
+ // the one already cached - cache a new one.
+ if cacheKey != "" && hs.session != nil && session != hs.session {
+ c.config.ClientSessionCache.Put(cacheKey, toClientSessionState(hs.session))
+ }
+
+ c.updateConnectionState()
+ return nil
+}
+
+// extract the app data saved in the session.nonce,
+// and set the session.nonce to the actual nonce value
+func (c *Conn) decodeSessionState(session *clientSessionState) (uint32 /* max early data */, []byte /* app data */, bool /* ok */) {
+ s := cryptobyte.String(session.nonce)
+ var version uint16
+ if !s.ReadUint16(&version) {
+ return 0, nil, false
+ }
+ if version != clientSessionStateVersion {
+ return 0, nil, false
+ }
+ var maxEarlyData uint32
+ if !s.ReadUint32(&maxEarlyData) {
+ return 0, nil, false
+ }
+ var appData []byte
+ if !readUint16LengthPrefixed(&s, &appData) {
+ return 0, nil, false
+ }
+ var nonce []byte
+ if !readUint16LengthPrefixed(&s, &nonce) {
+ return 0, nil, false
+ }
+ session.nonce = nonce
+ return maxEarlyData, appData, true
+}
+
+func (c *Conn) loadSession(hello *clientHelloMsg) (cacheKey string,
+ session *clientSessionState, earlySecret, binderKey []byte) {
+ if c.config.SessionTicketsDisabled || c.config.ClientSessionCache == nil {
+ return "", nil, nil, nil
+ }
+
+ hello.ticketSupported = true
+
+ if hello.supportedVersions[0] == VersionTLS13 {
+ // Require DHE on resumption as it guarantees forward secrecy against
+ // compromise of the session ticket key. See RFC 8446, Section 4.2.9.
+ hello.pskModes = []uint8{pskModeDHE}
+ }
+
+ // Session resumption is not allowed if renegotiating because
+ // renegotiation is primarily used to allow a client to send a client
+ // certificate, which would be skipped if session resumption occurred.
+ if c.handshakes != 0 {
+ return "", nil, nil, nil
+ }
+
+ // Try to resume a previously negotiated TLS session, if available.
+ cacheKey = clientSessionCacheKey(c.conn.RemoteAddr(), c.config)
+ sess, ok := c.config.ClientSessionCache.Get(cacheKey)
+ if !ok || sess == nil {
+ return cacheKey, nil, nil, nil
+ }
+ session = fromClientSessionState(sess)
+
+ var appData []byte
+ var maxEarlyData uint32
+ if session.vers == VersionTLS13 {
+ var ok bool
+ maxEarlyData, appData, ok = c.decodeSessionState(session)
+ if !ok { // delete it, if parsing failed
+ c.config.ClientSessionCache.Put(cacheKey, nil)
+ return cacheKey, nil, nil, nil
+ }
+ }
+
+ // Check that version used for the previous session is still valid.
+ versOk := false
+ for _, v := range hello.supportedVersions {
+ if v == session.vers {
+ versOk = true
+ break
+ }
+ }
+ if !versOk {
+ return cacheKey, nil, nil, nil
+ }
+
+ // Check that the cached server certificate is not expired, and that it's
+ // valid for the ServerName. This should be ensured by the cache key, but
+ // protect the application from a faulty ClientSessionCache implementation.
+ if !c.config.InsecureSkipVerify {
+ if len(session.verifiedChains) == 0 {
+ // The original connection had InsecureSkipVerify, while this doesn't.
+ return cacheKey, nil, nil, nil
+ }
+ serverCert := session.serverCertificates[0]
+ if c.config.time().After(serverCert.NotAfter) {
+ // Expired certificate, delete the entry.
+ c.config.ClientSessionCache.Put(cacheKey, nil)
+ return cacheKey, nil, nil, nil
+ }
+ if err := serverCert.VerifyHostname(c.config.ServerName); err != nil {
+ return cacheKey, nil, nil, nil
+ }
+ }
+
+ if session.vers != VersionTLS13 {
+ // In TLS 1.2 the cipher suite must match the resumed session. Ensure we
+ // are still offering it.
+ if mutualCipherSuite(hello.cipherSuites, session.cipherSuite) == nil {
+ return cacheKey, nil, nil, nil
+ }
+
+ hello.sessionTicket = session.sessionTicket
+ return
+ }
+
+ // Check that the session ticket is not expired.
+ if c.config.time().After(session.useBy) {
+ c.config.ClientSessionCache.Put(cacheKey, nil)
+ return cacheKey, nil, nil, nil
+ }
+
+ // In TLS 1.3 the KDF hash must match the resumed session. Ensure we
+ // offer at least one cipher suite with that hash.
+ cipherSuite := cipherSuiteTLS13ByID(session.cipherSuite)
+ if cipherSuite == nil {
+ return cacheKey, nil, nil, nil
+ }
+ cipherSuiteOk := false
+ for _, offeredID := range hello.cipherSuites {
+ offeredSuite := cipherSuiteTLS13ByID(offeredID)
+ if offeredSuite != nil && offeredSuite.hash == cipherSuite.hash {
+ cipherSuiteOk = true
+ break
+ }
+ }
+ if !cipherSuiteOk {
+ return cacheKey, nil, nil, nil
+ }
+
+ // Set the pre_shared_key extension. See RFC 8446, Section 4.2.11.1.
+ ticketAge := uint32(c.config.time().Sub(session.receivedAt) / time.Millisecond)
+ identity := pskIdentity{
+ label: session.sessionTicket,
+ obfuscatedTicketAge: ticketAge + session.ageAdd,
+ }
+ hello.pskIdentities = []pskIdentity{identity}
+ hello.pskBinders = [][]byte{make([]byte, cipherSuite.hash.Size())}
+
+ // Compute the PSK binders. See RFC 8446, Section 4.2.11.2.
+ psk := cipherSuite.expandLabel(session.masterSecret, "resumption",
+ session.nonce, cipherSuite.hash.Size())
+ earlySecret = cipherSuite.extract(psk, nil)
+ binderKey = cipherSuite.deriveSecret(earlySecret, resumptionBinderLabel, nil)
+ if c.extraConfig != nil {
+ hello.earlyData = c.extraConfig.Enable0RTT && maxEarlyData > 0
+ }
+ transcript := cipherSuite.hash.New()
+ transcript.Write(hello.marshalWithoutBinders())
+ pskBinders := [][]byte{cipherSuite.finishedHash(binderKey, transcript)}
+ hello.updateBinders(pskBinders)
+
+ if session.vers == VersionTLS13 && c.extraConfig != nil && c.extraConfig.SetAppDataFromSessionState != nil {
+ c.extraConfig.SetAppDataFromSessionState(appData)
+ }
+ return
+}
+
+func (c *Conn) pickTLSVersion(serverHello *serverHelloMsg) error {
+ peerVersion := serverHello.vers
+ if serverHello.supportedVersion != 0 {
+ peerVersion = serverHello.supportedVersion
+ }
+
+ vers, ok := c.config.mutualVersion(roleClient, []uint16{peerVersion})
+ if !ok {
+ c.sendAlert(alertProtocolVersion)
+ return fmt.Errorf("tls: server selected unsupported protocol version %x", peerVersion)
+ }
+
+ c.vers = vers
+ c.haveVers = true
+ c.in.version = vers
+ c.out.version = vers
+
+ return nil
+}
+
+// Does the handshake, either a full one or resumes old session. Requires hs.c,
+// hs.hello, hs.serverHello, and, optionally, hs.session to be set.
+func (hs *clientHandshakeState) handshake() error {
+ c := hs.c
+
+ isResume, err := hs.processServerHello()
+ if err != nil {
+ return err
+ }
+
+ hs.finishedHash = newFinishedHash(c.vers, hs.suite)
+
+ // No signatures of the handshake are needed in a resumption.
+ // Otherwise, in a full handshake, if we don't have any certificates
+ // configured then we will never send a CertificateVerify message and
+ // thus no signatures are needed in that case either.
+ if isResume || (len(c.config.Certificates) == 0 && c.config.GetClientCertificate == nil) {
+ hs.finishedHash.discardHandshakeBuffer()
+ }
+
+ hs.finishedHash.Write(hs.hello.marshal())
+ hs.finishedHash.Write(hs.serverHello.marshal())
+
+ c.buffering = true
+ c.didResume = isResume
+ if isResume {
+ if err := hs.establishKeys(); err != nil {
+ return err
+ }
+ if err := hs.readSessionTicket(); err != nil {
+ return err
+ }
+ if err := hs.readFinished(c.serverFinished[:]); err != nil {
+ return err
+ }
+ c.clientFinishedIsFirst = false
+ // Make sure the connection is still being verified whether or not this
+ // is a resumption. Resumptions currently don't reverify certificates so
+ // they don't call verifyServerCertificate. See Issue 31641.
+ if c.config.VerifyConnection != nil {
+ if err := c.config.VerifyConnection(c.connectionStateLocked()); err != nil {
+ c.sendAlert(alertBadCertificate)
+ return err
+ }
+ }
+ if err := hs.sendFinished(c.clientFinished[:]); err != nil {
+ return err
+ }
+ if _, err := c.flush(); err != nil {
+ return err
+ }
+ } else {
+ if err := hs.doFullHandshake(); err != nil {
+ return err
+ }
+ if err := hs.establishKeys(); err != nil {
+ return err
+ }
+ if err := hs.sendFinished(c.clientFinished[:]); err != nil {
+ return err
+ }
+ if _, err := c.flush(); err != nil {
+ return err
+ }
+ c.clientFinishedIsFirst = true
+ if err := hs.readSessionTicket(); err != nil {
+ return err
+ }
+ if err := hs.readFinished(c.serverFinished[:]); err != nil {
+ return err
+ }
+ }
+
+ c.ekm = ekmFromMasterSecret(c.vers, hs.suite, hs.masterSecret, hs.hello.random, hs.serverHello.random)
+ atomic.StoreUint32(&c.handshakeStatus, 1)
+
+ return nil
+}
+
+func (hs *clientHandshakeState) pickCipherSuite() error {
+ if hs.suite = mutualCipherSuite(hs.hello.cipherSuites, hs.serverHello.cipherSuite); hs.suite == nil {
+ hs.c.sendAlert(alertHandshakeFailure)
+ return errors.New("tls: server chose an unconfigured cipher suite")
+ }
+
+ hs.c.cipherSuite = hs.suite.id
+ return nil
+}
+
+func (hs *clientHandshakeState) doFullHandshake() error {
+ c := hs.c
+
+ msg, err := c.readHandshake()
+ if err != nil {
+ return err
+ }
+ certMsg, ok := msg.(*certificateMsg)
+ if !ok || len(certMsg.certificates) == 0 {
+ c.sendAlert(alertUnexpectedMessage)
+ return unexpectedMessageError(certMsg, msg)
+ }
+ hs.finishedHash.Write(certMsg.marshal())
+
+ msg, err = c.readHandshake()
+ if err != nil {
+ return err
+ }
+
+ cs, ok := msg.(*certificateStatusMsg)
+ if ok {
+ // RFC4366 on Certificate Status Request:
+ // The server MAY return a "certificate_status" message.
+
+ if !hs.serverHello.ocspStapling {
+ // If a server returns a "CertificateStatus" message, then the
+ // server MUST have included an extension of type "status_request"
+ // with empty "extension_data" in the extended server hello.
+
+ c.sendAlert(alertUnexpectedMessage)
+ return errors.New("tls: received unexpected CertificateStatus message")
+ }
+ hs.finishedHash.Write(cs.marshal())
+
+ c.ocspResponse = cs.response
+
+ msg, err = c.readHandshake()
+ if err != nil {
+ return err
+ }
+ }
+
+ if c.handshakes == 0 {
+ // If this is the first handshake on a connection, process and
+ // (optionally) verify the server's certificates.
+ if err := c.verifyServerCertificate(certMsg.certificates); err != nil {
+ return err
+ }
+ } else {
+ // This is a renegotiation handshake. We require that the
+ // server's identity (i.e. leaf certificate) is unchanged and
+ // thus any previous trust decision is still valid.
+ //
+ // See https://mitls.org/pages/attacks/3SHAKE for the
+ // motivation behind this requirement.
+ if !bytes.Equal(c.peerCertificates[0].Raw, certMsg.certificates[0]) {
+ c.sendAlert(alertBadCertificate)
+ return errors.New("tls: server's identity changed during renegotiation")
+ }
+ }
+
+ keyAgreement := hs.suite.ka(c.vers)
+
+ skx, ok := msg.(*serverKeyExchangeMsg)
+ if ok {
+ hs.finishedHash.Write(skx.marshal())
+ err = keyAgreement.processServerKeyExchange(c.config, hs.hello, hs.serverHello, c.peerCertificates[0], skx)
+ if err != nil {
+ c.sendAlert(alertUnexpectedMessage)
+ return err
+ }
+
+ msg, err = c.readHandshake()
+ if err != nil {
+ return err
+ }
+ }
+
+ var chainToSend *Certificate
+ var certRequested bool
+ certReq, ok := msg.(*certificateRequestMsg)
+ if ok {
+ certRequested = true
+ hs.finishedHash.Write(certReq.marshal())
+
+ cri := certificateRequestInfoFromMsg(hs.ctx, c.vers, certReq)
+ if chainToSend, err = c.getClientCertificate(cri); err != nil {
+ c.sendAlert(alertInternalError)
+ return err
+ }
+
+ msg, err = c.readHandshake()
+ if err != nil {
+ return err
+ }
+ }
+
+ shd, ok := msg.(*serverHelloDoneMsg)
+ if !ok {
+ c.sendAlert(alertUnexpectedMessage)
+ return unexpectedMessageError(shd, msg)
+ }
+ hs.finishedHash.Write(shd.marshal())
+
+ // If the server requested a certificate then we have to send a
+ // Certificate message, even if it's empty because we don't have a
+ // certificate to send.
+ if certRequested {
+ certMsg = new(certificateMsg)
+ certMsg.certificates = chainToSend.Certificate
+ hs.finishedHash.Write(certMsg.marshal())
+ if _, err := c.writeRecord(recordTypeHandshake, certMsg.marshal()); err != nil {
+ return err
+ }
+ }
+
+ preMasterSecret, ckx, err := keyAgreement.generateClientKeyExchange(c.config, hs.hello, c.peerCertificates[0])
+ if err != nil {
+ c.sendAlert(alertInternalError)
+ return err
+ }
+ if ckx != nil {
+ hs.finishedHash.Write(ckx.marshal())
+ if _, err := c.writeRecord(recordTypeHandshake, ckx.marshal()); err != nil {
+ return err
+ }
+ }
+
+ if chainToSend != nil && len(chainToSend.Certificate) > 0 {
+ certVerify := &certificateVerifyMsg{}
+
+ key, ok := chainToSend.PrivateKey.(crypto.Signer)
+ if !ok {
+ c.sendAlert(alertInternalError)
+ return fmt.Errorf("tls: client certificate private key of type %T does not implement crypto.Signer", chainToSend.PrivateKey)
+ }
+
+ var sigType uint8
+ var sigHash crypto.Hash
+ if c.vers >= VersionTLS12 {
+ signatureAlgorithm, err := selectSignatureScheme(c.vers, chainToSend, certReq.supportedSignatureAlgorithms)
+ if err != nil {
+ c.sendAlert(alertIllegalParameter)
+ return err
+ }
+ sigType, sigHash, err = typeAndHashFromSignatureScheme(signatureAlgorithm)
+ if err != nil {
+ return c.sendAlert(alertInternalError)
+ }
+ certVerify.hasSignatureAlgorithm = true
+ certVerify.signatureAlgorithm = signatureAlgorithm
+ } else {
+ sigType, sigHash, err = legacyTypeAndHashFromPublicKey(key.Public())
+ if err != nil {
+ c.sendAlert(alertIllegalParameter)
+ return err
+ }
+ }
+
+ signed := hs.finishedHash.hashForClientCertificate(sigType, sigHash, hs.masterSecret)
+ signOpts := crypto.SignerOpts(sigHash)
+ if sigType == signatureRSAPSS {
+ signOpts = &rsa.PSSOptions{SaltLength: rsa.PSSSaltLengthEqualsHash, Hash: sigHash}
+ }
+ certVerify.signature, err = key.Sign(c.config.rand(), signed, signOpts)
+ if err != nil {
+ c.sendAlert(alertInternalError)
+ return err
+ }
+
+ hs.finishedHash.Write(certVerify.marshal())
+ if _, err := c.writeRecord(recordTypeHandshake, certVerify.marshal()); err != nil {
+ return err
+ }
+ }
+
+ hs.masterSecret = masterFromPreMasterSecret(c.vers, hs.suite, preMasterSecret, hs.hello.random, hs.serverHello.random)
+ if err := c.config.writeKeyLog(keyLogLabelTLS12, hs.hello.random, hs.masterSecret); err != nil {
+ c.sendAlert(alertInternalError)
+ return errors.New("tls: failed to write to key log: " + err.Error())
+ }
+
+ hs.finishedHash.discardHandshakeBuffer()
+
+ return nil
+}
+
+func (hs *clientHandshakeState) establishKeys() error {
+ c := hs.c
+
+ clientMAC, serverMAC, clientKey, serverKey, clientIV, serverIV :=
+ keysFromMasterSecret(c.vers, hs.suite, hs.masterSecret, hs.hello.random, hs.serverHello.random, hs.suite.macLen, hs.suite.keyLen, hs.suite.ivLen)
+ var clientCipher, serverCipher any
+ var clientHash, serverHash hash.Hash
+ if hs.suite.cipher != nil {
+ clientCipher = hs.suite.cipher(clientKey, clientIV, false /* not for reading */)
+ clientHash = hs.suite.mac(clientMAC)
+ serverCipher = hs.suite.cipher(serverKey, serverIV, true /* for reading */)
+ serverHash = hs.suite.mac(serverMAC)
+ } else {
+ clientCipher = hs.suite.aead(clientKey, clientIV)
+ serverCipher = hs.suite.aead(serverKey, serverIV)
+ }
+
+ c.in.prepareCipherSpec(c.vers, serverCipher, serverHash)
+ c.out.prepareCipherSpec(c.vers, clientCipher, clientHash)
+ return nil
+}
+
+func (hs *clientHandshakeState) serverResumedSession() bool {
+ // If the server responded with the same sessionId then it means the
+ // sessionTicket is being used to resume a TLS session.
+ return hs.session != nil && hs.hello.sessionId != nil &&
+ bytes.Equal(hs.serverHello.sessionId, hs.hello.sessionId)
+}
+
+func (hs *clientHandshakeState) processServerHello() (bool, error) {
+ c := hs.c
+
+ if err := hs.pickCipherSuite(); err != nil {
+ return false, err
+ }
+
+ if hs.serverHello.compressionMethod != compressionNone {
+ c.sendAlert(alertUnexpectedMessage)
+ return false, errors.New("tls: server selected unsupported compression format")
+ }
+
+ if c.handshakes == 0 && hs.serverHello.secureRenegotiationSupported {
+ c.secureRenegotiation = true
+ if len(hs.serverHello.secureRenegotiation) != 0 {
+ c.sendAlert(alertHandshakeFailure)
+ return false, errors.New("tls: initial handshake had non-empty renegotiation extension")
+ }
+ }
+
+ if c.handshakes > 0 && c.secureRenegotiation {
+ var expectedSecureRenegotiation [24]byte
+ copy(expectedSecureRenegotiation[:], c.clientFinished[:])
+ copy(expectedSecureRenegotiation[12:], c.serverFinished[:])
+ if !bytes.Equal(hs.serverHello.secureRenegotiation, expectedSecureRenegotiation[:]) {
+ c.sendAlert(alertHandshakeFailure)
+ return false, errors.New("tls: incorrect renegotiation extension contents")
+ }
+ }
+
+ if err := checkALPN(hs.hello.alpnProtocols, hs.serverHello.alpnProtocol); err != nil {
+ c.sendAlert(alertUnsupportedExtension)
+ return false, err
+ }
+ c.clientProtocol = hs.serverHello.alpnProtocol
+
+ c.scts = hs.serverHello.scts
+
+ if !hs.serverResumedSession() {
+ return false, nil
+ }
+
+ if hs.session.vers != c.vers {
+ c.sendAlert(alertHandshakeFailure)
+ return false, errors.New("tls: server resumed a session with a different version")
+ }
+
+ if hs.session.cipherSuite != hs.suite.id {
+ c.sendAlert(alertHandshakeFailure)
+ return false, errors.New("tls: server resumed a session with a different cipher suite")
+ }
+
+ // Restore masterSecret, peerCerts, and ocspResponse from previous state
+ hs.masterSecret = hs.session.masterSecret
+ c.peerCertificates = hs.session.serverCertificates
+ c.verifiedChains = hs.session.verifiedChains
+ c.ocspResponse = hs.session.ocspResponse
+ // Let the ServerHello SCTs override the session SCTs from the original
+ // connection, if any are provided
+ if len(c.scts) == 0 && len(hs.session.scts) != 0 {
+ c.scts = hs.session.scts
+ }
+
+ return true, nil
+}
+
+// checkALPN ensure that the server's choice of ALPN protocol is compatible with
+// the protocols that we advertised in the Client Hello.
+func checkALPN(clientProtos []string, serverProto string) error {
+ if serverProto == "" {
+ return nil
+ }
+ if len(clientProtos) == 0 {
+ return errors.New("tls: server advertised unrequested ALPN extension")
+ }
+ for _, proto := range clientProtos {
+ if proto == serverProto {
+ return nil
+ }
+ }
+ return errors.New("tls: server selected unadvertised ALPN protocol")
+}
+
+func (hs *clientHandshakeState) readFinished(out []byte) error {
+ c := hs.c
+
+ if err := c.readChangeCipherSpec(); err != nil {
+ return err
+ }
+
+ msg, err := c.readHandshake()
+ if err != nil {
+ return err
+ }
+ serverFinished, ok := msg.(*finishedMsg)
+ if !ok {
+ c.sendAlert(alertUnexpectedMessage)
+ return unexpectedMessageError(serverFinished, msg)
+ }
+
+ verify := hs.finishedHash.serverSum(hs.masterSecret)
+ if len(verify) != len(serverFinished.verifyData) ||
+ subtle.ConstantTimeCompare(verify, serverFinished.verifyData) != 1 {
+ c.sendAlert(alertHandshakeFailure)
+ return errors.New("tls: server's Finished message was incorrect")
+ }
+ hs.finishedHash.Write(serverFinished.marshal())
+ copy(out, verify)
+ return nil
+}
+
+func (hs *clientHandshakeState) readSessionTicket() error {
+ if !hs.serverHello.ticketSupported {
+ return nil
+ }
+
+ c := hs.c
+ msg, err := c.readHandshake()
+ if err != nil {
+ return err
+ }
+ sessionTicketMsg, ok := msg.(*newSessionTicketMsg)
+ if !ok {
+ c.sendAlert(alertUnexpectedMessage)
+ return unexpectedMessageError(sessionTicketMsg, msg)
+ }
+ hs.finishedHash.Write(sessionTicketMsg.marshal())
+
+ hs.session = &clientSessionState{
+ sessionTicket: sessionTicketMsg.ticket,
+ vers: c.vers,
+ cipherSuite: hs.suite.id,
+ masterSecret: hs.masterSecret,
+ serverCertificates: c.peerCertificates,
+ verifiedChains: c.verifiedChains,
+ receivedAt: c.config.time(),
+ ocspResponse: c.ocspResponse,
+ scts: c.scts,
+ }
+
+ return nil
+}
+
+func (hs *clientHandshakeState) sendFinished(out []byte) error {
+ c := hs.c
+
+ if _, err := c.writeRecord(recordTypeChangeCipherSpec, []byte{1}); err != nil {
+ return err
+ }
+
+ finished := new(finishedMsg)
+ finished.verifyData = hs.finishedHash.clientSum(hs.masterSecret)
+ hs.finishedHash.Write(finished.marshal())
+ if _, err := c.writeRecord(recordTypeHandshake, finished.marshal()); err != nil {
+ return err
+ }
+ copy(out, finished.verifyData)
+ return nil
+}
+
+// verifyServerCertificate parses and verifies the provided chain, setting
+// c.verifiedChains and c.peerCertificates or sending the appropriate alert.
+func (c *Conn) verifyServerCertificate(certificates [][]byte) error {
+ certs := make([]*x509.Certificate, len(certificates))
+ for i, asn1Data := range certificates {
+ cert, err := x509.ParseCertificate(asn1Data)
+ if err != nil {
+ c.sendAlert(alertBadCertificate)
+ return errors.New("tls: failed to parse certificate from server: " + err.Error())
+ }
+ certs[i] = cert
+ }
+
+ if !c.config.InsecureSkipVerify {
+ opts := x509.VerifyOptions{
+ Roots: c.config.RootCAs,
+ CurrentTime: c.config.time(),
+ DNSName: c.config.ServerName,
+ Intermediates: x509.NewCertPool(),
+ }
+
+ for _, cert := range certs[1:] {
+ opts.Intermediates.AddCert(cert)
+ }
+ var err error
+ c.verifiedChains, err = certs[0].Verify(opts)
+ if err != nil {
+ c.sendAlert(alertBadCertificate)
+ return err
+ }
+ }
+
+ switch certs[0].PublicKey.(type) {
+ case *rsa.PublicKey, *ecdsa.PublicKey, ed25519.PublicKey:
+ break
+ default:
+ c.sendAlert(alertUnsupportedCertificate)
+ return fmt.Errorf("tls: server's certificate contains an unsupported type of public key: %T", certs[0].PublicKey)
+ }
+
+ c.peerCertificates = certs
+
+ if c.config.VerifyPeerCertificate != nil {
+ if err := c.config.VerifyPeerCertificate(certificates, c.verifiedChains); err != nil {
+ c.sendAlert(alertBadCertificate)
+ return err
+ }
+ }
+
+ if c.config.VerifyConnection != nil {
+ if err := c.config.VerifyConnection(c.connectionStateLocked()); err != nil {
+ c.sendAlert(alertBadCertificate)
+ return err
+ }
+ }
+
+ return nil
+}
+
+// certificateRequestInfoFromMsg generates a CertificateRequestInfo from a TLS
+// <= 1.2 CertificateRequest, making an effort to fill in missing information.
+func certificateRequestInfoFromMsg(ctx context.Context, vers uint16, certReq *certificateRequestMsg) *CertificateRequestInfo {
+ cri := &certificateRequestInfo{
+ AcceptableCAs: certReq.certificateAuthorities,
+ Version: vers,
+ ctx: ctx,
+ }
+
+ var rsaAvail, ecAvail bool
+ for _, certType := range certReq.certificateTypes {
+ switch certType {
+ case certTypeRSASign:
+ rsaAvail = true
+ case certTypeECDSASign:
+ ecAvail = true
+ }
+ }
+
+ if !certReq.hasSignatureAlgorithm {
+ // Prior to TLS 1.2, signature schemes did not exist. In this case we
+ // make up a list based on the acceptable certificate types, to help
+ // GetClientCertificate and SupportsCertificate select the right certificate.
+ // The hash part of the SignatureScheme is a lie here, because
+ // TLS 1.0 and 1.1 always use MD5+SHA1 for RSA and SHA1 for ECDSA.
+ switch {
+ case rsaAvail && ecAvail:
+ cri.SignatureSchemes = []SignatureScheme{
+ ECDSAWithP256AndSHA256, ECDSAWithP384AndSHA384, ECDSAWithP521AndSHA512,
+ PKCS1WithSHA256, PKCS1WithSHA384, PKCS1WithSHA512, PKCS1WithSHA1,
+ }
+ case rsaAvail:
+ cri.SignatureSchemes = []SignatureScheme{
+ PKCS1WithSHA256, PKCS1WithSHA384, PKCS1WithSHA512, PKCS1WithSHA1,
+ }
+ case ecAvail:
+ cri.SignatureSchemes = []SignatureScheme{
+ ECDSAWithP256AndSHA256, ECDSAWithP384AndSHA384, ECDSAWithP521AndSHA512,
+ }
+ }
+ return toCertificateRequestInfo(cri)
+ }
+
+ // Filter the signature schemes based on the certificate types.
+ // See RFC 5246, Section 7.4.4 (where it calls this "somewhat complicated").
+ cri.SignatureSchemes = make([]SignatureScheme, 0, len(certReq.supportedSignatureAlgorithms))
+ for _, sigScheme := range certReq.supportedSignatureAlgorithms {
+ sigType, _, err := typeAndHashFromSignatureScheme(sigScheme)
+ if err != nil {
+ continue
+ }
+ switch sigType {
+ case signatureECDSA, signatureEd25519:
+ if ecAvail {
+ cri.SignatureSchemes = append(cri.SignatureSchemes, sigScheme)
+ }
+ case signatureRSAPSS, signaturePKCS1v15:
+ if rsaAvail {
+ cri.SignatureSchemes = append(cri.SignatureSchemes, sigScheme)
+ }
+ }
+ }
+
+ return toCertificateRequestInfo(cri)
+}
+
+func (c *Conn) getClientCertificate(cri *CertificateRequestInfo) (*Certificate, error) {
+ if c.config.GetClientCertificate != nil {
+ return c.config.GetClientCertificate(cri)
+ }
+
+ for _, chain := range c.config.Certificates {
+ if err := cri.SupportsCertificate(&chain); err != nil {
+ continue
+ }
+ return &chain, nil
+ }
+
+ // No acceptable certificate found. Don't send a certificate.
+ return new(Certificate), nil
+}
+
+const clientSessionCacheKeyPrefix = "qtls-"
+
+// clientSessionCacheKey returns a key used to cache sessionTickets that could
+// be used to resume previously negotiated TLS sessions with a server.
+func clientSessionCacheKey(serverAddr net.Addr, config *config) string {
+ if len(config.ServerName) > 0 {
+ return clientSessionCacheKeyPrefix + config.ServerName
+ }
+ return clientSessionCacheKeyPrefix + serverAddr.String()
+}
+
+// hostnameInSNI converts name into an appropriate hostname for SNI.
+// Literal IP addresses and absolute FQDNs are not permitted as SNI values.
+// See RFC 6066, Section 3.
+func hostnameInSNI(name string) string {
+ host := name
+ if len(host) > 0 && host[0] == '[' && host[len(host)-1] == ']' {
+ host = host[1 : len(host)-1]
+ }
+ if i := strings.LastIndex(host, "%"); i > 0 {
+ host = host[:i]
+ }
+ if net.ParseIP(host) != nil {
+ return ""
+ }
+ for len(name) > 0 && name[len(name)-1] == '.' {
+ name = name[:len(name)-1]
+ }
+ return name
+}
diff --git a/vendor/github.com/quic-go/qtls-go1-19/handshake_client_tls13.go b/vendor/github.com/quic-go/qtls-go1-19/handshake_client_tls13.go
new file mode 100644
index 0000000000..5c3ed0bde3
--- /dev/null
+++ b/vendor/github.com/quic-go/qtls-go1-19/handshake_client_tls13.go
@@ -0,0 +1,738 @@
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package qtls
+
+import (
+ "bytes"
+ "context"
+ "crypto"
+ "crypto/hmac"
+ "crypto/rsa"
+ "encoding/binary"
+ "errors"
+ "hash"
+ "sync/atomic"
+ "time"
+
+ "golang.org/x/crypto/cryptobyte"
+)
+
+type clientHandshakeStateTLS13 struct {
+ c *Conn
+ ctx context.Context
+ serverHello *serverHelloMsg
+ hello *clientHelloMsg
+ ecdheParams ecdheParameters
+
+ session *clientSessionState
+ earlySecret []byte
+ binderKey []byte
+
+ certReq *certificateRequestMsgTLS13
+ usingPSK bool
+ sentDummyCCS bool
+ suite *cipherSuiteTLS13
+ transcript hash.Hash
+ masterSecret []byte
+ trafficSecret []byte // client_application_traffic_secret_0
+}
+
+// handshake requires hs.c, hs.hello, hs.serverHello, hs.ecdheParams, and,
+// optionally, hs.session, hs.earlySecret and hs.binderKey to be set.
+func (hs *clientHandshakeStateTLS13) handshake() error {
+ c := hs.c
+
+ if needFIPS() {
+ return errors.New("tls: internal error: TLS 1.3 reached in FIPS mode")
+ }
+
+ // The server must not select TLS 1.3 in a renegotiation. See RFC 8446,
+ // sections 4.1.2 and 4.1.3.
+ if c.handshakes > 0 {
+ c.sendAlert(alertProtocolVersion)
+ return errors.New("tls: server selected TLS 1.3 in a renegotiation")
+ }
+
+ // Consistency check on the presence of a keyShare and its parameters.
+ if hs.ecdheParams == nil || len(hs.hello.keyShares) != 1 {
+ return c.sendAlert(alertInternalError)
+ }
+
+ if err := hs.checkServerHelloOrHRR(); err != nil {
+ return err
+ }
+
+ hs.transcript = hs.suite.hash.New()
+ hs.transcript.Write(hs.hello.marshal())
+
+ if bytes.Equal(hs.serverHello.random, helloRetryRequestRandom) {
+ if err := hs.sendDummyChangeCipherSpec(); err != nil {
+ return err
+ }
+ if err := hs.processHelloRetryRequest(); err != nil {
+ return err
+ }
+ }
+
+ hs.transcript.Write(hs.serverHello.marshal())
+
+ c.buffering = true
+ if err := hs.processServerHello(); err != nil {
+ return err
+ }
+ c.updateConnectionState()
+ if err := hs.sendDummyChangeCipherSpec(); err != nil {
+ return err
+ }
+ if err := hs.establishHandshakeKeys(); err != nil {
+ return err
+ }
+ if err := hs.readServerParameters(); err != nil {
+ return err
+ }
+ if err := hs.readServerCertificate(); err != nil {
+ return err
+ }
+ c.updateConnectionState()
+ if err := hs.readServerFinished(); err != nil {
+ return err
+ }
+ if err := hs.sendClientCertificate(); err != nil {
+ return err
+ }
+ if err := hs.sendClientFinished(); err != nil {
+ return err
+ }
+ if _, err := c.flush(); err != nil {
+ return err
+ }
+
+ atomic.StoreUint32(&c.handshakeStatus, 1)
+ c.updateConnectionState()
+ return nil
+}
+
+// checkServerHelloOrHRR does validity checks that apply to both ServerHello and
+// HelloRetryRequest messages. It sets hs.suite.
+func (hs *clientHandshakeStateTLS13) checkServerHelloOrHRR() error {
+ c := hs.c
+
+ if hs.serverHello.supportedVersion == 0 {
+ c.sendAlert(alertMissingExtension)
+ return errors.New("tls: server selected TLS 1.3 using the legacy version field")
+ }
+
+ if hs.serverHello.supportedVersion != VersionTLS13 {
+ c.sendAlert(alertIllegalParameter)
+ return errors.New("tls: server selected an invalid version after a HelloRetryRequest")
+ }
+
+ if hs.serverHello.vers != VersionTLS12 {
+ c.sendAlert(alertIllegalParameter)
+ return errors.New("tls: server sent an incorrect legacy version")
+ }
+
+ if hs.serverHello.ocspStapling ||
+ hs.serverHello.ticketSupported ||
+ hs.serverHello.secureRenegotiationSupported ||
+ len(hs.serverHello.secureRenegotiation) != 0 ||
+ len(hs.serverHello.alpnProtocol) != 0 ||
+ len(hs.serverHello.scts) != 0 {
+ c.sendAlert(alertUnsupportedExtension)
+ return errors.New("tls: server sent a ServerHello extension forbidden in TLS 1.3")
+ }
+
+ if !bytes.Equal(hs.hello.sessionId, hs.serverHello.sessionId) {
+ c.sendAlert(alertIllegalParameter)
+ return errors.New("tls: server did not echo the legacy session ID")
+ }
+
+ if hs.serverHello.compressionMethod != compressionNone {
+ c.sendAlert(alertIllegalParameter)
+ return errors.New("tls: server selected unsupported compression format")
+ }
+
+ selectedSuite := mutualCipherSuiteTLS13(hs.hello.cipherSuites, hs.serverHello.cipherSuite)
+ if hs.suite != nil && selectedSuite != hs.suite {
+ c.sendAlert(alertIllegalParameter)
+ return errors.New("tls: server changed cipher suite after a HelloRetryRequest")
+ }
+ if selectedSuite == nil {
+ c.sendAlert(alertIllegalParameter)
+ return errors.New("tls: server chose an unconfigured cipher suite")
+ }
+ hs.suite = selectedSuite
+ c.cipherSuite = hs.suite.id
+
+ return nil
+}
+
+// sendDummyChangeCipherSpec sends a ChangeCipherSpec record for compatibility
+// with middleboxes that didn't implement TLS correctly. See RFC 8446, Appendix D.4.
+func (hs *clientHandshakeStateTLS13) sendDummyChangeCipherSpec() error {
+ if hs.sentDummyCCS {
+ return nil
+ }
+ hs.sentDummyCCS = true
+
+ _, err := hs.c.writeRecord(recordTypeChangeCipherSpec, []byte{1})
+ return err
+}
+
+// processHelloRetryRequest handles the HRR in hs.serverHello, modifies and
+// resends hs.hello, and reads the new ServerHello into hs.serverHello.
+func (hs *clientHandshakeStateTLS13) processHelloRetryRequest() error {
+ c := hs.c
+
+ // The first ClientHello gets double-hashed into the transcript upon a
+ // HelloRetryRequest. (The idea is that the server might offload transcript
+ // storage to the client in the cookie.) See RFC 8446, Section 4.4.1.
+ chHash := hs.transcript.Sum(nil)
+ hs.transcript.Reset()
+ hs.transcript.Write([]byte{typeMessageHash, 0, 0, uint8(len(chHash))})
+ hs.transcript.Write(chHash)
+ hs.transcript.Write(hs.serverHello.marshal())
+
+ // The only HelloRetryRequest extensions we support are key_share and
+ // cookie, and clients must abort the handshake if the HRR would not result
+ // in any change in the ClientHello.
+ if hs.serverHello.selectedGroup == 0 && hs.serverHello.cookie == nil {
+ c.sendAlert(alertIllegalParameter)
+ return errors.New("tls: server sent an unnecessary HelloRetryRequest message")
+ }
+
+ if hs.serverHello.cookie != nil {
+ hs.hello.cookie = hs.serverHello.cookie
+ }
+
+ if hs.serverHello.serverShare.group != 0 {
+ c.sendAlert(alertDecodeError)
+ return errors.New("tls: received malformed key_share extension")
+ }
+
+ // If the server sent a key_share extension selecting a group, ensure it's
+ // a group we advertised but did not send a key share for, and send a key
+ // share for it this time.
+ if curveID := hs.serverHello.selectedGroup; curveID != 0 {
+ curveOK := false
+ for _, id := range hs.hello.supportedCurves {
+ if id == curveID {
+ curveOK = true
+ break
+ }
+ }
+ if !curveOK {
+ c.sendAlert(alertIllegalParameter)
+ return errors.New("tls: server selected unsupported group")
+ }
+ if hs.ecdheParams.CurveID() == curveID {
+ c.sendAlert(alertIllegalParameter)
+ return errors.New("tls: server sent an unnecessary HelloRetryRequest key_share")
+ }
+ if _, ok := curveForCurveID(curveID); curveID != X25519 && !ok {
+ c.sendAlert(alertInternalError)
+ return errors.New("tls: CurvePreferences includes unsupported curve")
+ }
+ params, err := generateECDHEParameters(c.config.rand(), curveID)
+ if err != nil {
+ c.sendAlert(alertInternalError)
+ return err
+ }
+ hs.ecdheParams = params
+ hs.hello.keyShares = []keyShare{{group: curveID, data: params.PublicKey()}}
+ }
+
+ hs.hello.raw = nil
+ if len(hs.hello.pskIdentities) > 0 {
+ pskSuite := cipherSuiteTLS13ByID(hs.session.cipherSuite)
+ if pskSuite == nil {
+ return c.sendAlert(alertInternalError)
+ }
+ if pskSuite.hash == hs.suite.hash {
+ // Update binders and obfuscated_ticket_age.
+ ticketAge := uint32(c.config.time().Sub(hs.session.receivedAt) / time.Millisecond)
+ hs.hello.pskIdentities[0].obfuscatedTicketAge = ticketAge + hs.session.ageAdd
+
+ transcript := hs.suite.hash.New()
+ transcript.Write([]byte{typeMessageHash, 0, 0, uint8(len(chHash))})
+ transcript.Write(chHash)
+ transcript.Write(hs.serverHello.marshal())
+ transcript.Write(hs.hello.marshalWithoutBinders())
+ pskBinders := [][]byte{hs.suite.finishedHash(hs.binderKey, transcript)}
+ hs.hello.updateBinders(pskBinders)
+ } else {
+ // Server selected a cipher suite incompatible with the PSK.
+ hs.hello.pskIdentities = nil
+ hs.hello.pskBinders = nil
+ }
+ }
+
+ if hs.hello.earlyData && c.extraConfig != nil && c.extraConfig.Rejected0RTT != nil {
+ c.extraConfig.Rejected0RTT()
+ }
+ hs.hello.earlyData = false // disable 0-RTT
+
+ hs.transcript.Write(hs.hello.marshal())
+ if _, err := c.writeRecord(recordTypeHandshake, hs.hello.marshal()); err != nil {
+ return err
+ }
+
+ msg, err := c.readHandshake()
+ if err != nil {
+ return err
+ }
+
+ serverHello, ok := msg.(*serverHelloMsg)
+ if !ok {
+ c.sendAlert(alertUnexpectedMessage)
+ return unexpectedMessageError(serverHello, msg)
+ }
+ hs.serverHello = serverHello
+
+ if err := hs.checkServerHelloOrHRR(); err != nil {
+ return err
+ }
+
+ return nil
+}
+
+func (hs *clientHandshakeStateTLS13) processServerHello() error {
+ c := hs.c
+
+ if bytes.Equal(hs.serverHello.random, helloRetryRequestRandom) {
+ c.sendAlert(alertUnexpectedMessage)
+ return errors.New("tls: server sent two HelloRetryRequest messages")
+ }
+
+ if len(hs.serverHello.cookie) != 0 {
+ c.sendAlert(alertUnsupportedExtension)
+ return errors.New("tls: server sent a cookie in a normal ServerHello")
+ }
+
+ if hs.serverHello.selectedGroup != 0 {
+ c.sendAlert(alertDecodeError)
+ return errors.New("tls: malformed key_share extension")
+ }
+
+ if hs.serverHello.serverShare.group == 0 {
+ c.sendAlert(alertIllegalParameter)
+ return errors.New("tls: server did not send a key share")
+ }
+ if hs.serverHello.serverShare.group != hs.ecdheParams.CurveID() {
+ c.sendAlert(alertIllegalParameter)
+ return errors.New("tls: server selected unsupported group")
+ }
+
+ if !hs.serverHello.selectedIdentityPresent {
+ return nil
+ }
+
+ if int(hs.serverHello.selectedIdentity) >= len(hs.hello.pskIdentities) {
+ c.sendAlert(alertIllegalParameter)
+ return errors.New("tls: server selected an invalid PSK")
+ }
+
+ if len(hs.hello.pskIdentities) != 1 || hs.session == nil {
+ return c.sendAlert(alertInternalError)
+ }
+ pskSuite := cipherSuiteTLS13ByID(hs.session.cipherSuite)
+ if pskSuite == nil {
+ return c.sendAlert(alertInternalError)
+ }
+ if pskSuite.hash != hs.suite.hash {
+ c.sendAlert(alertIllegalParameter)
+ return errors.New("tls: server selected an invalid PSK and cipher suite pair")
+ }
+
+ hs.usingPSK = true
+ c.didResume = true
+ c.peerCertificates = hs.session.serverCertificates
+ c.verifiedChains = hs.session.verifiedChains
+ c.ocspResponse = hs.session.ocspResponse
+ c.scts = hs.session.scts
+ return nil
+}
+
+func (hs *clientHandshakeStateTLS13) establishHandshakeKeys() error {
+ c := hs.c
+
+ sharedKey := hs.ecdheParams.SharedKey(hs.serverHello.serverShare.data)
+ if sharedKey == nil {
+ c.sendAlert(alertIllegalParameter)
+ return errors.New("tls: invalid server key share")
+ }
+
+ earlySecret := hs.earlySecret
+ if !hs.usingPSK {
+ earlySecret = hs.suite.extract(nil, nil)
+ }
+ handshakeSecret := hs.suite.extract(sharedKey,
+ hs.suite.deriveSecret(earlySecret, "derived", nil))
+
+ clientSecret := hs.suite.deriveSecret(handshakeSecret,
+ clientHandshakeTrafficLabel, hs.transcript)
+ c.out.exportKey(EncryptionHandshake, hs.suite, clientSecret)
+ c.out.setTrafficSecret(hs.suite, clientSecret)
+ serverSecret := hs.suite.deriveSecret(handshakeSecret,
+ serverHandshakeTrafficLabel, hs.transcript)
+ c.in.exportKey(EncryptionHandshake, hs.suite, serverSecret)
+ c.in.setTrafficSecret(hs.suite, serverSecret)
+
+ err := c.config.writeKeyLog(keyLogLabelClientHandshake, hs.hello.random, clientSecret)
+ if err != nil {
+ c.sendAlert(alertInternalError)
+ return err
+ }
+ err = c.config.writeKeyLog(keyLogLabelServerHandshake, hs.hello.random, serverSecret)
+ if err != nil {
+ c.sendAlert(alertInternalError)
+ return err
+ }
+
+ hs.masterSecret = hs.suite.extract(nil,
+ hs.suite.deriveSecret(handshakeSecret, "derived", nil))
+
+ return nil
+}
+
+func (hs *clientHandshakeStateTLS13) readServerParameters() error {
+ c := hs.c
+
+ msg, err := c.readHandshake()
+ if err != nil {
+ return err
+ }
+
+ encryptedExtensions, ok := msg.(*encryptedExtensionsMsg)
+ if !ok {
+ c.sendAlert(alertUnexpectedMessage)
+ return unexpectedMessageError(encryptedExtensions, msg)
+ }
+ // Notify the caller if 0-RTT was rejected.
+ if !encryptedExtensions.earlyData && hs.hello.earlyData && c.extraConfig != nil && c.extraConfig.Rejected0RTT != nil {
+ c.extraConfig.Rejected0RTT()
+ }
+ c.used0RTT = encryptedExtensions.earlyData
+ if hs.c.extraConfig != nil && hs.c.extraConfig.ReceivedExtensions != nil {
+ hs.c.extraConfig.ReceivedExtensions(typeEncryptedExtensions, encryptedExtensions.additionalExtensions)
+ }
+ hs.transcript.Write(encryptedExtensions.marshal())
+
+ if err := checkALPN(hs.hello.alpnProtocols, encryptedExtensions.alpnProtocol); err != nil {
+ c.sendAlert(alertUnsupportedExtension)
+ return err
+ }
+ c.clientProtocol = encryptedExtensions.alpnProtocol
+
+ if c.extraConfig != nil && c.extraConfig.EnforceNextProtoSelection {
+ if len(encryptedExtensions.alpnProtocol) == 0 {
+ // the server didn't select an ALPN
+ c.sendAlert(alertNoApplicationProtocol)
+ return errors.New("ALPN negotiation failed. Server didn't offer any protocols")
+ }
+ }
+ return nil
+}
+
+func (hs *clientHandshakeStateTLS13) readServerCertificate() error {
+ c := hs.c
+
+ // Either a PSK or a certificate is always used, but not both.
+ // See RFC 8446, Section 4.1.1.
+ if hs.usingPSK {
+ // Make sure the connection is still being verified whether or not this
+ // is a resumption. Resumptions currently don't reverify certificates so
+ // they don't call verifyServerCertificate. See Issue 31641.
+ if c.config.VerifyConnection != nil {
+ if err := c.config.VerifyConnection(c.connectionStateLocked()); err != nil {
+ c.sendAlert(alertBadCertificate)
+ return err
+ }
+ }
+ return nil
+ }
+
+ msg, err := c.readHandshake()
+ if err != nil {
+ return err
+ }
+
+ certReq, ok := msg.(*certificateRequestMsgTLS13)
+ if ok {
+ hs.transcript.Write(certReq.marshal())
+
+ hs.certReq = certReq
+
+ msg, err = c.readHandshake()
+ if err != nil {
+ return err
+ }
+ }
+
+ certMsg, ok := msg.(*certificateMsgTLS13)
+ if !ok {
+ c.sendAlert(alertUnexpectedMessage)
+ return unexpectedMessageError(certMsg, msg)
+ }
+ if len(certMsg.certificate.Certificate) == 0 {
+ c.sendAlert(alertDecodeError)
+ return errors.New("tls: received empty certificates message")
+ }
+ hs.transcript.Write(certMsg.marshal())
+
+ c.scts = certMsg.certificate.SignedCertificateTimestamps
+ c.ocspResponse = certMsg.certificate.OCSPStaple
+
+ if err := c.verifyServerCertificate(certMsg.certificate.Certificate); err != nil {
+ return err
+ }
+
+ msg, err = c.readHandshake()
+ if err != nil {
+ return err
+ }
+
+ certVerify, ok := msg.(*certificateVerifyMsg)
+ if !ok {
+ c.sendAlert(alertUnexpectedMessage)
+ return unexpectedMessageError(certVerify, msg)
+ }
+
+ // See RFC 8446, Section 4.4.3.
+ if !isSupportedSignatureAlgorithm(certVerify.signatureAlgorithm, supportedSignatureAlgorithms()) {
+ c.sendAlert(alertIllegalParameter)
+ return errors.New("tls: certificate used with invalid signature algorithm")
+ }
+ sigType, sigHash, err := typeAndHashFromSignatureScheme(certVerify.signatureAlgorithm)
+ if err != nil {
+ return c.sendAlert(alertInternalError)
+ }
+ if sigType == signaturePKCS1v15 || sigHash == crypto.SHA1 {
+ c.sendAlert(alertIllegalParameter)
+ return errors.New("tls: certificate used with invalid signature algorithm")
+ }
+ signed := signedMessage(sigHash, serverSignatureContext, hs.transcript)
+ if err := verifyHandshakeSignature(sigType, c.peerCertificates[0].PublicKey,
+ sigHash, signed, certVerify.signature); err != nil {
+ c.sendAlert(alertDecryptError)
+ return errors.New("tls: invalid signature by the server certificate: " + err.Error())
+ }
+
+ hs.transcript.Write(certVerify.marshal())
+
+ return nil
+}
+
+func (hs *clientHandshakeStateTLS13) readServerFinished() error {
+ c := hs.c
+
+ msg, err := c.readHandshake()
+ if err != nil {
+ return err
+ }
+
+ finished, ok := msg.(*finishedMsg)
+ if !ok {
+ c.sendAlert(alertUnexpectedMessage)
+ return unexpectedMessageError(finished, msg)
+ }
+
+ expectedMAC := hs.suite.finishedHash(c.in.trafficSecret, hs.transcript)
+ if !hmac.Equal(expectedMAC, finished.verifyData) {
+ c.sendAlert(alertDecryptError)
+ return errors.New("tls: invalid server finished hash")
+ }
+
+ hs.transcript.Write(finished.marshal())
+
+ // Derive secrets that take context through the server Finished.
+
+ hs.trafficSecret = hs.suite.deriveSecret(hs.masterSecret,
+ clientApplicationTrafficLabel, hs.transcript)
+ serverSecret := hs.suite.deriveSecret(hs.masterSecret,
+ serverApplicationTrafficLabel, hs.transcript)
+ c.in.exportKey(EncryptionApplication, hs.suite, serverSecret)
+ c.in.setTrafficSecret(hs.suite, serverSecret)
+
+ err = c.config.writeKeyLog(keyLogLabelClientTraffic, hs.hello.random, hs.trafficSecret)
+ if err != nil {
+ c.sendAlert(alertInternalError)
+ return err
+ }
+ err = c.config.writeKeyLog(keyLogLabelServerTraffic, hs.hello.random, serverSecret)
+ if err != nil {
+ c.sendAlert(alertInternalError)
+ return err
+ }
+
+ c.ekm = hs.suite.exportKeyingMaterial(hs.masterSecret, hs.transcript)
+
+ return nil
+}
+
+func (hs *clientHandshakeStateTLS13) sendClientCertificate() error {
+ c := hs.c
+
+ if hs.certReq == nil {
+ return nil
+ }
+
+ cert, err := c.getClientCertificate(toCertificateRequestInfo(&certificateRequestInfo{
+ AcceptableCAs: hs.certReq.certificateAuthorities,
+ SignatureSchemes: hs.certReq.supportedSignatureAlgorithms,
+ Version: c.vers,
+ ctx: hs.ctx,
+ }))
+ if err != nil {
+ return err
+ }
+
+ certMsg := new(certificateMsgTLS13)
+
+ certMsg.certificate = *cert
+ certMsg.scts = hs.certReq.scts && len(cert.SignedCertificateTimestamps) > 0
+ certMsg.ocspStapling = hs.certReq.ocspStapling && len(cert.OCSPStaple) > 0
+
+ hs.transcript.Write(certMsg.marshal())
+ if _, err := c.writeRecord(recordTypeHandshake, certMsg.marshal()); err != nil {
+ return err
+ }
+
+ // If we sent an empty certificate message, skip the CertificateVerify.
+ if len(cert.Certificate) == 0 {
+ return nil
+ }
+
+ certVerifyMsg := new(certificateVerifyMsg)
+ certVerifyMsg.hasSignatureAlgorithm = true
+
+ certVerifyMsg.signatureAlgorithm, err = selectSignatureScheme(c.vers, cert, hs.certReq.supportedSignatureAlgorithms)
+ if err != nil {
+ // getClientCertificate returned a certificate incompatible with the
+ // CertificateRequestInfo supported signature algorithms.
+ c.sendAlert(alertHandshakeFailure)
+ return err
+ }
+
+ sigType, sigHash, err := typeAndHashFromSignatureScheme(certVerifyMsg.signatureAlgorithm)
+ if err != nil {
+ return c.sendAlert(alertInternalError)
+ }
+
+ signed := signedMessage(sigHash, clientSignatureContext, hs.transcript)
+ signOpts := crypto.SignerOpts(sigHash)
+ if sigType == signatureRSAPSS {
+ signOpts = &rsa.PSSOptions{SaltLength: rsa.PSSSaltLengthEqualsHash, Hash: sigHash}
+ }
+ sig, err := cert.PrivateKey.(crypto.Signer).Sign(c.config.rand(), signed, signOpts)
+ if err != nil {
+ c.sendAlert(alertInternalError)
+ return errors.New("tls: failed to sign handshake: " + err.Error())
+ }
+ certVerifyMsg.signature = sig
+
+ hs.transcript.Write(certVerifyMsg.marshal())
+ if _, err := c.writeRecord(recordTypeHandshake, certVerifyMsg.marshal()); err != nil {
+ return err
+ }
+
+ return nil
+}
+
+func (hs *clientHandshakeStateTLS13) sendClientFinished() error {
+ c := hs.c
+
+ finished := &finishedMsg{
+ verifyData: hs.suite.finishedHash(c.out.trafficSecret, hs.transcript),
+ }
+
+ hs.transcript.Write(finished.marshal())
+ if _, err := c.writeRecord(recordTypeHandshake, finished.marshal()); err != nil {
+ return err
+ }
+
+ c.out.exportKey(EncryptionApplication, hs.suite, hs.trafficSecret)
+ c.out.setTrafficSecret(hs.suite, hs.trafficSecret)
+
+ if !c.config.SessionTicketsDisabled && c.config.ClientSessionCache != nil {
+ c.resumptionSecret = hs.suite.deriveSecret(hs.masterSecret,
+ resumptionLabel, hs.transcript)
+ }
+
+ return nil
+}
+
+func (c *Conn) handleNewSessionTicket(msg *newSessionTicketMsgTLS13) error {
+ if !c.isClient {
+ c.sendAlert(alertUnexpectedMessage)
+ return errors.New("tls: received new session ticket from a client")
+ }
+
+ if c.config.SessionTicketsDisabled || c.config.ClientSessionCache == nil {
+ return nil
+ }
+
+ // See RFC 8446, Section 4.6.1.
+ if msg.lifetime == 0 {
+ return nil
+ }
+ lifetime := time.Duration(msg.lifetime) * time.Second
+ if lifetime > maxSessionTicketLifetime {
+ c.sendAlert(alertIllegalParameter)
+ return errors.New("tls: received a session ticket with invalid lifetime")
+ }
+
+ cipherSuite := cipherSuiteTLS13ByID(c.cipherSuite)
+ if cipherSuite == nil || c.resumptionSecret == nil {
+ return c.sendAlert(alertInternalError)
+ }
+
+ // We need to save the max_early_data_size that the server sent us, in order
+ // to decide if we're going to try 0-RTT with this ticket.
+ // However, at the same time, the qtls.ClientSessionTicket needs to be equal to
+ // the tls.ClientSessionTicket, so we can't just add a new field to the struct.
+ // We therefore abuse the nonce field (which is a byte slice)
+ nonceWithEarlyData := make([]byte, len(msg.nonce)+4)
+ binary.BigEndian.PutUint32(nonceWithEarlyData, msg.maxEarlyData)
+ copy(nonceWithEarlyData[4:], msg.nonce)
+
+ var appData []byte
+ if c.extraConfig != nil && c.extraConfig.GetAppDataForSessionState != nil {
+ appData = c.extraConfig.GetAppDataForSessionState()
+ }
+ var b cryptobyte.Builder
+ b.AddUint16(clientSessionStateVersion) // revision
+ b.AddUint32(msg.maxEarlyData)
+ b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
+ b.AddBytes(appData)
+ })
+ b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
+ b.AddBytes(msg.nonce)
+ })
+
+ // Save the resumption_master_secret and nonce instead of deriving the PSK
+ // to do the least amount of work on NewSessionTicket messages before we
+ // know if the ticket will be used. Forward secrecy of resumed connections
+ // is guaranteed by the requirement for pskModeDHE.
+ session := &clientSessionState{
+ sessionTicket: msg.label,
+ vers: c.vers,
+ cipherSuite: c.cipherSuite,
+ masterSecret: c.resumptionSecret,
+ serverCertificates: c.peerCertificates,
+ verifiedChains: c.verifiedChains,
+ receivedAt: c.config.time(),
+ nonce: b.BytesOrPanic(),
+ useBy: c.config.time().Add(lifetime),
+ ageAdd: msg.ageAdd,
+ ocspResponse: c.ocspResponse,
+ scts: c.scts,
+ }
+
+ cacheKey := clientSessionCacheKey(c.conn.RemoteAddr(), c.config)
+ c.config.ClientSessionCache.Put(cacheKey, toClientSessionState(session))
+
+ return nil
+}
diff --git a/vendor/github.com/quic-go/qtls-go1-19/handshake_messages.go b/vendor/github.com/quic-go/qtls-go1-19/handshake_messages.go
new file mode 100644
index 0000000000..07193c8efc
--- /dev/null
+++ b/vendor/github.com/quic-go/qtls-go1-19/handshake_messages.go
@@ -0,0 +1,1843 @@
+// Copyright 2009 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package qtls
+
+import (
+ "fmt"
+ "strings"
+
+ "golang.org/x/crypto/cryptobyte"
+)
+
+// The marshalingFunction type is an adapter to allow the use of ordinary
+// functions as cryptobyte.MarshalingValue.
+type marshalingFunction func(b *cryptobyte.Builder) error
+
+func (f marshalingFunction) Marshal(b *cryptobyte.Builder) error {
+ return f(b)
+}
+
+// addBytesWithLength appends a sequence of bytes to the cryptobyte.Builder. If
+// the length of the sequence is not the value specified, it produces an error.
+func addBytesWithLength(b *cryptobyte.Builder, v []byte, n int) {
+ b.AddValue(marshalingFunction(func(b *cryptobyte.Builder) error {
+ if len(v) != n {
+ return fmt.Errorf("invalid value length: expected %d, got %d", n, len(v))
+ }
+ b.AddBytes(v)
+ return nil
+ }))
+}
+
+// addUint64 appends a big-endian, 64-bit value to the cryptobyte.Builder.
+func addUint64(b *cryptobyte.Builder, v uint64) {
+ b.AddUint32(uint32(v >> 32))
+ b.AddUint32(uint32(v))
+}
+
+// readUint64 decodes a big-endian, 64-bit value into out and advances over it.
+// It reports whether the read was successful.
+func readUint64(s *cryptobyte.String, out *uint64) bool {
+ var hi, lo uint32
+ if !s.ReadUint32(&hi) || !s.ReadUint32(&lo) {
+ return false
+ }
+ *out = uint64(hi)<<32 | uint64(lo)
+ return true
+}
+
+// readUint8LengthPrefixed acts like s.ReadUint8LengthPrefixed, but targets a
+// []byte instead of a cryptobyte.String.
+func readUint8LengthPrefixed(s *cryptobyte.String, out *[]byte) bool {
+ return s.ReadUint8LengthPrefixed((*cryptobyte.String)(out))
+}
+
+// readUint16LengthPrefixed acts like s.ReadUint16LengthPrefixed, but targets a
+// []byte instead of a cryptobyte.String.
+func readUint16LengthPrefixed(s *cryptobyte.String, out *[]byte) bool {
+ return s.ReadUint16LengthPrefixed((*cryptobyte.String)(out))
+}
+
+// readUint24LengthPrefixed acts like s.ReadUint24LengthPrefixed, but targets a
+// []byte instead of a cryptobyte.String.
+func readUint24LengthPrefixed(s *cryptobyte.String, out *[]byte) bool {
+ return s.ReadUint24LengthPrefixed((*cryptobyte.String)(out))
+}
+
+type clientHelloMsg struct {
+ raw []byte
+ vers uint16
+ random []byte
+ sessionId []byte
+ cipherSuites []uint16
+ compressionMethods []uint8
+ serverName string
+ ocspStapling bool
+ supportedCurves []CurveID
+ supportedPoints []uint8
+ ticketSupported bool
+ sessionTicket []uint8
+ supportedSignatureAlgorithms []SignatureScheme
+ supportedSignatureAlgorithmsCert []SignatureScheme
+ secureRenegotiationSupported bool
+ secureRenegotiation []byte
+ alpnProtocols []string
+ scts bool
+ supportedVersions []uint16
+ cookie []byte
+ keyShares []keyShare
+ earlyData bool
+ pskModes []uint8
+ pskIdentities []pskIdentity
+ pskBinders [][]byte
+ additionalExtensions []Extension
+}
+
+func (m *clientHelloMsg) marshal() []byte {
+ if m.raw != nil {
+ return m.raw
+ }
+
+ var b cryptobyte.Builder
+ b.AddUint8(typeClientHello)
+ b.AddUint24LengthPrefixed(func(b *cryptobyte.Builder) {
+ b.AddUint16(m.vers)
+ addBytesWithLength(b, m.random, 32)
+ b.AddUint8LengthPrefixed(func(b *cryptobyte.Builder) {
+ b.AddBytes(m.sessionId)
+ })
+ b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
+ for _, suite := range m.cipherSuites {
+ b.AddUint16(suite)
+ }
+ })
+ b.AddUint8LengthPrefixed(func(b *cryptobyte.Builder) {
+ b.AddBytes(m.compressionMethods)
+ })
+
+ // If extensions aren't present, omit them.
+ var extensionsPresent bool
+ bWithoutExtensions := *b
+
+ b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
+ if len(m.serverName) > 0 {
+ // RFC 6066, Section 3
+ b.AddUint16(extensionServerName)
+ b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
+ b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
+ b.AddUint8(0) // name_type = host_name
+ b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
+ b.AddBytes([]byte(m.serverName))
+ })
+ })
+ })
+ }
+ if m.ocspStapling {
+ // RFC 4366, Section 3.6
+ b.AddUint16(extensionStatusRequest)
+ b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
+ b.AddUint8(1) // status_type = ocsp
+ b.AddUint16(0) // empty responder_id_list
+ b.AddUint16(0) // empty request_extensions
+ })
+ }
+ if len(m.supportedCurves) > 0 {
+ // RFC 4492, sections 5.1.1 and RFC 8446, Section 4.2.7
+ b.AddUint16(extensionSupportedCurves)
+ b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
+ b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
+ for _, curve := range m.supportedCurves {
+ b.AddUint16(uint16(curve))
+ }
+ })
+ })
+ }
+ if len(m.supportedPoints) > 0 {
+ // RFC 4492, Section 5.1.2
+ b.AddUint16(extensionSupportedPoints)
+ b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
+ b.AddUint8LengthPrefixed(func(b *cryptobyte.Builder) {
+ b.AddBytes(m.supportedPoints)
+ })
+ })
+ }
+ if m.ticketSupported {
+ // RFC 5077, Section 3.2
+ b.AddUint16(extensionSessionTicket)
+ b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
+ b.AddBytes(m.sessionTicket)
+ })
+ }
+ if len(m.supportedSignatureAlgorithms) > 0 {
+ // RFC 5246, Section 7.4.1.4.1
+ b.AddUint16(extensionSignatureAlgorithms)
+ b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
+ b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
+ for _, sigAlgo := range m.supportedSignatureAlgorithms {
+ b.AddUint16(uint16(sigAlgo))
+ }
+ })
+ })
+ }
+ if len(m.supportedSignatureAlgorithmsCert) > 0 {
+ // RFC 8446, Section 4.2.3
+ b.AddUint16(extensionSignatureAlgorithmsCert)
+ b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
+ b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
+ for _, sigAlgo := range m.supportedSignatureAlgorithmsCert {
+ b.AddUint16(uint16(sigAlgo))
+ }
+ })
+ })
+ }
+ if m.secureRenegotiationSupported {
+ // RFC 5746, Section 3.2
+ b.AddUint16(extensionRenegotiationInfo)
+ b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
+ b.AddUint8LengthPrefixed(func(b *cryptobyte.Builder) {
+ b.AddBytes(m.secureRenegotiation)
+ })
+ })
+ }
+ if len(m.alpnProtocols) > 0 {
+ // RFC 7301, Section 3.1
+ b.AddUint16(extensionALPN)
+ b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
+ b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
+ for _, proto := range m.alpnProtocols {
+ b.AddUint8LengthPrefixed(func(b *cryptobyte.Builder) {
+ b.AddBytes([]byte(proto))
+ })
+ }
+ })
+ })
+ }
+ if m.scts {
+ // RFC 6962, Section 3.3.1
+ b.AddUint16(extensionSCT)
+ b.AddUint16(0) // empty extension_data
+ }
+ if len(m.supportedVersions) > 0 {
+ // RFC 8446, Section 4.2.1
+ b.AddUint16(extensionSupportedVersions)
+ b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
+ b.AddUint8LengthPrefixed(func(b *cryptobyte.Builder) {
+ for _, vers := range m.supportedVersions {
+ b.AddUint16(vers)
+ }
+ })
+ })
+ }
+ if len(m.cookie) > 0 {
+ // RFC 8446, Section 4.2.2
+ b.AddUint16(extensionCookie)
+ b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
+ b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
+ b.AddBytes(m.cookie)
+ })
+ })
+ }
+ if len(m.keyShares) > 0 {
+ // RFC 8446, Section 4.2.8
+ b.AddUint16(extensionKeyShare)
+ b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
+ b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
+ for _, ks := range m.keyShares {
+ b.AddUint16(uint16(ks.group))
+ b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
+ b.AddBytes(ks.data)
+ })
+ }
+ })
+ })
+ }
+ if m.earlyData {
+ // RFC 8446, Section 4.2.10
+ b.AddUint16(extensionEarlyData)
+ b.AddUint16(0) // empty extension_data
+ }
+ if len(m.pskModes) > 0 {
+ // RFC 8446, Section 4.2.9
+ b.AddUint16(extensionPSKModes)
+ b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
+ b.AddUint8LengthPrefixed(func(b *cryptobyte.Builder) {
+ b.AddBytes(m.pskModes)
+ })
+ })
+ }
+ for _, ext := range m.additionalExtensions {
+ b.AddUint16(ext.Type)
+ b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
+ b.AddBytes(ext.Data)
+ })
+ }
+ if len(m.pskIdentities) > 0 { // pre_shared_key must be the last extension
+ // RFC 8446, Section 4.2.11
+ b.AddUint16(extensionPreSharedKey)
+ b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
+ b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
+ for _, psk := range m.pskIdentities {
+ b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
+ b.AddBytes(psk.label)
+ })
+ b.AddUint32(psk.obfuscatedTicketAge)
+ }
+ })
+ b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
+ for _, binder := range m.pskBinders {
+ b.AddUint8LengthPrefixed(func(b *cryptobyte.Builder) {
+ b.AddBytes(binder)
+ })
+ }
+ })
+ })
+ }
+
+ extensionsPresent = len(b.BytesOrPanic()) > 2
+ })
+
+ if !extensionsPresent {
+ *b = bWithoutExtensions
+ }
+ })
+
+ m.raw = b.BytesOrPanic()
+ return m.raw
+}
+
+// marshalWithoutBinders returns the ClientHello through the
+// PreSharedKeyExtension.identities field, according to RFC 8446, Section
+// 4.2.11.2. Note that m.pskBinders must be set to slices of the correct length.
+func (m *clientHelloMsg) marshalWithoutBinders() []byte {
+ bindersLen := 2 // uint16 length prefix
+ for _, binder := range m.pskBinders {
+ bindersLen += 1 // uint8 length prefix
+ bindersLen += len(binder)
+ }
+
+ fullMessage := m.marshal()
+ return fullMessage[:len(fullMessage)-bindersLen]
+}
+
+// updateBinders updates the m.pskBinders field, if necessary updating the
+// cached marshaled representation. The supplied binders must have the same
+// length as the current m.pskBinders.
+func (m *clientHelloMsg) updateBinders(pskBinders [][]byte) {
+ if len(pskBinders) != len(m.pskBinders) {
+ panic("tls: internal error: pskBinders length mismatch")
+ }
+ for i := range m.pskBinders {
+ if len(pskBinders[i]) != len(m.pskBinders[i]) {
+ panic("tls: internal error: pskBinders length mismatch")
+ }
+ }
+ m.pskBinders = pskBinders
+ if m.raw != nil {
+ lenWithoutBinders := len(m.marshalWithoutBinders())
+ b := cryptobyte.NewFixedBuilder(m.raw[:lenWithoutBinders])
+ b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
+ for _, binder := range m.pskBinders {
+ b.AddUint8LengthPrefixed(func(b *cryptobyte.Builder) {
+ b.AddBytes(binder)
+ })
+ }
+ })
+ if out, err := b.Bytes(); err != nil || len(out) != len(m.raw) {
+ panic("tls: internal error: failed to update binders")
+ }
+ }
+}
+
+func (m *clientHelloMsg) unmarshal(data []byte) bool {
+ *m = clientHelloMsg{raw: data}
+ s := cryptobyte.String(data)
+
+ if !s.Skip(4) || // message type and uint24 length field
+ !s.ReadUint16(&m.vers) || !s.ReadBytes(&m.random, 32) ||
+ !readUint8LengthPrefixed(&s, &m.sessionId) {
+ return false
+ }
+
+ var cipherSuites cryptobyte.String
+ if !s.ReadUint16LengthPrefixed(&cipherSuites) {
+ return false
+ }
+ m.cipherSuites = []uint16{}
+ m.secureRenegotiationSupported = false
+ for !cipherSuites.Empty() {
+ var suite uint16
+ if !cipherSuites.ReadUint16(&suite) {
+ return false
+ }
+ if suite == scsvRenegotiation {
+ m.secureRenegotiationSupported = true
+ }
+ m.cipherSuites = append(m.cipherSuites, suite)
+ }
+
+ if !readUint8LengthPrefixed(&s, &m.compressionMethods) {
+ return false
+ }
+
+ if s.Empty() {
+ // ClientHello is optionally followed by extension data
+ return true
+ }
+
+ var extensions cryptobyte.String
+ if !s.ReadUint16LengthPrefixed(&extensions) || !s.Empty() {
+ return false
+ }
+
+ seenExts := make(map[uint16]bool)
+ for !extensions.Empty() {
+ var extension uint16
+ var extData cryptobyte.String
+ if !extensions.ReadUint16(&extension) ||
+ !extensions.ReadUint16LengthPrefixed(&extData) {
+ return false
+ }
+
+ if seenExts[extension] {
+ return false
+ }
+ seenExts[extension] = true
+
+ switch extension {
+ case extensionServerName:
+ // RFC 6066, Section 3
+ var nameList cryptobyte.String
+ if !extData.ReadUint16LengthPrefixed(&nameList) || nameList.Empty() {
+ return false
+ }
+ for !nameList.Empty() {
+ var nameType uint8
+ var serverName cryptobyte.String
+ if !nameList.ReadUint8(&nameType) ||
+ !nameList.ReadUint16LengthPrefixed(&serverName) ||
+ serverName.Empty() {
+ return false
+ }
+ if nameType != 0 {
+ continue
+ }
+ if len(m.serverName) != 0 {
+ // Multiple names of the same name_type are prohibited.
+ return false
+ }
+ m.serverName = string(serverName)
+ // An SNI value may not include a trailing dot.
+ if strings.HasSuffix(m.serverName, ".") {
+ return false
+ }
+ }
+ case extensionStatusRequest:
+ // RFC 4366, Section 3.6
+ var statusType uint8
+ var ignored cryptobyte.String
+ if !extData.ReadUint8(&statusType) ||
+ !extData.ReadUint16LengthPrefixed(&ignored) ||
+ !extData.ReadUint16LengthPrefixed(&ignored) {
+ return false
+ }
+ m.ocspStapling = statusType == statusTypeOCSP
+ case extensionSupportedCurves:
+ // RFC 4492, sections 5.1.1 and RFC 8446, Section 4.2.7
+ var curves cryptobyte.String
+ if !extData.ReadUint16LengthPrefixed(&curves) || curves.Empty() {
+ return false
+ }
+ for !curves.Empty() {
+ var curve uint16
+ if !curves.ReadUint16(&curve) {
+ return false
+ }
+ m.supportedCurves = append(m.supportedCurves, CurveID(curve))
+ }
+ case extensionSupportedPoints:
+ // RFC 4492, Section 5.1.2
+ if !readUint8LengthPrefixed(&extData, &m.supportedPoints) ||
+ len(m.supportedPoints) == 0 {
+ return false
+ }
+ case extensionSessionTicket:
+ // RFC 5077, Section 3.2
+ m.ticketSupported = true
+ extData.ReadBytes(&m.sessionTicket, len(extData))
+ case extensionSignatureAlgorithms:
+ // RFC 5246, Section 7.4.1.4.1
+ var sigAndAlgs cryptobyte.String
+ if !extData.ReadUint16LengthPrefixed(&sigAndAlgs) || sigAndAlgs.Empty() {
+ return false
+ }
+ for !sigAndAlgs.Empty() {
+ var sigAndAlg uint16
+ if !sigAndAlgs.ReadUint16(&sigAndAlg) {
+ return false
+ }
+ m.supportedSignatureAlgorithms = append(
+ m.supportedSignatureAlgorithms, SignatureScheme(sigAndAlg))
+ }
+ case extensionSignatureAlgorithmsCert:
+ // RFC 8446, Section 4.2.3
+ var sigAndAlgs cryptobyte.String
+ if !extData.ReadUint16LengthPrefixed(&sigAndAlgs) || sigAndAlgs.Empty() {
+ return false
+ }
+ for !sigAndAlgs.Empty() {
+ var sigAndAlg uint16
+ if !sigAndAlgs.ReadUint16(&sigAndAlg) {
+ return false
+ }
+ m.supportedSignatureAlgorithmsCert = append(
+ m.supportedSignatureAlgorithmsCert, SignatureScheme(sigAndAlg))
+ }
+ case extensionRenegotiationInfo:
+ // RFC 5746, Section 3.2
+ if !readUint8LengthPrefixed(&extData, &m.secureRenegotiation) {
+ return false
+ }
+ m.secureRenegotiationSupported = true
+ case extensionALPN:
+ // RFC 7301, Section 3.1
+ var protoList cryptobyte.String
+ if !extData.ReadUint16LengthPrefixed(&protoList) || protoList.Empty() {
+ return false
+ }
+ for !protoList.Empty() {
+ var proto cryptobyte.String
+ if !protoList.ReadUint8LengthPrefixed(&proto) || proto.Empty() {
+ return false
+ }
+ m.alpnProtocols = append(m.alpnProtocols, string(proto))
+ }
+ case extensionSCT:
+ // RFC 6962, Section 3.3.1
+ m.scts = true
+ case extensionSupportedVersions:
+ // RFC 8446, Section 4.2.1
+ var versList cryptobyte.String
+ if !extData.ReadUint8LengthPrefixed(&versList) || versList.Empty() {
+ return false
+ }
+ for !versList.Empty() {
+ var vers uint16
+ if !versList.ReadUint16(&vers) {
+ return false
+ }
+ m.supportedVersions = append(m.supportedVersions, vers)
+ }
+ case extensionCookie:
+ // RFC 8446, Section 4.2.2
+ if !readUint16LengthPrefixed(&extData, &m.cookie) ||
+ len(m.cookie) == 0 {
+ return false
+ }
+ case extensionKeyShare:
+ // RFC 8446, Section 4.2.8
+ var clientShares cryptobyte.String
+ if !extData.ReadUint16LengthPrefixed(&clientShares) {
+ return false
+ }
+ for !clientShares.Empty() {
+ var ks keyShare
+ if !clientShares.ReadUint16((*uint16)(&ks.group)) ||
+ !readUint16LengthPrefixed(&clientShares, &ks.data) ||
+ len(ks.data) == 0 {
+ return false
+ }
+ m.keyShares = append(m.keyShares, ks)
+ }
+ case extensionEarlyData:
+ // RFC 8446, Section 4.2.10
+ m.earlyData = true
+ case extensionPSKModes:
+ // RFC 8446, Section 4.2.9
+ if !readUint8LengthPrefixed(&extData, &m.pskModes) {
+ return false
+ }
+ case extensionPreSharedKey:
+ // RFC 8446, Section 4.2.11
+ if !extensions.Empty() {
+ return false // pre_shared_key must be the last extension
+ }
+ var identities cryptobyte.String
+ if !extData.ReadUint16LengthPrefixed(&identities) || identities.Empty() {
+ return false
+ }
+ for !identities.Empty() {
+ var psk pskIdentity
+ if !readUint16LengthPrefixed(&identities, &psk.label) ||
+ !identities.ReadUint32(&psk.obfuscatedTicketAge) ||
+ len(psk.label) == 0 {
+ return false
+ }
+ m.pskIdentities = append(m.pskIdentities, psk)
+ }
+ var binders cryptobyte.String
+ if !extData.ReadUint16LengthPrefixed(&binders) || binders.Empty() {
+ return false
+ }
+ for !binders.Empty() {
+ var binder []byte
+ if !readUint8LengthPrefixed(&binders, &binder) ||
+ len(binder) == 0 {
+ return false
+ }
+ m.pskBinders = append(m.pskBinders, binder)
+ }
+ default:
+ m.additionalExtensions = append(m.additionalExtensions, Extension{Type: extension, Data: extData})
+ continue
+ }
+
+ if !extData.Empty() {
+ return false
+ }
+ }
+
+ return true
+}
+
+type serverHelloMsg struct {
+ raw []byte
+ vers uint16
+ random []byte
+ sessionId []byte
+ cipherSuite uint16
+ compressionMethod uint8
+ ocspStapling bool
+ ticketSupported bool
+ secureRenegotiationSupported bool
+ secureRenegotiation []byte
+ alpnProtocol string
+ scts [][]byte
+ supportedVersion uint16
+ serverShare keyShare
+ selectedIdentityPresent bool
+ selectedIdentity uint16
+ supportedPoints []uint8
+
+ // HelloRetryRequest extensions
+ cookie []byte
+ selectedGroup CurveID
+}
+
+func (m *serverHelloMsg) marshal() []byte {
+ if m.raw != nil {
+ return m.raw
+ }
+
+ var b cryptobyte.Builder
+ b.AddUint8(typeServerHello)
+ b.AddUint24LengthPrefixed(func(b *cryptobyte.Builder) {
+ b.AddUint16(m.vers)
+ addBytesWithLength(b, m.random, 32)
+ b.AddUint8LengthPrefixed(func(b *cryptobyte.Builder) {
+ b.AddBytes(m.sessionId)
+ })
+ b.AddUint16(m.cipherSuite)
+ b.AddUint8(m.compressionMethod)
+
+ // If extensions aren't present, omit them.
+ var extensionsPresent bool
+ bWithoutExtensions := *b
+
+ b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
+ if m.ocspStapling {
+ b.AddUint16(extensionStatusRequest)
+ b.AddUint16(0) // empty extension_data
+ }
+ if m.ticketSupported {
+ b.AddUint16(extensionSessionTicket)
+ b.AddUint16(0) // empty extension_data
+ }
+ if m.secureRenegotiationSupported {
+ b.AddUint16(extensionRenegotiationInfo)
+ b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
+ b.AddUint8LengthPrefixed(func(b *cryptobyte.Builder) {
+ b.AddBytes(m.secureRenegotiation)
+ })
+ })
+ }
+ if len(m.alpnProtocol) > 0 {
+ b.AddUint16(extensionALPN)
+ b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
+ b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
+ b.AddUint8LengthPrefixed(func(b *cryptobyte.Builder) {
+ b.AddBytes([]byte(m.alpnProtocol))
+ })
+ })
+ })
+ }
+ if len(m.scts) > 0 {
+ b.AddUint16(extensionSCT)
+ b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
+ b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
+ for _, sct := range m.scts {
+ b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
+ b.AddBytes(sct)
+ })
+ }
+ })
+ })
+ }
+ if m.supportedVersion != 0 {
+ b.AddUint16(extensionSupportedVersions)
+ b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
+ b.AddUint16(m.supportedVersion)
+ })
+ }
+ if m.serverShare.group != 0 {
+ b.AddUint16(extensionKeyShare)
+ b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
+ b.AddUint16(uint16(m.serverShare.group))
+ b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
+ b.AddBytes(m.serverShare.data)
+ })
+ })
+ }
+ if m.selectedIdentityPresent {
+ b.AddUint16(extensionPreSharedKey)
+ b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
+ b.AddUint16(m.selectedIdentity)
+ })
+ }
+
+ if len(m.cookie) > 0 {
+ b.AddUint16(extensionCookie)
+ b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
+ b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
+ b.AddBytes(m.cookie)
+ })
+ })
+ }
+ if m.selectedGroup != 0 {
+ b.AddUint16(extensionKeyShare)
+ b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
+ b.AddUint16(uint16(m.selectedGroup))
+ })
+ }
+ if len(m.supportedPoints) > 0 {
+ b.AddUint16(extensionSupportedPoints)
+ b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
+ b.AddUint8LengthPrefixed(func(b *cryptobyte.Builder) {
+ b.AddBytes(m.supportedPoints)
+ })
+ })
+ }
+
+ extensionsPresent = len(b.BytesOrPanic()) > 2
+ })
+
+ if !extensionsPresent {
+ *b = bWithoutExtensions
+ }
+ })
+
+ m.raw = b.BytesOrPanic()
+ return m.raw
+}
+
+func (m *serverHelloMsg) unmarshal(data []byte) bool {
+ *m = serverHelloMsg{raw: data}
+ s := cryptobyte.String(data)
+
+ if !s.Skip(4) || // message type and uint24 length field
+ !s.ReadUint16(&m.vers) || !s.ReadBytes(&m.random, 32) ||
+ !readUint8LengthPrefixed(&s, &m.sessionId) ||
+ !s.ReadUint16(&m.cipherSuite) ||
+ !s.ReadUint8(&m.compressionMethod) {
+ return false
+ }
+
+ if s.Empty() {
+ // ServerHello is optionally followed by extension data
+ return true
+ }
+
+ var extensions cryptobyte.String
+ if !s.ReadUint16LengthPrefixed(&extensions) || !s.Empty() {
+ return false
+ }
+
+ seenExts := make(map[uint16]bool)
+ for !extensions.Empty() {
+ var extension uint16
+ var extData cryptobyte.String
+ if !extensions.ReadUint16(&extension) ||
+ !extensions.ReadUint16LengthPrefixed(&extData) {
+ return false
+ }
+
+ if seenExts[extension] {
+ return false
+ }
+ seenExts[extension] = true
+
+ switch extension {
+ case extensionStatusRequest:
+ m.ocspStapling = true
+ case extensionSessionTicket:
+ m.ticketSupported = true
+ case extensionRenegotiationInfo:
+ if !readUint8LengthPrefixed(&extData, &m.secureRenegotiation) {
+ return false
+ }
+ m.secureRenegotiationSupported = true
+ case extensionALPN:
+ var protoList cryptobyte.String
+ if !extData.ReadUint16LengthPrefixed(&protoList) || protoList.Empty() {
+ return false
+ }
+ var proto cryptobyte.String
+ if !protoList.ReadUint8LengthPrefixed(&proto) ||
+ proto.Empty() || !protoList.Empty() {
+ return false
+ }
+ m.alpnProtocol = string(proto)
+ case extensionSCT:
+ var sctList cryptobyte.String
+ if !extData.ReadUint16LengthPrefixed(&sctList) || sctList.Empty() {
+ return false
+ }
+ for !sctList.Empty() {
+ var sct []byte
+ if !readUint16LengthPrefixed(&sctList, &sct) ||
+ len(sct) == 0 {
+ return false
+ }
+ m.scts = append(m.scts, sct)
+ }
+ case extensionSupportedVersions:
+ if !extData.ReadUint16(&m.supportedVersion) {
+ return false
+ }
+ case extensionCookie:
+ if !readUint16LengthPrefixed(&extData, &m.cookie) ||
+ len(m.cookie) == 0 {
+ return false
+ }
+ case extensionKeyShare:
+ // This extension has different formats in SH and HRR, accept either
+ // and let the handshake logic decide. See RFC 8446, Section 4.2.8.
+ if len(extData) == 2 {
+ if !extData.ReadUint16((*uint16)(&m.selectedGroup)) {
+ return false
+ }
+ } else {
+ if !extData.ReadUint16((*uint16)(&m.serverShare.group)) ||
+ !readUint16LengthPrefixed(&extData, &m.serverShare.data) {
+ return false
+ }
+ }
+ case extensionPreSharedKey:
+ m.selectedIdentityPresent = true
+ if !extData.ReadUint16(&m.selectedIdentity) {
+ return false
+ }
+ case extensionSupportedPoints:
+ // RFC 4492, Section 5.1.2
+ if !readUint8LengthPrefixed(&extData, &m.supportedPoints) ||
+ len(m.supportedPoints) == 0 {
+ return false
+ }
+ default:
+ // Ignore unknown extensions.
+ continue
+ }
+
+ if !extData.Empty() {
+ return false
+ }
+ }
+
+ return true
+}
+
+type encryptedExtensionsMsg struct {
+ raw []byte
+ alpnProtocol string
+ earlyData bool
+
+ additionalExtensions []Extension
+}
+
+func (m *encryptedExtensionsMsg) marshal() []byte {
+ if m.raw != nil {
+ return m.raw
+ }
+
+ var b cryptobyte.Builder
+ b.AddUint8(typeEncryptedExtensions)
+ b.AddUint24LengthPrefixed(func(b *cryptobyte.Builder) {
+ b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
+ if len(m.alpnProtocol) > 0 {
+ b.AddUint16(extensionALPN)
+ b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
+ b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
+ b.AddUint8LengthPrefixed(func(b *cryptobyte.Builder) {
+ b.AddBytes([]byte(m.alpnProtocol))
+ })
+ })
+ })
+ }
+ if m.earlyData {
+ // RFC 8446, Section 4.2.10
+ b.AddUint16(extensionEarlyData)
+ b.AddUint16(0) // empty extension_data
+ }
+ for _, ext := range m.additionalExtensions {
+ b.AddUint16(ext.Type)
+ b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
+ b.AddBytes(ext.Data)
+ })
+ }
+ })
+ })
+
+ m.raw = b.BytesOrPanic()
+ return m.raw
+}
+
+func (m *encryptedExtensionsMsg) unmarshal(data []byte) bool {
+ *m = encryptedExtensionsMsg{raw: data}
+ s := cryptobyte.String(data)
+
+ var extensions cryptobyte.String
+ if !s.Skip(4) || // message type and uint24 length field
+ !s.ReadUint16LengthPrefixed(&extensions) || !s.Empty() {
+ return false
+ }
+
+ for !extensions.Empty() {
+ var ext uint16
+ var extData cryptobyte.String
+ if !extensions.ReadUint16(&ext) ||
+ !extensions.ReadUint16LengthPrefixed(&extData) {
+ return false
+ }
+
+ switch ext {
+ case extensionALPN:
+ var protoList cryptobyte.String
+ if !extData.ReadUint16LengthPrefixed(&protoList) || protoList.Empty() {
+ return false
+ }
+ var proto cryptobyte.String
+ if !protoList.ReadUint8LengthPrefixed(&proto) ||
+ proto.Empty() || !protoList.Empty() {
+ return false
+ }
+ m.alpnProtocol = string(proto)
+ case extensionEarlyData:
+ m.earlyData = true
+ default:
+ m.additionalExtensions = append(m.additionalExtensions, Extension{Type: ext, Data: extData})
+ continue
+ }
+
+ if !extData.Empty() {
+ return false
+ }
+ }
+
+ return true
+}
+
+type endOfEarlyDataMsg struct{}
+
+func (m *endOfEarlyDataMsg) marshal() []byte {
+ x := make([]byte, 4)
+ x[0] = typeEndOfEarlyData
+ return x
+}
+
+func (m *endOfEarlyDataMsg) unmarshal(data []byte) bool {
+ return len(data) == 4
+}
+
+type keyUpdateMsg struct {
+ raw []byte
+ updateRequested bool
+}
+
+func (m *keyUpdateMsg) marshal() []byte {
+ if m.raw != nil {
+ return m.raw
+ }
+
+ var b cryptobyte.Builder
+ b.AddUint8(typeKeyUpdate)
+ b.AddUint24LengthPrefixed(func(b *cryptobyte.Builder) {
+ if m.updateRequested {
+ b.AddUint8(1)
+ } else {
+ b.AddUint8(0)
+ }
+ })
+
+ m.raw = b.BytesOrPanic()
+ return m.raw
+}
+
+func (m *keyUpdateMsg) unmarshal(data []byte) bool {
+ m.raw = data
+ s := cryptobyte.String(data)
+
+ var updateRequested uint8
+ if !s.Skip(4) || // message type and uint24 length field
+ !s.ReadUint8(&updateRequested) || !s.Empty() {
+ return false
+ }
+ switch updateRequested {
+ case 0:
+ m.updateRequested = false
+ case 1:
+ m.updateRequested = true
+ default:
+ return false
+ }
+ return true
+}
+
+type newSessionTicketMsgTLS13 struct {
+ raw []byte
+ lifetime uint32
+ ageAdd uint32
+ nonce []byte
+ label []byte
+ maxEarlyData uint32
+}
+
+func (m *newSessionTicketMsgTLS13) marshal() []byte {
+ if m.raw != nil {
+ return m.raw
+ }
+
+ var b cryptobyte.Builder
+ b.AddUint8(typeNewSessionTicket)
+ b.AddUint24LengthPrefixed(func(b *cryptobyte.Builder) {
+ b.AddUint32(m.lifetime)
+ b.AddUint32(m.ageAdd)
+ b.AddUint8LengthPrefixed(func(b *cryptobyte.Builder) {
+ b.AddBytes(m.nonce)
+ })
+ b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
+ b.AddBytes(m.label)
+ })
+
+ b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
+ if m.maxEarlyData > 0 {
+ b.AddUint16(extensionEarlyData)
+ b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
+ b.AddUint32(m.maxEarlyData)
+ })
+ }
+ })
+ })
+
+ m.raw = b.BytesOrPanic()
+ return m.raw
+}
+
+func (m *newSessionTicketMsgTLS13) unmarshal(data []byte) bool {
+ *m = newSessionTicketMsgTLS13{raw: data}
+ s := cryptobyte.String(data)
+
+ var extensions cryptobyte.String
+ if !s.Skip(4) || // message type and uint24 length field
+ !s.ReadUint32(&m.lifetime) ||
+ !s.ReadUint32(&m.ageAdd) ||
+ !readUint8LengthPrefixed(&s, &m.nonce) ||
+ !readUint16LengthPrefixed(&s, &m.label) ||
+ !s.ReadUint16LengthPrefixed(&extensions) ||
+ !s.Empty() {
+ return false
+ }
+
+ for !extensions.Empty() {
+ var extension uint16
+ var extData cryptobyte.String
+ if !extensions.ReadUint16(&extension) ||
+ !extensions.ReadUint16LengthPrefixed(&extData) {
+ return false
+ }
+
+ switch extension {
+ case extensionEarlyData:
+ if !extData.ReadUint32(&m.maxEarlyData) {
+ return false
+ }
+ default:
+ // Ignore unknown extensions.
+ continue
+ }
+
+ if !extData.Empty() {
+ return false
+ }
+ }
+
+ return true
+}
+
+type certificateRequestMsgTLS13 struct {
+ raw []byte
+ ocspStapling bool
+ scts bool
+ supportedSignatureAlgorithms []SignatureScheme
+ supportedSignatureAlgorithmsCert []SignatureScheme
+ certificateAuthorities [][]byte
+}
+
+func (m *certificateRequestMsgTLS13) marshal() []byte {
+ if m.raw != nil {
+ return m.raw
+ }
+
+ var b cryptobyte.Builder
+ b.AddUint8(typeCertificateRequest)
+ b.AddUint24LengthPrefixed(func(b *cryptobyte.Builder) {
+ // certificate_request_context (SHALL be zero length unless used for
+ // post-handshake authentication)
+ b.AddUint8(0)
+
+ b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
+ if m.ocspStapling {
+ b.AddUint16(extensionStatusRequest)
+ b.AddUint16(0) // empty extension_data
+ }
+ if m.scts {
+ // RFC 8446, Section 4.4.2.1 makes no mention of
+ // signed_certificate_timestamp in CertificateRequest, but
+ // "Extensions in the Certificate message from the client MUST
+ // correspond to extensions in the CertificateRequest message
+ // from the server." and it appears in the table in Section 4.2.
+ b.AddUint16(extensionSCT)
+ b.AddUint16(0) // empty extension_data
+ }
+ if len(m.supportedSignatureAlgorithms) > 0 {
+ b.AddUint16(extensionSignatureAlgorithms)
+ b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
+ b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
+ for _, sigAlgo := range m.supportedSignatureAlgorithms {
+ b.AddUint16(uint16(sigAlgo))
+ }
+ })
+ })
+ }
+ if len(m.supportedSignatureAlgorithmsCert) > 0 {
+ b.AddUint16(extensionSignatureAlgorithmsCert)
+ b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
+ b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
+ for _, sigAlgo := range m.supportedSignatureAlgorithmsCert {
+ b.AddUint16(uint16(sigAlgo))
+ }
+ })
+ })
+ }
+ if len(m.certificateAuthorities) > 0 {
+ b.AddUint16(extensionCertificateAuthorities)
+ b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
+ b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
+ for _, ca := range m.certificateAuthorities {
+ b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
+ b.AddBytes(ca)
+ })
+ }
+ })
+ })
+ }
+ })
+ })
+
+ m.raw = b.BytesOrPanic()
+ return m.raw
+}
+
+func (m *certificateRequestMsgTLS13) unmarshal(data []byte) bool {
+ *m = certificateRequestMsgTLS13{raw: data}
+ s := cryptobyte.String(data)
+
+ var context, extensions cryptobyte.String
+ if !s.Skip(4) || // message type and uint24 length field
+ !s.ReadUint8LengthPrefixed(&context) || !context.Empty() ||
+ !s.ReadUint16LengthPrefixed(&extensions) ||
+ !s.Empty() {
+ return false
+ }
+
+ for !extensions.Empty() {
+ var extension uint16
+ var extData cryptobyte.String
+ if !extensions.ReadUint16(&extension) ||
+ !extensions.ReadUint16LengthPrefixed(&extData) {
+ return false
+ }
+
+ switch extension {
+ case extensionStatusRequest:
+ m.ocspStapling = true
+ case extensionSCT:
+ m.scts = true
+ case extensionSignatureAlgorithms:
+ var sigAndAlgs cryptobyte.String
+ if !extData.ReadUint16LengthPrefixed(&sigAndAlgs) || sigAndAlgs.Empty() {
+ return false
+ }
+ for !sigAndAlgs.Empty() {
+ var sigAndAlg uint16
+ if !sigAndAlgs.ReadUint16(&sigAndAlg) {
+ return false
+ }
+ m.supportedSignatureAlgorithms = append(
+ m.supportedSignatureAlgorithms, SignatureScheme(sigAndAlg))
+ }
+ case extensionSignatureAlgorithmsCert:
+ var sigAndAlgs cryptobyte.String
+ if !extData.ReadUint16LengthPrefixed(&sigAndAlgs) || sigAndAlgs.Empty() {
+ return false
+ }
+ for !sigAndAlgs.Empty() {
+ var sigAndAlg uint16
+ if !sigAndAlgs.ReadUint16(&sigAndAlg) {
+ return false
+ }
+ m.supportedSignatureAlgorithmsCert = append(
+ m.supportedSignatureAlgorithmsCert, SignatureScheme(sigAndAlg))
+ }
+ case extensionCertificateAuthorities:
+ var auths cryptobyte.String
+ if !extData.ReadUint16LengthPrefixed(&auths) || auths.Empty() {
+ return false
+ }
+ for !auths.Empty() {
+ var ca []byte
+ if !readUint16LengthPrefixed(&auths, &ca) || len(ca) == 0 {
+ return false
+ }
+ m.certificateAuthorities = append(m.certificateAuthorities, ca)
+ }
+ default:
+ // Ignore unknown extensions.
+ continue
+ }
+
+ if !extData.Empty() {
+ return false
+ }
+ }
+
+ return true
+}
+
+type certificateMsg struct {
+ raw []byte
+ certificates [][]byte
+}
+
+func (m *certificateMsg) marshal() (x []byte) {
+ if m.raw != nil {
+ return m.raw
+ }
+
+ var i int
+ for _, slice := range m.certificates {
+ i += len(slice)
+ }
+
+ length := 3 + 3*len(m.certificates) + i
+ x = make([]byte, 4+length)
+ x[0] = typeCertificate
+ x[1] = uint8(length >> 16)
+ x[2] = uint8(length >> 8)
+ x[3] = uint8(length)
+
+ certificateOctets := length - 3
+ x[4] = uint8(certificateOctets >> 16)
+ x[5] = uint8(certificateOctets >> 8)
+ x[6] = uint8(certificateOctets)
+
+ y := x[7:]
+ for _, slice := range m.certificates {
+ y[0] = uint8(len(slice) >> 16)
+ y[1] = uint8(len(slice) >> 8)
+ y[2] = uint8(len(slice))
+ copy(y[3:], slice)
+ y = y[3+len(slice):]
+ }
+
+ m.raw = x
+ return
+}
+
+func (m *certificateMsg) unmarshal(data []byte) bool {
+ if len(data) < 7 {
+ return false
+ }
+
+ m.raw = data
+ certsLen := uint32(data[4])<<16 | uint32(data[5])<<8 | uint32(data[6])
+ if uint32(len(data)) != certsLen+7 {
+ return false
+ }
+
+ numCerts := 0
+ d := data[7:]
+ for certsLen > 0 {
+ if len(d) < 4 {
+ return false
+ }
+ certLen := uint32(d[0])<<16 | uint32(d[1])<<8 | uint32(d[2])
+ if uint32(len(d)) < 3+certLen {
+ return false
+ }
+ d = d[3+certLen:]
+ certsLen -= 3 + certLen
+ numCerts++
+ }
+
+ m.certificates = make([][]byte, numCerts)
+ d = data[7:]
+ for i := 0; i < numCerts; i++ {
+ certLen := uint32(d[0])<<16 | uint32(d[1])<<8 | uint32(d[2])
+ m.certificates[i] = d[3 : 3+certLen]
+ d = d[3+certLen:]
+ }
+
+ return true
+}
+
+type certificateMsgTLS13 struct {
+ raw []byte
+ certificate Certificate
+ ocspStapling bool
+ scts bool
+}
+
+func (m *certificateMsgTLS13) marshal() []byte {
+ if m.raw != nil {
+ return m.raw
+ }
+
+ var b cryptobyte.Builder
+ b.AddUint8(typeCertificate)
+ b.AddUint24LengthPrefixed(func(b *cryptobyte.Builder) {
+ b.AddUint8(0) // certificate_request_context
+
+ certificate := m.certificate
+ if !m.ocspStapling {
+ certificate.OCSPStaple = nil
+ }
+ if !m.scts {
+ certificate.SignedCertificateTimestamps = nil
+ }
+ marshalCertificate(b, certificate)
+ })
+
+ m.raw = b.BytesOrPanic()
+ return m.raw
+}
+
+func marshalCertificate(b *cryptobyte.Builder, certificate Certificate) {
+ b.AddUint24LengthPrefixed(func(b *cryptobyte.Builder) {
+ for i, cert := range certificate.Certificate {
+ b.AddUint24LengthPrefixed(func(b *cryptobyte.Builder) {
+ b.AddBytes(cert)
+ })
+ b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
+ if i > 0 {
+ // This library only supports OCSP and SCT for leaf certificates.
+ return
+ }
+ if certificate.OCSPStaple != nil {
+ b.AddUint16(extensionStatusRequest)
+ b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
+ b.AddUint8(statusTypeOCSP)
+ b.AddUint24LengthPrefixed(func(b *cryptobyte.Builder) {
+ b.AddBytes(certificate.OCSPStaple)
+ })
+ })
+ }
+ if certificate.SignedCertificateTimestamps != nil {
+ b.AddUint16(extensionSCT)
+ b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
+ b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
+ for _, sct := range certificate.SignedCertificateTimestamps {
+ b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
+ b.AddBytes(sct)
+ })
+ }
+ })
+ })
+ }
+ })
+ }
+ })
+}
+
+func (m *certificateMsgTLS13) unmarshal(data []byte) bool {
+ *m = certificateMsgTLS13{raw: data}
+ s := cryptobyte.String(data)
+
+ var context cryptobyte.String
+ if !s.Skip(4) || // message type and uint24 length field
+ !s.ReadUint8LengthPrefixed(&context) || !context.Empty() ||
+ !unmarshalCertificate(&s, &m.certificate) ||
+ !s.Empty() {
+ return false
+ }
+
+ m.scts = m.certificate.SignedCertificateTimestamps != nil
+ m.ocspStapling = m.certificate.OCSPStaple != nil
+
+ return true
+}
+
+func unmarshalCertificate(s *cryptobyte.String, certificate *Certificate) bool {
+ var certList cryptobyte.String
+ if !s.ReadUint24LengthPrefixed(&certList) {
+ return false
+ }
+ for !certList.Empty() {
+ var cert []byte
+ var extensions cryptobyte.String
+ if !readUint24LengthPrefixed(&certList, &cert) ||
+ !certList.ReadUint16LengthPrefixed(&extensions) {
+ return false
+ }
+ certificate.Certificate = append(certificate.Certificate, cert)
+ for !extensions.Empty() {
+ var extension uint16
+ var extData cryptobyte.String
+ if !extensions.ReadUint16(&extension) ||
+ !extensions.ReadUint16LengthPrefixed(&extData) {
+ return false
+ }
+ if len(certificate.Certificate) > 1 {
+ // This library only supports OCSP and SCT for leaf certificates.
+ continue
+ }
+
+ switch extension {
+ case extensionStatusRequest:
+ var statusType uint8
+ if !extData.ReadUint8(&statusType) || statusType != statusTypeOCSP ||
+ !readUint24LengthPrefixed(&extData, &certificate.OCSPStaple) ||
+ len(certificate.OCSPStaple) == 0 {
+ return false
+ }
+ case extensionSCT:
+ var sctList cryptobyte.String
+ if !extData.ReadUint16LengthPrefixed(&sctList) || sctList.Empty() {
+ return false
+ }
+ for !sctList.Empty() {
+ var sct []byte
+ if !readUint16LengthPrefixed(&sctList, &sct) ||
+ len(sct) == 0 {
+ return false
+ }
+ certificate.SignedCertificateTimestamps = append(
+ certificate.SignedCertificateTimestamps, sct)
+ }
+ default:
+ // Ignore unknown extensions.
+ continue
+ }
+
+ if !extData.Empty() {
+ return false
+ }
+ }
+ }
+ return true
+}
+
+type serverKeyExchangeMsg struct {
+ raw []byte
+ key []byte
+}
+
+func (m *serverKeyExchangeMsg) marshal() []byte {
+ if m.raw != nil {
+ return m.raw
+ }
+ length := len(m.key)
+ x := make([]byte, length+4)
+ x[0] = typeServerKeyExchange
+ x[1] = uint8(length >> 16)
+ x[2] = uint8(length >> 8)
+ x[3] = uint8(length)
+ copy(x[4:], m.key)
+
+ m.raw = x
+ return x
+}
+
+func (m *serverKeyExchangeMsg) unmarshal(data []byte) bool {
+ m.raw = data
+ if len(data) < 4 {
+ return false
+ }
+ m.key = data[4:]
+ return true
+}
+
+type certificateStatusMsg struct {
+ raw []byte
+ response []byte
+}
+
+func (m *certificateStatusMsg) marshal() []byte {
+ if m.raw != nil {
+ return m.raw
+ }
+
+ var b cryptobyte.Builder
+ b.AddUint8(typeCertificateStatus)
+ b.AddUint24LengthPrefixed(func(b *cryptobyte.Builder) {
+ b.AddUint8(statusTypeOCSP)
+ b.AddUint24LengthPrefixed(func(b *cryptobyte.Builder) {
+ b.AddBytes(m.response)
+ })
+ })
+
+ m.raw = b.BytesOrPanic()
+ return m.raw
+}
+
+func (m *certificateStatusMsg) unmarshal(data []byte) bool {
+ m.raw = data
+ s := cryptobyte.String(data)
+
+ var statusType uint8
+ if !s.Skip(4) || // message type and uint24 length field
+ !s.ReadUint8(&statusType) || statusType != statusTypeOCSP ||
+ !readUint24LengthPrefixed(&s, &m.response) ||
+ len(m.response) == 0 || !s.Empty() {
+ return false
+ }
+ return true
+}
+
+type serverHelloDoneMsg struct{}
+
+func (m *serverHelloDoneMsg) marshal() []byte {
+ x := make([]byte, 4)
+ x[0] = typeServerHelloDone
+ return x
+}
+
+func (m *serverHelloDoneMsg) unmarshal(data []byte) bool {
+ return len(data) == 4
+}
+
+type clientKeyExchangeMsg struct {
+ raw []byte
+ ciphertext []byte
+}
+
+func (m *clientKeyExchangeMsg) marshal() []byte {
+ if m.raw != nil {
+ return m.raw
+ }
+ length := len(m.ciphertext)
+ x := make([]byte, length+4)
+ x[0] = typeClientKeyExchange
+ x[1] = uint8(length >> 16)
+ x[2] = uint8(length >> 8)
+ x[3] = uint8(length)
+ copy(x[4:], m.ciphertext)
+
+ m.raw = x
+ return x
+}
+
+func (m *clientKeyExchangeMsg) unmarshal(data []byte) bool {
+ m.raw = data
+ if len(data) < 4 {
+ return false
+ }
+ l := int(data[1])<<16 | int(data[2])<<8 | int(data[3])
+ if l != len(data)-4 {
+ return false
+ }
+ m.ciphertext = data[4:]
+ return true
+}
+
+type finishedMsg struct {
+ raw []byte
+ verifyData []byte
+}
+
+func (m *finishedMsg) marshal() []byte {
+ if m.raw != nil {
+ return m.raw
+ }
+
+ var b cryptobyte.Builder
+ b.AddUint8(typeFinished)
+ b.AddUint24LengthPrefixed(func(b *cryptobyte.Builder) {
+ b.AddBytes(m.verifyData)
+ })
+
+ m.raw = b.BytesOrPanic()
+ return m.raw
+}
+
+func (m *finishedMsg) unmarshal(data []byte) bool {
+ m.raw = data
+ s := cryptobyte.String(data)
+ return s.Skip(1) &&
+ readUint24LengthPrefixed(&s, &m.verifyData) &&
+ s.Empty()
+}
+
+type certificateRequestMsg struct {
+ raw []byte
+ // hasSignatureAlgorithm indicates whether this message includes a list of
+ // supported signature algorithms. This change was introduced with TLS 1.2.
+ hasSignatureAlgorithm bool
+
+ certificateTypes []byte
+ supportedSignatureAlgorithms []SignatureScheme
+ certificateAuthorities [][]byte
+}
+
+func (m *certificateRequestMsg) marshal() (x []byte) {
+ if m.raw != nil {
+ return m.raw
+ }
+
+ // See RFC 4346, Section 7.4.4.
+ length := 1 + len(m.certificateTypes) + 2
+ casLength := 0
+ for _, ca := range m.certificateAuthorities {
+ casLength += 2 + len(ca)
+ }
+ length += casLength
+
+ if m.hasSignatureAlgorithm {
+ length += 2 + 2*len(m.supportedSignatureAlgorithms)
+ }
+
+ x = make([]byte, 4+length)
+ x[0] = typeCertificateRequest
+ x[1] = uint8(length >> 16)
+ x[2] = uint8(length >> 8)
+ x[3] = uint8(length)
+
+ x[4] = uint8(len(m.certificateTypes))
+
+ copy(x[5:], m.certificateTypes)
+ y := x[5+len(m.certificateTypes):]
+
+ if m.hasSignatureAlgorithm {
+ n := len(m.supportedSignatureAlgorithms) * 2
+ y[0] = uint8(n >> 8)
+ y[1] = uint8(n)
+ y = y[2:]
+ for _, sigAlgo := range m.supportedSignatureAlgorithms {
+ y[0] = uint8(sigAlgo >> 8)
+ y[1] = uint8(sigAlgo)
+ y = y[2:]
+ }
+ }
+
+ y[0] = uint8(casLength >> 8)
+ y[1] = uint8(casLength)
+ y = y[2:]
+ for _, ca := range m.certificateAuthorities {
+ y[0] = uint8(len(ca) >> 8)
+ y[1] = uint8(len(ca))
+ y = y[2:]
+ copy(y, ca)
+ y = y[len(ca):]
+ }
+
+ m.raw = x
+ return
+}
+
+func (m *certificateRequestMsg) unmarshal(data []byte) bool {
+ m.raw = data
+
+ if len(data) < 5 {
+ return false
+ }
+
+ length := uint32(data[1])<<16 | uint32(data[2])<<8 | uint32(data[3])
+ if uint32(len(data))-4 != length {
+ return false
+ }
+
+ numCertTypes := int(data[4])
+ data = data[5:]
+ if numCertTypes == 0 || len(data) <= numCertTypes {
+ return false
+ }
+
+ m.certificateTypes = make([]byte, numCertTypes)
+ if copy(m.certificateTypes, data) != numCertTypes {
+ return false
+ }
+
+ data = data[numCertTypes:]
+
+ if m.hasSignatureAlgorithm {
+ if len(data) < 2 {
+ return false
+ }
+ sigAndHashLen := uint16(data[0])<<8 | uint16(data[1])
+ data = data[2:]
+ if sigAndHashLen&1 != 0 {
+ return false
+ }
+ if len(data) < int(sigAndHashLen) {
+ return false
+ }
+ numSigAlgos := sigAndHashLen / 2
+ m.supportedSignatureAlgorithms = make([]SignatureScheme, numSigAlgos)
+ for i := range m.supportedSignatureAlgorithms {
+ m.supportedSignatureAlgorithms[i] = SignatureScheme(data[0])<<8 | SignatureScheme(data[1])
+ data = data[2:]
+ }
+ }
+
+ if len(data) < 2 {
+ return false
+ }
+ casLength := uint16(data[0])<<8 | uint16(data[1])
+ data = data[2:]
+ if len(data) < int(casLength) {
+ return false
+ }
+ cas := make([]byte, casLength)
+ copy(cas, data)
+ data = data[casLength:]
+
+ m.certificateAuthorities = nil
+ for len(cas) > 0 {
+ if len(cas) < 2 {
+ return false
+ }
+ caLen := uint16(cas[0])<<8 | uint16(cas[1])
+ cas = cas[2:]
+
+ if len(cas) < int(caLen) {
+ return false
+ }
+
+ m.certificateAuthorities = append(m.certificateAuthorities, cas[:caLen])
+ cas = cas[caLen:]
+ }
+
+ return len(data) == 0
+}
+
+type certificateVerifyMsg struct {
+ raw []byte
+ hasSignatureAlgorithm bool // format change introduced in TLS 1.2
+ signatureAlgorithm SignatureScheme
+ signature []byte
+}
+
+func (m *certificateVerifyMsg) marshal() (x []byte) {
+ if m.raw != nil {
+ return m.raw
+ }
+
+ var b cryptobyte.Builder
+ b.AddUint8(typeCertificateVerify)
+ b.AddUint24LengthPrefixed(func(b *cryptobyte.Builder) {
+ if m.hasSignatureAlgorithm {
+ b.AddUint16(uint16(m.signatureAlgorithm))
+ }
+ b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
+ b.AddBytes(m.signature)
+ })
+ })
+
+ m.raw = b.BytesOrPanic()
+ return m.raw
+}
+
+func (m *certificateVerifyMsg) unmarshal(data []byte) bool {
+ m.raw = data
+ s := cryptobyte.String(data)
+
+ if !s.Skip(4) { // message type and uint24 length field
+ return false
+ }
+ if m.hasSignatureAlgorithm {
+ if !s.ReadUint16((*uint16)(&m.signatureAlgorithm)) {
+ return false
+ }
+ }
+ return readUint16LengthPrefixed(&s, &m.signature) && s.Empty()
+}
+
+type newSessionTicketMsg struct {
+ raw []byte
+ ticket []byte
+}
+
+func (m *newSessionTicketMsg) marshal() (x []byte) {
+ if m.raw != nil {
+ return m.raw
+ }
+
+ // See RFC 5077, Section 3.3.
+ ticketLen := len(m.ticket)
+ length := 2 + 4 + ticketLen
+ x = make([]byte, 4+length)
+ x[0] = typeNewSessionTicket
+ x[1] = uint8(length >> 16)
+ x[2] = uint8(length >> 8)
+ x[3] = uint8(length)
+ x[8] = uint8(ticketLen >> 8)
+ x[9] = uint8(ticketLen)
+ copy(x[10:], m.ticket)
+
+ m.raw = x
+
+ return
+}
+
+func (m *newSessionTicketMsg) unmarshal(data []byte) bool {
+ m.raw = data
+
+ if len(data) < 10 {
+ return false
+ }
+
+ length := uint32(data[1])<<16 | uint32(data[2])<<8 | uint32(data[3])
+ if uint32(len(data))-4 != length {
+ return false
+ }
+
+ ticketLen := int(data[8])<<8 + int(data[9])
+ if len(data)-10 != ticketLen {
+ return false
+ }
+
+ m.ticket = data[10:]
+
+ return true
+}
+
+type helloRequestMsg struct {
+}
+
+func (*helloRequestMsg) marshal() []byte {
+ return []byte{typeHelloRequest, 0, 0, 0}
+}
+
+func (*helloRequestMsg) unmarshal(data []byte) bool {
+ return len(data) == 4
+}
diff --git a/vendor/github.com/quic-go/qtls-go1-19/handshake_server.go b/vendor/github.com/quic-go/qtls-go1-19/handshake_server.go
new file mode 100644
index 0000000000..b363d53fef
--- /dev/null
+++ b/vendor/github.com/quic-go/qtls-go1-19/handshake_server.go
@@ -0,0 +1,913 @@
+// Copyright 2009 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package qtls
+
+import (
+ "context"
+ "crypto"
+ "crypto/ecdsa"
+ "crypto/ed25519"
+ "crypto/rsa"
+ "crypto/subtle"
+ "crypto/x509"
+ "errors"
+ "fmt"
+ "hash"
+ "io"
+ "sync/atomic"
+ "time"
+)
+
+// serverHandshakeState contains details of a server handshake in progress.
+// It's discarded once the handshake has completed.
+type serverHandshakeState struct {
+ c *Conn
+ ctx context.Context
+ clientHello *clientHelloMsg
+ hello *serverHelloMsg
+ suite *cipherSuite
+ ecdheOk bool
+ ecSignOk bool
+ rsaDecryptOk bool
+ rsaSignOk bool
+ sessionState *sessionState
+ finishedHash finishedHash
+ masterSecret []byte
+ cert *Certificate
+}
+
+// serverHandshake performs a TLS handshake as a server.
+func (c *Conn) serverHandshake(ctx context.Context) error {
+ c.setAlternativeRecordLayer()
+
+ clientHello, err := c.readClientHello(ctx)
+ if err != nil {
+ return err
+ }
+
+ if c.vers == VersionTLS13 {
+ hs := serverHandshakeStateTLS13{
+ c: c,
+ ctx: ctx,
+ clientHello: clientHello,
+ }
+ return hs.handshake()
+ } else if c.extraConfig.usesAlternativeRecordLayer() {
+ // This should already have been caught by the check that the ClientHello doesn't
+ // offer any (supported) versions older than TLS 1.3.
+ // Check again to make sure we can't be tricked into using an older version.
+ c.sendAlert(alertProtocolVersion)
+ return errors.New("tls: negotiated TLS < 1.3 when using QUIC")
+ }
+
+ hs := serverHandshakeState{
+ c: c,
+ ctx: ctx,
+ clientHello: clientHello,
+ }
+ return hs.handshake()
+}
+
+func (hs *serverHandshakeState) handshake() error {
+ c := hs.c
+
+ if err := hs.processClientHello(); err != nil {
+ return err
+ }
+
+ // For an overview of TLS handshaking, see RFC 5246, Section 7.3.
+ c.buffering = true
+ if hs.checkForResumption() {
+ // The client has included a session ticket and so we do an abbreviated handshake.
+ c.didResume = true
+ if err := hs.doResumeHandshake(); err != nil {
+ return err
+ }
+ if err := hs.establishKeys(); err != nil {
+ return err
+ }
+ if err := hs.sendSessionTicket(); err != nil {
+ return err
+ }
+ if err := hs.sendFinished(c.serverFinished[:]); err != nil {
+ return err
+ }
+ if _, err := c.flush(); err != nil {
+ return err
+ }
+ c.clientFinishedIsFirst = false
+ if err := hs.readFinished(nil); err != nil {
+ return err
+ }
+ } else {
+ // The client didn't include a session ticket, or it wasn't
+ // valid so we do a full handshake.
+ if err := hs.pickCipherSuite(); err != nil {
+ return err
+ }
+ if err := hs.doFullHandshake(); err != nil {
+ return err
+ }
+ if err := hs.establishKeys(); err != nil {
+ return err
+ }
+ if err := hs.readFinished(c.clientFinished[:]); err != nil {
+ return err
+ }
+ c.clientFinishedIsFirst = true
+ c.buffering = true
+ if err := hs.sendSessionTicket(); err != nil {
+ return err
+ }
+ if err := hs.sendFinished(nil); err != nil {
+ return err
+ }
+ if _, err := c.flush(); err != nil {
+ return err
+ }
+ }
+
+ c.ekm = ekmFromMasterSecret(c.vers, hs.suite, hs.masterSecret, hs.clientHello.random, hs.hello.random)
+ atomic.StoreUint32(&c.handshakeStatus, 1)
+
+ c.updateConnectionState()
+ return nil
+}
+
+// readClientHello reads a ClientHello message and selects the protocol version.
+func (c *Conn) readClientHello(ctx context.Context) (*clientHelloMsg, error) {
+ msg, err := c.readHandshake()
+ if err != nil {
+ return nil, err
+ }
+ clientHello, ok := msg.(*clientHelloMsg)
+ if !ok {
+ c.sendAlert(alertUnexpectedMessage)
+ return nil, unexpectedMessageError(clientHello, msg)
+ }
+
+ var configForClient *config
+ originalConfig := c.config
+ if c.config.GetConfigForClient != nil {
+ chi := newClientHelloInfo(ctx, c, clientHello)
+ if cfc, err := c.config.GetConfigForClient(chi); err != nil {
+ c.sendAlert(alertInternalError)
+ return nil, err
+ } else if cfc != nil {
+ configForClient = fromConfig(cfc)
+ c.config = configForClient
+ }
+ }
+ c.ticketKeys = originalConfig.ticketKeys(configForClient)
+
+ clientVersions := clientHello.supportedVersions
+ if len(clientHello.supportedVersions) == 0 {
+ clientVersions = supportedVersionsFromMax(clientHello.vers)
+ }
+ if c.extraConfig.usesAlternativeRecordLayer() {
+ // In QUIC, the client MUST NOT offer any old TLS versions.
+ // Here, we can only check that none of the other supported versions of this library
+ // (TLS 1.0 - TLS 1.2) is offered. We don't check for any SSL versions here.
+ for _, ver := range clientVersions {
+ if ver == VersionTLS13 {
+ continue
+ }
+ for _, v := range supportedVersions {
+ if ver == v {
+ c.sendAlert(alertProtocolVersion)
+ return nil, fmt.Errorf("tls: client offered old TLS version %#x", ver)
+ }
+ }
+ }
+ // Make the config we're using allows us to use TLS 1.3.
+ if c.config.maxSupportedVersion(roleServer) < VersionTLS13 {
+ c.sendAlert(alertInternalError)
+ return nil, errors.New("tls: MaxVersion prevents QUIC from using TLS 1.3")
+ }
+ }
+ c.vers, ok = c.config.mutualVersion(roleServer, clientVersions)
+ if !ok {
+ c.sendAlert(alertProtocolVersion)
+ return nil, fmt.Errorf("tls: client offered only unsupported versions: %x", clientVersions)
+ }
+ c.haveVers = true
+ c.in.version = c.vers
+ c.out.version = c.vers
+
+ return clientHello, nil
+}
+
+func (hs *serverHandshakeState) processClientHello() error {
+ c := hs.c
+
+ hs.hello = new(serverHelloMsg)
+ hs.hello.vers = c.vers
+
+ foundCompression := false
+ // We only support null compression, so check that the client offered it.
+ for _, compression := range hs.clientHello.compressionMethods {
+ if compression == compressionNone {
+ foundCompression = true
+ break
+ }
+ }
+
+ if !foundCompression {
+ c.sendAlert(alertHandshakeFailure)
+ return errors.New("tls: client does not support uncompressed connections")
+ }
+
+ hs.hello.random = make([]byte, 32)
+ serverRandom := hs.hello.random
+ // Downgrade protection canaries. See RFC 8446, Section 4.1.3.
+ maxVers := c.config.maxSupportedVersion(roleServer)
+ if maxVers >= VersionTLS12 && c.vers < maxVers || testingOnlyForceDowngradeCanary {
+ if c.vers == VersionTLS12 {
+ copy(serverRandom[24:], downgradeCanaryTLS12)
+ } else {
+ copy(serverRandom[24:], downgradeCanaryTLS11)
+ }
+ serverRandom = serverRandom[:24]
+ }
+ _, err := io.ReadFull(c.config.rand(), serverRandom)
+ if err != nil {
+ c.sendAlert(alertInternalError)
+ return err
+ }
+
+ if len(hs.clientHello.secureRenegotiation) != 0 {
+ c.sendAlert(alertHandshakeFailure)
+ return errors.New("tls: initial handshake had non-empty renegotiation extension")
+ }
+
+ hs.hello.secureRenegotiationSupported = hs.clientHello.secureRenegotiationSupported
+ hs.hello.compressionMethod = compressionNone
+ if len(hs.clientHello.serverName) > 0 {
+ c.serverName = hs.clientHello.serverName
+ }
+
+ selectedProto, err := negotiateALPN(c.config.NextProtos, hs.clientHello.alpnProtocols)
+ if err != nil {
+ c.sendAlert(alertNoApplicationProtocol)
+ return err
+ }
+ hs.hello.alpnProtocol = selectedProto
+ c.clientProtocol = selectedProto
+
+ hs.cert, err = c.config.getCertificate(newClientHelloInfo(hs.ctx, c, hs.clientHello))
+ if err != nil {
+ if err == errNoCertificates {
+ c.sendAlert(alertUnrecognizedName)
+ } else {
+ c.sendAlert(alertInternalError)
+ }
+ return err
+ }
+ if hs.clientHello.scts {
+ hs.hello.scts = hs.cert.SignedCertificateTimestamps
+ }
+
+ hs.ecdheOk = supportsECDHE(c.config, hs.clientHello.supportedCurves, hs.clientHello.supportedPoints)
+
+ if hs.ecdheOk && len(hs.clientHello.supportedPoints) > 0 {
+ // Although omitting the ec_point_formats extension is permitted, some
+ // old OpenSSL version will refuse to handshake if not present.
+ //
+ // Per RFC 4492, section 5.1.2, implementations MUST support the
+ // uncompressed point format. See golang.org/issue/31943.
+ hs.hello.supportedPoints = []uint8{pointFormatUncompressed}
+ }
+
+ if priv, ok := hs.cert.PrivateKey.(crypto.Signer); ok {
+ switch priv.Public().(type) {
+ case *ecdsa.PublicKey:
+ hs.ecSignOk = true
+ case ed25519.PublicKey:
+ hs.ecSignOk = true
+ case *rsa.PublicKey:
+ hs.rsaSignOk = true
+ default:
+ c.sendAlert(alertInternalError)
+ return fmt.Errorf("tls: unsupported signing key type (%T)", priv.Public())
+ }
+ }
+ if priv, ok := hs.cert.PrivateKey.(crypto.Decrypter); ok {
+ switch priv.Public().(type) {
+ case *rsa.PublicKey:
+ hs.rsaDecryptOk = true
+ default:
+ c.sendAlert(alertInternalError)
+ return fmt.Errorf("tls: unsupported decryption key type (%T)", priv.Public())
+ }
+ }
+
+ return nil
+}
+
+// negotiateALPN picks a shared ALPN protocol that both sides support in server
+// preference order. If ALPN is not configured or the peer doesn't support it,
+// it returns "" and no error.
+func negotiateALPN(serverProtos, clientProtos []string) (string, error) {
+ if len(serverProtos) == 0 || len(clientProtos) == 0 {
+ return "", nil
+ }
+ var http11fallback bool
+ for _, s := range serverProtos {
+ for _, c := range clientProtos {
+ if s == c {
+ return s, nil
+ }
+ if s == "h2" && c == "http/1.1" {
+ http11fallback = true
+ }
+ }
+ }
+ // As a special case, let http/1.1 clients connect to h2 servers as if they
+ // didn't support ALPN. We used not to enforce protocol overlap, so over
+ // time a number of HTTP servers were configured with only "h2", but
+ // expected to accept connections from "http/1.1" clients. See Issue 46310.
+ if http11fallback {
+ return "", nil
+ }
+ return "", fmt.Errorf("tls: client requested unsupported application protocols (%s)", clientProtos)
+}
+
+// supportsECDHE returns whether ECDHE key exchanges can be used with this
+// pre-TLS 1.3 client.
+func supportsECDHE(c *config, supportedCurves []CurveID, supportedPoints []uint8) bool {
+ supportsCurve := false
+ for _, curve := range supportedCurves {
+ if c.supportsCurve(curve) {
+ supportsCurve = true
+ break
+ }
+ }
+
+ supportsPointFormat := false
+ for _, pointFormat := range supportedPoints {
+ if pointFormat == pointFormatUncompressed {
+ supportsPointFormat = true
+ break
+ }
+ }
+ // Per RFC 8422, Section 5.1.2, if the Supported Point Formats extension is
+ // missing, uncompressed points are supported. If supportedPoints is empty,
+ // the extension must be missing, as an empty extension body is rejected by
+ // the parser. See https://go.dev/issue/49126.
+ if len(supportedPoints) == 0 {
+ supportsPointFormat = true
+ }
+
+ return supportsCurve && supportsPointFormat
+}
+
+func (hs *serverHandshakeState) pickCipherSuite() error {
+ c := hs.c
+
+ preferenceOrder := cipherSuitesPreferenceOrder
+ if !hasAESGCMHardwareSupport || !aesgcmPreferred(hs.clientHello.cipherSuites) {
+ preferenceOrder = cipherSuitesPreferenceOrderNoAES
+ }
+
+ configCipherSuites := c.config.cipherSuites()
+ preferenceList := make([]uint16, 0, len(configCipherSuites))
+ for _, suiteID := range preferenceOrder {
+ for _, id := range configCipherSuites {
+ if id == suiteID {
+ preferenceList = append(preferenceList, id)
+ break
+ }
+ }
+ }
+
+ hs.suite = selectCipherSuite(preferenceList, hs.clientHello.cipherSuites, hs.cipherSuiteOk)
+ if hs.suite == nil {
+ c.sendAlert(alertHandshakeFailure)
+ return errors.New("tls: no cipher suite supported by both client and server")
+ }
+ c.cipherSuite = hs.suite.id
+
+ for _, id := range hs.clientHello.cipherSuites {
+ if id == TLS_FALLBACK_SCSV {
+ // The client is doing a fallback connection. See RFC 7507.
+ if hs.clientHello.vers < c.config.maxSupportedVersion(roleServer) {
+ c.sendAlert(alertInappropriateFallback)
+ return errors.New("tls: client using inappropriate protocol fallback")
+ }
+ break
+ }
+ }
+
+ return nil
+}
+
+func (hs *serverHandshakeState) cipherSuiteOk(c *cipherSuite) bool {
+ if c.flags&suiteECDHE != 0 {
+ if !hs.ecdheOk {
+ return false
+ }
+ if c.flags&suiteECSign != 0 {
+ if !hs.ecSignOk {
+ return false
+ }
+ } else if !hs.rsaSignOk {
+ return false
+ }
+ } else if !hs.rsaDecryptOk {
+ return false
+ }
+ if hs.c.vers < VersionTLS12 && c.flags&suiteTLS12 != 0 {
+ return false
+ }
+ return true
+}
+
+// checkForResumption reports whether we should perform resumption on this connection.
+func (hs *serverHandshakeState) checkForResumption() bool {
+ c := hs.c
+
+ if c.config.SessionTicketsDisabled {
+ return false
+ }
+
+ plaintext, usedOldKey := c.decryptTicket(hs.clientHello.sessionTicket)
+ if plaintext == nil {
+ return false
+ }
+ hs.sessionState = &sessionState{usedOldKey: usedOldKey}
+ ok := hs.sessionState.unmarshal(plaintext)
+ if !ok {
+ return false
+ }
+
+ createdAt := time.Unix(int64(hs.sessionState.createdAt), 0)
+ if c.config.time().Sub(createdAt) > maxSessionTicketLifetime {
+ return false
+ }
+
+ // Never resume a session for a different TLS version.
+ if c.vers != hs.sessionState.vers {
+ return false
+ }
+
+ cipherSuiteOk := false
+ // Check that the client is still offering the ciphersuite in the session.
+ for _, id := range hs.clientHello.cipherSuites {
+ if id == hs.sessionState.cipherSuite {
+ cipherSuiteOk = true
+ break
+ }
+ }
+ if !cipherSuiteOk {
+ return false
+ }
+
+ // Check that we also support the ciphersuite from the session.
+ hs.suite = selectCipherSuite([]uint16{hs.sessionState.cipherSuite},
+ c.config.cipherSuites(), hs.cipherSuiteOk)
+ if hs.suite == nil {
+ return false
+ }
+
+ sessionHasClientCerts := len(hs.sessionState.certificates) != 0
+ needClientCerts := requiresClientCert(c.config.ClientAuth)
+ if needClientCerts && !sessionHasClientCerts {
+ return false
+ }
+ if sessionHasClientCerts && c.config.ClientAuth == NoClientCert {
+ return false
+ }
+
+ return true
+}
+
+func (hs *serverHandshakeState) doResumeHandshake() error {
+ c := hs.c
+
+ hs.hello.cipherSuite = hs.suite.id
+ c.cipherSuite = hs.suite.id
+ // We echo the client's session ID in the ServerHello to let it know
+ // that we're doing a resumption.
+ hs.hello.sessionId = hs.clientHello.sessionId
+ hs.hello.ticketSupported = hs.sessionState.usedOldKey
+ hs.finishedHash = newFinishedHash(c.vers, hs.suite)
+ hs.finishedHash.discardHandshakeBuffer()
+ hs.finishedHash.Write(hs.clientHello.marshal())
+ hs.finishedHash.Write(hs.hello.marshal())
+ if _, err := c.writeRecord(recordTypeHandshake, hs.hello.marshal()); err != nil {
+ return err
+ }
+
+ if err := c.processCertsFromClient(Certificate{
+ Certificate: hs.sessionState.certificates,
+ }); err != nil {
+ return err
+ }
+
+ if c.config.VerifyConnection != nil {
+ if err := c.config.VerifyConnection(c.connectionStateLocked()); err != nil {
+ c.sendAlert(alertBadCertificate)
+ return err
+ }
+ }
+
+ hs.masterSecret = hs.sessionState.masterSecret
+
+ return nil
+}
+
+func (hs *serverHandshakeState) doFullHandshake() error {
+ c := hs.c
+
+ if hs.clientHello.ocspStapling && len(hs.cert.OCSPStaple) > 0 {
+ hs.hello.ocspStapling = true
+ }
+
+ hs.hello.ticketSupported = hs.clientHello.ticketSupported && !c.config.SessionTicketsDisabled
+ hs.hello.cipherSuite = hs.suite.id
+
+ hs.finishedHash = newFinishedHash(hs.c.vers, hs.suite)
+ if c.config.ClientAuth == NoClientCert {
+ // No need to keep a full record of the handshake if client
+ // certificates won't be used.
+ hs.finishedHash.discardHandshakeBuffer()
+ }
+ hs.finishedHash.Write(hs.clientHello.marshal())
+ hs.finishedHash.Write(hs.hello.marshal())
+ if _, err := c.writeRecord(recordTypeHandshake, hs.hello.marshal()); err != nil {
+ return err
+ }
+
+ certMsg := new(certificateMsg)
+ certMsg.certificates = hs.cert.Certificate
+ hs.finishedHash.Write(certMsg.marshal())
+ if _, err := c.writeRecord(recordTypeHandshake, certMsg.marshal()); err != nil {
+ return err
+ }
+
+ if hs.hello.ocspStapling {
+ certStatus := new(certificateStatusMsg)
+ certStatus.response = hs.cert.OCSPStaple
+ hs.finishedHash.Write(certStatus.marshal())
+ if _, err := c.writeRecord(recordTypeHandshake, certStatus.marshal()); err != nil {
+ return err
+ }
+ }
+
+ keyAgreement := hs.suite.ka(c.vers)
+ skx, err := keyAgreement.generateServerKeyExchange(c.config, hs.cert, hs.clientHello, hs.hello)
+ if err != nil {
+ c.sendAlert(alertHandshakeFailure)
+ return err
+ }
+ if skx != nil {
+ hs.finishedHash.Write(skx.marshal())
+ if _, err := c.writeRecord(recordTypeHandshake, skx.marshal()); err != nil {
+ return err
+ }
+ }
+
+ var certReq *certificateRequestMsg
+ if c.config.ClientAuth >= RequestClientCert {
+ // Request a client certificate
+ certReq = new(certificateRequestMsg)
+ certReq.certificateTypes = []byte{
+ byte(certTypeRSASign),
+ byte(certTypeECDSASign),
+ }
+ if c.vers >= VersionTLS12 {
+ certReq.hasSignatureAlgorithm = true
+ certReq.supportedSignatureAlgorithms = supportedSignatureAlgorithms()
+ }
+
+ // 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.
+ if c.config.ClientCAs != nil {
+ certReq.certificateAuthorities = c.config.ClientCAs.Subjects()
+ }
+ hs.finishedHash.Write(certReq.marshal())
+ if _, err := c.writeRecord(recordTypeHandshake, certReq.marshal()); err != nil {
+ return err
+ }
+ }
+
+ helloDone := new(serverHelloDoneMsg)
+ hs.finishedHash.Write(helloDone.marshal())
+ if _, err := c.writeRecord(recordTypeHandshake, helloDone.marshal()); err != nil {
+ return err
+ }
+
+ if _, err := c.flush(); err != nil {
+ return err
+ }
+
+ var pub crypto.PublicKey // public key for client auth, if any
+
+ msg, err := c.readHandshake()
+ if err != nil {
+ return err
+ }
+
+ // If we requested a client certificate, then the client must send a
+ // certificate message, even if it's empty.
+ if c.config.ClientAuth >= RequestClientCert {
+ certMsg, ok := msg.(*certificateMsg)
+ if !ok {
+ c.sendAlert(alertUnexpectedMessage)
+ return unexpectedMessageError(certMsg, msg)
+ }
+ hs.finishedHash.Write(certMsg.marshal())
+
+ if err := c.processCertsFromClient(Certificate{
+ Certificate: certMsg.certificates,
+ }); err != nil {
+ return err
+ }
+ if len(certMsg.certificates) != 0 {
+ pub = c.peerCertificates[0].PublicKey
+ }
+
+ msg, err = c.readHandshake()
+ if err != nil {
+ return err
+ }
+ }
+ if c.config.VerifyConnection != nil {
+ if err := c.config.VerifyConnection(c.connectionStateLocked()); err != nil {
+ c.sendAlert(alertBadCertificate)
+ return err
+ }
+ }
+
+ // Get client key exchange
+ ckx, ok := msg.(*clientKeyExchangeMsg)
+ if !ok {
+ c.sendAlert(alertUnexpectedMessage)
+ return unexpectedMessageError(ckx, msg)
+ }
+ hs.finishedHash.Write(ckx.marshal())
+
+ preMasterSecret, err := keyAgreement.processClientKeyExchange(c.config, hs.cert, ckx, c.vers)
+ if err != nil {
+ c.sendAlert(alertHandshakeFailure)
+ return err
+ }
+ hs.masterSecret = masterFromPreMasterSecret(c.vers, hs.suite, preMasterSecret, hs.clientHello.random, hs.hello.random)
+ if err := c.config.writeKeyLog(keyLogLabelTLS12, hs.clientHello.random, hs.masterSecret); err != nil {
+ c.sendAlert(alertInternalError)
+ return err
+ }
+
+ // If we received a client cert in response to our certificate request message,
+ // the client will send us a certificateVerifyMsg immediately after the
+ // clientKeyExchangeMsg. This message is a digest of all preceding
+ // handshake-layer messages that is signed using the private key corresponding
+ // to the client's certificate. This allows us to verify that the client is in
+ // possession of the private key of the certificate.
+ if len(c.peerCertificates) > 0 {
+ msg, err = c.readHandshake()
+ if err != nil {
+ return err
+ }
+ certVerify, ok := msg.(*certificateVerifyMsg)
+ if !ok {
+ c.sendAlert(alertUnexpectedMessage)
+ return unexpectedMessageError(certVerify, msg)
+ }
+
+ var sigType uint8
+ var sigHash crypto.Hash
+ if c.vers >= VersionTLS12 {
+ if !isSupportedSignatureAlgorithm(certVerify.signatureAlgorithm, certReq.supportedSignatureAlgorithms) {
+ c.sendAlert(alertIllegalParameter)
+ return errors.New("tls: client certificate used with invalid signature algorithm")
+ }
+ sigType, sigHash, err = typeAndHashFromSignatureScheme(certVerify.signatureAlgorithm)
+ if err != nil {
+ return c.sendAlert(alertInternalError)
+ }
+ } else {
+ sigType, sigHash, err = legacyTypeAndHashFromPublicKey(pub)
+ if err != nil {
+ c.sendAlert(alertIllegalParameter)
+ return err
+ }
+ }
+
+ signed := hs.finishedHash.hashForClientCertificate(sigType, sigHash, hs.masterSecret)
+ if err := verifyHandshakeSignature(sigType, pub, sigHash, signed, certVerify.signature); err != nil {
+ c.sendAlert(alertDecryptError)
+ return errors.New("tls: invalid signature by the client certificate: " + err.Error())
+ }
+
+ hs.finishedHash.Write(certVerify.marshal())
+ }
+
+ hs.finishedHash.discardHandshakeBuffer()
+
+ return nil
+}
+
+func (hs *serverHandshakeState) establishKeys() error {
+ c := hs.c
+
+ clientMAC, serverMAC, clientKey, serverKey, clientIV, serverIV :=
+ keysFromMasterSecret(c.vers, hs.suite, hs.masterSecret, hs.clientHello.random, hs.hello.random, hs.suite.macLen, hs.suite.keyLen, hs.suite.ivLen)
+
+ var clientCipher, serverCipher any
+ var clientHash, serverHash hash.Hash
+
+ if hs.suite.aead == nil {
+ clientCipher = hs.suite.cipher(clientKey, clientIV, true /* for reading */)
+ clientHash = hs.suite.mac(clientMAC)
+ serverCipher = hs.suite.cipher(serverKey, serverIV, false /* not for reading */)
+ serverHash = hs.suite.mac(serverMAC)
+ } else {
+ clientCipher = hs.suite.aead(clientKey, clientIV)
+ serverCipher = hs.suite.aead(serverKey, serverIV)
+ }
+
+ c.in.prepareCipherSpec(c.vers, clientCipher, clientHash)
+ c.out.prepareCipherSpec(c.vers, serverCipher, serverHash)
+
+ return nil
+}
+
+func (hs *serverHandshakeState) readFinished(out []byte) error {
+ c := hs.c
+
+ if err := c.readChangeCipherSpec(); err != nil {
+ return err
+ }
+
+ msg, err := c.readHandshake()
+ if err != nil {
+ return err
+ }
+ clientFinished, ok := msg.(*finishedMsg)
+ if !ok {
+ c.sendAlert(alertUnexpectedMessage)
+ return unexpectedMessageError(clientFinished, msg)
+ }
+
+ verify := hs.finishedHash.clientSum(hs.masterSecret)
+ if len(verify) != len(clientFinished.verifyData) ||
+ subtle.ConstantTimeCompare(verify, clientFinished.verifyData) != 1 {
+ c.sendAlert(alertHandshakeFailure)
+ return errors.New("tls: client's Finished message is incorrect")
+ }
+
+ hs.finishedHash.Write(clientFinished.marshal())
+ copy(out, verify)
+ return nil
+}
+
+func (hs *serverHandshakeState) sendSessionTicket() error {
+ // ticketSupported is set in a resumption handshake if the
+ // ticket from the client was encrypted with an old session
+ // ticket key and thus a refreshed ticket should be sent.
+ if !hs.hello.ticketSupported {
+ return nil
+ }
+
+ c := hs.c
+ m := new(newSessionTicketMsg)
+
+ createdAt := uint64(c.config.time().Unix())
+ if hs.sessionState != nil {
+ // If this is re-wrapping an old key, then keep
+ // the original time it was created.
+ createdAt = hs.sessionState.createdAt
+ }
+
+ var certsFromClient [][]byte
+ for _, cert := range c.peerCertificates {
+ certsFromClient = append(certsFromClient, cert.Raw)
+ }
+ state := sessionState{
+ vers: c.vers,
+ cipherSuite: hs.suite.id,
+ createdAt: createdAt,
+ masterSecret: hs.masterSecret,
+ certificates: certsFromClient,
+ }
+ var err error
+ m.ticket, err = c.encryptTicket(state.marshal())
+ if err != nil {
+ return err
+ }
+
+ hs.finishedHash.Write(m.marshal())
+ if _, err := c.writeRecord(recordTypeHandshake, m.marshal()); err != nil {
+ return err
+ }
+
+ return nil
+}
+
+func (hs *serverHandshakeState) sendFinished(out []byte) error {
+ c := hs.c
+
+ if _, err := c.writeRecord(recordTypeChangeCipherSpec, []byte{1}); err != nil {
+ return err
+ }
+
+ finished := new(finishedMsg)
+ finished.verifyData = hs.finishedHash.serverSum(hs.masterSecret)
+ hs.finishedHash.Write(finished.marshal())
+ if _, err := c.writeRecord(recordTypeHandshake, finished.marshal()); err != nil {
+ return err
+ }
+
+ copy(out, finished.verifyData)
+
+ return nil
+}
+
+// processCertsFromClient takes a chain of client certificates either from a
+// Certificates message or from a sessionState and verifies them. It returns
+// the public key of the leaf certificate.
+func (c *Conn) processCertsFromClient(certificate Certificate) error {
+ certificates := certificate.Certificate
+ certs := make([]*x509.Certificate, len(certificates))
+ var err error
+ for i, asn1Data := range certificates {
+ if certs[i], err = x509.ParseCertificate(asn1Data); err != nil {
+ c.sendAlert(alertBadCertificate)
+ return errors.New("tls: failed to parse client certificate: " + err.Error())
+ }
+ }
+
+ if len(certs) == 0 && requiresClientCert(c.config.ClientAuth) {
+ c.sendAlert(alertBadCertificate)
+ return errors.New("tls: client didn't provide a certificate")
+ }
+
+ if c.config.ClientAuth >= VerifyClientCertIfGiven && len(certs) > 0 {
+ opts := x509.VerifyOptions{
+ Roots: c.config.ClientCAs,
+ CurrentTime: c.config.time(),
+ Intermediates: x509.NewCertPool(),
+ KeyUsages: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth},
+ }
+
+ for _, cert := range certs[1:] {
+ opts.Intermediates.AddCert(cert)
+ }
+
+ chains, err := certs[0].Verify(opts)
+ if err != nil {
+ c.sendAlert(alertBadCertificate)
+ return errors.New("tls: failed to verify client certificate: " + err.Error())
+ }
+
+ c.verifiedChains = chains
+ }
+
+ c.peerCertificates = certs
+ c.ocspResponse = certificate.OCSPStaple
+ c.scts = certificate.SignedCertificateTimestamps
+
+ if len(certs) > 0 {
+ switch certs[0].PublicKey.(type) {
+ case *ecdsa.PublicKey, *rsa.PublicKey, ed25519.PublicKey:
+ default:
+ c.sendAlert(alertUnsupportedCertificate)
+ return fmt.Errorf("tls: client certificate contains an unsupported public key of type %T", certs[0].PublicKey)
+ }
+ }
+
+ if c.config.VerifyPeerCertificate != nil {
+ if err := c.config.VerifyPeerCertificate(certificates, c.verifiedChains); err != nil {
+ c.sendAlert(alertBadCertificate)
+ return err
+ }
+ }
+
+ return nil
+}
+
+func newClientHelloInfo(ctx context.Context, c *Conn, clientHello *clientHelloMsg) *ClientHelloInfo {
+ supportedVersions := clientHello.supportedVersions
+ if len(clientHello.supportedVersions) == 0 {
+ supportedVersions = supportedVersionsFromMax(clientHello.vers)
+ }
+
+ return toClientHelloInfo(&clientHelloInfo{
+ CipherSuites: clientHello.cipherSuites,
+ ServerName: clientHello.serverName,
+ SupportedCurves: clientHello.supportedCurves,
+ SupportedPoints: clientHello.supportedPoints,
+ SignatureSchemes: clientHello.supportedSignatureAlgorithms,
+ SupportedProtos: clientHello.alpnProtocols,
+ SupportedVersions: supportedVersions,
+ Conn: c.conn,
+ config: toConfig(c.config),
+ ctx: ctx,
+ })
+}
diff --git a/vendor/github.com/quic-go/qtls-go1-19/handshake_server_tls13.go b/vendor/github.com/quic-go/qtls-go1-19/handshake_server_tls13.go
new file mode 100644
index 0000000000..3801777686
--- /dev/null
+++ b/vendor/github.com/quic-go/qtls-go1-19/handshake_server_tls13.go
@@ -0,0 +1,902 @@
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package qtls
+
+import (
+ "bytes"
+ "context"
+ "crypto"
+ "crypto/hmac"
+ "crypto/rsa"
+ "errors"
+ "hash"
+ "io"
+ "sync/atomic"
+ "time"
+)
+
+// maxClientPSKIdentities is the number of client PSK identities the server will
+// attempt to validate. It will ignore the rest not to let cheap ClientHello
+// messages cause too much work in session ticket decryption attempts.
+const maxClientPSKIdentities = 5
+
+type serverHandshakeStateTLS13 struct {
+ c *Conn
+ ctx context.Context
+ clientHello *clientHelloMsg
+ hello *serverHelloMsg
+ alpnNegotiationErr error
+ encryptedExtensions *encryptedExtensionsMsg
+ sentDummyCCS bool
+ usingPSK bool
+ suite *cipherSuiteTLS13
+ cert *Certificate
+ sigAlg SignatureScheme
+ earlySecret []byte
+ sharedKey []byte
+ handshakeSecret []byte
+ masterSecret []byte
+ trafficSecret []byte // client_application_traffic_secret_0
+ transcript hash.Hash
+ clientFinished []byte
+}
+
+func (hs *serverHandshakeStateTLS13) handshake() error {
+ c := hs.c
+
+ if needFIPS() {
+ return errors.New("tls: internal error: TLS 1.3 reached in FIPS mode")
+ }
+
+ // For an overview of the TLS 1.3 handshake, see RFC 8446, Section 2.
+ if err := hs.processClientHello(); err != nil {
+ return err
+ }
+ if err := hs.checkForResumption(); err != nil {
+ return err
+ }
+ c.updateConnectionState()
+ if err := hs.pickCertificate(); err != nil {
+ return err
+ }
+ c.buffering = true
+ if err := hs.sendServerParameters(); err != nil {
+ return err
+ }
+ if err := hs.sendServerCertificate(); err != nil {
+ return err
+ }
+ if err := hs.sendServerFinished(); err != nil {
+ return err
+ }
+ // Note that at this point we could start sending application data without
+ // waiting for the client's second flight, but the application might not
+ // expect the lack of replay protection of the ClientHello parameters.
+ if _, err := c.flush(); err != nil {
+ return err
+ }
+ if err := hs.readClientCertificate(); err != nil {
+ return err
+ }
+ c.updateConnectionState()
+ if err := hs.readClientFinished(); err != nil {
+ return err
+ }
+
+ atomic.StoreUint32(&c.handshakeStatus, 1)
+ c.updateConnectionState()
+ return nil
+}
+
+func (hs *serverHandshakeStateTLS13) processClientHello() error {
+ c := hs.c
+
+ hs.hello = new(serverHelloMsg)
+ hs.encryptedExtensions = new(encryptedExtensionsMsg)
+
+ // TLS 1.3 froze the ServerHello.legacy_version field, and uses
+ // supported_versions instead. See RFC 8446, sections 4.1.3 and 4.2.1.
+ hs.hello.vers = VersionTLS12
+ hs.hello.supportedVersion = c.vers
+
+ if len(hs.clientHello.supportedVersions) == 0 {
+ c.sendAlert(alertIllegalParameter)
+ return errors.New("tls: client used the legacy version field to negotiate TLS 1.3")
+ }
+
+ // Abort if the client is doing a fallback and landing lower than what we
+ // support. See RFC 7507, which however does not specify the interaction
+ // with supported_versions. The only difference is that with
+ // supported_versions a client has a chance to attempt a [TLS 1.2, TLS 1.4]
+ // handshake in case TLS 1.3 is broken but 1.2 is not. Alas, in that case,
+ // it will have to drop the TLS_FALLBACK_SCSV protection if it falls back to
+ // TLS 1.2, because a TLS 1.3 server would abort here. The situation before
+ // supported_versions was not better because there was just no way to do a
+ // TLS 1.4 handshake without risking the server selecting TLS 1.3.
+ for _, id := range hs.clientHello.cipherSuites {
+ if id == TLS_FALLBACK_SCSV {
+ // Use c.vers instead of max(supported_versions) because an attacker
+ // could defeat this by adding an arbitrary high version otherwise.
+ if c.vers < c.config.maxSupportedVersion(roleServer) {
+ c.sendAlert(alertInappropriateFallback)
+ return errors.New("tls: client using inappropriate protocol fallback")
+ }
+ break
+ }
+ }
+
+ if len(hs.clientHello.compressionMethods) != 1 ||
+ hs.clientHello.compressionMethods[0] != compressionNone {
+ c.sendAlert(alertIllegalParameter)
+ return errors.New("tls: TLS 1.3 client supports illegal compression methods")
+ }
+
+ hs.hello.random = make([]byte, 32)
+ if _, err := io.ReadFull(c.config.rand(), hs.hello.random); err != nil {
+ c.sendAlert(alertInternalError)
+ return err
+ }
+
+ if len(hs.clientHello.secureRenegotiation) != 0 {
+ c.sendAlert(alertHandshakeFailure)
+ return errors.New("tls: initial handshake had non-empty renegotiation extension")
+ }
+
+ hs.hello.sessionId = hs.clientHello.sessionId
+ hs.hello.compressionMethod = compressionNone
+
+ if hs.suite == nil {
+ var preferenceList []uint16
+ for _, suiteID := range c.config.CipherSuites {
+ for _, suite := range cipherSuitesTLS13 {
+ if suite.id == suiteID {
+ preferenceList = append(preferenceList, suiteID)
+ break
+ }
+ }
+ }
+ if len(preferenceList) == 0 {
+ preferenceList = defaultCipherSuitesTLS13
+ if !hasAESGCMHardwareSupport || !aesgcmPreferred(hs.clientHello.cipherSuites) {
+ preferenceList = defaultCipherSuitesTLS13NoAES
+ }
+ }
+ for _, suiteID := range preferenceList {
+ hs.suite = mutualCipherSuiteTLS13(hs.clientHello.cipherSuites, suiteID)
+ if hs.suite != nil {
+ break
+ }
+ }
+ }
+ if hs.suite == nil {
+ c.sendAlert(alertHandshakeFailure)
+ return errors.New("tls: no cipher suite supported by both client and server")
+ }
+ c.cipherSuite = hs.suite.id
+ hs.hello.cipherSuite = hs.suite.id
+ hs.transcript = hs.suite.hash.New()
+
+ // Pick the ECDHE group in server preference order, but give priority to
+ // groups with a key share, to avoid a HelloRetryRequest round-trip.
+ var selectedGroup CurveID
+ var clientKeyShare *keyShare
+GroupSelection:
+ for _, preferredGroup := range c.config.curvePreferences() {
+ for _, ks := range hs.clientHello.keyShares {
+ if ks.group == preferredGroup {
+ selectedGroup = ks.group
+ clientKeyShare = &ks
+ break GroupSelection
+ }
+ }
+ if selectedGroup != 0 {
+ continue
+ }
+ for _, group := range hs.clientHello.supportedCurves {
+ if group == preferredGroup {
+ selectedGroup = group
+ break
+ }
+ }
+ }
+ if selectedGroup == 0 {
+ c.sendAlert(alertHandshakeFailure)
+ return errors.New("tls: no ECDHE curve supported by both client and server")
+ }
+ if clientKeyShare == nil {
+ if err := hs.doHelloRetryRequest(selectedGroup); err != nil {
+ return err
+ }
+ clientKeyShare = &hs.clientHello.keyShares[0]
+ }
+
+ if _, ok := curveForCurveID(selectedGroup); selectedGroup != X25519 && !ok {
+ c.sendAlert(alertInternalError)
+ return errors.New("tls: CurvePreferences includes unsupported curve")
+ }
+ params, err := generateECDHEParameters(c.config.rand(), selectedGroup)
+ if err != nil {
+ c.sendAlert(alertInternalError)
+ return err
+ }
+ hs.hello.serverShare = keyShare{group: selectedGroup, data: params.PublicKey()}
+ hs.sharedKey = params.SharedKey(clientKeyShare.data)
+ if hs.sharedKey == nil {
+ c.sendAlert(alertIllegalParameter)
+ return errors.New("tls: invalid client key share")
+ }
+
+ c.serverName = hs.clientHello.serverName
+
+ if c.extraConfig != nil && c.extraConfig.ReceivedExtensions != nil {
+ c.extraConfig.ReceivedExtensions(typeClientHello, hs.clientHello.additionalExtensions)
+ }
+
+ selectedProto, err := negotiateALPN(c.config.NextProtos, hs.clientHello.alpnProtocols)
+ if err != nil {
+ hs.alpnNegotiationErr = err
+ }
+ hs.encryptedExtensions.alpnProtocol = selectedProto
+ c.clientProtocol = selectedProto
+
+ return nil
+}
+
+func (hs *serverHandshakeStateTLS13) checkForResumption() error {
+ c := hs.c
+
+ if c.config.SessionTicketsDisabled {
+ return nil
+ }
+
+ modeOK := false
+ for _, mode := range hs.clientHello.pskModes {
+ if mode == pskModeDHE {
+ modeOK = true
+ break
+ }
+ }
+ if !modeOK {
+ return nil
+ }
+
+ if len(hs.clientHello.pskIdentities) != len(hs.clientHello.pskBinders) {
+ c.sendAlert(alertIllegalParameter)
+ return errors.New("tls: invalid or missing PSK binders")
+ }
+ if len(hs.clientHello.pskIdentities) == 0 {
+ return nil
+ }
+
+ for i, identity := range hs.clientHello.pskIdentities {
+ if i >= maxClientPSKIdentities {
+ break
+ }
+
+ plaintext, _ := c.decryptTicket(identity.label)
+ if plaintext == nil {
+ continue
+ }
+ sessionState := new(sessionStateTLS13)
+ if ok := sessionState.unmarshal(plaintext); !ok {
+ continue
+ }
+
+ if hs.clientHello.earlyData {
+ if sessionState.maxEarlyData == 0 {
+ c.sendAlert(alertUnsupportedExtension)
+ return errors.New("tls: client sent unexpected early data")
+ }
+
+ if hs.alpnNegotiationErr == nil && sessionState.alpn == c.clientProtocol &&
+ c.extraConfig != nil && c.extraConfig.MaxEarlyData > 0 &&
+ c.extraConfig.Accept0RTT != nil && c.extraConfig.Accept0RTT(sessionState.appData) {
+ hs.encryptedExtensions.earlyData = true
+ c.used0RTT = true
+ }
+ }
+
+ createdAt := time.Unix(int64(sessionState.createdAt), 0)
+ if c.config.time().Sub(createdAt) > maxSessionTicketLifetime {
+ continue
+ }
+
+ // We don't check the obfuscated ticket age because it's affected by
+ // clock skew and it's only a freshness signal useful for shrinking the
+ // window for replay attacks, which don't affect us as we don't do 0-RTT.
+
+ pskSuite := cipherSuiteTLS13ByID(sessionState.cipherSuite)
+ if pskSuite == nil || pskSuite.hash != hs.suite.hash {
+ continue
+ }
+
+ // PSK connections don't re-establish client certificates, but carry
+ // them over in the session ticket. Ensure the presence of client certs
+ // in the ticket is consistent with the configured requirements.
+ sessionHasClientCerts := len(sessionState.certificate.Certificate) != 0
+ needClientCerts := requiresClientCert(c.config.ClientAuth)
+ if needClientCerts && !sessionHasClientCerts {
+ continue
+ }
+ if sessionHasClientCerts && c.config.ClientAuth == NoClientCert {
+ continue
+ }
+
+ psk := hs.suite.expandLabel(sessionState.resumptionSecret, "resumption",
+ nil, hs.suite.hash.Size())
+ hs.earlySecret = hs.suite.extract(psk, nil)
+ binderKey := hs.suite.deriveSecret(hs.earlySecret, resumptionBinderLabel, nil)
+ // Clone the transcript in case a HelloRetryRequest was recorded.
+ transcript := cloneHash(hs.transcript, hs.suite.hash)
+ if transcript == nil {
+ c.sendAlert(alertInternalError)
+ return errors.New("tls: internal error: failed to clone hash")
+ }
+ transcript.Write(hs.clientHello.marshalWithoutBinders())
+ pskBinder := hs.suite.finishedHash(binderKey, transcript)
+ if !hmac.Equal(hs.clientHello.pskBinders[i], pskBinder) {
+ c.sendAlert(alertDecryptError)
+ return errors.New("tls: invalid PSK binder")
+ }
+
+ c.didResume = true
+ if err := c.processCertsFromClient(sessionState.certificate); err != nil {
+ return err
+ }
+
+ h := cloneHash(hs.transcript, hs.suite.hash)
+ h.Write(hs.clientHello.marshal())
+ if hs.encryptedExtensions.earlyData {
+ clientEarlySecret := hs.suite.deriveSecret(hs.earlySecret, "c e traffic", h)
+ c.in.exportKey(Encryption0RTT, hs.suite, clientEarlySecret)
+ if err := c.config.writeKeyLog(keyLogLabelEarlyTraffic, hs.clientHello.random, clientEarlySecret); err != nil {
+ c.sendAlert(alertInternalError)
+ return err
+ }
+ }
+
+ hs.hello.selectedIdentityPresent = true
+ hs.hello.selectedIdentity = uint16(i)
+ hs.usingPSK = true
+ return nil
+ }
+
+ return nil
+}
+
+// cloneHash uses the encoding.BinaryMarshaler and encoding.BinaryUnmarshaler
+// interfaces implemented by standard library hashes to clone the state of in
+// to a new instance of h. It returns nil if the operation fails.
+func cloneHash(in hash.Hash, h crypto.Hash) hash.Hash {
+ // Recreate the interface to avoid importing encoding.
+ type binaryMarshaler interface {
+ MarshalBinary() (data []byte, err error)
+ UnmarshalBinary(data []byte) error
+ }
+ marshaler, ok := in.(binaryMarshaler)
+ if !ok {
+ return nil
+ }
+ state, err := marshaler.MarshalBinary()
+ if err != nil {
+ return nil
+ }
+ out := h.New()
+ unmarshaler, ok := out.(binaryMarshaler)
+ if !ok {
+ return nil
+ }
+ if err := unmarshaler.UnmarshalBinary(state); err != nil {
+ return nil
+ }
+ return out
+}
+
+func (hs *serverHandshakeStateTLS13) pickCertificate() error {
+ c := hs.c
+
+ // Only one of PSK and certificates are used at a time.
+ if hs.usingPSK {
+ return nil
+ }
+
+ // signature_algorithms is required in TLS 1.3. See RFC 8446, Section 4.2.3.
+ if len(hs.clientHello.supportedSignatureAlgorithms) == 0 {
+ return c.sendAlert(alertMissingExtension)
+ }
+
+ certificate, err := c.config.getCertificate(newClientHelloInfo(hs.ctx, c, hs.clientHello))
+ if err != nil {
+ if err == errNoCertificates {
+ c.sendAlert(alertUnrecognizedName)
+ } else {
+ c.sendAlert(alertInternalError)
+ }
+ return err
+ }
+ hs.sigAlg, err = selectSignatureScheme(c.vers, certificate, hs.clientHello.supportedSignatureAlgorithms)
+ if err != nil {
+ // getCertificate returned a certificate that is unsupported or
+ // incompatible with the client's signature algorithms.
+ c.sendAlert(alertHandshakeFailure)
+ return err
+ }
+ hs.cert = certificate
+
+ return nil
+}
+
+// sendDummyChangeCipherSpec sends a ChangeCipherSpec record for compatibility
+// with middleboxes that didn't implement TLS correctly. See RFC 8446, Appendix D.4.
+func (hs *serverHandshakeStateTLS13) sendDummyChangeCipherSpec() error {
+ if hs.sentDummyCCS {
+ return nil
+ }
+ hs.sentDummyCCS = true
+
+ _, err := hs.c.writeRecord(recordTypeChangeCipherSpec, []byte{1})
+ return err
+}
+
+func (hs *serverHandshakeStateTLS13) doHelloRetryRequest(selectedGroup CurveID) error {
+ c := hs.c
+
+ // The first ClientHello gets double-hashed into the transcript upon a
+ // HelloRetryRequest. See RFC 8446, Section 4.4.1.
+ hs.transcript.Write(hs.clientHello.marshal())
+ chHash := hs.transcript.Sum(nil)
+ hs.transcript.Reset()
+ hs.transcript.Write([]byte{typeMessageHash, 0, 0, uint8(len(chHash))})
+ hs.transcript.Write(chHash)
+
+ helloRetryRequest := &serverHelloMsg{
+ vers: hs.hello.vers,
+ random: helloRetryRequestRandom,
+ sessionId: hs.hello.sessionId,
+ cipherSuite: hs.hello.cipherSuite,
+ compressionMethod: hs.hello.compressionMethod,
+ supportedVersion: hs.hello.supportedVersion,
+ selectedGroup: selectedGroup,
+ }
+
+ hs.transcript.Write(helloRetryRequest.marshal())
+ if _, err := c.writeRecord(recordTypeHandshake, helloRetryRequest.marshal()); err != nil {
+ return err
+ }
+
+ if err := hs.sendDummyChangeCipherSpec(); err != nil {
+ return err
+ }
+
+ msg, err := c.readHandshake()
+ if err != nil {
+ return err
+ }
+
+ clientHello, ok := msg.(*clientHelloMsg)
+ if !ok {
+ c.sendAlert(alertUnexpectedMessage)
+ return unexpectedMessageError(clientHello, msg)
+ }
+
+ if len(clientHello.keyShares) != 1 || clientHello.keyShares[0].group != selectedGroup {
+ c.sendAlert(alertIllegalParameter)
+ return errors.New("tls: client sent invalid key share in second ClientHello")
+ }
+
+ if clientHello.earlyData {
+ c.sendAlert(alertIllegalParameter)
+ return errors.New("tls: client indicated early data in second ClientHello")
+ }
+
+ if illegalClientHelloChange(clientHello, hs.clientHello) {
+ c.sendAlert(alertIllegalParameter)
+ return errors.New("tls: client illegally modified second ClientHello")
+ }
+
+ if clientHello.earlyData {
+ c.sendAlert(alertIllegalParameter)
+ return errors.New("tls: client offered 0-RTT data in second ClientHello")
+ }
+
+ hs.clientHello = clientHello
+ return nil
+}
+
+// illegalClientHelloChange reports whether the two ClientHello messages are
+// different, with the exception of the changes allowed before and after a
+// HelloRetryRequest. See RFC 8446, Section 4.1.2.
+func illegalClientHelloChange(ch, ch1 *clientHelloMsg) bool {
+ if len(ch.supportedVersions) != len(ch1.supportedVersions) ||
+ len(ch.cipherSuites) != len(ch1.cipherSuites) ||
+ len(ch.supportedCurves) != len(ch1.supportedCurves) ||
+ len(ch.supportedSignatureAlgorithms) != len(ch1.supportedSignatureAlgorithms) ||
+ len(ch.supportedSignatureAlgorithmsCert) != len(ch1.supportedSignatureAlgorithmsCert) ||
+ len(ch.alpnProtocols) != len(ch1.alpnProtocols) {
+ return true
+ }
+ for i := range ch.supportedVersions {
+ if ch.supportedVersions[i] != ch1.supportedVersions[i] {
+ return true
+ }
+ }
+ for i := range ch.cipherSuites {
+ if ch.cipherSuites[i] != ch1.cipherSuites[i] {
+ return true
+ }
+ }
+ for i := range ch.supportedCurves {
+ if ch.supportedCurves[i] != ch1.supportedCurves[i] {
+ return true
+ }
+ }
+ for i := range ch.supportedSignatureAlgorithms {
+ if ch.supportedSignatureAlgorithms[i] != ch1.supportedSignatureAlgorithms[i] {
+ return true
+ }
+ }
+ for i := range ch.supportedSignatureAlgorithmsCert {
+ if ch.supportedSignatureAlgorithmsCert[i] != ch1.supportedSignatureAlgorithmsCert[i] {
+ return true
+ }
+ }
+ for i := range ch.alpnProtocols {
+ if ch.alpnProtocols[i] != ch1.alpnProtocols[i] {
+ return true
+ }
+ }
+ return ch.vers != ch1.vers ||
+ !bytes.Equal(ch.random, ch1.random) ||
+ !bytes.Equal(ch.sessionId, ch1.sessionId) ||
+ !bytes.Equal(ch.compressionMethods, ch1.compressionMethods) ||
+ ch.serverName != ch1.serverName ||
+ ch.ocspStapling != ch1.ocspStapling ||
+ !bytes.Equal(ch.supportedPoints, ch1.supportedPoints) ||
+ ch.ticketSupported != ch1.ticketSupported ||
+ !bytes.Equal(ch.sessionTicket, ch1.sessionTicket) ||
+ ch.secureRenegotiationSupported != ch1.secureRenegotiationSupported ||
+ !bytes.Equal(ch.secureRenegotiation, ch1.secureRenegotiation) ||
+ ch.scts != ch1.scts ||
+ !bytes.Equal(ch.cookie, ch1.cookie) ||
+ !bytes.Equal(ch.pskModes, ch1.pskModes)
+}
+
+func (hs *serverHandshakeStateTLS13) sendServerParameters() error {
+ c := hs.c
+
+ hs.transcript.Write(hs.clientHello.marshal())
+ hs.transcript.Write(hs.hello.marshal())
+ if _, err := c.writeRecord(recordTypeHandshake, hs.hello.marshal()); err != nil {
+ return err
+ }
+
+ if err := hs.sendDummyChangeCipherSpec(); err != nil {
+ return err
+ }
+
+ earlySecret := hs.earlySecret
+ if earlySecret == nil {
+ earlySecret = hs.suite.extract(nil, nil)
+ }
+ hs.handshakeSecret = hs.suite.extract(hs.sharedKey,
+ hs.suite.deriveSecret(earlySecret, "derived", nil))
+
+ clientSecret := hs.suite.deriveSecret(hs.handshakeSecret,
+ clientHandshakeTrafficLabel, hs.transcript)
+ c.in.exportKey(EncryptionHandshake, hs.suite, clientSecret)
+ c.in.setTrafficSecret(hs.suite, clientSecret)
+ serverSecret := hs.suite.deriveSecret(hs.handshakeSecret,
+ serverHandshakeTrafficLabel, hs.transcript)
+ c.out.exportKey(EncryptionHandshake, hs.suite, serverSecret)
+ c.out.setTrafficSecret(hs.suite, serverSecret)
+
+ err := c.config.writeKeyLog(keyLogLabelClientHandshake, hs.clientHello.random, clientSecret)
+ if err != nil {
+ c.sendAlert(alertInternalError)
+ return err
+ }
+ err = c.config.writeKeyLog(keyLogLabelServerHandshake, hs.clientHello.random, serverSecret)
+ if err != nil {
+ c.sendAlert(alertInternalError)
+ return err
+ }
+
+ if hs.alpnNegotiationErr != nil {
+ c.sendAlert(alertNoApplicationProtocol)
+ return hs.alpnNegotiationErr
+ }
+ if hs.c.extraConfig != nil && hs.c.extraConfig.GetExtensions != nil {
+ hs.encryptedExtensions.additionalExtensions = hs.c.extraConfig.GetExtensions(typeEncryptedExtensions)
+ }
+
+ hs.transcript.Write(hs.encryptedExtensions.marshal())
+ if _, err := c.writeRecord(recordTypeHandshake, hs.encryptedExtensions.marshal()); err != nil {
+ return err
+ }
+
+ return nil
+}
+
+func (hs *serverHandshakeStateTLS13) requestClientCert() bool {
+ return hs.c.config.ClientAuth >= RequestClientCert && !hs.usingPSK
+}
+
+func (hs *serverHandshakeStateTLS13) sendServerCertificate() error {
+ c := hs.c
+
+ // Only one of PSK and certificates are used at a time.
+ if hs.usingPSK {
+ return nil
+ }
+
+ if hs.requestClientCert() {
+ // Request a client certificate
+ certReq := new(certificateRequestMsgTLS13)
+ certReq.ocspStapling = true
+ certReq.scts = true
+ certReq.supportedSignatureAlgorithms = supportedSignatureAlgorithms()
+ if c.config.ClientCAs != nil {
+ certReq.certificateAuthorities = c.config.ClientCAs.Subjects()
+ }
+
+ hs.transcript.Write(certReq.marshal())
+ if _, err := c.writeRecord(recordTypeHandshake, certReq.marshal()); err != nil {
+ return err
+ }
+ }
+
+ certMsg := new(certificateMsgTLS13)
+
+ certMsg.certificate = *hs.cert
+ certMsg.scts = hs.clientHello.scts && len(hs.cert.SignedCertificateTimestamps) > 0
+ certMsg.ocspStapling = hs.clientHello.ocspStapling && len(hs.cert.OCSPStaple) > 0
+
+ hs.transcript.Write(certMsg.marshal())
+ if _, err := c.writeRecord(recordTypeHandshake, certMsg.marshal()); err != nil {
+ return err
+ }
+
+ certVerifyMsg := new(certificateVerifyMsg)
+ certVerifyMsg.hasSignatureAlgorithm = true
+ certVerifyMsg.signatureAlgorithm = hs.sigAlg
+
+ sigType, sigHash, err := typeAndHashFromSignatureScheme(hs.sigAlg)
+ if err != nil {
+ return c.sendAlert(alertInternalError)
+ }
+
+ signed := signedMessage(sigHash, serverSignatureContext, hs.transcript)
+ signOpts := crypto.SignerOpts(sigHash)
+ if sigType == signatureRSAPSS {
+ signOpts = &rsa.PSSOptions{SaltLength: rsa.PSSSaltLengthEqualsHash, Hash: sigHash}
+ }
+ sig, err := hs.cert.PrivateKey.(crypto.Signer).Sign(c.config.rand(), signed, signOpts)
+ if err != nil {
+ public := hs.cert.PrivateKey.(crypto.Signer).Public()
+ if rsaKey, ok := public.(*rsa.PublicKey); ok && sigType == signatureRSAPSS &&
+ rsaKey.N.BitLen()/8 < sigHash.Size()*2+2 { // key too small for RSA-PSS
+ c.sendAlert(alertHandshakeFailure)
+ } else {
+ c.sendAlert(alertInternalError)
+ }
+ return errors.New("tls: failed to sign handshake: " + err.Error())
+ }
+ certVerifyMsg.signature = sig
+
+ hs.transcript.Write(certVerifyMsg.marshal())
+ if _, err := c.writeRecord(recordTypeHandshake, certVerifyMsg.marshal()); err != nil {
+ return err
+ }
+
+ return nil
+}
+
+func (hs *serverHandshakeStateTLS13) sendServerFinished() error {
+ c := hs.c
+
+ finished := &finishedMsg{
+ verifyData: hs.suite.finishedHash(c.out.trafficSecret, hs.transcript),
+ }
+
+ hs.transcript.Write(finished.marshal())
+ if _, err := c.writeRecord(recordTypeHandshake, finished.marshal()); err != nil {
+ return err
+ }
+
+ // Derive secrets that take context through the server Finished.
+
+ hs.masterSecret = hs.suite.extract(nil,
+ hs.suite.deriveSecret(hs.handshakeSecret, "derived", nil))
+
+ hs.trafficSecret = hs.suite.deriveSecret(hs.masterSecret,
+ clientApplicationTrafficLabel, hs.transcript)
+ serverSecret := hs.suite.deriveSecret(hs.masterSecret,
+ serverApplicationTrafficLabel, hs.transcript)
+ c.out.exportKey(EncryptionApplication, hs.suite, serverSecret)
+ c.out.setTrafficSecret(hs.suite, serverSecret)
+
+ err := c.config.writeKeyLog(keyLogLabelClientTraffic, hs.clientHello.random, hs.trafficSecret)
+ if err != nil {
+ c.sendAlert(alertInternalError)
+ return err
+ }
+ err = c.config.writeKeyLog(keyLogLabelServerTraffic, hs.clientHello.random, serverSecret)
+ if err != nil {
+ c.sendAlert(alertInternalError)
+ return err
+ }
+
+ c.ekm = hs.suite.exportKeyingMaterial(hs.masterSecret, hs.transcript)
+
+ // If we did not request client certificates, at this point we can
+ // precompute the client finished and roll the transcript forward to send
+ // session tickets in our first flight.
+ if !hs.requestClientCert() {
+ if err := hs.sendSessionTickets(); err != nil {
+ return err
+ }
+ }
+
+ return nil
+}
+
+func (hs *serverHandshakeStateTLS13) shouldSendSessionTickets() bool {
+ if hs.c.config.SessionTicketsDisabled {
+ return false
+ }
+
+ // Don't send tickets the client wouldn't use. See RFC 8446, Section 4.2.9.
+ for _, pskMode := range hs.clientHello.pskModes {
+ if pskMode == pskModeDHE {
+ return true
+ }
+ }
+ return false
+}
+
+func (hs *serverHandshakeStateTLS13) sendSessionTickets() error {
+ c := hs.c
+
+ hs.clientFinished = hs.suite.finishedHash(c.in.trafficSecret, hs.transcript)
+ finishedMsg := &finishedMsg{
+ verifyData: hs.clientFinished,
+ }
+ hs.transcript.Write(finishedMsg.marshal())
+
+ if !hs.shouldSendSessionTickets() {
+ return nil
+ }
+
+ c.resumptionSecret = hs.suite.deriveSecret(hs.masterSecret,
+ resumptionLabel, hs.transcript)
+
+ // Don't send session tickets when the alternative record layer is set.
+ // Instead, save the resumption secret on the Conn.
+ // Session tickets can then be generated by calling Conn.GetSessionTicket().
+ if hs.c.extraConfig != nil && hs.c.extraConfig.AlternativeRecordLayer != nil {
+ return nil
+ }
+
+ m, err := hs.c.getSessionTicketMsg(nil)
+ if err != nil {
+ return err
+ }
+
+ if _, err := c.writeRecord(recordTypeHandshake, m.marshal()); err != nil {
+ return err
+ }
+
+ return nil
+}
+
+func (hs *serverHandshakeStateTLS13) readClientCertificate() error {
+ c := hs.c
+
+ if !hs.requestClientCert() {
+ // Make sure the connection is still being verified whether or not
+ // the server requested a client certificate.
+ if c.config.VerifyConnection != nil {
+ if err := c.config.VerifyConnection(c.connectionStateLocked()); err != nil {
+ c.sendAlert(alertBadCertificate)
+ return err
+ }
+ }
+ return nil
+ }
+
+ // If we requested a client certificate, then the client must send a
+ // certificate message. If it's empty, no CertificateVerify is sent.
+
+ msg, err := c.readHandshake()
+ if err != nil {
+ return err
+ }
+
+ certMsg, ok := msg.(*certificateMsgTLS13)
+ if !ok {
+ c.sendAlert(alertUnexpectedMessage)
+ return unexpectedMessageError(certMsg, msg)
+ }
+ hs.transcript.Write(certMsg.marshal())
+
+ if err := c.processCertsFromClient(certMsg.certificate); err != nil {
+ return err
+ }
+
+ if c.config.VerifyConnection != nil {
+ if err := c.config.VerifyConnection(c.connectionStateLocked()); err != nil {
+ c.sendAlert(alertBadCertificate)
+ return err
+ }
+ }
+
+ if len(certMsg.certificate.Certificate) != 0 {
+ msg, err = c.readHandshake()
+ if err != nil {
+ return err
+ }
+
+ certVerify, ok := msg.(*certificateVerifyMsg)
+ if !ok {
+ c.sendAlert(alertUnexpectedMessage)
+ return unexpectedMessageError(certVerify, msg)
+ }
+
+ // See RFC 8446, Section 4.4.3.
+ if !isSupportedSignatureAlgorithm(certVerify.signatureAlgorithm, supportedSignatureAlgorithms()) {
+ c.sendAlert(alertIllegalParameter)
+ return errors.New("tls: client certificate used with invalid signature algorithm")
+ }
+ sigType, sigHash, err := typeAndHashFromSignatureScheme(certVerify.signatureAlgorithm)
+ if err != nil {
+ return c.sendAlert(alertInternalError)
+ }
+ if sigType == signaturePKCS1v15 || sigHash == crypto.SHA1 {
+ c.sendAlert(alertIllegalParameter)
+ return errors.New("tls: client certificate used with invalid signature algorithm")
+ }
+ signed := signedMessage(sigHash, clientSignatureContext, hs.transcript)
+ if err := verifyHandshakeSignature(sigType, c.peerCertificates[0].PublicKey,
+ sigHash, signed, certVerify.signature); err != nil {
+ c.sendAlert(alertDecryptError)
+ return errors.New("tls: invalid signature by the client certificate: " + err.Error())
+ }
+
+ hs.transcript.Write(certVerify.marshal())
+ }
+
+ // If we waited until the client certificates to send session tickets, we
+ // are ready to do it now.
+ if err := hs.sendSessionTickets(); err != nil {
+ return err
+ }
+
+ return nil
+}
+
+func (hs *serverHandshakeStateTLS13) readClientFinished() error {
+ c := hs.c
+
+ msg, err := c.readHandshake()
+ if err != nil {
+ return err
+ }
+
+ finished, ok := msg.(*finishedMsg)
+ if !ok {
+ c.sendAlert(alertUnexpectedMessage)
+ return unexpectedMessageError(finished, msg)
+ }
+
+ if !hmac.Equal(hs.clientFinished, finished.verifyData) {
+ c.sendAlert(alertDecryptError)
+ return errors.New("tls: invalid client finished hash")
+ }
+
+ c.in.exportKey(EncryptionApplication, hs.suite, hs.trafficSecret)
+ c.in.setTrafficSecret(hs.suite, hs.trafficSecret)
+
+ return nil
+}
diff --git a/vendor/github.com/quic-go/qtls-go1-19/key_agreement.go b/vendor/github.com/quic-go/qtls-go1-19/key_agreement.go
new file mode 100644
index 0000000000..453a8dcf08
--- /dev/null
+++ b/vendor/github.com/quic-go/qtls-go1-19/key_agreement.go
@@ -0,0 +1,357 @@
+// Copyright 2010 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package qtls
+
+import (
+ "crypto"
+ "crypto/md5"
+ "crypto/rsa"
+ "crypto/sha1"
+ "crypto/x509"
+ "errors"
+ "fmt"
+ "io"
+)
+
+// a keyAgreement implements the client and server side of a TLS key agreement
+// protocol by generating and processing key exchange messages.
+type keyAgreement interface {
+ // On the server side, the first two methods are called in order.
+
+ // In the case that the key agreement protocol doesn't use a
+ // ServerKeyExchange message, generateServerKeyExchange can return nil,
+ // nil.
+ generateServerKeyExchange(*config, *Certificate, *clientHelloMsg, *serverHelloMsg) (*serverKeyExchangeMsg, error)
+ processClientKeyExchange(*config, *Certificate, *clientKeyExchangeMsg, uint16) ([]byte, error)
+
+ // On the client side, the next two methods are called in order.
+
+ // This method may not be called if the server doesn't send a
+ // ServerKeyExchange message.
+ processServerKeyExchange(*config, *clientHelloMsg, *serverHelloMsg, *x509.Certificate, *serverKeyExchangeMsg) error
+ generateClientKeyExchange(*config, *clientHelloMsg, *x509.Certificate) ([]byte, *clientKeyExchangeMsg, error)
+}
+
+var errClientKeyExchange = errors.New("tls: invalid ClientKeyExchange message")
+var errServerKeyExchange = errors.New("tls: invalid ServerKeyExchange message")
+
+// rsaKeyAgreement implements the standard TLS key agreement where the client
+// encrypts the pre-master secret to the server's public key.
+type rsaKeyAgreement struct{}
+
+func (ka rsaKeyAgreement) generateServerKeyExchange(config *config, cert *Certificate, clientHello *clientHelloMsg, hello *serverHelloMsg) (*serverKeyExchangeMsg, error) {
+ return nil, nil
+}
+
+func (ka rsaKeyAgreement) processClientKeyExchange(config *config, cert *Certificate, ckx *clientKeyExchangeMsg, version uint16) ([]byte, error) {
+ if len(ckx.ciphertext) < 2 {
+ return nil, errClientKeyExchange
+ }
+ ciphertextLen := int(ckx.ciphertext[0])<<8 | int(ckx.ciphertext[1])
+ if ciphertextLen != len(ckx.ciphertext)-2 {
+ return nil, errClientKeyExchange
+ }
+ ciphertext := ckx.ciphertext[2:]
+
+ priv, ok := cert.PrivateKey.(crypto.Decrypter)
+ if !ok {
+ return nil, errors.New("tls: certificate private key does not implement crypto.Decrypter")
+ }
+ // Perform constant time RSA PKCS #1 v1.5 decryption
+ preMasterSecret, err := priv.Decrypt(config.rand(), ciphertext, &rsa.PKCS1v15DecryptOptions{SessionKeyLen: 48})
+ if err != nil {
+ return nil, err
+ }
+ // We don't check the version number in the premaster secret. For one,
+ // by checking it, we would leak information about the validity of the
+ // encrypted pre-master secret. Secondly, it provides only a small
+ // benefit against a downgrade attack and some implementations send the
+ // wrong version anyway. See the discussion at the end of section
+ // 7.4.7.1 of RFC 4346.
+ return preMasterSecret, nil
+}
+
+func (ka rsaKeyAgreement) processServerKeyExchange(config *config, clientHello *clientHelloMsg, serverHello *serverHelloMsg, cert *x509.Certificate, skx *serverKeyExchangeMsg) error {
+ return errors.New("tls: unexpected ServerKeyExchange")
+}
+
+func (ka rsaKeyAgreement) generateClientKeyExchange(config *config, clientHello *clientHelloMsg, cert *x509.Certificate) ([]byte, *clientKeyExchangeMsg, error) {
+ preMasterSecret := make([]byte, 48)
+ preMasterSecret[0] = byte(clientHello.vers >> 8)
+ preMasterSecret[1] = byte(clientHello.vers)
+ _, err := io.ReadFull(config.rand(), preMasterSecret[2:])
+ if err != nil {
+ return nil, nil, err
+ }
+
+ rsaKey, ok := cert.PublicKey.(*rsa.PublicKey)
+ if !ok {
+ return nil, nil, errors.New("tls: server certificate contains incorrect key type for selected ciphersuite")
+ }
+ encrypted, err := rsa.EncryptPKCS1v15(config.rand(), rsaKey, preMasterSecret)
+ if err != nil {
+ return nil, nil, err
+ }
+ ckx := new(clientKeyExchangeMsg)
+ ckx.ciphertext = make([]byte, len(encrypted)+2)
+ ckx.ciphertext[0] = byte(len(encrypted) >> 8)
+ ckx.ciphertext[1] = byte(len(encrypted))
+ copy(ckx.ciphertext[2:], encrypted)
+ return preMasterSecret, ckx, nil
+}
+
+// sha1Hash calculates a SHA1 hash over the given byte slices.
+func sha1Hash(slices [][]byte) []byte {
+ hsha1 := sha1.New()
+ for _, slice := range slices {
+ hsha1.Write(slice)
+ }
+ return hsha1.Sum(nil)
+}
+
+// md5SHA1Hash implements TLS 1.0's hybrid hash function which consists of the
+// concatenation of an MD5 and SHA1 hash.
+func md5SHA1Hash(slices [][]byte) []byte {
+ md5sha1 := make([]byte, md5.Size+sha1.Size)
+ hmd5 := md5.New()
+ for _, slice := range slices {
+ hmd5.Write(slice)
+ }
+ copy(md5sha1, hmd5.Sum(nil))
+ copy(md5sha1[md5.Size:], sha1Hash(slices))
+ return md5sha1
+}
+
+// hashForServerKeyExchange hashes the given slices and returns their digest
+// using the given hash function (for >= TLS 1.2) or using a default based on
+// the sigType (for earlier TLS versions). For Ed25519 signatures, which don't
+// do pre-hashing, it returns the concatenation of the slices.
+func hashForServerKeyExchange(sigType uint8, hashFunc crypto.Hash, version uint16, slices ...[]byte) []byte {
+ if sigType == signatureEd25519 {
+ var signed []byte
+ for _, slice := range slices {
+ signed = append(signed, slice...)
+ }
+ return signed
+ }
+ if version >= VersionTLS12 {
+ h := hashFunc.New()
+ for _, slice := range slices {
+ h.Write(slice)
+ }
+ digest := h.Sum(nil)
+ return digest
+ }
+ if sigType == signatureECDSA {
+ return sha1Hash(slices)
+ }
+ return md5SHA1Hash(slices)
+}
+
+// ecdheKeyAgreement implements a TLS key agreement where the server
+// generates an ephemeral EC public/private key pair and signs it. The
+// pre-master secret is then calculated using ECDH. The signature may
+// be ECDSA, Ed25519 or RSA.
+type ecdheKeyAgreement struct {
+ version uint16
+ isRSA bool
+ params ecdheParameters
+
+ // ckx and preMasterSecret are generated in processServerKeyExchange
+ // and returned in generateClientKeyExchange.
+ ckx *clientKeyExchangeMsg
+ preMasterSecret []byte
+}
+
+func (ka *ecdheKeyAgreement) generateServerKeyExchange(config *config, cert *Certificate, clientHello *clientHelloMsg, hello *serverHelloMsg) (*serverKeyExchangeMsg, error) {
+ var curveID CurveID
+ for _, c := range clientHello.supportedCurves {
+ if config.supportsCurve(c) {
+ curveID = c
+ break
+ }
+ }
+
+ if curveID == 0 {
+ return nil, errors.New("tls: no supported elliptic curves offered")
+ }
+ if _, ok := curveForCurveID(curveID); curveID != X25519 && !ok {
+ return nil, errors.New("tls: CurvePreferences includes unsupported curve")
+ }
+
+ params, err := generateECDHEParameters(config.rand(), curveID)
+ if err != nil {
+ return nil, err
+ }
+ ka.params = params
+
+ // See RFC 4492, Section 5.4.
+ ecdhePublic := params.PublicKey()
+ serverECDHEParams := make([]byte, 1+2+1+len(ecdhePublic))
+ serverECDHEParams[0] = 3 // named curve
+ serverECDHEParams[1] = byte(curveID >> 8)
+ serverECDHEParams[2] = byte(curveID)
+ serverECDHEParams[3] = byte(len(ecdhePublic))
+ copy(serverECDHEParams[4:], ecdhePublic)
+
+ priv, ok := cert.PrivateKey.(crypto.Signer)
+ if !ok {
+ return nil, fmt.Errorf("tls: certificate private key of type %T does not implement crypto.Signer", cert.PrivateKey)
+ }
+
+ var signatureAlgorithm SignatureScheme
+ var sigType uint8
+ var sigHash crypto.Hash
+ if ka.version >= VersionTLS12 {
+ signatureAlgorithm, err = selectSignatureScheme(ka.version, cert, clientHello.supportedSignatureAlgorithms)
+ if err != nil {
+ return nil, err
+ }
+ sigType, sigHash, err = typeAndHashFromSignatureScheme(signatureAlgorithm)
+ if err != nil {
+ return nil, err
+ }
+ } else {
+ sigType, sigHash, err = legacyTypeAndHashFromPublicKey(priv.Public())
+ if err != nil {
+ return nil, err
+ }
+ }
+ if (sigType == signaturePKCS1v15 || sigType == signatureRSAPSS) != ka.isRSA {
+ return nil, errors.New("tls: certificate cannot be used with the selected cipher suite")
+ }
+
+ signed := hashForServerKeyExchange(sigType, sigHash, ka.version, clientHello.random, hello.random, serverECDHEParams)
+
+ signOpts := crypto.SignerOpts(sigHash)
+ if sigType == signatureRSAPSS {
+ signOpts = &rsa.PSSOptions{SaltLength: rsa.PSSSaltLengthEqualsHash, Hash: sigHash}
+ }
+ sig, err := priv.Sign(config.rand(), signed, signOpts)
+ if err != nil {
+ return nil, errors.New("tls: failed to sign ECDHE parameters: " + err.Error())
+ }
+
+ skx := new(serverKeyExchangeMsg)
+ sigAndHashLen := 0
+ if ka.version >= VersionTLS12 {
+ sigAndHashLen = 2
+ }
+ skx.key = make([]byte, len(serverECDHEParams)+sigAndHashLen+2+len(sig))
+ copy(skx.key, serverECDHEParams)
+ k := skx.key[len(serverECDHEParams):]
+ if ka.version >= VersionTLS12 {
+ k[0] = byte(signatureAlgorithm >> 8)
+ k[1] = byte(signatureAlgorithm)
+ k = k[2:]
+ }
+ k[0] = byte(len(sig) >> 8)
+ k[1] = byte(len(sig))
+ copy(k[2:], sig)
+
+ return skx, nil
+}
+
+func (ka *ecdheKeyAgreement) processClientKeyExchange(config *config, cert *Certificate, ckx *clientKeyExchangeMsg, version uint16) ([]byte, error) {
+ if len(ckx.ciphertext) == 0 || int(ckx.ciphertext[0]) != len(ckx.ciphertext)-1 {
+ return nil, errClientKeyExchange
+ }
+
+ preMasterSecret := ka.params.SharedKey(ckx.ciphertext[1:])
+ if preMasterSecret == nil {
+ return nil, errClientKeyExchange
+ }
+
+ return preMasterSecret, nil
+}
+
+func (ka *ecdheKeyAgreement) processServerKeyExchange(config *config, clientHello *clientHelloMsg, serverHello *serverHelloMsg, cert *x509.Certificate, skx *serverKeyExchangeMsg) error {
+ if len(skx.key) < 4 {
+ return errServerKeyExchange
+ }
+ if skx.key[0] != 3 { // named curve
+ return errors.New("tls: server selected unsupported curve")
+ }
+ curveID := CurveID(skx.key[1])<<8 | CurveID(skx.key[2])
+
+ publicLen := int(skx.key[3])
+ if publicLen+4 > len(skx.key) {
+ return errServerKeyExchange
+ }
+ serverECDHEParams := skx.key[:4+publicLen]
+ publicKey := serverECDHEParams[4:]
+
+ sig := skx.key[4+publicLen:]
+ if len(sig) < 2 {
+ return errServerKeyExchange
+ }
+
+ if _, ok := curveForCurveID(curveID); curveID != X25519 && !ok {
+ return errors.New("tls: server selected unsupported curve")
+ }
+
+ params, err := generateECDHEParameters(config.rand(), curveID)
+ if err != nil {
+ return err
+ }
+ ka.params = params
+
+ ka.preMasterSecret = params.SharedKey(publicKey)
+ if ka.preMasterSecret == nil {
+ return errServerKeyExchange
+ }
+
+ ourPublicKey := params.PublicKey()
+ ka.ckx = new(clientKeyExchangeMsg)
+ ka.ckx.ciphertext = make([]byte, 1+len(ourPublicKey))
+ ka.ckx.ciphertext[0] = byte(len(ourPublicKey))
+ copy(ka.ckx.ciphertext[1:], ourPublicKey)
+
+ var sigType uint8
+ var sigHash crypto.Hash
+ if ka.version >= VersionTLS12 {
+ signatureAlgorithm := SignatureScheme(sig[0])<<8 | SignatureScheme(sig[1])
+ sig = sig[2:]
+ if len(sig) < 2 {
+ return errServerKeyExchange
+ }
+
+ if !isSupportedSignatureAlgorithm(signatureAlgorithm, clientHello.supportedSignatureAlgorithms) {
+ return errors.New("tls: certificate used with invalid signature algorithm")
+ }
+ sigType, sigHash, err = typeAndHashFromSignatureScheme(signatureAlgorithm)
+ if err != nil {
+ return err
+ }
+ } else {
+ sigType, sigHash, err = legacyTypeAndHashFromPublicKey(cert.PublicKey)
+ if err != nil {
+ return err
+ }
+ }
+ if (sigType == signaturePKCS1v15 || sigType == signatureRSAPSS) != ka.isRSA {
+ return errServerKeyExchange
+ }
+
+ sigLen := int(sig[0])<<8 | int(sig[1])
+ if sigLen+2 != len(sig) {
+ return errServerKeyExchange
+ }
+ sig = sig[2:]
+
+ signed := hashForServerKeyExchange(sigType, sigHash, ka.version, clientHello.random, serverHello.random, serverECDHEParams)
+ if err := verifyHandshakeSignature(sigType, cert.PublicKey, sigHash, signed, sig); err != nil {
+ return errors.New("tls: invalid signature by the server certificate: " + err.Error())
+ }
+ return nil
+}
+
+func (ka *ecdheKeyAgreement) generateClientKeyExchange(config *config, clientHello *clientHelloMsg, cert *x509.Certificate) ([]byte, *clientKeyExchangeMsg, error) {
+ if ka.ckx == nil {
+ return nil, nil, errors.New("tls: missing ServerKeyExchange message")
+ }
+
+ return ka.preMasterSecret, ka.ckx, nil
+}
diff --git a/vendor/github.com/quic-go/qtls-go1-19/key_schedule.go b/vendor/github.com/quic-go/qtls-go1-19/key_schedule.go
new file mode 100644
index 0000000000..da13904a6e
--- /dev/null
+++ b/vendor/github.com/quic-go/qtls-go1-19/key_schedule.go
@@ -0,0 +1,199 @@
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package qtls
+
+import (
+ "crypto/elliptic"
+ "crypto/hmac"
+ "errors"
+ "hash"
+ "io"
+ "math/big"
+
+ "golang.org/x/crypto/cryptobyte"
+ "golang.org/x/crypto/curve25519"
+ "golang.org/x/crypto/hkdf"
+)
+
+// This file contains the functions necessary to compute the TLS 1.3 key
+// schedule. See RFC 8446, Section 7.
+
+const (
+ resumptionBinderLabel = "res binder"
+ clientHandshakeTrafficLabel = "c hs traffic"
+ serverHandshakeTrafficLabel = "s hs traffic"
+ clientApplicationTrafficLabel = "c ap traffic"
+ serverApplicationTrafficLabel = "s ap traffic"
+ exporterLabel = "exp master"
+ resumptionLabel = "res master"
+ trafficUpdateLabel = "traffic upd"
+)
+
+// expandLabel implements HKDF-Expand-Label from RFC 8446, Section 7.1.
+func (c *cipherSuiteTLS13) expandLabel(secret []byte, label string, context []byte, length int) []byte {
+ var hkdfLabel cryptobyte.Builder
+ hkdfLabel.AddUint16(uint16(length))
+ hkdfLabel.AddUint8LengthPrefixed(func(b *cryptobyte.Builder) {
+ b.AddBytes([]byte("tls13 "))
+ b.AddBytes([]byte(label))
+ })
+ hkdfLabel.AddUint8LengthPrefixed(func(b *cryptobyte.Builder) {
+ b.AddBytes(context)
+ })
+ out := make([]byte, length)
+ n, err := hkdf.Expand(c.hash.New, secret, hkdfLabel.BytesOrPanic()).Read(out)
+ if err != nil || n != length {
+ panic("tls: HKDF-Expand-Label invocation failed unexpectedly")
+ }
+ return out
+}
+
+// deriveSecret implements Derive-Secret from RFC 8446, Section 7.1.
+func (c *cipherSuiteTLS13) deriveSecret(secret []byte, label string, transcript hash.Hash) []byte {
+ if transcript == nil {
+ transcript = c.hash.New()
+ }
+ return c.expandLabel(secret, label, transcript.Sum(nil), c.hash.Size())
+}
+
+// extract implements HKDF-Extract with the cipher suite hash.
+func (c *cipherSuiteTLS13) extract(newSecret, currentSecret []byte) []byte {
+ if newSecret == nil {
+ newSecret = make([]byte, c.hash.Size())
+ }
+ return hkdf.Extract(c.hash.New, newSecret, currentSecret)
+}
+
+// nextTrafficSecret generates the next traffic secret, given the current one,
+// according to RFC 8446, Section 7.2.
+func (c *cipherSuiteTLS13) nextTrafficSecret(trafficSecret []byte) []byte {
+ return c.expandLabel(trafficSecret, trafficUpdateLabel, nil, c.hash.Size())
+}
+
+// trafficKey generates traffic keys according to RFC 8446, Section 7.3.
+func (c *cipherSuiteTLS13) trafficKey(trafficSecret []byte) (key, iv []byte) {
+ key = c.expandLabel(trafficSecret, "key", nil, c.keyLen)
+ iv = c.expandLabel(trafficSecret, "iv", nil, aeadNonceLength)
+ return
+}
+
+// finishedHash generates the Finished verify_data or PskBinderEntry according
+// to RFC 8446, Section 4.4.4. See sections 4.4 and 4.2.11.2 for the baseKey
+// selection.
+func (c *cipherSuiteTLS13) finishedHash(baseKey []byte, transcript hash.Hash) []byte {
+ finishedKey := c.expandLabel(baseKey, "finished", nil, c.hash.Size())
+ verifyData := hmac.New(c.hash.New, finishedKey)
+ verifyData.Write(transcript.Sum(nil))
+ return verifyData.Sum(nil)
+}
+
+// exportKeyingMaterial implements RFC5705 exporters for TLS 1.3 according to
+// RFC 8446, Section 7.5.
+func (c *cipherSuiteTLS13) exportKeyingMaterial(masterSecret []byte, transcript hash.Hash) func(string, []byte, int) ([]byte, error) {
+ expMasterSecret := c.deriveSecret(masterSecret, exporterLabel, transcript)
+ return func(label string, context []byte, length int) ([]byte, error) {
+ secret := c.deriveSecret(expMasterSecret, label, nil)
+ h := c.hash.New()
+ h.Write(context)
+ return c.expandLabel(secret, "exporter", h.Sum(nil), length), nil
+ }
+}
+
+// ecdheParameters implements Diffie-Hellman with either NIST curves or X25519,
+// according to RFC 8446, Section 4.2.8.2.
+type ecdheParameters interface {
+ CurveID() CurveID
+ PublicKey() []byte
+ SharedKey(peerPublicKey []byte) []byte
+}
+
+func generateECDHEParameters(rand io.Reader, curveID CurveID) (ecdheParameters, error) {
+ if curveID == X25519 {
+ privateKey := make([]byte, curve25519.ScalarSize)
+ if _, err := io.ReadFull(rand, privateKey); err != nil {
+ return nil, err
+ }
+ publicKey, err := curve25519.X25519(privateKey, curve25519.Basepoint)
+ if err != nil {
+ return nil, err
+ }
+ return &x25519Parameters{privateKey: privateKey, publicKey: publicKey}, nil
+ }
+
+ curve, ok := curveForCurveID(curveID)
+ if !ok {
+ return nil, errors.New("tls: internal error: unsupported curve")
+ }
+
+ p := &nistParameters{curveID: curveID}
+ var err error
+ p.privateKey, p.x, p.y, err = elliptic.GenerateKey(curve, rand)
+ if err != nil {
+ return nil, err
+ }
+ return p, nil
+}
+
+func curveForCurveID(id CurveID) (elliptic.Curve, bool) {
+ switch id {
+ case CurveP256:
+ return elliptic.P256(), true
+ case CurveP384:
+ return elliptic.P384(), true
+ case CurveP521:
+ return elliptic.P521(), true
+ default:
+ return nil, false
+ }
+}
+
+type nistParameters struct {
+ privateKey []byte
+ x, y *big.Int // public key
+ curveID CurveID
+}
+
+func (p *nistParameters) CurveID() CurveID {
+ return p.curveID
+}
+
+func (p *nistParameters) PublicKey() []byte {
+ curve, _ := curveForCurveID(p.curveID)
+ return elliptic.Marshal(curve, p.x, p.y)
+}
+
+func (p *nistParameters) SharedKey(peerPublicKey []byte) []byte {
+ curve, _ := curveForCurveID(p.curveID)
+ // Unmarshal also checks whether the given point is on the curve.
+ x, y := elliptic.Unmarshal(curve, peerPublicKey)
+ if x == nil {
+ return nil
+ }
+
+ xShared, _ := curve.ScalarMult(x, y, p.privateKey)
+ sharedKey := make([]byte, (curve.Params().BitSize+7)/8)
+ return xShared.FillBytes(sharedKey)
+}
+
+type x25519Parameters struct {
+ privateKey []byte
+ publicKey []byte
+}
+
+func (p *x25519Parameters) CurveID() CurveID {
+ return X25519
+}
+
+func (p *x25519Parameters) PublicKey() []byte {
+ return p.publicKey[:]
+}
+
+func (p *x25519Parameters) SharedKey(peerPublicKey []byte) []byte {
+ sharedKey, err := curve25519.X25519(p.privateKey, peerPublicKey)
+ if err != nil {
+ return nil
+ }
+ return sharedKey
+}
diff --git a/vendor/github.com/quic-go/qtls-go1-19/notboring.go b/vendor/github.com/quic-go/qtls-go1-19/notboring.go
new file mode 100644
index 0000000000..f292e4f028
--- /dev/null
+++ b/vendor/github.com/quic-go/qtls-go1-19/notboring.go
@@ -0,0 +1,18 @@
+// Copyright 2022 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package qtls
+
+func needFIPS() bool { return false }
+
+func supportedSignatureAlgorithms() []SignatureScheme {
+ return defaultSupportedSignatureAlgorithms
+}
+
+func fipsMinVersion(c *config) uint16 { panic("fipsMinVersion") }
+func fipsMaxVersion(c *config) uint16 { panic("fipsMaxVersion") }
+func fipsCurvePreferences(c *config) []CurveID { panic("fipsCurvePreferences") }
+func fipsCipherSuites(c *config) []uint16 { panic("fipsCipherSuites") }
+
+var fipsSupportedSignatureAlgorithms []SignatureScheme
diff --git a/vendor/github.com/quic-go/qtls-go1-19/prf.go b/vendor/github.com/quic-go/qtls-go1-19/prf.go
new file mode 100644
index 0000000000..9eb0221a0c
--- /dev/null
+++ b/vendor/github.com/quic-go/qtls-go1-19/prf.go
@@ -0,0 +1,283 @@
+// Copyright 2009 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package qtls
+
+import (
+ "crypto"
+ "crypto/hmac"
+ "crypto/md5"
+ "crypto/sha1"
+ "crypto/sha256"
+ "crypto/sha512"
+ "errors"
+ "fmt"
+ "hash"
+)
+
+// Split a premaster secret in two as specified in RFC 4346, Section 5.
+func splitPreMasterSecret(secret []byte) (s1, s2 []byte) {
+ s1 = secret[0 : (len(secret)+1)/2]
+ s2 = secret[len(secret)/2:]
+ return
+}
+
+// pHash implements the P_hash function, as defined in RFC 4346, Section 5.
+func pHash(result, secret, seed []byte, hash func() hash.Hash) {
+ h := hmac.New(hash, secret)
+ h.Write(seed)
+ a := h.Sum(nil)
+
+ j := 0
+ for j < len(result) {
+ h.Reset()
+ h.Write(a)
+ h.Write(seed)
+ b := h.Sum(nil)
+ copy(result[j:], b)
+ j += len(b)
+
+ h.Reset()
+ h.Write(a)
+ a = h.Sum(nil)
+ }
+}
+
+// prf10 implements the TLS 1.0 pseudo-random function, as defined in RFC 2246, Section 5.
+func prf10(result, secret, label, seed []byte) {
+ hashSHA1 := sha1.New
+ hashMD5 := md5.New
+
+ labelAndSeed := make([]byte, len(label)+len(seed))
+ copy(labelAndSeed, label)
+ copy(labelAndSeed[len(label):], seed)
+
+ s1, s2 := splitPreMasterSecret(secret)
+ pHash(result, s1, labelAndSeed, hashMD5)
+ result2 := make([]byte, len(result))
+ pHash(result2, s2, labelAndSeed, hashSHA1)
+
+ for i, b := range result2 {
+ result[i] ^= b
+ }
+}
+
+// prf12 implements the TLS 1.2 pseudo-random function, as defined in RFC 5246, Section 5.
+func prf12(hashFunc func() hash.Hash) func(result, secret, label, seed []byte) {
+ return func(result, secret, label, seed []byte) {
+ labelAndSeed := make([]byte, len(label)+len(seed))
+ copy(labelAndSeed, label)
+ copy(labelAndSeed[len(label):], seed)
+
+ pHash(result, secret, labelAndSeed, hashFunc)
+ }
+}
+
+const (
+ masterSecretLength = 48 // Length of a master secret in TLS 1.1.
+ finishedVerifyLength = 12 // Length of verify_data in a Finished message.
+)
+
+var masterSecretLabel = []byte("master secret")
+var keyExpansionLabel = []byte("key expansion")
+var clientFinishedLabel = []byte("client finished")
+var serverFinishedLabel = []byte("server finished")
+
+func prfAndHashForVersion(version uint16, suite *cipherSuite) (func(result, secret, label, seed []byte), crypto.Hash) {
+ switch version {
+ case VersionTLS10, VersionTLS11:
+ return prf10, crypto.Hash(0)
+ case VersionTLS12:
+ if suite.flags&suiteSHA384 != 0 {
+ return prf12(sha512.New384), crypto.SHA384
+ }
+ return prf12(sha256.New), crypto.SHA256
+ default:
+ panic("unknown version")
+ }
+}
+
+func prfForVersion(version uint16, suite *cipherSuite) func(result, secret, label, seed []byte) {
+ prf, _ := prfAndHashForVersion(version, suite)
+ return prf
+}
+
+// masterFromPreMasterSecret generates the master secret from the pre-master
+// secret. See RFC 5246, Section 8.1.
+func masterFromPreMasterSecret(version uint16, suite *cipherSuite, preMasterSecret, clientRandom, serverRandom []byte) []byte {
+ seed := make([]byte, 0, len(clientRandom)+len(serverRandom))
+ seed = append(seed, clientRandom...)
+ seed = append(seed, serverRandom...)
+
+ masterSecret := make([]byte, masterSecretLength)
+ prfForVersion(version, suite)(masterSecret, preMasterSecret, masterSecretLabel, seed)
+ return masterSecret
+}
+
+// keysFromMasterSecret generates the connection keys from the master
+// secret, given the lengths of the MAC key, cipher key and IV, as defined in
+// RFC 2246, Section 6.3.
+func keysFromMasterSecret(version uint16, suite *cipherSuite, masterSecret, clientRandom, serverRandom []byte, macLen, keyLen, ivLen int) (clientMAC, serverMAC, clientKey, serverKey, clientIV, serverIV []byte) {
+ seed := make([]byte, 0, len(serverRandom)+len(clientRandom))
+ seed = append(seed, serverRandom...)
+ seed = append(seed, clientRandom...)
+
+ n := 2*macLen + 2*keyLen + 2*ivLen
+ keyMaterial := make([]byte, n)
+ prfForVersion(version, suite)(keyMaterial, masterSecret, keyExpansionLabel, seed)
+ clientMAC = keyMaterial[:macLen]
+ keyMaterial = keyMaterial[macLen:]
+ serverMAC = keyMaterial[:macLen]
+ keyMaterial = keyMaterial[macLen:]
+ clientKey = keyMaterial[:keyLen]
+ keyMaterial = keyMaterial[keyLen:]
+ serverKey = keyMaterial[:keyLen]
+ keyMaterial = keyMaterial[keyLen:]
+ clientIV = keyMaterial[:ivLen]
+ keyMaterial = keyMaterial[ivLen:]
+ serverIV = keyMaterial[:ivLen]
+ return
+}
+
+func newFinishedHash(version uint16, cipherSuite *cipherSuite) finishedHash {
+ var buffer []byte
+ if version >= VersionTLS12 {
+ buffer = []byte{}
+ }
+
+ prf, hash := prfAndHashForVersion(version, cipherSuite)
+ if hash != 0 {
+ return finishedHash{hash.New(), hash.New(), nil, nil, buffer, version, prf}
+ }
+
+ return finishedHash{sha1.New(), sha1.New(), md5.New(), md5.New(), buffer, version, prf}
+}
+
+// A finishedHash calculates the hash of a set of handshake messages suitable
+// for including in a Finished message.
+type finishedHash struct {
+ client hash.Hash
+ server hash.Hash
+
+ // Prior to TLS 1.2, an additional MD5 hash is required.
+ clientMD5 hash.Hash
+ serverMD5 hash.Hash
+
+ // In TLS 1.2, a full buffer is sadly required.
+ buffer []byte
+
+ version uint16
+ prf func(result, secret, label, seed []byte)
+}
+
+func (h *finishedHash) Write(msg []byte) (n int, err error) {
+ h.client.Write(msg)
+ h.server.Write(msg)
+
+ if h.version < VersionTLS12 {
+ h.clientMD5.Write(msg)
+ h.serverMD5.Write(msg)
+ }
+
+ if h.buffer != nil {
+ h.buffer = append(h.buffer, msg...)
+ }
+
+ return len(msg), nil
+}
+
+func (h finishedHash) Sum() []byte {
+ if h.version >= VersionTLS12 {
+ return h.client.Sum(nil)
+ }
+
+ out := make([]byte, 0, md5.Size+sha1.Size)
+ out = h.clientMD5.Sum(out)
+ return h.client.Sum(out)
+}
+
+// clientSum returns the contents of the verify_data member of a client's
+// Finished message.
+func (h finishedHash) clientSum(masterSecret []byte) []byte {
+ out := make([]byte, finishedVerifyLength)
+ h.prf(out, masterSecret, clientFinishedLabel, h.Sum())
+ return out
+}
+
+// serverSum returns the contents of the verify_data member of a server's
+// Finished message.
+func (h finishedHash) serverSum(masterSecret []byte) []byte {
+ out := make([]byte, finishedVerifyLength)
+ h.prf(out, masterSecret, serverFinishedLabel, h.Sum())
+ return out
+}
+
+// hashForClientCertificate returns the handshake messages so far, pre-hashed if
+// necessary, suitable for signing by a TLS client certificate.
+func (h finishedHash) hashForClientCertificate(sigType uint8, hashAlg crypto.Hash, masterSecret []byte) []byte {
+ if (h.version >= VersionTLS12 || sigType == signatureEd25519) && h.buffer == nil {
+ panic("tls: handshake hash for a client certificate requested after discarding the handshake buffer")
+ }
+
+ if sigType == signatureEd25519 {
+ return h.buffer
+ }
+
+ if h.version >= VersionTLS12 {
+ hash := hashAlg.New()
+ hash.Write(h.buffer)
+ return hash.Sum(nil)
+ }
+
+ if sigType == signatureECDSA {
+ return h.server.Sum(nil)
+ }
+
+ return h.Sum()
+}
+
+// discardHandshakeBuffer is called when there is no more need to
+// buffer the entirety of the handshake messages.
+func (h *finishedHash) discardHandshakeBuffer() {
+ h.buffer = nil
+}
+
+// noExportedKeyingMaterial is used as a value of
+// ConnectionState.ekm when renegotiation is enabled and thus
+// we wish to fail all key-material export requests.
+func noExportedKeyingMaterial(label string, context []byte, length int) ([]byte, error) {
+ return nil, errors.New("crypto/tls: ExportKeyingMaterial is unavailable when renegotiation is enabled")
+}
+
+// ekmFromMasterSecret generates exported keying material as defined in RFC 5705.
+func ekmFromMasterSecret(version uint16, suite *cipherSuite, masterSecret, clientRandom, serverRandom []byte) func(string, []byte, int) ([]byte, error) {
+ return func(label string, context []byte, length int) ([]byte, error) {
+ switch label {
+ case "client finished", "server finished", "master secret", "key expansion":
+ // These values are reserved and may not be used.
+ return nil, fmt.Errorf("crypto/tls: reserved ExportKeyingMaterial label: %s", label)
+ }
+
+ seedLen := len(serverRandom) + len(clientRandom)
+ if context != nil {
+ seedLen += 2 + len(context)
+ }
+ seed := make([]byte, 0, seedLen)
+
+ seed = append(seed, clientRandom...)
+ seed = append(seed, serverRandom...)
+
+ if context != nil {
+ if len(context) >= 1<<16 {
+ return nil, fmt.Errorf("crypto/tls: ExportKeyingMaterial context too long")
+ }
+ seed = append(seed, byte(len(context)>>8), byte(len(context)))
+ seed = append(seed, context...)
+ }
+
+ keyMaterial := make([]byte, length)
+ prfForVersion(version, suite)(keyMaterial, masterSecret, []byte(label), seed)
+ return keyMaterial, nil
+ }
+}
diff --git a/vendor/github.com/quic-go/qtls-go1-19/ticket.go b/vendor/github.com/quic-go/qtls-go1-19/ticket.go
new file mode 100644
index 0000000000..81e8a52eac
--- /dev/null
+++ b/vendor/github.com/quic-go/qtls-go1-19/ticket.go
@@ -0,0 +1,274 @@
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package qtls
+
+import (
+ "bytes"
+ "crypto/aes"
+ "crypto/cipher"
+ "crypto/hmac"
+ "crypto/sha256"
+ "crypto/subtle"
+ "encoding/binary"
+ "errors"
+ "io"
+ "time"
+
+ "golang.org/x/crypto/cryptobyte"
+)
+
+// sessionState contains the information that is serialized into a session
+// ticket in order to later resume a connection.
+type sessionState struct {
+ vers uint16
+ cipherSuite uint16
+ createdAt uint64
+ masterSecret []byte // opaque master_secret<1..2^16-1>;
+ // struct { opaque certificate<1..2^24-1> } Certificate;
+ certificates [][]byte // Certificate certificate_list<0..2^24-1>;
+
+ // usedOldKey is true if the ticket from which this session came from
+ // was encrypted with an older key and thus should be refreshed.
+ usedOldKey bool
+}
+
+func (m *sessionState) marshal() []byte {
+ var b cryptobyte.Builder
+ b.AddUint16(m.vers)
+ b.AddUint16(m.cipherSuite)
+ addUint64(&b, m.createdAt)
+ b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
+ b.AddBytes(m.masterSecret)
+ })
+ b.AddUint24LengthPrefixed(func(b *cryptobyte.Builder) {
+ for _, cert := range m.certificates {
+ b.AddUint24LengthPrefixed(func(b *cryptobyte.Builder) {
+ b.AddBytes(cert)
+ })
+ }
+ })
+ return b.BytesOrPanic()
+}
+
+func (m *sessionState) unmarshal(data []byte) bool {
+ *m = sessionState{usedOldKey: m.usedOldKey}
+ s := cryptobyte.String(data)
+ if ok := s.ReadUint16(&m.vers) &&
+ s.ReadUint16(&m.cipherSuite) &&
+ readUint64(&s, &m.createdAt) &&
+ readUint16LengthPrefixed(&s, &m.masterSecret) &&
+ len(m.masterSecret) != 0; !ok {
+ return false
+ }
+ var certList cryptobyte.String
+ if !s.ReadUint24LengthPrefixed(&certList) {
+ return false
+ }
+ for !certList.Empty() {
+ var cert []byte
+ if !readUint24LengthPrefixed(&certList, &cert) {
+ return false
+ }
+ m.certificates = append(m.certificates, cert)
+ }
+ return s.Empty()
+}
+
+// sessionStateTLS13 is the content of a TLS 1.3 session ticket. Its first
+// version (revision = 0) doesn't carry any of the information needed for 0-RTT
+// validation and the nonce is always empty.
+// version (revision = 1) carries the max_early_data_size sent in the ticket.
+// version (revision = 2) carries the ALPN sent in the ticket.
+type sessionStateTLS13 struct {
+ // uint8 version = 0x0304;
+ // uint8 revision = 2;
+ cipherSuite uint16
+ createdAt uint64
+ resumptionSecret []byte // opaque resumption_master_secret<1..2^8-1>;
+ certificate Certificate // CertificateEntry certificate_list<0..2^24-1>;
+ maxEarlyData uint32
+ alpn string
+
+ appData []byte
+}
+
+func (m *sessionStateTLS13) marshal() []byte {
+ var b cryptobyte.Builder
+ b.AddUint16(VersionTLS13)
+ b.AddUint8(2) // revision
+ b.AddUint16(m.cipherSuite)
+ addUint64(&b, m.createdAt)
+ b.AddUint8LengthPrefixed(func(b *cryptobyte.Builder) {
+ b.AddBytes(m.resumptionSecret)
+ })
+ marshalCertificate(&b, m.certificate)
+ b.AddUint32(m.maxEarlyData)
+ b.AddUint8LengthPrefixed(func(b *cryptobyte.Builder) {
+ b.AddBytes([]byte(m.alpn))
+ })
+ b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
+ b.AddBytes(m.appData)
+ })
+ return b.BytesOrPanic()
+}
+
+func (m *sessionStateTLS13) unmarshal(data []byte) bool {
+ *m = sessionStateTLS13{}
+ s := cryptobyte.String(data)
+ var version uint16
+ var revision uint8
+ var alpn []byte
+ ret := s.ReadUint16(&version) &&
+ version == VersionTLS13 &&
+ s.ReadUint8(&revision) &&
+ revision == 2 &&
+ s.ReadUint16(&m.cipherSuite) &&
+ readUint64(&s, &m.createdAt) &&
+ readUint8LengthPrefixed(&s, &m.resumptionSecret) &&
+ len(m.resumptionSecret) != 0 &&
+ unmarshalCertificate(&s, &m.certificate) &&
+ s.ReadUint32(&m.maxEarlyData) &&
+ readUint8LengthPrefixed(&s, &alpn) &&
+ readUint16LengthPrefixed(&s, &m.appData) &&
+ s.Empty()
+ m.alpn = string(alpn)
+ return ret
+}
+
+func (c *Conn) encryptTicket(state []byte) ([]byte, error) {
+ if len(c.ticketKeys) == 0 {
+ return nil, errors.New("tls: internal error: session ticket keys unavailable")
+ }
+
+ encrypted := make([]byte, ticketKeyNameLen+aes.BlockSize+len(state)+sha256.Size)
+ keyName := encrypted[:ticketKeyNameLen]
+ iv := encrypted[ticketKeyNameLen : ticketKeyNameLen+aes.BlockSize]
+ macBytes := encrypted[len(encrypted)-sha256.Size:]
+
+ if _, err := io.ReadFull(c.config.rand(), iv); err != nil {
+ return nil, err
+ }
+ key := c.ticketKeys[0]
+ copy(keyName, key.keyName[:])
+ block, err := aes.NewCipher(key.aesKey[:])
+ if err != nil {
+ return nil, errors.New("tls: failed to create cipher while encrypting ticket: " + err.Error())
+ }
+ cipher.NewCTR(block, iv).XORKeyStream(encrypted[ticketKeyNameLen+aes.BlockSize:], state)
+
+ mac := hmac.New(sha256.New, key.hmacKey[:])
+ mac.Write(encrypted[:len(encrypted)-sha256.Size])
+ mac.Sum(macBytes[:0])
+
+ return encrypted, nil
+}
+
+func (c *Conn) decryptTicket(encrypted []byte) (plaintext []byte, usedOldKey bool) {
+ if len(encrypted) < ticketKeyNameLen+aes.BlockSize+sha256.Size {
+ return nil, false
+ }
+
+ keyName := encrypted[:ticketKeyNameLen]
+ iv := encrypted[ticketKeyNameLen : ticketKeyNameLen+aes.BlockSize]
+ macBytes := encrypted[len(encrypted)-sha256.Size:]
+ ciphertext := encrypted[ticketKeyNameLen+aes.BlockSize : len(encrypted)-sha256.Size]
+
+ keyIndex := -1
+ for i, candidateKey := range c.ticketKeys {
+ if bytes.Equal(keyName, candidateKey.keyName[:]) {
+ keyIndex = i
+ break
+ }
+ }
+ if keyIndex == -1 {
+ return nil, false
+ }
+ key := &c.ticketKeys[keyIndex]
+
+ mac := hmac.New(sha256.New, key.hmacKey[:])
+ mac.Write(encrypted[:len(encrypted)-sha256.Size])
+ expected := mac.Sum(nil)
+
+ if subtle.ConstantTimeCompare(macBytes, expected) != 1 {
+ return nil, false
+ }
+
+ block, err := aes.NewCipher(key.aesKey[:])
+ if err != nil {
+ return nil, false
+ }
+ plaintext = make([]byte, len(ciphertext))
+ cipher.NewCTR(block, iv).XORKeyStream(plaintext, ciphertext)
+
+ return plaintext, keyIndex > 0
+}
+
+func (c *Conn) getSessionTicketMsg(appData []byte) (*newSessionTicketMsgTLS13, error) {
+ m := new(newSessionTicketMsgTLS13)
+
+ var certsFromClient [][]byte
+ for _, cert := range c.peerCertificates {
+ certsFromClient = append(certsFromClient, cert.Raw)
+ }
+ state := sessionStateTLS13{
+ cipherSuite: c.cipherSuite,
+ createdAt: uint64(c.config.time().Unix()),
+ resumptionSecret: c.resumptionSecret,
+ certificate: Certificate{
+ Certificate: certsFromClient,
+ OCSPStaple: c.ocspResponse,
+ SignedCertificateTimestamps: c.scts,
+ },
+ appData: appData,
+ alpn: c.clientProtocol,
+ }
+ if c.extraConfig != nil {
+ state.maxEarlyData = c.extraConfig.MaxEarlyData
+ }
+ var err error
+ m.label, err = c.encryptTicket(state.marshal())
+ if err != nil {
+ return nil, err
+ }
+ m.lifetime = uint32(maxSessionTicketLifetime / time.Second)
+
+ // ticket_age_add is a random 32-bit value. See RFC 8446, section 4.6.1
+ // The value is not stored anywhere; we never need to check the ticket age
+ // because 0-RTT is not supported.
+ ageAdd := make([]byte, 4)
+ _, err = c.config.rand().Read(ageAdd)
+ if err != nil {
+ return nil, err
+ }
+ m.ageAdd = binary.LittleEndian.Uint32(ageAdd)
+
+ // ticket_nonce, which must be unique per connection, is always left at
+ // zero because we only ever send one ticket per connection.
+
+ if c.extraConfig != nil {
+ m.maxEarlyData = c.extraConfig.MaxEarlyData
+ }
+ return m, nil
+}
+
+// GetSessionTicket generates a new session ticket.
+// It should only be called after the handshake completes.
+// It can only be used for servers, and only if the alternative record layer is set.
+// The ticket may be nil if config.SessionTicketsDisabled is set,
+// or if the client isn't able to receive session tickets.
+func (c *Conn) GetSessionTicket(appData []byte) ([]byte, error) {
+ if c.isClient || !c.handshakeComplete() || c.extraConfig == nil || c.extraConfig.AlternativeRecordLayer == nil {
+ return nil, errors.New("GetSessionTicket is only valid for servers after completion of the handshake, and if an alternative record layer is set.")
+ }
+ if c.config.SessionTicketsDisabled {
+ return nil, nil
+ }
+
+ m, err := c.getSessionTicketMsg(appData)
+ if err != nil {
+ return nil, err
+ }
+ return m.marshal(), nil
+}
diff --git a/vendor/github.com/quic-go/qtls-go1-19/tls.go b/vendor/github.com/quic-go/qtls-go1-19/tls.go
new file mode 100644
index 0000000000..42207c235f
--- /dev/null
+++ b/vendor/github.com/quic-go/qtls-go1-19/tls.go
@@ -0,0 +1,362 @@
+// Copyright 2009 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// package qtls partially implements TLS 1.2, as specified in RFC 5246,
+// and TLS 1.3, as specified in RFC 8446.
+package qtls
+
+// BUG(agl): The crypto/tls package only implements some countermeasures
+// against Lucky13 attacks on CBC-mode encryption, and only on SHA1
+// variants. See http://www.isg.rhul.ac.uk/tls/TLStiming.pdf and
+// https://www.imperialviolet.org/2013/02/04/luckythirteen.html.
+
+import (
+ "bytes"
+ "context"
+ "crypto"
+ "crypto/ecdsa"
+ "crypto/ed25519"
+ "crypto/rsa"
+ "crypto/x509"
+ "encoding/pem"
+ "errors"
+ "fmt"
+ "net"
+ "os"
+ "strings"
+)
+
+// Server returns a new TLS server side connection
+// using conn as the underlying transport.
+// The configuration config must be non-nil and must include
+// at least one certificate or else set GetCertificate.
+func Server(conn net.Conn, config *Config, extraConfig *ExtraConfig) *Conn {
+ c := &Conn{
+ conn: conn,
+ config: fromConfig(config),
+ extraConfig: extraConfig,
+ }
+ c.handshakeFn = c.serverHandshake
+ return c
+}
+
+// Client returns a new TLS client side connection
+// using conn as the underlying transport.
+// The config cannot be nil: users must set either ServerName or
+// InsecureSkipVerify in the config.
+func Client(conn net.Conn, config *Config, extraConfig *ExtraConfig) *Conn {
+ c := &Conn{
+ conn: conn,
+ config: fromConfig(config),
+ extraConfig: extraConfig,
+ isClient: true,
+ }
+ c.handshakeFn = c.clientHandshake
+ return c
+}
+
+// A listener implements a network listener (net.Listener) for TLS connections.
+type listener struct {
+ net.Listener
+ config *Config
+ extraConfig *ExtraConfig
+}
+
+// Accept waits for and returns the next incoming TLS connection.
+// The returned connection is of type *Conn.
+func (l *listener) Accept() (net.Conn, error) {
+ c, err := l.Listener.Accept()
+ if err != nil {
+ return nil, err
+ }
+ return Server(c, l.config, l.extraConfig), nil
+}
+
+// NewListener creates a Listener which accepts connections from an inner
+// Listener and wraps each connection with Server.
+// The configuration config must be non-nil and must include
+// at least one certificate or else set GetCertificate.
+func NewListener(inner net.Listener, config *Config, extraConfig *ExtraConfig) net.Listener {
+ l := new(listener)
+ l.Listener = inner
+ l.config = config
+ l.extraConfig = extraConfig
+ return l
+}
+
+// Listen creates a TLS listener accepting connections on the
+// given network address using net.Listen.
+// The configuration config must be non-nil and must include
+// at least one certificate or else set GetCertificate.
+func Listen(network, laddr string, config *Config, extraConfig *ExtraConfig) (net.Listener, error) {
+ if config == nil || len(config.Certificates) == 0 &&
+ config.GetCertificate == nil && config.GetConfigForClient == nil {
+ return nil, errors.New("tls: neither Certificates, GetCertificate, nor GetConfigForClient set in Config")
+ }
+ l, err := net.Listen(network, laddr)
+ if err != nil {
+ return nil, err
+ }
+ return NewListener(l, config, extraConfig), nil
+}
+
+type timeoutError struct{}
+
+func (timeoutError) Error() string { return "tls: DialWithDialer timed out" }
+func (timeoutError) Timeout() bool { return true }
+func (timeoutError) Temporary() bool { return true }
+
+// DialWithDialer connects to the given network address using dialer.Dial and
+// then initiates a TLS handshake, returning the resulting TLS connection. Any
+// timeout or deadline given in the dialer apply to connection and TLS
+// handshake as a whole.
+//
+// DialWithDialer interprets a nil configuration as equivalent to the zero
+// configuration; see the documentation of Config for the defaults.
+//
+// DialWithDialer uses context.Background internally; to specify the context,
+// use Dialer.DialContext with NetDialer set to the desired dialer.
+func DialWithDialer(dialer *net.Dialer, network, addr string, config *Config, extraConfig *ExtraConfig) (*Conn, error) {
+ return dial(context.Background(), dialer, network, addr, config, extraConfig)
+}
+
+func dial(ctx context.Context, netDialer *net.Dialer, network, addr string, config *Config, extraConfig *ExtraConfig) (*Conn, error) {
+ if netDialer.Timeout != 0 {
+ var cancel context.CancelFunc
+ ctx, cancel = context.WithTimeout(ctx, netDialer.Timeout)
+ defer cancel()
+ }
+
+ if !netDialer.Deadline.IsZero() {
+ var cancel context.CancelFunc
+ ctx, cancel = context.WithDeadline(ctx, netDialer.Deadline)
+ defer cancel()
+ }
+
+ rawConn, err := netDialer.DialContext(ctx, network, addr)
+ if err != nil {
+ return nil, err
+ }
+
+ colonPos := strings.LastIndex(addr, ":")
+ if colonPos == -1 {
+ colonPos = len(addr)
+ }
+ hostname := addr[:colonPos]
+
+ if config == nil {
+ config = defaultConfig()
+ }
+ // If no ServerName is set, infer the ServerName
+ // from the hostname we're connecting to.
+ if config.ServerName == "" {
+ // Make a copy to avoid polluting argument or default.
+ c := config.Clone()
+ c.ServerName = hostname
+ config = c
+ }
+
+ conn := Client(rawConn, config, extraConfig)
+ if err := conn.HandshakeContext(ctx); err != nil {
+ rawConn.Close()
+ return nil, err
+ }
+ return conn, nil
+}
+
+// Dial connects to the given network address using net.Dial
+// and then initiates a TLS handshake, returning the resulting
+// TLS connection.
+// Dial interprets a nil configuration as equivalent to
+// the zero configuration; see the documentation of Config
+// for the defaults.
+func Dial(network, addr string, config *Config, extraConfig *ExtraConfig) (*Conn, error) {
+ return DialWithDialer(new(net.Dialer), network, addr, config, extraConfig)
+}
+
+// Dialer dials TLS connections given a configuration and a Dialer for the
+// underlying connection.
+type Dialer struct {
+ // NetDialer is the optional dialer to use for the TLS connections'
+ // underlying TCP connections.
+ // A nil NetDialer is equivalent to the net.Dialer zero value.
+ NetDialer *net.Dialer
+
+ // Config is the TLS configuration to use for new connections.
+ // A nil configuration is equivalent to the zero
+ // configuration; see the documentation of Config for the
+ // defaults.
+ Config *Config
+
+ ExtraConfig *ExtraConfig
+}
+
+// Dial connects to the given network address and initiates a TLS
+// handshake, returning the resulting TLS connection.
+//
+// The returned Conn, if any, will always be of type *Conn.
+//
+// Dial uses context.Background internally; to specify the context,
+// use DialContext.
+func (d *Dialer) Dial(network, addr string) (net.Conn, error) {
+ return d.DialContext(context.Background(), network, addr)
+}
+
+func (d *Dialer) netDialer() *net.Dialer {
+ if d.NetDialer != nil {
+ return d.NetDialer
+ }
+ return new(net.Dialer)
+}
+
+// DialContext connects to the given network address and initiates a TLS
+// handshake, returning the resulting TLS connection.
+//
+// The provided Context must be non-nil. If the context expires before
+// the connection is complete, an error is returned. Once successfully
+// connected, any expiration of the context will not affect the
+// connection.
+//
+// The returned Conn, if any, will always be of type *Conn.
+func (d *Dialer) DialContext(ctx context.Context, network, addr string) (net.Conn, error) {
+ c, err := dial(ctx, d.netDialer(), network, addr, d.Config, d.ExtraConfig)
+ if err != nil {
+ // Don't return c (a typed nil) in an interface.
+ return nil, err
+ }
+ return c, nil
+}
+
+// LoadX509KeyPair reads and parses a public/private key pair from a pair
+// of files. The files must contain PEM encoded data. The certificate file
+// may contain intermediate certificates following the leaf certificate to
+// form a certificate chain. On successful return, Certificate.Leaf will
+// be nil because the parsed form of the certificate is not retained.
+func LoadX509KeyPair(certFile, keyFile string) (Certificate, error) {
+ certPEMBlock, err := os.ReadFile(certFile)
+ if err != nil {
+ return Certificate{}, err
+ }
+ keyPEMBlock, err := os.ReadFile(keyFile)
+ if err != nil {
+ return Certificate{}, err
+ }
+ return X509KeyPair(certPEMBlock, keyPEMBlock)
+}
+
+// X509KeyPair parses a public/private key pair from a pair of
+// PEM encoded data. On successful return, Certificate.Leaf will be nil because
+// the parsed form of the certificate is not retained.
+func X509KeyPair(certPEMBlock, keyPEMBlock []byte) (Certificate, error) {
+ fail := func(err error) (Certificate, error) { return Certificate{}, err }
+
+ var cert Certificate
+ var skippedBlockTypes []string
+ for {
+ var certDERBlock *pem.Block
+ certDERBlock, certPEMBlock = pem.Decode(certPEMBlock)
+ if certDERBlock == nil {
+ break
+ }
+ if certDERBlock.Type == "CERTIFICATE" {
+ cert.Certificate = append(cert.Certificate, certDERBlock.Bytes)
+ } else {
+ skippedBlockTypes = append(skippedBlockTypes, certDERBlock.Type)
+ }
+ }
+
+ if len(cert.Certificate) == 0 {
+ if len(skippedBlockTypes) == 0 {
+ return fail(errors.New("tls: failed to find any PEM data in certificate input"))
+ }
+ if len(skippedBlockTypes) == 1 && strings.HasSuffix(skippedBlockTypes[0], "PRIVATE KEY") {
+ return fail(errors.New("tls: failed to find certificate PEM data in certificate input, but did find a private key; PEM inputs may have been switched"))
+ }
+ return fail(fmt.Errorf("tls: failed to find \"CERTIFICATE\" PEM block in certificate input after skipping PEM blocks of the following types: %v", skippedBlockTypes))
+ }
+
+ skippedBlockTypes = skippedBlockTypes[:0]
+ var keyDERBlock *pem.Block
+ for {
+ keyDERBlock, keyPEMBlock = pem.Decode(keyPEMBlock)
+ if keyDERBlock == nil {
+ if len(skippedBlockTypes) == 0 {
+ return fail(errors.New("tls: failed to find any PEM data in key input"))
+ }
+ if len(skippedBlockTypes) == 1 && skippedBlockTypes[0] == "CERTIFICATE" {
+ return fail(errors.New("tls: found a certificate rather than a key in the PEM for the private key"))
+ }
+ return fail(fmt.Errorf("tls: failed to find PEM block with type ending in \"PRIVATE KEY\" in key input after skipping PEM blocks of the following types: %v", skippedBlockTypes))
+ }
+ if keyDERBlock.Type == "PRIVATE KEY" || strings.HasSuffix(keyDERBlock.Type, " PRIVATE KEY") {
+ break
+ }
+ skippedBlockTypes = append(skippedBlockTypes, keyDERBlock.Type)
+ }
+
+ // We don't need to parse the public key for TLS, but we so do anyway
+ // to check that it looks sane and matches the private key.
+ x509Cert, err := x509.ParseCertificate(cert.Certificate[0])
+ if err != nil {
+ return fail(err)
+ }
+
+ cert.PrivateKey, err = parsePrivateKey(keyDERBlock.Bytes)
+ if err != nil {
+ return fail(err)
+ }
+
+ switch pub := x509Cert.PublicKey.(type) {
+ case *rsa.PublicKey:
+ priv, ok := cert.PrivateKey.(*rsa.PrivateKey)
+ if !ok {
+ return fail(errors.New("tls: private key type does not match public key type"))
+ }
+ if pub.N.Cmp(priv.N) != 0 {
+ return fail(errors.New("tls: private key does not match public key"))
+ }
+ case *ecdsa.PublicKey:
+ priv, ok := cert.PrivateKey.(*ecdsa.PrivateKey)
+ if !ok {
+ return fail(errors.New("tls: private key type does not match public key type"))
+ }
+ if pub.X.Cmp(priv.X) != 0 || pub.Y.Cmp(priv.Y) != 0 {
+ return fail(errors.New("tls: private key does not match public key"))
+ }
+ case ed25519.PublicKey:
+ priv, ok := cert.PrivateKey.(ed25519.PrivateKey)
+ if !ok {
+ return fail(errors.New("tls: private key type does not match public key type"))
+ }
+ if !bytes.Equal(priv.Public().(ed25519.PublicKey), pub) {
+ return fail(errors.New("tls: private key does not match public key"))
+ }
+ default:
+ return fail(errors.New("tls: unknown public key algorithm"))
+ }
+
+ return cert, nil
+}
+
+// Attempt to parse the given private key DER block. OpenSSL 0.9.8 generates
+// PKCS #1 private keys by default, while OpenSSL 1.0.0 generates PKCS #8 keys.
+// OpenSSL ecparam generates SEC1 EC private keys for ECDSA. We try all three.
+func parsePrivateKey(der []byte) (crypto.PrivateKey, error) {
+ if key, err := x509.ParsePKCS1PrivateKey(der); err == nil {
+ return key, nil
+ }
+ if key, err := x509.ParsePKCS8PrivateKey(der); err == nil {
+ switch key := key.(type) {
+ case *rsa.PrivateKey, *ecdsa.PrivateKey, ed25519.PrivateKey:
+ return key, nil
+ default:
+ return nil, errors.New("tls: found unknown private key type in PKCS#8 wrapping")
+ }
+ }
+ if key, err := x509.ParseECPrivateKey(der); err == nil {
+ return key, nil
+ }
+
+ return nil, errors.New("tls: failed to parse private key")
+}
diff --git a/vendor/github.com/quic-go/qtls-go1-19/unsafe.go b/vendor/github.com/quic-go/qtls-go1-19/unsafe.go
new file mode 100644
index 0000000000..55fa01b3d6
--- /dev/null
+++ b/vendor/github.com/quic-go/qtls-go1-19/unsafe.go
@@ -0,0 +1,96 @@
+package qtls
+
+import (
+ "crypto/tls"
+ "reflect"
+ "unsafe"
+)
+
+func init() {
+ if !structsEqual(&tls.ConnectionState{}, &connectionState{}) {
+ panic("qtls.ConnectionState doesn't match")
+ }
+ if !structsEqual(&tls.ClientSessionState{}, &clientSessionState{}) {
+ panic("qtls.ClientSessionState doesn't match")
+ }
+ if !structsEqual(&tls.CertificateRequestInfo{}, &certificateRequestInfo{}) {
+ panic("qtls.CertificateRequestInfo doesn't match")
+ }
+ if !structsEqual(&tls.Config{}, &config{}) {
+ panic("qtls.Config doesn't match")
+ }
+ if !structsEqual(&tls.ClientHelloInfo{}, &clientHelloInfo{}) {
+ panic("qtls.ClientHelloInfo doesn't match")
+ }
+}
+
+func toConnectionState(c connectionState) ConnectionState {
+ return *(*ConnectionState)(unsafe.Pointer(&c))
+}
+
+func toClientSessionState(s *clientSessionState) *ClientSessionState {
+ return (*ClientSessionState)(unsafe.Pointer(s))
+}
+
+func fromClientSessionState(s *ClientSessionState) *clientSessionState {
+ return (*clientSessionState)(unsafe.Pointer(s))
+}
+
+func toCertificateRequestInfo(i *certificateRequestInfo) *CertificateRequestInfo {
+ return (*CertificateRequestInfo)(unsafe.Pointer(i))
+}
+
+func toConfig(c *config) *Config {
+ return (*Config)(unsafe.Pointer(c))
+}
+
+func fromConfig(c *Config) *config {
+ return (*config)(unsafe.Pointer(c))
+}
+
+func toClientHelloInfo(chi *clientHelloInfo) *ClientHelloInfo {
+ return (*ClientHelloInfo)(unsafe.Pointer(chi))
+}
+
+func structsEqual(a, b interface{}) bool {
+ return compare(reflect.ValueOf(a), reflect.ValueOf(b))
+}
+
+func compare(a, b reflect.Value) bool {
+ sa := a.Elem()
+ sb := b.Elem()
+ if sa.NumField() != sb.NumField() {
+ return false
+ }
+ for i := 0; i < sa.NumField(); i++ {
+ fa := sa.Type().Field(i)
+ fb := sb.Type().Field(i)
+ if !reflect.DeepEqual(fa.Index, fb.Index) || fa.Name != fb.Name || fa.Anonymous != fb.Anonymous || fa.Offset != fb.Offset || !reflect.DeepEqual(fa.Type, fb.Type) {
+ if fa.Type.Kind() != fb.Type.Kind() {
+ return false
+ }
+ if fa.Type.Kind() == reflect.Slice {
+ if !compareStruct(fa.Type.Elem(), fb.Type.Elem()) {
+ return false
+ }
+ continue
+ }
+ return false
+ }
+ }
+ return true
+}
+
+func compareStruct(a, b reflect.Type) bool {
+ if a.NumField() != b.NumField() {
+ return false
+ }
+ for i := 0; i < a.NumField(); i++ {
+ fa := a.Field(i)
+ fb := b.Field(i)
+ if !reflect.DeepEqual(fa.Index, fb.Index) || fa.Name != fb.Name || fa.Anonymous != fb.Anonymous || fa.Offset != fb.Offset || !reflect.DeepEqual(fa.Type, fb.Type) {
+ return false
+ }
+ }
+ return true
+}
diff --git a/vendor/github.com/quic-go/qtls-go1-20/LICENSE b/vendor/github.com/quic-go/qtls-go1-20/LICENSE
new file mode 100644
index 0000000000..6a66aea5ea
--- /dev/null
+++ b/vendor/github.com/quic-go/qtls-go1-20/LICENSE
@@ -0,0 +1,27 @@
+Copyright (c) 2009 The Go Authors. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with the
+distribution.
+ * Neither the name of Google Inc. nor the names of its
+contributors may be used to endorse or promote products derived from
+this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/vendor/github.com/quic-go/qtls-go1-20/README.md b/vendor/github.com/quic-go/qtls-go1-20/README.md
new file mode 100644
index 0000000000..2beaa2f236
--- /dev/null
+++ b/vendor/github.com/quic-go/qtls-go1-20/README.md
@@ -0,0 +1,6 @@
+# qtls
+
+[![Go Reference](https://pkg.go.dev/badge/github.com/quic-go/qtls-go1-20.svg)](https://pkg.go.dev/github.com/quic-go/qtls-go1-20)
+[![.github/workflows/go-test.yml](https://github.com/quic-go/qtls-go1-20/actions/workflows/go-test.yml/badge.svg)](https://github.com/quic-go/qtls-go1-20/actions/workflows/go-test.yml)
+
+This repository contains a modified version of the standard library's TLS implementation, modified for the QUIC protocol. It is used by [quic-go](https://github.com/quic-go/quic-go).
diff --git a/vendor/github.com/quic-go/qtls-go1-20/alert.go b/vendor/github.com/quic-go/qtls-go1-20/alert.go
new file mode 100644
index 0000000000..3feac79be8
--- /dev/null
+++ b/vendor/github.com/quic-go/qtls-go1-20/alert.go
@@ -0,0 +1,102 @@
+// Copyright 2009 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package qtls
+
+import "strconv"
+
+type alert uint8
+
+// Alert is a TLS alert
+type Alert = alert
+
+const (
+ // alert level
+ alertLevelWarning = 1
+ alertLevelError = 2
+)
+
+const (
+ alertCloseNotify alert = 0
+ alertUnexpectedMessage alert = 10
+ alertBadRecordMAC alert = 20
+ alertDecryptionFailed alert = 21
+ alertRecordOverflow alert = 22
+ alertDecompressionFailure alert = 30
+ alertHandshakeFailure alert = 40
+ alertBadCertificate alert = 42
+ alertUnsupportedCertificate alert = 43
+ alertCertificateRevoked alert = 44
+ alertCertificateExpired alert = 45
+ alertCertificateUnknown alert = 46
+ alertIllegalParameter alert = 47
+ alertUnknownCA alert = 48
+ alertAccessDenied alert = 49
+ alertDecodeError alert = 50
+ alertDecryptError alert = 51
+ alertExportRestriction alert = 60
+ alertProtocolVersion alert = 70
+ alertInsufficientSecurity alert = 71
+ alertInternalError alert = 80
+ alertInappropriateFallback alert = 86
+ alertUserCanceled alert = 90
+ alertNoRenegotiation alert = 100
+ alertMissingExtension alert = 109
+ alertUnsupportedExtension alert = 110
+ alertCertificateUnobtainable alert = 111
+ alertUnrecognizedName alert = 112
+ alertBadCertificateStatusResponse alert = 113
+ alertBadCertificateHashValue alert = 114
+ alertUnknownPSKIdentity alert = 115
+ alertCertificateRequired alert = 116
+ alertNoApplicationProtocol alert = 120
+)
+
+var alertText = map[alert]string{
+ alertCloseNotify: "close notify",
+ alertUnexpectedMessage: "unexpected message",
+ alertBadRecordMAC: "bad record MAC",
+ alertDecryptionFailed: "decryption failed",
+ alertRecordOverflow: "record overflow",
+ alertDecompressionFailure: "decompression failure",
+ alertHandshakeFailure: "handshake failure",
+ alertBadCertificate: "bad certificate",
+ alertUnsupportedCertificate: "unsupported certificate",
+ alertCertificateRevoked: "revoked certificate",
+ alertCertificateExpired: "expired certificate",
+ alertCertificateUnknown: "unknown certificate",
+ alertIllegalParameter: "illegal parameter",
+ alertUnknownCA: "unknown certificate authority",
+ alertAccessDenied: "access denied",
+ alertDecodeError: "error decoding message",
+ alertDecryptError: "error decrypting message",
+ alertExportRestriction: "export restriction",
+ alertProtocolVersion: "protocol version not supported",
+ alertInsufficientSecurity: "insufficient security level",
+ alertInternalError: "internal error",
+ alertInappropriateFallback: "inappropriate fallback",
+ alertUserCanceled: "user canceled",
+ alertNoRenegotiation: "no renegotiation",
+ alertMissingExtension: "missing extension",
+ alertUnsupportedExtension: "unsupported extension",
+ alertCertificateUnobtainable: "certificate unobtainable",
+ alertUnrecognizedName: "unrecognized name",
+ alertBadCertificateStatusResponse: "bad certificate status response",
+ alertBadCertificateHashValue: "bad certificate hash value",
+ alertUnknownPSKIdentity: "unknown PSK identity",
+ alertCertificateRequired: "certificate required",
+ alertNoApplicationProtocol: "no application protocol",
+}
+
+func (e alert) String() string {
+ s, ok := alertText[e]
+ if ok {
+ return "tls: " + s
+ }
+ return "tls: alert(" + strconv.Itoa(int(e)) + ")"
+}
+
+func (e alert) Error() string {
+ return e.String()
+}
diff --git a/vendor/github.com/quic-go/qtls-go1-20/auth.go b/vendor/github.com/quic-go/qtls-go1-20/auth.go
new file mode 100644
index 0000000000..effc9aced8
--- /dev/null
+++ b/vendor/github.com/quic-go/qtls-go1-20/auth.go
@@ -0,0 +1,293 @@
+// Copyright 2017 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package qtls
+
+import (
+ "bytes"
+ "crypto"
+ "crypto/ecdsa"
+ "crypto/ed25519"
+ "crypto/elliptic"
+ "crypto/rsa"
+ "errors"
+ "fmt"
+ "hash"
+ "io"
+)
+
+// verifyHandshakeSignature verifies a signature against pre-hashed
+// (if required) handshake contents.
+func verifyHandshakeSignature(sigType uint8, pubkey crypto.PublicKey, hashFunc crypto.Hash, signed, sig []byte) error {
+ switch sigType {
+ case signatureECDSA:
+ pubKey, ok := pubkey.(*ecdsa.PublicKey)
+ if !ok {
+ return fmt.Errorf("expected an ECDSA public key, got %T", pubkey)
+ }
+ if !ecdsa.VerifyASN1(pubKey, signed, sig) {
+ return errors.New("ECDSA verification failure")
+ }
+ case signatureEd25519:
+ pubKey, ok := pubkey.(ed25519.PublicKey)
+ if !ok {
+ return fmt.Errorf("expected an Ed25519 public key, got %T", pubkey)
+ }
+ if !ed25519.Verify(pubKey, signed, sig) {
+ return errors.New("Ed25519 verification failure")
+ }
+ case signaturePKCS1v15:
+ pubKey, ok := pubkey.(*rsa.PublicKey)
+ if !ok {
+ return fmt.Errorf("expected an RSA public key, got %T", pubkey)
+ }
+ if err := rsa.VerifyPKCS1v15(pubKey, hashFunc, signed, sig); err != nil {
+ return err
+ }
+ case signatureRSAPSS:
+ pubKey, ok := pubkey.(*rsa.PublicKey)
+ if !ok {
+ return fmt.Errorf("expected an RSA public key, got %T", pubkey)
+ }
+ signOpts := &rsa.PSSOptions{SaltLength: rsa.PSSSaltLengthEqualsHash}
+ if err := rsa.VerifyPSS(pubKey, hashFunc, signed, sig, signOpts); err != nil {
+ return err
+ }
+ default:
+ return errors.New("internal error: unknown signature type")
+ }
+ return nil
+}
+
+const (
+ serverSignatureContext = "TLS 1.3, server CertificateVerify\x00"
+ clientSignatureContext = "TLS 1.3, client CertificateVerify\x00"
+)
+
+var signaturePadding = []byte{
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+}
+
+// signedMessage returns the pre-hashed (if necessary) message to be signed by
+// certificate keys in TLS 1.3. See RFC 8446, Section 4.4.3.
+func signedMessage(sigHash crypto.Hash, context string, transcript hash.Hash) []byte {
+ if sigHash == directSigning {
+ b := &bytes.Buffer{}
+ b.Write(signaturePadding)
+ io.WriteString(b, context)
+ b.Write(transcript.Sum(nil))
+ return b.Bytes()
+ }
+ h := sigHash.New()
+ h.Write(signaturePadding)
+ io.WriteString(h, context)
+ h.Write(transcript.Sum(nil))
+ return h.Sum(nil)
+}
+
+// typeAndHashFromSignatureScheme returns the corresponding signature type and
+// crypto.Hash for a given TLS SignatureScheme.
+func typeAndHashFromSignatureScheme(signatureAlgorithm SignatureScheme) (sigType uint8, hash crypto.Hash, err error) {
+ switch signatureAlgorithm {
+ case PKCS1WithSHA1, PKCS1WithSHA256, PKCS1WithSHA384, PKCS1WithSHA512:
+ sigType = signaturePKCS1v15
+ case PSSWithSHA256, PSSWithSHA384, PSSWithSHA512:
+ sigType = signatureRSAPSS
+ case ECDSAWithSHA1, ECDSAWithP256AndSHA256, ECDSAWithP384AndSHA384, ECDSAWithP521AndSHA512:
+ sigType = signatureECDSA
+ case Ed25519:
+ sigType = signatureEd25519
+ default:
+ return 0, 0, fmt.Errorf("unsupported signature algorithm: %v", signatureAlgorithm)
+ }
+ switch signatureAlgorithm {
+ case PKCS1WithSHA1, ECDSAWithSHA1:
+ hash = crypto.SHA1
+ case PKCS1WithSHA256, PSSWithSHA256, ECDSAWithP256AndSHA256:
+ hash = crypto.SHA256
+ case PKCS1WithSHA384, PSSWithSHA384, ECDSAWithP384AndSHA384:
+ hash = crypto.SHA384
+ case PKCS1WithSHA512, PSSWithSHA512, ECDSAWithP521AndSHA512:
+ hash = crypto.SHA512
+ case Ed25519:
+ hash = directSigning
+ default:
+ return 0, 0, fmt.Errorf("unsupported signature algorithm: %v", signatureAlgorithm)
+ }
+ return sigType, hash, nil
+}
+
+// legacyTypeAndHashFromPublicKey returns the fixed signature type and crypto.Hash for
+// a given public key used with TLS 1.0 and 1.1, before the introduction of
+// signature algorithm negotiation.
+func legacyTypeAndHashFromPublicKey(pub crypto.PublicKey) (sigType uint8, hash crypto.Hash, err error) {
+ switch pub.(type) {
+ case *rsa.PublicKey:
+ return signaturePKCS1v15, crypto.MD5SHA1, nil
+ case *ecdsa.PublicKey:
+ return signatureECDSA, crypto.SHA1, nil
+ case ed25519.PublicKey:
+ // RFC 8422 specifies support for Ed25519 in TLS 1.0 and 1.1,
+ // but it requires holding on to a handshake transcript to do a
+ // full signature, and not even OpenSSL bothers with the
+ // complexity, so we can't even test it properly.
+ return 0, 0, fmt.Errorf("tls: Ed25519 public keys are not supported before TLS 1.2")
+ default:
+ return 0, 0, fmt.Errorf("tls: unsupported public key: %T", pub)
+ }
+}
+
+var rsaSignatureSchemes = []struct {
+ scheme SignatureScheme
+ minModulusBytes int
+ maxVersion uint16
+}{
+ // RSA-PSS is used with PSSSaltLengthEqualsHash, and requires
+ // emLen >= hLen + sLen + 2
+ {PSSWithSHA256, crypto.SHA256.Size()*2 + 2, VersionTLS13},
+ {PSSWithSHA384, crypto.SHA384.Size()*2 + 2, VersionTLS13},
+ {PSSWithSHA512, crypto.SHA512.Size()*2 + 2, VersionTLS13},
+ // PKCS #1 v1.5 uses prefixes from hashPrefixes in crypto/rsa, and requires
+ // emLen >= len(prefix) + hLen + 11
+ // TLS 1.3 dropped support for PKCS #1 v1.5 in favor of RSA-PSS.
+ {PKCS1WithSHA256, 19 + crypto.SHA256.Size() + 11, VersionTLS12},
+ {PKCS1WithSHA384, 19 + crypto.SHA384.Size() + 11, VersionTLS12},
+ {PKCS1WithSHA512, 19 + crypto.SHA512.Size() + 11, VersionTLS12},
+ {PKCS1WithSHA1, 15 + crypto.SHA1.Size() + 11, VersionTLS12},
+}
+
+// signatureSchemesForCertificate returns the list of supported SignatureSchemes
+// for a given certificate, based on the public key and the protocol version,
+// and optionally filtered by its explicit SupportedSignatureAlgorithms.
+//
+// This function must be kept in sync with supportedSignatureAlgorithms.
+// FIPS filtering is applied in the caller, selectSignatureScheme.
+func signatureSchemesForCertificate(version uint16, cert *Certificate) []SignatureScheme {
+ priv, ok := cert.PrivateKey.(crypto.Signer)
+ if !ok {
+ return nil
+ }
+
+ var sigAlgs []SignatureScheme
+ switch pub := priv.Public().(type) {
+ case *ecdsa.PublicKey:
+ if version != VersionTLS13 {
+ // In TLS 1.2 and earlier, ECDSA algorithms are not
+ // constrained to a single curve.
+ sigAlgs = []SignatureScheme{
+ ECDSAWithP256AndSHA256,
+ ECDSAWithP384AndSHA384,
+ ECDSAWithP521AndSHA512,
+ ECDSAWithSHA1,
+ }
+ break
+ }
+ switch pub.Curve {
+ case elliptic.P256():
+ sigAlgs = []SignatureScheme{ECDSAWithP256AndSHA256}
+ case elliptic.P384():
+ sigAlgs = []SignatureScheme{ECDSAWithP384AndSHA384}
+ case elliptic.P521():
+ sigAlgs = []SignatureScheme{ECDSAWithP521AndSHA512}
+ default:
+ return nil
+ }
+ case *rsa.PublicKey:
+ size := pub.Size()
+ sigAlgs = make([]SignatureScheme, 0, len(rsaSignatureSchemes))
+ for _, candidate := range rsaSignatureSchemes {
+ if size >= candidate.minModulusBytes && version <= candidate.maxVersion {
+ sigAlgs = append(sigAlgs, candidate.scheme)
+ }
+ }
+ case ed25519.PublicKey:
+ sigAlgs = []SignatureScheme{Ed25519}
+ default:
+ return nil
+ }
+
+ if cert.SupportedSignatureAlgorithms != nil {
+ var filteredSigAlgs []SignatureScheme
+ for _, sigAlg := range sigAlgs {
+ if isSupportedSignatureAlgorithm(sigAlg, cert.SupportedSignatureAlgorithms) {
+ filteredSigAlgs = append(filteredSigAlgs, sigAlg)
+ }
+ }
+ return filteredSigAlgs
+ }
+ return sigAlgs
+}
+
+// selectSignatureScheme picks a SignatureScheme from the peer's preference list
+// that works with the selected certificate. It's only called for protocol
+// versions that support signature algorithms, so TLS 1.2 and 1.3.
+func selectSignatureScheme(vers uint16, c *Certificate, peerAlgs []SignatureScheme) (SignatureScheme, error) {
+ supportedAlgs := signatureSchemesForCertificate(vers, c)
+ if len(supportedAlgs) == 0 {
+ return 0, unsupportedCertificateError(c)
+ }
+ if len(peerAlgs) == 0 && vers == VersionTLS12 {
+ // For TLS 1.2, if the client didn't send signature_algorithms then we
+ // can assume that it supports SHA1. See RFC 5246, Section 7.4.1.4.1.
+ peerAlgs = []SignatureScheme{PKCS1WithSHA1, ECDSAWithSHA1}
+ }
+ // Pick signature scheme in the peer's preference order, as our
+ // preference order is not configurable.
+ for _, preferredAlg := range peerAlgs {
+ if needFIPS() && !isSupportedSignatureAlgorithm(preferredAlg, fipsSupportedSignatureAlgorithms) {
+ continue
+ }
+ if isSupportedSignatureAlgorithm(preferredAlg, supportedAlgs) {
+ return preferredAlg, nil
+ }
+ }
+ return 0, errors.New("tls: peer doesn't support any of the certificate's signature algorithms")
+}
+
+// unsupportedCertificateError returns a helpful error for certificates with
+// an unsupported private key.
+func unsupportedCertificateError(cert *Certificate) error {
+ switch cert.PrivateKey.(type) {
+ case rsa.PrivateKey, ecdsa.PrivateKey:
+ return fmt.Errorf("tls: unsupported certificate: private key is %T, expected *%T",
+ cert.PrivateKey, cert.PrivateKey)
+ case *ed25519.PrivateKey:
+ return fmt.Errorf("tls: unsupported certificate: private key is *ed25519.PrivateKey, expected ed25519.PrivateKey")
+ }
+
+ signer, ok := cert.PrivateKey.(crypto.Signer)
+ if !ok {
+ return fmt.Errorf("tls: certificate private key (%T) does not implement crypto.Signer",
+ cert.PrivateKey)
+ }
+
+ switch pub := signer.Public().(type) {
+ case *ecdsa.PublicKey:
+ switch pub.Curve {
+ case elliptic.P256():
+ case elliptic.P384():
+ case elliptic.P521():
+ default:
+ return fmt.Errorf("tls: unsupported certificate curve (%s)", pub.Curve.Params().Name)
+ }
+ case *rsa.PublicKey:
+ return fmt.Errorf("tls: certificate RSA key size too small for supported signature algorithms")
+ case ed25519.PublicKey:
+ default:
+ return fmt.Errorf("tls: unsupported certificate key (%T)", pub)
+ }
+
+ if cert.SupportedSignatureAlgorithms != nil {
+ return fmt.Errorf("tls: peer doesn't support the certificate custom signature algorithms")
+ }
+
+ return fmt.Errorf("tls: internal error: unsupported key (%T)", cert.PrivateKey)
+}
diff --git a/vendor/github.com/quic-go/qtls-go1-20/cache.go b/vendor/github.com/quic-go/qtls-go1-20/cache.go
new file mode 100644
index 0000000000..99e0c5fb86
--- /dev/null
+++ b/vendor/github.com/quic-go/qtls-go1-20/cache.go
@@ -0,0 +1,95 @@
+// Copyright 2022 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package qtls
+
+import (
+ "crypto/x509"
+ "runtime"
+ "sync"
+ "sync/atomic"
+)
+
+type cacheEntry struct {
+ refs atomic.Int64
+ cert *x509.Certificate
+}
+
+// certCache implements an intern table for reference counted x509.Certificates,
+// implemented in a similar fashion to BoringSSL's CRYPTO_BUFFER_POOL. This
+// allows for a single x509.Certificate to be kept in memory and referenced from
+// multiple Conns. Returned references should not be mutated by callers. Certificates
+// are still safe to use after they are removed from the cache.
+//
+// Certificates are returned wrapped in a activeCert struct that should be held by
+// the caller. When references to the activeCert are freed, the number of references
+// to the certificate in the cache is decremented. Once the number of references
+// reaches zero, the entry is evicted from the cache.
+//
+// The main difference between this implementation and CRYPTO_BUFFER_POOL is that
+// CRYPTO_BUFFER_POOL is a more generic structure which supports blobs of data,
+// rather than specific structures. Since we only care about x509.Certificates,
+// certCache is implemented as a specific cache, rather than a generic one.
+//
+// See https://boringssl.googlesource.com/boringssl/+/master/include/openssl/pool.h
+// and https://boringssl.googlesource.com/boringssl/+/master/crypto/pool/pool.c
+// for the BoringSSL reference.
+type certCache struct {
+ sync.Map
+}
+
+var clientCertCache = new(certCache)
+
+// activeCert is a handle to a certificate held in the cache. Once there are
+// no alive activeCerts for a given certificate, the certificate is removed
+// from the cache by a finalizer.
+type activeCert struct {
+ cert *x509.Certificate
+}
+
+// active increments the number of references to the entry, wraps the
+// certificate in the entry in a activeCert, and sets the finalizer.
+//
+// Note that there is a race between active and the finalizer set on the
+// returned activeCert, triggered if active is called after the ref count is
+// decremented such that refs may be > 0 when evict is called. We consider this
+// safe, since the caller holding an activeCert for an entry that is no longer
+// in the cache is fine, with the only side effect being the memory overhead of
+// there being more than one distinct reference to a certificate alive at once.
+func (cc *certCache) active(e *cacheEntry) *activeCert {
+ e.refs.Add(1)
+ a := &activeCert{e.cert}
+ runtime.SetFinalizer(a, func(_ *activeCert) {
+ if e.refs.Add(-1) == 0 {
+ cc.evict(e)
+ }
+ })
+ return a
+}
+
+// evict removes a cacheEntry from the cache.
+func (cc *certCache) evict(e *cacheEntry) {
+ cc.Delete(string(e.cert.Raw))
+}
+
+// newCert returns a x509.Certificate parsed from der. If there is already a copy
+// of the certificate in the cache, a reference to the existing certificate will
+// be returned. Otherwise, a fresh certificate will be added to the cache, and
+// the reference returned. The returned reference should not be mutated.
+func (cc *certCache) newCert(der []byte) (*activeCert, error) {
+ if entry, ok := cc.Load(string(der)); ok {
+ return cc.active(entry.(*cacheEntry)), nil
+ }
+
+ cert, err := x509.ParseCertificate(der)
+ if err != nil {
+ return nil, err
+ }
+
+ entry := &cacheEntry{cert: cert}
+ if entry, loaded := cc.LoadOrStore(string(der), entry); loaded {
+ return cc.active(entry.(*cacheEntry)), nil
+ }
+ return cc.active(entry), nil
+}
diff --git a/vendor/github.com/quic-go/qtls-go1-20/cipher_suites.go b/vendor/github.com/quic-go/qtls-go1-20/cipher_suites.go
new file mode 100644
index 0000000000..43d2131573
--- /dev/null
+++ b/vendor/github.com/quic-go/qtls-go1-20/cipher_suites.go
@@ -0,0 +1,693 @@
+// Copyright 2010 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package qtls
+
+import (
+ "crypto"
+ "crypto/aes"
+ "crypto/cipher"
+ "crypto/des"
+ "crypto/hmac"
+ "crypto/rc4"
+ "crypto/sha1"
+ "crypto/sha256"
+ "fmt"
+ "hash"
+
+ "golang.org/x/crypto/chacha20poly1305"
+)
+
+// CipherSuite is a TLS cipher suite. Note that most functions in this package
+// accept and expose cipher suite IDs instead of this type.
+type CipherSuite struct {
+ ID uint16
+ Name string
+
+ // Supported versions is the list of TLS protocol versions that can
+ // negotiate this cipher suite.
+ SupportedVersions []uint16
+
+ // Insecure is true if the cipher suite has known security issues
+ // due to its primitives, design, or implementation.
+ Insecure bool
+}
+
+var (
+ supportedUpToTLS12 = []uint16{VersionTLS10, VersionTLS11, VersionTLS12}
+ supportedOnlyTLS12 = []uint16{VersionTLS12}
+ supportedOnlyTLS13 = []uint16{VersionTLS13}
+)
+
+// CipherSuites returns a list of cipher suites currently implemented by this
+// package, excluding those with security issues, which are returned by
+// InsecureCipherSuites.
+//
+// The list is sorted by ID. Note that the default cipher suites selected by
+// this package might depend on logic that can't be captured by a static list,
+// and might not match those returned by this function.
+func CipherSuites() []*CipherSuite {
+ return []*CipherSuite{
+ {TLS_RSA_WITH_AES_128_CBC_SHA, "TLS_RSA_WITH_AES_128_CBC_SHA", supportedUpToTLS12, false},
+ {TLS_RSA_WITH_AES_256_CBC_SHA, "TLS_RSA_WITH_AES_256_CBC_SHA", supportedUpToTLS12, false},
+ {TLS_RSA_WITH_AES_128_GCM_SHA256, "TLS_RSA_WITH_AES_128_GCM_SHA256", supportedOnlyTLS12, false},
+ {TLS_RSA_WITH_AES_256_GCM_SHA384, "TLS_RSA_WITH_AES_256_GCM_SHA384", supportedOnlyTLS12, false},
+
+ {TLS_AES_128_GCM_SHA256, "TLS_AES_128_GCM_SHA256", supportedOnlyTLS13, false},
+ {TLS_AES_256_GCM_SHA384, "TLS_AES_256_GCM_SHA384", supportedOnlyTLS13, false},
+ {TLS_CHACHA20_POLY1305_SHA256, "TLS_CHACHA20_POLY1305_SHA256", supportedOnlyTLS13, false},
+
+ {TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA", supportedUpToTLS12, false},
+ {TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA", supportedUpToTLS12, false},
+ {TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA", supportedUpToTLS12, false},
+ {TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA", supportedUpToTLS12, false},
+ {TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256", supportedOnlyTLS12, false},
+ {TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384", supportedOnlyTLS12, false},
+ {TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256", supportedOnlyTLS12, false},
+ {TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384", supportedOnlyTLS12, false},
+ {TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256", supportedOnlyTLS12, false},
+ {TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256", supportedOnlyTLS12, false},
+ }
+}
+
+// InsecureCipherSuites returns a list of cipher suites currently implemented by
+// this package and which have security issues.
+//
+// Most applications should not use the cipher suites in this list, and should
+// only use those returned by CipherSuites.
+func InsecureCipherSuites() []*CipherSuite {
+ // This list includes RC4, CBC_SHA256, and 3DES cipher suites. See
+ // cipherSuitesPreferenceOrder for details.
+ return []*CipherSuite{
+ {TLS_RSA_WITH_RC4_128_SHA, "TLS_RSA_WITH_RC4_128_SHA", supportedUpToTLS12, true},
+ {TLS_RSA_WITH_3DES_EDE_CBC_SHA, "TLS_RSA_WITH_3DES_EDE_CBC_SHA", supportedUpToTLS12, true},
+ {TLS_RSA_WITH_AES_128_CBC_SHA256, "TLS_RSA_WITH_AES_128_CBC_SHA256", supportedOnlyTLS12, true},
+ {TLS_ECDHE_ECDSA_WITH_RC4_128_SHA, "TLS_ECDHE_ECDSA_WITH_RC4_128_SHA", supportedUpToTLS12, true},
+ {TLS_ECDHE_RSA_WITH_RC4_128_SHA, "TLS_ECDHE_RSA_WITH_RC4_128_SHA", supportedUpToTLS12, true},
+ {TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA, "TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA", supportedUpToTLS12, true},
+ {TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256, "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256", supportedOnlyTLS12, true},
+ {TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256, "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256", supportedOnlyTLS12, true},
+ }
+}
+
+// CipherSuiteName returns the standard name for the passed cipher suite ID
+// (e.g. "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256"), or a fallback representation
+// of the ID value if the cipher suite is not implemented by this package.
+func CipherSuiteName(id uint16) string {
+ for _, c := range CipherSuites() {
+ if c.ID == id {
+ return c.Name
+ }
+ }
+ for _, c := range InsecureCipherSuites() {
+ if c.ID == id {
+ return c.Name
+ }
+ }
+ return fmt.Sprintf("0x%04X", id)
+}
+
+const (
+ // suiteECDHE indicates that the cipher suite involves elliptic curve
+ // Diffie-Hellman. This means that it should only be selected when the
+ // client indicates that it supports ECC with a curve and point format
+ // that we're happy with.
+ suiteECDHE = 1 << iota
+ // suiteECSign indicates that the cipher suite involves an ECDSA or
+ // EdDSA signature and therefore may only be selected when the server's
+ // certificate is ECDSA or EdDSA. If this is not set then the cipher suite
+ // is RSA based.
+ suiteECSign
+ // suiteTLS12 indicates that the cipher suite should only be advertised
+ // and accepted when using TLS 1.2.
+ suiteTLS12
+ // suiteSHA384 indicates that the cipher suite uses SHA384 as the
+ // handshake hash.
+ suiteSHA384
+)
+
+// A cipherSuite is a TLS 1.0–1.2 cipher suite, and defines the key exchange
+// mechanism, as well as the cipher+MAC pair or the AEAD.
+type cipherSuite struct {
+ id uint16
+ // the lengths, in bytes, of the key material needed for each component.
+ keyLen int
+ macLen int
+ ivLen int
+ ka func(version uint16) keyAgreement
+ // flags is a bitmask of the suite* values, above.
+ flags int
+ cipher func(key, iv []byte, isRead bool) any
+ mac func(key []byte) hash.Hash
+ aead func(key, fixedNonce []byte) aead
+}
+
+var cipherSuites = []*cipherSuite{ // TODO: replace with a map, since the order doesn't matter.
+ {TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305, 32, 0, 12, ecdheRSAKA, suiteECDHE | suiteTLS12, nil, nil, aeadChaCha20Poly1305},
+ {TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305, 32, 0, 12, ecdheECDSAKA, suiteECDHE | suiteECSign | suiteTLS12, nil, nil, aeadChaCha20Poly1305},
+ {TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, 16, 0, 4, ecdheRSAKA, suiteECDHE | suiteTLS12, nil, nil, aeadAESGCM},
+ {TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, 16, 0, 4, ecdheECDSAKA, suiteECDHE | suiteECSign | suiteTLS12, nil, nil, aeadAESGCM},
+ {TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, 32, 0, 4, ecdheRSAKA, suiteECDHE | suiteTLS12 | suiteSHA384, nil, nil, aeadAESGCM},
+ {TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, 32, 0, 4, ecdheECDSAKA, suiteECDHE | suiteECSign | suiteTLS12 | suiteSHA384, nil, nil, aeadAESGCM},
+ {TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256, 16, 32, 16, ecdheRSAKA, suiteECDHE | suiteTLS12, cipherAES, macSHA256, nil},
+ {TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, 16, 20, 16, ecdheRSAKA, suiteECDHE, cipherAES, macSHA1, nil},
+ {TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256, 16, 32, 16, ecdheECDSAKA, suiteECDHE | suiteECSign | suiteTLS12, cipherAES, macSHA256, nil},
+ {TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, 16, 20, 16, ecdheECDSAKA, suiteECDHE | suiteECSign, cipherAES, macSHA1, nil},
+ {TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, 32, 20, 16, ecdheRSAKA, suiteECDHE, cipherAES, macSHA1, nil},
+ {TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, 32, 20, 16, ecdheECDSAKA, suiteECDHE | suiteECSign, cipherAES, macSHA1, nil},
+ {TLS_RSA_WITH_AES_128_GCM_SHA256, 16, 0, 4, rsaKA, suiteTLS12, nil, nil, aeadAESGCM},
+ {TLS_RSA_WITH_AES_256_GCM_SHA384, 32, 0, 4, rsaKA, suiteTLS12 | suiteSHA384, nil, nil, aeadAESGCM},
+ {TLS_RSA_WITH_AES_128_CBC_SHA256, 16, 32, 16, rsaKA, suiteTLS12, cipherAES, macSHA256, nil},
+ {TLS_RSA_WITH_AES_128_CBC_SHA, 16, 20, 16, rsaKA, 0, cipherAES, macSHA1, nil},
+ {TLS_RSA_WITH_AES_256_CBC_SHA, 32, 20, 16, rsaKA, 0, cipherAES, macSHA1, nil},
+ {TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA, 24, 20, 8, ecdheRSAKA, suiteECDHE, cipher3DES, macSHA1, nil},
+ {TLS_RSA_WITH_3DES_EDE_CBC_SHA, 24, 20, 8, rsaKA, 0, cipher3DES, macSHA1, nil},
+ {TLS_RSA_WITH_RC4_128_SHA, 16, 20, 0, rsaKA, 0, cipherRC4, macSHA1, nil},
+ {TLS_ECDHE_RSA_WITH_RC4_128_SHA, 16, 20, 0, ecdheRSAKA, suiteECDHE, cipherRC4, macSHA1, nil},
+ {TLS_ECDHE_ECDSA_WITH_RC4_128_SHA, 16, 20, 0, ecdheECDSAKA, suiteECDHE | suiteECSign, cipherRC4, macSHA1, nil},
+}
+
+// selectCipherSuite returns the first TLS 1.0–1.2 cipher suite from ids which
+// is also in supportedIDs and passes the ok filter.
+func selectCipherSuite(ids, supportedIDs []uint16, ok func(*cipherSuite) bool) *cipherSuite {
+ for _, id := range ids {
+ candidate := cipherSuiteByID(id)
+ if candidate == nil || !ok(candidate) {
+ continue
+ }
+
+ for _, suppID := range supportedIDs {
+ if id == suppID {
+ return candidate
+ }
+ }
+ }
+ return nil
+}
+
+// A cipherSuiteTLS13 defines only the pair of the AEAD algorithm and hash
+// algorithm to be used with HKDF. See RFC 8446, Appendix B.4.
+type cipherSuiteTLS13 struct {
+ id uint16
+ keyLen int
+ aead func(key, fixedNonce []byte) aead
+ hash crypto.Hash
+}
+
+type CipherSuiteTLS13 struct {
+ ID uint16
+ KeyLen int
+ Hash crypto.Hash
+ AEAD func(key, fixedNonce []byte) cipher.AEAD
+}
+
+func (c *CipherSuiteTLS13) IVLen() int {
+ return aeadNonceLength
+}
+
+var cipherSuitesTLS13 = []*cipherSuiteTLS13{ // TODO: replace with a map.
+ {TLS_AES_128_GCM_SHA256, 16, aeadAESGCMTLS13, crypto.SHA256},
+ {TLS_CHACHA20_POLY1305_SHA256, 32, aeadChaCha20Poly1305, crypto.SHA256},
+ {TLS_AES_256_GCM_SHA384, 32, aeadAESGCMTLS13, crypto.SHA384},
+}
+
+// cipherSuitesPreferenceOrder is the order in which we'll select (on the
+// server) or advertise (on the client) TLS 1.0–1.2 cipher suites.
+//
+// Cipher suites are filtered but not reordered based on the application and
+// peer's preferences, meaning we'll never select a suite lower in this list if
+// any higher one is available. This makes it more defensible to keep weaker
+// cipher suites enabled, especially on the server side where we get the last
+// word, since there are no known downgrade attacks on cipher suites selection.
+//
+// The list is sorted by applying the following priority rules, stopping at the
+// first (most important) applicable one:
+//
+// - Anything else comes before RC4
+//
+// RC4 has practically exploitable biases. See https://www.rc4nomore.com.
+//
+// - Anything else comes before CBC_SHA256
+//
+// SHA-256 variants of the CBC ciphersuites don't implement any Lucky13
+// countermeasures. See http://www.isg.rhul.ac.uk/tls/Lucky13.html and
+// https://www.imperialviolet.org/2013/02/04/luckythirteen.html.
+//
+// - Anything else comes before 3DES
+//
+// 3DES has 64-bit blocks, which makes it fundamentally susceptible to
+// birthday attacks. See https://sweet32.info.
+//
+// - ECDHE comes before anything else
+//
+// Once we got the broken stuff out of the way, the most important
+// property a cipher suite can have is forward secrecy. We don't
+// implement FFDHE, so that means ECDHE.
+//
+// - AEADs come before CBC ciphers
+//
+// Even with Lucky13 countermeasures, MAC-then-Encrypt CBC cipher suites
+// are fundamentally fragile, and suffered from an endless sequence of
+// padding oracle attacks. See https://eprint.iacr.org/2015/1129,
+// https://www.imperialviolet.org/2014/12/08/poodleagain.html, and
+// https://blog.cloudflare.com/yet-another-padding-oracle-in-openssl-cbc-ciphersuites/.
+//
+// - AES comes before ChaCha20
+//
+// When AES hardware is available, AES-128-GCM and AES-256-GCM are faster
+// than ChaCha20Poly1305.
+//
+// When AES hardware is not available, AES-128-GCM is one or more of: much
+// slower, way more complex, and less safe (because not constant time)
+// than ChaCha20Poly1305.
+//
+// We use this list if we think both peers have AES hardware, and
+// cipherSuitesPreferenceOrderNoAES otherwise.
+//
+// - AES-128 comes before AES-256
+//
+// The only potential advantages of AES-256 are better multi-target
+// margins, and hypothetical post-quantum properties. Neither apply to
+// TLS, and AES-256 is slower due to its four extra rounds (which don't
+// contribute to the advantages above).
+//
+// - ECDSA comes before RSA
+//
+// The relative order of ECDSA and RSA cipher suites doesn't matter,
+// as they depend on the certificate. Pick one to get a stable order.
+var cipherSuitesPreferenceOrder = []uint16{
+ // AEADs w/ ECDHE
+ TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
+ TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
+ TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305, TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305,
+
+ // CBC w/ ECDHE
+ TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
+ TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
+
+ // AEADs w/o ECDHE
+ TLS_RSA_WITH_AES_128_GCM_SHA256,
+ TLS_RSA_WITH_AES_256_GCM_SHA384,
+
+ // CBC w/o ECDHE
+ TLS_RSA_WITH_AES_128_CBC_SHA,
+ TLS_RSA_WITH_AES_256_CBC_SHA,
+
+ // 3DES
+ TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA,
+ TLS_RSA_WITH_3DES_EDE_CBC_SHA,
+
+ // CBC_SHA256
+ TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256, TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256,
+ TLS_RSA_WITH_AES_128_CBC_SHA256,
+
+ // RC4
+ TLS_ECDHE_ECDSA_WITH_RC4_128_SHA, TLS_ECDHE_RSA_WITH_RC4_128_SHA,
+ TLS_RSA_WITH_RC4_128_SHA,
+}
+
+var cipherSuitesPreferenceOrderNoAES = []uint16{
+ // ChaCha20Poly1305
+ TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305, TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305,
+
+ // AES-GCM w/ ECDHE
+ TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
+ TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
+
+ // The rest of cipherSuitesPreferenceOrder.
+ TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
+ TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
+ TLS_RSA_WITH_AES_128_GCM_SHA256,
+ TLS_RSA_WITH_AES_256_GCM_SHA384,
+ TLS_RSA_WITH_AES_128_CBC_SHA,
+ TLS_RSA_WITH_AES_256_CBC_SHA,
+ TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA,
+ TLS_RSA_WITH_3DES_EDE_CBC_SHA,
+ TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256, TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256,
+ TLS_RSA_WITH_AES_128_CBC_SHA256,
+ TLS_ECDHE_ECDSA_WITH_RC4_128_SHA, TLS_ECDHE_RSA_WITH_RC4_128_SHA,
+ TLS_RSA_WITH_RC4_128_SHA,
+}
+
+// disabledCipherSuites are not used unless explicitly listed in
+// Config.CipherSuites. They MUST be at the end of cipherSuitesPreferenceOrder.
+var disabledCipherSuites = []uint16{
+ // CBC_SHA256
+ TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256, TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256,
+ TLS_RSA_WITH_AES_128_CBC_SHA256,
+
+ // RC4
+ TLS_ECDHE_ECDSA_WITH_RC4_128_SHA, TLS_ECDHE_RSA_WITH_RC4_128_SHA,
+ TLS_RSA_WITH_RC4_128_SHA,
+}
+
+var (
+ defaultCipherSuitesLen = len(cipherSuitesPreferenceOrder) - len(disabledCipherSuites)
+ defaultCipherSuites = cipherSuitesPreferenceOrder[:defaultCipherSuitesLen]
+)
+
+// defaultCipherSuitesTLS13 is also the preference order, since there are no
+// disabled by default TLS 1.3 cipher suites. The same AES vs ChaCha20 logic as
+// cipherSuitesPreferenceOrder applies.
+var defaultCipherSuitesTLS13 = []uint16{
+ TLS_AES_128_GCM_SHA256,
+ TLS_AES_256_GCM_SHA384,
+ TLS_CHACHA20_POLY1305_SHA256,
+}
+
+var defaultCipherSuitesTLS13NoAES = []uint16{
+ TLS_CHACHA20_POLY1305_SHA256,
+ TLS_AES_128_GCM_SHA256,
+ TLS_AES_256_GCM_SHA384,
+}
+
+var aesgcmCiphers = map[uint16]bool{
+ // TLS 1.2
+ TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256: true,
+ TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384: true,
+ TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256: true,
+ TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384: true,
+ // TLS 1.3
+ TLS_AES_128_GCM_SHA256: true,
+ TLS_AES_256_GCM_SHA384: true,
+}
+
+var nonAESGCMAEADCiphers = map[uint16]bool{
+ // TLS 1.2
+ TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305: true,
+ TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305: true,
+ // TLS 1.3
+ TLS_CHACHA20_POLY1305_SHA256: true,
+}
+
+// aesgcmPreferred returns whether the first known cipher in the preference list
+// is an AES-GCM cipher, implying the peer has hardware support for it.
+func aesgcmPreferred(ciphers []uint16) bool {
+ for _, cID := range ciphers {
+ if c := cipherSuiteByID(cID); c != nil {
+ return aesgcmCiphers[cID]
+ }
+ if c := cipherSuiteTLS13ByID(cID); c != nil {
+ return aesgcmCiphers[cID]
+ }
+ }
+ return false
+}
+
+func cipherRC4(key, iv []byte, isRead bool) any {
+ cipher, _ := rc4.NewCipher(key)
+ return cipher
+}
+
+func cipher3DES(key, iv []byte, isRead bool) any {
+ block, _ := des.NewTripleDESCipher(key)
+ if isRead {
+ return cipher.NewCBCDecrypter(block, iv)
+ }
+ return cipher.NewCBCEncrypter(block, iv)
+}
+
+func cipherAES(key, iv []byte, isRead bool) any {
+ block, _ := aes.NewCipher(key)
+ if isRead {
+ return cipher.NewCBCDecrypter(block, iv)
+ }
+ return cipher.NewCBCEncrypter(block, iv)
+}
+
+// macSHA1 returns a SHA-1 based constant time MAC.
+func macSHA1(key []byte) hash.Hash {
+ h := sha1.New
+ h = newConstantTimeHash(h)
+ return hmac.New(h, key)
+}
+
+// macSHA256 returns a SHA-256 based MAC. This is only supported in TLS 1.2 and
+// is currently only used in disabled-by-default cipher suites.
+func macSHA256(key []byte) hash.Hash {
+ return hmac.New(sha256.New, key)
+}
+
+type aead interface {
+ cipher.AEAD
+
+ // explicitNonceLen returns the number of bytes of explicit nonce
+ // included in each record. This is eight for older AEADs and
+ // zero for modern ones.
+ explicitNonceLen() int
+}
+
+const (
+ aeadNonceLength = 12
+ noncePrefixLength = 4
+)
+
+// prefixNonceAEAD wraps an AEAD and prefixes a fixed portion of the nonce to
+// each call.
+type prefixNonceAEAD struct {
+ // nonce contains the fixed part of the nonce in the first four bytes.
+ nonce [aeadNonceLength]byte
+ aead cipher.AEAD
+}
+
+func (f *prefixNonceAEAD) NonceSize() int { return aeadNonceLength - noncePrefixLength }
+func (f *prefixNonceAEAD) Overhead() int { return f.aead.Overhead() }
+func (f *prefixNonceAEAD) explicitNonceLen() int { return f.NonceSize() }
+
+func (f *prefixNonceAEAD) Seal(out, nonce, plaintext, additionalData []byte) []byte {
+ copy(f.nonce[4:], nonce)
+ return f.aead.Seal(out, f.nonce[:], plaintext, additionalData)
+}
+
+func (f *prefixNonceAEAD) Open(out, nonce, ciphertext, additionalData []byte) ([]byte, error) {
+ copy(f.nonce[4:], nonce)
+ return f.aead.Open(out, f.nonce[:], ciphertext, additionalData)
+}
+
+// xorNonceAEAD wraps an AEAD by XORing in a fixed pattern to the nonce
+// before each call.
+type xorNonceAEAD struct {
+ nonceMask [aeadNonceLength]byte
+ aead cipher.AEAD
+}
+
+func (f *xorNonceAEAD) NonceSize() int { return 8 } // 64-bit sequence number
+func (f *xorNonceAEAD) Overhead() int { return f.aead.Overhead() }
+func (f *xorNonceAEAD) explicitNonceLen() int { return 0 }
+
+func (f *xorNonceAEAD) Seal(out, nonce, plaintext, additionalData []byte) []byte {
+ for i, b := range nonce {
+ f.nonceMask[4+i] ^= b
+ }
+ result := f.aead.Seal(out, f.nonceMask[:], plaintext, additionalData)
+ for i, b := range nonce {
+ f.nonceMask[4+i] ^= b
+ }
+
+ return result
+}
+
+func (f *xorNonceAEAD) Open(out, nonce, ciphertext, additionalData []byte) ([]byte, error) {
+ for i, b := range nonce {
+ f.nonceMask[4+i] ^= b
+ }
+ result, err := f.aead.Open(out, f.nonceMask[:], ciphertext, additionalData)
+ for i, b := range nonce {
+ f.nonceMask[4+i] ^= b
+ }
+
+ return result, err
+}
+
+func aeadAESGCM(key, noncePrefix []byte) aead {
+ if len(noncePrefix) != noncePrefixLength {
+ panic("tls: internal error: wrong nonce length")
+ }
+ aes, err := aes.NewCipher(key)
+ if err != nil {
+ panic(err)
+ }
+ var aead cipher.AEAD
+ aead, err = cipher.NewGCM(aes)
+ if err != nil {
+ panic(err)
+ }
+
+ ret := &prefixNonceAEAD{aead: aead}
+ copy(ret.nonce[:], noncePrefix)
+ return ret
+}
+
+// AEADAESGCMTLS13 creates a new AES-GCM AEAD for TLS 1.3
+func AEADAESGCMTLS13(key, fixedNonce []byte) cipher.AEAD {
+ return aeadAESGCMTLS13(key, fixedNonce)
+}
+
+func aeadAESGCMTLS13(key, nonceMask []byte) aead {
+ if len(nonceMask) != aeadNonceLength {
+ panic("tls: internal error: wrong nonce length")
+ }
+ aes, err := aes.NewCipher(key)
+ if err != nil {
+ panic(err)
+ }
+ aead, err := cipher.NewGCM(aes)
+ if err != nil {
+ panic(err)
+ }
+
+ ret := &xorNonceAEAD{aead: aead}
+ copy(ret.nonceMask[:], nonceMask)
+ return ret
+}
+
+func aeadChaCha20Poly1305(key, nonceMask []byte) aead {
+ if len(nonceMask) != aeadNonceLength {
+ panic("tls: internal error: wrong nonce length")
+ }
+ aead, err := chacha20poly1305.New(key)
+ if err != nil {
+ panic(err)
+ }
+
+ ret := &xorNonceAEAD{aead: aead}
+ copy(ret.nonceMask[:], nonceMask)
+ return ret
+}
+
+type constantTimeHash interface {
+ hash.Hash
+ ConstantTimeSum(b []byte) []byte
+}
+
+// cthWrapper wraps any hash.Hash that implements ConstantTimeSum, and replaces
+// with that all calls to Sum. It's used to obtain a ConstantTimeSum-based HMAC.
+type cthWrapper struct {
+ h constantTimeHash
+}
+
+func (c *cthWrapper) Size() int { return c.h.Size() }
+func (c *cthWrapper) BlockSize() int { return c.h.BlockSize() }
+func (c *cthWrapper) Reset() { c.h.Reset() }
+func (c *cthWrapper) Write(p []byte) (int, error) { return c.h.Write(p) }
+func (c *cthWrapper) Sum(b []byte) []byte { return c.h.ConstantTimeSum(b) }
+
+func newConstantTimeHash(h func() hash.Hash) func() hash.Hash {
+ return func() hash.Hash {
+ return &cthWrapper{h().(constantTimeHash)}
+ }
+}
+
+// tls10MAC implements the TLS 1.0 MAC function. RFC 2246, Section 6.2.3.
+func tls10MAC(h hash.Hash, out, seq, header, data, extra []byte) []byte {
+ h.Reset()
+ h.Write(seq)
+ h.Write(header)
+ h.Write(data)
+ res := h.Sum(out)
+ if extra != nil {
+ h.Write(extra)
+ }
+ return res
+}
+
+func rsaKA(version uint16) keyAgreement {
+ return rsaKeyAgreement{}
+}
+
+func ecdheECDSAKA(version uint16) keyAgreement {
+ return &ecdheKeyAgreement{
+ isRSA: false,
+ version: version,
+ }
+}
+
+func ecdheRSAKA(version uint16) keyAgreement {
+ return &ecdheKeyAgreement{
+ isRSA: true,
+ version: version,
+ }
+}
+
+// mutualCipherSuite returns a cipherSuite given a list of supported
+// ciphersuites and the id requested by the peer.
+func mutualCipherSuite(have []uint16, want uint16) *cipherSuite {
+ for _, id := range have {
+ if id == want {
+ return cipherSuiteByID(id)
+ }
+ }
+ return nil
+}
+
+func cipherSuiteByID(id uint16) *cipherSuite {
+ for _, cipherSuite := range cipherSuites {
+ if cipherSuite.id == id {
+ return cipherSuite
+ }
+ }
+ return nil
+}
+
+func mutualCipherSuiteTLS13(have []uint16, want uint16) *cipherSuiteTLS13 {
+ for _, id := range have {
+ if id == want {
+ return cipherSuiteTLS13ByID(id)
+ }
+ }
+ return nil
+}
+
+func cipherSuiteTLS13ByID(id uint16) *cipherSuiteTLS13 {
+ for _, cipherSuite := range cipherSuitesTLS13 {
+ if cipherSuite.id == id {
+ return cipherSuite
+ }
+ }
+ return nil
+}
+
+// A list of cipher suite IDs that are, or have been, implemented by this
+// package.
+//
+// See https://www.iana.org/assignments/tls-parameters/tls-parameters.xml
+const (
+ // TLS 1.0 - 1.2 cipher suites.
+ TLS_RSA_WITH_RC4_128_SHA uint16 = 0x0005
+ TLS_RSA_WITH_3DES_EDE_CBC_SHA uint16 = 0x000a
+ TLS_RSA_WITH_AES_128_CBC_SHA uint16 = 0x002f
+ TLS_RSA_WITH_AES_256_CBC_SHA uint16 = 0x0035
+ TLS_RSA_WITH_AES_128_CBC_SHA256 uint16 = 0x003c
+ TLS_RSA_WITH_AES_128_GCM_SHA256 uint16 = 0x009c
+ TLS_RSA_WITH_AES_256_GCM_SHA384 uint16 = 0x009d
+ TLS_ECDHE_ECDSA_WITH_RC4_128_SHA uint16 = 0xc007
+ TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA uint16 = 0xc009
+ TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA uint16 = 0xc00a
+ TLS_ECDHE_RSA_WITH_RC4_128_SHA uint16 = 0xc011
+ TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA uint16 = 0xc012
+ TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA uint16 = 0xc013
+ TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA uint16 = 0xc014
+ TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256 uint16 = 0xc023
+ TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256 uint16 = 0xc027
+ TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 uint16 = 0xc02f
+ TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 uint16 = 0xc02b
+ TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 uint16 = 0xc030
+ TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 uint16 = 0xc02c
+ TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256 uint16 = 0xcca8
+ TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256 uint16 = 0xcca9
+
+ // TLS 1.3 cipher suites.
+ TLS_AES_128_GCM_SHA256 uint16 = 0x1301
+ TLS_AES_256_GCM_SHA384 uint16 = 0x1302
+ TLS_CHACHA20_POLY1305_SHA256 uint16 = 0x1303
+
+ // TLS_FALLBACK_SCSV isn't a standard cipher suite but an indicator
+ // that the client is doing version fallback. See RFC 7507.
+ TLS_FALLBACK_SCSV uint16 = 0x5600
+
+ // Legacy names for the corresponding cipher suites with the correct _SHA256
+ // suffix, retained for backward compatibility.
+ TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305 = TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256
+ TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305 = TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256
+)
diff --git a/vendor/github.com/quic-go/qtls-go1-20/common.go b/vendor/github.com/quic-go/qtls-go1-20/common.go
new file mode 100644
index 0000000000..4490e867ef
--- /dev/null
+++ b/vendor/github.com/quic-go/qtls-go1-20/common.go
@@ -0,0 +1,1538 @@
+// Copyright 2009 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package qtls
+
+import (
+ "bytes"
+ "container/list"
+ "context"
+ "crypto"
+ "crypto/ecdsa"
+ "crypto/ed25519"
+ "crypto/elliptic"
+ "crypto/rand"
+ "crypto/rsa"
+ "crypto/sha512"
+ "crypto/tls"
+ "crypto/x509"
+ "errors"
+ "fmt"
+ "io"
+ "net"
+ "strings"
+ "sync"
+ "time"
+)
+
+const (
+ VersionTLS10 = 0x0301
+ VersionTLS11 = 0x0302
+ VersionTLS12 = 0x0303
+ VersionTLS13 = 0x0304
+
+ // Deprecated: SSLv3 is cryptographically broken, and is no longer
+ // supported by this package. See golang.org/issue/32716.
+ VersionSSL30 = 0x0300
+)
+
+const (
+ maxPlaintext = 16384 // maximum plaintext payload length
+ maxCiphertext = 16384 + 2048 // maximum ciphertext payload length
+ maxCiphertextTLS13 = 16384 + 256 // maximum ciphertext length in TLS 1.3
+ recordHeaderLen = 5 // record header length
+ maxHandshake = 65536 // maximum handshake we support (protocol max is 16 MB)
+ maxUselessRecords = 16 // maximum number of consecutive non-advancing records
+)
+
+// TLS record types.
+type recordType uint8
+
+const (
+ recordTypeChangeCipherSpec recordType = 20
+ recordTypeAlert recordType = 21
+ recordTypeHandshake recordType = 22
+ recordTypeApplicationData recordType = 23
+)
+
+// TLS handshake message types.
+const (
+ typeHelloRequest uint8 = 0
+ typeClientHello uint8 = 1
+ typeServerHello uint8 = 2
+ typeNewSessionTicket uint8 = 4
+ typeEndOfEarlyData uint8 = 5
+ typeEncryptedExtensions uint8 = 8
+ typeCertificate uint8 = 11
+ typeServerKeyExchange uint8 = 12
+ typeCertificateRequest uint8 = 13
+ typeServerHelloDone uint8 = 14
+ typeCertificateVerify uint8 = 15
+ typeClientKeyExchange uint8 = 16
+ typeFinished uint8 = 20
+ typeCertificateStatus uint8 = 22
+ typeKeyUpdate uint8 = 24
+ typeNextProtocol uint8 = 67 // Not IANA assigned
+ typeMessageHash uint8 = 254 // synthetic message
+)
+
+// TLS compression types.
+const (
+ compressionNone uint8 = 0
+)
+
+type Extension struct {
+ Type uint16
+ Data []byte
+}
+
+// TLS extension numbers
+const (
+ extensionServerName uint16 = 0
+ extensionStatusRequest uint16 = 5
+ extensionSupportedCurves uint16 = 10 // supported_groups in TLS 1.3, see RFC 8446, Section 4.2.7
+ extensionSupportedPoints uint16 = 11
+ extensionSignatureAlgorithms uint16 = 13
+ extensionALPN uint16 = 16
+ extensionSCT uint16 = 18
+ extensionSessionTicket uint16 = 35
+ extensionPreSharedKey uint16 = 41
+ extensionEarlyData uint16 = 42
+ extensionSupportedVersions uint16 = 43
+ extensionCookie uint16 = 44
+ extensionPSKModes uint16 = 45
+ extensionCertificateAuthorities uint16 = 47
+ extensionSignatureAlgorithmsCert uint16 = 50
+ extensionKeyShare uint16 = 51
+ extensionRenegotiationInfo uint16 = 0xff01
+)
+
+// TLS signaling cipher suite values
+const (
+ scsvRenegotiation uint16 = 0x00ff
+)
+
+type EncryptionLevel uint8
+
+const (
+ EncryptionHandshake EncryptionLevel = iota
+ Encryption0RTT
+ EncryptionApplication
+)
+
+// CurveID is a tls.CurveID
+type CurveID = tls.CurveID
+
+const (
+ CurveP256 CurveID = 23
+ CurveP384 CurveID = 24
+ CurveP521 CurveID = 25
+ X25519 CurveID = 29
+)
+
+// TLS 1.3 Key Share. See RFC 8446, Section 4.2.8.
+type keyShare struct {
+ group CurveID
+ data []byte
+}
+
+// TLS 1.3 PSK Key Exchange Modes. See RFC 8446, Section 4.2.9.
+const (
+ pskModePlain uint8 = 0
+ pskModeDHE uint8 = 1
+)
+
+// TLS 1.3 PSK Identity. Can be a Session Ticket, or a reference to a saved
+// session. See RFC 8446, Section 4.2.11.
+type pskIdentity struct {
+ label []byte
+ obfuscatedTicketAge uint32
+}
+
+// TLS Elliptic Curve Point Formats
+// https://www.iana.org/assignments/tls-parameters/tls-parameters.xml#tls-parameters-9
+const (
+ pointFormatUncompressed uint8 = 0
+)
+
+// TLS CertificateStatusType (RFC 3546)
+const (
+ statusTypeOCSP uint8 = 1
+)
+
+// Certificate types (for certificateRequestMsg)
+const (
+ certTypeRSASign = 1
+ certTypeECDSASign = 64 // ECDSA or EdDSA keys, see RFC 8422, Section 3.
+)
+
+// Signature algorithms (for internal signaling use). Starting at 225 to avoid overlap with
+// TLS 1.2 codepoints (RFC 5246, Appendix A.4.1), with which these have nothing to do.
+const (
+ signaturePKCS1v15 uint8 = iota + 225
+ signatureRSAPSS
+ signatureECDSA
+ signatureEd25519
+)
+
+// directSigning is a standard Hash value that signals that no pre-hashing
+// should be performed, and that the input should be signed directly. It is the
+// hash function associated with the Ed25519 signature scheme.
+var directSigning crypto.Hash = 0
+
+// defaultSupportedSignatureAlgorithms contains the signature and hash algorithms that
+// the code advertises as supported in a TLS 1.2+ ClientHello and in a TLS 1.2+
+// CertificateRequest. The two fields are merged to match with TLS 1.3.
+// Note that in TLS 1.2, the ECDSA algorithms are not constrained to P-256, etc.
+var defaultSupportedSignatureAlgorithms = []SignatureScheme{
+ PSSWithSHA256,
+ ECDSAWithP256AndSHA256,
+ Ed25519,
+ PSSWithSHA384,
+ PSSWithSHA512,
+ PKCS1WithSHA256,
+ PKCS1WithSHA384,
+ PKCS1WithSHA512,
+ ECDSAWithP384AndSHA384,
+ ECDSAWithP521AndSHA512,
+ PKCS1WithSHA1,
+ ECDSAWithSHA1,
+}
+
+// helloRetryRequestRandom is set as the Random value of a ServerHello
+// to signal that the message is actually a HelloRetryRequest.
+var helloRetryRequestRandom = []byte{ // See RFC 8446, Section 4.1.3.
+ 0xCF, 0x21, 0xAD, 0x74, 0xE5, 0x9A, 0x61, 0x11,
+ 0xBE, 0x1D, 0x8C, 0x02, 0x1E, 0x65, 0xB8, 0x91,
+ 0xC2, 0xA2, 0x11, 0x16, 0x7A, 0xBB, 0x8C, 0x5E,
+ 0x07, 0x9E, 0x09, 0xE2, 0xC8, 0xA8, 0x33, 0x9C,
+}
+
+const (
+ // downgradeCanaryTLS12 or downgradeCanaryTLS11 is embedded in the server
+ // random as a downgrade protection if the server would be capable of
+ // negotiating a higher version. See RFC 8446, Section 4.1.3.
+ downgradeCanaryTLS12 = "DOWNGRD\x01"
+ downgradeCanaryTLS11 = "DOWNGRD\x00"
+)
+
+// testingOnlyForceDowngradeCanary is set in tests to force the server side to
+// include downgrade canaries even if it's using its highers supported version.
+var testingOnlyForceDowngradeCanary bool
+
+type ConnectionState = tls.ConnectionState
+
+// ConnectionState records basic TLS details about the connection.
+type connectionState struct {
+ // Version is the TLS version used by the connection (e.g. VersionTLS12).
+ Version uint16
+
+ // HandshakeComplete is true if the handshake has concluded.
+ HandshakeComplete bool
+
+ // DidResume is true if this connection was successfully resumed from a
+ // previous session with a session ticket or similar mechanism.
+ DidResume bool
+
+ // CipherSuite is the cipher suite negotiated for the connection (e.g.
+ // TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, TLS_AES_128_GCM_SHA256).
+ CipherSuite uint16
+
+ // NegotiatedProtocol is the application protocol negotiated with ALPN.
+ NegotiatedProtocol string
+
+ // NegotiatedProtocolIsMutual used to indicate a mutual NPN negotiation.
+ //
+ // Deprecated: this value is always true.
+ NegotiatedProtocolIsMutual bool
+
+ // ServerName is the value of the Server Name Indication extension sent by
+ // the client. It's available both on the server and on the client side.
+ ServerName string
+
+ // PeerCertificates are the parsed certificates sent by the peer, in the
+ // order in which they were sent. The first element is the leaf certificate
+ // that the connection is verified against.
+ //
+ // On the client side, it can't be empty. On the server side, it can be
+ // empty if Config.ClientAuth is not RequireAnyClientCert or
+ // RequireAndVerifyClientCert.
+ //
+ // PeerCertificates and its contents should not be modified.
+ PeerCertificates []*x509.Certificate
+
+ // VerifiedChains is a list of one or more chains where the first element is
+ // PeerCertificates[0] and the last element is from Config.RootCAs (on the
+ // client side) or Config.ClientCAs (on the server side).
+ //
+ // On the client side, it's set if Config.InsecureSkipVerify is false. On
+ // the server side, it's set if Config.ClientAuth is VerifyClientCertIfGiven
+ // (and the peer provided a certificate) or RequireAndVerifyClientCert.
+ //
+ // VerifiedChains and its contents should not be modified.
+ VerifiedChains [][]*x509.Certificate
+
+ // SignedCertificateTimestamps is a list of SCTs provided by the peer
+ // through the TLS handshake for the leaf certificate, if any.
+ SignedCertificateTimestamps [][]byte
+
+ // OCSPResponse is a stapled Online Certificate Status Protocol (OCSP)
+ // response provided by the peer for the leaf certificate, if any.
+ OCSPResponse []byte
+
+ // TLSUnique contains the "tls-unique" channel binding value (see RFC 5929,
+ // Section 3). This value will be nil for TLS 1.3 connections and for all
+ // resumed connections.
+ //
+ // Deprecated: there are conditions in which this value might not be unique
+ // to a connection. See the Security Considerations sections of RFC 5705 and
+ // RFC 7627, and https://mitls.org/pages/attacks/3SHAKE#channelbindings.
+ TLSUnique []byte
+
+ // ekm is a closure exposed via ExportKeyingMaterial.
+ ekm func(label string, context []byte, length int) ([]byte, error)
+}
+
+type ConnectionStateWith0RTT struct {
+ ConnectionState
+
+ Used0RTT bool // true if 0-RTT was both offered and accepted
+}
+
+// ClientAuthType is tls.ClientAuthType
+type ClientAuthType = tls.ClientAuthType
+
+const (
+ NoClientCert = tls.NoClientCert
+ RequestClientCert = tls.RequestClientCert
+ RequireAnyClientCert = tls.RequireAnyClientCert
+ VerifyClientCertIfGiven = tls.VerifyClientCertIfGiven
+ RequireAndVerifyClientCert = tls.RequireAndVerifyClientCert
+)
+
+// requiresClientCert reports whether the ClientAuthType requires a client
+// certificate to be provided.
+func requiresClientCert(c ClientAuthType) bool {
+ switch c {
+ case RequireAnyClientCert, RequireAndVerifyClientCert:
+ return true
+ default:
+ return false
+ }
+}
+
+// ClientSessionState contains the state needed by clients to resume TLS
+// sessions.
+type ClientSessionState = tls.ClientSessionState
+
+type clientSessionState struct {
+ sessionTicket []uint8 // Encrypted ticket used for session resumption with server
+ vers uint16 // TLS version negotiated for the session
+ cipherSuite uint16 // Ciphersuite negotiated for the session
+ masterSecret []byte // Full handshake MasterSecret, or TLS 1.3 resumption_master_secret
+ serverCertificates []*x509.Certificate // Certificate chain presented by the server
+ verifiedChains [][]*x509.Certificate // Certificate chains we built for verification
+ receivedAt time.Time // When the session ticket was received from the server
+ ocspResponse []byte // Stapled OCSP response presented by the server
+ scts [][]byte // SCTs presented by the server
+
+ // TLS 1.3 fields.
+ nonce []byte // Ticket nonce sent by the server, to derive PSK
+ useBy time.Time // Expiration of the ticket lifetime as set by the server
+ ageAdd uint32 // Random obfuscation factor for sending the ticket age
+}
+
+// ClientSessionCache is a cache of ClientSessionState objects that can be used
+// by a client to resume a TLS session with a given server. ClientSessionCache
+// implementations should expect to be called concurrently from different
+// goroutines. Up to TLS 1.2, only ticket-based resumption is supported, not
+// SessionID-based resumption. In TLS 1.3 they were merged into PSK modes, which
+// are supported via this interface.
+//
+//go:generate sh -c "mockgen -package qtls -destination mock_client_session_cache_test.go github.com/quic-go/qtls-go1-20 ClientSessionCache"
+type ClientSessionCache = tls.ClientSessionCache
+
+// SignatureScheme is a tls.SignatureScheme
+type SignatureScheme = tls.SignatureScheme
+
+const (
+ // RSASSA-PKCS1-v1_5 algorithms.
+ PKCS1WithSHA256 SignatureScheme = 0x0401
+ PKCS1WithSHA384 SignatureScheme = 0x0501
+ PKCS1WithSHA512 SignatureScheme = 0x0601
+
+ // RSASSA-PSS algorithms with public key OID rsaEncryption.
+ PSSWithSHA256 SignatureScheme = 0x0804
+ PSSWithSHA384 SignatureScheme = 0x0805
+ PSSWithSHA512 SignatureScheme = 0x0806
+
+ // ECDSA algorithms. Only constrained to a specific curve in TLS 1.3.
+ ECDSAWithP256AndSHA256 SignatureScheme = 0x0403
+ ECDSAWithP384AndSHA384 SignatureScheme = 0x0503
+ ECDSAWithP521AndSHA512 SignatureScheme = 0x0603
+
+ // EdDSA algorithms.
+ Ed25519 SignatureScheme = 0x0807
+
+ // Legacy signature and hash algorithms for TLS 1.2.
+ PKCS1WithSHA1 SignatureScheme = 0x0201
+ ECDSAWithSHA1 SignatureScheme = 0x0203
+)
+
+// ClientHelloInfo contains information from a ClientHello message in order to
+// guide application logic in the GetCertificate and GetConfigForClient callbacks.
+type ClientHelloInfo = tls.ClientHelloInfo
+
+type clientHelloInfo struct {
+ // CipherSuites lists the CipherSuites supported by the client (e.g.
+ // TLS_AES_128_GCM_SHA256, TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256).
+ CipherSuites []uint16
+
+ // 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
+
+ // SupportedCurves lists the elliptic curves supported by the client.
+ // SupportedCurves is set only if the Supported Elliptic Curves
+ // Extension is being used (see RFC 4492, Section 5.1.1).
+ SupportedCurves []CurveID
+
+ // SupportedPoints lists the point formats supported by the client.
+ // SupportedPoints is set only if the Supported Point Formats Extension
+ // is being used (see RFC 4492, Section 5.1.2).
+ SupportedPoints []uint8
+
+ // SignatureSchemes lists the signature and hash schemes that the client
+ // is willing to verify. SignatureSchemes is set only if the Signature
+ // Algorithms Extension is being used (see RFC 5246, Section 7.4.1.4.1).
+ SignatureSchemes []SignatureScheme
+
+ // SupportedProtos lists the application protocols supported by the client.
+ // SupportedProtos is set only if the Application-Layer Protocol
+ // Negotiation Extension is being used (see RFC 7301, Section 3.1).
+ //
+ // Servers can select a protocol by setting Config.NextProtos in a
+ // GetConfigForClient return value.
+ SupportedProtos []string
+
+ // SupportedVersions lists the TLS versions supported by the client.
+ // For TLS versions less than 1.3, this is extrapolated from the max
+ // version advertised by the client, so values other than the greatest
+ // might be rejected if used.
+ SupportedVersions []uint16
+
+ // Conn is the underlying net.Conn for the connection. Do not read
+ // from, or write to, this connection; that will cause the TLS
+ // connection to fail.
+ Conn net.Conn
+
+ // config is embedded by the GetCertificate or GetConfigForClient caller,
+ // for use with SupportsCertificate.
+ config *Config
+
+ // ctx is the context of the handshake that is in progress.
+ ctx context.Context
+}
+
+// Context returns the context of the handshake that is in progress.
+// This context is a child of the context passed to HandshakeContext,
+// if any, and is canceled when the handshake concludes.
+func (c *clientHelloInfo) Context() context.Context {
+ return c.ctx
+}
+
+// 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 = tls.CertificateRequestInfo
+
+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
+
+ // SignatureSchemes lists the signature schemes that the server is
+ // willing to verify.
+ SignatureSchemes []SignatureScheme
+
+ // Version is the TLS version that was negotiated for this connection.
+ Version uint16
+
+ // ctx is the context of the handshake that is in progress.
+ ctx context.Context
+}
+
+// Context returns the context of the handshake that is in progress.
+// This context is a child of the context passed to HandshakeContext,
+// if any, and is canceled when the handshake concludes.
+func (c *certificateRequestInfo) Context() context.Context {
+ return c.ctx
+}
+
+// RenegotiationSupport enumerates the different levels of support for TLS
+// renegotiation. TLS renegotiation is the act of performing subsequent
+// handshakes on a connection after the first. This significantly complicates
+// the state machine and has been the source of numerous, subtle security
+// issues. Initiating a renegotiation is not supported, but support for
+// accepting renegotiation requests may be enabled.
+//
+// Even when enabled, the server may not change its identity between handshakes
+// (i.e. the leaf certificate must be the same). Additionally, concurrent
+// handshake and application data flow is not permitted so renegotiation can
+// only be used with protocols that synchronise with the renegotiation, such as
+// HTTPS.
+//
+// Renegotiation is not defined in TLS 1.3.
+type RenegotiationSupport = tls.RenegotiationSupport
+
+const (
+ // RenegotiateNever disables renegotiation.
+ RenegotiateNever = tls.RenegotiateNever
+
+ // RenegotiateOnceAsClient allows a remote server to request
+ // renegotiation once per connection.
+ RenegotiateOnceAsClient = tls.RenegotiateOnceAsClient
+
+ // RenegotiateFreelyAsClient allows a remote server to repeatedly
+ // request renegotiation.
+ RenegotiateFreelyAsClient = tls.RenegotiateFreelyAsClient
+)
+
+// A Config structure is used to configure a TLS client or server.
+// After one has been passed to a TLS function it must not be
+// modified. A Config may be reused; the tls package will also not
+// modify it.
+type Config = tls.Config
+
+type config struct {
+ // Rand provides the source of entropy for nonces and RSA blinding.
+ // If Rand is nil, TLS uses the cryptographic random reader in package
+ // crypto/rand.
+ // The Reader must be safe for use by multiple goroutines.
+ Rand io.Reader
+
+ // Time returns the current time as the number of seconds since the epoch.
+ // If Time is nil, TLS uses time.Now.
+ Time func() time.Time
+
+ // Certificates contains one or more certificate chains to present to the
+ // other side of the connection. The first certificate compatible with the
+ // peer's requirements is selected automatically.
+ //
+ // Server configurations must set one of Certificates, GetCertificate or
+ // GetConfigForClient. Clients doing client-authentication may set either
+ // Certificates or GetClientCertificate.
+ //
+ // Note: if there are multiple Certificates, and they don't have the
+ // optional field Leaf set, certificate selection will incur a significant
+ // per-handshake performance cost.
+ Certificates []Certificate
+
+ // NameToCertificate maps from a certificate name to an element of
+ // Certificates. Note that a certificate name can be of the form
+ // '*.example.com' and so doesn't have to be a domain name as such.
+ //
+ // Deprecated: NameToCertificate only allows associating a single
+ // certificate with a given name. Leave this field nil to let the library
+ // select the first compatible chain from Certificates.
+ NameToCertificate map[string]*Certificate
+
+ // 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.
+ //
+ // Once a Certificate is returned it should not be modified.
+ GetCertificate func(*ClientHelloInfo) (*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 may be called multiple times for the same
+ // connection if renegotiation occurs or if TLS 1.3 is in use.
+ //
+ // Once a Certificate is returned it should not be modified.
+ GetClientCertificate func(*CertificateRequestInfo) (*Certificate, error)
+
+ // GetConfigForClient, if not nil, is called after a ClientHello is
+ // received from a client. It may return a non-nil Config in order to
+ // change the Config that will be used to handle this connection. If
+ // the returned Config is nil, the original Config will be used. The
+ // Config returned by this callback may not be subsequently modified.
+ //
+ // If GetConfigForClient is nil, the Config passed to Server() will be
+ // used for all connections.
+ //
+ // If SessionTicketKey was explicitly set on the returned Config, or if
+ // SetSessionTicketKeys was called on the returned Config, those keys will
+ // be used. Otherwise, the original Config keys will be used (and possibly
+ // rotated if they are automatically managed).
+ GetConfigForClient func(*ClientHelloInfo) (*Config, error)
+
+ // VerifyPeerCertificate, if not nil, is called after normal
+ // certificate verification by either a TLS client or server. It
+ // receives the raw ASN.1 certificates provided by the peer and also
+ // any verified chains that normal processing found. 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 argument will always be nil.
+ //
+ // verifiedChains and its contents should not be modified.
+ VerifyPeerCertificate func(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error
+
+ // VerifyConnection, if not nil, is called after normal certificate
+ // verification 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(ConnectionState) error
+
+ // RootCAs defines the set of root certificate authorities
+ // that clients use when verifying server certificates.
+ // If RootCAs is nil, TLS uses the host's root CA set.
+ RootCAs *x509.CertPool
+
+ // NextProtos is a list of supported application level protocols, in
+ // order of preference. If both peers support ALPN, the selected
+ // protocol will be one from this list, and the connection will fail
+ // if there is no mutually supported protocol. If NextProtos is empty
+ // or the peer doesn't support ALPN, the connection will succeed and
+ // ConnectionState.NegotiatedProtocol will be empty.
+ NextProtos []string
+
+ // ServerName is used to verify the hostname on the returned
+ // certificates unless InsecureSkipVerify is given. It is also included
+ // in the client's handshake to support virtual hosting unless it is
+ // an IP address.
+ ServerName string
+
+ // ClientAuth determines the server's policy for
+ // TLS Client Authentication. The default is NoClientCert.
+ ClientAuth ClientAuthType
+
+ // 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
+
+ // InsecureSkipVerify controls whether a client verifies the server's
+ // certificate chain and host name. If InsecureSkipVerify is true, crypto/tls
+ // accepts any certificate presented by the server and any host name in that
+ // certificate. In this mode, TLS is susceptible to machine-in-the-middle
+ // attacks unless custom verification is used. This should be used only for
+ // testing or in combination with VerifyConnection or VerifyPeerCertificate.
+ InsecureSkipVerify bool
+
+ // CipherSuites is a list of enabled TLS 1.0–1.2 cipher suites. The order of
+ // the list is ignored. Note that TLS 1.3 ciphersuites are not configurable.
+ //
+ // If CipherSuites is nil, a safe default list is used. The default cipher
+ // suites might change over time.
+ CipherSuites []uint16
+
+ // PreferServerCipherSuites is a legacy field and has no effect.
+ //
+ // It used to control whether the server would follow the client's or the
+ // server's preference. Servers now select the best mutually supported
+ // cipher suite based on logic that takes into account inferred client
+ // hardware, server hardware, and security.
+ //
+ // Deprecated: PreferServerCipherSuites is ignored.
+ PreferServerCipherSuites bool
+
+ // SessionTicketsDisabled may be set to true to disable session ticket and
+ // PSK (resumption) support. Note that on clients, session ticket support is
+ // also disabled if ClientSessionCache is nil.
+ SessionTicketsDisabled bool
+
+ // SessionTicketKey is used by TLS servers to provide session resumption.
+ // See RFC 5077 and the PSK mode of RFC 8446. If zero, it will be filled
+ // with random data before the first server handshake.
+ //
+ // Deprecated: if this field is left at zero, session ticket keys will be
+ // automatically rotated every day and dropped after seven days. For
+ // customizing the rotation schedule or synchronizing servers that are
+ // terminating connections for the same host, use SetSessionTicketKeys.
+ SessionTicketKey [32]byte
+
+ // ClientSessionCache is a cache of ClientSessionState entries for TLS
+ // session resumption. It is only used by clients.
+ ClientSessionCache ClientSessionCache
+
+ // MinVersion contains the minimum TLS version that is acceptable.
+ //
+ // By default, TLS 1.2 is currently used as the minimum when acting as a
+ // client, and TLS 1.0 when acting as a server. TLS 1.0 is the minimum
+ // supported by this package, both as a client and as a server.
+ //
+ // The client-side default can temporarily be reverted to TLS 1.0 by
+ // including the value "x509sha1=1" in the GODEBUG environment variable.
+ // Note that this option will be removed in Go 1.19 (but it will still be
+ // possible to set this field to VersionTLS10 explicitly).
+ MinVersion uint16
+
+ // MaxVersion contains the maximum TLS version that is acceptable.
+ //
+ // By default, the maximum version supported by this package is used,
+ // which is currently TLS 1.3.
+ MaxVersion uint16
+
+ // CurvePreferences contains the elliptic curves that will be used in
+ // an ECDHE handshake, in preference order. If empty, the default will
+ // be used. The client will use the first preference as the type for
+ // its key share in TLS 1.3. This may change in the future.
+ CurvePreferences []CurveID
+
+ // DynamicRecordSizingDisabled disables adaptive sizing of TLS records.
+ // When true, the largest possible TLS record size is always used. When
+ // false, the size of TLS records may be adjusted in an attempt to
+ // improve latency.
+ DynamicRecordSizingDisabled bool
+
+ // Renegotiation controls what types of renegotiation are supported.
+ // The default, none, is correct for the vast majority of applications.
+ Renegotiation RenegotiationSupport
+
+ // 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
+
+ // mutex protects sessionTicketKeys and autoSessionTicketKeys.
+ mutex sync.RWMutex
+ // sessionTicketKeys contains zero or more ticket keys. If set, it means
+ // the keys were set with SessionTicketKey or SetSessionTicketKeys. The
+ // first key is used for new tickets and any subsequent keys can be used to
+ // decrypt old tickets. The slice contents are not protected by the mutex
+ // and are immutable.
+ sessionTicketKeys []ticketKey
+ // autoSessionTicketKeys is like sessionTicketKeys but is owned by the
+ // auto-rotation logic. See Config.ticketKeys.
+ autoSessionTicketKeys []ticketKey
+}
+
+// A RecordLayer handles encrypting and decrypting of TLS messages.
+type RecordLayer interface {
+ SetReadKey(encLevel EncryptionLevel, suite *CipherSuiteTLS13, trafficSecret []byte)
+ SetWriteKey(encLevel EncryptionLevel, suite *CipherSuiteTLS13, trafficSecret []byte)
+ ReadHandshakeMessage() ([]byte, error)
+ WriteRecord([]byte) (int, error)
+ SendAlert(uint8)
+}
+
+type ExtraConfig struct {
+ // GetExtensions, if not nil, is called before a message that allows
+ // sending of extensions is sent.
+ // Currently only implemented for the ClientHello message (for the client)
+ // and for the EncryptedExtensions message (for the server).
+ // Only valid for TLS 1.3.
+ GetExtensions func(handshakeMessageType uint8) []Extension
+
+ // ReceivedExtensions, if not nil, is called when a message that allows the
+ // inclusion of extensions is received.
+ // It is called with an empty slice of extensions, if the message didn't
+ // contain any extensions.
+ // Currently only implemented for the ClientHello message (sent by the
+ // client) and for the EncryptedExtensions message (sent by the server).
+ // Only valid for TLS 1.3.
+ ReceivedExtensions func(handshakeMessageType uint8, exts []Extension)
+
+ // AlternativeRecordLayer is used by QUIC
+ AlternativeRecordLayer RecordLayer
+
+ // Enforce the selection of a supported application protocol.
+ // Only works for TLS 1.3.
+ // If enabled, client and server have to agree on an application protocol.
+ // Otherwise, connection establishment fails.
+ EnforceNextProtoSelection bool
+
+ // If MaxEarlyData is greater than 0, the client will be allowed to send early
+ // data when resuming a session.
+ // Requires the AlternativeRecordLayer to be set.
+ //
+ // It has no meaning on the client.
+ MaxEarlyData uint32
+
+ // The Accept0RTT callback is called when the client offers 0-RTT.
+ // The server then has to decide if it wants to accept or reject 0-RTT.
+ // It is only used for servers.
+ Accept0RTT func(appData []byte) bool
+
+ // 0RTTRejected is called when the server rejectes 0-RTT.
+ // It is only used for clients.
+ Rejected0RTT func()
+
+ // If set, the client will export the 0-RTT key when resuming a session that
+ // allows sending of early data.
+ // Requires the AlternativeRecordLayer to be set.
+ //
+ // It has no meaning to the server.
+ Enable0RTT bool
+
+ // Is called when the client saves a session ticket to the session ticket.
+ // This gives the application the opportunity to save some data along with the ticket,
+ // which can be restored when the session ticket is used.
+ GetAppDataForSessionState func() []byte
+
+ // Is called when the client uses a session ticket.
+ // Restores the application data that was saved earlier on GetAppDataForSessionTicket.
+ SetAppDataFromSessionState func([]byte)
+}
+
+// Clone clones.
+func (c *ExtraConfig) Clone() *ExtraConfig {
+ return &ExtraConfig{
+ GetExtensions: c.GetExtensions,
+ ReceivedExtensions: c.ReceivedExtensions,
+ AlternativeRecordLayer: c.AlternativeRecordLayer,
+ EnforceNextProtoSelection: c.EnforceNextProtoSelection,
+ MaxEarlyData: c.MaxEarlyData,
+ Enable0RTT: c.Enable0RTT,
+ Accept0RTT: c.Accept0RTT,
+ Rejected0RTT: c.Rejected0RTT,
+ GetAppDataForSessionState: c.GetAppDataForSessionState,
+ SetAppDataFromSessionState: c.SetAppDataFromSessionState,
+ }
+}
+
+func (c *ExtraConfig) usesAlternativeRecordLayer() bool {
+ return c != nil && c.AlternativeRecordLayer != nil
+}
+
+const (
+ // ticketKeyNameLen is the number of bytes of identifier that is prepended to
+ // an encrypted session ticket in order to identify the key used to encrypt it.
+ ticketKeyNameLen = 16
+
+ // ticketKeyLifetime is how long a ticket key remains valid and can be used to
+ // resume a client connection.
+ ticketKeyLifetime = 7 * 24 * time.Hour // 7 days
+
+ // ticketKeyRotation is how often the server should rotate the session ticket key
+ // that is used for new tickets.
+ ticketKeyRotation = 24 * time.Hour
+)
+
+// ticketKey is the internal representation of a session ticket key.
+type ticketKey struct {
+ // keyName is an opaque byte string that serves to identify the session
+ // ticket key. It's exposed as plaintext in every session ticket.
+ keyName [ticketKeyNameLen]byte
+ aesKey [16]byte
+ hmacKey [16]byte
+ // created is the time at which this ticket key was created. See Config.ticketKeys.
+ created time.Time
+}
+
+// ticketKeyFromBytes converts from the external representation of a session
+// ticket key to a ticketKey. Externally, session ticket keys are 32 random
+// bytes and this function expands that into sufficient name and key material.
+func (c *config) ticketKeyFromBytes(b [32]byte) (key ticketKey) {
+ hashed := sha512.Sum512(b[:])
+ copy(key.keyName[:], hashed[:ticketKeyNameLen])
+ copy(key.aesKey[:], hashed[ticketKeyNameLen:ticketKeyNameLen+16])
+ copy(key.hmacKey[:], hashed[ticketKeyNameLen+16:ticketKeyNameLen+32])
+ key.created = c.time()
+ return key
+}
+
+// maxSessionTicketLifetime is the maximum allowed lifetime of a TLS 1.3 session
+// ticket, and the lifetime we set for tickets we send.
+const maxSessionTicketLifetime = 7 * 24 * time.Hour
+
+// Clone returns a shallow clone of c or nil if c is nil. It is safe to clone a Config that is
+// being used concurrently by a TLS client or server.
+func (c *config) Clone() *config {
+ if c == nil {
+ return nil
+ }
+ c.mutex.RLock()
+ defer c.mutex.RUnlock()
+ return &config{
+ Rand: c.Rand,
+ Time: c.Time,
+ Certificates: c.Certificates,
+ NameToCertificate: c.NameToCertificate,
+ GetCertificate: c.GetCertificate,
+ GetClientCertificate: c.GetClientCertificate,
+ GetConfigForClient: c.GetConfigForClient,
+ VerifyPeerCertificate: c.VerifyPeerCertificate,
+ VerifyConnection: c.VerifyConnection,
+ RootCAs: c.RootCAs,
+ NextProtos: c.NextProtos,
+ ServerName: c.ServerName,
+ ClientAuth: c.ClientAuth,
+ ClientCAs: c.ClientCAs,
+ InsecureSkipVerify: c.InsecureSkipVerify,
+ CipherSuites: c.CipherSuites,
+ PreferServerCipherSuites: c.PreferServerCipherSuites,
+ SessionTicketsDisabled: c.SessionTicketsDisabled,
+ SessionTicketKey: c.SessionTicketKey,
+ ClientSessionCache: c.ClientSessionCache,
+ MinVersion: c.MinVersion,
+ MaxVersion: c.MaxVersion,
+ CurvePreferences: c.CurvePreferences,
+ DynamicRecordSizingDisabled: c.DynamicRecordSizingDisabled,
+ Renegotiation: c.Renegotiation,
+ KeyLogWriter: c.KeyLogWriter,
+ sessionTicketKeys: c.sessionTicketKeys,
+ autoSessionTicketKeys: c.autoSessionTicketKeys,
+ }
+}
+
+// deprecatedSessionTicketKey is set as the prefix of SessionTicketKey if it was
+// randomized for backwards compatibility but is not in use.
+var deprecatedSessionTicketKey = []byte("DEPRECATED")
+
+// initLegacySessionTicketKeyRLocked ensures the legacy SessionTicketKey field is
+// randomized if empty, and that sessionTicketKeys is populated from it otherwise.
+func (c *config) initLegacySessionTicketKeyRLocked() {
+ // Don't write if SessionTicketKey is already defined as our deprecated string,
+ // or if it is defined by the user but sessionTicketKeys is already set.
+ if c.SessionTicketKey != [32]byte{} &&
+ (bytes.HasPrefix(c.SessionTicketKey[:], deprecatedSessionTicketKey) || len(c.sessionTicketKeys) > 0) {
+ return
+ }
+
+ // We need to write some data, so get an exclusive lock and re-check any conditions.
+ c.mutex.RUnlock()
+ defer c.mutex.RLock()
+ c.mutex.Lock()
+ defer c.mutex.Unlock()
+ if c.SessionTicketKey == [32]byte{} {
+ if _, err := io.ReadFull(c.rand(), c.SessionTicketKey[:]); err != nil {
+ panic(fmt.Sprintf("tls: unable to generate random session ticket key: %v", err))
+ }
+ // Write the deprecated prefix at the beginning so we know we created
+ // it. This key with the DEPRECATED prefix isn't used as an actual
+ // session ticket key, and is only randomized in case the application
+ // reuses it for some reason.
+ copy(c.SessionTicketKey[:], deprecatedSessionTicketKey)
+ } else if !bytes.HasPrefix(c.SessionTicketKey[:], deprecatedSessionTicketKey) && len(c.sessionTicketKeys) == 0 {
+ c.sessionTicketKeys = []ticketKey{c.ticketKeyFromBytes(c.SessionTicketKey)}
+ }
+
+}
+
+// ticketKeys returns the ticketKeys for this connection.
+// If configForClient has explicitly set keys, those will
+// be returned. Otherwise, the keys on c will be used and
+// may be rotated if auto-managed.
+// During rotation, any expired session ticket keys are deleted from
+// c.sessionTicketKeys. If the session ticket key that is currently
+// encrypting tickets (ie. the first ticketKey in c.sessionTicketKeys)
+// is not fresh, then a new session ticket key will be
+// created and prepended to c.sessionTicketKeys.
+func (c *config) ticketKeys(configForClient *config) []ticketKey {
+ // If the ConfigForClient callback returned a Config with explicitly set
+ // keys, use those, otherwise just use the original Config.
+ if configForClient != nil {
+ configForClient.mutex.RLock()
+ if configForClient.SessionTicketsDisabled {
+ return nil
+ }
+ configForClient.initLegacySessionTicketKeyRLocked()
+ if len(configForClient.sessionTicketKeys) != 0 {
+ ret := configForClient.sessionTicketKeys
+ configForClient.mutex.RUnlock()
+ return ret
+ }
+ configForClient.mutex.RUnlock()
+ }
+
+ c.mutex.RLock()
+ defer c.mutex.RUnlock()
+ if c.SessionTicketsDisabled {
+ return nil
+ }
+ c.initLegacySessionTicketKeyRLocked()
+ if len(c.sessionTicketKeys) != 0 {
+ return c.sessionTicketKeys
+ }
+ // Fast path for the common case where the key is fresh enough.
+ if len(c.autoSessionTicketKeys) > 0 && c.time().Sub(c.autoSessionTicketKeys[0].created) < ticketKeyRotation {
+ return c.autoSessionTicketKeys
+ }
+
+ // autoSessionTicketKeys are managed by auto-rotation.
+ c.mutex.RUnlock()
+ defer c.mutex.RLock()
+ c.mutex.Lock()
+ defer c.mutex.Unlock()
+ // Re-check the condition in case it changed since obtaining the new lock.
+ if len(c.autoSessionTicketKeys) == 0 || c.time().Sub(c.autoSessionTicketKeys[0].created) >= ticketKeyRotation {
+ var newKey [32]byte
+ if _, err := io.ReadFull(c.rand(), newKey[:]); err != nil {
+ panic(fmt.Sprintf("unable to generate random session ticket key: %v", err))
+ }
+ valid := make([]ticketKey, 0, len(c.autoSessionTicketKeys)+1)
+ valid = append(valid, c.ticketKeyFromBytes(newKey))
+ for _, k := range c.autoSessionTicketKeys {
+ // While rotating the current key, also remove any expired ones.
+ if c.time().Sub(k.created) < ticketKeyLifetime {
+ valid = append(valid, k)
+ }
+ }
+ c.autoSessionTicketKeys = valid
+ }
+ return c.autoSessionTicketKeys
+}
+
+// SetSessionTicketKeys updates the session ticket keys for a server.
+//
+// The first key will be used when creating new tickets, while all keys can be
+// used for decrypting tickets. It is safe to call this function while the
+// server is running in order to rotate the session ticket keys. The function
+// will panic if keys is empty.
+//
+// Calling this function will turn off automatic session ticket key rotation.
+//
+// If multiple servers are terminating connections for the same host they should
+// all have the same session ticket keys. If the session ticket keys leaks,
+// previously recorded and future TLS connections using those keys might be
+// compromised.
+func (c *config) SetSessionTicketKeys(keys [][32]byte) {
+ if len(keys) == 0 {
+ panic("tls: keys must have at least one key")
+ }
+
+ newKeys := make([]ticketKey, len(keys))
+ for i, bytes := range keys {
+ newKeys[i] = c.ticketKeyFromBytes(bytes)
+ }
+
+ c.mutex.Lock()
+ c.sessionTicketKeys = newKeys
+ c.mutex.Unlock()
+}
+
+func (c *config) rand() io.Reader {
+ r := c.Rand
+ if r == nil {
+ return rand.Reader
+ }
+ return r
+}
+
+func (c *config) time() time.Time {
+ t := c.Time
+ if t == nil {
+ t = time.Now
+ }
+ return t()
+}
+
+func (c *config) cipherSuites() []uint16 {
+ if needFIPS() {
+ return fipsCipherSuites(c)
+ }
+ if c.CipherSuites != nil {
+ return c.CipherSuites
+ }
+ return defaultCipherSuites
+}
+
+var supportedVersions = []uint16{
+ VersionTLS13,
+ VersionTLS12,
+ VersionTLS11,
+ VersionTLS10,
+}
+
+// roleClient and roleServer are meant to call supportedVersions and parents
+// with more readability at the callsite.
+const roleClient = true
+const roleServer = false
+
+func (c *config) supportedVersions(isClient bool) []uint16 {
+ versions := make([]uint16, 0, len(supportedVersions))
+ for _, v := range supportedVersions {
+ if needFIPS() && (v < fipsMinVersion(c) || v > fipsMaxVersion(c)) {
+ continue
+ }
+ if (c == nil || c.MinVersion == 0) &&
+ isClient && v < VersionTLS12 {
+ continue
+ }
+ if c != nil && c.MinVersion != 0 && v < c.MinVersion {
+ continue
+ }
+ if c != nil && c.MaxVersion != 0 && v > c.MaxVersion {
+ continue
+ }
+ versions = append(versions, v)
+ }
+ return versions
+}
+
+func (c *config) maxSupportedVersion(isClient bool) uint16 {
+ supportedVersions := c.supportedVersions(isClient)
+ if len(supportedVersions) == 0 {
+ return 0
+ }
+ return supportedVersions[0]
+}
+
+// supportedVersionsFromMax returns a list of supported versions derived from a
+// legacy maximum version value. Note that only versions supported by this
+// library are returned. Any newer peer will use supportedVersions anyway.
+func supportedVersionsFromMax(maxVersion uint16) []uint16 {
+ versions := make([]uint16, 0, len(supportedVersions))
+ for _, v := range supportedVersions {
+ if v > maxVersion {
+ continue
+ }
+ versions = append(versions, v)
+ }
+ return versions
+}
+
+var defaultCurvePreferences = []CurveID{X25519, CurveP256, CurveP384, CurveP521}
+
+func (c *config) curvePreferences() []CurveID {
+ if needFIPS() {
+ return fipsCurvePreferences(c)
+ }
+ if c == nil || len(c.CurvePreferences) == 0 {
+ return defaultCurvePreferences
+ }
+ return c.CurvePreferences
+}
+
+func (c *config) supportsCurve(curve CurveID) bool {
+ for _, cc := range c.curvePreferences() {
+ if cc == curve {
+ return true
+ }
+ }
+ return false
+}
+
+// mutualVersion returns the protocol version to use given the advertised
+// versions of the peer. Priority is given to the peer preference order.
+func (c *config) mutualVersion(isClient bool, peerVersions []uint16) (uint16, bool) {
+ supportedVersions := c.supportedVersions(isClient)
+ for _, peerVersion := range peerVersions {
+ for _, v := range supportedVersions {
+ if v == peerVersion {
+ return v, true
+ }
+ }
+ }
+ return 0, false
+}
+
+var errNoCertificates = errors.New("tls: no certificates configured")
+
+// getCertificate returns the best certificate for the given ClientHelloInfo,
+// defaulting to the first element of c.Certificates.
+func (c *config) getCertificate(clientHello *ClientHelloInfo) (*Certificate, error) {
+ if c.GetCertificate != nil &&
+ (len(c.Certificates) == 0 || len(clientHello.ServerName) > 0) {
+ cert, err := c.GetCertificate(clientHello)
+ if cert != nil || err != nil {
+ return cert, err
+ }
+ }
+
+ if len(c.Certificates) == 0 {
+ return nil, errNoCertificates
+ }
+
+ if len(c.Certificates) == 1 {
+ // There's only one choice, so no point doing any work.
+ return &c.Certificates[0], nil
+ }
+
+ if c.NameToCertificate != nil {
+ name := strings.ToLower(clientHello.ServerName)
+ if cert, ok := c.NameToCertificate[name]; ok {
+ return cert, nil
+ }
+ if len(name) > 0 {
+ labels := strings.Split(name, ".")
+ labels[0] = "*"
+ wildcardName := strings.Join(labels, ".")
+ if cert, ok := c.NameToCertificate[wildcardName]; ok {
+ return cert, nil
+ }
+ }
+ }
+
+ for _, cert := range c.Certificates {
+ if err := clientHello.SupportsCertificate(&cert); err == nil {
+ return &cert, nil
+ }
+ }
+
+ // If nothing matches, return the first certificate.
+ return &c.Certificates[0], nil
+}
+
+// SupportsCertificate returns nil if the provided certificate is supported by
+// the client that sent the ClientHello. Otherwise, it returns an error
+// describing the reason for the incompatibility.
+//
+// If this ClientHelloInfo was passed to a GetConfigForClient or GetCertificate
+// callback, this method will take into account the associated Config. Note that
+// if GetConfigForClient returns a different Config, the change can't be
+// accounted for by this method.
+//
+// This function will call x509.ParseCertificate unless c.Leaf is set, which can
+// incur a significant performance cost.
+func (chi *clientHelloInfo) SupportsCertificate(c *Certificate) error {
+ // Note we don't currently support certificate_authorities nor
+ // signature_algorithms_cert, and don't check the algorithms of the
+ // signatures on the chain (which anyway are a SHOULD, see RFC 8446,
+ // Section 4.4.2.2).
+
+ config := chi.config
+ if config == nil {
+ config = &Config{}
+ }
+ conf := fromConfig(config)
+ vers, ok := conf.mutualVersion(roleServer, chi.SupportedVersions)
+ if !ok {
+ return errors.New("no mutually supported protocol versions")
+ }
+
+ // If the client specified the name they are trying to connect to, the
+ // certificate needs to be valid for it.
+ if chi.ServerName != "" {
+ x509Cert, err := leafCertificate(c)
+ if err != nil {
+ return fmt.Errorf("failed to parse certificate: %w", err)
+ }
+ if err := x509Cert.VerifyHostname(chi.ServerName); err != nil {
+ return fmt.Errorf("certificate is not valid for requested server name: %w", err)
+ }
+ }
+
+ // supportsRSAFallback returns nil if the certificate and connection support
+ // the static RSA key exchange, and unsupported otherwise. The logic for
+ // supporting static RSA is completely disjoint from the logic for
+ // supporting signed key exchanges, so we just check it as a fallback.
+ supportsRSAFallback := func(unsupported error) error {
+ // TLS 1.3 dropped support for the static RSA key exchange.
+ if vers == VersionTLS13 {
+ return unsupported
+ }
+ // The static RSA key exchange works by decrypting a challenge with the
+ // RSA private key, not by signing, so check the PrivateKey implements
+ // crypto.Decrypter, like *rsa.PrivateKey does.
+ if priv, ok := c.PrivateKey.(crypto.Decrypter); ok {
+ if _, ok := priv.Public().(*rsa.PublicKey); !ok {
+ return unsupported
+ }
+ } else {
+ return unsupported
+ }
+ // Finally, there needs to be a mutual cipher suite that uses the static
+ // RSA key exchange instead of ECDHE.
+ rsaCipherSuite := selectCipherSuite(chi.CipherSuites, conf.cipherSuites(), func(c *cipherSuite) bool {
+ if c.flags&suiteECDHE != 0 {
+ return false
+ }
+ if vers < VersionTLS12 && c.flags&suiteTLS12 != 0 {
+ return false
+ }
+ return true
+ })
+ if rsaCipherSuite == nil {
+ return unsupported
+ }
+ return nil
+ }
+
+ // If the client sent the signature_algorithms extension, ensure it supports
+ // schemes we can use with this certificate and TLS version.
+ if len(chi.SignatureSchemes) > 0 {
+ if _, err := selectSignatureScheme(vers, c, chi.SignatureSchemes); err != nil {
+ return supportsRSAFallback(err)
+ }
+ }
+
+ // In TLS 1.3 we are done because supported_groups is only relevant to the
+ // ECDHE computation, point format negotiation is removed, cipher suites are
+ // only relevant to the AEAD choice, and static RSA does not exist.
+ if vers == VersionTLS13 {
+ return nil
+ }
+
+ // The only signed key exchange we support is ECDHE.
+ if !supportsECDHE(conf, chi.SupportedCurves, chi.SupportedPoints) {
+ return supportsRSAFallback(errors.New("client doesn't support ECDHE, can only use legacy RSA key exchange"))
+ }
+
+ var ecdsaCipherSuite bool
+ if priv, ok := c.PrivateKey.(crypto.Signer); ok {
+ switch pub := priv.Public().(type) {
+ case *ecdsa.PublicKey:
+ var curve CurveID
+ switch pub.Curve {
+ case elliptic.P256():
+ curve = CurveP256
+ case elliptic.P384():
+ curve = CurveP384
+ case elliptic.P521():
+ curve = CurveP521
+ default:
+ return supportsRSAFallback(unsupportedCertificateError(c))
+ }
+ var curveOk bool
+ for _, c := range chi.SupportedCurves {
+ if c == curve && conf.supportsCurve(c) {
+ curveOk = true
+ break
+ }
+ }
+ if !curveOk {
+ return errors.New("client doesn't support certificate curve")
+ }
+ ecdsaCipherSuite = true
+ case ed25519.PublicKey:
+ if vers < VersionTLS12 || len(chi.SignatureSchemes) == 0 {
+ return errors.New("connection doesn't support Ed25519")
+ }
+ ecdsaCipherSuite = true
+ case *rsa.PublicKey:
+ default:
+ return supportsRSAFallback(unsupportedCertificateError(c))
+ }
+ } else {
+ return supportsRSAFallback(unsupportedCertificateError(c))
+ }
+
+ // Make sure that there is a mutually supported cipher suite that works with
+ // this certificate. Cipher suite selection will then apply the logic in
+ // reverse to pick it. See also serverHandshakeState.cipherSuiteOk.
+ cipherSuite := selectCipherSuite(chi.CipherSuites, conf.cipherSuites(), func(c *cipherSuite) bool {
+ if c.flags&suiteECDHE == 0 {
+ return false
+ }
+ if c.flags&suiteECSign != 0 {
+ if !ecdsaCipherSuite {
+ return false
+ }
+ } else {
+ if ecdsaCipherSuite {
+ return false
+ }
+ }
+ if vers < VersionTLS12 && c.flags&suiteTLS12 != 0 {
+ return false
+ }
+ return true
+ })
+ if cipherSuite == nil {
+ return supportsRSAFallback(errors.New("client doesn't support any cipher suites compatible with the certificate"))
+ }
+
+ return nil
+}
+
+// BuildNameToCertificate parses c.Certificates and builds c.NameToCertificate
+// from the CommonName and SubjectAlternateName fields of each of the leaf
+// certificates.
+//
+// Deprecated: NameToCertificate only allows associating a single certificate
+// with a given name. Leave that field nil to let the library select the first
+// compatible chain from Certificates.
+func (c *config) BuildNameToCertificate() {
+ c.NameToCertificate = make(map[string]*Certificate)
+ for i := range c.Certificates {
+ cert := &c.Certificates[i]
+ x509Cert, err := leafCertificate(cert)
+ if err != nil {
+ continue
+ }
+ // If SANs are *not* present, some clients will consider the certificate
+ // valid for the name in the Common Name.
+ if x509Cert.Subject.CommonName != "" && len(x509Cert.DNSNames) == 0 {
+ c.NameToCertificate[x509Cert.Subject.CommonName] = cert
+ }
+ for _, san := range x509Cert.DNSNames {
+ c.NameToCertificate[san] = cert
+ }
+ }
+}
+
+const (
+ keyLogLabelTLS12 = "CLIENT_RANDOM"
+ keyLogLabelEarlyTraffic = "CLIENT_EARLY_TRAFFIC_SECRET"
+ keyLogLabelClientHandshake = "CLIENT_HANDSHAKE_TRAFFIC_SECRET"
+ keyLogLabelServerHandshake = "SERVER_HANDSHAKE_TRAFFIC_SECRET"
+ keyLogLabelClientTraffic = "CLIENT_TRAFFIC_SECRET_0"
+ keyLogLabelServerTraffic = "SERVER_TRAFFIC_SECRET_0"
+)
+
+func (c *config) writeKeyLog(label string, clientRandom, secret []byte) error {
+ if c.KeyLogWriter == nil {
+ return nil
+ }
+
+ logLine := fmt.Appendf(nil, "%s %x %x\n", label, clientRandom, secret)
+
+ writerMutex.Lock()
+ _, err := c.KeyLogWriter.Write(logLine)
+ writerMutex.Unlock()
+
+ return err
+}
+
+// writerMutex protects all KeyLogWriters globally. It is rarely enabled,
+// and is only for debugging, so a global mutex saves space.
+var writerMutex sync.Mutex
+
+// A Certificate is a chain of one or more certificates, leaf first.
+type Certificate = tls.Certificate
+
+// leaf returns the parsed leaf certificate, either from c.Leaf or by parsing
+// the corresponding c.Certificate[0].
+func leafCertificate(c *Certificate) (*x509.Certificate, error) {
+ if c.Leaf != nil {
+ return c.Leaf, nil
+ }
+ return x509.ParseCertificate(c.Certificate[0])
+}
+
+type handshakeMessage interface {
+ marshal() []byte
+ unmarshal([]byte) bool
+}
+
+// lruSessionCache is a ClientSessionCache implementation that uses an LRU
+// caching strategy.
+type lruSessionCache struct {
+ sync.Mutex
+
+ m map[string]*list.Element
+ q *list.List
+ capacity int
+}
+
+type lruSessionCacheEntry struct {
+ sessionKey string
+ state *ClientSessionState
+}
+
+// NewLRUClientSessionCache returns a ClientSessionCache with the given
+// capacity that uses an LRU strategy. If capacity is < 1, a default capacity
+// is used instead.
+func NewLRUClientSessionCache(capacity int) ClientSessionCache {
+ const defaultSessionCacheCapacity = 64
+
+ if capacity < 1 {
+ capacity = defaultSessionCacheCapacity
+ }
+ return &lruSessionCache{
+ m: make(map[string]*list.Element),
+ q: list.New(),
+ capacity: capacity,
+ }
+}
+
+// Put adds the provided (sessionKey, cs) pair to the cache. If cs is nil, the entry
+// corresponding to sessionKey is removed from the cache instead.
+func (c *lruSessionCache) Put(sessionKey string, cs *ClientSessionState) {
+ c.Lock()
+ defer c.Unlock()
+
+ if elem, ok := c.m[sessionKey]; ok {
+ if cs == nil {
+ c.q.Remove(elem)
+ delete(c.m, sessionKey)
+ } else {
+ entry := elem.Value.(*lruSessionCacheEntry)
+ entry.state = cs
+ c.q.MoveToFront(elem)
+ }
+ return
+ }
+
+ if c.q.Len() < c.capacity {
+ entry := &lruSessionCacheEntry{sessionKey, cs}
+ c.m[sessionKey] = c.q.PushFront(entry)
+ return
+ }
+
+ elem := c.q.Back()
+ entry := elem.Value.(*lruSessionCacheEntry)
+ delete(c.m, entry.sessionKey)
+ entry.sessionKey = sessionKey
+ entry.state = cs
+ c.q.MoveToFront(elem)
+ c.m[sessionKey] = elem
+}
+
+// Get returns the ClientSessionState value associated with a given key. It
+// returns (nil, false) if no value is found.
+func (c *lruSessionCache) Get(sessionKey string) (*ClientSessionState, bool) {
+ c.Lock()
+ defer c.Unlock()
+
+ if elem, ok := c.m[sessionKey]; ok {
+ c.q.MoveToFront(elem)
+ return elem.Value.(*lruSessionCacheEntry).state, true
+ }
+ return nil, false
+}
+
+var emptyConfig Config
+
+func defaultConfig() *Config {
+ return &emptyConfig
+}
+
+func unexpectedMessageError(wanted, got any) error {
+ return fmt.Errorf("tls: received unexpected handshake message of type %T when waiting for %T", got, wanted)
+}
+
+func isSupportedSignatureAlgorithm(sigAlg SignatureScheme, supportedSignatureAlgorithms []SignatureScheme) bool {
+ for _, s := range supportedSignatureAlgorithms {
+ if s == sigAlg {
+ return true
+ }
+ }
+ return false
+}
+
+// CertificateVerificationError is returned when certificate verification fails during the handshake.
+type CertificateVerificationError struct {
+ // UnverifiedCertificates and its contents should not be modified.
+ UnverifiedCertificates []*x509.Certificate
+ Err error
+}
+
+func (e *CertificateVerificationError) Error() string {
+ return fmt.Sprintf("tls: failed to verify certificate: %s", e.Err)
+}
+
+func (e *CertificateVerificationError) Unwrap() error {
+ return e.Err
+}
diff --git a/vendor/github.com/quic-go/qtls-go1-20/conn.go b/vendor/github.com/quic-go/qtls-go1-20/conn.go
new file mode 100644
index 0000000000..d84fa3442b
--- /dev/null
+++ b/vendor/github.com/quic-go/qtls-go1-20/conn.go
@@ -0,0 +1,1616 @@
+// Copyright 2010 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// TLS low level connection and record layer
+
+package qtls
+
+import (
+ "bytes"
+ "context"
+ "crypto/cipher"
+ "crypto/subtle"
+ "crypto/x509"
+ "errors"
+ "fmt"
+ "hash"
+ "io"
+ "net"
+ "sync"
+ "sync/atomic"
+ "time"
+)
+
+// A Conn represents a secured connection.
+// It implements the net.Conn interface.
+type Conn struct {
+ // constant
+ conn net.Conn
+ isClient bool
+ handshakeFn func(context.Context) error // (*Conn).clientHandshake or serverHandshake
+
+ // isHandshakeComplete is true if the connection is currently transferring
+ // application data (i.e. is not currently processing a handshake).
+ // isHandshakeComplete is true implies handshakeErr == nil.
+ isHandshakeComplete atomic.Bool
+ // constant after handshake; protected by handshakeMutex
+ handshakeMutex sync.Mutex
+ handshakeErr error // error resulting from handshake
+ vers uint16 // TLS version
+ haveVers bool // version has been negotiated
+ config *config // configuration passed to constructor
+ // handshakes counts the number of handshakes performed on the
+ // connection so far. If renegotiation is disabled then this is either
+ // zero or one.
+ extraConfig *ExtraConfig
+
+ handshakes int
+ didResume bool // whether this connection was a session resumption
+ cipherSuite uint16
+ ocspResponse []byte // stapled OCSP response
+ scts [][]byte // signed certificate timestamps from server
+ peerCertificates []*x509.Certificate
+ // activeCertHandles contains the cache handles to certificates in
+ // peerCertificates that are used to track active references.
+ activeCertHandles []*activeCert
+ // verifiedChains contains the certificate chains that we built, as
+ // opposed to the ones presented by the server.
+ verifiedChains [][]*x509.Certificate
+ // serverName contains the server name indicated by the client, if any.
+ serverName string
+ // secureRenegotiation is true if the server echoed the secure
+ // renegotiation extension. (This is meaningless as a server because
+ // renegotiation is not supported in that case.)
+ secureRenegotiation bool
+ // ekm is a closure for exporting keying material.
+ ekm func(label string, context []byte, length int) ([]byte, error)
+ // For the client:
+ // resumptionSecret is the resumption_master_secret for handling
+ // NewSessionTicket messages. nil if config.SessionTicketsDisabled.
+ // For the server:
+ // resumptionSecret is the resumption_master_secret for generating
+ // NewSessionTicket messages. Only used when the alternative record
+ // layer is set. nil if config.SessionTicketsDisabled.
+ resumptionSecret []byte
+
+ // ticketKeys is the set of active session ticket keys for this
+ // connection. The first one is used to encrypt new tickets and
+ // all are tried to decrypt tickets.
+ ticketKeys []ticketKey
+
+ // clientFinishedIsFirst is true if the client sent the first Finished
+ // message during the most recent handshake. This is recorded because
+ // the first transmitted Finished message is the tls-unique
+ // channel-binding value.
+ clientFinishedIsFirst bool
+
+ // closeNotifyErr is any error from sending the alertCloseNotify record.
+ closeNotifyErr error
+ // closeNotifySent is true if the Conn attempted to send an
+ // alertCloseNotify record.
+ closeNotifySent bool
+
+ // clientFinished and serverFinished contain the Finished message sent
+ // by the client or server in the most recent handshake. This is
+ // retained to support the renegotiation extension and tls-unique
+ // channel-binding.
+ clientFinished [12]byte
+ serverFinished [12]byte
+
+ // clientProtocol is the negotiated ALPN protocol.
+ clientProtocol string
+
+ // input/output
+ in, out halfConn
+ rawInput bytes.Buffer // raw input, starting with a record header
+ input bytes.Reader // application data waiting to be read, from rawInput.Next
+ hand bytes.Buffer // handshake data waiting to be read
+ buffering bool // whether records are buffered in sendBuf
+ sendBuf []byte // a buffer of records waiting to be sent
+
+ // bytesSent counts the bytes of application data sent.
+ // packetsSent counts packets.
+ bytesSent int64
+ packetsSent int64
+
+ // retryCount counts the number of consecutive non-advancing records
+ // received by Conn.readRecord. That is, records that neither advance the
+ // handshake, nor deliver application data. Protected by in.Mutex.
+ retryCount int
+
+ // activeCall indicates whether Close has been call in the low bit.
+ // the rest of the bits are the number of goroutines in Conn.Write.
+ activeCall atomic.Int32
+
+ used0RTT bool
+
+ tmp [16]byte
+
+ connStateMutex sync.Mutex
+ connState ConnectionStateWith0RTT
+}
+
+// Access to net.Conn methods.
+// Cannot just embed net.Conn because that would
+// export the struct field too.
+
+// LocalAddr returns the local network address.
+func (c *Conn) LocalAddr() net.Addr {
+ return c.conn.LocalAddr()
+}
+
+// RemoteAddr returns the remote network address.
+func (c *Conn) RemoteAddr() net.Addr {
+ return c.conn.RemoteAddr()
+}
+
+// SetDeadline sets the read and write deadlines associated with the connection.
+// A zero value for t means Read and Write will not time out.
+// After a Write has timed out, the TLS state is corrupt and all future writes will return the same error.
+func (c *Conn) SetDeadline(t time.Time) error {
+ return c.conn.SetDeadline(t)
+}
+
+// SetReadDeadline sets the read deadline on the underlying connection.
+// A zero value for t means Read will not time out.
+func (c *Conn) SetReadDeadline(t time.Time) error {
+ return c.conn.SetReadDeadline(t)
+}
+
+// SetWriteDeadline sets the write deadline on the underlying connection.
+// A zero value for t means Write will not time out.
+// After a Write has timed out, the TLS state is corrupt and all future writes will return the same error.
+func (c *Conn) SetWriteDeadline(t time.Time) error {
+ return c.conn.SetWriteDeadline(t)
+}
+
+// NetConn returns the underlying connection that is wrapped by c.
+// Note that writing to or reading from this connection directly will corrupt the
+// TLS session.
+func (c *Conn) NetConn() net.Conn {
+ return c.conn
+}
+
+// A halfConn represents one direction of the record layer
+// connection, either sending or receiving.
+type halfConn struct {
+ sync.Mutex
+
+ err error // first permanent error
+ version uint16 // protocol version
+ cipher any // cipher algorithm
+ mac hash.Hash
+ seq [8]byte // 64-bit sequence number
+
+ scratchBuf [13]byte // to avoid allocs; interface method args escape
+
+ nextCipher any // next encryption state
+ nextMac hash.Hash // next MAC algorithm
+
+ trafficSecret []byte // current TLS 1.3 traffic secret
+
+ setKeyCallback func(encLevel EncryptionLevel, suite *CipherSuiteTLS13, trafficSecret []byte)
+}
+
+type permanentError struct {
+ err net.Error
+}
+
+func (e *permanentError) Error() string { return e.err.Error() }
+func (e *permanentError) Unwrap() error { return e.err }
+func (e *permanentError) Timeout() bool { return e.err.Timeout() }
+func (e *permanentError) Temporary() bool { return false }
+
+func (hc *halfConn) setErrorLocked(err error) error {
+ if e, ok := err.(net.Error); ok {
+ hc.err = &permanentError{err: e}
+ } else {
+ hc.err = err
+ }
+ return hc.err
+}
+
+// prepareCipherSpec sets the encryption and MAC states
+// that a subsequent changeCipherSpec will use.
+func (hc *halfConn) prepareCipherSpec(version uint16, cipher any, mac hash.Hash) {
+ hc.version = version
+ hc.nextCipher = cipher
+ hc.nextMac = mac
+}
+
+// changeCipherSpec changes the encryption and MAC states
+// to the ones previously passed to prepareCipherSpec.
+func (hc *halfConn) changeCipherSpec() error {
+ if hc.nextCipher == nil || hc.version == VersionTLS13 {
+ return alertInternalError
+ }
+ hc.cipher = hc.nextCipher
+ hc.mac = hc.nextMac
+ hc.nextCipher = nil
+ hc.nextMac = nil
+ for i := range hc.seq {
+ hc.seq[i] = 0
+ }
+ return nil
+}
+
+func (hc *halfConn) exportKey(encLevel EncryptionLevel, suite *cipherSuiteTLS13, trafficSecret []byte) {
+ if hc.setKeyCallback != nil {
+ s := &CipherSuiteTLS13{
+ ID: suite.id,
+ KeyLen: suite.keyLen,
+ Hash: suite.hash,
+ AEAD: func(key, fixedNonce []byte) cipher.AEAD { return suite.aead(key, fixedNonce) },
+ }
+ hc.setKeyCallback(encLevel, s, trafficSecret)
+ }
+}
+
+func (hc *halfConn) setTrafficSecret(suite *cipherSuiteTLS13, secret []byte) {
+ hc.trafficSecret = secret
+ key, iv := suite.trafficKey(secret)
+ hc.cipher = suite.aead(key, iv)
+ for i := range hc.seq {
+ hc.seq[i] = 0
+ }
+}
+
+// incSeq increments the sequence number.
+func (hc *halfConn) incSeq() {
+ for i := 7; i >= 0; i-- {
+ hc.seq[i]++
+ if hc.seq[i] != 0 {
+ return
+ }
+ }
+
+ // Not allowed to let sequence number wrap.
+ // Instead, must renegotiate before it does.
+ // Not likely enough to bother.
+ panic("TLS: sequence number wraparound")
+}
+
+// explicitNonceLen returns the number of bytes of explicit nonce or IV included
+// in each record. Explicit nonces are present only in CBC modes after TLS 1.0
+// and in certain AEAD modes in TLS 1.2.
+func (hc *halfConn) explicitNonceLen() int {
+ if hc.cipher == nil {
+ return 0
+ }
+
+ switch c := hc.cipher.(type) {
+ case cipher.Stream:
+ return 0
+ case aead:
+ return c.explicitNonceLen()
+ case cbcMode:
+ // TLS 1.1 introduced a per-record explicit IV to fix the BEAST attack.
+ if hc.version >= VersionTLS11 {
+ return c.BlockSize()
+ }
+ return 0
+ default:
+ panic("unknown cipher type")
+ }
+}
+
+// extractPadding 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.
+func extractPadding(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)
+
+ // Zero the padding length on error. This ensures any unchecked bytes
+ // are included in the MAC. Otherwise, an attacker that could
+ // distinguish MAC failures from padding failures could mount an attack
+ // similar to POODLE in SSL 3.0: given a good ciphertext that uses a
+ // full block's worth of padding, replace the final block with another
+ // block. If the MAC check passed but the padding check failed, the
+ // last byte of that block decrypted to the block size.
+ //
+ // See also macAndPaddingGood logic below.
+ paddingLen &= good
+
+ toRemove = int(paddingLen) + 1
+ return
+}
+
+func roundUp(a, b int) int {
+ return a + (b-a%b)%b
+}
+
+// cbcMode is an interface for block ciphers using cipher block chaining.
+type cbcMode interface {
+ cipher.BlockMode
+ SetIV([]byte)
+}
+
+// decrypt authenticates and decrypts the record if protection is active at
+// this stage. The returned plaintext might overlap with the input.
+func (hc *halfConn) decrypt(record []byte) ([]byte, recordType, error) {
+ var plaintext []byte
+ typ := recordType(record[0])
+ payload := record[recordHeaderLen:]
+
+ // In TLS 1.3, change_cipher_spec messages are to be ignored without being
+ // decrypted. See RFC 8446, Appendix D.4.
+ if hc.version == VersionTLS13 && typ == recordTypeChangeCipherSpec {
+ return payload, typ, nil
+ }
+
+ paddingGood := byte(255)
+ paddingLen := 0
+
+ explicitNonceLen := hc.explicitNonceLen()
+
+ if hc.cipher != nil {
+ switch c := hc.cipher.(type) {
+ case cipher.Stream:
+ c.XORKeyStream(payload, payload)
+ case aead:
+ if len(payload) < explicitNonceLen {
+ return nil, 0, alertBadRecordMAC
+ }
+ nonce := payload[:explicitNonceLen]
+ if len(nonce) == 0 {
+ nonce = hc.seq[:]
+ }
+ payload = payload[explicitNonceLen:]
+
+ var additionalData []byte
+ if hc.version == VersionTLS13 {
+ additionalData = record[:recordHeaderLen]
+ } else {
+ additionalData = append(hc.scratchBuf[:0], hc.seq[:]...)
+ additionalData = append(additionalData, record[:3]...)
+ n := len(payload) - c.Overhead()
+ additionalData = append(additionalData, byte(n>>8), byte(n))
+ }
+
+ var err error
+ plaintext, err = c.Open(payload[:0], nonce, payload, additionalData)
+ if err != nil {
+ return nil, 0, alertBadRecordMAC
+ }
+ case cbcMode:
+ blockSize := c.BlockSize()
+ minPayload := explicitNonceLen + roundUp(hc.mac.Size()+1, blockSize)
+ if len(payload)%blockSize != 0 || len(payload) < minPayload {
+ return nil, 0, alertBadRecordMAC
+ }
+
+ if explicitNonceLen > 0 {
+ c.SetIV(payload[:explicitNonceLen])
+ payload = payload[explicitNonceLen:]
+ }
+ c.CryptBlocks(payload, payload)
+
+ // In a limited attempt to protect against CBC padding oracles like
+ // Lucky13, the data past paddingLen (which is secret) is passed to
+ // the MAC function as extra data, to be fed into the HMAC after
+ // computing the digest. This makes the MAC roughly constant time as
+ // long as the digest computation is constant time and does not
+ // affect the subsequent write, modulo cache effects.
+ paddingLen, paddingGood = extractPadding(payload)
+ default:
+ panic("unknown cipher type")
+ }
+
+ if hc.version == VersionTLS13 {
+ if typ != recordTypeApplicationData {
+ return nil, 0, alertUnexpectedMessage
+ }
+ if len(plaintext) > maxPlaintext+1 {
+ return nil, 0, alertRecordOverflow
+ }
+ // Remove padding and find the ContentType scanning from the end.
+ for i := len(plaintext) - 1; i >= 0; i-- {
+ if plaintext[i] != 0 {
+ typ = recordType(plaintext[i])
+ plaintext = plaintext[:i]
+ break
+ }
+ if i == 0 {
+ return nil, 0, alertUnexpectedMessage
+ }
+ }
+ }
+ } else {
+ plaintext = payload
+ }
+
+ if hc.mac != nil {
+ macSize := hc.mac.Size()
+ if len(payload) < macSize {
+ return nil, 0, alertBadRecordMAC
+ }
+
+ n := len(payload) - macSize - paddingLen
+ n = subtle.ConstantTimeSelect(int(uint32(n)>>31), 0, n) // if n < 0 { n = 0 }
+ record[3] = byte(n >> 8)
+ record[4] = byte(n)
+ remoteMAC := payload[n : n+macSize]
+ localMAC := tls10MAC(hc.mac, hc.scratchBuf[:0], hc.seq[:], record[:recordHeaderLen], payload[:n], payload[n+macSize:])
+
+ // This is equivalent to checking the MACs and paddingGood
+ // separately, but in constant-time to prevent distinguishing
+ // padding failures from MAC failures. Depending on what value
+ // of paddingLen was returned on bad padding, distinguishing
+ // bad MAC from bad padding can lead to an attack.
+ //
+ // See also the logic at the end of extractPadding.
+ macAndPaddingGood := subtle.ConstantTimeCompare(localMAC, remoteMAC) & int(paddingGood)
+ if macAndPaddingGood != 1 {
+ return nil, 0, alertBadRecordMAC
+ }
+
+ plaintext = payload[:n]
+ }
+
+ hc.incSeq()
+ return plaintext, typ, nil
+}
+
+func (c *Conn) setAlternativeRecordLayer() {
+ if c.extraConfig != nil && c.extraConfig.AlternativeRecordLayer != nil {
+ c.in.setKeyCallback = c.extraConfig.AlternativeRecordLayer.SetReadKey
+ c.out.setKeyCallback = c.extraConfig.AlternativeRecordLayer.SetWriteKey
+ }
+}
+
+// sliceForAppend extends the input slice by n bytes. head is the full extended
+// slice, while tail is the appended part. If the original slice has sufficient
+// capacity no allocation is performed.
+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
+}
+
+// encrypt encrypts payload, adding the appropriate nonce and/or MAC, and
+// appends it to record, which must already contain the record header.
+func (hc *halfConn) encrypt(record, payload []byte, rand io.Reader) ([]byte, error) {
+ if hc.cipher == nil {
+ return append(record, payload...), nil
+ }
+
+ var explicitNonce []byte
+ if explicitNonceLen := hc.explicitNonceLen(); explicitNonceLen > 0 {
+ record, explicitNonce = sliceForAppend(record, explicitNonceLen)
+ if _, isCBC := hc.cipher.(cbcMode); !isCBC && explicitNonceLen < 16 {
+ // The AES-GCM construction in TLS has an explicit nonce so that the
+ // nonce can be random. However, the nonce is only 8 bytes which is
+ // too small for a secure, random nonce. Therefore we use the
+ // sequence number as the nonce. The 3DES-CBC construction also has
+ // an 8 bytes nonce but its nonces must be unpredictable (see RFC
+ // 5246, Appendix F.3), forcing us to use randomness. That's not
+ // 3DES' biggest problem anyway because the birthday bound on block
+ // collision is reached first due to its similarly small block size
+ // (see the Sweet32 attack).
+ copy(explicitNonce, hc.seq[:])
+ } else {
+ if _, err := io.ReadFull(rand, explicitNonce); err != nil {
+ return nil, err
+ }
+ }
+ }
+
+ var dst []byte
+ switch c := hc.cipher.(type) {
+ case cipher.Stream:
+ mac := tls10MAC(hc.mac, hc.scratchBuf[:0], hc.seq[:], record[:recordHeaderLen], payload, nil)
+ record, dst = sliceForAppend(record, len(payload)+len(mac))
+ c.XORKeyStream(dst[:len(payload)], payload)
+ c.XORKeyStream(dst[len(payload):], mac)
+ case aead:
+ nonce := explicitNonce
+ if len(nonce) == 0 {
+ nonce = hc.seq[:]
+ }
+
+ if hc.version == VersionTLS13 {
+ record = append(record, payload...)
+
+ // Encrypt the actual ContentType and replace the plaintext one.
+ record = append(record, record[0])
+ record[0] = byte(recordTypeApplicationData)
+
+ n := len(payload) + 1 + c.Overhead()
+ record[3] = byte(n >> 8)
+ record[4] = byte(n)
+
+ record = c.Seal(record[:recordHeaderLen],
+ nonce, record[recordHeaderLen:], record[:recordHeaderLen])
+ } else {
+ additionalData := append(hc.scratchBuf[:0], hc.seq[:]...)
+ additionalData = append(additionalData, record[:recordHeaderLen]...)
+ record = c.Seal(record, nonce, payload, additionalData)
+ }
+ case cbcMode:
+ mac := tls10MAC(hc.mac, hc.scratchBuf[:0], hc.seq[:], record[:recordHeaderLen], payload, nil)
+ blockSize := c.BlockSize()
+ plaintextLen := len(payload) + len(mac)
+ paddingLen := blockSize - plaintextLen%blockSize
+ record, dst = sliceForAppend(record, plaintextLen+paddingLen)
+ copy(dst, payload)
+ copy(dst[len(payload):], mac)
+ for i := plaintextLen; i < len(dst); i++ {
+ dst[i] = byte(paddingLen - 1)
+ }
+ if len(explicitNonce) > 0 {
+ c.SetIV(explicitNonce)
+ }
+ c.CryptBlocks(dst, dst)
+ default:
+ panic("unknown cipher type")
+ }
+
+ // Update length to include nonce, MAC and any block padding needed.
+ n := len(record) - recordHeaderLen
+ record[3] = byte(n >> 8)
+ record[4] = byte(n)
+ hc.incSeq()
+
+ return record, nil
+}
+
+// RecordHeaderError is returned when a TLS record header is invalid.
+type RecordHeaderError struct {
+ // Msg contains a human readable string that describes the error.
+ Msg string
+ // RecordHeader contains the five bytes of TLS record header that
+ // triggered the error.
+ RecordHeader [5]byte
+ // Conn provides the underlying net.Conn in the case that a client
+ // sent an initial handshake that didn't look like TLS.
+ // It is nil if there's already been a handshake or a TLS alert has
+ // been written to the connection.
+ Conn net.Conn
+}
+
+func (e RecordHeaderError) Error() string { return "tls: " + e.Msg }
+
+func (c *Conn) newRecordHeaderError(conn net.Conn, msg string) (err RecordHeaderError) {
+ err.Msg = msg
+ err.Conn = conn
+ copy(err.RecordHeader[:], c.rawInput.Bytes())
+ return err
+}
+
+func (c *Conn) readRecord() error {
+ return c.readRecordOrCCS(false)
+}
+
+func (c *Conn) readChangeCipherSpec() error {
+ return c.readRecordOrCCS(true)
+}
+
+// readRecordOrCCS reads one or more TLS records from the connection and
+// updates the record layer state. Some invariants:
+// - c.in must be locked
+// - c.input must be empty
+//
+// During the handshake one and only one of the following will happen:
+// - c.hand grows
+// - c.in.changeCipherSpec is called
+// - an error is returned
+//
+// After the handshake one and only one of the following will happen:
+// - c.hand grows
+// - c.input is set
+// - an error is returned
+func (c *Conn) readRecordOrCCS(expectChangeCipherSpec bool) error {
+ if c.in.err != nil {
+ return c.in.err
+ }
+ handshakeComplete := c.isHandshakeComplete.Load()
+
+ // This function modifies c.rawInput, which owns the c.input memory.
+ if c.input.Len() != 0 {
+ return c.in.setErrorLocked(errors.New("tls: internal error: attempted to read record with pending application data"))
+ }
+ c.input.Reset(nil)
+
+ // Read header, payload.
+ if err := c.readFromUntil(c.conn, recordHeaderLen); err != nil {
+ // RFC 8446, Section 6.1 suggests that EOF without an alertCloseNotify
+ // is an error, but popular web sites seem to do this, so we accept it
+ // if and only if at the record boundary.
+ if err == io.ErrUnexpectedEOF && c.rawInput.Len() == 0 {
+ err = io.EOF
+ }
+ if e, ok := err.(net.Error); !ok || !e.Temporary() {
+ c.in.setErrorLocked(err)
+ }
+ return err
+ }
+ hdr := c.rawInput.Bytes()[:recordHeaderLen]
+ typ := recordType(hdr[0])
+
+ // No valid TLS record has a type of 0x80, however SSLv2 handshakes
+ // start with a uint16 length where the MSB is set and the first record
+ // is always < 256 bytes long. Therefore typ == 0x80 strongly suggests
+ // an SSLv2 client.
+ if !handshakeComplete && typ == 0x80 {
+ c.sendAlert(alertProtocolVersion)
+ return c.in.setErrorLocked(c.newRecordHeaderError(nil, "unsupported SSLv2 handshake received"))
+ }
+
+ vers := uint16(hdr[1])<<8 | uint16(hdr[2])
+ n := int(hdr[3])<<8 | int(hdr[4])
+ if c.haveVers && c.vers != VersionTLS13 && vers != c.vers {
+ c.sendAlert(alertProtocolVersion)
+ msg := fmt.Sprintf("received record with version %x when expecting version %x", vers, c.vers)
+ return c.in.setErrorLocked(c.newRecordHeaderError(nil, msg))
+ }
+ if !c.haveVers {
+ // First message, be extra suspicious: this might not be a TLS
+ // client. Bail out before reading a full 'body', if possible.
+ // The current max version is 3.3 so if the version is >= 16.0,
+ // it's probably not real.
+ if (typ != recordTypeAlert && typ != recordTypeHandshake) || vers >= 0x1000 {
+ return c.in.setErrorLocked(c.newRecordHeaderError(c.conn, "first record does not look like a TLS handshake"))
+ }
+ }
+ if c.vers == VersionTLS13 && n > maxCiphertextTLS13 || n > maxCiphertext {
+ c.sendAlert(alertRecordOverflow)
+ msg := fmt.Sprintf("oversized record received with length %d", n)
+ return c.in.setErrorLocked(c.newRecordHeaderError(nil, msg))
+ }
+ if err := c.readFromUntil(c.conn, recordHeaderLen+n); err != nil {
+ if e, ok := err.(net.Error); !ok || !e.Temporary() {
+ c.in.setErrorLocked(err)
+ }
+ return err
+ }
+
+ // Process message.
+ record := c.rawInput.Next(recordHeaderLen + n)
+ data, typ, err := c.in.decrypt(record)
+ if err != nil {
+ return c.in.setErrorLocked(c.sendAlert(err.(alert)))
+ }
+ if len(data) > maxPlaintext {
+ return c.in.setErrorLocked(c.sendAlert(alertRecordOverflow))
+ }
+
+ // Application Data messages are always protected.
+ if c.in.cipher == nil && typ == recordTypeApplicationData {
+ return c.in.setErrorLocked(c.sendAlert(alertUnexpectedMessage))
+ }
+
+ if typ != recordTypeAlert && typ != recordTypeChangeCipherSpec && len(data) > 0 {
+ // This is a state-advancing message: reset the retry count.
+ c.retryCount = 0
+ }
+
+ // Handshake messages MUST NOT be interleaved with other record types in TLS 1.3.
+ if c.vers == VersionTLS13 && typ != recordTypeHandshake && c.hand.Len() > 0 {
+ return c.in.setErrorLocked(c.sendAlert(alertUnexpectedMessage))
+ }
+
+ switch typ {
+ default:
+ return c.in.setErrorLocked(c.sendAlert(alertUnexpectedMessage))
+
+ case recordTypeAlert:
+ if len(data) != 2 {
+ return c.in.setErrorLocked(c.sendAlert(alertUnexpectedMessage))
+ }
+ if alert(data[1]) == alertCloseNotify {
+ return c.in.setErrorLocked(io.EOF)
+ }
+ if c.vers == VersionTLS13 {
+ return c.in.setErrorLocked(&net.OpError{Op: "remote error", Err: alert(data[1])})
+ }
+ switch data[0] {
+ case alertLevelWarning:
+ // Drop the record on the floor and retry.
+ return c.retryReadRecord(expectChangeCipherSpec)
+ case alertLevelError:
+ return c.in.setErrorLocked(&net.OpError{Op: "remote error", Err: alert(data[1])})
+ default:
+ return c.in.setErrorLocked(c.sendAlert(alertUnexpectedMessage))
+ }
+
+ case recordTypeChangeCipherSpec:
+ if len(data) != 1 || data[0] != 1 {
+ return c.in.setErrorLocked(c.sendAlert(alertDecodeError))
+ }
+ // Handshake messages are not allowed to fragment across the CCS.
+ if c.hand.Len() > 0 {
+ return c.in.setErrorLocked(c.sendAlert(alertUnexpectedMessage))
+ }
+ // In TLS 1.3, change_cipher_spec records are ignored until the
+ // Finished. See RFC 8446, Appendix D.4. Note that according to Section
+ // 5, a server can send a ChangeCipherSpec before its ServerHello, when
+ // c.vers is still unset. That's not useful though and suspicious if the
+ // server then selects a lower protocol version, so don't allow that.
+ if c.vers == VersionTLS13 {
+ return c.retryReadRecord(expectChangeCipherSpec)
+ }
+ if !expectChangeCipherSpec {
+ return c.in.setErrorLocked(c.sendAlert(alertUnexpectedMessage))
+ }
+ if err := c.in.changeCipherSpec(); err != nil {
+ return c.in.setErrorLocked(c.sendAlert(err.(alert)))
+ }
+
+ case recordTypeApplicationData:
+ if !handshakeComplete || expectChangeCipherSpec {
+ return c.in.setErrorLocked(c.sendAlert(alertUnexpectedMessage))
+ }
+ // Some OpenSSL servers send empty records in order to randomize the
+ // CBC IV. Ignore a limited number of empty records.
+ if len(data) == 0 {
+ return c.retryReadRecord(expectChangeCipherSpec)
+ }
+ // Note that data is owned by c.rawInput, following the Next call above,
+ // to avoid copying the plaintext. This is safe because c.rawInput is
+ // not read from or written to until c.input is drained.
+ c.input.Reset(data)
+
+ case recordTypeHandshake:
+ if len(data) == 0 || expectChangeCipherSpec {
+ return c.in.setErrorLocked(c.sendAlert(alertUnexpectedMessage))
+ }
+ c.hand.Write(data)
+ }
+
+ return nil
+}
+
+// retryReadRecord recurs into readRecordOrCCS to drop a non-advancing record, like
+// a warning alert, empty application_data, or a change_cipher_spec in TLS 1.3.
+func (c *Conn) retryReadRecord(expectChangeCipherSpec bool) error {
+ c.retryCount++
+ if c.retryCount > maxUselessRecords {
+ c.sendAlert(alertUnexpectedMessage)
+ return c.in.setErrorLocked(errors.New("tls: too many ignored records"))
+ }
+ return c.readRecordOrCCS(expectChangeCipherSpec)
+}
+
+// atLeastReader reads from R, stopping with EOF once at least N bytes have been
+// read. It is different from an io.LimitedReader in that it doesn't cut short
+// the last Read call, and in that it considers an early EOF an error.
+type atLeastReader struct {
+ R io.Reader
+ N int64
+}
+
+func (r *atLeastReader) Read(p []byte) (int, error) {
+ if r.N <= 0 {
+ return 0, io.EOF
+ }
+ n, err := r.R.Read(p)
+ r.N -= int64(n) // won't underflow unless len(p) >= n > 9223372036854775809
+ if r.N > 0 && err == io.EOF {
+ return n, io.ErrUnexpectedEOF
+ }
+ if r.N <= 0 && err == nil {
+ return n, io.EOF
+ }
+ return n, err
+}
+
+// readFromUntil reads from r into c.rawInput until c.rawInput contains
+// at least n bytes or else returns an error.
+func (c *Conn) readFromUntil(r io.Reader, n int) error {
+ if c.rawInput.Len() >= n {
+ return nil
+ }
+ needs := n - c.rawInput.Len()
+ // There might be extra input waiting on the wire. Make a best effort
+ // attempt to fetch it so that it can be used in (*Conn).Read to
+ // "predict" closeNotify alerts.
+ c.rawInput.Grow(needs + bytes.MinRead)
+ _, err := c.rawInput.ReadFrom(&atLeastReader{r, int64(needs)})
+ return err
+}
+
+// sendAlert sends a TLS alert message.
+func (c *Conn) sendAlertLocked(err alert) error {
+ switch err {
+ case alertNoRenegotiation, alertCloseNotify:
+ c.tmp[0] = alertLevelWarning
+ default:
+ c.tmp[0] = alertLevelError
+ }
+ c.tmp[1] = byte(err)
+
+ _, writeErr := c.writeRecordLocked(recordTypeAlert, c.tmp[0:2])
+ if err == alertCloseNotify {
+ // closeNotify is a special case in that it isn't an error.
+ return writeErr
+ }
+
+ return c.out.setErrorLocked(&net.OpError{Op: "local error", Err: err})
+}
+
+// sendAlert sends a TLS alert message.
+func (c *Conn) sendAlert(err alert) error {
+ if c.extraConfig != nil && c.extraConfig.AlternativeRecordLayer != nil {
+ c.extraConfig.AlternativeRecordLayer.SendAlert(uint8(err))
+ return &net.OpError{Op: "local error", Err: err}
+ }
+
+ c.out.Lock()
+ defer c.out.Unlock()
+ return c.sendAlertLocked(err)
+}
+
+const (
+ // tcpMSSEstimate is a conservative estimate of the TCP maximum segment
+ // size (MSS). A constant is used, rather than querying the kernel for
+ // the actual MSS, to avoid complexity. The value here is the IPv6
+ // minimum MTU (1280 bytes) minus the overhead of an IPv6 header (40
+ // bytes) and a TCP header with timestamps (32 bytes).
+ tcpMSSEstimate = 1208
+
+ // recordSizeBoostThreshold is the number of bytes of application data
+ // sent after which the TLS record size will be increased to the
+ // maximum.
+ recordSizeBoostThreshold = 128 * 1024
+)
+
+// maxPayloadSizeForWrite returns the maximum TLS payload size to use for the
+// next application data record. There is the following trade-off:
+//
+// - For latency-sensitive applications, such as web browsing, each TLS
+// record should fit in one TCP segment.
+// - For throughput-sensitive applications, such as large file transfers,
+// larger TLS records better amortize framing and encryption overheads.
+//
+// A simple heuristic that works well in practice is to use small records for
+// the first 1MB of data, then use larger records for subsequent data, and
+// reset back to smaller records after the connection becomes idle. See "High
+// Performance Web Networking", Chapter 4, or:
+// https://www.igvita.com/2013/10/24/optimizing-tls-record-size-and-buffering-latency/
+//
+// In the interests of simplicity and determinism, this code does not attempt
+// to reset the record size once the connection is idle, however.
+func (c *Conn) maxPayloadSizeForWrite(typ recordType) int {
+ if c.config.DynamicRecordSizingDisabled || typ != recordTypeApplicationData {
+ return maxPlaintext
+ }
+
+ if c.bytesSent >= recordSizeBoostThreshold {
+ return maxPlaintext
+ }
+
+ // Subtract TLS overheads to get the maximum payload size.
+ payloadBytes := tcpMSSEstimate - recordHeaderLen - c.out.explicitNonceLen()
+ if c.out.cipher != nil {
+ switch ciph := c.out.cipher.(type) {
+ case cipher.Stream:
+ payloadBytes -= c.out.mac.Size()
+ case cipher.AEAD:
+ payloadBytes -= ciph.Overhead()
+ case cbcMode:
+ blockSize := ciph.BlockSize()
+ // The payload must fit in a multiple of blockSize, with
+ // room for at least one padding byte.
+ payloadBytes = (payloadBytes & ^(blockSize - 1)) - 1
+ // The MAC is appended before padding so affects the
+ // payload size directly.
+ payloadBytes -= c.out.mac.Size()
+ default:
+ panic("unknown cipher type")
+ }
+ }
+ if c.vers == VersionTLS13 {
+ payloadBytes-- // encrypted ContentType
+ }
+
+ // Allow packet growth in arithmetic progression up to max.
+ pkt := c.packetsSent
+ c.packetsSent++
+ if pkt > 1000 {
+ return maxPlaintext // avoid overflow in multiply below
+ }
+
+ n := payloadBytes * int(pkt+1)
+ if n > maxPlaintext {
+ n = maxPlaintext
+ }
+ return n
+}
+
+func (c *Conn) write(data []byte) (int, error) {
+ if c.buffering {
+ c.sendBuf = append(c.sendBuf, data...)
+ return len(data), nil
+ }
+
+ n, err := c.conn.Write(data)
+ c.bytesSent += int64(n)
+ return n, err
+}
+
+func (c *Conn) flush() (int, error) {
+ if len(c.sendBuf) == 0 {
+ return 0, nil
+ }
+
+ n, err := c.conn.Write(c.sendBuf)
+ c.bytesSent += int64(n)
+ c.sendBuf = nil
+ c.buffering = false
+ return n, err
+}
+
+// outBufPool pools the record-sized scratch buffers used by writeRecordLocked.
+var outBufPool = sync.Pool{
+ New: func() any {
+ return new([]byte)
+ },
+}
+
+// writeRecordLocked writes a TLS record with the given type and payload to the
+// connection and updates the record layer state.
+func (c *Conn) writeRecordLocked(typ recordType, data []byte) (int, error) {
+ outBufPtr := outBufPool.Get().(*[]byte)
+ outBuf := *outBufPtr
+ defer func() {
+ // You might be tempted to simplify this by just passing &outBuf to Put,
+ // but that would make the local copy of the outBuf slice header escape
+ // to the heap, causing an allocation. Instead, we keep around the
+ // pointer to the slice header returned by Get, which is already on the
+ // heap, and overwrite and return that.
+ *outBufPtr = outBuf
+ outBufPool.Put(outBufPtr)
+ }()
+
+ var n int
+ for len(data) > 0 {
+ m := len(data)
+ if maxPayload := c.maxPayloadSizeForWrite(typ); m > maxPayload {
+ m = maxPayload
+ }
+
+ _, outBuf = sliceForAppend(outBuf[:0], recordHeaderLen)
+ outBuf[0] = byte(typ)
+ vers := c.vers
+ if vers == 0 {
+ // Some TLS servers fail if the record version is
+ // greater than TLS 1.0 for the initial ClientHello.
+ vers = VersionTLS10
+ } else if vers == VersionTLS13 {
+ // TLS 1.3 froze the record layer version to 1.2.
+ // See RFC 8446, Section 5.1.
+ vers = VersionTLS12
+ }
+ outBuf[1] = byte(vers >> 8)
+ outBuf[2] = byte(vers)
+ outBuf[3] = byte(m >> 8)
+ outBuf[4] = byte(m)
+
+ var err error
+ outBuf, err = c.out.encrypt(outBuf, data[:m], c.config.rand())
+ if err != nil {
+ return n, err
+ }
+ if _, err := c.write(outBuf); err != nil {
+ return n, err
+ }
+ n += m
+ data = data[m:]
+ }
+
+ if typ == recordTypeChangeCipherSpec && c.vers != VersionTLS13 {
+ if err := c.out.changeCipherSpec(); err != nil {
+ return n, c.sendAlertLocked(err.(alert))
+ }
+ }
+
+ return n, nil
+}
+
+// writeRecord writes a TLS record with the given type and payload to the
+// connection and updates the record layer state.
+func (c *Conn) writeRecord(typ recordType, data []byte) (int, error) {
+ if c.extraConfig != nil && c.extraConfig.AlternativeRecordLayer != nil {
+ if typ == recordTypeChangeCipherSpec {
+ return len(data), nil
+ }
+ return c.extraConfig.AlternativeRecordLayer.WriteRecord(data)
+ }
+
+ c.out.Lock()
+ defer c.out.Unlock()
+
+ return c.writeRecordLocked(typ, data)
+}
+
+// readHandshake reads the next handshake message from
+// the record layer.
+func (c *Conn) readHandshake() (any, error) {
+ var data []byte
+ if c.extraConfig != nil && c.extraConfig.AlternativeRecordLayer != nil {
+ var err error
+ data, err = c.extraConfig.AlternativeRecordLayer.ReadHandshakeMessage()
+ if err != nil {
+ return nil, err
+ }
+ } else {
+ for c.hand.Len() < 4 {
+ if err := c.readRecord(); err != nil {
+ return nil, err
+ }
+ }
+
+ data = c.hand.Bytes()
+ n := int(data[1])<<16 | int(data[2])<<8 | int(data[3])
+ if n > maxHandshake {
+ c.sendAlertLocked(alertInternalError)
+ return nil, c.in.setErrorLocked(fmt.Errorf("tls: handshake message of length %d bytes exceeds maximum of %d bytes", n, maxHandshake))
+ }
+ for c.hand.Len() < 4+n {
+ if err := c.readRecord(); err != nil {
+ return nil, err
+ }
+ }
+ data = c.hand.Next(4 + n)
+ }
+ var m handshakeMessage
+ switch data[0] {
+ case typeHelloRequest:
+ m = new(helloRequestMsg)
+ case typeClientHello:
+ m = new(clientHelloMsg)
+ case typeServerHello:
+ m = new(serverHelloMsg)
+ case typeNewSessionTicket:
+ if c.vers == VersionTLS13 {
+ m = new(newSessionTicketMsgTLS13)
+ } else {
+ m = new(newSessionTicketMsg)
+ }
+ case typeCertificate:
+ if c.vers == VersionTLS13 {
+ m = new(certificateMsgTLS13)
+ } else {
+ m = new(certificateMsg)
+ }
+ case typeCertificateRequest:
+ if c.vers == VersionTLS13 {
+ m = new(certificateRequestMsgTLS13)
+ } else {
+ m = &certificateRequestMsg{
+ hasSignatureAlgorithm: c.vers >= VersionTLS12,
+ }
+ }
+ case typeCertificateStatus:
+ m = new(certificateStatusMsg)
+ case typeServerKeyExchange:
+ m = new(serverKeyExchangeMsg)
+ case typeServerHelloDone:
+ m = new(serverHelloDoneMsg)
+ case typeClientKeyExchange:
+ m = new(clientKeyExchangeMsg)
+ case typeCertificateVerify:
+ m = &certificateVerifyMsg{
+ hasSignatureAlgorithm: c.vers >= VersionTLS12,
+ }
+ case typeFinished:
+ m = new(finishedMsg)
+ case typeEncryptedExtensions:
+ m = new(encryptedExtensionsMsg)
+ case typeEndOfEarlyData:
+ m = new(endOfEarlyDataMsg)
+ case typeKeyUpdate:
+ m = new(keyUpdateMsg)
+ default:
+ return nil, c.in.setErrorLocked(c.sendAlert(alertUnexpectedMessage))
+ }
+
+ // The handshake message unmarshalers
+ // expect to be able to keep references to data,
+ // so pass in a fresh copy that won't be overwritten.
+ data = append([]byte(nil), data...)
+
+ if !m.unmarshal(data) {
+ return nil, c.in.setErrorLocked(c.sendAlert(alertUnexpectedMessage))
+ }
+ return m, nil
+}
+
+var (
+ errShutdown = errors.New("tls: protocol is shutdown")
+)
+
+// Write writes data to the connection.
+//
+// As Write calls Handshake, in order to prevent indefinite blocking a deadline
+// must be set for both Read and Write before Write is called when the handshake
+// has not yet completed. See SetDeadline, SetReadDeadline, and
+// SetWriteDeadline.
+func (c *Conn) Write(b []byte) (int, error) {
+ // interlock with Close below
+ for {
+ x := c.activeCall.Load()
+ if x&1 != 0 {
+ return 0, net.ErrClosed
+ }
+ if c.activeCall.CompareAndSwap(x, x+2) {
+ break
+ }
+ }
+ defer c.activeCall.Add(-2)
+
+ if err := c.Handshake(); err != nil {
+ return 0, err
+ }
+
+ c.out.Lock()
+ defer c.out.Unlock()
+
+ if err := c.out.err; err != nil {
+ return 0, err
+ }
+
+ if !c.isHandshakeComplete.Load() {
+ return 0, alertInternalError
+ }
+
+ if c.closeNotifySent {
+ return 0, errShutdown
+ }
+
+ // TLS 1.0 is susceptible to a chosen-plaintext
+ // attack when using block mode ciphers due to predictable IVs.
+ // This can be prevented by splitting each Application Data
+ // record into two records, effectively randomizing the IV.
+ //
+ // https://www.openssl.org/~bodo/tls-cbc.txt
+ // https://bugzilla.mozilla.org/show_bug.cgi?id=665814
+ // https://www.imperialviolet.org/2012/01/15/beastfollowup.html
+
+ var m int
+ if len(b) > 1 && c.vers == VersionTLS10 {
+ if _, ok := c.out.cipher.(cipher.BlockMode); ok {
+ n, err := c.writeRecordLocked(recordTypeApplicationData, b[:1])
+ if err != nil {
+ return n, c.out.setErrorLocked(err)
+ }
+ m, b = 1, b[1:]
+ }
+ }
+
+ n, err := c.writeRecordLocked(recordTypeApplicationData, b)
+ return n + m, c.out.setErrorLocked(err)
+}
+
+// handleRenegotiation processes a HelloRequest handshake message.
+func (c *Conn) handleRenegotiation() error {
+ if c.vers == VersionTLS13 {
+ return errors.New("tls: internal error: unexpected renegotiation")
+ }
+
+ msg, err := c.readHandshake()
+ if err != nil {
+ return err
+ }
+
+ helloReq, ok := msg.(*helloRequestMsg)
+ if !ok {
+ c.sendAlert(alertUnexpectedMessage)
+ return unexpectedMessageError(helloReq, msg)
+ }
+
+ if !c.isClient {
+ return c.sendAlert(alertNoRenegotiation)
+ }
+
+ switch c.config.Renegotiation {
+ case RenegotiateNever:
+ return c.sendAlert(alertNoRenegotiation)
+ case RenegotiateOnceAsClient:
+ if c.handshakes > 1 {
+ return c.sendAlert(alertNoRenegotiation)
+ }
+ case RenegotiateFreelyAsClient:
+ // Ok.
+ default:
+ c.sendAlert(alertInternalError)
+ return errors.New("tls: unknown Renegotiation value")
+ }
+
+ c.handshakeMutex.Lock()
+ defer c.handshakeMutex.Unlock()
+
+ c.isHandshakeComplete.Store(false)
+ if c.handshakeErr = c.clientHandshake(context.Background()); c.handshakeErr == nil {
+ c.handshakes++
+ }
+ return c.handshakeErr
+}
+
+func (c *Conn) HandlePostHandshakeMessage() error {
+ return c.handlePostHandshakeMessage()
+}
+
+// handlePostHandshakeMessage processes a handshake message arrived after the
+// handshake is complete. Up to TLS 1.2, it indicates the start of a renegotiation.
+func (c *Conn) handlePostHandshakeMessage() error {
+ if c.vers != VersionTLS13 {
+ return c.handleRenegotiation()
+ }
+
+ msg, err := c.readHandshake()
+ if err != nil {
+ return err
+ }
+
+ c.retryCount++
+ if c.retryCount > maxUselessRecords {
+ c.sendAlert(alertUnexpectedMessage)
+ return c.in.setErrorLocked(errors.New("tls: too many non-advancing records"))
+ }
+
+ switch msg := msg.(type) {
+ case *newSessionTicketMsgTLS13:
+ return c.handleNewSessionTicket(msg)
+ case *keyUpdateMsg:
+ return c.handleKeyUpdate(msg)
+ default:
+ c.sendAlert(alertUnexpectedMessage)
+ return fmt.Errorf("tls: received unexpected handshake message of type %T", msg)
+ }
+}
+
+func (c *Conn) handleKeyUpdate(keyUpdate *keyUpdateMsg) error {
+ cipherSuite := cipherSuiteTLS13ByID(c.cipherSuite)
+ if cipherSuite == nil {
+ return c.in.setErrorLocked(c.sendAlert(alertInternalError))
+ }
+
+ newSecret := cipherSuite.nextTrafficSecret(c.in.trafficSecret)
+ c.in.setTrafficSecret(cipherSuite, newSecret)
+
+ if keyUpdate.updateRequested {
+ c.out.Lock()
+ defer c.out.Unlock()
+
+ msg := &keyUpdateMsg{}
+ _, err := c.writeRecordLocked(recordTypeHandshake, msg.marshal())
+ if err != nil {
+ // Surface the error at the next write.
+ c.out.setErrorLocked(err)
+ return nil
+ }
+
+ newSecret := cipherSuite.nextTrafficSecret(c.out.trafficSecret)
+ c.out.setTrafficSecret(cipherSuite, newSecret)
+ }
+
+ return nil
+}
+
+// Read reads data from the connection.
+//
+// As Read calls Handshake, in order to prevent indefinite blocking a deadline
+// must be set for both Read and Write before Read is called when the handshake
+// has not yet completed. See SetDeadline, SetReadDeadline, and
+// SetWriteDeadline.
+func (c *Conn) Read(b []byte) (int, error) {
+ if err := c.Handshake(); err != nil {
+ return 0, err
+ }
+ if len(b) == 0 {
+ // Put this after Handshake, in case people were calling
+ // Read(nil) for the side effect of the Handshake.
+ return 0, nil
+ }
+
+ c.in.Lock()
+ defer c.in.Unlock()
+
+ for c.input.Len() == 0 {
+ if err := c.readRecord(); err != nil {
+ return 0, err
+ }
+ for c.hand.Len() > 0 {
+ if err := c.handlePostHandshakeMessage(); err != nil {
+ return 0, err
+ }
+ }
+ }
+
+ n, _ := c.input.Read(b)
+
+ // If a close-notify alert is waiting, read it so that we can return (n,
+ // EOF) instead of (n, nil), to signal to the HTTP response reading
+ // goroutine that the connection is now closed. This eliminates a race
+ // where the HTTP response reading goroutine would otherwise not observe
+ // the EOF until its next read, by which time a client goroutine might
+ // have already tried to reuse the HTTP connection for a new request.
+ // See https://golang.org/cl/76400046 and https://golang.org/issue/3514
+ if n != 0 && c.input.Len() == 0 && c.rawInput.Len() > 0 &&
+ recordType(c.rawInput.Bytes()[0]) == recordTypeAlert {
+ if err := c.readRecord(); err != nil {
+ return n, err // will be io.EOF on closeNotify
+ }
+ }
+
+ return n, nil
+}
+
+// Close closes the connection.
+func (c *Conn) Close() error {
+ // Interlock with Conn.Write above.
+ var x int32
+ for {
+ x = c.activeCall.Load()
+ if x&1 != 0 {
+ return net.ErrClosed
+ }
+ if c.activeCall.CompareAndSwap(x, x|1) {
+ break
+ }
+ }
+ if x != 0 {
+ // io.Writer and io.Closer should not be used concurrently.
+ // If Close is called while a Write is currently in-flight,
+ // interpret that as a sign that this Close is really just
+ // being used to break the Write and/or clean up resources and
+ // avoid sending the alertCloseNotify, which may block
+ // waiting on handshakeMutex or the c.out mutex.
+ return c.conn.Close()
+ }
+
+ var alertErr error
+ if c.isHandshakeComplete.Load() {
+ if err := c.closeNotify(); err != nil {
+ alertErr = fmt.Errorf("tls: failed to send closeNotify alert (but connection was closed anyway): %w", err)
+ }
+ }
+
+ if err := c.conn.Close(); err != nil {
+ return err
+ }
+ return alertErr
+}
+
+var errEarlyCloseWrite = errors.New("tls: CloseWrite called before handshake complete")
+
+// CloseWrite shuts down the writing side of the connection. It should only be
+// called once the handshake has completed and does not call CloseWrite on the
+// underlying connection. Most callers should just use Close.
+func (c *Conn) CloseWrite() error {
+ if !c.isHandshakeComplete.Load() {
+ return errEarlyCloseWrite
+ }
+
+ return c.closeNotify()
+}
+
+func (c *Conn) closeNotify() error {
+ c.out.Lock()
+ defer c.out.Unlock()
+
+ if !c.closeNotifySent {
+ // Set a Write Deadline to prevent possibly blocking forever.
+ c.SetWriteDeadline(time.Now().Add(time.Second * 5))
+ c.closeNotifyErr = c.sendAlertLocked(alertCloseNotify)
+ c.closeNotifySent = true
+ // Any subsequent writes will fail.
+ c.SetWriteDeadline(time.Now())
+ }
+ return c.closeNotifyErr
+}
+
+// Handshake runs the client or server handshake
+// protocol if it has not yet been run.
+//
+// Most uses of this package need not call Handshake explicitly: the
+// first Read or Write will call it automatically.
+//
+// For control over canceling or setting a timeout on a handshake, use
+// HandshakeContext or the Dialer's DialContext method instead.
+func (c *Conn) Handshake() error {
+ return c.HandshakeContext(context.Background())
+}
+
+// HandshakeContext runs the client or server handshake
+// protocol if it has not yet been run.
+//
+// The provided Context must be non-nil. If the context is canceled before
+// the handshake is complete, the handshake is interrupted and an error is returned.
+// Once the handshake has completed, cancellation of the context will not affect the
+// connection.
+//
+// Most uses of this package need not call HandshakeContext explicitly: the
+// first Read or Write will call it automatically.
+func (c *Conn) HandshakeContext(ctx context.Context) error {
+ // Delegate to unexported method for named return
+ // without confusing documented signature.
+ return c.handshakeContext(ctx)
+}
+
+func (c *Conn) handshakeContext(ctx context.Context) (ret error) {
+ // Fast sync/atomic-based exit if there is no handshake in flight and the
+ // last one succeeded without an error. Avoids the expensive context setup
+ // and mutex for most Read and Write calls.
+ if c.isHandshakeComplete.Load() {
+ return nil
+ }
+
+ handshakeCtx, cancel := context.WithCancel(ctx)
+ // Note: defer this before starting the "interrupter" goroutine
+ // so that we can tell the difference between the input being canceled and
+ // this cancellation. In the former case, we need to close the connection.
+ defer cancel()
+
+ // Start the "interrupter" goroutine, if this context might be canceled.
+ // (The background context cannot).
+ //
+ // The interrupter goroutine waits for the input context to be done and
+ // closes the connection if this happens before the function returns.
+ if ctx.Done() != nil {
+ done := make(chan struct{})
+ interruptRes := make(chan error, 1)
+ defer func() {
+ close(done)
+ if ctxErr := <-interruptRes; ctxErr != nil {
+ // Return context error to user.
+ ret = ctxErr
+ }
+ }()
+ go func() {
+ select {
+ case <-handshakeCtx.Done():
+ // Close the connection, discarding the error
+ _ = c.conn.Close()
+ interruptRes <- handshakeCtx.Err()
+ case <-done:
+ interruptRes <- nil
+ }
+ }()
+ }
+
+ c.handshakeMutex.Lock()
+ defer c.handshakeMutex.Unlock()
+
+ if err := c.handshakeErr; err != nil {
+ return err
+ }
+ if c.isHandshakeComplete.Load() {
+ return nil
+ }
+
+ c.in.Lock()
+ defer c.in.Unlock()
+
+ c.handshakeErr = c.handshakeFn(handshakeCtx)
+ if c.handshakeErr == nil {
+ c.handshakes++
+ } else {
+ // If an error occurred during the handshake try to flush the
+ // alert that might be left in the buffer.
+ c.flush()
+ }
+
+ if c.handshakeErr == nil && !c.isHandshakeComplete.Load() {
+ c.handshakeErr = errors.New("tls: internal error: handshake should have had a result")
+ }
+ if c.handshakeErr != nil && c.isHandshakeComplete.Load() {
+ panic("tls: internal error: handshake returned an error but is marked successful")
+ }
+
+ return c.handshakeErr
+}
+
+// ConnectionState returns basic TLS details about the connection.
+func (c *Conn) ConnectionState() ConnectionState {
+ c.connStateMutex.Lock()
+ defer c.connStateMutex.Unlock()
+ return c.connState.ConnectionState
+}
+
+// ConnectionStateWith0RTT returns basic TLS details (incl. 0-RTT status) about the connection.
+func (c *Conn) ConnectionStateWith0RTT() ConnectionStateWith0RTT {
+ c.connStateMutex.Lock()
+ defer c.connStateMutex.Unlock()
+ return c.connState
+}
+
+func (c *Conn) connectionStateLocked() ConnectionState {
+ var state connectionState
+ state.HandshakeComplete = c.isHandshakeComplete.Load()
+ state.Version = c.vers
+ state.NegotiatedProtocol = c.clientProtocol
+ state.DidResume = c.didResume
+ state.NegotiatedProtocolIsMutual = true
+ state.ServerName = c.serverName
+ state.CipherSuite = c.cipherSuite
+ state.PeerCertificates = c.peerCertificates
+ state.VerifiedChains = c.verifiedChains
+ state.SignedCertificateTimestamps = c.scts
+ state.OCSPResponse = c.ocspResponse
+ if !c.didResume && c.vers != VersionTLS13 {
+ if c.clientFinishedIsFirst {
+ state.TLSUnique = c.clientFinished[:]
+ } else {
+ state.TLSUnique = c.serverFinished[:]
+ }
+ }
+ if c.config.Renegotiation != RenegotiateNever {
+ state.ekm = noExportedKeyingMaterial
+ } else {
+ state.ekm = c.ekm
+ }
+ return toConnectionState(state)
+}
+
+func (c *Conn) updateConnectionState() {
+ c.connStateMutex.Lock()
+ defer c.connStateMutex.Unlock()
+ c.connState = ConnectionStateWith0RTT{
+ Used0RTT: c.used0RTT,
+ ConnectionState: c.connectionStateLocked(),
+ }
+}
+
+// OCSPResponse returns the stapled OCSP response from the TLS server, if
+// any. (Only valid for client connections.)
+func (c *Conn) OCSPResponse() []byte {
+ c.handshakeMutex.Lock()
+ defer c.handshakeMutex.Unlock()
+
+ return c.ocspResponse
+}
+
+// VerifyHostname checks that the peer certificate chain is valid for
+// connecting to host. If so, it returns nil; if not, it returns an error
+// describing the problem.
+func (c *Conn) VerifyHostname(host string) error {
+ c.handshakeMutex.Lock()
+ defer c.handshakeMutex.Unlock()
+ if !c.isClient {
+ return errors.New("tls: VerifyHostname called on TLS server connection")
+ }
+ if !c.isHandshakeComplete.Load() {
+ return errors.New("tls: handshake has not yet been performed")
+ }
+ if len(c.verifiedChains) == 0 {
+ return errors.New("tls: handshake did not verify certificate chain")
+ }
+ return c.peerCertificates[0].VerifyHostname(host)
+}
diff --git a/vendor/github.com/quic-go/qtls-go1-20/cpu.go b/vendor/github.com/quic-go/qtls-go1-20/cpu.go
new file mode 100644
index 0000000000..1219450879
--- /dev/null
+++ b/vendor/github.com/quic-go/qtls-go1-20/cpu.go
@@ -0,0 +1,22 @@
+//go:build !js
+// +build !js
+
+package qtls
+
+import (
+ "runtime"
+
+ "golang.org/x/sys/cpu"
+)
+
+var (
+ hasGCMAsmAMD64 = cpu.X86.HasAES && cpu.X86.HasPCLMULQDQ
+ hasGCMAsmARM64 = cpu.ARM64.HasAES && cpu.ARM64.HasPMULL
+ // Keep in sync with crypto/aes/cipher_s390x.go.
+ hasGCMAsmS390X = cpu.S390X.HasAES && cpu.S390X.HasAESCBC && cpu.S390X.HasAESCTR &&
+ (cpu.S390X.HasGHASH || cpu.S390X.HasAESGCM)
+
+ hasAESGCMHardwareSupport = runtime.GOARCH == "amd64" && hasGCMAsmAMD64 ||
+ runtime.GOARCH == "arm64" && hasGCMAsmARM64 ||
+ runtime.GOARCH == "s390x" && hasGCMAsmS390X
+)
diff --git a/vendor/github.com/quic-go/qtls-go1-20/cpu_other.go b/vendor/github.com/quic-go/qtls-go1-20/cpu_other.go
new file mode 100644
index 0000000000..33f7d21942
--- /dev/null
+++ b/vendor/github.com/quic-go/qtls-go1-20/cpu_other.go
@@ -0,0 +1,12 @@
+//go:build js
+// +build js
+
+package qtls
+
+var (
+ hasGCMAsmAMD64 = false
+ hasGCMAsmARM64 = false
+ hasGCMAsmS390X = false
+
+ hasAESGCMHardwareSupport = false
+)
diff --git a/vendor/github.com/quic-go/qtls-go1-20/handshake_client.go b/vendor/github.com/quic-go/qtls-go1-20/handshake_client.go
new file mode 100644
index 0000000000..67603455a8
--- /dev/null
+++ b/vendor/github.com/quic-go/qtls-go1-20/handshake_client.go
@@ -0,0 +1,1121 @@
+// Copyright 2009 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package qtls
+
+import (
+ "bytes"
+ "context"
+ "crypto"
+ "crypto/ecdh"
+ "crypto/ecdsa"
+ "crypto/ed25519"
+ "crypto/rsa"
+ "crypto/subtle"
+ "crypto/x509"
+ "errors"
+ "fmt"
+ "hash"
+ "io"
+ "net"
+ "strings"
+ "time"
+
+ "golang.org/x/crypto/cryptobyte"
+)
+
+const clientSessionStateVersion = 1
+
+type clientHandshakeState struct {
+ c *Conn
+ ctx context.Context
+ serverHello *serverHelloMsg
+ hello *clientHelloMsg
+ suite *cipherSuite
+ finishedHash finishedHash
+ masterSecret []byte
+ session *clientSessionState
+}
+
+var testingOnlyForceClientHelloSignatureAlgorithms []SignatureScheme
+
+func (c *Conn) makeClientHello() (*clientHelloMsg, *ecdh.PrivateKey, error) {
+ config := c.config
+ if len(config.ServerName) == 0 && !config.InsecureSkipVerify {
+ return nil, nil, errors.New("tls: either ServerName or InsecureSkipVerify must be specified in the tls.Config")
+ }
+
+ nextProtosLength := 0
+ for _, proto := range config.NextProtos {
+ if l := len(proto); l == 0 || l > 255 {
+ return nil, nil, errors.New("tls: invalid NextProtos value")
+ } else {
+ nextProtosLength += 1 + l
+ }
+ }
+ if nextProtosLength > 0xffff {
+ return nil, nil, errors.New("tls: NextProtos values too large")
+ }
+
+ var supportedVersions []uint16
+ var clientHelloVersion uint16
+ if c.extraConfig.usesAlternativeRecordLayer() {
+ if config.maxSupportedVersion(roleClient) < VersionTLS13 {
+ return nil, nil, errors.New("tls: MaxVersion prevents QUIC from using TLS 1.3")
+ }
+ // Only offer TLS 1.3 when QUIC is used.
+ supportedVersions = []uint16{VersionTLS13}
+ clientHelloVersion = VersionTLS13
+ } else {
+ supportedVersions = config.supportedVersions(roleClient)
+ if len(supportedVersions) == 0 {
+ return nil, nil, errors.New("tls: no supported versions satisfy MinVersion and MaxVersion")
+ }
+ clientHelloVersion = config.maxSupportedVersion(roleClient)
+ }
+
+ // The version at the beginning of the ClientHello was capped at TLS 1.2
+ // for compatibility reasons. The supported_versions extension is used
+ // to negotiate versions now. See RFC 8446, Section 4.2.1.
+ if clientHelloVersion > VersionTLS12 {
+ clientHelloVersion = VersionTLS12
+ }
+
+ hello := &clientHelloMsg{
+ vers: clientHelloVersion,
+ compressionMethods: []uint8{compressionNone},
+ random: make([]byte, 32),
+ ocspStapling: true,
+ scts: true,
+ serverName: hostnameInSNI(config.ServerName),
+ supportedCurves: config.curvePreferences(),
+ supportedPoints: []uint8{pointFormatUncompressed},
+ secureRenegotiationSupported: true,
+ alpnProtocols: config.NextProtos,
+ supportedVersions: supportedVersions,
+ }
+
+ if c.handshakes > 0 {
+ hello.secureRenegotiation = c.clientFinished[:]
+ }
+
+ preferenceOrder := cipherSuitesPreferenceOrder
+ if !hasAESGCMHardwareSupport {
+ preferenceOrder = cipherSuitesPreferenceOrderNoAES
+ }
+ configCipherSuites := config.cipherSuites()
+ hello.cipherSuites = make([]uint16, 0, len(configCipherSuites))
+
+ for _, suiteId := range preferenceOrder {
+ suite := mutualCipherSuite(configCipherSuites, suiteId)
+ if suite == nil {
+ continue
+ }
+ // Don't advertise TLS 1.2-only cipher suites unless
+ // we're attempting TLS 1.2.
+ if hello.vers < VersionTLS12 && suite.flags&suiteTLS12 != 0 {
+ continue
+ }
+ hello.cipherSuites = append(hello.cipherSuites, suiteId)
+ }
+
+ _, err := io.ReadFull(config.rand(), hello.random)
+ if err != nil {
+ return nil, nil, errors.New("tls: short read from Rand: " + err.Error())
+ }
+
+ // A random session ID is used to detect when the server accepted a ticket
+ // and is resuming a session (see RFC 5077). In TLS 1.3, it's always set as
+ // a compatibility measure (see RFC 8446, Section 4.1.2).
+ if c.extraConfig == nil || c.extraConfig.AlternativeRecordLayer == nil {
+ hello.sessionId = make([]byte, 32)
+ if _, err := io.ReadFull(config.rand(), hello.sessionId); err != nil {
+ return nil, nil, errors.New("tls: short read from Rand: " + err.Error())
+ }
+ }
+
+ if hello.vers >= VersionTLS12 {
+ hello.supportedSignatureAlgorithms = supportedSignatureAlgorithms()
+ }
+ if testingOnlyForceClientHelloSignatureAlgorithms != nil {
+ hello.supportedSignatureAlgorithms = testingOnlyForceClientHelloSignatureAlgorithms
+ }
+
+ var key *ecdh.PrivateKey
+ if hello.supportedVersions[0] == VersionTLS13 {
+ var suites []uint16
+ for _, suiteID := range configCipherSuites {
+ for _, suite := range cipherSuitesTLS13 {
+ if suite.id == suiteID {
+ suites = append(suites, suiteID)
+ }
+ }
+ }
+ if len(suites) > 0 {
+ hello.cipherSuites = suites
+ } else {
+ if hasAESGCMHardwareSupport {
+ hello.cipherSuites = append(hello.cipherSuites, defaultCipherSuitesTLS13...)
+ } else {
+ hello.cipherSuites = append(hello.cipherSuites, defaultCipherSuitesTLS13NoAES...)
+ }
+ }
+
+ curveID := config.curvePreferences()[0]
+ if _, ok := curveForCurveID(curveID); !ok {
+ return nil, nil, errors.New("tls: CurvePreferences includes unsupported curve")
+ }
+ key, err = generateECDHEKey(config.rand(), curveID)
+ if err != nil {
+ return nil, nil, err
+ }
+ hello.keyShares = []keyShare{{group: curveID, data: key.PublicKey().Bytes()}}
+ }
+
+ if hello.supportedVersions[0] == VersionTLS13 && c.extraConfig != nil && c.extraConfig.GetExtensions != nil {
+ hello.additionalExtensions = c.extraConfig.GetExtensions(typeClientHello)
+ }
+
+ return hello, key, nil
+}
+
+func (c *Conn) clientHandshake(ctx context.Context) (err error) {
+ if c.config == nil {
+ c.config = fromConfig(defaultConfig())
+ }
+ c.setAlternativeRecordLayer()
+
+ // This may be a renegotiation handshake, in which case some fields
+ // need to be reset.
+ c.didResume = false
+
+ hello, ecdheKey, err := c.makeClientHello()
+ if err != nil {
+ return err
+ }
+ c.serverName = hello.serverName
+
+ cacheKey, session, earlySecret, binderKey := c.loadSession(hello)
+ if cacheKey != "" && session != nil {
+ var deletedTicket bool
+ if session.vers == VersionTLS13 && hello.earlyData && c.extraConfig != nil && c.extraConfig.Enable0RTT {
+ // don't reuse a session ticket that enabled 0-RTT
+ c.config.ClientSessionCache.Put(cacheKey, nil)
+ deletedTicket = true
+
+ if suite := cipherSuiteTLS13ByID(session.cipherSuite); suite != nil {
+ h := suite.hash.New()
+ h.Write(hello.marshal())
+ clientEarlySecret := suite.deriveSecret(earlySecret, "c e traffic", h)
+ c.out.exportKey(Encryption0RTT, suite, clientEarlySecret)
+ if err := c.config.writeKeyLog(keyLogLabelEarlyTraffic, hello.random, clientEarlySecret); err != nil {
+ c.sendAlert(alertInternalError)
+ return err
+ }
+ }
+ }
+ if !deletedTicket {
+ defer func() {
+ // If we got a handshake failure when resuming a session, throw away
+ // the session ticket. See RFC 5077, Section 3.2.
+ //
+ // RFC 8446 makes no mention of dropping tickets on failure, but it
+ // does require servers to abort on invalid binders, so we need to
+ // delete tickets to recover from a corrupted PSK.
+ if err != nil {
+ c.config.ClientSessionCache.Put(cacheKey, nil)
+ }
+ }()
+ }
+ }
+
+ if _, err := c.writeRecord(recordTypeHandshake, hello.marshal()); err != nil {
+ return err
+ }
+
+ msg, err := c.readHandshake()
+ if err != nil {
+ return err
+ }
+
+ serverHello, ok := msg.(*serverHelloMsg)
+ if !ok {
+ c.sendAlert(alertUnexpectedMessage)
+ return unexpectedMessageError(serverHello, msg)
+ }
+
+ if err := c.pickTLSVersion(serverHello); err != nil {
+ return err
+ }
+
+ // If we are negotiating a protocol version that's lower than what we
+ // support, check for the server downgrade canaries.
+ // See RFC 8446, Section 4.1.3.
+ maxVers := c.config.maxSupportedVersion(roleClient)
+ tls12Downgrade := string(serverHello.random[24:]) == downgradeCanaryTLS12
+ tls11Downgrade := string(serverHello.random[24:]) == downgradeCanaryTLS11
+ if maxVers == VersionTLS13 && c.vers <= VersionTLS12 && (tls12Downgrade || tls11Downgrade) ||
+ maxVers == VersionTLS12 && c.vers <= VersionTLS11 && tls11Downgrade {
+ c.sendAlert(alertIllegalParameter)
+ return errors.New("tls: downgrade attempt detected, possibly due to a MitM attack or a broken middlebox")
+ }
+
+ if c.vers == VersionTLS13 {
+ hs := &clientHandshakeStateTLS13{
+ c: c,
+ ctx: ctx,
+ serverHello: serverHello,
+ hello: hello,
+ ecdheKey: ecdheKey,
+ session: session,
+ earlySecret: earlySecret,
+ binderKey: binderKey,
+ }
+
+ // In TLS 1.3, session tickets are delivered after the handshake.
+ return hs.handshake()
+ }
+
+ hs := &clientHandshakeState{
+ c: c,
+ ctx: ctx,
+ serverHello: serverHello,
+ hello: hello,
+ session: session,
+ }
+
+ if err := hs.handshake(); err != nil {
+ return err
+ }
+
+ // If we had a successful handshake and hs.session is different from
+ // the one already cached - cache a new one.
+ if cacheKey != "" && hs.session != nil && session != hs.session {
+ c.config.ClientSessionCache.Put(cacheKey, toClientSessionState(hs.session))
+ }
+
+ c.updateConnectionState()
+ return nil
+}
+
+// extract the app data saved in the session.nonce,
+// and set the session.nonce to the actual nonce value
+func (c *Conn) decodeSessionState(session *clientSessionState) (uint32 /* max early data */, []byte /* app data */, bool /* ok */) {
+ s := cryptobyte.String(session.nonce)
+ var version uint16
+ if !s.ReadUint16(&version) {
+ return 0, nil, false
+ }
+ if version != clientSessionStateVersion {
+ return 0, nil, false
+ }
+ var maxEarlyData uint32
+ if !s.ReadUint32(&maxEarlyData) {
+ return 0, nil, false
+ }
+ var appData []byte
+ if !readUint16LengthPrefixed(&s, &appData) {
+ return 0, nil, false
+ }
+ var nonce []byte
+ if !readUint16LengthPrefixed(&s, &nonce) {
+ return 0, nil, false
+ }
+ session.nonce = nonce
+ return maxEarlyData, appData, true
+}
+
+func (c *Conn) loadSession(hello *clientHelloMsg) (cacheKey string,
+ session *clientSessionState, earlySecret, binderKey []byte) {
+ if c.config.SessionTicketsDisabled || c.config.ClientSessionCache == nil {
+ return "", nil, nil, nil
+ }
+
+ hello.ticketSupported = true
+
+ if hello.supportedVersions[0] == VersionTLS13 {
+ // Require DHE on resumption as it guarantees forward secrecy against
+ // compromise of the session ticket key. See RFC 8446, Section 4.2.9.
+ hello.pskModes = []uint8{pskModeDHE}
+ }
+
+ // Session resumption is not allowed if renegotiating because
+ // renegotiation is primarily used to allow a client to send a client
+ // certificate, which would be skipped if session resumption occurred.
+ if c.handshakes != 0 {
+ return "", nil, nil, nil
+ }
+
+ // Try to resume a previously negotiated TLS session, if available.
+ cacheKey = clientSessionCacheKey(c.conn.RemoteAddr(), c.config)
+ sess, ok := c.config.ClientSessionCache.Get(cacheKey)
+ if !ok || sess == nil {
+ return cacheKey, nil, nil, nil
+ }
+ session = fromClientSessionState(sess)
+
+ var appData []byte
+ var maxEarlyData uint32
+ if session.vers == VersionTLS13 {
+ var ok bool
+ maxEarlyData, appData, ok = c.decodeSessionState(session)
+ if !ok { // delete it, if parsing failed
+ c.config.ClientSessionCache.Put(cacheKey, nil)
+ return cacheKey, nil, nil, nil
+ }
+ }
+
+ // Check that version used for the previous session is still valid.
+ versOk := false
+ for _, v := range hello.supportedVersions {
+ if v == session.vers {
+ versOk = true
+ break
+ }
+ }
+ if !versOk {
+ return cacheKey, nil, nil, nil
+ }
+
+ // Check that the cached server certificate is not expired, and that it's
+ // valid for the ServerName. This should be ensured by the cache key, but
+ // protect the application from a faulty ClientSessionCache implementation.
+ if !c.config.InsecureSkipVerify {
+ if len(session.verifiedChains) == 0 {
+ // The original connection had InsecureSkipVerify, while this doesn't.
+ return cacheKey, nil, nil, nil
+ }
+ serverCert := session.serverCertificates[0]
+ if c.config.time().After(serverCert.NotAfter) {
+ // Expired certificate, delete the entry.
+ c.config.ClientSessionCache.Put(cacheKey, nil)
+ return cacheKey, nil, nil, nil
+ }
+ if err := serverCert.VerifyHostname(c.config.ServerName); err != nil {
+ return cacheKey, nil, nil, nil
+ }
+ }
+
+ if session.vers != VersionTLS13 {
+ // In TLS 1.2 the cipher suite must match the resumed session. Ensure we
+ // are still offering it.
+ if mutualCipherSuite(hello.cipherSuites, session.cipherSuite) == nil {
+ return cacheKey, nil, nil, nil
+ }
+
+ hello.sessionTicket = session.sessionTicket
+ return
+ }
+
+ // Check that the session ticket is not expired.
+ if c.config.time().After(session.useBy) {
+ c.config.ClientSessionCache.Put(cacheKey, nil)
+ return cacheKey, nil, nil, nil
+ }
+
+ // In TLS 1.3 the KDF hash must match the resumed session. Ensure we
+ // offer at least one cipher suite with that hash.
+ cipherSuite := cipherSuiteTLS13ByID(session.cipherSuite)
+ if cipherSuite == nil {
+ return cacheKey, nil, nil, nil
+ }
+ cipherSuiteOk := false
+ for _, offeredID := range hello.cipherSuites {
+ offeredSuite := cipherSuiteTLS13ByID(offeredID)
+ if offeredSuite != nil && offeredSuite.hash == cipherSuite.hash {
+ cipherSuiteOk = true
+ break
+ }
+ }
+ if !cipherSuiteOk {
+ return cacheKey, nil, nil, nil
+ }
+
+ // Set the pre_shared_key extension. See RFC 8446, Section 4.2.11.1.
+ ticketAge := uint32(c.config.time().Sub(session.receivedAt) / time.Millisecond)
+ identity := pskIdentity{
+ label: session.sessionTicket,
+ obfuscatedTicketAge: ticketAge + session.ageAdd,
+ }
+ hello.pskIdentities = []pskIdentity{identity}
+ hello.pskBinders = [][]byte{make([]byte, cipherSuite.hash.Size())}
+
+ // Compute the PSK binders. See RFC 8446, Section 4.2.11.2.
+ psk := cipherSuite.expandLabel(session.masterSecret, "resumption",
+ session.nonce, cipherSuite.hash.Size())
+ earlySecret = cipherSuite.extract(psk, nil)
+ binderKey = cipherSuite.deriveSecret(earlySecret, resumptionBinderLabel, nil)
+ if c.extraConfig != nil {
+ hello.earlyData = c.extraConfig.Enable0RTT && maxEarlyData > 0
+ }
+ transcript := cipherSuite.hash.New()
+ transcript.Write(hello.marshalWithoutBinders())
+ pskBinders := [][]byte{cipherSuite.finishedHash(binderKey, transcript)}
+ hello.updateBinders(pskBinders)
+
+ if session.vers == VersionTLS13 && c.extraConfig != nil && c.extraConfig.SetAppDataFromSessionState != nil {
+ c.extraConfig.SetAppDataFromSessionState(appData)
+ }
+ return
+}
+
+func (c *Conn) pickTLSVersion(serverHello *serverHelloMsg) error {
+ peerVersion := serverHello.vers
+ if serverHello.supportedVersion != 0 {
+ peerVersion = serverHello.supportedVersion
+ }
+
+ vers, ok := c.config.mutualVersion(roleClient, []uint16{peerVersion})
+ if !ok {
+ c.sendAlert(alertProtocolVersion)
+ return fmt.Errorf("tls: server selected unsupported protocol version %x", peerVersion)
+ }
+
+ c.vers = vers
+ c.haveVers = true
+ c.in.version = vers
+ c.out.version = vers
+
+ return nil
+}
+
+// Does the handshake, either a full one or resumes old session. Requires hs.c,
+// hs.hello, hs.serverHello, and, optionally, hs.session to be set.
+func (hs *clientHandshakeState) handshake() error {
+ c := hs.c
+
+ isResume, err := hs.processServerHello()
+ if err != nil {
+ return err
+ }
+
+ hs.finishedHash = newFinishedHash(c.vers, hs.suite)
+
+ // No signatures of the handshake are needed in a resumption.
+ // Otherwise, in a full handshake, if we don't have any certificates
+ // configured then we will never send a CertificateVerify message and
+ // thus no signatures are needed in that case either.
+ if isResume || (len(c.config.Certificates) == 0 && c.config.GetClientCertificate == nil) {
+ hs.finishedHash.discardHandshakeBuffer()
+ }
+
+ hs.finishedHash.Write(hs.hello.marshal())
+ hs.finishedHash.Write(hs.serverHello.marshal())
+
+ c.buffering = true
+ c.didResume = isResume
+ if isResume {
+ if err := hs.establishKeys(); err != nil {
+ return err
+ }
+ if err := hs.readSessionTicket(); err != nil {
+ return err
+ }
+ if err := hs.readFinished(c.serverFinished[:]); err != nil {
+ return err
+ }
+ c.clientFinishedIsFirst = false
+ // Make sure the connection is still being verified whether or not this
+ // is a resumption. Resumptions currently don't reverify certificates so
+ // they don't call verifyServerCertificate. See Issue 31641.
+ if c.config.VerifyConnection != nil {
+ if err := c.config.VerifyConnection(c.connectionStateLocked()); err != nil {
+ c.sendAlert(alertBadCertificate)
+ return err
+ }
+ }
+ if err := hs.sendFinished(c.clientFinished[:]); err != nil {
+ return err
+ }
+ if _, err := c.flush(); err != nil {
+ return err
+ }
+ } else {
+ if err := hs.doFullHandshake(); err != nil {
+ return err
+ }
+ if err := hs.establishKeys(); err != nil {
+ return err
+ }
+ if err := hs.sendFinished(c.clientFinished[:]); err != nil {
+ return err
+ }
+ if _, err := c.flush(); err != nil {
+ return err
+ }
+ c.clientFinishedIsFirst = true
+ if err := hs.readSessionTicket(); err != nil {
+ return err
+ }
+ if err := hs.readFinished(c.serverFinished[:]); err != nil {
+ return err
+ }
+ }
+
+ c.ekm = ekmFromMasterSecret(c.vers, hs.suite, hs.masterSecret, hs.hello.random, hs.serverHello.random)
+ c.isHandshakeComplete.Store(true)
+
+ return nil
+}
+
+func (hs *clientHandshakeState) pickCipherSuite() error {
+ if hs.suite = mutualCipherSuite(hs.hello.cipherSuites, hs.serverHello.cipherSuite); hs.suite == nil {
+ hs.c.sendAlert(alertHandshakeFailure)
+ return errors.New("tls: server chose an unconfigured cipher suite")
+ }
+
+ hs.c.cipherSuite = hs.suite.id
+ return nil
+}
+
+func (hs *clientHandshakeState) doFullHandshake() error {
+ c := hs.c
+
+ msg, err := c.readHandshake()
+ if err != nil {
+ return err
+ }
+ certMsg, ok := msg.(*certificateMsg)
+ if !ok || len(certMsg.certificates) == 0 {
+ c.sendAlert(alertUnexpectedMessage)
+ return unexpectedMessageError(certMsg, msg)
+ }
+ hs.finishedHash.Write(certMsg.marshal())
+
+ msg, err = c.readHandshake()
+ if err != nil {
+ return err
+ }
+
+ cs, ok := msg.(*certificateStatusMsg)
+ if ok {
+ // RFC4366 on Certificate Status Request:
+ // The server MAY return a "certificate_status" message.
+
+ if !hs.serverHello.ocspStapling {
+ // If a server returns a "CertificateStatus" message, then the
+ // server MUST have included an extension of type "status_request"
+ // with empty "extension_data" in the extended server hello.
+
+ c.sendAlert(alertUnexpectedMessage)
+ return errors.New("tls: received unexpected CertificateStatus message")
+ }
+ hs.finishedHash.Write(cs.marshal())
+
+ c.ocspResponse = cs.response
+
+ msg, err = c.readHandshake()
+ if err != nil {
+ return err
+ }
+ }
+
+ if c.handshakes == 0 {
+ // If this is the first handshake on a connection, process and
+ // (optionally) verify the server's certificates.
+ if err := c.verifyServerCertificate(certMsg.certificates); err != nil {
+ return err
+ }
+ } else {
+ // This is a renegotiation handshake. We require that the
+ // server's identity (i.e. leaf certificate) is unchanged and
+ // thus any previous trust decision is still valid.
+ //
+ // See https://mitls.org/pages/attacks/3SHAKE for the
+ // motivation behind this requirement.
+ if !bytes.Equal(c.peerCertificates[0].Raw, certMsg.certificates[0]) {
+ c.sendAlert(alertBadCertificate)
+ return errors.New("tls: server's identity changed during renegotiation")
+ }
+ }
+
+ keyAgreement := hs.suite.ka(c.vers)
+
+ skx, ok := msg.(*serverKeyExchangeMsg)
+ if ok {
+ hs.finishedHash.Write(skx.marshal())
+ err = keyAgreement.processServerKeyExchange(c.config, hs.hello, hs.serverHello, c.peerCertificates[0], skx)
+ if err != nil {
+ c.sendAlert(alertUnexpectedMessage)
+ return err
+ }
+
+ msg, err = c.readHandshake()
+ if err != nil {
+ return err
+ }
+ }
+
+ var chainToSend *Certificate
+ var certRequested bool
+ certReq, ok := msg.(*certificateRequestMsg)
+ if ok {
+ certRequested = true
+ hs.finishedHash.Write(certReq.marshal())
+
+ cri := certificateRequestInfoFromMsg(hs.ctx, c.vers, certReq)
+ if chainToSend, err = c.getClientCertificate(cri); err != nil {
+ c.sendAlert(alertInternalError)
+ return err
+ }
+
+ msg, err = c.readHandshake()
+ if err != nil {
+ return err
+ }
+ }
+
+ shd, ok := msg.(*serverHelloDoneMsg)
+ if !ok {
+ c.sendAlert(alertUnexpectedMessage)
+ return unexpectedMessageError(shd, msg)
+ }
+ hs.finishedHash.Write(shd.marshal())
+
+ // If the server requested a certificate then we have to send a
+ // Certificate message, even if it's empty because we don't have a
+ // certificate to send.
+ if certRequested {
+ certMsg = new(certificateMsg)
+ certMsg.certificates = chainToSend.Certificate
+ hs.finishedHash.Write(certMsg.marshal())
+ if _, err := c.writeRecord(recordTypeHandshake, certMsg.marshal()); err != nil {
+ return err
+ }
+ }
+
+ preMasterSecret, ckx, err := keyAgreement.generateClientKeyExchange(c.config, hs.hello, c.peerCertificates[0])
+ if err != nil {
+ c.sendAlert(alertInternalError)
+ return err
+ }
+ if ckx != nil {
+ hs.finishedHash.Write(ckx.marshal())
+ if _, err := c.writeRecord(recordTypeHandshake, ckx.marshal()); err != nil {
+ return err
+ }
+ }
+
+ if chainToSend != nil && len(chainToSend.Certificate) > 0 {
+ certVerify := &certificateVerifyMsg{}
+
+ key, ok := chainToSend.PrivateKey.(crypto.Signer)
+ if !ok {
+ c.sendAlert(alertInternalError)
+ return fmt.Errorf("tls: client certificate private key of type %T does not implement crypto.Signer", chainToSend.PrivateKey)
+ }
+
+ var sigType uint8
+ var sigHash crypto.Hash
+ if c.vers >= VersionTLS12 {
+ signatureAlgorithm, err := selectSignatureScheme(c.vers, chainToSend, certReq.supportedSignatureAlgorithms)
+ if err != nil {
+ c.sendAlert(alertIllegalParameter)
+ return err
+ }
+ sigType, sigHash, err = typeAndHashFromSignatureScheme(signatureAlgorithm)
+ if err != nil {
+ return c.sendAlert(alertInternalError)
+ }
+ certVerify.hasSignatureAlgorithm = true
+ certVerify.signatureAlgorithm = signatureAlgorithm
+ } else {
+ sigType, sigHash, err = legacyTypeAndHashFromPublicKey(key.Public())
+ if err != nil {
+ c.sendAlert(alertIllegalParameter)
+ return err
+ }
+ }
+
+ signed := hs.finishedHash.hashForClientCertificate(sigType, sigHash)
+ signOpts := crypto.SignerOpts(sigHash)
+ if sigType == signatureRSAPSS {
+ signOpts = &rsa.PSSOptions{SaltLength: rsa.PSSSaltLengthEqualsHash, Hash: sigHash}
+ }
+ certVerify.signature, err = key.Sign(c.config.rand(), signed, signOpts)
+ if err != nil {
+ c.sendAlert(alertInternalError)
+ return err
+ }
+
+ hs.finishedHash.Write(certVerify.marshal())
+ if _, err := c.writeRecord(recordTypeHandshake, certVerify.marshal()); err != nil {
+ return err
+ }
+ }
+
+ hs.masterSecret = masterFromPreMasterSecret(c.vers, hs.suite, preMasterSecret, hs.hello.random, hs.serverHello.random)
+ if err := c.config.writeKeyLog(keyLogLabelTLS12, hs.hello.random, hs.masterSecret); err != nil {
+ c.sendAlert(alertInternalError)
+ return errors.New("tls: failed to write to key log: " + err.Error())
+ }
+
+ hs.finishedHash.discardHandshakeBuffer()
+
+ return nil
+}
+
+func (hs *clientHandshakeState) establishKeys() error {
+ c := hs.c
+
+ clientMAC, serverMAC, clientKey, serverKey, clientIV, serverIV :=
+ keysFromMasterSecret(c.vers, hs.suite, hs.masterSecret, hs.hello.random, hs.serverHello.random, hs.suite.macLen, hs.suite.keyLen, hs.suite.ivLen)
+ var clientCipher, serverCipher any
+ var clientHash, serverHash hash.Hash
+ if hs.suite.cipher != nil {
+ clientCipher = hs.suite.cipher(clientKey, clientIV, false /* not for reading */)
+ clientHash = hs.suite.mac(clientMAC)
+ serverCipher = hs.suite.cipher(serverKey, serverIV, true /* for reading */)
+ serverHash = hs.suite.mac(serverMAC)
+ } else {
+ clientCipher = hs.suite.aead(clientKey, clientIV)
+ serverCipher = hs.suite.aead(serverKey, serverIV)
+ }
+
+ c.in.prepareCipherSpec(c.vers, serverCipher, serverHash)
+ c.out.prepareCipherSpec(c.vers, clientCipher, clientHash)
+ return nil
+}
+
+func (hs *clientHandshakeState) serverResumedSession() bool {
+ // If the server responded with the same sessionId then it means the
+ // sessionTicket is being used to resume a TLS session.
+ return hs.session != nil && hs.hello.sessionId != nil &&
+ bytes.Equal(hs.serverHello.sessionId, hs.hello.sessionId)
+}
+
+func (hs *clientHandshakeState) processServerHello() (bool, error) {
+ c := hs.c
+
+ if err := hs.pickCipherSuite(); err != nil {
+ return false, err
+ }
+
+ if hs.serverHello.compressionMethod != compressionNone {
+ c.sendAlert(alertUnexpectedMessage)
+ return false, errors.New("tls: server selected unsupported compression format")
+ }
+
+ if c.handshakes == 0 && hs.serverHello.secureRenegotiationSupported {
+ c.secureRenegotiation = true
+ if len(hs.serverHello.secureRenegotiation) != 0 {
+ c.sendAlert(alertHandshakeFailure)
+ return false, errors.New("tls: initial handshake had non-empty renegotiation extension")
+ }
+ }
+
+ if c.handshakes > 0 && c.secureRenegotiation {
+ var expectedSecureRenegotiation [24]byte
+ copy(expectedSecureRenegotiation[:], c.clientFinished[:])
+ copy(expectedSecureRenegotiation[12:], c.serverFinished[:])
+ if !bytes.Equal(hs.serverHello.secureRenegotiation, expectedSecureRenegotiation[:]) {
+ c.sendAlert(alertHandshakeFailure)
+ return false, errors.New("tls: incorrect renegotiation extension contents")
+ }
+ }
+
+ if err := checkALPN(hs.hello.alpnProtocols, hs.serverHello.alpnProtocol); err != nil {
+ c.sendAlert(alertUnsupportedExtension)
+ return false, err
+ }
+ c.clientProtocol = hs.serverHello.alpnProtocol
+
+ c.scts = hs.serverHello.scts
+
+ if !hs.serverResumedSession() {
+ return false, nil
+ }
+
+ if hs.session.vers != c.vers {
+ c.sendAlert(alertHandshakeFailure)
+ return false, errors.New("tls: server resumed a session with a different version")
+ }
+
+ if hs.session.cipherSuite != hs.suite.id {
+ c.sendAlert(alertHandshakeFailure)
+ return false, errors.New("tls: server resumed a session with a different cipher suite")
+ }
+
+ // Restore masterSecret, peerCerts, and ocspResponse from previous state
+ hs.masterSecret = hs.session.masterSecret
+ c.peerCertificates = hs.session.serverCertificates
+ c.verifiedChains = hs.session.verifiedChains
+ c.ocspResponse = hs.session.ocspResponse
+ // Let the ServerHello SCTs override the session SCTs from the original
+ // connection, if any are provided
+ if len(c.scts) == 0 && len(hs.session.scts) != 0 {
+ c.scts = hs.session.scts
+ }
+
+ return true, nil
+}
+
+// checkALPN ensure that the server's choice of ALPN protocol is compatible with
+// the protocols that we advertised in the Client Hello.
+func checkALPN(clientProtos []string, serverProto string) error {
+ if serverProto == "" {
+ return nil
+ }
+ if len(clientProtos) == 0 {
+ return errors.New("tls: server advertised unrequested ALPN extension")
+ }
+ for _, proto := range clientProtos {
+ if proto == serverProto {
+ return nil
+ }
+ }
+ return errors.New("tls: server selected unadvertised ALPN protocol")
+}
+
+func (hs *clientHandshakeState) readFinished(out []byte) error {
+ c := hs.c
+
+ if err := c.readChangeCipherSpec(); err != nil {
+ return err
+ }
+
+ msg, err := c.readHandshake()
+ if err != nil {
+ return err
+ }
+ serverFinished, ok := msg.(*finishedMsg)
+ if !ok {
+ c.sendAlert(alertUnexpectedMessage)
+ return unexpectedMessageError(serverFinished, msg)
+ }
+
+ verify := hs.finishedHash.serverSum(hs.masterSecret)
+ if len(verify) != len(serverFinished.verifyData) ||
+ subtle.ConstantTimeCompare(verify, serverFinished.verifyData) != 1 {
+ c.sendAlert(alertHandshakeFailure)
+ return errors.New("tls: server's Finished message was incorrect")
+ }
+ hs.finishedHash.Write(serverFinished.marshal())
+ copy(out, verify)
+ return nil
+}
+
+func (hs *clientHandshakeState) readSessionTicket() error {
+ if !hs.serverHello.ticketSupported {
+ return nil
+ }
+
+ c := hs.c
+ msg, err := c.readHandshake()
+ if err != nil {
+ return err
+ }
+ sessionTicketMsg, ok := msg.(*newSessionTicketMsg)
+ if !ok {
+ c.sendAlert(alertUnexpectedMessage)
+ return unexpectedMessageError(sessionTicketMsg, msg)
+ }
+ hs.finishedHash.Write(sessionTicketMsg.marshal())
+
+ hs.session = &clientSessionState{
+ sessionTicket: sessionTicketMsg.ticket,
+ vers: c.vers,
+ cipherSuite: hs.suite.id,
+ masterSecret: hs.masterSecret,
+ serverCertificates: c.peerCertificates,
+ verifiedChains: c.verifiedChains,
+ receivedAt: c.config.time(),
+ ocspResponse: c.ocspResponse,
+ scts: c.scts,
+ }
+
+ return nil
+}
+
+func (hs *clientHandshakeState) sendFinished(out []byte) error {
+ c := hs.c
+
+ if _, err := c.writeRecord(recordTypeChangeCipherSpec, []byte{1}); err != nil {
+ return err
+ }
+
+ finished := new(finishedMsg)
+ finished.verifyData = hs.finishedHash.clientSum(hs.masterSecret)
+ hs.finishedHash.Write(finished.marshal())
+ if _, err := c.writeRecord(recordTypeHandshake, finished.marshal()); err != nil {
+ return err
+ }
+ copy(out, finished.verifyData)
+ return nil
+}
+
+// verifyServerCertificate parses and verifies the provided chain, setting
+// c.verifiedChains and c.peerCertificates or sending the appropriate alert.
+func (c *Conn) verifyServerCertificate(certificates [][]byte) error {
+ activeHandles := make([]*activeCert, len(certificates))
+ certs := make([]*x509.Certificate, len(certificates))
+ for i, asn1Data := range certificates {
+ cert, err := clientCertCache.newCert(asn1Data)
+ if err != nil {
+ c.sendAlert(alertBadCertificate)
+ return errors.New("tls: failed to parse certificate from server: " + err.Error())
+ }
+ activeHandles[i] = cert
+ certs[i] = cert.cert
+ }
+
+ if !c.config.InsecureSkipVerify {
+ opts := x509.VerifyOptions{
+ Roots: c.config.RootCAs,
+ CurrentTime: c.config.time(),
+ DNSName: c.config.ServerName,
+ Intermediates: x509.NewCertPool(),
+ }
+
+ for _, cert := range certs[1:] {
+ opts.Intermediates.AddCert(cert)
+ }
+ var err error
+ c.verifiedChains, err = certs[0].Verify(opts)
+ if err != nil {
+ c.sendAlert(alertBadCertificate)
+ return &CertificateVerificationError{UnverifiedCertificates: certs, Err: err}
+ }
+ }
+
+ switch certs[0].PublicKey.(type) {
+ case *rsa.PublicKey, *ecdsa.PublicKey, ed25519.PublicKey:
+ break
+ default:
+ c.sendAlert(alertUnsupportedCertificate)
+ return fmt.Errorf("tls: server's certificate contains an unsupported type of public key: %T", certs[0].PublicKey)
+ }
+
+ c.activeCertHandles = activeHandles
+ c.peerCertificates = certs
+
+ if c.config.VerifyPeerCertificate != nil {
+ if err := c.config.VerifyPeerCertificate(certificates, c.verifiedChains); err != nil {
+ c.sendAlert(alertBadCertificate)
+ return err
+ }
+ }
+
+ if c.config.VerifyConnection != nil {
+ if err := c.config.VerifyConnection(c.connectionStateLocked()); err != nil {
+ c.sendAlert(alertBadCertificate)
+ return err
+ }
+ }
+
+ return nil
+}
+
+// certificateRequestInfoFromMsg generates a CertificateRequestInfo from a TLS
+// <= 1.2 CertificateRequest, making an effort to fill in missing information.
+func certificateRequestInfoFromMsg(ctx context.Context, vers uint16, certReq *certificateRequestMsg) *CertificateRequestInfo {
+ cri := &certificateRequestInfo{
+ AcceptableCAs: certReq.certificateAuthorities,
+ Version: vers,
+ ctx: ctx,
+ }
+
+ var rsaAvail, ecAvail bool
+ for _, certType := range certReq.certificateTypes {
+ switch certType {
+ case certTypeRSASign:
+ rsaAvail = true
+ case certTypeECDSASign:
+ ecAvail = true
+ }
+ }
+
+ if !certReq.hasSignatureAlgorithm {
+ // Prior to TLS 1.2, signature schemes did not exist. In this case we
+ // make up a list based on the acceptable certificate types, to help
+ // GetClientCertificate and SupportsCertificate select the right certificate.
+ // The hash part of the SignatureScheme is a lie here, because
+ // TLS 1.0 and 1.1 always use MD5+SHA1 for RSA and SHA1 for ECDSA.
+ switch {
+ case rsaAvail && ecAvail:
+ cri.SignatureSchemes = []SignatureScheme{
+ ECDSAWithP256AndSHA256, ECDSAWithP384AndSHA384, ECDSAWithP521AndSHA512,
+ PKCS1WithSHA256, PKCS1WithSHA384, PKCS1WithSHA512, PKCS1WithSHA1,
+ }
+ case rsaAvail:
+ cri.SignatureSchemes = []SignatureScheme{
+ PKCS1WithSHA256, PKCS1WithSHA384, PKCS1WithSHA512, PKCS1WithSHA1,
+ }
+ case ecAvail:
+ cri.SignatureSchemes = []SignatureScheme{
+ ECDSAWithP256AndSHA256, ECDSAWithP384AndSHA384, ECDSAWithP521AndSHA512,
+ }
+ }
+ return toCertificateRequestInfo(cri)
+ }
+
+ // Filter the signature schemes based on the certificate types.
+ // See RFC 5246, Section 7.4.4 (where it calls this "somewhat complicated").
+ cri.SignatureSchemes = make([]SignatureScheme, 0, len(certReq.supportedSignatureAlgorithms))
+ for _, sigScheme := range certReq.supportedSignatureAlgorithms {
+ sigType, _, err := typeAndHashFromSignatureScheme(sigScheme)
+ if err != nil {
+ continue
+ }
+ switch sigType {
+ case signatureECDSA, signatureEd25519:
+ if ecAvail {
+ cri.SignatureSchemes = append(cri.SignatureSchemes, sigScheme)
+ }
+ case signatureRSAPSS, signaturePKCS1v15:
+ if rsaAvail {
+ cri.SignatureSchemes = append(cri.SignatureSchemes, sigScheme)
+ }
+ }
+ }
+
+ return toCertificateRequestInfo(cri)
+}
+
+func (c *Conn) getClientCertificate(cri *CertificateRequestInfo) (*Certificate, error) {
+ if c.config.GetClientCertificate != nil {
+ return c.config.GetClientCertificate(cri)
+ }
+
+ for _, chain := range c.config.Certificates {
+ if err := cri.SupportsCertificate(&chain); err != nil {
+ continue
+ }
+ return &chain, nil
+ }
+
+ // No acceptable certificate found. Don't send a certificate.
+ return new(Certificate), nil
+}
+
+const clientSessionCacheKeyPrefix = "qtls-"
+
+// clientSessionCacheKey returns a key used to cache sessionTickets that could
+// be used to resume previously negotiated TLS sessions with a server.
+func clientSessionCacheKey(serverAddr net.Addr, config *config) string {
+ if len(config.ServerName) > 0 {
+ return clientSessionCacheKeyPrefix + config.ServerName
+ }
+ return clientSessionCacheKeyPrefix + serverAddr.String()
+}
+
+// hostnameInSNI converts name into an appropriate hostname for SNI.
+// Literal IP addresses and absolute FQDNs are not permitted as SNI values.
+// See RFC 6066, Section 3.
+func hostnameInSNI(name string) string {
+ host := name
+ if len(host) > 0 && host[0] == '[' && host[len(host)-1] == ']' {
+ host = host[1 : len(host)-1]
+ }
+ if i := strings.LastIndex(host, "%"); i > 0 {
+ host = host[:i]
+ }
+ if net.ParseIP(host) != nil {
+ return ""
+ }
+ for len(name) > 0 && name[len(name)-1] == '.' {
+ name = name[:len(name)-1]
+ }
+ return name
+}
diff --git a/vendor/github.com/quic-go/qtls-go1-20/handshake_client_tls13.go b/vendor/github.com/quic-go/qtls-go1-20/handshake_client_tls13.go
new file mode 100644
index 0000000000..caf42d8caa
--- /dev/null
+++ b/vendor/github.com/quic-go/qtls-go1-20/handshake_client_tls13.go
@@ -0,0 +1,743 @@
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package qtls
+
+import (
+ "bytes"
+ "context"
+ "crypto"
+ "crypto/ecdh"
+ "crypto/hmac"
+ "crypto/rsa"
+ "encoding/binary"
+ "errors"
+ "hash"
+ "time"
+
+ "golang.org/x/crypto/cryptobyte"
+)
+
+type clientHandshakeStateTLS13 struct {
+ c *Conn
+ ctx context.Context
+ serverHello *serverHelloMsg
+ hello *clientHelloMsg
+ ecdheKey *ecdh.PrivateKey
+
+ session *clientSessionState
+ earlySecret []byte
+ binderKey []byte
+
+ certReq *certificateRequestMsgTLS13
+ usingPSK bool
+ sentDummyCCS bool
+ suite *cipherSuiteTLS13
+ transcript hash.Hash
+ masterSecret []byte
+ trafficSecret []byte // client_application_traffic_secret_0
+}
+
+// handshake requires hs.c, hs.hello, hs.serverHello, hs.ecdheKey, and,
+// optionally, hs.session, hs.earlySecret and hs.binderKey to be set.
+func (hs *clientHandshakeStateTLS13) handshake() error {
+ c := hs.c
+
+ if needFIPS() {
+ return errors.New("tls: internal error: TLS 1.3 reached in FIPS mode")
+ }
+
+ // The server must not select TLS 1.3 in a renegotiation. See RFC 8446,
+ // sections 4.1.2 and 4.1.3.
+ if c.handshakes > 0 {
+ c.sendAlert(alertProtocolVersion)
+ return errors.New("tls: server selected TLS 1.3 in a renegotiation")
+ }
+
+ // Consistency check on the presence of a keyShare and its parameters.
+ if hs.ecdheKey == nil || len(hs.hello.keyShares) != 1 {
+ return c.sendAlert(alertInternalError)
+ }
+
+ if err := hs.checkServerHelloOrHRR(); err != nil {
+ return err
+ }
+
+ hs.transcript = hs.suite.hash.New()
+ hs.transcript.Write(hs.hello.marshal())
+
+ if bytes.Equal(hs.serverHello.random, helloRetryRequestRandom) {
+ if err := hs.sendDummyChangeCipherSpec(); err != nil {
+ return err
+ }
+ if err := hs.processHelloRetryRequest(); err != nil {
+ return err
+ }
+ }
+
+ hs.transcript.Write(hs.serverHello.marshal())
+
+ c.buffering = true
+ if err := hs.processServerHello(); err != nil {
+ return err
+ }
+ c.updateConnectionState()
+ if err := hs.sendDummyChangeCipherSpec(); err != nil {
+ return err
+ }
+ if err := hs.establishHandshakeKeys(); err != nil {
+ return err
+ }
+ if err := hs.readServerParameters(); err != nil {
+ return err
+ }
+ if err := hs.readServerCertificate(); err != nil {
+ return err
+ }
+ c.updateConnectionState()
+ if err := hs.readServerFinished(); err != nil {
+ return err
+ }
+ if err := hs.sendClientCertificate(); err != nil {
+ return err
+ }
+ if err := hs.sendClientFinished(); err != nil {
+ return err
+ }
+ if _, err := c.flush(); err != nil {
+ return err
+ }
+
+ c.isHandshakeComplete.Store(true)
+ c.updateConnectionState()
+ return nil
+}
+
+// checkServerHelloOrHRR does validity checks that apply to both ServerHello and
+// HelloRetryRequest messages. It sets hs.suite.
+func (hs *clientHandshakeStateTLS13) checkServerHelloOrHRR() error {
+ c := hs.c
+
+ if hs.serverHello.supportedVersion == 0 {
+ c.sendAlert(alertMissingExtension)
+ return errors.New("tls: server selected TLS 1.3 using the legacy version field")
+ }
+
+ if hs.serverHello.supportedVersion != VersionTLS13 {
+ c.sendAlert(alertIllegalParameter)
+ return errors.New("tls: server selected an invalid version after a HelloRetryRequest")
+ }
+
+ if hs.serverHello.vers != VersionTLS12 {
+ c.sendAlert(alertIllegalParameter)
+ return errors.New("tls: server sent an incorrect legacy version")
+ }
+
+ if hs.serverHello.ocspStapling ||
+ hs.serverHello.ticketSupported ||
+ hs.serverHello.secureRenegotiationSupported ||
+ len(hs.serverHello.secureRenegotiation) != 0 ||
+ len(hs.serverHello.alpnProtocol) != 0 ||
+ len(hs.serverHello.scts) != 0 {
+ c.sendAlert(alertUnsupportedExtension)
+ return errors.New("tls: server sent a ServerHello extension forbidden in TLS 1.3")
+ }
+
+ if !bytes.Equal(hs.hello.sessionId, hs.serverHello.sessionId) {
+ c.sendAlert(alertIllegalParameter)
+ return errors.New("tls: server did not echo the legacy session ID")
+ }
+
+ if hs.serverHello.compressionMethod != compressionNone {
+ c.sendAlert(alertIllegalParameter)
+ return errors.New("tls: server selected unsupported compression format")
+ }
+
+ selectedSuite := mutualCipherSuiteTLS13(hs.hello.cipherSuites, hs.serverHello.cipherSuite)
+ if hs.suite != nil && selectedSuite != hs.suite {
+ c.sendAlert(alertIllegalParameter)
+ return errors.New("tls: server changed cipher suite after a HelloRetryRequest")
+ }
+ if selectedSuite == nil {
+ c.sendAlert(alertIllegalParameter)
+ return errors.New("tls: server chose an unconfigured cipher suite")
+ }
+ hs.suite = selectedSuite
+ c.cipherSuite = hs.suite.id
+
+ return nil
+}
+
+// sendDummyChangeCipherSpec sends a ChangeCipherSpec record for compatibility
+// with middleboxes that didn't implement TLS correctly. See RFC 8446, Appendix D.4.
+func (hs *clientHandshakeStateTLS13) sendDummyChangeCipherSpec() error {
+ if hs.sentDummyCCS {
+ return nil
+ }
+ hs.sentDummyCCS = true
+
+ _, err := hs.c.writeRecord(recordTypeChangeCipherSpec, []byte{1})
+ return err
+}
+
+// processHelloRetryRequest handles the HRR in hs.serverHello, modifies and
+// resends hs.hello, and reads the new ServerHello into hs.serverHello.
+func (hs *clientHandshakeStateTLS13) processHelloRetryRequest() error {
+ c := hs.c
+
+ // The first ClientHello gets double-hashed into the transcript upon a
+ // HelloRetryRequest. (The idea is that the server might offload transcript
+ // storage to the client in the cookie.) See RFC 8446, Section 4.4.1.
+ chHash := hs.transcript.Sum(nil)
+ hs.transcript.Reset()
+ hs.transcript.Write([]byte{typeMessageHash, 0, 0, uint8(len(chHash))})
+ hs.transcript.Write(chHash)
+ hs.transcript.Write(hs.serverHello.marshal())
+
+ // The only HelloRetryRequest extensions we support are key_share and
+ // cookie, and clients must abort the handshake if the HRR would not result
+ // in any change in the ClientHello.
+ if hs.serverHello.selectedGroup == 0 && hs.serverHello.cookie == nil {
+ c.sendAlert(alertIllegalParameter)
+ return errors.New("tls: server sent an unnecessary HelloRetryRequest message")
+ }
+
+ if hs.serverHello.cookie != nil {
+ hs.hello.cookie = hs.serverHello.cookie
+ }
+
+ if hs.serverHello.serverShare.group != 0 {
+ c.sendAlert(alertDecodeError)
+ return errors.New("tls: received malformed key_share extension")
+ }
+
+ // If the server sent a key_share extension selecting a group, ensure it's
+ // a group we advertised but did not send a key share for, and send a key
+ // share for it this time.
+ if curveID := hs.serverHello.selectedGroup; curveID != 0 {
+ curveOK := false
+ for _, id := range hs.hello.supportedCurves {
+ if id == curveID {
+ curveOK = true
+ break
+ }
+ }
+ if !curveOK {
+ c.sendAlert(alertIllegalParameter)
+ return errors.New("tls: server selected unsupported group")
+ }
+ if sentID, _ := curveIDForCurve(hs.ecdheKey.Curve()); sentID == curveID {
+ c.sendAlert(alertIllegalParameter)
+ return errors.New("tls: server sent an unnecessary HelloRetryRequest key_share")
+ }
+ if _, ok := curveForCurveID(curveID); !ok {
+ c.sendAlert(alertInternalError)
+ return errors.New("tls: CurvePreferences includes unsupported curve")
+ }
+ key, err := generateECDHEKey(c.config.rand(), curveID)
+ if err != nil {
+ c.sendAlert(alertInternalError)
+ return err
+ }
+ hs.ecdheKey = key
+ hs.hello.keyShares = []keyShare{{group: curveID, data: key.PublicKey().Bytes()}}
+ }
+
+ hs.hello.raw = nil
+ if len(hs.hello.pskIdentities) > 0 {
+ pskSuite := cipherSuiteTLS13ByID(hs.session.cipherSuite)
+ if pskSuite == nil {
+ return c.sendAlert(alertInternalError)
+ }
+ if pskSuite.hash == hs.suite.hash {
+ // Update binders and obfuscated_ticket_age.
+ ticketAge := uint32(c.config.time().Sub(hs.session.receivedAt) / time.Millisecond)
+ hs.hello.pskIdentities[0].obfuscatedTicketAge = ticketAge + hs.session.ageAdd
+
+ transcript := hs.suite.hash.New()
+ transcript.Write([]byte{typeMessageHash, 0, 0, uint8(len(chHash))})
+ transcript.Write(chHash)
+ transcript.Write(hs.serverHello.marshal())
+ transcript.Write(hs.hello.marshalWithoutBinders())
+ pskBinders := [][]byte{hs.suite.finishedHash(hs.binderKey, transcript)}
+ hs.hello.updateBinders(pskBinders)
+ } else {
+ // Server selected a cipher suite incompatible with the PSK.
+ hs.hello.pskIdentities = nil
+ hs.hello.pskBinders = nil
+ }
+ }
+
+ if hs.hello.earlyData && c.extraConfig != nil && c.extraConfig.Rejected0RTT != nil {
+ c.extraConfig.Rejected0RTT()
+ }
+ hs.hello.earlyData = false // disable 0-RTT
+
+ hs.transcript.Write(hs.hello.marshal())
+ if _, err := c.writeRecord(recordTypeHandshake, hs.hello.marshal()); err != nil {
+ return err
+ }
+
+ msg, err := c.readHandshake()
+ if err != nil {
+ return err
+ }
+
+ serverHello, ok := msg.(*serverHelloMsg)
+ if !ok {
+ c.sendAlert(alertUnexpectedMessage)
+ return unexpectedMessageError(serverHello, msg)
+ }
+ hs.serverHello = serverHello
+
+ if err := hs.checkServerHelloOrHRR(); err != nil {
+ return err
+ }
+
+ return nil
+}
+
+func (hs *clientHandshakeStateTLS13) processServerHello() error {
+ c := hs.c
+
+ if bytes.Equal(hs.serverHello.random, helloRetryRequestRandom) {
+ c.sendAlert(alertUnexpectedMessage)
+ return errors.New("tls: server sent two HelloRetryRequest messages")
+ }
+
+ if len(hs.serverHello.cookie) != 0 {
+ c.sendAlert(alertUnsupportedExtension)
+ return errors.New("tls: server sent a cookie in a normal ServerHello")
+ }
+
+ if hs.serverHello.selectedGroup != 0 {
+ c.sendAlert(alertDecodeError)
+ return errors.New("tls: malformed key_share extension")
+ }
+
+ if hs.serverHello.serverShare.group == 0 {
+ c.sendAlert(alertIllegalParameter)
+ return errors.New("tls: server did not send a key share")
+ }
+ if sentID, _ := curveIDForCurve(hs.ecdheKey.Curve()); hs.serverHello.serverShare.group != sentID {
+ c.sendAlert(alertIllegalParameter)
+ return errors.New("tls: server selected unsupported group")
+ }
+
+ if !hs.serverHello.selectedIdentityPresent {
+ return nil
+ }
+
+ if int(hs.serverHello.selectedIdentity) >= len(hs.hello.pskIdentities) {
+ c.sendAlert(alertIllegalParameter)
+ return errors.New("tls: server selected an invalid PSK")
+ }
+
+ if len(hs.hello.pskIdentities) != 1 || hs.session == nil {
+ return c.sendAlert(alertInternalError)
+ }
+ pskSuite := cipherSuiteTLS13ByID(hs.session.cipherSuite)
+ if pskSuite == nil {
+ return c.sendAlert(alertInternalError)
+ }
+ if pskSuite.hash != hs.suite.hash {
+ c.sendAlert(alertIllegalParameter)
+ return errors.New("tls: server selected an invalid PSK and cipher suite pair")
+ }
+
+ hs.usingPSK = true
+ c.didResume = true
+ c.peerCertificates = hs.session.serverCertificates
+ c.verifiedChains = hs.session.verifiedChains
+ c.ocspResponse = hs.session.ocspResponse
+ c.scts = hs.session.scts
+ return nil
+}
+
+func (hs *clientHandshakeStateTLS13) establishHandshakeKeys() error {
+ c := hs.c
+
+ peerKey, err := hs.ecdheKey.Curve().NewPublicKey(hs.serverHello.serverShare.data)
+ if err != nil {
+ c.sendAlert(alertIllegalParameter)
+ return errors.New("tls: invalid server key share")
+ }
+ sharedKey, err := hs.ecdheKey.ECDH(peerKey)
+ if err != nil {
+ c.sendAlert(alertIllegalParameter)
+ return errors.New("tls: invalid server key share")
+ }
+
+ earlySecret := hs.earlySecret
+ if !hs.usingPSK {
+ earlySecret = hs.suite.extract(nil, nil)
+ }
+ handshakeSecret := hs.suite.extract(sharedKey,
+ hs.suite.deriveSecret(earlySecret, "derived", nil))
+
+ clientSecret := hs.suite.deriveSecret(handshakeSecret,
+ clientHandshakeTrafficLabel, hs.transcript)
+ c.out.exportKey(EncryptionHandshake, hs.suite, clientSecret)
+ c.out.setTrafficSecret(hs.suite, clientSecret)
+ serverSecret := hs.suite.deriveSecret(handshakeSecret,
+ serverHandshakeTrafficLabel, hs.transcript)
+ c.in.exportKey(EncryptionHandshake, hs.suite, serverSecret)
+ c.in.setTrafficSecret(hs.suite, serverSecret)
+
+ err = c.config.writeKeyLog(keyLogLabelClientHandshake, hs.hello.random, clientSecret)
+ if err != nil {
+ c.sendAlert(alertInternalError)
+ return err
+ }
+ err = c.config.writeKeyLog(keyLogLabelServerHandshake, hs.hello.random, serverSecret)
+ if err != nil {
+ c.sendAlert(alertInternalError)
+ return err
+ }
+
+ hs.masterSecret = hs.suite.extract(nil,
+ hs.suite.deriveSecret(handshakeSecret, "derived", nil))
+
+ return nil
+}
+
+func (hs *clientHandshakeStateTLS13) readServerParameters() error {
+ c := hs.c
+
+ msg, err := c.readHandshake()
+ if err != nil {
+ return err
+ }
+
+ encryptedExtensions, ok := msg.(*encryptedExtensionsMsg)
+ if !ok {
+ c.sendAlert(alertUnexpectedMessage)
+ return unexpectedMessageError(encryptedExtensions, msg)
+ }
+ // Notify the caller if 0-RTT was rejected.
+ if !encryptedExtensions.earlyData && hs.hello.earlyData && c.extraConfig != nil && c.extraConfig.Rejected0RTT != nil {
+ c.extraConfig.Rejected0RTT()
+ }
+ c.used0RTT = encryptedExtensions.earlyData
+ if hs.c.extraConfig != nil && hs.c.extraConfig.ReceivedExtensions != nil {
+ hs.c.extraConfig.ReceivedExtensions(typeEncryptedExtensions, encryptedExtensions.additionalExtensions)
+ }
+ hs.transcript.Write(encryptedExtensions.marshal())
+
+ if err := checkALPN(hs.hello.alpnProtocols, encryptedExtensions.alpnProtocol); err != nil {
+ c.sendAlert(alertUnsupportedExtension)
+ return err
+ }
+ c.clientProtocol = encryptedExtensions.alpnProtocol
+
+ if c.extraConfig != nil && c.extraConfig.EnforceNextProtoSelection {
+ if len(encryptedExtensions.alpnProtocol) == 0 {
+ // the server didn't select an ALPN
+ c.sendAlert(alertNoApplicationProtocol)
+ return errors.New("ALPN negotiation failed. Server didn't offer any protocols")
+ }
+ }
+ return nil
+}
+
+func (hs *clientHandshakeStateTLS13) readServerCertificate() error {
+ c := hs.c
+
+ // Either a PSK or a certificate is always used, but not both.
+ // See RFC 8446, Section 4.1.1.
+ if hs.usingPSK {
+ // Make sure the connection is still being verified whether or not this
+ // is a resumption. Resumptions currently don't reverify certificates so
+ // they don't call verifyServerCertificate. See Issue 31641.
+ if c.config.VerifyConnection != nil {
+ if err := c.config.VerifyConnection(c.connectionStateLocked()); err != nil {
+ c.sendAlert(alertBadCertificate)
+ return err
+ }
+ }
+ return nil
+ }
+
+ msg, err := c.readHandshake()
+ if err != nil {
+ return err
+ }
+
+ certReq, ok := msg.(*certificateRequestMsgTLS13)
+ if ok {
+ hs.transcript.Write(certReq.marshal())
+
+ hs.certReq = certReq
+
+ msg, err = c.readHandshake()
+ if err != nil {
+ return err
+ }
+ }
+
+ certMsg, ok := msg.(*certificateMsgTLS13)
+ if !ok {
+ c.sendAlert(alertUnexpectedMessage)
+ return unexpectedMessageError(certMsg, msg)
+ }
+ if len(certMsg.certificate.Certificate) == 0 {
+ c.sendAlert(alertDecodeError)
+ return errors.New("tls: received empty certificates message")
+ }
+ hs.transcript.Write(certMsg.marshal())
+
+ c.scts = certMsg.certificate.SignedCertificateTimestamps
+ c.ocspResponse = certMsg.certificate.OCSPStaple
+
+ if err := c.verifyServerCertificate(certMsg.certificate.Certificate); err != nil {
+ return err
+ }
+
+ msg, err = c.readHandshake()
+ if err != nil {
+ return err
+ }
+
+ certVerify, ok := msg.(*certificateVerifyMsg)
+ if !ok {
+ c.sendAlert(alertUnexpectedMessage)
+ return unexpectedMessageError(certVerify, msg)
+ }
+
+ // See RFC 8446, Section 4.4.3.
+ if !isSupportedSignatureAlgorithm(certVerify.signatureAlgorithm, supportedSignatureAlgorithms()) {
+ c.sendAlert(alertIllegalParameter)
+ return errors.New("tls: certificate used with invalid signature algorithm")
+ }
+ sigType, sigHash, err := typeAndHashFromSignatureScheme(certVerify.signatureAlgorithm)
+ if err != nil {
+ return c.sendAlert(alertInternalError)
+ }
+ if sigType == signaturePKCS1v15 || sigHash == crypto.SHA1 {
+ c.sendAlert(alertIllegalParameter)
+ return errors.New("tls: certificate used with invalid signature algorithm")
+ }
+ signed := signedMessage(sigHash, serverSignatureContext, hs.transcript)
+ if err := verifyHandshakeSignature(sigType, c.peerCertificates[0].PublicKey,
+ sigHash, signed, certVerify.signature); err != nil {
+ c.sendAlert(alertDecryptError)
+ return errors.New("tls: invalid signature by the server certificate: " + err.Error())
+ }
+
+ hs.transcript.Write(certVerify.marshal())
+
+ return nil
+}
+
+func (hs *clientHandshakeStateTLS13) readServerFinished() error {
+ c := hs.c
+
+ msg, err := c.readHandshake()
+ if err != nil {
+ return err
+ }
+
+ finished, ok := msg.(*finishedMsg)
+ if !ok {
+ c.sendAlert(alertUnexpectedMessage)
+ return unexpectedMessageError(finished, msg)
+ }
+
+ expectedMAC := hs.suite.finishedHash(c.in.trafficSecret, hs.transcript)
+ if !hmac.Equal(expectedMAC, finished.verifyData) {
+ c.sendAlert(alertDecryptError)
+ return errors.New("tls: invalid server finished hash")
+ }
+
+ hs.transcript.Write(finished.marshal())
+
+ // Derive secrets that take context through the server Finished.
+
+ hs.trafficSecret = hs.suite.deriveSecret(hs.masterSecret,
+ clientApplicationTrafficLabel, hs.transcript)
+ serverSecret := hs.suite.deriveSecret(hs.masterSecret,
+ serverApplicationTrafficLabel, hs.transcript)
+ c.in.exportKey(EncryptionApplication, hs.suite, serverSecret)
+ c.in.setTrafficSecret(hs.suite, serverSecret)
+
+ err = c.config.writeKeyLog(keyLogLabelClientTraffic, hs.hello.random, hs.trafficSecret)
+ if err != nil {
+ c.sendAlert(alertInternalError)
+ return err
+ }
+ err = c.config.writeKeyLog(keyLogLabelServerTraffic, hs.hello.random, serverSecret)
+ if err != nil {
+ c.sendAlert(alertInternalError)
+ return err
+ }
+
+ c.ekm = hs.suite.exportKeyingMaterial(hs.masterSecret, hs.transcript)
+
+ return nil
+}
+
+func (hs *clientHandshakeStateTLS13) sendClientCertificate() error {
+ c := hs.c
+
+ if hs.certReq == nil {
+ return nil
+ }
+
+ cert, err := c.getClientCertificate(toCertificateRequestInfo(&certificateRequestInfo{
+ AcceptableCAs: hs.certReq.certificateAuthorities,
+ SignatureSchemes: hs.certReq.supportedSignatureAlgorithms,
+ Version: c.vers,
+ ctx: hs.ctx,
+ }))
+ if err != nil {
+ return err
+ }
+
+ certMsg := new(certificateMsgTLS13)
+
+ certMsg.certificate = *cert
+ certMsg.scts = hs.certReq.scts && len(cert.SignedCertificateTimestamps) > 0
+ certMsg.ocspStapling = hs.certReq.ocspStapling && len(cert.OCSPStaple) > 0
+
+ hs.transcript.Write(certMsg.marshal())
+ if _, err := c.writeRecord(recordTypeHandshake, certMsg.marshal()); err != nil {
+ return err
+ }
+
+ // If we sent an empty certificate message, skip the CertificateVerify.
+ if len(cert.Certificate) == 0 {
+ return nil
+ }
+
+ certVerifyMsg := new(certificateVerifyMsg)
+ certVerifyMsg.hasSignatureAlgorithm = true
+
+ certVerifyMsg.signatureAlgorithm, err = selectSignatureScheme(c.vers, cert, hs.certReq.supportedSignatureAlgorithms)
+ if err != nil {
+ // getClientCertificate returned a certificate incompatible with the
+ // CertificateRequestInfo supported signature algorithms.
+ c.sendAlert(alertHandshakeFailure)
+ return err
+ }
+
+ sigType, sigHash, err := typeAndHashFromSignatureScheme(certVerifyMsg.signatureAlgorithm)
+ if err != nil {
+ return c.sendAlert(alertInternalError)
+ }
+
+ signed := signedMessage(sigHash, clientSignatureContext, hs.transcript)
+ signOpts := crypto.SignerOpts(sigHash)
+ if sigType == signatureRSAPSS {
+ signOpts = &rsa.PSSOptions{SaltLength: rsa.PSSSaltLengthEqualsHash, Hash: sigHash}
+ }
+ sig, err := cert.PrivateKey.(crypto.Signer).Sign(c.config.rand(), signed, signOpts)
+ if err != nil {
+ c.sendAlert(alertInternalError)
+ return errors.New("tls: failed to sign handshake: " + err.Error())
+ }
+ certVerifyMsg.signature = sig
+
+ hs.transcript.Write(certVerifyMsg.marshal())
+ if _, err := c.writeRecord(recordTypeHandshake, certVerifyMsg.marshal()); err != nil {
+ return err
+ }
+
+ return nil
+}
+
+func (hs *clientHandshakeStateTLS13) sendClientFinished() error {
+ c := hs.c
+
+ finished := &finishedMsg{
+ verifyData: hs.suite.finishedHash(c.out.trafficSecret, hs.transcript),
+ }
+
+ hs.transcript.Write(finished.marshal())
+ if _, err := c.writeRecord(recordTypeHandshake, finished.marshal()); err != nil {
+ return err
+ }
+
+ c.out.exportKey(EncryptionApplication, hs.suite, hs.trafficSecret)
+ c.out.setTrafficSecret(hs.suite, hs.trafficSecret)
+
+ if !c.config.SessionTicketsDisabled && c.config.ClientSessionCache != nil {
+ c.resumptionSecret = hs.suite.deriveSecret(hs.masterSecret,
+ resumptionLabel, hs.transcript)
+ }
+
+ return nil
+}
+
+func (c *Conn) handleNewSessionTicket(msg *newSessionTicketMsgTLS13) error {
+ if !c.isClient {
+ c.sendAlert(alertUnexpectedMessage)
+ return errors.New("tls: received new session ticket from a client")
+ }
+
+ if c.config.SessionTicketsDisabled || c.config.ClientSessionCache == nil {
+ return nil
+ }
+
+ // See RFC 8446, Section 4.6.1.
+ if msg.lifetime == 0 {
+ return nil
+ }
+ lifetime := time.Duration(msg.lifetime) * time.Second
+ if lifetime > maxSessionTicketLifetime {
+ c.sendAlert(alertIllegalParameter)
+ return errors.New("tls: received a session ticket with invalid lifetime")
+ }
+
+ cipherSuite := cipherSuiteTLS13ByID(c.cipherSuite)
+ if cipherSuite == nil || c.resumptionSecret == nil {
+ return c.sendAlert(alertInternalError)
+ }
+
+ // We need to save the max_early_data_size that the server sent us, in order
+ // to decide if we're going to try 0-RTT with this ticket.
+ // However, at the same time, the qtls.ClientSessionTicket needs to be equal to
+ // the tls.ClientSessionTicket, so we can't just add a new field to the struct.
+ // We therefore abuse the nonce field (which is a byte slice)
+ nonceWithEarlyData := make([]byte, len(msg.nonce)+4)
+ binary.BigEndian.PutUint32(nonceWithEarlyData, msg.maxEarlyData)
+ copy(nonceWithEarlyData[4:], msg.nonce)
+
+ var appData []byte
+ if c.extraConfig != nil && c.extraConfig.GetAppDataForSessionState != nil {
+ appData = c.extraConfig.GetAppDataForSessionState()
+ }
+ var b cryptobyte.Builder
+ b.AddUint16(clientSessionStateVersion) // revision
+ b.AddUint32(msg.maxEarlyData)
+ b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
+ b.AddBytes(appData)
+ })
+ b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
+ b.AddBytes(msg.nonce)
+ })
+
+ // Save the resumption_master_secret and nonce instead of deriving the PSK
+ // to do the least amount of work on NewSessionTicket messages before we
+ // know if the ticket will be used. Forward secrecy of resumed connections
+ // is guaranteed by the requirement for pskModeDHE.
+ session := &clientSessionState{
+ sessionTicket: msg.label,
+ vers: c.vers,
+ cipherSuite: c.cipherSuite,
+ masterSecret: c.resumptionSecret,
+ serverCertificates: c.peerCertificates,
+ verifiedChains: c.verifiedChains,
+ receivedAt: c.config.time(),
+ nonce: b.BytesOrPanic(),
+ useBy: c.config.time().Add(lifetime),
+ ageAdd: msg.ageAdd,
+ ocspResponse: c.ocspResponse,
+ scts: c.scts,
+ }
+
+ cacheKey := clientSessionCacheKey(c.conn.RemoteAddr(), c.config)
+ c.config.ClientSessionCache.Put(cacheKey, toClientSessionState(session))
+
+ return nil
+}
diff --git a/vendor/github.com/quic-go/qtls-go1-20/handshake_messages.go b/vendor/github.com/quic-go/qtls-go1-20/handshake_messages.go
new file mode 100644
index 0000000000..07193c8efc
--- /dev/null
+++ b/vendor/github.com/quic-go/qtls-go1-20/handshake_messages.go
@@ -0,0 +1,1843 @@
+// Copyright 2009 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package qtls
+
+import (
+ "fmt"
+ "strings"
+
+ "golang.org/x/crypto/cryptobyte"
+)
+
+// The marshalingFunction type is an adapter to allow the use of ordinary
+// functions as cryptobyte.MarshalingValue.
+type marshalingFunction func(b *cryptobyte.Builder) error
+
+func (f marshalingFunction) Marshal(b *cryptobyte.Builder) error {
+ return f(b)
+}
+
+// addBytesWithLength appends a sequence of bytes to the cryptobyte.Builder. If
+// the length of the sequence is not the value specified, it produces an error.
+func addBytesWithLength(b *cryptobyte.Builder, v []byte, n int) {
+ b.AddValue(marshalingFunction(func(b *cryptobyte.Builder) error {
+ if len(v) != n {
+ return fmt.Errorf("invalid value length: expected %d, got %d", n, len(v))
+ }
+ b.AddBytes(v)
+ return nil
+ }))
+}
+
+// addUint64 appends a big-endian, 64-bit value to the cryptobyte.Builder.
+func addUint64(b *cryptobyte.Builder, v uint64) {
+ b.AddUint32(uint32(v >> 32))
+ b.AddUint32(uint32(v))
+}
+
+// readUint64 decodes a big-endian, 64-bit value into out and advances over it.
+// It reports whether the read was successful.
+func readUint64(s *cryptobyte.String, out *uint64) bool {
+ var hi, lo uint32
+ if !s.ReadUint32(&hi) || !s.ReadUint32(&lo) {
+ return false
+ }
+ *out = uint64(hi)<<32 | uint64(lo)
+ return true
+}
+
+// readUint8LengthPrefixed acts like s.ReadUint8LengthPrefixed, but targets a
+// []byte instead of a cryptobyte.String.
+func readUint8LengthPrefixed(s *cryptobyte.String, out *[]byte) bool {
+ return s.ReadUint8LengthPrefixed((*cryptobyte.String)(out))
+}
+
+// readUint16LengthPrefixed acts like s.ReadUint16LengthPrefixed, but targets a
+// []byte instead of a cryptobyte.String.
+func readUint16LengthPrefixed(s *cryptobyte.String, out *[]byte) bool {
+ return s.ReadUint16LengthPrefixed((*cryptobyte.String)(out))
+}
+
+// readUint24LengthPrefixed acts like s.ReadUint24LengthPrefixed, but targets a
+// []byte instead of a cryptobyte.String.
+func readUint24LengthPrefixed(s *cryptobyte.String, out *[]byte) bool {
+ return s.ReadUint24LengthPrefixed((*cryptobyte.String)(out))
+}
+
+type clientHelloMsg struct {
+ raw []byte
+ vers uint16
+ random []byte
+ sessionId []byte
+ cipherSuites []uint16
+ compressionMethods []uint8
+ serverName string
+ ocspStapling bool
+ supportedCurves []CurveID
+ supportedPoints []uint8
+ ticketSupported bool
+ sessionTicket []uint8
+ supportedSignatureAlgorithms []SignatureScheme
+ supportedSignatureAlgorithmsCert []SignatureScheme
+ secureRenegotiationSupported bool
+ secureRenegotiation []byte
+ alpnProtocols []string
+ scts bool
+ supportedVersions []uint16
+ cookie []byte
+ keyShares []keyShare
+ earlyData bool
+ pskModes []uint8
+ pskIdentities []pskIdentity
+ pskBinders [][]byte
+ additionalExtensions []Extension
+}
+
+func (m *clientHelloMsg) marshal() []byte {
+ if m.raw != nil {
+ return m.raw
+ }
+
+ var b cryptobyte.Builder
+ b.AddUint8(typeClientHello)
+ b.AddUint24LengthPrefixed(func(b *cryptobyte.Builder) {
+ b.AddUint16(m.vers)
+ addBytesWithLength(b, m.random, 32)
+ b.AddUint8LengthPrefixed(func(b *cryptobyte.Builder) {
+ b.AddBytes(m.sessionId)
+ })
+ b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
+ for _, suite := range m.cipherSuites {
+ b.AddUint16(suite)
+ }
+ })
+ b.AddUint8LengthPrefixed(func(b *cryptobyte.Builder) {
+ b.AddBytes(m.compressionMethods)
+ })
+
+ // If extensions aren't present, omit them.
+ var extensionsPresent bool
+ bWithoutExtensions := *b
+
+ b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
+ if len(m.serverName) > 0 {
+ // RFC 6066, Section 3
+ b.AddUint16(extensionServerName)
+ b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
+ b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
+ b.AddUint8(0) // name_type = host_name
+ b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
+ b.AddBytes([]byte(m.serverName))
+ })
+ })
+ })
+ }
+ if m.ocspStapling {
+ // RFC 4366, Section 3.6
+ b.AddUint16(extensionStatusRequest)
+ b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
+ b.AddUint8(1) // status_type = ocsp
+ b.AddUint16(0) // empty responder_id_list
+ b.AddUint16(0) // empty request_extensions
+ })
+ }
+ if len(m.supportedCurves) > 0 {
+ // RFC 4492, sections 5.1.1 and RFC 8446, Section 4.2.7
+ b.AddUint16(extensionSupportedCurves)
+ b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
+ b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
+ for _, curve := range m.supportedCurves {
+ b.AddUint16(uint16(curve))
+ }
+ })
+ })
+ }
+ if len(m.supportedPoints) > 0 {
+ // RFC 4492, Section 5.1.2
+ b.AddUint16(extensionSupportedPoints)
+ b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
+ b.AddUint8LengthPrefixed(func(b *cryptobyte.Builder) {
+ b.AddBytes(m.supportedPoints)
+ })
+ })
+ }
+ if m.ticketSupported {
+ // RFC 5077, Section 3.2
+ b.AddUint16(extensionSessionTicket)
+ b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
+ b.AddBytes(m.sessionTicket)
+ })
+ }
+ if len(m.supportedSignatureAlgorithms) > 0 {
+ // RFC 5246, Section 7.4.1.4.1
+ b.AddUint16(extensionSignatureAlgorithms)
+ b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
+ b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
+ for _, sigAlgo := range m.supportedSignatureAlgorithms {
+ b.AddUint16(uint16(sigAlgo))
+ }
+ })
+ })
+ }
+ if len(m.supportedSignatureAlgorithmsCert) > 0 {
+ // RFC 8446, Section 4.2.3
+ b.AddUint16(extensionSignatureAlgorithmsCert)
+ b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
+ b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
+ for _, sigAlgo := range m.supportedSignatureAlgorithmsCert {
+ b.AddUint16(uint16(sigAlgo))
+ }
+ })
+ })
+ }
+ if m.secureRenegotiationSupported {
+ // RFC 5746, Section 3.2
+ b.AddUint16(extensionRenegotiationInfo)
+ b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
+ b.AddUint8LengthPrefixed(func(b *cryptobyte.Builder) {
+ b.AddBytes(m.secureRenegotiation)
+ })
+ })
+ }
+ if len(m.alpnProtocols) > 0 {
+ // RFC 7301, Section 3.1
+ b.AddUint16(extensionALPN)
+ b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
+ b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
+ for _, proto := range m.alpnProtocols {
+ b.AddUint8LengthPrefixed(func(b *cryptobyte.Builder) {
+ b.AddBytes([]byte(proto))
+ })
+ }
+ })
+ })
+ }
+ if m.scts {
+ // RFC 6962, Section 3.3.1
+ b.AddUint16(extensionSCT)
+ b.AddUint16(0) // empty extension_data
+ }
+ if len(m.supportedVersions) > 0 {
+ // RFC 8446, Section 4.2.1
+ b.AddUint16(extensionSupportedVersions)
+ b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
+ b.AddUint8LengthPrefixed(func(b *cryptobyte.Builder) {
+ for _, vers := range m.supportedVersions {
+ b.AddUint16(vers)
+ }
+ })
+ })
+ }
+ if len(m.cookie) > 0 {
+ // RFC 8446, Section 4.2.2
+ b.AddUint16(extensionCookie)
+ b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
+ b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
+ b.AddBytes(m.cookie)
+ })
+ })
+ }
+ if len(m.keyShares) > 0 {
+ // RFC 8446, Section 4.2.8
+ b.AddUint16(extensionKeyShare)
+ b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
+ b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
+ for _, ks := range m.keyShares {
+ b.AddUint16(uint16(ks.group))
+ b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
+ b.AddBytes(ks.data)
+ })
+ }
+ })
+ })
+ }
+ if m.earlyData {
+ // RFC 8446, Section 4.2.10
+ b.AddUint16(extensionEarlyData)
+ b.AddUint16(0) // empty extension_data
+ }
+ if len(m.pskModes) > 0 {
+ // RFC 8446, Section 4.2.9
+ b.AddUint16(extensionPSKModes)
+ b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
+ b.AddUint8LengthPrefixed(func(b *cryptobyte.Builder) {
+ b.AddBytes(m.pskModes)
+ })
+ })
+ }
+ for _, ext := range m.additionalExtensions {
+ b.AddUint16(ext.Type)
+ b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
+ b.AddBytes(ext.Data)
+ })
+ }
+ if len(m.pskIdentities) > 0 { // pre_shared_key must be the last extension
+ // RFC 8446, Section 4.2.11
+ b.AddUint16(extensionPreSharedKey)
+ b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
+ b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
+ for _, psk := range m.pskIdentities {
+ b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
+ b.AddBytes(psk.label)
+ })
+ b.AddUint32(psk.obfuscatedTicketAge)
+ }
+ })
+ b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
+ for _, binder := range m.pskBinders {
+ b.AddUint8LengthPrefixed(func(b *cryptobyte.Builder) {
+ b.AddBytes(binder)
+ })
+ }
+ })
+ })
+ }
+
+ extensionsPresent = len(b.BytesOrPanic()) > 2
+ })
+
+ if !extensionsPresent {
+ *b = bWithoutExtensions
+ }
+ })
+
+ m.raw = b.BytesOrPanic()
+ return m.raw
+}
+
+// marshalWithoutBinders returns the ClientHello through the
+// PreSharedKeyExtension.identities field, according to RFC 8446, Section
+// 4.2.11.2. Note that m.pskBinders must be set to slices of the correct length.
+func (m *clientHelloMsg) marshalWithoutBinders() []byte {
+ bindersLen := 2 // uint16 length prefix
+ for _, binder := range m.pskBinders {
+ bindersLen += 1 // uint8 length prefix
+ bindersLen += len(binder)
+ }
+
+ fullMessage := m.marshal()
+ return fullMessage[:len(fullMessage)-bindersLen]
+}
+
+// updateBinders updates the m.pskBinders field, if necessary updating the
+// cached marshaled representation. The supplied binders must have the same
+// length as the current m.pskBinders.
+func (m *clientHelloMsg) updateBinders(pskBinders [][]byte) {
+ if len(pskBinders) != len(m.pskBinders) {
+ panic("tls: internal error: pskBinders length mismatch")
+ }
+ for i := range m.pskBinders {
+ if len(pskBinders[i]) != len(m.pskBinders[i]) {
+ panic("tls: internal error: pskBinders length mismatch")
+ }
+ }
+ m.pskBinders = pskBinders
+ if m.raw != nil {
+ lenWithoutBinders := len(m.marshalWithoutBinders())
+ b := cryptobyte.NewFixedBuilder(m.raw[:lenWithoutBinders])
+ b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
+ for _, binder := range m.pskBinders {
+ b.AddUint8LengthPrefixed(func(b *cryptobyte.Builder) {
+ b.AddBytes(binder)
+ })
+ }
+ })
+ if out, err := b.Bytes(); err != nil || len(out) != len(m.raw) {
+ panic("tls: internal error: failed to update binders")
+ }
+ }
+}
+
+func (m *clientHelloMsg) unmarshal(data []byte) bool {
+ *m = clientHelloMsg{raw: data}
+ s := cryptobyte.String(data)
+
+ if !s.Skip(4) || // message type and uint24 length field
+ !s.ReadUint16(&m.vers) || !s.ReadBytes(&m.random, 32) ||
+ !readUint8LengthPrefixed(&s, &m.sessionId) {
+ return false
+ }
+
+ var cipherSuites cryptobyte.String
+ if !s.ReadUint16LengthPrefixed(&cipherSuites) {
+ return false
+ }
+ m.cipherSuites = []uint16{}
+ m.secureRenegotiationSupported = false
+ for !cipherSuites.Empty() {
+ var suite uint16
+ if !cipherSuites.ReadUint16(&suite) {
+ return false
+ }
+ if suite == scsvRenegotiation {
+ m.secureRenegotiationSupported = true
+ }
+ m.cipherSuites = append(m.cipherSuites, suite)
+ }
+
+ if !readUint8LengthPrefixed(&s, &m.compressionMethods) {
+ return false
+ }
+
+ if s.Empty() {
+ // ClientHello is optionally followed by extension data
+ return true
+ }
+
+ var extensions cryptobyte.String
+ if !s.ReadUint16LengthPrefixed(&extensions) || !s.Empty() {
+ return false
+ }
+
+ seenExts := make(map[uint16]bool)
+ for !extensions.Empty() {
+ var extension uint16
+ var extData cryptobyte.String
+ if !extensions.ReadUint16(&extension) ||
+ !extensions.ReadUint16LengthPrefixed(&extData) {
+ return false
+ }
+
+ if seenExts[extension] {
+ return false
+ }
+ seenExts[extension] = true
+
+ switch extension {
+ case extensionServerName:
+ // RFC 6066, Section 3
+ var nameList cryptobyte.String
+ if !extData.ReadUint16LengthPrefixed(&nameList) || nameList.Empty() {
+ return false
+ }
+ for !nameList.Empty() {
+ var nameType uint8
+ var serverName cryptobyte.String
+ if !nameList.ReadUint8(&nameType) ||
+ !nameList.ReadUint16LengthPrefixed(&serverName) ||
+ serverName.Empty() {
+ return false
+ }
+ if nameType != 0 {
+ continue
+ }
+ if len(m.serverName) != 0 {
+ // Multiple names of the same name_type are prohibited.
+ return false
+ }
+ m.serverName = string(serverName)
+ // An SNI value may not include a trailing dot.
+ if strings.HasSuffix(m.serverName, ".") {
+ return false
+ }
+ }
+ case extensionStatusRequest:
+ // RFC 4366, Section 3.6
+ var statusType uint8
+ var ignored cryptobyte.String
+ if !extData.ReadUint8(&statusType) ||
+ !extData.ReadUint16LengthPrefixed(&ignored) ||
+ !extData.ReadUint16LengthPrefixed(&ignored) {
+ return false
+ }
+ m.ocspStapling = statusType == statusTypeOCSP
+ case extensionSupportedCurves:
+ // RFC 4492, sections 5.1.1 and RFC 8446, Section 4.2.7
+ var curves cryptobyte.String
+ if !extData.ReadUint16LengthPrefixed(&curves) || curves.Empty() {
+ return false
+ }
+ for !curves.Empty() {
+ var curve uint16
+ if !curves.ReadUint16(&curve) {
+ return false
+ }
+ m.supportedCurves = append(m.supportedCurves, CurveID(curve))
+ }
+ case extensionSupportedPoints:
+ // RFC 4492, Section 5.1.2
+ if !readUint8LengthPrefixed(&extData, &m.supportedPoints) ||
+ len(m.supportedPoints) == 0 {
+ return false
+ }
+ case extensionSessionTicket:
+ // RFC 5077, Section 3.2
+ m.ticketSupported = true
+ extData.ReadBytes(&m.sessionTicket, len(extData))
+ case extensionSignatureAlgorithms:
+ // RFC 5246, Section 7.4.1.4.1
+ var sigAndAlgs cryptobyte.String
+ if !extData.ReadUint16LengthPrefixed(&sigAndAlgs) || sigAndAlgs.Empty() {
+ return false
+ }
+ for !sigAndAlgs.Empty() {
+ var sigAndAlg uint16
+ if !sigAndAlgs.ReadUint16(&sigAndAlg) {
+ return false
+ }
+ m.supportedSignatureAlgorithms = append(
+ m.supportedSignatureAlgorithms, SignatureScheme(sigAndAlg))
+ }
+ case extensionSignatureAlgorithmsCert:
+ // RFC 8446, Section 4.2.3
+ var sigAndAlgs cryptobyte.String
+ if !extData.ReadUint16LengthPrefixed(&sigAndAlgs) || sigAndAlgs.Empty() {
+ return false
+ }
+ for !sigAndAlgs.Empty() {
+ var sigAndAlg uint16
+ if !sigAndAlgs.ReadUint16(&sigAndAlg) {
+ return false
+ }
+ m.supportedSignatureAlgorithmsCert = append(
+ m.supportedSignatureAlgorithmsCert, SignatureScheme(sigAndAlg))
+ }
+ case extensionRenegotiationInfo:
+ // RFC 5746, Section 3.2
+ if !readUint8LengthPrefixed(&extData, &m.secureRenegotiation) {
+ return false
+ }
+ m.secureRenegotiationSupported = true
+ case extensionALPN:
+ // RFC 7301, Section 3.1
+ var protoList cryptobyte.String
+ if !extData.ReadUint16LengthPrefixed(&protoList) || protoList.Empty() {
+ return false
+ }
+ for !protoList.Empty() {
+ var proto cryptobyte.String
+ if !protoList.ReadUint8LengthPrefixed(&proto) || proto.Empty() {
+ return false
+ }
+ m.alpnProtocols = append(m.alpnProtocols, string(proto))
+ }
+ case extensionSCT:
+ // RFC 6962, Section 3.3.1
+ m.scts = true
+ case extensionSupportedVersions:
+ // RFC 8446, Section 4.2.1
+ var versList cryptobyte.String
+ if !extData.ReadUint8LengthPrefixed(&versList) || versList.Empty() {
+ return false
+ }
+ for !versList.Empty() {
+ var vers uint16
+ if !versList.ReadUint16(&vers) {
+ return false
+ }
+ m.supportedVersions = append(m.supportedVersions, vers)
+ }
+ case extensionCookie:
+ // RFC 8446, Section 4.2.2
+ if !readUint16LengthPrefixed(&extData, &m.cookie) ||
+ len(m.cookie) == 0 {
+ return false
+ }
+ case extensionKeyShare:
+ // RFC 8446, Section 4.2.8
+ var clientShares cryptobyte.String
+ if !extData.ReadUint16LengthPrefixed(&clientShares) {
+ return false
+ }
+ for !clientShares.Empty() {
+ var ks keyShare
+ if !clientShares.ReadUint16((*uint16)(&ks.group)) ||
+ !readUint16LengthPrefixed(&clientShares, &ks.data) ||
+ len(ks.data) == 0 {
+ return false
+ }
+ m.keyShares = append(m.keyShares, ks)
+ }
+ case extensionEarlyData:
+ // RFC 8446, Section 4.2.10
+ m.earlyData = true
+ case extensionPSKModes:
+ // RFC 8446, Section 4.2.9
+ if !readUint8LengthPrefixed(&extData, &m.pskModes) {
+ return false
+ }
+ case extensionPreSharedKey:
+ // RFC 8446, Section 4.2.11
+ if !extensions.Empty() {
+ return false // pre_shared_key must be the last extension
+ }
+ var identities cryptobyte.String
+ if !extData.ReadUint16LengthPrefixed(&identities) || identities.Empty() {
+ return false
+ }
+ for !identities.Empty() {
+ var psk pskIdentity
+ if !readUint16LengthPrefixed(&identities, &psk.label) ||
+ !identities.ReadUint32(&psk.obfuscatedTicketAge) ||
+ len(psk.label) == 0 {
+ return false
+ }
+ m.pskIdentities = append(m.pskIdentities, psk)
+ }
+ var binders cryptobyte.String
+ if !extData.ReadUint16LengthPrefixed(&binders) || binders.Empty() {
+ return false
+ }
+ for !binders.Empty() {
+ var binder []byte
+ if !readUint8LengthPrefixed(&binders, &binder) ||
+ len(binder) == 0 {
+ return false
+ }
+ m.pskBinders = append(m.pskBinders, binder)
+ }
+ default:
+ m.additionalExtensions = append(m.additionalExtensions, Extension{Type: extension, Data: extData})
+ continue
+ }
+
+ if !extData.Empty() {
+ return false
+ }
+ }
+
+ return true
+}
+
+type serverHelloMsg struct {
+ raw []byte
+ vers uint16
+ random []byte
+ sessionId []byte
+ cipherSuite uint16
+ compressionMethod uint8
+ ocspStapling bool
+ ticketSupported bool
+ secureRenegotiationSupported bool
+ secureRenegotiation []byte
+ alpnProtocol string
+ scts [][]byte
+ supportedVersion uint16
+ serverShare keyShare
+ selectedIdentityPresent bool
+ selectedIdentity uint16
+ supportedPoints []uint8
+
+ // HelloRetryRequest extensions
+ cookie []byte
+ selectedGroup CurveID
+}
+
+func (m *serverHelloMsg) marshal() []byte {
+ if m.raw != nil {
+ return m.raw
+ }
+
+ var b cryptobyte.Builder
+ b.AddUint8(typeServerHello)
+ b.AddUint24LengthPrefixed(func(b *cryptobyte.Builder) {
+ b.AddUint16(m.vers)
+ addBytesWithLength(b, m.random, 32)
+ b.AddUint8LengthPrefixed(func(b *cryptobyte.Builder) {
+ b.AddBytes(m.sessionId)
+ })
+ b.AddUint16(m.cipherSuite)
+ b.AddUint8(m.compressionMethod)
+
+ // If extensions aren't present, omit them.
+ var extensionsPresent bool
+ bWithoutExtensions := *b
+
+ b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
+ if m.ocspStapling {
+ b.AddUint16(extensionStatusRequest)
+ b.AddUint16(0) // empty extension_data
+ }
+ if m.ticketSupported {
+ b.AddUint16(extensionSessionTicket)
+ b.AddUint16(0) // empty extension_data
+ }
+ if m.secureRenegotiationSupported {
+ b.AddUint16(extensionRenegotiationInfo)
+ b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
+ b.AddUint8LengthPrefixed(func(b *cryptobyte.Builder) {
+ b.AddBytes(m.secureRenegotiation)
+ })
+ })
+ }
+ if len(m.alpnProtocol) > 0 {
+ b.AddUint16(extensionALPN)
+ b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
+ b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
+ b.AddUint8LengthPrefixed(func(b *cryptobyte.Builder) {
+ b.AddBytes([]byte(m.alpnProtocol))
+ })
+ })
+ })
+ }
+ if len(m.scts) > 0 {
+ b.AddUint16(extensionSCT)
+ b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
+ b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
+ for _, sct := range m.scts {
+ b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
+ b.AddBytes(sct)
+ })
+ }
+ })
+ })
+ }
+ if m.supportedVersion != 0 {
+ b.AddUint16(extensionSupportedVersions)
+ b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
+ b.AddUint16(m.supportedVersion)
+ })
+ }
+ if m.serverShare.group != 0 {
+ b.AddUint16(extensionKeyShare)
+ b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
+ b.AddUint16(uint16(m.serverShare.group))
+ b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
+ b.AddBytes(m.serverShare.data)
+ })
+ })
+ }
+ if m.selectedIdentityPresent {
+ b.AddUint16(extensionPreSharedKey)
+ b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
+ b.AddUint16(m.selectedIdentity)
+ })
+ }
+
+ if len(m.cookie) > 0 {
+ b.AddUint16(extensionCookie)
+ b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
+ b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
+ b.AddBytes(m.cookie)
+ })
+ })
+ }
+ if m.selectedGroup != 0 {
+ b.AddUint16(extensionKeyShare)
+ b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
+ b.AddUint16(uint16(m.selectedGroup))
+ })
+ }
+ if len(m.supportedPoints) > 0 {
+ b.AddUint16(extensionSupportedPoints)
+ b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
+ b.AddUint8LengthPrefixed(func(b *cryptobyte.Builder) {
+ b.AddBytes(m.supportedPoints)
+ })
+ })
+ }
+
+ extensionsPresent = len(b.BytesOrPanic()) > 2
+ })
+
+ if !extensionsPresent {
+ *b = bWithoutExtensions
+ }
+ })
+
+ m.raw = b.BytesOrPanic()
+ return m.raw
+}
+
+func (m *serverHelloMsg) unmarshal(data []byte) bool {
+ *m = serverHelloMsg{raw: data}
+ s := cryptobyte.String(data)
+
+ if !s.Skip(4) || // message type and uint24 length field
+ !s.ReadUint16(&m.vers) || !s.ReadBytes(&m.random, 32) ||
+ !readUint8LengthPrefixed(&s, &m.sessionId) ||
+ !s.ReadUint16(&m.cipherSuite) ||
+ !s.ReadUint8(&m.compressionMethod) {
+ return false
+ }
+
+ if s.Empty() {
+ // ServerHello is optionally followed by extension data
+ return true
+ }
+
+ var extensions cryptobyte.String
+ if !s.ReadUint16LengthPrefixed(&extensions) || !s.Empty() {
+ return false
+ }
+
+ seenExts := make(map[uint16]bool)
+ for !extensions.Empty() {
+ var extension uint16
+ var extData cryptobyte.String
+ if !extensions.ReadUint16(&extension) ||
+ !extensions.ReadUint16LengthPrefixed(&extData) {
+ return false
+ }
+
+ if seenExts[extension] {
+ return false
+ }
+ seenExts[extension] = true
+
+ switch extension {
+ case extensionStatusRequest:
+ m.ocspStapling = true
+ case extensionSessionTicket:
+ m.ticketSupported = true
+ case extensionRenegotiationInfo:
+ if !readUint8LengthPrefixed(&extData, &m.secureRenegotiation) {
+ return false
+ }
+ m.secureRenegotiationSupported = true
+ case extensionALPN:
+ var protoList cryptobyte.String
+ if !extData.ReadUint16LengthPrefixed(&protoList) || protoList.Empty() {
+ return false
+ }
+ var proto cryptobyte.String
+ if !protoList.ReadUint8LengthPrefixed(&proto) ||
+ proto.Empty() || !protoList.Empty() {
+ return false
+ }
+ m.alpnProtocol = string(proto)
+ case extensionSCT:
+ var sctList cryptobyte.String
+ if !extData.ReadUint16LengthPrefixed(&sctList) || sctList.Empty() {
+ return false
+ }
+ for !sctList.Empty() {
+ var sct []byte
+ if !readUint16LengthPrefixed(&sctList, &sct) ||
+ len(sct) == 0 {
+ return false
+ }
+ m.scts = append(m.scts, sct)
+ }
+ case extensionSupportedVersions:
+ if !extData.ReadUint16(&m.supportedVersion) {
+ return false
+ }
+ case extensionCookie:
+ if !readUint16LengthPrefixed(&extData, &m.cookie) ||
+ len(m.cookie) == 0 {
+ return false
+ }
+ case extensionKeyShare:
+ // This extension has different formats in SH and HRR, accept either
+ // and let the handshake logic decide. See RFC 8446, Section 4.2.8.
+ if len(extData) == 2 {
+ if !extData.ReadUint16((*uint16)(&m.selectedGroup)) {
+ return false
+ }
+ } else {
+ if !extData.ReadUint16((*uint16)(&m.serverShare.group)) ||
+ !readUint16LengthPrefixed(&extData, &m.serverShare.data) {
+ return false
+ }
+ }
+ case extensionPreSharedKey:
+ m.selectedIdentityPresent = true
+ if !extData.ReadUint16(&m.selectedIdentity) {
+ return false
+ }
+ case extensionSupportedPoints:
+ // RFC 4492, Section 5.1.2
+ if !readUint8LengthPrefixed(&extData, &m.supportedPoints) ||
+ len(m.supportedPoints) == 0 {
+ return false
+ }
+ default:
+ // Ignore unknown extensions.
+ continue
+ }
+
+ if !extData.Empty() {
+ return false
+ }
+ }
+
+ return true
+}
+
+type encryptedExtensionsMsg struct {
+ raw []byte
+ alpnProtocol string
+ earlyData bool
+
+ additionalExtensions []Extension
+}
+
+func (m *encryptedExtensionsMsg) marshal() []byte {
+ if m.raw != nil {
+ return m.raw
+ }
+
+ var b cryptobyte.Builder
+ b.AddUint8(typeEncryptedExtensions)
+ b.AddUint24LengthPrefixed(func(b *cryptobyte.Builder) {
+ b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
+ if len(m.alpnProtocol) > 0 {
+ b.AddUint16(extensionALPN)
+ b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
+ b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
+ b.AddUint8LengthPrefixed(func(b *cryptobyte.Builder) {
+ b.AddBytes([]byte(m.alpnProtocol))
+ })
+ })
+ })
+ }
+ if m.earlyData {
+ // RFC 8446, Section 4.2.10
+ b.AddUint16(extensionEarlyData)
+ b.AddUint16(0) // empty extension_data
+ }
+ for _, ext := range m.additionalExtensions {
+ b.AddUint16(ext.Type)
+ b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
+ b.AddBytes(ext.Data)
+ })
+ }
+ })
+ })
+
+ m.raw = b.BytesOrPanic()
+ return m.raw
+}
+
+func (m *encryptedExtensionsMsg) unmarshal(data []byte) bool {
+ *m = encryptedExtensionsMsg{raw: data}
+ s := cryptobyte.String(data)
+
+ var extensions cryptobyte.String
+ if !s.Skip(4) || // message type and uint24 length field
+ !s.ReadUint16LengthPrefixed(&extensions) || !s.Empty() {
+ return false
+ }
+
+ for !extensions.Empty() {
+ var ext uint16
+ var extData cryptobyte.String
+ if !extensions.ReadUint16(&ext) ||
+ !extensions.ReadUint16LengthPrefixed(&extData) {
+ return false
+ }
+
+ switch ext {
+ case extensionALPN:
+ var protoList cryptobyte.String
+ if !extData.ReadUint16LengthPrefixed(&protoList) || protoList.Empty() {
+ return false
+ }
+ var proto cryptobyte.String
+ if !protoList.ReadUint8LengthPrefixed(&proto) ||
+ proto.Empty() || !protoList.Empty() {
+ return false
+ }
+ m.alpnProtocol = string(proto)
+ case extensionEarlyData:
+ m.earlyData = true
+ default:
+ m.additionalExtensions = append(m.additionalExtensions, Extension{Type: ext, Data: extData})
+ continue
+ }
+
+ if !extData.Empty() {
+ return false
+ }
+ }
+
+ return true
+}
+
+type endOfEarlyDataMsg struct{}
+
+func (m *endOfEarlyDataMsg) marshal() []byte {
+ x := make([]byte, 4)
+ x[0] = typeEndOfEarlyData
+ return x
+}
+
+func (m *endOfEarlyDataMsg) unmarshal(data []byte) bool {
+ return len(data) == 4
+}
+
+type keyUpdateMsg struct {
+ raw []byte
+ updateRequested bool
+}
+
+func (m *keyUpdateMsg) marshal() []byte {
+ if m.raw != nil {
+ return m.raw
+ }
+
+ var b cryptobyte.Builder
+ b.AddUint8(typeKeyUpdate)
+ b.AddUint24LengthPrefixed(func(b *cryptobyte.Builder) {
+ if m.updateRequested {
+ b.AddUint8(1)
+ } else {
+ b.AddUint8(0)
+ }
+ })
+
+ m.raw = b.BytesOrPanic()
+ return m.raw
+}
+
+func (m *keyUpdateMsg) unmarshal(data []byte) bool {
+ m.raw = data
+ s := cryptobyte.String(data)
+
+ var updateRequested uint8
+ if !s.Skip(4) || // message type and uint24 length field
+ !s.ReadUint8(&updateRequested) || !s.Empty() {
+ return false
+ }
+ switch updateRequested {
+ case 0:
+ m.updateRequested = false
+ case 1:
+ m.updateRequested = true
+ default:
+ return false
+ }
+ return true
+}
+
+type newSessionTicketMsgTLS13 struct {
+ raw []byte
+ lifetime uint32
+ ageAdd uint32
+ nonce []byte
+ label []byte
+ maxEarlyData uint32
+}
+
+func (m *newSessionTicketMsgTLS13) marshal() []byte {
+ if m.raw != nil {
+ return m.raw
+ }
+
+ var b cryptobyte.Builder
+ b.AddUint8(typeNewSessionTicket)
+ b.AddUint24LengthPrefixed(func(b *cryptobyte.Builder) {
+ b.AddUint32(m.lifetime)
+ b.AddUint32(m.ageAdd)
+ b.AddUint8LengthPrefixed(func(b *cryptobyte.Builder) {
+ b.AddBytes(m.nonce)
+ })
+ b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
+ b.AddBytes(m.label)
+ })
+
+ b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
+ if m.maxEarlyData > 0 {
+ b.AddUint16(extensionEarlyData)
+ b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
+ b.AddUint32(m.maxEarlyData)
+ })
+ }
+ })
+ })
+
+ m.raw = b.BytesOrPanic()
+ return m.raw
+}
+
+func (m *newSessionTicketMsgTLS13) unmarshal(data []byte) bool {
+ *m = newSessionTicketMsgTLS13{raw: data}
+ s := cryptobyte.String(data)
+
+ var extensions cryptobyte.String
+ if !s.Skip(4) || // message type and uint24 length field
+ !s.ReadUint32(&m.lifetime) ||
+ !s.ReadUint32(&m.ageAdd) ||
+ !readUint8LengthPrefixed(&s, &m.nonce) ||
+ !readUint16LengthPrefixed(&s, &m.label) ||
+ !s.ReadUint16LengthPrefixed(&extensions) ||
+ !s.Empty() {
+ return false
+ }
+
+ for !extensions.Empty() {
+ var extension uint16
+ var extData cryptobyte.String
+ if !extensions.ReadUint16(&extension) ||
+ !extensions.ReadUint16LengthPrefixed(&extData) {
+ return false
+ }
+
+ switch extension {
+ case extensionEarlyData:
+ if !extData.ReadUint32(&m.maxEarlyData) {
+ return false
+ }
+ default:
+ // Ignore unknown extensions.
+ continue
+ }
+
+ if !extData.Empty() {
+ return false
+ }
+ }
+
+ return true
+}
+
+type certificateRequestMsgTLS13 struct {
+ raw []byte
+ ocspStapling bool
+ scts bool
+ supportedSignatureAlgorithms []SignatureScheme
+ supportedSignatureAlgorithmsCert []SignatureScheme
+ certificateAuthorities [][]byte
+}
+
+func (m *certificateRequestMsgTLS13) marshal() []byte {
+ if m.raw != nil {
+ return m.raw
+ }
+
+ var b cryptobyte.Builder
+ b.AddUint8(typeCertificateRequest)
+ b.AddUint24LengthPrefixed(func(b *cryptobyte.Builder) {
+ // certificate_request_context (SHALL be zero length unless used for
+ // post-handshake authentication)
+ b.AddUint8(0)
+
+ b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
+ if m.ocspStapling {
+ b.AddUint16(extensionStatusRequest)
+ b.AddUint16(0) // empty extension_data
+ }
+ if m.scts {
+ // RFC 8446, Section 4.4.2.1 makes no mention of
+ // signed_certificate_timestamp in CertificateRequest, but
+ // "Extensions in the Certificate message from the client MUST
+ // correspond to extensions in the CertificateRequest message
+ // from the server." and it appears in the table in Section 4.2.
+ b.AddUint16(extensionSCT)
+ b.AddUint16(0) // empty extension_data
+ }
+ if len(m.supportedSignatureAlgorithms) > 0 {
+ b.AddUint16(extensionSignatureAlgorithms)
+ b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
+ b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
+ for _, sigAlgo := range m.supportedSignatureAlgorithms {
+ b.AddUint16(uint16(sigAlgo))
+ }
+ })
+ })
+ }
+ if len(m.supportedSignatureAlgorithmsCert) > 0 {
+ b.AddUint16(extensionSignatureAlgorithmsCert)
+ b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
+ b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
+ for _, sigAlgo := range m.supportedSignatureAlgorithmsCert {
+ b.AddUint16(uint16(sigAlgo))
+ }
+ })
+ })
+ }
+ if len(m.certificateAuthorities) > 0 {
+ b.AddUint16(extensionCertificateAuthorities)
+ b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
+ b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
+ for _, ca := range m.certificateAuthorities {
+ b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
+ b.AddBytes(ca)
+ })
+ }
+ })
+ })
+ }
+ })
+ })
+
+ m.raw = b.BytesOrPanic()
+ return m.raw
+}
+
+func (m *certificateRequestMsgTLS13) unmarshal(data []byte) bool {
+ *m = certificateRequestMsgTLS13{raw: data}
+ s := cryptobyte.String(data)
+
+ var context, extensions cryptobyte.String
+ if !s.Skip(4) || // message type and uint24 length field
+ !s.ReadUint8LengthPrefixed(&context) || !context.Empty() ||
+ !s.ReadUint16LengthPrefixed(&extensions) ||
+ !s.Empty() {
+ return false
+ }
+
+ for !extensions.Empty() {
+ var extension uint16
+ var extData cryptobyte.String
+ if !extensions.ReadUint16(&extension) ||
+ !extensions.ReadUint16LengthPrefixed(&extData) {
+ return false
+ }
+
+ switch extension {
+ case extensionStatusRequest:
+ m.ocspStapling = true
+ case extensionSCT:
+ m.scts = true
+ case extensionSignatureAlgorithms:
+ var sigAndAlgs cryptobyte.String
+ if !extData.ReadUint16LengthPrefixed(&sigAndAlgs) || sigAndAlgs.Empty() {
+ return false
+ }
+ for !sigAndAlgs.Empty() {
+ var sigAndAlg uint16
+ if !sigAndAlgs.ReadUint16(&sigAndAlg) {
+ return false
+ }
+ m.supportedSignatureAlgorithms = append(
+ m.supportedSignatureAlgorithms, SignatureScheme(sigAndAlg))
+ }
+ case extensionSignatureAlgorithmsCert:
+ var sigAndAlgs cryptobyte.String
+ if !extData.ReadUint16LengthPrefixed(&sigAndAlgs) || sigAndAlgs.Empty() {
+ return false
+ }
+ for !sigAndAlgs.Empty() {
+ var sigAndAlg uint16
+ if !sigAndAlgs.ReadUint16(&sigAndAlg) {
+ return false
+ }
+ m.supportedSignatureAlgorithmsCert = append(
+ m.supportedSignatureAlgorithmsCert, SignatureScheme(sigAndAlg))
+ }
+ case extensionCertificateAuthorities:
+ var auths cryptobyte.String
+ if !extData.ReadUint16LengthPrefixed(&auths) || auths.Empty() {
+ return false
+ }
+ for !auths.Empty() {
+ var ca []byte
+ if !readUint16LengthPrefixed(&auths, &ca) || len(ca) == 0 {
+ return false
+ }
+ m.certificateAuthorities = append(m.certificateAuthorities, ca)
+ }
+ default:
+ // Ignore unknown extensions.
+ continue
+ }
+
+ if !extData.Empty() {
+ return false
+ }
+ }
+
+ return true
+}
+
+type certificateMsg struct {
+ raw []byte
+ certificates [][]byte
+}
+
+func (m *certificateMsg) marshal() (x []byte) {
+ if m.raw != nil {
+ return m.raw
+ }
+
+ var i int
+ for _, slice := range m.certificates {
+ i += len(slice)
+ }
+
+ length := 3 + 3*len(m.certificates) + i
+ x = make([]byte, 4+length)
+ x[0] = typeCertificate
+ x[1] = uint8(length >> 16)
+ x[2] = uint8(length >> 8)
+ x[3] = uint8(length)
+
+ certificateOctets := length - 3
+ x[4] = uint8(certificateOctets >> 16)
+ x[5] = uint8(certificateOctets >> 8)
+ x[6] = uint8(certificateOctets)
+
+ y := x[7:]
+ for _, slice := range m.certificates {
+ y[0] = uint8(len(slice) >> 16)
+ y[1] = uint8(len(slice) >> 8)
+ y[2] = uint8(len(slice))
+ copy(y[3:], slice)
+ y = y[3+len(slice):]
+ }
+
+ m.raw = x
+ return
+}
+
+func (m *certificateMsg) unmarshal(data []byte) bool {
+ if len(data) < 7 {
+ return false
+ }
+
+ m.raw = data
+ certsLen := uint32(data[4])<<16 | uint32(data[5])<<8 | uint32(data[6])
+ if uint32(len(data)) != certsLen+7 {
+ return false
+ }
+
+ numCerts := 0
+ d := data[7:]
+ for certsLen > 0 {
+ if len(d) < 4 {
+ return false
+ }
+ certLen := uint32(d[0])<<16 | uint32(d[1])<<8 | uint32(d[2])
+ if uint32(len(d)) < 3+certLen {
+ return false
+ }
+ d = d[3+certLen:]
+ certsLen -= 3 + certLen
+ numCerts++
+ }
+
+ m.certificates = make([][]byte, numCerts)
+ d = data[7:]
+ for i := 0; i < numCerts; i++ {
+ certLen := uint32(d[0])<<16 | uint32(d[1])<<8 | uint32(d[2])
+ m.certificates[i] = d[3 : 3+certLen]
+ d = d[3+certLen:]
+ }
+
+ return true
+}
+
+type certificateMsgTLS13 struct {
+ raw []byte
+ certificate Certificate
+ ocspStapling bool
+ scts bool
+}
+
+func (m *certificateMsgTLS13) marshal() []byte {
+ if m.raw != nil {
+ return m.raw
+ }
+
+ var b cryptobyte.Builder
+ b.AddUint8(typeCertificate)
+ b.AddUint24LengthPrefixed(func(b *cryptobyte.Builder) {
+ b.AddUint8(0) // certificate_request_context
+
+ certificate := m.certificate
+ if !m.ocspStapling {
+ certificate.OCSPStaple = nil
+ }
+ if !m.scts {
+ certificate.SignedCertificateTimestamps = nil
+ }
+ marshalCertificate(b, certificate)
+ })
+
+ m.raw = b.BytesOrPanic()
+ return m.raw
+}
+
+func marshalCertificate(b *cryptobyte.Builder, certificate Certificate) {
+ b.AddUint24LengthPrefixed(func(b *cryptobyte.Builder) {
+ for i, cert := range certificate.Certificate {
+ b.AddUint24LengthPrefixed(func(b *cryptobyte.Builder) {
+ b.AddBytes(cert)
+ })
+ b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
+ if i > 0 {
+ // This library only supports OCSP and SCT for leaf certificates.
+ return
+ }
+ if certificate.OCSPStaple != nil {
+ b.AddUint16(extensionStatusRequest)
+ b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
+ b.AddUint8(statusTypeOCSP)
+ b.AddUint24LengthPrefixed(func(b *cryptobyte.Builder) {
+ b.AddBytes(certificate.OCSPStaple)
+ })
+ })
+ }
+ if certificate.SignedCertificateTimestamps != nil {
+ b.AddUint16(extensionSCT)
+ b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
+ b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
+ for _, sct := range certificate.SignedCertificateTimestamps {
+ b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
+ b.AddBytes(sct)
+ })
+ }
+ })
+ })
+ }
+ })
+ }
+ })
+}
+
+func (m *certificateMsgTLS13) unmarshal(data []byte) bool {
+ *m = certificateMsgTLS13{raw: data}
+ s := cryptobyte.String(data)
+
+ var context cryptobyte.String
+ if !s.Skip(4) || // message type and uint24 length field
+ !s.ReadUint8LengthPrefixed(&context) || !context.Empty() ||
+ !unmarshalCertificate(&s, &m.certificate) ||
+ !s.Empty() {
+ return false
+ }
+
+ m.scts = m.certificate.SignedCertificateTimestamps != nil
+ m.ocspStapling = m.certificate.OCSPStaple != nil
+
+ return true
+}
+
+func unmarshalCertificate(s *cryptobyte.String, certificate *Certificate) bool {
+ var certList cryptobyte.String
+ if !s.ReadUint24LengthPrefixed(&certList) {
+ return false
+ }
+ for !certList.Empty() {
+ var cert []byte
+ var extensions cryptobyte.String
+ if !readUint24LengthPrefixed(&certList, &cert) ||
+ !certList.ReadUint16LengthPrefixed(&extensions) {
+ return false
+ }
+ certificate.Certificate = append(certificate.Certificate, cert)
+ for !extensions.Empty() {
+ var extension uint16
+ var extData cryptobyte.String
+ if !extensions.ReadUint16(&extension) ||
+ !extensions.ReadUint16LengthPrefixed(&extData) {
+ return false
+ }
+ if len(certificate.Certificate) > 1 {
+ // This library only supports OCSP and SCT for leaf certificates.
+ continue
+ }
+
+ switch extension {
+ case extensionStatusRequest:
+ var statusType uint8
+ if !extData.ReadUint8(&statusType) || statusType != statusTypeOCSP ||
+ !readUint24LengthPrefixed(&extData, &certificate.OCSPStaple) ||
+ len(certificate.OCSPStaple) == 0 {
+ return false
+ }
+ case extensionSCT:
+ var sctList cryptobyte.String
+ if !extData.ReadUint16LengthPrefixed(&sctList) || sctList.Empty() {
+ return false
+ }
+ for !sctList.Empty() {
+ var sct []byte
+ if !readUint16LengthPrefixed(&sctList, &sct) ||
+ len(sct) == 0 {
+ return false
+ }
+ certificate.SignedCertificateTimestamps = append(
+ certificate.SignedCertificateTimestamps, sct)
+ }
+ default:
+ // Ignore unknown extensions.
+ continue
+ }
+
+ if !extData.Empty() {
+ return false
+ }
+ }
+ }
+ return true
+}
+
+type serverKeyExchangeMsg struct {
+ raw []byte
+ key []byte
+}
+
+func (m *serverKeyExchangeMsg) marshal() []byte {
+ if m.raw != nil {
+ return m.raw
+ }
+ length := len(m.key)
+ x := make([]byte, length+4)
+ x[0] = typeServerKeyExchange
+ x[1] = uint8(length >> 16)
+ x[2] = uint8(length >> 8)
+ x[3] = uint8(length)
+ copy(x[4:], m.key)
+
+ m.raw = x
+ return x
+}
+
+func (m *serverKeyExchangeMsg) unmarshal(data []byte) bool {
+ m.raw = data
+ if len(data) < 4 {
+ return false
+ }
+ m.key = data[4:]
+ return true
+}
+
+type certificateStatusMsg struct {
+ raw []byte
+ response []byte
+}
+
+func (m *certificateStatusMsg) marshal() []byte {
+ if m.raw != nil {
+ return m.raw
+ }
+
+ var b cryptobyte.Builder
+ b.AddUint8(typeCertificateStatus)
+ b.AddUint24LengthPrefixed(func(b *cryptobyte.Builder) {
+ b.AddUint8(statusTypeOCSP)
+ b.AddUint24LengthPrefixed(func(b *cryptobyte.Builder) {
+ b.AddBytes(m.response)
+ })
+ })
+
+ m.raw = b.BytesOrPanic()
+ return m.raw
+}
+
+func (m *certificateStatusMsg) unmarshal(data []byte) bool {
+ m.raw = data
+ s := cryptobyte.String(data)
+
+ var statusType uint8
+ if !s.Skip(4) || // message type and uint24 length field
+ !s.ReadUint8(&statusType) || statusType != statusTypeOCSP ||
+ !readUint24LengthPrefixed(&s, &m.response) ||
+ len(m.response) == 0 || !s.Empty() {
+ return false
+ }
+ return true
+}
+
+type serverHelloDoneMsg struct{}
+
+func (m *serverHelloDoneMsg) marshal() []byte {
+ x := make([]byte, 4)
+ x[0] = typeServerHelloDone
+ return x
+}
+
+func (m *serverHelloDoneMsg) unmarshal(data []byte) bool {
+ return len(data) == 4
+}
+
+type clientKeyExchangeMsg struct {
+ raw []byte
+ ciphertext []byte
+}
+
+func (m *clientKeyExchangeMsg) marshal() []byte {
+ if m.raw != nil {
+ return m.raw
+ }
+ length := len(m.ciphertext)
+ x := make([]byte, length+4)
+ x[0] = typeClientKeyExchange
+ x[1] = uint8(length >> 16)
+ x[2] = uint8(length >> 8)
+ x[3] = uint8(length)
+ copy(x[4:], m.ciphertext)
+
+ m.raw = x
+ return x
+}
+
+func (m *clientKeyExchangeMsg) unmarshal(data []byte) bool {
+ m.raw = data
+ if len(data) < 4 {
+ return false
+ }
+ l := int(data[1])<<16 | int(data[2])<<8 | int(data[3])
+ if l != len(data)-4 {
+ return false
+ }
+ m.ciphertext = data[4:]
+ return true
+}
+
+type finishedMsg struct {
+ raw []byte
+ verifyData []byte
+}
+
+func (m *finishedMsg) marshal() []byte {
+ if m.raw != nil {
+ return m.raw
+ }
+
+ var b cryptobyte.Builder
+ b.AddUint8(typeFinished)
+ b.AddUint24LengthPrefixed(func(b *cryptobyte.Builder) {
+ b.AddBytes(m.verifyData)
+ })
+
+ m.raw = b.BytesOrPanic()
+ return m.raw
+}
+
+func (m *finishedMsg) unmarshal(data []byte) bool {
+ m.raw = data
+ s := cryptobyte.String(data)
+ return s.Skip(1) &&
+ readUint24LengthPrefixed(&s, &m.verifyData) &&
+ s.Empty()
+}
+
+type certificateRequestMsg struct {
+ raw []byte
+ // hasSignatureAlgorithm indicates whether this message includes a list of
+ // supported signature algorithms. This change was introduced with TLS 1.2.
+ hasSignatureAlgorithm bool
+
+ certificateTypes []byte
+ supportedSignatureAlgorithms []SignatureScheme
+ certificateAuthorities [][]byte
+}
+
+func (m *certificateRequestMsg) marshal() (x []byte) {
+ if m.raw != nil {
+ return m.raw
+ }
+
+ // See RFC 4346, Section 7.4.4.
+ length := 1 + len(m.certificateTypes) + 2
+ casLength := 0
+ for _, ca := range m.certificateAuthorities {
+ casLength += 2 + len(ca)
+ }
+ length += casLength
+
+ if m.hasSignatureAlgorithm {
+ length += 2 + 2*len(m.supportedSignatureAlgorithms)
+ }
+
+ x = make([]byte, 4+length)
+ x[0] = typeCertificateRequest
+ x[1] = uint8(length >> 16)
+ x[2] = uint8(length >> 8)
+ x[3] = uint8(length)
+
+ x[4] = uint8(len(m.certificateTypes))
+
+ copy(x[5:], m.certificateTypes)
+ y := x[5+len(m.certificateTypes):]
+
+ if m.hasSignatureAlgorithm {
+ n := len(m.supportedSignatureAlgorithms) * 2
+ y[0] = uint8(n >> 8)
+ y[1] = uint8(n)
+ y = y[2:]
+ for _, sigAlgo := range m.supportedSignatureAlgorithms {
+ y[0] = uint8(sigAlgo >> 8)
+ y[1] = uint8(sigAlgo)
+ y = y[2:]
+ }
+ }
+
+ y[0] = uint8(casLength >> 8)
+ y[1] = uint8(casLength)
+ y = y[2:]
+ for _, ca := range m.certificateAuthorities {
+ y[0] = uint8(len(ca) >> 8)
+ y[1] = uint8(len(ca))
+ y = y[2:]
+ copy(y, ca)
+ y = y[len(ca):]
+ }
+
+ m.raw = x
+ return
+}
+
+func (m *certificateRequestMsg) unmarshal(data []byte) bool {
+ m.raw = data
+
+ if len(data) < 5 {
+ return false
+ }
+
+ length := uint32(data[1])<<16 | uint32(data[2])<<8 | uint32(data[3])
+ if uint32(len(data))-4 != length {
+ return false
+ }
+
+ numCertTypes := int(data[4])
+ data = data[5:]
+ if numCertTypes == 0 || len(data) <= numCertTypes {
+ return false
+ }
+
+ m.certificateTypes = make([]byte, numCertTypes)
+ if copy(m.certificateTypes, data) != numCertTypes {
+ return false
+ }
+
+ data = data[numCertTypes:]
+
+ if m.hasSignatureAlgorithm {
+ if len(data) < 2 {
+ return false
+ }
+ sigAndHashLen := uint16(data[0])<<8 | uint16(data[1])
+ data = data[2:]
+ if sigAndHashLen&1 != 0 {
+ return false
+ }
+ if len(data) < int(sigAndHashLen) {
+ return false
+ }
+ numSigAlgos := sigAndHashLen / 2
+ m.supportedSignatureAlgorithms = make([]SignatureScheme, numSigAlgos)
+ for i := range m.supportedSignatureAlgorithms {
+ m.supportedSignatureAlgorithms[i] = SignatureScheme(data[0])<<8 | SignatureScheme(data[1])
+ data = data[2:]
+ }
+ }
+
+ if len(data) < 2 {
+ return false
+ }
+ casLength := uint16(data[0])<<8 | uint16(data[1])
+ data = data[2:]
+ if len(data) < int(casLength) {
+ return false
+ }
+ cas := make([]byte, casLength)
+ copy(cas, data)
+ data = data[casLength:]
+
+ m.certificateAuthorities = nil
+ for len(cas) > 0 {
+ if len(cas) < 2 {
+ return false
+ }
+ caLen := uint16(cas[0])<<8 | uint16(cas[1])
+ cas = cas[2:]
+
+ if len(cas) < int(caLen) {
+ return false
+ }
+
+ m.certificateAuthorities = append(m.certificateAuthorities, cas[:caLen])
+ cas = cas[caLen:]
+ }
+
+ return len(data) == 0
+}
+
+type certificateVerifyMsg struct {
+ raw []byte
+ hasSignatureAlgorithm bool // format change introduced in TLS 1.2
+ signatureAlgorithm SignatureScheme
+ signature []byte
+}
+
+func (m *certificateVerifyMsg) marshal() (x []byte) {
+ if m.raw != nil {
+ return m.raw
+ }
+
+ var b cryptobyte.Builder
+ b.AddUint8(typeCertificateVerify)
+ b.AddUint24LengthPrefixed(func(b *cryptobyte.Builder) {
+ if m.hasSignatureAlgorithm {
+ b.AddUint16(uint16(m.signatureAlgorithm))
+ }
+ b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
+ b.AddBytes(m.signature)
+ })
+ })
+
+ m.raw = b.BytesOrPanic()
+ return m.raw
+}
+
+func (m *certificateVerifyMsg) unmarshal(data []byte) bool {
+ m.raw = data
+ s := cryptobyte.String(data)
+
+ if !s.Skip(4) { // message type and uint24 length field
+ return false
+ }
+ if m.hasSignatureAlgorithm {
+ if !s.ReadUint16((*uint16)(&m.signatureAlgorithm)) {
+ return false
+ }
+ }
+ return readUint16LengthPrefixed(&s, &m.signature) && s.Empty()
+}
+
+type newSessionTicketMsg struct {
+ raw []byte
+ ticket []byte
+}
+
+func (m *newSessionTicketMsg) marshal() (x []byte) {
+ if m.raw != nil {
+ return m.raw
+ }
+
+ // See RFC 5077, Section 3.3.
+ ticketLen := len(m.ticket)
+ length := 2 + 4 + ticketLen
+ x = make([]byte, 4+length)
+ x[0] = typeNewSessionTicket
+ x[1] = uint8(length >> 16)
+ x[2] = uint8(length >> 8)
+ x[3] = uint8(length)
+ x[8] = uint8(ticketLen >> 8)
+ x[9] = uint8(ticketLen)
+ copy(x[10:], m.ticket)
+
+ m.raw = x
+
+ return
+}
+
+func (m *newSessionTicketMsg) unmarshal(data []byte) bool {
+ m.raw = data
+
+ if len(data) < 10 {
+ return false
+ }
+
+ length := uint32(data[1])<<16 | uint32(data[2])<<8 | uint32(data[3])
+ if uint32(len(data))-4 != length {
+ return false
+ }
+
+ ticketLen := int(data[8])<<8 + int(data[9])
+ if len(data)-10 != ticketLen {
+ return false
+ }
+
+ m.ticket = data[10:]
+
+ return true
+}
+
+type helloRequestMsg struct {
+}
+
+func (*helloRequestMsg) marshal() []byte {
+ return []byte{typeHelloRequest, 0, 0, 0}
+}
+
+func (*helloRequestMsg) unmarshal(data []byte) bool {
+ return len(data) == 4
+}
diff --git a/vendor/github.com/quic-go/qtls-go1-20/handshake_server.go b/vendor/github.com/quic-go/qtls-go1-20/handshake_server.go
new file mode 100644
index 0000000000..64bfa1fce3
--- /dev/null
+++ b/vendor/github.com/quic-go/qtls-go1-20/handshake_server.go
@@ -0,0 +1,912 @@
+// Copyright 2009 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package qtls
+
+import (
+ "context"
+ "crypto"
+ "crypto/ecdsa"
+ "crypto/ed25519"
+ "crypto/rsa"
+ "crypto/subtle"
+ "crypto/x509"
+ "errors"
+ "fmt"
+ "hash"
+ "io"
+ "time"
+)
+
+// serverHandshakeState contains details of a server handshake in progress.
+// It's discarded once the handshake has completed.
+type serverHandshakeState struct {
+ c *Conn
+ ctx context.Context
+ clientHello *clientHelloMsg
+ hello *serverHelloMsg
+ suite *cipherSuite
+ ecdheOk bool
+ ecSignOk bool
+ rsaDecryptOk bool
+ rsaSignOk bool
+ sessionState *sessionState
+ finishedHash finishedHash
+ masterSecret []byte
+ cert *Certificate
+}
+
+// serverHandshake performs a TLS handshake as a server.
+func (c *Conn) serverHandshake(ctx context.Context) error {
+ c.setAlternativeRecordLayer()
+
+ clientHello, err := c.readClientHello(ctx)
+ if err != nil {
+ return err
+ }
+
+ if c.vers == VersionTLS13 {
+ hs := serverHandshakeStateTLS13{
+ c: c,
+ ctx: ctx,
+ clientHello: clientHello,
+ }
+ return hs.handshake()
+ } else if c.extraConfig.usesAlternativeRecordLayer() {
+ // This should already have been caught by the check that the ClientHello doesn't
+ // offer any (supported) versions older than TLS 1.3.
+ // Check again to make sure we can't be tricked into using an older version.
+ c.sendAlert(alertProtocolVersion)
+ return errors.New("tls: negotiated TLS < 1.3 when using QUIC")
+ }
+
+ hs := serverHandshakeState{
+ c: c,
+ ctx: ctx,
+ clientHello: clientHello,
+ }
+ return hs.handshake()
+}
+
+func (hs *serverHandshakeState) handshake() error {
+ c := hs.c
+
+ if err := hs.processClientHello(); err != nil {
+ return err
+ }
+
+ // For an overview of TLS handshaking, see RFC 5246, Section 7.3.
+ c.buffering = true
+ if hs.checkForResumption() {
+ // The client has included a session ticket and so we do an abbreviated handshake.
+ c.didResume = true
+ if err := hs.doResumeHandshake(); err != nil {
+ return err
+ }
+ if err := hs.establishKeys(); err != nil {
+ return err
+ }
+ if err := hs.sendSessionTicket(); err != nil {
+ return err
+ }
+ if err := hs.sendFinished(c.serverFinished[:]); err != nil {
+ return err
+ }
+ if _, err := c.flush(); err != nil {
+ return err
+ }
+ c.clientFinishedIsFirst = false
+ if err := hs.readFinished(nil); err != nil {
+ return err
+ }
+ } else {
+ // The client didn't include a session ticket, or it wasn't
+ // valid so we do a full handshake.
+ if err := hs.pickCipherSuite(); err != nil {
+ return err
+ }
+ if err := hs.doFullHandshake(); err != nil {
+ return err
+ }
+ if err := hs.establishKeys(); err != nil {
+ return err
+ }
+ if err := hs.readFinished(c.clientFinished[:]); err != nil {
+ return err
+ }
+ c.clientFinishedIsFirst = true
+ c.buffering = true
+ if err := hs.sendSessionTicket(); err != nil {
+ return err
+ }
+ if err := hs.sendFinished(nil); err != nil {
+ return err
+ }
+ if _, err := c.flush(); err != nil {
+ return err
+ }
+ }
+
+ c.ekm = ekmFromMasterSecret(c.vers, hs.suite, hs.masterSecret, hs.clientHello.random, hs.hello.random)
+ c.isHandshakeComplete.Store(true)
+
+ c.updateConnectionState()
+ return nil
+}
+
+// readClientHello reads a ClientHello message and selects the protocol version.
+func (c *Conn) readClientHello(ctx context.Context) (*clientHelloMsg, error) {
+ msg, err := c.readHandshake()
+ if err != nil {
+ return nil, err
+ }
+ clientHello, ok := msg.(*clientHelloMsg)
+ if !ok {
+ c.sendAlert(alertUnexpectedMessage)
+ return nil, unexpectedMessageError(clientHello, msg)
+ }
+
+ var configForClient *config
+ originalConfig := c.config
+ if c.config.GetConfigForClient != nil {
+ chi := newClientHelloInfo(ctx, c, clientHello)
+ if cfc, err := c.config.GetConfigForClient(chi); err != nil {
+ c.sendAlert(alertInternalError)
+ return nil, err
+ } else if cfc != nil {
+ configForClient = fromConfig(cfc)
+ c.config = configForClient
+ }
+ }
+ c.ticketKeys = originalConfig.ticketKeys(configForClient)
+
+ clientVersions := clientHello.supportedVersions
+ if len(clientHello.supportedVersions) == 0 {
+ clientVersions = supportedVersionsFromMax(clientHello.vers)
+ }
+ if c.extraConfig.usesAlternativeRecordLayer() {
+ // In QUIC, the client MUST NOT offer any old TLS versions.
+ // Here, we can only check that none of the other supported versions of this library
+ // (TLS 1.0 - TLS 1.2) is offered. We don't check for any SSL versions here.
+ for _, ver := range clientVersions {
+ if ver == VersionTLS13 {
+ continue
+ }
+ for _, v := range supportedVersions {
+ if ver == v {
+ c.sendAlert(alertProtocolVersion)
+ return nil, fmt.Errorf("tls: client offered old TLS version %#x", ver)
+ }
+ }
+ }
+ // Make the config we're using allows us to use TLS 1.3.
+ if c.config.maxSupportedVersion(roleServer) < VersionTLS13 {
+ c.sendAlert(alertInternalError)
+ return nil, errors.New("tls: MaxVersion prevents QUIC from using TLS 1.3")
+ }
+ }
+ c.vers, ok = c.config.mutualVersion(roleServer, clientVersions)
+ if !ok {
+ c.sendAlert(alertProtocolVersion)
+ return nil, fmt.Errorf("tls: client offered only unsupported versions: %x", clientVersions)
+ }
+ c.haveVers = true
+ c.in.version = c.vers
+ c.out.version = c.vers
+
+ return clientHello, nil
+}
+
+func (hs *serverHandshakeState) processClientHello() error {
+ c := hs.c
+
+ hs.hello = new(serverHelloMsg)
+ hs.hello.vers = c.vers
+
+ foundCompression := false
+ // We only support null compression, so check that the client offered it.
+ for _, compression := range hs.clientHello.compressionMethods {
+ if compression == compressionNone {
+ foundCompression = true
+ break
+ }
+ }
+
+ if !foundCompression {
+ c.sendAlert(alertHandshakeFailure)
+ return errors.New("tls: client does not support uncompressed connections")
+ }
+
+ hs.hello.random = make([]byte, 32)
+ serverRandom := hs.hello.random
+ // Downgrade protection canaries. See RFC 8446, Section 4.1.3.
+ maxVers := c.config.maxSupportedVersion(roleServer)
+ if maxVers >= VersionTLS12 && c.vers < maxVers || testingOnlyForceDowngradeCanary {
+ if c.vers == VersionTLS12 {
+ copy(serverRandom[24:], downgradeCanaryTLS12)
+ } else {
+ copy(serverRandom[24:], downgradeCanaryTLS11)
+ }
+ serverRandom = serverRandom[:24]
+ }
+ _, err := io.ReadFull(c.config.rand(), serverRandom)
+ if err != nil {
+ c.sendAlert(alertInternalError)
+ return err
+ }
+
+ if len(hs.clientHello.secureRenegotiation) != 0 {
+ c.sendAlert(alertHandshakeFailure)
+ return errors.New("tls: initial handshake had non-empty renegotiation extension")
+ }
+
+ hs.hello.secureRenegotiationSupported = hs.clientHello.secureRenegotiationSupported
+ hs.hello.compressionMethod = compressionNone
+ if len(hs.clientHello.serverName) > 0 {
+ c.serverName = hs.clientHello.serverName
+ }
+
+ selectedProto, err := negotiateALPN(c.config.NextProtos, hs.clientHello.alpnProtocols)
+ if err != nil {
+ c.sendAlert(alertNoApplicationProtocol)
+ return err
+ }
+ hs.hello.alpnProtocol = selectedProto
+ c.clientProtocol = selectedProto
+
+ hs.cert, err = c.config.getCertificate(newClientHelloInfo(hs.ctx, c, hs.clientHello))
+ if err != nil {
+ if err == errNoCertificates {
+ c.sendAlert(alertUnrecognizedName)
+ } else {
+ c.sendAlert(alertInternalError)
+ }
+ return err
+ }
+ if hs.clientHello.scts {
+ hs.hello.scts = hs.cert.SignedCertificateTimestamps
+ }
+
+ hs.ecdheOk = supportsECDHE(c.config, hs.clientHello.supportedCurves, hs.clientHello.supportedPoints)
+
+ if hs.ecdheOk && len(hs.clientHello.supportedPoints) > 0 {
+ // Although omitting the ec_point_formats extension is permitted, some
+ // old OpenSSL version will refuse to handshake if not present.
+ //
+ // Per RFC 4492, section 5.1.2, implementations MUST support the
+ // uncompressed point format. See golang.org/issue/31943.
+ hs.hello.supportedPoints = []uint8{pointFormatUncompressed}
+ }
+
+ if priv, ok := hs.cert.PrivateKey.(crypto.Signer); ok {
+ switch priv.Public().(type) {
+ case *ecdsa.PublicKey:
+ hs.ecSignOk = true
+ case ed25519.PublicKey:
+ hs.ecSignOk = true
+ case *rsa.PublicKey:
+ hs.rsaSignOk = true
+ default:
+ c.sendAlert(alertInternalError)
+ return fmt.Errorf("tls: unsupported signing key type (%T)", priv.Public())
+ }
+ }
+ if priv, ok := hs.cert.PrivateKey.(crypto.Decrypter); ok {
+ switch priv.Public().(type) {
+ case *rsa.PublicKey:
+ hs.rsaDecryptOk = true
+ default:
+ c.sendAlert(alertInternalError)
+ return fmt.Errorf("tls: unsupported decryption key type (%T)", priv.Public())
+ }
+ }
+
+ return nil
+}
+
+// negotiateALPN picks a shared ALPN protocol that both sides support in server
+// preference order. If ALPN is not configured or the peer doesn't support it,
+// it returns "" and no error.
+func negotiateALPN(serverProtos, clientProtos []string) (string, error) {
+ if len(serverProtos) == 0 || len(clientProtos) == 0 {
+ return "", nil
+ }
+ var http11fallback bool
+ for _, s := range serverProtos {
+ for _, c := range clientProtos {
+ if s == c {
+ return s, nil
+ }
+ if s == "h2" && c == "http/1.1" {
+ http11fallback = true
+ }
+ }
+ }
+ // As a special case, let http/1.1 clients connect to h2 servers as if they
+ // didn't support ALPN. We used not to enforce protocol overlap, so over
+ // time a number of HTTP servers were configured with only "h2", but
+ // expected to accept connections from "http/1.1" clients. See Issue 46310.
+ if http11fallback {
+ return "", nil
+ }
+ return "", fmt.Errorf("tls: client requested unsupported application protocols (%s)", clientProtos)
+}
+
+// supportsECDHE returns whether ECDHE key exchanges can be used with this
+// pre-TLS 1.3 client.
+func supportsECDHE(c *config, supportedCurves []CurveID, supportedPoints []uint8) bool {
+ supportsCurve := false
+ for _, curve := range supportedCurves {
+ if c.supportsCurve(curve) {
+ supportsCurve = true
+ break
+ }
+ }
+
+ supportsPointFormat := false
+ for _, pointFormat := range supportedPoints {
+ if pointFormat == pointFormatUncompressed {
+ supportsPointFormat = true
+ break
+ }
+ }
+ // Per RFC 8422, Section 5.1.2, if the Supported Point Formats extension is
+ // missing, uncompressed points are supported. If supportedPoints is empty,
+ // the extension must be missing, as an empty extension body is rejected by
+ // the parser. See https://go.dev/issue/49126.
+ if len(supportedPoints) == 0 {
+ supportsPointFormat = true
+ }
+
+ return supportsCurve && supportsPointFormat
+}
+
+func (hs *serverHandshakeState) pickCipherSuite() error {
+ c := hs.c
+
+ preferenceOrder := cipherSuitesPreferenceOrder
+ if !hasAESGCMHardwareSupport || !aesgcmPreferred(hs.clientHello.cipherSuites) {
+ preferenceOrder = cipherSuitesPreferenceOrderNoAES
+ }
+
+ configCipherSuites := c.config.cipherSuites()
+ preferenceList := make([]uint16, 0, len(configCipherSuites))
+ for _, suiteID := range preferenceOrder {
+ for _, id := range configCipherSuites {
+ if id == suiteID {
+ preferenceList = append(preferenceList, id)
+ break
+ }
+ }
+ }
+
+ hs.suite = selectCipherSuite(preferenceList, hs.clientHello.cipherSuites, hs.cipherSuiteOk)
+ if hs.suite == nil {
+ c.sendAlert(alertHandshakeFailure)
+ return errors.New("tls: no cipher suite supported by both client and server")
+ }
+ c.cipherSuite = hs.suite.id
+
+ for _, id := range hs.clientHello.cipherSuites {
+ if id == TLS_FALLBACK_SCSV {
+ // The client is doing a fallback connection. See RFC 7507.
+ if hs.clientHello.vers < c.config.maxSupportedVersion(roleServer) {
+ c.sendAlert(alertInappropriateFallback)
+ return errors.New("tls: client using inappropriate protocol fallback")
+ }
+ break
+ }
+ }
+
+ return nil
+}
+
+func (hs *serverHandshakeState) cipherSuiteOk(c *cipherSuite) bool {
+ if c.flags&suiteECDHE != 0 {
+ if !hs.ecdheOk {
+ return false
+ }
+ if c.flags&suiteECSign != 0 {
+ if !hs.ecSignOk {
+ return false
+ }
+ } else if !hs.rsaSignOk {
+ return false
+ }
+ } else if !hs.rsaDecryptOk {
+ return false
+ }
+ if hs.c.vers < VersionTLS12 && c.flags&suiteTLS12 != 0 {
+ return false
+ }
+ return true
+}
+
+// checkForResumption reports whether we should perform resumption on this connection.
+func (hs *serverHandshakeState) checkForResumption() bool {
+ c := hs.c
+
+ if c.config.SessionTicketsDisabled {
+ return false
+ }
+
+ plaintext, usedOldKey := c.decryptTicket(hs.clientHello.sessionTicket)
+ if plaintext == nil {
+ return false
+ }
+ hs.sessionState = &sessionState{usedOldKey: usedOldKey}
+ ok := hs.sessionState.unmarshal(plaintext)
+ if !ok {
+ return false
+ }
+
+ createdAt := time.Unix(int64(hs.sessionState.createdAt), 0)
+ if c.config.time().Sub(createdAt) > maxSessionTicketLifetime {
+ return false
+ }
+
+ // Never resume a session for a different TLS version.
+ if c.vers != hs.sessionState.vers {
+ return false
+ }
+
+ cipherSuiteOk := false
+ // Check that the client is still offering the ciphersuite in the session.
+ for _, id := range hs.clientHello.cipherSuites {
+ if id == hs.sessionState.cipherSuite {
+ cipherSuiteOk = true
+ break
+ }
+ }
+ if !cipherSuiteOk {
+ return false
+ }
+
+ // Check that we also support the ciphersuite from the session.
+ hs.suite = selectCipherSuite([]uint16{hs.sessionState.cipherSuite},
+ c.config.cipherSuites(), hs.cipherSuiteOk)
+ if hs.suite == nil {
+ return false
+ }
+
+ sessionHasClientCerts := len(hs.sessionState.certificates) != 0
+ needClientCerts := requiresClientCert(c.config.ClientAuth)
+ if needClientCerts && !sessionHasClientCerts {
+ return false
+ }
+ if sessionHasClientCerts && c.config.ClientAuth == NoClientCert {
+ return false
+ }
+
+ return true
+}
+
+func (hs *serverHandshakeState) doResumeHandshake() error {
+ c := hs.c
+
+ hs.hello.cipherSuite = hs.suite.id
+ c.cipherSuite = hs.suite.id
+ // We echo the client's session ID in the ServerHello to let it know
+ // that we're doing a resumption.
+ hs.hello.sessionId = hs.clientHello.sessionId
+ hs.hello.ticketSupported = hs.sessionState.usedOldKey
+ hs.finishedHash = newFinishedHash(c.vers, hs.suite)
+ hs.finishedHash.discardHandshakeBuffer()
+ hs.finishedHash.Write(hs.clientHello.marshal())
+ hs.finishedHash.Write(hs.hello.marshal())
+ if _, err := c.writeRecord(recordTypeHandshake, hs.hello.marshal()); err != nil {
+ return err
+ }
+
+ if err := c.processCertsFromClient(Certificate{
+ Certificate: hs.sessionState.certificates,
+ }); err != nil {
+ return err
+ }
+
+ if c.config.VerifyConnection != nil {
+ if err := c.config.VerifyConnection(c.connectionStateLocked()); err != nil {
+ c.sendAlert(alertBadCertificate)
+ return err
+ }
+ }
+
+ hs.masterSecret = hs.sessionState.masterSecret
+
+ return nil
+}
+
+func (hs *serverHandshakeState) doFullHandshake() error {
+ c := hs.c
+
+ if hs.clientHello.ocspStapling && len(hs.cert.OCSPStaple) > 0 {
+ hs.hello.ocspStapling = true
+ }
+
+ hs.hello.ticketSupported = hs.clientHello.ticketSupported && !c.config.SessionTicketsDisabled
+ hs.hello.cipherSuite = hs.suite.id
+
+ hs.finishedHash = newFinishedHash(hs.c.vers, hs.suite)
+ if c.config.ClientAuth == NoClientCert {
+ // No need to keep a full record of the handshake if client
+ // certificates won't be used.
+ hs.finishedHash.discardHandshakeBuffer()
+ }
+ hs.finishedHash.Write(hs.clientHello.marshal())
+ hs.finishedHash.Write(hs.hello.marshal())
+ if _, err := c.writeRecord(recordTypeHandshake, hs.hello.marshal()); err != nil {
+ return err
+ }
+
+ certMsg := new(certificateMsg)
+ certMsg.certificates = hs.cert.Certificate
+ hs.finishedHash.Write(certMsg.marshal())
+ if _, err := c.writeRecord(recordTypeHandshake, certMsg.marshal()); err != nil {
+ return err
+ }
+
+ if hs.hello.ocspStapling {
+ certStatus := new(certificateStatusMsg)
+ certStatus.response = hs.cert.OCSPStaple
+ hs.finishedHash.Write(certStatus.marshal())
+ if _, err := c.writeRecord(recordTypeHandshake, certStatus.marshal()); err != nil {
+ return err
+ }
+ }
+
+ keyAgreement := hs.suite.ka(c.vers)
+ skx, err := keyAgreement.generateServerKeyExchange(c.config, hs.cert, hs.clientHello, hs.hello)
+ if err != nil {
+ c.sendAlert(alertHandshakeFailure)
+ return err
+ }
+ if skx != nil {
+ hs.finishedHash.Write(skx.marshal())
+ if _, err := c.writeRecord(recordTypeHandshake, skx.marshal()); err != nil {
+ return err
+ }
+ }
+
+ var certReq *certificateRequestMsg
+ if c.config.ClientAuth >= RequestClientCert {
+ // Request a client certificate
+ certReq = new(certificateRequestMsg)
+ certReq.certificateTypes = []byte{
+ byte(certTypeRSASign),
+ byte(certTypeECDSASign),
+ }
+ if c.vers >= VersionTLS12 {
+ certReq.hasSignatureAlgorithm = true
+ certReq.supportedSignatureAlgorithms = supportedSignatureAlgorithms()
+ }
+
+ // 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.
+ if c.config.ClientCAs != nil {
+ certReq.certificateAuthorities = c.config.ClientCAs.Subjects()
+ }
+ hs.finishedHash.Write(certReq.marshal())
+ if _, err := c.writeRecord(recordTypeHandshake, certReq.marshal()); err != nil {
+ return err
+ }
+ }
+
+ helloDone := new(serverHelloDoneMsg)
+ hs.finishedHash.Write(helloDone.marshal())
+ if _, err := c.writeRecord(recordTypeHandshake, helloDone.marshal()); err != nil {
+ return err
+ }
+
+ if _, err := c.flush(); err != nil {
+ return err
+ }
+
+ var pub crypto.PublicKey // public key for client auth, if any
+
+ msg, err := c.readHandshake()
+ if err != nil {
+ return err
+ }
+
+ // If we requested a client certificate, then the client must send a
+ // certificate message, even if it's empty.
+ if c.config.ClientAuth >= RequestClientCert {
+ certMsg, ok := msg.(*certificateMsg)
+ if !ok {
+ c.sendAlert(alertUnexpectedMessage)
+ return unexpectedMessageError(certMsg, msg)
+ }
+ hs.finishedHash.Write(certMsg.marshal())
+
+ if err := c.processCertsFromClient(Certificate{
+ Certificate: certMsg.certificates,
+ }); err != nil {
+ return err
+ }
+ if len(certMsg.certificates) != 0 {
+ pub = c.peerCertificates[0].PublicKey
+ }
+
+ msg, err = c.readHandshake()
+ if err != nil {
+ return err
+ }
+ }
+ if c.config.VerifyConnection != nil {
+ if err := c.config.VerifyConnection(c.connectionStateLocked()); err != nil {
+ c.sendAlert(alertBadCertificate)
+ return err
+ }
+ }
+
+ // Get client key exchange
+ ckx, ok := msg.(*clientKeyExchangeMsg)
+ if !ok {
+ c.sendAlert(alertUnexpectedMessage)
+ return unexpectedMessageError(ckx, msg)
+ }
+ hs.finishedHash.Write(ckx.marshal())
+
+ preMasterSecret, err := keyAgreement.processClientKeyExchange(c.config, hs.cert, ckx, c.vers)
+ if err != nil {
+ c.sendAlert(alertHandshakeFailure)
+ return err
+ }
+ hs.masterSecret = masterFromPreMasterSecret(c.vers, hs.suite, preMasterSecret, hs.clientHello.random, hs.hello.random)
+ if err := c.config.writeKeyLog(keyLogLabelTLS12, hs.clientHello.random, hs.masterSecret); err != nil {
+ c.sendAlert(alertInternalError)
+ return err
+ }
+
+ // If we received a client cert in response to our certificate request message,
+ // the client will send us a certificateVerifyMsg immediately after the
+ // clientKeyExchangeMsg. This message is a digest of all preceding
+ // handshake-layer messages that is signed using the private key corresponding
+ // to the client's certificate. This allows us to verify that the client is in
+ // possession of the private key of the certificate.
+ if len(c.peerCertificates) > 0 {
+ msg, err = c.readHandshake()
+ if err != nil {
+ return err
+ }
+ certVerify, ok := msg.(*certificateVerifyMsg)
+ if !ok {
+ c.sendAlert(alertUnexpectedMessage)
+ return unexpectedMessageError(certVerify, msg)
+ }
+
+ var sigType uint8
+ var sigHash crypto.Hash
+ if c.vers >= VersionTLS12 {
+ if !isSupportedSignatureAlgorithm(certVerify.signatureAlgorithm, certReq.supportedSignatureAlgorithms) {
+ c.sendAlert(alertIllegalParameter)
+ return errors.New("tls: client certificate used with invalid signature algorithm")
+ }
+ sigType, sigHash, err = typeAndHashFromSignatureScheme(certVerify.signatureAlgorithm)
+ if err != nil {
+ return c.sendAlert(alertInternalError)
+ }
+ } else {
+ sigType, sigHash, err = legacyTypeAndHashFromPublicKey(pub)
+ if err != nil {
+ c.sendAlert(alertIllegalParameter)
+ return err
+ }
+ }
+
+ signed := hs.finishedHash.hashForClientCertificate(sigType, sigHash)
+ if err := verifyHandshakeSignature(sigType, pub, sigHash, signed, certVerify.signature); err != nil {
+ c.sendAlert(alertDecryptError)
+ return errors.New("tls: invalid signature by the client certificate: " + err.Error())
+ }
+
+ hs.finishedHash.Write(certVerify.marshal())
+ }
+
+ hs.finishedHash.discardHandshakeBuffer()
+
+ return nil
+}
+
+func (hs *serverHandshakeState) establishKeys() error {
+ c := hs.c
+
+ clientMAC, serverMAC, clientKey, serverKey, clientIV, serverIV :=
+ keysFromMasterSecret(c.vers, hs.suite, hs.masterSecret, hs.clientHello.random, hs.hello.random, hs.suite.macLen, hs.suite.keyLen, hs.suite.ivLen)
+
+ var clientCipher, serverCipher any
+ var clientHash, serverHash hash.Hash
+
+ if hs.suite.aead == nil {
+ clientCipher = hs.suite.cipher(clientKey, clientIV, true /* for reading */)
+ clientHash = hs.suite.mac(clientMAC)
+ serverCipher = hs.suite.cipher(serverKey, serverIV, false /* not for reading */)
+ serverHash = hs.suite.mac(serverMAC)
+ } else {
+ clientCipher = hs.suite.aead(clientKey, clientIV)
+ serverCipher = hs.suite.aead(serverKey, serverIV)
+ }
+
+ c.in.prepareCipherSpec(c.vers, clientCipher, clientHash)
+ c.out.prepareCipherSpec(c.vers, serverCipher, serverHash)
+
+ return nil
+}
+
+func (hs *serverHandshakeState) readFinished(out []byte) error {
+ c := hs.c
+
+ if err := c.readChangeCipherSpec(); err != nil {
+ return err
+ }
+
+ msg, err := c.readHandshake()
+ if err != nil {
+ return err
+ }
+ clientFinished, ok := msg.(*finishedMsg)
+ if !ok {
+ c.sendAlert(alertUnexpectedMessage)
+ return unexpectedMessageError(clientFinished, msg)
+ }
+
+ verify := hs.finishedHash.clientSum(hs.masterSecret)
+ if len(verify) != len(clientFinished.verifyData) ||
+ subtle.ConstantTimeCompare(verify, clientFinished.verifyData) != 1 {
+ c.sendAlert(alertHandshakeFailure)
+ return errors.New("tls: client's Finished message is incorrect")
+ }
+
+ hs.finishedHash.Write(clientFinished.marshal())
+ copy(out, verify)
+ return nil
+}
+
+func (hs *serverHandshakeState) sendSessionTicket() error {
+ // ticketSupported is set in a resumption handshake if the
+ // ticket from the client was encrypted with an old session
+ // ticket key and thus a refreshed ticket should be sent.
+ if !hs.hello.ticketSupported {
+ return nil
+ }
+
+ c := hs.c
+ m := new(newSessionTicketMsg)
+
+ createdAt := uint64(c.config.time().Unix())
+ if hs.sessionState != nil {
+ // If this is re-wrapping an old key, then keep
+ // the original time it was created.
+ createdAt = hs.sessionState.createdAt
+ }
+
+ var certsFromClient [][]byte
+ for _, cert := range c.peerCertificates {
+ certsFromClient = append(certsFromClient, cert.Raw)
+ }
+ state := sessionState{
+ vers: c.vers,
+ cipherSuite: hs.suite.id,
+ createdAt: createdAt,
+ masterSecret: hs.masterSecret,
+ certificates: certsFromClient,
+ }
+ var err error
+ m.ticket, err = c.encryptTicket(state.marshal())
+ if err != nil {
+ return err
+ }
+
+ hs.finishedHash.Write(m.marshal())
+ if _, err := c.writeRecord(recordTypeHandshake, m.marshal()); err != nil {
+ return err
+ }
+
+ return nil
+}
+
+func (hs *serverHandshakeState) sendFinished(out []byte) error {
+ c := hs.c
+
+ if _, err := c.writeRecord(recordTypeChangeCipherSpec, []byte{1}); err != nil {
+ return err
+ }
+
+ finished := new(finishedMsg)
+ finished.verifyData = hs.finishedHash.serverSum(hs.masterSecret)
+ hs.finishedHash.Write(finished.marshal())
+ if _, err := c.writeRecord(recordTypeHandshake, finished.marshal()); err != nil {
+ return err
+ }
+
+ copy(out, finished.verifyData)
+
+ return nil
+}
+
+// processCertsFromClient takes a chain of client certificates either from a
+// Certificates message or from a sessionState and verifies them. It returns
+// the public key of the leaf certificate.
+func (c *Conn) processCertsFromClient(certificate Certificate) error {
+ certificates := certificate.Certificate
+ certs := make([]*x509.Certificate, len(certificates))
+ var err error
+ for i, asn1Data := range certificates {
+ if certs[i], err = x509.ParseCertificate(asn1Data); err != nil {
+ c.sendAlert(alertBadCertificate)
+ return errors.New("tls: failed to parse client certificate: " + err.Error())
+ }
+ }
+
+ if len(certs) == 0 && requiresClientCert(c.config.ClientAuth) {
+ c.sendAlert(alertBadCertificate)
+ return errors.New("tls: client didn't provide a certificate")
+ }
+
+ if c.config.ClientAuth >= VerifyClientCertIfGiven && len(certs) > 0 {
+ opts := x509.VerifyOptions{
+ Roots: c.config.ClientCAs,
+ CurrentTime: c.config.time(),
+ Intermediates: x509.NewCertPool(),
+ KeyUsages: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth},
+ }
+
+ for _, cert := range certs[1:] {
+ opts.Intermediates.AddCert(cert)
+ }
+
+ chains, err := certs[0].Verify(opts)
+ if err != nil {
+ c.sendAlert(alertBadCertificate)
+ return &CertificateVerificationError{UnverifiedCertificates: certs, Err: err}
+ }
+
+ c.verifiedChains = chains
+ }
+
+ c.peerCertificates = certs
+ c.ocspResponse = certificate.OCSPStaple
+ c.scts = certificate.SignedCertificateTimestamps
+
+ if len(certs) > 0 {
+ switch certs[0].PublicKey.(type) {
+ case *ecdsa.PublicKey, *rsa.PublicKey, ed25519.PublicKey:
+ default:
+ c.sendAlert(alertUnsupportedCertificate)
+ return fmt.Errorf("tls: client certificate contains an unsupported public key of type %T", certs[0].PublicKey)
+ }
+ }
+
+ if c.config.VerifyPeerCertificate != nil {
+ if err := c.config.VerifyPeerCertificate(certificates, c.verifiedChains); err != nil {
+ c.sendAlert(alertBadCertificate)
+ return err
+ }
+ }
+
+ return nil
+}
+
+func newClientHelloInfo(ctx context.Context, c *Conn, clientHello *clientHelloMsg) *ClientHelloInfo {
+ supportedVersions := clientHello.supportedVersions
+ if len(clientHello.supportedVersions) == 0 {
+ supportedVersions = supportedVersionsFromMax(clientHello.vers)
+ }
+
+ return toClientHelloInfo(&clientHelloInfo{
+ CipherSuites: clientHello.cipherSuites,
+ ServerName: clientHello.serverName,
+ SupportedCurves: clientHello.supportedCurves,
+ SupportedPoints: clientHello.supportedPoints,
+ SignatureSchemes: clientHello.supportedSignatureAlgorithms,
+ SupportedProtos: clientHello.alpnProtocols,
+ SupportedVersions: supportedVersions,
+ Conn: c.conn,
+ config: toConfig(c.config),
+ ctx: ctx,
+ })
+}
diff --git a/vendor/github.com/quic-go/qtls-go1-20/handshake_server_tls13.go b/vendor/github.com/quic-go/qtls-go1-20/handshake_server_tls13.go
new file mode 100644
index 0000000000..3a5acfb96b
--- /dev/null
+++ b/vendor/github.com/quic-go/qtls-go1-20/handshake_server_tls13.go
@@ -0,0 +1,906 @@
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package qtls
+
+import (
+ "bytes"
+ "context"
+ "crypto"
+ "crypto/hmac"
+ "crypto/rsa"
+ "errors"
+ "hash"
+ "io"
+ "time"
+)
+
+// maxClientPSKIdentities is the number of client PSK identities the server will
+// attempt to validate. It will ignore the rest not to let cheap ClientHello
+// messages cause too much work in session ticket decryption attempts.
+const maxClientPSKIdentities = 5
+
+type serverHandshakeStateTLS13 struct {
+ c *Conn
+ ctx context.Context
+ clientHello *clientHelloMsg
+ hello *serverHelloMsg
+ alpnNegotiationErr error
+ encryptedExtensions *encryptedExtensionsMsg
+ sentDummyCCS bool
+ usingPSK bool
+ suite *cipherSuiteTLS13
+ cert *Certificate
+ sigAlg SignatureScheme
+ earlySecret []byte
+ sharedKey []byte
+ handshakeSecret []byte
+ masterSecret []byte
+ trafficSecret []byte // client_application_traffic_secret_0
+ transcript hash.Hash
+ clientFinished []byte
+}
+
+func (hs *serverHandshakeStateTLS13) handshake() error {
+ c := hs.c
+
+ if needFIPS() {
+ return errors.New("tls: internal error: TLS 1.3 reached in FIPS mode")
+ }
+
+ // For an overview of the TLS 1.3 handshake, see RFC 8446, Section 2.
+ if err := hs.processClientHello(); err != nil {
+ return err
+ }
+ if err := hs.checkForResumption(); err != nil {
+ return err
+ }
+ c.updateConnectionState()
+ if err := hs.pickCertificate(); err != nil {
+ return err
+ }
+ c.buffering = true
+ if err := hs.sendServerParameters(); err != nil {
+ return err
+ }
+ if err := hs.sendServerCertificate(); err != nil {
+ return err
+ }
+ if err := hs.sendServerFinished(); err != nil {
+ return err
+ }
+ // Note that at this point we could start sending application data without
+ // waiting for the client's second flight, but the application might not
+ // expect the lack of replay protection of the ClientHello parameters.
+ if _, err := c.flush(); err != nil {
+ return err
+ }
+ if err := hs.readClientCertificate(); err != nil {
+ return err
+ }
+ c.updateConnectionState()
+ if err := hs.readClientFinished(); err != nil {
+ return err
+ }
+
+ c.isHandshakeComplete.Store(true)
+ c.updateConnectionState()
+ return nil
+}
+
+func (hs *serverHandshakeStateTLS13) processClientHello() error {
+ c := hs.c
+
+ hs.hello = new(serverHelloMsg)
+ hs.encryptedExtensions = new(encryptedExtensionsMsg)
+
+ // TLS 1.3 froze the ServerHello.legacy_version field, and uses
+ // supported_versions instead. See RFC 8446, sections 4.1.3 and 4.2.1.
+ hs.hello.vers = VersionTLS12
+ hs.hello.supportedVersion = c.vers
+
+ if len(hs.clientHello.supportedVersions) == 0 {
+ c.sendAlert(alertIllegalParameter)
+ return errors.New("tls: client used the legacy version field to negotiate TLS 1.3")
+ }
+
+ // Abort if the client is doing a fallback and landing lower than what we
+ // support. See RFC 7507, which however does not specify the interaction
+ // with supported_versions. The only difference is that with
+ // supported_versions a client has a chance to attempt a [TLS 1.2, TLS 1.4]
+ // handshake in case TLS 1.3 is broken but 1.2 is not. Alas, in that case,
+ // it will have to drop the TLS_FALLBACK_SCSV protection if it falls back to
+ // TLS 1.2, because a TLS 1.3 server would abort here. The situation before
+ // supported_versions was not better because there was just no way to do a
+ // TLS 1.4 handshake without risking the server selecting TLS 1.3.
+ for _, id := range hs.clientHello.cipherSuites {
+ if id == TLS_FALLBACK_SCSV {
+ // Use c.vers instead of max(supported_versions) because an attacker
+ // could defeat this by adding an arbitrary high version otherwise.
+ if c.vers < c.config.maxSupportedVersion(roleServer) {
+ c.sendAlert(alertInappropriateFallback)
+ return errors.New("tls: client using inappropriate protocol fallback")
+ }
+ break
+ }
+ }
+
+ if len(hs.clientHello.compressionMethods) != 1 ||
+ hs.clientHello.compressionMethods[0] != compressionNone {
+ c.sendAlert(alertIllegalParameter)
+ return errors.New("tls: TLS 1.3 client supports illegal compression methods")
+ }
+
+ hs.hello.random = make([]byte, 32)
+ if _, err := io.ReadFull(c.config.rand(), hs.hello.random); err != nil {
+ c.sendAlert(alertInternalError)
+ return err
+ }
+
+ if len(hs.clientHello.secureRenegotiation) != 0 {
+ c.sendAlert(alertHandshakeFailure)
+ return errors.New("tls: initial handshake had non-empty renegotiation extension")
+ }
+
+ hs.hello.sessionId = hs.clientHello.sessionId
+ hs.hello.compressionMethod = compressionNone
+
+ if hs.suite == nil {
+ var preferenceList []uint16
+ for _, suiteID := range c.config.CipherSuites {
+ for _, suite := range cipherSuitesTLS13 {
+ if suite.id == suiteID {
+ preferenceList = append(preferenceList, suiteID)
+ break
+ }
+ }
+ }
+ if len(preferenceList) == 0 {
+ preferenceList = defaultCipherSuitesTLS13
+ if !hasAESGCMHardwareSupport || !aesgcmPreferred(hs.clientHello.cipherSuites) {
+ preferenceList = defaultCipherSuitesTLS13NoAES
+ }
+ }
+ for _, suiteID := range preferenceList {
+ hs.suite = mutualCipherSuiteTLS13(hs.clientHello.cipherSuites, suiteID)
+ if hs.suite != nil {
+ break
+ }
+ }
+ }
+ if hs.suite == nil {
+ c.sendAlert(alertHandshakeFailure)
+ return errors.New("tls: no cipher suite supported by both client and server")
+ }
+ c.cipherSuite = hs.suite.id
+ hs.hello.cipherSuite = hs.suite.id
+ hs.transcript = hs.suite.hash.New()
+
+ // Pick the ECDHE group in server preference order, but give priority to
+ // groups with a key share, to avoid a HelloRetryRequest round-trip.
+ var selectedGroup CurveID
+ var clientKeyShare *keyShare
+GroupSelection:
+ for _, preferredGroup := range c.config.curvePreferences() {
+ for _, ks := range hs.clientHello.keyShares {
+ if ks.group == preferredGroup {
+ selectedGroup = ks.group
+ clientKeyShare = &ks
+ break GroupSelection
+ }
+ }
+ if selectedGroup != 0 {
+ continue
+ }
+ for _, group := range hs.clientHello.supportedCurves {
+ if group == preferredGroup {
+ selectedGroup = group
+ break
+ }
+ }
+ }
+ if selectedGroup == 0 {
+ c.sendAlert(alertHandshakeFailure)
+ return errors.New("tls: no ECDHE curve supported by both client and server")
+ }
+ if clientKeyShare == nil {
+ if err := hs.doHelloRetryRequest(selectedGroup); err != nil {
+ return err
+ }
+ clientKeyShare = &hs.clientHello.keyShares[0]
+ }
+
+ if _, ok := curveForCurveID(selectedGroup); !ok {
+ c.sendAlert(alertInternalError)
+ return errors.New("tls: CurvePreferences includes unsupported curve")
+ }
+ key, err := generateECDHEKey(c.config.rand(), selectedGroup)
+ if err != nil {
+ c.sendAlert(alertInternalError)
+ return err
+ }
+ hs.hello.serverShare = keyShare{group: selectedGroup, data: key.PublicKey().Bytes()}
+ peerKey, err := key.Curve().NewPublicKey(clientKeyShare.data)
+ if err != nil {
+ c.sendAlert(alertIllegalParameter)
+ return errors.New("tls: invalid client key share")
+ }
+ hs.sharedKey, err = key.ECDH(peerKey)
+ if err != nil {
+ c.sendAlert(alertIllegalParameter)
+ return errors.New("tls: invalid client key share")
+ }
+
+ c.serverName = hs.clientHello.serverName
+
+ if c.extraConfig != nil && c.extraConfig.ReceivedExtensions != nil {
+ c.extraConfig.ReceivedExtensions(typeClientHello, hs.clientHello.additionalExtensions)
+ }
+
+ selectedProto, err := negotiateALPN(c.config.NextProtos, hs.clientHello.alpnProtocols)
+ if err != nil {
+ hs.alpnNegotiationErr = err
+ }
+ hs.encryptedExtensions.alpnProtocol = selectedProto
+ c.clientProtocol = selectedProto
+
+ return nil
+}
+
+func (hs *serverHandshakeStateTLS13) checkForResumption() error {
+ c := hs.c
+
+ if c.config.SessionTicketsDisabled {
+ return nil
+ }
+
+ modeOK := false
+ for _, mode := range hs.clientHello.pskModes {
+ if mode == pskModeDHE {
+ modeOK = true
+ break
+ }
+ }
+ if !modeOK {
+ return nil
+ }
+
+ if len(hs.clientHello.pskIdentities) != len(hs.clientHello.pskBinders) {
+ c.sendAlert(alertIllegalParameter)
+ return errors.New("tls: invalid or missing PSK binders")
+ }
+ if len(hs.clientHello.pskIdentities) == 0 {
+ return nil
+ }
+
+ for i, identity := range hs.clientHello.pskIdentities {
+ if i >= maxClientPSKIdentities {
+ break
+ }
+
+ plaintext, _ := c.decryptTicket(identity.label)
+ if plaintext == nil {
+ continue
+ }
+ sessionState := new(sessionStateTLS13)
+ if ok := sessionState.unmarshal(plaintext); !ok {
+ continue
+ }
+
+ if hs.clientHello.earlyData {
+ if sessionState.maxEarlyData == 0 {
+ c.sendAlert(alertUnsupportedExtension)
+ return errors.New("tls: client sent unexpected early data")
+ }
+
+ if hs.alpnNegotiationErr == nil && sessionState.alpn == c.clientProtocol &&
+ c.extraConfig != nil && c.extraConfig.MaxEarlyData > 0 &&
+ c.extraConfig.Accept0RTT != nil && c.extraConfig.Accept0RTT(sessionState.appData) {
+ hs.encryptedExtensions.earlyData = true
+ c.used0RTT = true
+ }
+ }
+
+ createdAt := time.Unix(int64(sessionState.createdAt), 0)
+ if c.config.time().Sub(createdAt) > maxSessionTicketLifetime {
+ continue
+ }
+
+ // We don't check the obfuscated ticket age because it's affected by
+ // clock skew and it's only a freshness signal useful for shrinking the
+ // window for replay attacks, which don't affect us as we don't do 0-RTT.
+
+ pskSuite := cipherSuiteTLS13ByID(sessionState.cipherSuite)
+ if pskSuite == nil || pskSuite.hash != hs.suite.hash {
+ continue
+ }
+
+ // PSK connections don't re-establish client certificates, but carry
+ // them over in the session ticket. Ensure the presence of client certs
+ // in the ticket is consistent with the configured requirements.
+ sessionHasClientCerts := len(sessionState.certificate.Certificate) != 0
+ needClientCerts := requiresClientCert(c.config.ClientAuth)
+ if needClientCerts && !sessionHasClientCerts {
+ continue
+ }
+ if sessionHasClientCerts && c.config.ClientAuth == NoClientCert {
+ continue
+ }
+
+ psk := hs.suite.expandLabel(sessionState.resumptionSecret, "resumption",
+ nil, hs.suite.hash.Size())
+ hs.earlySecret = hs.suite.extract(psk, nil)
+ binderKey := hs.suite.deriveSecret(hs.earlySecret, resumptionBinderLabel, nil)
+ // Clone the transcript in case a HelloRetryRequest was recorded.
+ transcript := cloneHash(hs.transcript, hs.suite.hash)
+ if transcript == nil {
+ c.sendAlert(alertInternalError)
+ return errors.New("tls: internal error: failed to clone hash")
+ }
+ transcript.Write(hs.clientHello.marshalWithoutBinders())
+ pskBinder := hs.suite.finishedHash(binderKey, transcript)
+ if !hmac.Equal(hs.clientHello.pskBinders[i], pskBinder) {
+ c.sendAlert(alertDecryptError)
+ return errors.New("tls: invalid PSK binder")
+ }
+
+ c.didResume = true
+ if err := c.processCertsFromClient(sessionState.certificate); err != nil {
+ return err
+ }
+
+ h := cloneHash(hs.transcript, hs.suite.hash)
+ h.Write(hs.clientHello.marshal())
+ if hs.encryptedExtensions.earlyData {
+ clientEarlySecret := hs.suite.deriveSecret(hs.earlySecret, "c e traffic", h)
+ c.in.exportKey(Encryption0RTT, hs.suite, clientEarlySecret)
+ if err := c.config.writeKeyLog(keyLogLabelEarlyTraffic, hs.clientHello.random, clientEarlySecret); err != nil {
+ c.sendAlert(alertInternalError)
+ return err
+ }
+ }
+
+ hs.hello.selectedIdentityPresent = true
+ hs.hello.selectedIdentity = uint16(i)
+ hs.usingPSK = true
+ return nil
+ }
+
+ return nil
+}
+
+// cloneHash uses the encoding.BinaryMarshaler and encoding.BinaryUnmarshaler
+// interfaces implemented by standard library hashes to clone the state of in
+// to a new instance of h. It returns nil if the operation fails.
+func cloneHash(in hash.Hash, h crypto.Hash) hash.Hash {
+ // Recreate the interface to avoid importing encoding.
+ type binaryMarshaler interface {
+ MarshalBinary() (data []byte, err error)
+ UnmarshalBinary(data []byte) error
+ }
+ marshaler, ok := in.(binaryMarshaler)
+ if !ok {
+ return nil
+ }
+ state, err := marshaler.MarshalBinary()
+ if err != nil {
+ return nil
+ }
+ out := h.New()
+ unmarshaler, ok := out.(binaryMarshaler)
+ if !ok {
+ return nil
+ }
+ if err := unmarshaler.UnmarshalBinary(state); err != nil {
+ return nil
+ }
+ return out
+}
+
+func (hs *serverHandshakeStateTLS13) pickCertificate() error {
+ c := hs.c
+
+ // Only one of PSK and certificates are used at a time.
+ if hs.usingPSK {
+ return nil
+ }
+
+ // signature_algorithms is required in TLS 1.3. See RFC 8446, Section 4.2.3.
+ if len(hs.clientHello.supportedSignatureAlgorithms) == 0 {
+ return c.sendAlert(alertMissingExtension)
+ }
+
+ certificate, err := c.config.getCertificate(newClientHelloInfo(hs.ctx, c, hs.clientHello))
+ if err != nil {
+ if err == errNoCertificates {
+ c.sendAlert(alertUnrecognizedName)
+ } else {
+ c.sendAlert(alertInternalError)
+ }
+ return err
+ }
+ hs.sigAlg, err = selectSignatureScheme(c.vers, certificate, hs.clientHello.supportedSignatureAlgorithms)
+ if err != nil {
+ // getCertificate returned a certificate that is unsupported or
+ // incompatible with the client's signature algorithms.
+ c.sendAlert(alertHandshakeFailure)
+ return err
+ }
+ hs.cert = certificate
+
+ return nil
+}
+
+// sendDummyChangeCipherSpec sends a ChangeCipherSpec record for compatibility
+// with middleboxes that didn't implement TLS correctly. See RFC 8446, Appendix D.4.
+func (hs *serverHandshakeStateTLS13) sendDummyChangeCipherSpec() error {
+ if hs.sentDummyCCS {
+ return nil
+ }
+ hs.sentDummyCCS = true
+
+ _, err := hs.c.writeRecord(recordTypeChangeCipherSpec, []byte{1})
+ return err
+}
+
+func (hs *serverHandshakeStateTLS13) doHelloRetryRequest(selectedGroup CurveID) error {
+ c := hs.c
+
+ // The first ClientHello gets double-hashed into the transcript upon a
+ // HelloRetryRequest. See RFC 8446, Section 4.4.1.
+ hs.transcript.Write(hs.clientHello.marshal())
+ chHash := hs.transcript.Sum(nil)
+ hs.transcript.Reset()
+ hs.transcript.Write([]byte{typeMessageHash, 0, 0, uint8(len(chHash))})
+ hs.transcript.Write(chHash)
+
+ helloRetryRequest := &serverHelloMsg{
+ vers: hs.hello.vers,
+ random: helloRetryRequestRandom,
+ sessionId: hs.hello.sessionId,
+ cipherSuite: hs.hello.cipherSuite,
+ compressionMethod: hs.hello.compressionMethod,
+ supportedVersion: hs.hello.supportedVersion,
+ selectedGroup: selectedGroup,
+ }
+
+ hs.transcript.Write(helloRetryRequest.marshal())
+ if _, err := c.writeRecord(recordTypeHandshake, helloRetryRequest.marshal()); err != nil {
+ return err
+ }
+
+ if err := hs.sendDummyChangeCipherSpec(); err != nil {
+ return err
+ }
+
+ msg, err := c.readHandshake()
+ if err != nil {
+ return err
+ }
+
+ clientHello, ok := msg.(*clientHelloMsg)
+ if !ok {
+ c.sendAlert(alertUnexpectedMessage)
+ return unexpectedMessageError(clientHello, msg)
+ }
+
+ if len(clientHello.keyShares) != 1 || clientHello.keyShares[0].group != selectedGroup {
+ c.sendAlert(alertIllegalParameter)
+ return errors.New("tls: client sent invalid key share in second ClientHello")
+ }
+
+ if clientHello.earlyData {
+ c.sendAlert(alertIllegalParameter)
+ return errors.New("tls: client indicated early data in second ClientHello")
+ }
+
+ if illegalClientHelloChange(clientHello, hs.clientHello) {
+ c.sendAlert(alertIllegalParameter)
+ return errors.New("tls: client illegally modified second ClientHello")
+ }
+
+ if clientHello.earlyData {
+ c.sendAlert(alertIllegalParameter)
+ return errors.New("tls: client offered 0-RTT data in second ClientHello")
+ }
+
+ hs.clientHello = clientHello
+ return nil
+}
+
+// illegalClientHelloChange reports whether the two ClientHello messages are
+// different, with the exception of the changes allowed before and after a
+// HelloRetryRequest. See RFC 8446, Section 4.1.2.
+func illegalClientHelloChange(ch, ch1 *clientHelloMsg) bool {
+ if len(ch.supportedVersions) != len(ch1.supportedVersions) ||
+ len(ch.cipherSuites) != len(ch1.cipherSuites) ||
+ len(ch.supportedCurves) != len(ch1.supportedCurves) ||
+ len(ch.supportedSignatureAlgorithms) != len(ch1.supportedSignatureAlgorithms) ||
+ len(ch.supportedSignatureAlgorithmsCert) != len(ch1.supportedSignatureAlgorithmsCert) ||
+ len(ch.alpnProtocols) != len(ch1.alpnProtocols) {
+ return true
+ }
+ for i := range ch.supportedVersions {
+ if ch.supportedVersions[i] != ch1.supportedVersions[i] {
+ return true
+ }
+ }
+ for i := range ch.cipherSuites {
+ if ch.cipherSuites[i] != ch1.cipherSuites[i] {
+ return true
+ }
+ }
+ for i := range ch.supportedCurves {
+ if ch.supportedCurves[i] != ch1.supportedCurves[i] {
+ return true
+ }
+ }
+ for i := range ch.supportedSignatureAlgorithms {
+ if ch.supportedSignatureAlgorithms[i] != ch1.supportedSignatureAlgorithms[i] {
+ return true
+ }
+ }
+ for i := range ch.supportedSignatureAlgorithmsCert {
+ if ch.supportedSignatureAlgorithmsCert[i] != ch1.supportedSignatureAlgorithmsCert[i] {
+ return true
+ }
+ }
+ for i := range ch.alpnProtocols {
+ if ch.alpnProtocols[i] != ch1.alpnProtocols[i] {
+ return true
+ }
+ }
+ return ch.vers != ch1.vers ||
+ !bytes.Equal(ch.random, ch1.random) ||
+ !bytes.Equal(ch.sessionId, ch1.sessionId) ||
+ !bytes.Equal(ch.compressionMethods, ch1.compressionMethods) ||
+ ch.serverName != ch1.serverName ||
+ ch.ocspStapling != ch1.ocspStapling ||
+ !bytes.Equal(ch.supportedPoints, ch1.supportedPoints) ||
+ ch.ticketSupported != ch1.ticketSupported ||
+ !bytes.Equal(ch.sessionTicket, ch1.sessionTicket) ||
+ ch.secureRenegotiationSupported != ch1.secureRenegotiationSupported ||
+ !bytes.Equal(ch.secureRenegotiation, ch1.secureRenegotiation) ||
+ ch.scts != ch1.scts ||
+ !bytes.Equal(ch.cookie, ch1.cookie) ||
+ !bytes.Equal(ch.pskModes, ch1.pskModes)
+}
+
+func (hs *serverHandshakeStateTLS13) sendServerParameters() error {
+ c := hs.c
+
+ hs.transcript.Write(hs.clientHello.marshal())
+ hs.transcript.Write(hs.hello.marshal())
+ if _, err := c.writeRecord(recordTypeHandshake, hs.hello.marshal()); err != nil {
+ return err
+ }
+
+ if err := hs.sendDummyChangeCipherSpec(); err != nil {
+ return err
+ }
+
+ earlySecret := hs.earlySecret
+ if earlySecret == nil {
+ earlySecret = hs.suite.extract(nil, nil)
+ }
+ hs.handshakeSecret = hs.suite.extract(hs.sharedKey,
+ hs.suite.deriveSecret(earlySecret, "derived", nil))
+
+ clientSecret := hs.suite.deriveSecret(hs.handshakeSecret,
+ clientHandshakeTrafficLabel, hs.transcript)
+ c.in.exportKey(EncryptionHandshake, hs.suite, clientSecret)
+ c.in.setTrafficSecret(hs.suite, clientSecret)
+ serverSecret := hs.suite.deriveSecret(hs.handshakeSecret,
+ serverHandshakeTrafficLabel, hs.transcript)
+ c.out.exportKey(EncryptionHandshake, hs.suite, serverSecret)
+ c.out.setTrafficSecret(hs.suite, serverSecret)
+
+ err := c.config.writeKeyLog(keyLogLabelClientHandshake, hs.clientHello.random, clientSecret)
+ if err != nil {
+ c.sendAlert(alertInternalError)
+ return err
+ }
+ err = c.config.writeKeyLog(keyLogLabelServerHandshake, hs.clientHello.random, serverSecret)
+ if err != nil {
+ c.sendAlert(alertInternalError)
+ return err
+ }
+
+ if hs.alpnNegotiationErr != nil {
+ c.sendAlert(alertNoApplicationProtocol)
+ return hs.alpnNegotiationErr
+ }
+ if hs.c.extraConfig != nil && hs.c.extraConfig.GetExtensions != nil {
+ hs.encryptedExtensions.additionalExtensions = hs.c.extraConfig.GetExtensions(typeEncryptedExtensions)
+ }
+
+ hs.transcript.Write(hs.encryptedExtensions.marshal())
+ if _, err := c.writeRecord(recordTypeHandshake, hs.encryptedExtensions.marshal()); err != nil {
+ return err
+ }
+
+ return nil
+}
+
+func (hs *serverHandshakeStateTLS13) requestClientCert() bool {
+ return hs.c.config.ClientAuth >= RequestClientCert && !hs.usingPSK
+}
+
+func (hs *serverHandshakeStateTLS13) sendServerCertificate() error {
+ c := hs.c
+
+ // Only one of PSK and certificates are used at a time.
+ if hs.usingPSK {
+ return nil
+ }
+
+ if hs.requestClientCert() {
+ // Request a client certificate
+ certReq := new(certificateRequestMsgTLS13)
+ certReq.ocspStapling = true
+ certReq.scts = true
+ certReq.supportedSignatureAlgorithms = supportedSignatureAlgorithms()
+ if c.config.ClientCAs != nil {
+ certReq.certificateAuthorities = c.config.ClientCAs.Subjects()
+ }
+
+ hs.transcript.Write(certReq.marshal())
+ if _, err := c.writeRecord(recordTypeHandshake, certReq.marshal()); err != nil {
+ return err
+ }
+ }
+
+ certMsg := new(certificateMsgTLS13)
+
+ certMsg.certificate = *hs.cert
+ certMsg.scts = hs.clientHello.scts && len(hs.cert.SignedCertificateTimestamps) > 0
+ certMsg.ocspStapling = hs.clientHello.ocspStapling && len(hs.cert.OCSPStaple) > 0
+
+ hs.transcript.Write(certMsg.marshal())
+ if _, err := c.writeRecord(recordTypeHandshake, certMsg.marshal()); err != nil {
+ return err
+ }
+
+ certVerifyMsg := new(certificateVerifyMsg)
+ certVerifyMsg.hasSignatureAlgorithm = true
+ certVerifyMsg.signatureAlgorithm = hs.sigAlg
+
+ sigType, sigHash, err := typeAndHashFromSignatureScheme(hs.sigAlg)
+ if err != nil {
+ return c.sendAlert(alertInternalError)
+ }
+
+ signed := signedMessage(sigHash, serverSignatureContext, hs.transcript)
+ signOpts := crypto.SignerOpts(sigHash)
+ if sigType == signatureRSAPSS {
+ signOpts = &rsa.PSSOptions{SaltLength: rsa.PSSSaltLengthEqualsHash, Hash: sigHash}
+ }
+ sig, err := hs.cert.PrivateKey.(crypto.Signer).Sign(c.config.rand(), signed, signOpts)
+ if err != nil {
+ public := hs.cert.PrivateKey.(crypto.Signer).Public()
+ if rsaKey, ok := public.(*rsa.PublicKey); ok && sigType == signatureRSAPSS &&
+ rsaKey.N.BitLen()/8 < sigHash.Size()*2+2 { // key too small for RSA-PSS
+ c.sendAlert(alertHandshakeFailure)
+ } else {
+ c.sendAlert(alertInternalError)
+ }
+ return errors.New("tls: failed to sign handshake: " + err.Error())
+ }
+ certVerifyMsg.signature = sig
+
+ hs.transcript.Write(certVerifyMsg.marshal())
+ if _, err := c.writeRecord(recordTypeHandshake, certVerifyMsg.marshal()); err != nil {
+ return err
+ }
+
+ return nil
+}
+
+func (hs *serverHandshakeStateTLS13) sendServerFinished() error {
+ c := hs.c
+
+ finished := &finishedMsg{
+ verifyData: hs.suite.finishedHash(c.out.trafficSecret, hs.transcript),
+ }
+
+ hs.transcript.Write(finished.marshal())
+ if _, err := c.writeRecord(recordTypeHandshake, finished.marshal()); err != nil {
+ return err
+ }
+
+ // Derive secrets that take context through the server Finished.
+
+ hs.masterSecret = hs.suite.extract(nil,
+ hs.suite.deriveSecret(hs.handshakeSecret, "derived", nil))
+
+ hs.trafficSecret = hs.suite.deriveSecret(hs.masterSecret,
+ clientApplicationTrafficLabel, hs.transcript)
+ serverSecret := hs.suite.deriveSecret(hs.masterSecret,
+ serverApplicationTrafficLabel, hs.transcript)
+ c.out.exportKey(EncryptionApplication, hs.suite, serverSecret)
+ c.out.setTrafficSecret(hs.suite, serverSecret)
+
+ err := c.config.writeKeyLog(keyLogLabelClientTraffic, hs.clientHello.random, hs.trafficSecret)
+ if err != nil {
+ c.sendAlert(alertInternalError)
+ return err
+ }
+ err = c.config.writeKeyLog(keyLogLabelServerTraffic, hs.clientHello.random, serverSecret)
+ if err != nil {
+ c.sendAlert(alertInternalError)
+ return err
+ }
+
+ c.ekm = hs.suite.exportKeyingMaterial(hs.masterSecret, hs.transcript)
+
+ // If we did not request client certificates, at this point we can
+ // precompute the client finished and roll the transcript forward to send
+ // session tickets in our first flight.
+ if !hs.requestClientCert() {
+ if err := hs.sendSessionTickets(); err != nil {
+ return err
+ }
+ }
+
+ return nil
+}
+
+func (hs *serverHandshakeStateTLS13) shouldSendSessionTickets() bool {
+ if hs.c.config.SessionTicketsDisabled {
+ return false
+ }
+
+ // Don't send tickets the client wouldn't use. See RFC 8446, Section 4.2.9.
+ for _, pskMode := range hs.clientHello.pskModes {
+ if pskMode == pskModeDHE {
+ return true
+ }
+ }
+ return false
+}
+
+func (hs *serverHandshakeStateTLS13) sendSessionTickets() error {
+ c := hs.c
+
+ hs.clientFinished = hs.suite.finishedHash(c.in.trafficSecret, hs.transcript)
+ finishedMsg := &finishedMsg{
+ verifyData: hs.clientFinished,
+ }
+ hs.transcript.Write(finishedMsg.marshal())
+
+ if !hs.shouldSendSessionTickets() {
+ return nil
+ }
+
+ c.resumptionSecret = hs.suite.deriveSecret(hs.masterSecret,
+ resumptionLabel, hs.transcript)
+
+ // Don't send session tickets when the alternative record layer is set.
+ // Instead, save the resumption secret on the Conn.
+ // Session tickets can then be generated by calling Conn.GetSessionTicket().
+ if hs.c.extraConfig != nil && hs.c.extraConfig.AlternativeRecordLayer != nil {
+ return nil
+ }
+
+ m, err := hs.c.getSessionTicketMsg(nil)
+ if err != nil {
+ return err
+ }
+
+ if _, err := c.writeRecord(recordTypeHandshake, m.marshal()); err != nil {
+ return err
+ }
+
+ return nil
+}
+
+func (hs *serverHandshakeStateTLS13) readClientCertificate() error {
+ c := hs.c
+
+ if !hs.requestClientCert() {
+ // Make sure the connection is still being verified whether or not
+ // the server requested a client certificate.
+ if c.config.VerifyConnection != nil {
+ if err := c.config.VerifyConnection(c.connectionStateLocked()); err != nil {
+ c.sendAlert(alertBadCertificate)
+ return err
+ }
+ }
+ return nil
+ }
+
+ // If we requested a client certificate, then the client must send a
+ // certificate message. If it's empty, no CertificateVerify is sent.
+
+ msg, err := c.readHandshake()
+ if err != nil {
+ return err
+ }
+
+ certMsg, ok := msg.(*certificateMsgTLS13)
+ if !ok {
+ c.sendAlert(alertUnexpectedMessage)
+ return unexpectedMessageError(certMsg, msg)
+ }
+ hs.transcript.Write(certMsg.marshal())
+
+ if err := c.processCertsFromClient(certMsg.certificate); err != nil {
+ return err
+ }
+
+ if c.config.VerifyConnection != nil {
+ if err := c.config.VerifyConnection(c.connectionStateLocked()); err != nil {
+ c.sendAlert(alertBadCertificate)
+ return err
+ }
+ }
+
+ if len(certMsg.certificate.Certificate) != 0 {
+ msg, err = c.readHandshake()
+ if err != nil {
+ return err
+ }
+
+ certVerify, ok := msg.(*certificateVerifyMsg)
+ if !ok {
+ c.sendAlert(alertUnexpectedMessage)
+ return unexpectedMessageError(certVerify, msg)
+ }
+
+ // See RFC 8446, Section 4.4.3.
+ if !isSupportedSignatureAlgorithm(certVerify.signatureAlgorithm, supportedSignatureAlgorithms()) {
+ c.sendAlert(alertIllegalParameter)
+ return errors.New("tls: client certificate used with invalid signature algorithm")
+ }
+ sigType, sigHash, err := typeAndHashFromSignatureScheme(certVerify.signatureAlgorithm)
+ if err != nil {
+ return c.sendAlert(alertInternalError)
+ }
+ if sigType == signaturePKCS1v15 || sigHash == crypto.SHA1 {
+ c.sendAlert(alertIllegalParameter)
+ return errors.New("tls: client certificate used with invalid signature algorithm")
+ }
+ signed := signedMessage(sigHash, clientSignatureContext, hs.transcript)
+ if err := verifyHandshakeSignature(sigType, c.peerCertificates[0].PublicKey,
+ sigHash, signed, certVerify.signature); err != nil {
+ c.sendAlert(alertDecryptError)
+ return errors.New("tls: invalid signature by the client certificate: " + err.Error())
+ }
+
+ hs.transcript.Write(certVerify.marshal())
+ }
+
+ // If we waited until the client certificates to send session tickets, we
+ // are ready to do it now.
+ if err := hs.sendSessionTickets(); err != nil {
+ return err
+ }
+
+ return nil
+}
+
+func (hs *serverHandshakeStateTLS13) readClientFinished() error {
+ c := hs.c
+
+ msg, err := c.readHandshake()
+ if err != nil {
+ return err
+ }
+
+ finished, ok := msg.(*finishedMsg)
+ if !ok {
+ c.sendAlert(alertUnexpectedMessage)
+ return unexpectedMessageError(finished, msg)
+ }
+
+ if !hmac.Equal(hs.clientFinished, finished.verifyData) {
+ c.sendAlert(alertDecryptError)
+ return errors.New("tls: invalid client finished hash")
+ }
+
+ c.in.exportKey(EncryptionApplication, hs.suite, hs.trafficSecret)
+ c.in.setTrafficSecret(hs.suite, hs.trafficSecret)
+
+ return nil
+}
diff --git a/vendor/github.com/quic-go/qtls-go1-20/key_agreement.go b/vendor/github.com/quic-go/qtls-go1-20/key_agreement.go
new file mode 100644
index 0000000000..f926869a1b
--- /dev/null
+++ b/vendor/github.com/quic-go/qtls-go1-20/key_agreement.go
@@ -0,0 +1,366 @@
+// Copyright 2010 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package qtls
+
+import (
+ "crypto"
+ "crypto/ecdh"
+ "crypto/md5"
+ "crypto/rsa"
+ "crypto/sha1"
+ "crypto/x509"
+ "errors"
+ "fmt"
+ "io"
+)
+
+// a keyAgreement implements the client and server side of a TLS key agreement
+// protocol by generating and processing key exchange messages.
+type keyAgreement interface {
+ // On the server side, the first two methods are called in order.
+
+ // In the case that the key agreement protocol doesn't use a
+ // ServerKeyExchange message, generateServerKeyExchange can return nil,
+ // nil.
+ generateServerKeyExchange(*config, *Certificate, *clientHelloMsg, *serverHelloMsg) (*serverKeyExchangeMsg, error)
+ processClientKeyExchange(*config, *Certificate, *clientKeyExchangeMsg, uint16) ([]byte, error)
+
+ // On the client side, the next two methods are called in order.
+
+ // This method may not be called if the server doesn't send a
+ // ServerKeyExchange message.
+ processServerKeyExchange(*config, *clientHelloMsg, *serverHelloMsg, *x509.Certificate, *serverKeyExchangeMsg) error
+ generateClientKeyExchange(*config, *clientHelloMsg, *x509.Certificate) ([]byte, *clientKeyExchangeMsg, error)
+}
+
+var errClientKeyExchange = errors.New("tls: invalid ClientKeyExchange message")
+var errServerKeyExchange = errors.New("tls: invalid ServerKeyExchange message")
+
+// rsaKeyAgreement implements the standard TLS key agreement where the client
+// encrypts the pre-master secret to the server's public key.
+type rsaKeyAgreement struct{}
+
+func (ka rsaKeyAgreement) generateServerKeyExchange(config *config, cert *Certificate, clientHello *clientHelloMsg, hello *serverHelloMsg) (*serverKeyExchangeMsg, error) {
+ return nil, nil
+}
+
+func (ka rsaKeyAgreement) processClientKeyExchange(config *config, cert *Certificate, ckx *clientKeyExchangeMsg, version uint16) ([]byte, error) {
+ if len(ckx.ciphertext) < 2 {
+ return nil, errClientKeyExchange
+ }
+ ciphertextLen := int(ckx.ciphertext[0])<<8 | int(ckx.ciphertext[1])
+ if ciphertextLen != len(ckx.ciphertext)-2 {
+ return nil, errClientKeyExchange
+ }
+ ciphertext := ckx.ciphertext[2:]
+
+ priv, ok := cert.PrivateKey.(crypto.Decrypter)
+ if !ok {
+ return nil, errors.New("tls: certificate private key does not implement crypto.Decrypter")
+ }
+ // Perform constant time RSA PKCS #1 v1.5 decryption
+ preMasterSecret, err := priv.Decrypt(config.rand(), ciphertext, &rsa.PKCS1v15DecryptOptions{SessionKeyLen: 48})
+ if err != nil {
+ return nil, err
+ }
+ // We don't check the version number in the premaster secret. For one,
+ // by checking it, we would leak information about the validity of the
+ // encrypted pre-master secret. Secondly, it provides only a small
+ // benefit against a downgrade attack and some implementations send the
+ // wrong version anyway. See the discussion at the end of section
+ // 7.4.7.1 of RFC 4346.
+ return preMasterSecret, nil
+}
+
+func (ka rsaKeyAgreement) processServerKeyExchange(config *config, clientHello *clientHelloMsg, serverHello *serverHelloMsg, cert *x509.Certificate, skx *serverKeyExchangeMsg) error {
+ return errors.New("tls: unexpected ServerKeyExchange")
+}
+
+func (ka rsaKeyAgreement) generateClientKeyExchange(config *config, clientHello *clientHelloMsg, cert *x509.Certificate) ([]byte, *clientKeyExchangeMsg, error) {
+ preMasterSecret := make([]byte, 48)
+ preMasterSecret[0] = byte(clientHello.vers >> 8)
+ preMasterSecret[1] = byte(clientHello.vers)
+ _, err := io.ReadFull(config.rand(), preMasterSecret[2:])
+ if err != nil {
+ return nil, nil, err
+ }
+
+ rsaKey, ok := cert.PublicKey.(*rsa.PublicKey)
+ if !ok {
+ return nil, nil, errors.New("tls: server certificate contains incorrect key type for selected ciphersuite")
+ }
+ encrypted, err := rsa.EncryptPKCS1v15(config.rand(), rsaKey, preMasterSecret)
+ if err != nil {
+ return nil, nil, err
+ }
+ ckx := new(clientKeyExchangeMsg)
+ ckx.ciphertext = make([]byte, len(encrypted)+2)
+ ckx.ciphertext[0] = byte(len(encrypted) >> 8)
+ ckx.ciphertext[1] = byte(len(encrypted))
+ copy(ckx.ciphertext[2:], encrypted)
+ return preMasterSecret, ckx, nil
+}
+
+// sha1Hash calculates a SHA1 hash over the given byte slices.
+func sha1Hash(slices [][]byte) []byte {
+ hsha1 := sha1.New()
+ for _, slice := range slices {
+ hsha1.Write(slice)
+ }
+ return hsha1.Sum(nil)
+}
+
+// md5SHA1Hash implements TLS 1.0's hybrid hash function which consists of the
+// concatenation of an MD5 and SHA1 hash.
+func md5SHA1Hash(slices [][]byte) []byte {
+ md5sha1 := make([]byte, md5.Size+sha1.Size)
+ hmd5 := md5.New()
+ for _, slice := range slices {
+ hmd5.Write(slice)
+ }
+ copy(md5sha1, hmd5.Sum(nil))
+ copy(md5sha1[md5.Size:], sha1Hash(slices))
+ return md5sha1
+}
+
+// hashForServerKeyExchange hashes the given slices and returns their digest
+// using the given hash function (for >= TLS 1.2) or using a default based on
+// the sigType (for earlier TLS versions). For Ed25519 signatures, which don't
+// do pre-hashing, it returns the concatenation of the slices.
+func hashForServerKeyExchange(sigType uint8, hashFunc crypto.Hash, version uint16, slices ...[]byte) []byte {
+ if sigType == signatureEd25519 {
+ var signed []byte
+ for _, slice := range slices {
+ signed = append(signed, slice...)
+ }
+ return signed
+ }
+ if version >= VersionTLS12 {
+ h := hashFunc.New()
+ for _, slice := range slices {
+ h.Write(slice)
+ }
+ digest := h.Sum(nil)
+ return digest
+ }
+ if sigType == signatureECDSA {
+ return sha1Hash(slices)
+ }
+ return md5SHA1Hash(slices)
+}
+
+// ecdheKeyAgreement implements a TLS key agreement where the server
+// generates an ephemeral EC public/private key pair and signs it. The
+// pre-master secret is then calculated using ECDH. The signature may
+// be ECDSA, Ed25519 or RSA.
+type ecdheKeyAgreement struct {
+ version uint16
+ isRSA bool
+ key *ecdh.PrivateKey
+
+ // ckx and preMasterSecret are generated in processServerKeyExchange
+ // and returned in generateClientKeyExchange.
+ ckx *clientKeyExchangeMsg
+ preMasterSecret []byte
+}
+
+func (ka *ecdheKeyAgreement) generateServerKeyExchange(config *config, cert *Certificate, clientHello *clientHelloMsg, hello *serverHelloMsg) (*serverKeyExchangeMsg, error) {
+ var curveID CurveID
+ for _, c := range clientHello.supportedCurves {
+ if config.supportsCurve(c) {
+ curveID = c
+ break
+ }
+ }
+
+ if curveID == 0 {
+ return nil, errors.New("tls: no supported elliptic curves offered")
+ }
+ if _, ok := curveForCurveID(curveID); !ok {
+ return nil, errors.New("tls: CurvePreferences includes unsupported curve")
+ }
+
+ key, err := generateECDHEKey(config.rand(), curveID)
+ if err != nil {
+ return nil, err
+ }
+ ka.key = key
+
+ // See RFC 4492, Section 5.4.
+ ecdhePublic := key.PublicKey().Bytes()
+ serverECDHEParams := make([]byte, 1+2+1+len(ecdhePublic))
+ serverECDHEParams[0] = 3 // named curve
+ serverECDHEParams[1] = byte(curveID >> 8)
+ serverECDHEParams[2] = byte(curveID)
+ serverECDHEParams[3] = byte(len(ecdhePublic))
+ copy(serverECDHEParams[4:], ecdhePublic)
+
+ priv, ok := cert.PrivateKey.(crypto.Signer)
+ if !ok {
+ return nil, fmt.Errorf("tls: certificate private key of type %T does not implement crypto.Signer", cert.PrivateKey)
+ }
+
+ var signatureAlgorithm SignatureScheme
+ var sigType uint8
+ var sigHash crypto.Hash
+ if ka.version >= VersionTLS12 {
+ signatureAlgorithm, err = selectSignatureScheme(ka.version, cert, clientHello.supportedSignatureAlgorithms)
+ if err != nil {
+ return nil, err
+ }
+ sigType, sigHash, err = typeAndHashFromSignatureScheme(signatureAlgorithm)
+ if err != nil {
+ return nil, err
+ }
+ } else {
+ sigType, sigHash, err = legacyTypeAndHashFromPublicKey(priv.Public())
+ if err != nil {
+ return nil, err
+ }
+ }
+ if (sigType == signaturePKCS1v15 || sigType == signatureRSAPSS) != ka.isRSA {
+ return nil, errors.New("tls: certificate cannot be used with the selected cipher suite")
+ }
+
+ signed := hashForServerKeyExchange(sigType, sigHash, ka.version, clientHello.random, hello.random, serverECDHEParams)
+
+ signOpts := crypto.SignerOpts(sigHash)
+ if sigType == signatureRSAPSS {
+ signOpts = &rsa.PSSOptions{SaltLength: rsa.PSSSaltLengthEqualsHash, Hash: sigHash}
+ }
+ sig, err := priv.Sign(config.rand(), signed, signOpts)
+ if err != nil {
+ return nil, errors.New("tls: failed to sign ECDHE parameters: " + err.Error())
+ }
+
+ skx := new(serverKeyExchangeMsg)
+ sigAndHashLen := 0
+ if ka.version >= VersionTLS12 {
+ sigAndHashLen = 2
+ }
+ skx.key = make([]byte, len(serverECDHEParams)+sigAndHashLen+2+len(sig))
+ copy(skx.key, serverECDHEParams)
+ k := skx.key[len(serverECDHEParams):]
+ if ka.version >= VersionTLS12 {
+ k[0] = byte(signatureAlgorithm >> 8)
+ k[1] = byte(signatureAlgorithm)
+ k = k[2:]
+ }
+ k[0] = byte(len(sig) >> 8)
+ k[1] = byte(len(sig))
+ copy(k[2:], sig)
+
+ return skx, nil
+}
+
+func (ka *ecdheKeyAgreement) processClientKeyExchange(config *config, cert *Certificate, ckx *clientKeyExchangeMsg, version uint16) ([]byte, error) {
+ if len(ckx.ciphertext) == 0 || int(ckx.ciphertext[0]) != len(ckx.ciphertext)-1 {
+ return nil, errClientKeyExchange
+ }
+
+ peerKey, err := ka.key.Curve().NewPublicKey(ckx.ciphertext[1:])
+ if err != nil {
+ return nil, errClientKeyExchange
+ }
+ preMasterSecret, err := ka.key.ECDH(peerKey)
+ if err != nil {
+ return nil, errClientKeyExchange
+ }
+
+ return preMasterSecret, nil
+}
+
+func (ka *ecdheKeyAgreement) processServerKeyExchange(config *config, clientHello *clientHelloMsg, serverHello *serverHelloMsg, cert *x509.Certificate, skx *serverKeyExchangeMsg) error {
+ if len(skx.key) < 4 {
+ return errServerKeyExchange
+ }
+ if skx.key[0] != 3 { // named curve
+ return errors.New("tls: server selected unsupported curve")
+ }
+ curveID := CurveID(skx.key[1])<<8 | CurveID(skx.key[2])
+
+ publicLen := int(skx.key[3])
+ if publicLen+4 > len(skx.key) {
+ return errServerKeyExchange
+ }
+ serverECDHEParams := skx.key[:4+publicLen]
+ publicKey := serverECDHEParams[4:]
+
+ sig := skx.key[4+publicLen:]
+ if len(sig) < 2 {
+ return errServerKeyExchange
+ }
+
+ if _, ok := curveForCurveID(curveID); !ok {
+ return errors.New("tls: server selected unsupported curve")
+ }
+
+ key, err := generateECDHEKey(config.rand(), curveID)
+ if err != nil {
+ return err
+ }
+ ka.key = key
+
+ peerKey, err := key.Curve().NewPublicKey(publicKey)
+ if err != nil {
+ return errServerKeyExchange
+ }
+ ka.preMasterSecret, err = key.ECDH(peerKey)
+ if err != nil {
+ return errServerKeyExchange
+ }
+
+ ourPublicKey := key.PublicKey().Bytes()
+ ka.ckx = new(clientKeyExchangeMsg)
+ ka.ckx.ciphertext = make([]byte, 1+len(ourPublicKey))
+ ka.ckx.ciphertext[0] = byte(len(ourPublicKey))
+ copy(ka.ckx.ciphertext[1:], ourPublicKey)
+
+ var sigType uint8
+ var sigHash crypto.Hash
+ if ka.version >= VersionTLS12 {
+ signatureAlgorithm := SignatureScheme(sig[0])<<8 | SignatureScheme(sig[1])
+ sig = sig[2:]
+ if len(sig) < 2 {
+ return errServerKeyExchange
+ }
+
+ if !isSupportedSignatureAlgorithm(signatureAlgorithm, clientHello.supportedSignatureAlgorithms) {
+ return errors.New("tls: certificate used with invalid signature algorithm")
+ }
+ sigType, sigHash, err = typeAndHashFromSignatureScheme(signatureAlgorithm)
+ if err != nil {
+ return err
+ }
+ } else {
+ sigType, sigHash, err = legacyTypeAndHashFromPublicKey(cert.PublicKey)
+ if err != nil {
+ return err
+ }
+ }
+ if (sigType == signaturePKCS1v15 || sigType == signatureRSAPSS) != ka.isRSA {
+ return errServerKeyExchange
+ }
+
+ sigLen := int(sig[0])<<8 | int(sig[1])
+ if sigLen+2 != len(sig) {
+ return errServerKeyExchange
+ }
+ sig = sig[2:]
+
+ signed := hashForServerKeyExchange(sigType, sigHash, ka.version, clientHello.random, serverHello.random, serverECDHEParams)
+ if err := verifyHandshakeSignature(sigType, cert.PublicKey, sigHash, signed, sig); err != nil {
+ return errors.New("tls: invalid signature by the server certificate: " + err.Error())
+ }
+ return nil
+}
+
+func (ka *ecdheKeyAgreement) generateClientKeyExchange(config *config, clientHello *clientHelloMsg, cert *x509.Certificate) ([]byte, *clientKeyExchangeMsg, error) {
+ if ka.ckx == nil {
+ return nil, nil, errors.New("tls: missing ServerKeyExchange message")
+ }
+
+ return ka.preMasterSecret, ka.ckx, nil
+}
diff --git a/vendor/github.com/quic-go/qtls-go1-20/key_schedule.go b/vendor/github.com/quic-go/qtls-go1-20/key_schedule.go
new file mode 100644
index 0000000000..ef6d1ba29d
--- /dev/null
+++ b/vendor/github.com/quic-go/qtls-go1-20/key_schedule.go
@@ -0,0 +1,141 @@
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package qtls
+
+import (
+ "crypto/ecdh"
+ "crypto/hmac"
+ "errors"
+ "hash"
+ "io"
+
+ "golang.org/x/crypto/cryptobyte"
+ "golang.org/x/crypto/hkdf"
+)
+
+// This file contains the functions necessary to compute the TLS 1.3 key
+// schedule. See RFC 8446, Section 7.
+
+const (
+ resumptionBinderLabel = "res binder"
+ clientHandshakeTrafficLabel = "c hs traffic"
+ serverHandshakeTrafficLabel = "s hs traffic"
+ clientApplicationTrafficLabel = "c ap traffic"
+ serverApplicationTrafficLabel = "s ap traffic"
+ exporterLabel = "exp master"
+ resumptionLabel = "res master"
+ trafficUpdateLabel = "traffic upd"
+)
+
+// expandLabel implements HKDF-Expand-Label from RFC 8446, Section 7.1.
+func (c *cipherSuiteTLS13) expandLabel(secret []byte, label string, context []byte, length int) []byte {
+ var hkdfLabel cryptobyte.Builder
+ hkdfLabel.AddUint16(uint16(length))
+ hkdfLabel.AddUint8LengthPrefixed(func(b *cryptobyte.Builder) {
+ b.AddBytes([]byte("tls13 "))
+ b.AddBytes([]byte(label))
+ })
+ hkdfLabel.AddUint8LengthPrefixed(func(b *cryptobyte.Builder) {
+ b.AddBytes(context)
+ })
+ out := make([]byte, length)
+ n, err := hkdf.Expand(c.hash.New, secret, hkdfLabel.BytesOrPanic()).Read(out)
+ if err != nil || n != length {
+ panic("tls: HKDF-Expand-Label invocation failed unexpectedly")
+ }
+ return out
+}
+
+// deriveSecret implements Derive-Secret from RFC 8446, Section 7.1.
+func (c *cipherSuiteTLS13) deriveSecret(secret []byte, label string, transcript hash.Hash) []byte {
+ if transcript == nil {
+ transcript = c.hash.New()
+ }
+ return c.expandLabel(secret, label, transcript.Sum(nil), c.hash.Size())
+}
+
+// extract implements HKDF-Extract with the cipher suite hash.
+func (c *cipherSuiteTLS13) extract(newSecret, currentSecret []byte) []byte {
+ if newSecret == nil {
+ newSecret = make([]byte, c.hash.Size())
+ }
+ return hkdf.Extract(c.hash.New, newSecret, currentSecret)
+}
+
+// nextTrafficSecret generates the next traffic secret, given the current one,
+// according to RFC 8446, Section 7.2.
+func (c *cipherSuiteTLS13) nextTrafficSecret(trafficSecret []byte) []byte {
+ return c.expandLabel(trafficSecret, trafficUpdateLabel, nil, c.hash.Size())
+}
+
+// trafficKey generates traffic keys according to RFC 8446, Section 7.3.
+func (c *cipherSuiteTLS13) trafficKey(trafficSecret []byte) (key, iv []byte) {
+ key = c.expandLabel(trafficSecret, "key", nil, c.keyLen)
+ iv = c.expandLabel(trafficSecret, "iv", nil, aeadNonceLength)
+ return
+}
+
+// finishedHash generates the Finished verify_data or PskBinderEntry according
+// to RFC 8446, Section 4.4.4. See sections 4.4 and 4.2.11.2 for the baseKey
+// selection.
+func (c *cipherSuiteTLS13) finishedHash(baseKey []byte, transcript hash.Hash) []byte {
+ finishedKey := c.expandLabel(baseKey, "finished", nil, c.hash.Size())
+ verifyData := hmac.New(c.hash.New, finishedKey)
+ verifyData.Write(transcript.Sum(nil))
+ return verifyData.Sum(nil)
+}
+
+// exportKeyingMaterial implements RFC5705 exporters for TLS 1.3 according to
+// RFC 8446, Section 7.5.
+func (c *cipherSuiteTLS13) exportKeyingMaterial(masterSecret []byte, transcript hash.Hash) func(string, []byte, int) ([]byte, error) {
+ expMasterSecret := c.deriveSecret(masterSecret, exporterLabel, transcript)
+ return func(label string, context []byte, length int) ([]byte, error) {
+ secret := c.deriveSecret(expMasterSecret, label, nil)
+ h := c.hash.New()
+ h.Write(context)
+ return c.expandLabel(secret, "exporter", h.Sum(nil), length), nil
+ }
+}
+
+// generateECDHEKey returns a PrivateKey that implements Diffie-Hellman
+// according to RFC 8446, Section 4.2.8.2.
+func generateECDHEKey(rand io.Reader, curveID CurveID) (*ecdh.PrivateKey, error) {
+ curve, ok := curveForCurveID(curveID)
+ if !ok {
+ return nil, errors.New("tls: internal error: unsupported curve")
+ }
+
+ return curve.GenerateKey(rand)
+}
+
+func curveForCurveID(id CurveID) (ecdh.Curve, bool) {
+ switch id {
+ case X25519:
+ return ecdh.X25519(), true
+ case CurveP256:
+ return ecdh.P256(), true
+ case CurveP384:
+ return ecdh.P384(), true
+ case CurveP521:
+ return ecdh.P521(), true
+ default:
+ return nil, false
+ }
+}
+
+func curveIDForCurve(curve ecdh.Curve) (CurveID, bool) {
+ switch curve {
+ case ecdh.X25519():
+ return X25519, true
+ case ecdh.P256():
+ return CurveP256, true
+ case ecdh.P384():
+ return CurveP384, true
+ case ecdh.P521():
+ return CurveP521, true
+ default:
+ return 0, false
+ }
+}
diff --git a/vendor/github.com/quic-go/qtls-go1-20/notboring.go b/vendor/github.com/quic-go/qtls-go1-20/notboring.go
new file mode 100644
index 0000000000..f292e4f028
--- /dev/null
+++ b/vendor/github.com/quic-go/qtls-go1-20/notboring.go
@@ -0,0 +1,18 @@
+// Copyright 2022 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package qtls
+
+func needFIPS() bool { return false }
+
+func supportedSignatureAlgorithms() []SignatureScheme {
+ return defaultSupportedSignatureAlgorithms
+}
+
+func fipsMinVersion(c *config) uint16 { panic("fipsMinVersion") }
+func fipsMaxVersion(c *config) uint16 { panic("fipsMaxVersion") }
+func fipsCurvePreferences(c *config) []CurveID { panic("fipsCurvePreferences") }
+func fipsCipherSuites(c *config) []uint16 { panic("fipsCipherSuites") }
+
+var fipsSupportedSignatureAlgorithms []SignatureScheme
diff --git a/vendor/github.com/quic-go/qtls-go1-20/prf.go b/vendor/github.com/quic-go/qtls-go1-20/prf.go
new file mode 100644
index 0000000000..1471289182
--- /dev/null
+++ b/vendor/github.com/quic-go/qtls-go1-20/prf.go
@@ -0,0 +1,283 @@
+// Copyright 2009 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package qtls
+
+import (
+ "crypto"
+ "crypto/hmac"
+ "crypto/md5"
+ "crypto/sha1"
+ "crypto/sha256"
+ "crypto/sha512"
+ "errors"
+ "fmt"
+ "hash"
+)
+
+// Split a premaster secret in two as specified in RFC 4346, Section 5.
+func splitPreMasterSecret(secret []byte) (s1, s2 []byte) {
+ s1 = secret[0 : (len(secret)+1)/2]
+ s2 = secret[len(secret)/2:]
+ return
+}
+
+// pHash implements the P_hash function, as defined in RFC 4346, Section 5.
+func pHash(result, secret, seed []byte, hash func() hash.Hash) {
+ h := hmac.New(hash, secret)
+ h.Write(seed)
+ a := h.Sum(nil)
+
+ j := 0
+ for j < len(result) {
+ h.Reset()
+ h.Write(a)
+ h.Write(seed)
+ b := h.Sum(nil)
+ copy(result[j:], b)
+ j += len(b)
+
+ h.Reset()
+ h.Write(a)
+ a = h.Sum(nil)
+ }
+}
+
+// prf10 implements the TLS 1.0 pseudo-random function, as defined in RFC 2246, Section 5.
+func prf10(result, secret, label, seed []byte) {
+ hashSHA1 := sha1.New
+ hashMD5 := md5.New
+
+ labelAndSeed := make([]byte, len(label)+len(seed))
+ copy(labelAndSeed, label)
+ copy(labelAndSeed[len(label):], seed)
+
+ s1, s2 := splitPreMasterSecret(secret)
+ pHash(result, s1, labelAndSeed, hashMD5)
+ result2 := make([]byte, len(result))
+ pHash(result2, s2, labelAndSeed, hashSHA1)
+
+ for i, b := range result2 {
+ result[i] ^= b
+ }
+}
+
+// prf12 implements the TLS 1.2 pseudo-random function, as defined in RFC 5246, Section 5.
+func prf12(hashFunc func() hash.Hash) func(result, secret, label, seed []byte) {
+ return func(result, secret, label, seed []byte) {
+ labelAndSeed := make([]byte, len(label)+len(seed))
+ copy(labelAndSeed, label)
+ copy(labelAndSeed[len(label):], seed)
+
+ pHash(result, secret, labelAndSeed, hashFunc)
+ }
+}
+
+const (
+ masterSecretLength = 48 // Length of a master secret in TLS 1.1.
+ finishedVerifyLength = 12 // Length of verify_data in a Finished message.
+)
+
+var masterSecretLabel = []byte("master secret")
+var keyExpansionLabel = []byte("key expansion")
+var clientFinishedLabel = []byte("client finished")
+var serverFinishedLabel = []byte("server finished")
+
+func prfAndHashForVersion(version uint16, suite *cipherSuite) (func(result, secret, label, seed []byte), crypto.Hash) {
+ switch version {
+ case VersionTLS10, VersionTLS11:
+ return prf10, crypto.Hash(0)
+ case VersionTLS12:
+ if suite.flags&suiteSHA384 != 0 {
+ return prf12(sha512.New384), crypto.SHA384
+ }
+ return prf12(sha256.New), crypto.SHA256
+ default:
+ panic("unknown version")
+ }
+}
+
+func prfForVersion(version uint16, suite *cipherSuite) func(result, secret, label, seed []byte) {
+ prf, _ := prfAndHashForVersion(version, suite)
+ return prf
+}
+
+// masterFromPreMasterSecret generates the master secret from the pre-master
+// secret. See RFC 5246, Section 8.1.
+func masterFromPreMasterSecret(version uint16, suite *cipherSuite, preMasterSecret, clientRandom, serverRandom []byte) []byte {
+ seed := make([]byte, 0, len(clientRandom)+len(serverRandom))
+ seed = append(seed, clientRandom...)
+ seed = append(seed, serverRandom...)
+
+ masterSecret := make([]byte, masterSecretLength)
+ prfForVersion(version, suite)(masterSecret, preMasterSecret, masterSecretLabel, seed)
+ return masterSecret
+}
+
+// keysFromMasterSecret generates the connection keys from the master
+// secret, given the lengths of the MAC key, cipher key and IV, as defined in
+// RFC 2246, Section 6.3.
+func keysFromMasterSecret(version uint16, suite *cipherSuite, masterSecret, clientRandom, serverRandom []byte, macLen, keyLen, ivLen int) (clientMAC, serverMAC, clientKey, serverKey, clientIV, serverIV []byte) {
+ seed := make([]byte, 0, len(serverRandom)+len(clientRandom))
+ seed = append(seed, serverRandom...)
+ seed = append(seed, clientRandom...)
+
+ n := 2*macLen + 2*keyLen + 2*ivLen
+ keyMaterial := make([]byte, n)
+ prfForVersion(version, suite)(keyMaterial, masterSecret, keyExpansionLabel, seed)
+ clientMAC = keyMaterial[:macLen]
+ keyMaterial = keyMaterial[macLen:]
+ serverMAC = keyMaterial[:macLen]
+ keyMaterial = keyMaterial[macLen:]
+ clientKey = keyMaterial[:keyLen]
+ keyMaterial = keyMaterial[keyLen:]
+ serverKey = keyMaterial[:keyLen]
+ keyMaterial = keyMaterial[keyLen:]
+ clientIV = keyMaterial[:ivLen]
+ keyMaterial = keyMaterial[ivLen:]
+ serverIV = keyMaterial[:ivLen]
+ return
+}
+
+func newFinishedHash(version uint16, cipherSuite *cipherSuite) finishedHash {
+ var buffer []byte
+ if version >= VersionTLS12 {
+ buffer = []byte{}
+ }
+
+ prf, hash := prfAndHashForVersion(version, cipherSuite)
+ if hash != 0 {
+ return finishedHash{hash.New(), hash.New(), nil, nil, buffer, version, prf}
+ }
+
+ return finishedHash{sha1.New(), sha1.New(), md5.New(), md5.New(), buffer, version, prf}
+}
+
+// A finishedHash calculates the hash of a set of handshake messages suitable
+// for including in a Finished message.
+type finishedHash struct {
+ client hash.Hash
+ server hash.Hash
+
+ // Prior to TLS 1.2, an additional MD5 hash is required.
+ clientMD5 hash.Hash
+ serverMD5 hash.Hash
+
+ // In TLS 1.2, a full buffer is sadly required.
+ buffer []byte
+
+ version uint16
+ prf func(result, secret, label, seed []byte)
+}
+
+func (h *finishedHash) Write(msg []byte) (n int, err error) {
+ h.client.Write(msg)
+ h.server.Write(msg)
+
+ if h.version < VersionTLS12 {
+ h.clientMD5.Write(msg)
+ h.serverMD5.Write(msg)
+ }
+
+ if h.buffer != nil {
+ h.buffer = append(h.buffer, msg...)
+ }
+
+ return len(msg), nil
+}
+
+func (h finishedHash) Sum() []byte {
+ if h.version >= VersionTLS12 {
+ return h.client.Sum(nil)
+ }
+
+ out := make([]byte, 0, md5.Size+sha1.Size)
+ out = h.clientMD5.Sum(out)
+ return h.client.Sum(out)
+}
+
+// clientSum returns the contents of the verify_data member of a client's
+// Finished message.
+func (h finishedHash) clientSum(masterSecret []byte) []byte {
+ out := make([]byte, finishedVerifyLength)
+ h.prf(out, masterSecret, clientFinishedLabel, h.Sum())
+ return out
+}
+
+// serverSum returns the contents of the verify_data member of a server's
+// Finished message.
+func (h finishedHash) serverSum(masterSecret []byte) []byte {
+ out := make([]byte, finishedVerifyLength)
+ h.prf(out, masterSecret, serverFinishedLabel, h.Sum())
+ return out
+}
+
+// hashForClientCertificate returns the handshake messages so far, pre-hashed if
+// necessary, suitable for signing by a TLS client certificate.
+func (h finishedHash) hashForClientCertificate(sigType uint8, hashAlg crypto.Hash) []byte {
+ if (h.version >= VersionTLS12 || sigType == signatureEd25519) && h.buffer == nil {
+ panic("tls: handshake hash for a client certificate requested after discarding the handshake buffer")
+ }
+
+ if sigType == signatureEd25519 {
+ return h.buffer
+ }
+
+ if h.version >= VersionTLS12 {
+ hash := hashAlg.New()
+ hash.Write(h.buffer)
+ return hash.Sum(nil)
+ }
+
+ if sigType == signatureECDSA {
+ return h.server.Sum(nil)
+ }
+
+ return h.Sum()
+}
+
+// discardHandshakeBuffer is called when there is no more need to
+// buffer the entirety of the handshake messages.
+func (h *finishedHash) discardHandshakeBuffer() {
+ h.buffer = nil
+}
+
+// noExportedKeyingMaterial is used as a value of
+// ConnectionState.ekm when renegotiation is enabled and thus
+// we wish to fail all key-material export requests.
+func noExportedKeyingMaterial(label string, context []byte, length int) ([]byte, error) {
+ return nil, errors.New("crypto/tls: ExportKeyingMaterial is unavailable when renegotiation is enabled")
+}
+
+// ekmFromMasterSecret generates exported keying material as defined in RFC 5705.
+func ekmFromMasterSecret(version uint16, suite *cipherSuite, masterSecret, clientRandom, serverRandom []byte) func(string, []byte, int) ([]byte, error) {
+ return func(label string, context []byte, length int) ([]byte, error) {
+ switch label {
+ case "client finished", "server finished", "master secret", "key expansion":
+ // These values are reserved and may not be used.
+ return nil, fmt.Errorf("crypto/tls: reserved ExportKeyingMaterial label: %s", label)
+ }
+
+ seedLen := len(serverRandom) + len(clientRandom)
+ if context != nil {
+ seedLen += 2 + len(context)
+ }
+ seed := make([]byte, 0, seedLen)
+
+ seed = append(seed, clientRandom...)
+ seed = append(seed, serverRandom...)
+
+ if context != nil {
+ if len(context) >= 1<<16 {
+ return nil, fmt.Errorf("crypto/tls: ExportKeyingMaterial context too long")
+ }
+ seed = append(seed, byte(len(context)>>8), byte(len(context)))
+ seed = append(seed, context...)
+ }
+
+ keyMaterial := make([]byte, length)
+ prfForVersion(version, suite)(keyMaterial, masterSecret, []byte(label), seed)
+ return keyMaterial, nil
+ }
+}
diff --git a/vendor/github.com/quic-go/qtls-go1-20/ticket.go b/vendor/github.com/quic-go/qtls-go1-20/ticket.go
new file mode 100644
index 0000000000..7eb555c459
--- /dev/null
+++ b/vendor/github.com/quic-go/qtls-go1-20/ticket.go
@@ -0,0 +1,274 @@
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package qtls
+
+import (
+ "bytes"
+ "crypto/aes"
+ "crypto/cipher"
+ "crypto/hmac"
+ "crypto/sha256"
+ "crypto/subtle"
+ "encoding/binary"
+ "errors"
+ "io"
+ "time"
+
+ "golang.org/x/crypto/cryptobyte"
+)
+
+// sessionState contains the information that is serialized into a session
+// ticket in order to later resume a connection.
+type sessionState struct {
+ vers uint16
+ cipherSuite uint16
+ createdAt uint64
+ masterSecret []byte // opaque master_secret<1..2^16-1>;
+ // struct { opaque certificate<1..2^24-1> } Certificate;
+ certificates [][]byte // Certificate certificate_list<0..2^24-1>;
+
+ // usedOldKey is true if the ticket from which this session came from
+ // was encrypted with an older key and thus should be refreshed.
+ usedOldKey bool
+}
+
+func (m *sessionState) marshal() []byte {
+ var b cryptobyte.Builder
+ b.AddUint16(m.vers)
+ b.AddUint16(m.cipherSuite)
+ addUint64(&b, m.createdAt)
+ b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
+ b.AddBytes(m.masterSecret)
+ })
+ b.AddUint24LengthPrefixed(func(b *cryptobyte.Builder) {
+ for _, cert := range m.certificates {
+ b.AddUint24LengthPrefixed(func(b *cryptobyte.Builder) {
+ b.AddBytes(cert)
+ })
+ }
+ })
+ return b.BytesOrPanic()
+}
+
+func (m *sessionState) unmarshal(data []byte) bool {
+ *m = sessionState{usedOldKey: m.usedOldKey}
+ s := cryptobyte.String(data)
+ if ok := s.ReadUint16(&m.vers) &&
+ s.ReadUint16(&m.cipherSuite) &&
+ readUint64(&s, &m.createdAt) &&
+ readUint16LengthPrefixed(&s, &m.masterSecret) &&
+ len(m.masterSecret) != 0; !ok {
+ return false
+ }
+ var certList cryptobyte.String
+ if !s.ReadUint24LengthPrefixed(&certList) {
+ return false
+ }
+ for !certList.Empty() {
+ var cert []byte
+ if !readUint24LengthPrefixed(&certList, &cert) {
+ return false
+ }
+ m.certificates = append(m.certificates, cert)
+ }
+ return s.Empty()
+}
+
+// sessionStateTLS13 is the content of a TLS 1.3 session ticket. Its first
+// version (revision = 0) doesn't carry any of the information needed for 0-RTT
+// validation and the nonce is always empty.
+// version (revision = 1) carries the max_early_data_size sent in the ticket.
+// version (revision = 2) carries the ALPN sent in the ticket.
+type sessionStateTLS13 struct {
+ // uint8 version = 0x0304;
+ // uint8 revision = 2;
+ cipherSuite uint16
+ createdAt uint64
+ resumptionSecret []byte // opaque resumption_master_secret<1..2^8-1>;
+ certificate Certificate // CertificateEntry certificate_list<0..2^24-1>;
+ maxEarlyData uint32
+ alpn string
+
+ appData []byte
+}
+
+func (m *sessionStateTLS13) marshal() []byte {
+ var b cryptobyte.Builder
+ b.AddUint16(VersionTLS13)
+ b.AddUint8(2) // revision
+ b.AddUint16(m.cipherSuite)
+ addUint64(&b, m.createdAt)
+ b.AddUint8LengthPrefixed(func(b *cryptobyte.Builder) {
+ b.AddBytes(m.resumptionSecret)
+ })
+ marshalCertificate(&b, m.certificate)
+ b.AddUint32(m.maxEarlyData)
+ b.AddUint8LengthPrefixed(func(b *cryptobyte.Builder) {
+ b.AddBytes([]byte(m.alpn))
+ })
+ b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
+ b.AddBytes(m.appData)
+ })
+ return b.BytesOrPanic()
+}
+
+func (m *sessionStateTLS13) unmarshal(data []byte) bool {
+ *m = sessionStateTLS13{}
+ s := cryptobyte.String(data)
+ var version uint16
+ var revision uint8
+ var alpn []byte
+ ret := s.ReadUint16(&version) &&
+ version == VersionTLS13 &&
+ s.ReadUint8(&revision) &&
+ revision == 2 &&
+ s.ReadUint16(&m.cipherSuite) &&
+ readUint64(&s, &m.createdAt) &&
+ readUint8LengthPrefixed(&s, &m.resumptionSecret) &&
+ len(m.resumptionSecret) != 0 &&
+ unmarshalCertificate(&s, &m.certificate) &&
+ s.ReadUint32(&m.maxEarlyData) &&
+ readUint8LengthPrefixed(&s, &alpn) &&
+ readUint16LengthPrefixed(&s, &m.appData) &&
+ s.Empty()
+ m.alpn = string(alpn)
+ return ret
+}
+
+func (c *Conn) encryptTicket(state []byte) ([]byte, error) {
+ if len(c.ticketKeys) == 0 {
+ return nil, errors.New("tls: internal error: session ticket keys unavailable")
+ }
+
+ encrypted := make([]byte, ticketKeyNameLen+aes.BlockSize+len(state)+sha256.Size)
+ keyName := encrypted[:ticketKeyNameLen]
+ iv := encrypted[ticketKeyNameLen : ticketKeyNameLen+aes.BlockSize]
+ macBytes := encrypted[len(encrypted)-sha256.Size:]
+
+ if _, err := io.ReadFull(c.config.rand(), iv); err != nil {
+ return nil, err
+ }
+ key := c.ticketKeys[0]
+ copy(keyName, key.keyName[:])
+ block, err := aes.NewCipher(key.aesKey[:])
+ if err != nil {
+ return nil, errors.New("tls: failed to create cipher while encrypting ticket: " + err.Error())
+ }
+ cipher.NewCTR(block, iv).XORKeyStream(encrypted[ticketKeyNameLen+aes.BlockSize:], state)
+
+ mac := hmac.New(sha256.New, key.hmacKey[:])
+ mac.Write(encrypted[:len(encrypted)-sha256.Size])
+ mac.Sum(macBytes[:0])
+
+ return encrypted, nil
+}
+
+func (c *Conn) decryptTicket(encrypted []byte) (plaintext []byte, usedOldKey bool) {
+ if len(encrypted) < ticketKeyNameLen+aes.BlockSize+sha256.Size {
+ return nil, false
+ }
+
+ keyName := encrypted[:ticketKeyNameLen]
+ iv := encrypted[ticketKeyNameLen : ticketKeyNameLen+aes.BlockSize]
+ macBytes := encrypted[len(encrypted)-sha256.Size:]
+ ciphertext := encrypted[ticketKeyNameLen+aes.BlockSize : len(encrypted)-sha256.Size]
+
+ keyIndex := -1
+ for i, candidateKey := range c.ticketKeys {
+ if bytes.Equal(keyName, candidateKey.keyName[:]) {
+ keyIndex = i
+ break
+ }
+ }
+ if keyIndex == -1 {
+ return nil, false
+ }
+ key := &c.ticketKeys[keyIndex]
+
+ mac := hmac.New(sha256.New, key.hmacKey[:])
+ mac.Write(encrypted[:len(encrypted)-sha256.Size])
+ expected := mac.Sum(nil)
+
+ if subtle.ConstantTimeCompare(macBytes, expected) != 1 {
+ return nil, false
+ }
+
+ block, err := aes.NewCipher(key.aesKey[:])
+ if err != nil {
+ return nil, false
+ }
+ plaintext = make([]byte, len(ciphertext))
+ cipher.NewCTR(block, iv).XORKeyStream(plaintext, ciphertext)
+
+ return plaintext, keyIndex > 0
+}
+
+func (c *Conn) getSessionTicketMsg(appData []byte) (*newSessionTicketMsgTLS13, error) {
+ m := new(newSessionTicketMsgTLS13)
+
+ var certsFromClient [][]byte
+ for _, cert := range c.peerCertificates {
+ certsFromClient = append(certsFromClient, cert.Raw)
+ }
+ state := sessionStateTLS13{
+ cipherSuite: c.cipherSuite,
+ createdAt: uint64(c.config.time().Unix()),
+ resumptionSecret: c.resumptionSecret,
+ certificate: Certificate{
+ Certificate: certsFromClient,
+ OCSPStaple: c.ocspResponse,
+ SignedCertificateTimestamps: c.scts,
+ },
+ appData: appData,
+ alpn: c.clientProtocol,
+ }
+ if c.extraConfig != nil {
+ state.maxEarlyData = c.extraConfig.MaxEarlyData
+ }
+ var err error
+ m.label, err = c.encryptTicket(state.marshal())
+ if err != nil {
+ return nil, err
+ }
+ m.lifetime = uint32(maxSessionTicketLifetime / time.Second)
+
+ // ticket_age_add is a random 32-bit value. See RFC 8446, section 4.6.1
+ // The value is not stored anywhere; we never need to check the ticket age
+ // because 0-RTT is not supported.
+ ageAdd := make([]byte, 4)
+ _, err = c.config.rand().Read(ageAdd)
+ if err != nil {
+ return nil, err
+ }
+ m.ageAdd = binary.LittleEndian.Uint32(ageAdd)
+
+ // ticket_nonce, which must be unique per connection, is always left at
+ // zero because we only ever send one ticket per connection.
+
+ if c.extraConfig != nil {
+ m.maxEarlyData = c.extraConfig.MaxEarlyData
+ }
+ return m, nil
+}
+
+// GetSessionTicket generates a new session ticket.
+// It should only be called after the handshake completes.
+// It can only be used for servers, and only if the alternative record layer is set.
+// The ticket may be nil if config.SessionTicketsDisabled is set,
+// or if the client isn't able to receive session tickets.
+func (c *Conn) GetSessionTicket(appData []byte) ([]byte, error) {
+ if c.isClient || !c.isHandshakeComplete.Load() || c.extraConfig == nil || c.extraConfig.AlternativeRecordLayer == nil {
+ return nil, errors.New("GetSessionTicket is only valid for servers after completion of the handshake, and if an alternative record layer is set.")
+ }
+ if c.config.SessionTicketsDisabled {
+ return nil, nil
+ }
+
+ m, err := c.getSessionTicketMsg(appData)
+ if err != nil {
+ return nil, err
+ }
+ return m.marshal(), nil
+}
diff --git a/vendor/github.com/quic-go/qtls-go1-20/tls.go b/vendor/github.com/quic-go/qtls-go1-20/tls.go
new file mode 100644
index 0000000000..42207c235f
--- /dev/null
+++ b/vendor/github.com/quic-go/qtls-go1-20/tls.go
@@ -0,0 +1,362 @@
+// Copyright 2009 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// package qtls partially implements TLS 1.2, as specified in RFC 5246,
+// and TLS 1.3, as specified in RFC 8446.
+package qtls
+
+// BUG(agl): The crypto/tls package only implements some countermeasures
+// against Lucky13 attacks on CBC-mode encryption, and only on SHA1
+// variants. See http://www.isg.rhul.ac.uk/tls/TLStiming.pdf and
+// https://www.imperialviolet.org/2013/02/04/luckythirteen.html.
+
+import (
+ "bytes"
+ "context"
+ "crypto"
+ "crypto/ecdsa"
+ "crypto/ed25519"
+ "crypto/rsa"
+ "crypto/x509"
+ "encoding/pem"
+ "errors"
+ "fmt"
+ "net"
+ "os"
+ "strings"
+)
+
+// Server returns a new TLS server side connection
+// using conn as the underlying transport.
+// The configuration config must be non-nil and must include
+// at least one certificate or else set GetCertificate.
+func Server(conn net.Conn, config *Config, extraConfig *ExtraConfig) *Conn {
+ c := &Conn{
+ conn: conn,
+ config: fromConfig(config),
+ extraConfig: extraConfig,
+ }
+ c.handshakeFn = c.serverHandshake
+ return c
+}
+
+// Client returns a new TLS client side connection
+// using conn as the underlying transport.
+// The config cannot be nil: users must set either ServerName or
+// InsecureSkipVerify in the config.
+func Client(conn net.Conn, config *Config, extraConfig *ExtraConfig) *Conn {
+ c := &Conn{
+ conn: conn,
+ config: fromConfig(config),
+ extraConfig: extraConfig,
+ isClient: true,
+ }
+ c.handshakeFn = c.clientHandshake
+ return c
+}
+
+// A listener implements a network listener (net.Listener) for TLS connections.
+type listener struct {
+ net.Listener
+ config *Config
+ extraConfig *ExtraConfig
+}
+
+// Accept waits for and returns the next incoming TLS connection.
+// The returned connection is of type *Conn.
+func (l *listener) Accept() (net.Conn, error) {
+ c, err := l.Listener.Accept()
+ if err != nil {
+ return nil, err
+ }
+ return Server(c, l.config, l.extraConfig), nil
+}
+
+// NewListener creates a Listener which accepts connections from an inner
+// Listener and wraps each connection with Server.
+// The configuration config must be non-nil and must include
+// at least one certificate or else set GetCertificate.
+func NewListener(inner net.Listener, config *Config, extraConfig *ExtraConfig) net.Listener {
+ l := new(listener)
+ l.Listener = inner
+ l.config = config
+ l.extraConfig = extraConfig
+ return l
+}
+
+// Listen creates a TLS listener accepting connections on the
+// given network address using net.Listen.
+// The configuration config must be non-nil and must include
+// at least one certificate or else set GetCertificate.
+func Listen(network, laddr string, config *Config, extraConfig *ExtraConfig) (net.Listener, error) {
+ if config == nil || len(config.Certificates) == 0 &&
+ config.GetCertificate == nil && config.GetConfigForClient == nil {
+ return nil, errors.New("tls: neither Certificates, GetCertificate, nor GetConfigForClient set in Config")
+ }
+ l, err := net.Listen(network, laddr)
+ if err != nil {
+ return nil, err
+ }
+ return NewListener(l, config, extraConfig), nil
+}
+
+type timeoutError struct{}
+
+func (timeoutError) Error() string { return "tls: DialWithDialer timed out" }
+func (timeoutError) Timeout() bool { return true }
+func (timeoutError) Temporary() bool { return true }
+
+// DialWithDialer connects to the given network address using dialer.Dial and
+// then initiates a TLS handshake, returning the resulting TLS connection. Any
+// timeout or deadline given in the dialer apply to connection and TLS
+// handshake as a whole.
+//
+// DialWithDialer interprets a nil configuration as equivalent to the zero
+// configuration; see the documentation of Config for the defaults.
+//
+// DialWithDialer uses context.Background internally; to specify the context,
+// use Dialer.DialContext with NetDialer set to the desired dialer.
+func DialWithDialer(dialer *net.Dialer, network, addr string, config *Config, extraConfig *ExtraConfig) (*Conn, error) {
+ return dial(context.Background(), dialer, network, addr, config, extraConfig)
+}
+
+func dial(ctx context.Context, netDialer *net.Dialer, network, addr string, config *Config, extraConfig *ExtraConfig) (*Conn, error) {
+ if netDialer.Timeout != 0 {
+ var cancel context.CancelFunc
+ ctx, cancel = context.WithTimeout(ctx, netDialer.Timeout)
+ defer cancel()
+ }
+
+ if !netDialer.Deadline.IsZero() {
+ var cancel context.CancelFunc
+ ctx, cancel = context.WithDeadline(ctx, netDialer.Deadline)
+ defer cancel()
+ }
+
+ rawConn, err := netDialer.DialContext(ctx, network, addr)
+ if err != nil {
+ return nil, err
+ }
+
+ colonPos := strings.LastIndex(addr, ":")
+ if colonPos == -1 {
+ colonPos = len(addr)
+ }
+ hostname := addr[:colonPos]
+
+ if config == nil {
+ config = defaultConfig()
+ }
+ // If no ServerName is set, infer the ServerName
+ // from the hostname we're connecting to.
+ if config.ServerName == "" {
+ // Make a copy to avoid polluting argument or default.
+ c := config.Clone()
+ c.ServerName = hostname
+ config = c
+ }
+
+ conn := Client(rawConn, config, extraConfig)
+ if err := conn.HandshakeContext(ctx); err != nil {
+ rawConn.Close()
+ return nil, err
+ }
+ return conn, nil
+}
+
+// Dial connects to the given network address using net.Dial
+// and then initiates a TLS handshake, returning the resulting
+// TLS connection.
+// Dial interprets a nil configuration as equivalent to
+// the zero configuration; see the documentation of Config
+// for the defaults.
+func Dial(network, addr string, config *Config, extraConfig *ExtraConfig) (*Conn, error) {
+ return DialWithDialer(new(net.Dialer), network, addr, config, extraConfig)
+}
+
+// Dialer dials TLS connections given a configuration and a Dialer for the
+// underlying connection.
+type Dialer struct {
+ // NetDialer is the optional dialer to use for the TLS connections'
+ // underlying TCP connections.
+ // A nil NetDialer is equivalent to the net.Dialer zero value.
+ NetDialer *net.Dialer
+
+ // Config is the TLS configuration to use for new connections.
+ // A nil configuration is equivalent to the zero
+ // configuration; see the documentation of Config for the
+ // defaults.
+ Config *Config
+
+ ExtraConfig *ExtraConfig
+}
+
+// Dial connects to the given network address and initiates a TLS
+// handshake, returning the resulting TLS connection.
+//
+// The returned Conn, if any, will always be of type *Conn.
+//
+// Dial uses context.Background internally; to specify the context,
+// use DialContext.
+func (d *Dialer) Dial(network, addr string) (net.Conn, error) {
+ return d.DialContext(context.Background(), network, addr)
+}
+
+func (d *Dialer) netDialer() *net.Dialer {
+ if d.NetDialer != nil {
+ return d.NetDialer
+ }
+ return new(net.Dialer)
+}
+
+// DialContext connects to the given network address and initiates a TLS
+// handshake, returning the resulting TLS connection.
+//
+// The provided Context must be non-nil. If the context expires before
+// the connection is complete, an error is returned. Once successfully
+// connected, any expiration of the context will not affect the
+// connection.
+//
+// The returned Conn, if any, will always be of type *Conn.
+func (d *Dialer) DialContext(ctx context.Context, network, addr string) (net.Conn, error) {
+ c, err := dial(ctx, d.netDialer(), network, addr, d.Config, d.ExtraConfig)
+ if err != nil {
+ // Don't return c (a typed nil) in an interface.
+ return nil, err
+ }
+ return c, nil
+}
+
+// LoadX509KeyPair reads and parses a public/private key pair from a pair
+// of files. The files must contain PEM encoded data. The certificate file
+// may contain intermediate certificates following the leaf certificate to
+// form a certificate chain. On successful return, Certificate.Leaf will
+// be nil because the parsed form of the certificate is not retained.
+func LoadX509KeyPair(certFile, keyFile string) (Certificate, error) {
+ certPEMBlock, err := os.ReadFile(certFile)
+ if err != nil {
+ return Certificate{}, err
+ }
+ keyPEMBlock, err := os.ReadFile(keyFile)
+ if err != nil {
+ return Certificate{}, err
+ }
+ return X509KeyPair(certPEMBlock, keyPEMBlock)
+}
+
+// X509KeyPair parses a public/private key pair from a pair of
+// PEM encoded data. On successful return, Certificate.Leaf will be nil because
+// the parsed form of the certificate is not retained.
+func X509KeyPair(certPEMBlock, keyPEMBlock []byte) (Certificate, error) {
+ fail := func(err error) (Certificate, error) { return Certificate{}, err }
+
+ var cert Certificate
+ var skippedBlockTypes []string
+ for {
+ var certDERBlock *pem.Block
+ certDERBlock, certPEMBlock = pem.Decode(certPEMBlock)
+ if certDERBlock == nil {
+ break
+ }
+ if certDERBlock.Type == "CERTIFICATE" {
+ cert.Certificate = append(cert.Certificate, certDERBlock.Bytes)
+ } else {
+ skippedBlockTypes = append(skippedBlockTypes, certDERBlock.Type)
+ }
+ }
+
+ if len(cert.Certificate) == 0 {
+ if len(skippedBlockTypes) == 0 {
+ return fail(errors.New("tls: failed to find any PEM data in certificate input"))
+ }
+ if len(skippedBlockTypes) == 1 && strings.HasSuffix(skippedBlockTypes[0], "PRIVATE KEY") {
+ return fail(errors.New("tls: failed to find certificate PEM data in certificate input, but did find a private key; PEM inputs may have been switched"))
+ }
+ return fail(fmt.Errorf("tls: failed to find \"CERTIFICATE\" PEM block in certificate input after skipping PEM blocks of the following types: %v", skippedBlockTypes))
+ }
+
+ skippedBlockTypes = skippedBlockTypes[:0]
+ var keyDERBlock *pem.Block
+ for {
+ keyDERBlock, keyPEMBlock = pem.Decode(keyPEMBlock)
+ if keyDERBlock == nil {
+ if len(skippedBlockTypes) == 0 {
+ return fail(errors.New("tls: failed to find any PEM data in key input"))
+ }
+ if len(skippedBlockTypes) == 1 && skippedBlockTypes[0] == "CERTIFICATE" {
+ return fail(errors.New("tls: found a certificate rather than a key in the PEM for the private key"))
+ }
+ return fail(fmt.Errorf("tls: failed to find PEM block with type ending in \"PRIVATE KEY\" in key input after skipping PEM blocks of the following types: %v", skippedBlockTypes))
+ }
+ if keyDERBlock.Type == "PRIVATE KEY" || strings.HasSuffix(keyDERBlock.Type, " PRIVATE KEY") {
+ break
+ }
+ skippedBlockTypes = append(skippedBlockTypes, keyDERBlock.Type)
+ }
+
+ // We don't need to parse the public key for TLS, but we so do anyway
+ // to check that it looks sane and matches the private key.
+ x509Cert, err := x509.ParseCertificate(cert.Certificate[0])
+ if err != nil {
+ return fail(err)
+ }
+
+ cert.PrivateKey, err = parsePrivateKey(keyDERBlock.Bytes)
+ if err != nil {
+ return fail(err)
+ }
+
+ switch pub := x509Cert.PublicKey.(type) {
+ case *rsa.PublicKey:
+ priv, ok := cert.PrivateKey.(*rsa.PrivateKey)
+ if !ok {
+ return fail(errors.New("tls: private key type does not match public key type"))
+ }
+ if pub.N.Cmp(priv.N) != 0 {
+ return fail(errors.New("tls: private key does not match public key"))
+ }
+ case *ecdsa.PublicKey:
+ priv, ok := cert.PrivateKey.(*ecdsa.PrivateKey)
+ if !ok {
+ return fail(errors.New("tls: private key type does not match public key type"))
+ }
+ if pub.X.Cmp(priv.X) != 0 || pub.Y.Cmp(priv.Y) != 0 {
+ return fail(errors.New("tls: private key does not match public key"))
+ }
+ case ed25519.PublicKey:
+ priv, ok := cert.PrivateKey.(ed25519.PrivateKey)
+ if !ok {
+ return fail(errors.New("tls: private key type does not match public key type"))
+ }
+ if !bytes.Equal(priv.Public().(ed25519.PublicKey), pub) {
+ return fail(errors.New("tls: private key does not match public key"))
+ }
+ default:
+ return fail(errors.New("tls: unknown public key algorithm"))
+ }
+
+ return cert, nil
+}
+
+// Attempt to parse the given private key DER block. OpenSSL 0.9.8 generates
+// PKCS #1 private keys by default, while OpenSSL 1.0.0 generates PKCS #8 keys.
+// OpenSSL ecparam generates SEC1 EC private keys for ECDSA. We try all three.
+func parsePrivateKey(der []byte) (crypto.PrivateKey, error) {
+ if key, err := x509.ParsePKCS1PrivateKey(der); err == nil {
+ return key, nil
+ }
+ if key, err := x509.ParsePKCS8PrivateKey(der); err == nil {
+ switch key := key.(type) {
+ case *rsa.PrivateKey, *ecdsa.PrivateKey, ed25519.PrivateKey:
+ return key, nil
+ default:
+ return nil, errors.New("tls: found unknown private key type in PKCS#8 wrapping")
+ }
+ }
+ if key, err := x509.ParseECPrivateKey(der); err == nil {
+ return key, nil
+ }
+
+ return nil, errors.New("tls: failed to parse private key")
+}
diff --git a/vendor/github.com/quic-go/qtls-go1-20/unsafe.go b/vendor/github.com/quic-go/qtls-go1-20/unsafe.go
new file mode 100644
index 0000000000..55fa01b3d6
--- /dev/null
+++ b/vendor/github.com/quic-go/qtls-go1-20/unsafe.go
@@ -0,0 +1,96 @@
+package qtls
+
+import (
+ "crypto/tls"
+ "reflect"
+ "unsafe"
+)
+
+func init() {
+ if !structsEqual(&tls.ConnectionState{}, &connectionState{}) {
+ panic("qtls.ConnectionState doesn't match")
+ }
+ if !structsEqual(&tls.ClientSessionState{}, &clientSessionState{}) {
+ panic("qtls.ClientSessionState doesn't match")
+ }
+ if !structsEqual(&tls.CertificateRequestInfo{}, &certificateRequestInfo{}) {
+ panic("qtls.CertificateRequestInfo doesn't match")
+ }
+ if !structsEqual(&tls.Config{}, &config{}) {
+ panic("qtls.Config doesn't match")
+ }
+ if !structsEqual(&tls.ClientHelloInfo{}, &clientHelloInfo{}) {
+ panic("qtls.ClientHelloInfo doesn't match")
+ }
+}
+
+func toConnectionState(c connectionState) ConnectionState {
+ return *(*ConnectionState)(unsafe.Pointer(&c))
+}
+
+func toClientSessionState(s *clientSessionState) *ClientSessionState {
+ return (*ClientSessionState)(unsafe.Pointer(s))
+}
+
+func fromClientSessionState(s *ClientSessionState) *clientSessionState {
+ return (*clientSessionState)(unsafe.Pointer(s))
+}
+
+func toCertificateRequestInfo(i *certificateRequestInfo) *CertificateRequestInfo {
+ return (*CertificateRequestInfo)(unsafe.Pointer(i))
+}
+
+func toConfig(c *config) *Config {
+ return (*Config)(unsafe.Pointer(c))
+}
+
+func fromConfig(c *Config) *config {
+ return (*config)(unsafe.Pointer(c))
+}
+
+func toClientHelloInfo(chi *clientHelloInfo) *ClientHelloInfo {
+ return (*ClientHelloInfo)(unsafe.Pointer(chi))
+}
+
+func structsEqual(a, b interface{}) bool {
+ return compare(reflect.ValueOf(a), reflect.ValueOf(b))
+}
+
+func compare(a, b reflect.Value) bool {
+ sa := a.Elem()
+ sb := b.Elem()
+ if sa.NumField() != sb.NumField() {
+ return false
+ }
+ for i := 0; i < sa.NumField(); i++ {
+ fa := sa.Type().Field(i)
+ fb := sb.Type().Field(i)
+ if !reflect.DeepEqual(fa.Index, fb.Index) || fa.Name != fb.Name || fa.Anonymous != fb.Anonymous || fa.Offset != fb.Offset || !reflect.DeepEqual(fa.Type, fb.Type) {
+ if fa.Type.Kind() != fb.Type.Kind() {
+ return false
+ }
+ if fa.Type.Kind() == reflect.Slice {
+ if !compareStruct(fa.Type.Elem(), fb.Type.Elem()) {
+ return false
+ }
+ continue
+ }
+ return false
+ }
+ }
+ return true
+}
+
+func compareStruct(a, b reflect.Type) bool {
+ if a.NumField() != b.NumField() {
+ return false
+ }
+ for i := 0; i < a.NumField(); i++ {
+ fa := a.Field(i)
+ fb := b.Field(i)
+ if !reflect.DeepEqual(fa.Index, fb.Index) || fa.Name != fb.Name || fa.Anonymous != fb.Anonymous || fa.Offset != fb.Offset || !reflect.DeepEqual(fa.Type, fb.Type) {
+ return false
+ }
+ }
+ return true
+}
diff --git a/vendor/github.com/quic-go/quic-go/.gitignore b/vendor/github.com/quic-go/quic-go/.gitignore
new file mode 100644
index 0000000000..3cc06f240f
--- /dev/null
+++ b/vendor/github.com/quic-go/quic-go/.gitignore
@@ -0,0 +1,17 @@
+debug
+debug.test
+main
+mockgen_tmp.go
+*.qtr
+*.qlog
+*.txt
+race.[0-9]*
+
+fuzzing/*/*.zip
+fuzzing/*/coverprofile
+fuzzing/*/crashers
+fuzzing/*/sonarprofile
+fuzzing/*/suppressions
+fuzzing/*/corpus/
+
+gomock_reflect_*/
diff --git a/vendor/github.com/quic-go/quic-go/.golangci.yml b/vendor/github.com/quic-go/quic-go/.golangci.yml
new file mode 100644
index 0000000000..2589c05389
--- /dev/null
+++ b/vendor/github.com/quic-go/quic-go/.golangci.yml
@@ -0,0 +1,44 @@
+run:
+ skip-files:
+ - internal/qtls/structs_equal_test.go
+
+linters-settings:
+ depguard:
+ type: blacklist
+ packages:
+ - github.com/marten-seemann/qtls
+ packages-with-error-message:
+ - github.com/marten-seemann/qtls: "importing qtls only allowed in internal/qtls"
+ misspell:
+ ignore-words:
+ - ect
+
+linters:
+ disable-all: true
+ enable:
+ - asciicheck
+ - deadcode
+ - depguard
+ - exhaustive
+ - exportloopref
+ - goimports
+ - gofmt # redundant, since gofmt *should* be a no-op after gofumpt
+ - gofumpt
+ - gosimple
+ - ineffassign
+ - misspell
+ - prealloc
+ - staticcheck
+ - stylecheck
+ - structcheck
+ - unconvert
+ - unparam
+ - unused
+ - varcheck
+ - vet
+
+issues:
+ exclude-rules:
+ - path: internal/qtls
+ linters:
+ - depguard
diff --git a/vendor/github.com/quic-go/quic-go/Changelog.md b/vendor/github.com/quic-go/quic-go/Changelog.md
new file mode 100644
index 0000000000..82df5fb245
--- /dev/null
+++ b/vendor/github.com/quic-go/quic-go/Changelog.md
@@ -0,0 +1,109 @@
+# Changelog
+
+## v0.22.0 (2021-07-25)
+
+- Use `ReadBatch` to read multiple UDP packets from the socket with a single syscall
+- Add a config option (`Config.DisableVersionNegotiationPackets`) to disable sending of Version Negotiation packets
+- Drop support for QUIC draft versions 32 and 34
+- Remove the `RetireBugBackwardsCompatibilityMode`, which was intended to mitigate a bug when retiring connection IDs in quic-go in v0.17.2 and ealier
+
+## v0.21.2 (2021-07-15)
+
+- Update qtls (for Go 1.15, 1.16 and 1.17rc1) to include the fix for the crypto/tls panic (see https://groups.google.com/g/golang-dev/c/5LJ2V7rd-Ag/m/YGLHVBZ6AAAJ for details)
+
+## v0.21.0 (2021-06-01)
+
+- quic-go now supports RFC 9000!
+
+## v0.20.0 (2021-03-19)
+
+- Remove the `quic.Config.HandshakeTimeout`. Introduce a `quic.Config.HandshakeIdleTimeout`.
+
+## v0.17.1 (2020-06-20)
+
+- Supports QUIC WG draft-29.
+- Improve bundling of ACK frames (#2543).
+
+## v0.16.0 (2020-05-31)
+
+- Supports QUIC WG draft-28.
+
+## v0.15.0 (2020-03-01)
+
+- Supports QUIC WG draft-27.
+- Add support for 0-RTT.
+- Remove `Session.Close()`. Applications need to pass an application error code to the transport using `Session.CloseWithError()`.
+- Make the TLS Cipher Suites configurable (via `tls.Config.CipherSuites`).
+
+## v0.14.0 (2019-12-04)
+
+- Supports QUIC WG draft-24.
+
+## v0.13.0 (2019-11-05)
+
+- Supports QUIC WG draft-23.
+- Add an `EarlyListener` that allows sending of 0.5-RTT data.
+- Add a `TokenStore` to store address validation tokens.
+- Issue and use new connection IDs during a connection.
+
+## v0.12.0 (2019-08-05)
+
+- Implement HTTP/3.
+- Rename `quic.Cookie` to `quic.Token` and `quic.Config.AcceptCookie` to `quic.Config.AcceptToken`.
+- Distinguish between Retry tokens and tokens sent in NEW_TOKEN frames.
+- Enforce application protocol negotiation (via `tls.Config.NextProtos`).
+- Use a varint for error codes.
+- Add support for [quic-trace](https://github.com/google/quic-trace).
+- Add a context to `Listener.Accept`, `Session.Accept{Uni}Stream` and `Session.Open{Uni}StreamSync`.
+- Implement TLS key updates.
+
+## v0.11.0 (2019-04-05)
+
+- Drop support for gQUIC. For qQUIC support, please switch to the *gquic* branch.
+- Implement QUIC WG draft-19.
+- Use [qtls](https://github.com/marten-seemann/qtls) for TLS 1.3.
+- Return a `tls.ConnectionState` from `quic.Session.ConnectionState()`.
+- Remove the error return values from `quic.Stream.CancelRead()` and `quic.Stream.CancelWrite()`
+
+## v0.10.0 (2018-08-28)
+
+- Add support for QUIC 44, drop support for QUIC 42.
+
+## v0.9.0 (2018-08-15)
+
+- Add a `quic.Config` option for the length of the connection ID (for IETF QUIC).
+- Split Session.Close into one method for regular closing and one for closing with an error.
+
+## v0.8.0 (2018-06-26)
+
+- Add support for unidirectional streams (for IETF QUIC).
+- Add a `quic.Config` option for the maximum number of incoming streams.
+- Add support for QUIC 42 and 43.
+- Add dial functions that use a context.
+- Multiplex clients on a net.PacketConn, when using Dial(conn).
+
+## v0.7.0 (2018-02-03)
+
+- The lower boundary for packets included in ACKs is now derived, and the value sent in STOP_WAITING frames is ignored.
+- Remove `DialNonFWSecure` and `DialAddrNonFWSecure`.
+- Expose the `ConnectionState` in the `Session` (experimental API).
+- Implement packet pacing.
+
+## v0.6.0 (2017-12-12)
+
+- Add support for QUIC 39, drop support for QUIC 35 - 37
+- Added `quic.Config` options for maximal flow control windows
+- Add a `quic.Config` option for QUIC versions
+- Add a `quic.Config` option to request omission of the connection ID from a server
+- Add a `quic.Config` option to configure the source address validation
+- Add a `quic.Config` option to configure the handshake timeout
+- Add a `quic.Config` option to configure the idle timeout
+- Add a `quic.Config` option to configure keep-alive
+- Rename the STK to Cookie
+- Implement `net.Conn`-style deadlines for streams
+- Remove the `tls.Config` from the `quic.Config`. The `tls.Config` must now be passed to the `Dial` and `Listen` functions as a separate parameter. See the [Godoc](https://godoc.org/github.com/quic-go/quic-go) for details.
+- Changed the log level environment variable to only accept strings ("DEBUG", "INFO", "ERROR"), see [the wiki](https://github.com/quic-go/quic-go/wiki/Logging) for more details.
+- Rename the `h2quic.QuicRoundTripper` to `h2quic.RoundTripper`
+- Changed `h2quic.Server.Serve()` to accept a `net.PacketConn`
+- Drop support for Go 1.7 and 1.8.
+- Various bugfixes
diff --git a/vendor/github.com/quic-go/quic-go/LICENSE b/vendor/github.com/quic-go/quic-go/LICENSE
new file mode 100644
index 0000000000..51378befb8
--- /dev/null
+++ b/vendor/github.com/quic-go/quic-go/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2016 the quic-go authors & Google, Inc.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/vendor/github.com/quic-go/quic-go/README.md b/vendor/github.com/quic-go/quic-go/README.md
new file mode 100644
index 0000000000..b41a2de4cc
--- /dev/null
+++ b/vendor/github.com/quic-go/quic-go/README.md
@@ -0,0 +1,63 @@
+# A QUIC implementation in pure Go
+
+
+
+[![PkgGoDev](https://pkg.go.dev/badge/github.com/quic-go/quic-go)](https://pkg.go.dev/github.com/quic-go/quic-go)
+[![Code Coverage](https://img.shields.io/codecov/c/github/quic-go/quic-go/master.svg?style=flat-square)](https://codecov.io/gh/quic-go/quic-go/)
+
+quic-go is an implementation of the QUIC protocol ([RFC 9000](https://datatracker.ietf.org/doc/html/rfc9000), [RFC 9001](https://datatracker.ietf.org/doc/html/rfc9001), [RFC 9002](https://datatracker.ietf.org/doc/html/rfc9002)) in Go, including the Unreliable Datagram Extension ([RFC 9221](https://datatracker.ietf.org/doc/html/rfc9221)) and Datagram Packetization Layer Path MTU
+ Discovery (DPLPMTUD, [RFC 8899](https://datatracker.ietf.org/doc/html/rfc8899)). It has support for HTTP/3 ([RFC 9114](https://datatracker.ietf.org/doc/html/rfc9114)), including QPACK ([RFC 9204](https://datatracker.ietf.org/doc/html/rfc9204)).
+
+In addition to the RFCs listed above, it currently implements the [IETF QUIC draft-29](https://tools.ietf.org/html/draft-ietf-quic-transport-29). Support for draft-29 will eventually be dropped, as it is phased out of the ecosystem.
+
+## Guides
+
+*We currently support Go 1.18.x and Go 1.19.x.*
+
+Running tests:
+
+ go test ./...
+
+### QUIC without HTTP/3
+
+Take a look at [this echo example](example/echo/echo.go).
+
+## Usage
+
+### As a server
+
+See the [example server](example/main.go). Starting a QUIC server is very similar to the standard lib http in go:
+
+```go
+http.Handle("/", http.FileServer(http.Dir(wwwDir)))
+http3.ListenAndServeQUIC("localhost:4242", "/path/to/cert/chain.pem", "/path/to/privkey.pem", nil)
+```
+
+### As a client
+
+See the [example client](example/client/main.go). Use a `http3.RoundTripper` as a `Transport` in a `http.Client`.
+
+```go
+http.Client{
+ Transport: &http3.RoundTripper{},
+}
+```
+
+## Projects using quic-go
+
+| Project | Description | Stars |
+|-----------------------------------------------------------|---------------------------------------------------------------------------------------------------------|-------|
+| [AdGuardHome](https://github.com/AdguardTeam/AdGuardHome) | Free and open source, powerful network-wide ads & trackers blocking DNS server. | ![GitHub Repo stars](https://img.shields.io/github/stars/AdguardTeam/AdGuardHome?style=flat-square) |
+| [algernon](https://github.com/xyproto/algernon) | Small self-contained pure-Go web server with Lua, Markdown, HTTP/2, QUIC, Redis and PostgreSQL support | ![GitHub Repo stars](https://img.shields.io/github/stars/xyproto/algernon?style=flat-square) |
+| [caddy](https://github.com/caddyserver/caddy/) | Fast, multi-platform web server with automatic HTTPS | ![GitHub Repo stars](https://img.shields.io/github/stars/caddyserver/caddy?style=flat-square) |
+| [cloudflared](https://github.com/cloudflare/cloudflared) | A tunneling daemon that proxies traffic from the Cloudflare network to your origins | ![GitHub Repo stars](https://img.shields.io/github/stars/cloudflare/cloudflared?style=flat-square) |
+| [go-libp2p](https://github.com/libp2p/go-libp2p) | libp2p implementation in Go, powering [Kubo](https://github.com/ipfs/kubo) (IPFS) and [Lotus](https://github.com/filecoin-project/lotus) (Filecoin), among others | ![GitHub Repo stars](https://img.shields.io/github/stars/libp2p/go-libp2p?style=flat-square) |
+| [OONI Probe](https://github.com/ooni/probe-cli) | Next generation OONI Probe. Library and CLI tool. | ![GitHub Repo stars](https://img.shields.io/github/stars/ooni/probe-cli?style=flat-square) |
+| [syncthing](https://github.com/syncthing/syncthing/) | Open Source Continuous File Synchronization | ![GitHub Repo stars](https://img.shields.io/github/stars/syncthing/syncthing?style=flat-square) |
+| [traefik](https://github.com/traefik/traefik) | The Cloud Native Application Proxy | ![GitHub Repo stars](https://img.shields.io/github/stars/traefik/traefik?style=flat-square) |
+| [v2ray-core](https://github.com/v2fly/v2ray-core) | A platform for building proxies to bypass network restrictions | ![GitHub Repo stars](https://img.shields.io/github/stars/v2fly/v2ray-core?style=flat-square) |
+| [YoMo](https://github.com/yomorun/yomo) | Streaming Serverless Framework for Geo-distributed System | ![GitHub Repo stars](https://img.shields.io/github/stars/yomorun/yomo?style=flat-square) |
+
+## Contributing
+
+We are always happy to welcome new contributors! We have a number of self-contained issues that are suitable for first-time contributors, they are tagged with [help wanted](https://github.com/quic-go/quic-go/issues?q=is%3Aissue+is%3Aopen+label%3A%22help+wanted%22). If you have any questions, please feel free to reach out by opening an issue or leaving a comment.
diff --git a/vendor/github.com/quic-go/quic-go/buffer_pool.go b/vendor/github.com/quic-go/quic-go/buffer_pool.go
new file mode 100644
index 0000000000..f6745b0803
--- /dev/null
+++ b/vendor/github.com/quic-go/quic-go/buffer_pool.go
@@ -0,0 +1,80 @@
+package quic
+
+import (
+ "sync"
+
+ "github.com/quic-go/quic-go/internal/protocol"
+)
+
+type packetBuffer struct {
+ Data []byte
+
+ // refCount counts how many packets Data is used in.
+ // It doesn't support concurrent use.
+ // It is > 1 when used for coalesced packet.
+ refCount int
+}
+
+// Split increases the refCount.
+// It must be called when a packet buffer is used for more than one packet,
+// e.g. when splitting coalesced packets.
+func (b *packetBuffer) Split() {
+ b.refCount++
+}
+
+// Decrement decrements the reference counter.
+// It doesn't put the buffer back into the pool.
+func (b *packetBuffer) Decrement() {
+ b.refCount--
+ if b.refCount < 0 {
+ panic("negative packetBuffer refCount")
+ }
+}
+
+// MaybeRelease puts the packet buffer back into the pool,
+// if the reference counter already reached 0.
+func (b *packetBuffer) MaybeRelease() {
+ // only put the packetBuffer back if it's not used any more
+ if b.refCount == 0 {
+ b.putBack()
+ }
+}
+
+// Release puts back the packet buffer into the pool.
+// It should be called when processing is definitely finished.
+func (b *packetBuffer) Release() {
+ b.Decrement()
+ if b.refCount != 0 {
+ panic("packetBuffer refCount not zero")
+ }
+ b.putBack()
+}
+
+// Len returns the length of Data
+func (b *packetBuffer) Len() protocol.ByteCount {
+ return protocol.ByteCount(len(b.Data))
+}
+
+func (b *packetBuffer) putBack() {
+ if cap(b.Data) != int(protocol.MaxPacketBufferSize) {
+ panic("putPacketBuffer called with packet of wrong size!")
+ }
+ bufferPool.Put(b)
+}
+
+var bufferPool sync.Pool
+
+func getPacketBuffer() *packetBuffer {
+ buf := bufferPool.Get().(*packetBuffer)
+ buf.refCount = 1
+ buf.Data = buf.Data[:0]
+ return buf
+}
+
+func init() {
+ bufferPool.New = func() interface{} {
+ return &packetBuffer{
+ Data: make([]byte, 0, protocol.MaxPacketBufferSize),
+ }
+ }
+}
diff --git a/vendor/github.com/quic-go/quic-go/client.go b/vendor/github.com/quic-go/quic-go/client.go
new file mode 100644
index 0000000000..b05f0ab2e1
--- /dev/null
+++ b/vendor/github.com/quic-go/quic-go/client.go
@@ -0,0 +1,332 @@
+package quic
+
+import (
+ "context"
+ "crypto/tls"
+ "errors"
+ "fmt"
+ "net"
+
+ "github.com/quic-go/quic-go/internal/protocol"
+ "github.com/quic-go/quic-go/internal/utils"
+ "github.com/quic-go/quic-go/logging"
+)
+
+type client struct {
+ sconn sendConn
+ // If the client is created with DialAddr, we create a packet conn.
+ // If it is started with Dial, we take a packet conn as a parameter.
+ createdPacketConn bool
+
+ use0RTT bool
+
+ packetHandlers packetHandlerManager
+
+ tlsConf *tls.Config
+ config *Config
+
+ srcConnID protocol.ConnectionID
+ destConnID protocol.ConnectionID
+
+ initialPacketNumber protocol.PacketNumber
+ hasNegotiatedVersion bool
+ version protocol.VersionNumber
+
+ handshakeChan chan struct{}
+
+ conn quicConn
+
+ tracer logging.ConnectionTracer
+ tracingID uint64
+ logger utils.Logger
+}
+
+// make it possible to mock connection ID for initial generation in the tests
+var generateConnectionIDForInitial = protocol.GenerateConnectionIDForInitial
+
+// DialAddr establishes a new QUIC connection to a server.
+// It uses a new UDP connection and closes this connection when the QUIC connection is closed.
+// The hostname for SNI is taken from the given address.
+// The tls.Config.CipherSuites allows setting of TLS 1.3 cipher suites.
+func DialAddr(
+ addr string,
+ tlsConf *tls.Config,
+ config *Config,
+) (Connection, error) {
+ return DialAddrContext(context.Background(), addr, tlsConf, config)
+}
+
+// DialAddrEarly establishes a new 0-RTT QUIC connection to a server.
+// It uses a new UDP connection and closes this connection when the QUIC connection is closed.
+// The hostname for SNI is taken from the given address.
+// The tls.Config.CipherSuites allows setting of TLS 1.3 cipher suites.
+func DialAddrEarly(
+ addr string,
+ tlsConf *tls.Config,
+ config *Config,
+) (EarlyConnection, error) {
+ return DialAddrEarlyContext(context.Background(), addr, tlsConf, config)
+}
+
+// DialAddrEarlyContext establishes a new 0-RTT QUIC connection to a server using provided context.
+// See DialAddrEarly for details
+func DialAddrEarlyContext(
+ ctx context.Context,
+ addr string,
+ tlsConf *tls.Config,
+ config *Config,
+) (EarlyConnection, error) {
+ conn, err := dialAddrContext(ctx, addr, tlsConf, config, true)
+ if err != nil {
+ return nil, err
+ }
+ utils.Logger.WithPrefix(utils.DefaultLogger, "client").Debugf("Returning early connection")
+ return conn, nil
+}
+
+// DialAddrContext establishes a new QUIC connection to a server using the provided context.
+// See DialAddr for details.
+func DialAddrContext(
+ ctx context.Context,
+ addr string,
+ tlsConf *tls.Config,
+ config *Config,
+) (Connection, error) {
+ return dialAddrContext(ctx, addr, tlsConf, config, false)
+}
+
+func dialAddrContext(
+ ctx context.Context,
+ addr string,
+ tlsConf *tls.Config,
+ config *Config,
+ use0RTT bool,
+) (quicConn, error) {
+ udpAddr, err := net.ResolveUDPAddr("udp", addr)
+ if err != nil {
+ return nil, err
+ }
+ udpConn, err := net.ListenUDP("udp", &net.UDPAddr{IP: net.IPv4zero, Port: 0})
+ if err != nil {
+ return nil, err
+ }
+ return dialContext(ctx, udpConn, udpAddr, addr, tlsConf, config, use0RTT, true)
+}
+
+// Dial establishes a new QUIC connection to a server using a net.PacketConn. If
+// the PacketConn satisfies the OOBCapablePacketConn interface (as a net.UDPConn
+// does), ECN and packet info support will be enabled. In this case, ReadMsgUDP
+// and WriteMsgUDP will be used instead of ReadFrom and WriteTo to read/write
+// packets. The same PacketConn can be used for multiple calls to Dial and
+// Listen, QUIC connection IDs are used for demultiplexing the different
+// connections. The host parameter is used for SNI. The tls.Config must define
+// an application protocol (using NextProtos).
+func Dial(
+ pconn net.PacketConn,
+ remoteAddr net.Addr,
+ host string,
+ tlsConf *tls.Config,
+ config *Config,
+) (Connection, error) {
+ return dialContext(context.Background(), pconn, remoteAddr, host, tlsConf, config, false, false)
+}
+
+// DialEarly establishes a new 0-RTT QUIC connection to a server using a net.PacketConn.
+// The same PacketConn can be used for multiple calls to Dial and Listen,
+// QUIC connection IDs are used for demultiplexing the different connections.
+// The host parameter is used for SNI.
+// The tls.Config must define an application protocol (using NextProtos).
+func DialEarly(
+ pconn net.PacketConn,
+ remoteAddr net.Addr,
+ host string,
+ tlsConf *tls.Config,
+ config *Config,
+) (EarlyConnection, error) {
+ return DialEarlyContext(context.Background(), pconn, remoteAddr, host, tlsConf, config)
+}
+
+// DialEarlyContext establishes a new 0-RTT QUIC connection to a server using a net.PacketConn using the provided context.
+// See DialEarly for details.
+func DialEarlyContext(
+ ctx context.Context,
+ pconn net.PacketConn,
+ remoteAddr net.Addr,
+ host string,
+ tlsConf *tls.Config,
+ config *Config,
+) (EarlyConnection, error) {
+ return dialContext(ctx, pconn, remoteAddr, host, tlsConf, config, true, false)
+}
+
+// DialContext establishes a new QUIC connection to a server using a net.PacketConn using the provided context.
+// See Dial for details.
+func DialContext(
+ ctx context.Context,
+ pconn net.PacketConn,
+ remoteAddr net.Addr,
+ host string,
+ tlsConf *tls.Config,
+ config *Config,
+) (Connection, error) {
+ return dialContext(ctx, pconn, remoteAddr, host, tlsConf, config, false, false)
+}
+
+func dialContext(
+ ctx context.Context,
+ pconn net.PacketConn,
+ remoteAddr net.Addr,
+ host string,
+ tlsConf *tls.Config,
+ config *Config,
+ use0RTT bool,
+ createdPacketConn bool,
+) (quicConn, error) {
+ if tlsConf == nil {
+ return nil, errors.New("quic: tls.Config not set")
+ }
+ if err := validateConfig(config); err != nil {
+ return nil, err
+ }
+ config = populateClientConfig(config, createdPacketConn)
+ packetHandlers, err := getMultiplexer().AddConn(pconn, config.ConnectionIDGenerator.ConnectionIDLen(), config.StatelessResetKey, config.Tracer)
+ if err != nil {
+ return nil, err
+ }
+ c, err := newClient(pconn, remoteAddr, config, tlsConf, host, use0RTT, createdPacketConn)
+ if err != nil {
+ return nil, err
+ }
+ c.packetHandlers = packetHandlers
+
+ c.tracingID = nextConnTracingID()
+ if c.config.Tracer != nil {
+ c.tracer = c.config.Tracer.TracerForConnection(
+ context.WithValue(ctx, ConnectionTracingKey, c.tracingID),
+ protocol.PerspectiveClient,
+ c.destConnID,
+ )
+ }
+ if c.tracer != nil {
+ c.tracer.StartedConnection(c.sconn.LocalAddr(), c.sconn.RemoteAddr(), c.srcConnID, c.destConnID)
+ }
+ if err := c.dial(ctx); err != nil {
+ return nil, err
+ }
+ return c.conn, nil
+}
+
+func newClient(
+ pconn net.PacketConn,
+ remoteAddr net.Addr,
+ config *Config,
+ tlsConf *tls.Config,
+ host string,
+ use0RTT bool,
+ createdPacketConn bool,
+) (*client, error) {
+ if tlsConf == nil {
+ tlsConf = &tls.Config{}
+ } else {
+ tlsConf = tlsConf.Clone()
+ }
+ if tlsConf.ServerName == "" {
+ sni, _, err := net.SplitHostPort(host)
+ if err != nil {
+ // It's ok if net.SplitHostPort returns an error - it could be a hostname/IP address without a port.
+ sni = host
+ }
+
+ tlsConf.ServerName = sni
+ }
+
+ // check that all versions are actually supported
+ if config != nil {
+ for _, v := range config.Versions {
+ if !protocol.IsValidVersion(v) {
+ return nil, fmt.Errorf("%s is not a valid QUIC version", v)
+ }
+ }
+ }
+
+ srcConnID, err := config.ConnectionIDGenerator.GenerateConnectionID()
+ if err != nil {
+ return nil, err
+ }
+ destConnID, err := generateConnectionIDForInitial()
+ if err != nil {
+ return nil, err
+ }
+ c := &client{
+ srcConnID: srcConnID,
+ destConnID: destConnID,
+ sconn: newSendPconn(pconn, remoteAddr),
+ createdPacketConn: createdPacketConn,
+ use0RTT: use0RTT,
+ tlsConf: tlsConf,
+ config: config,
+ version: config.Versions[0],
+ handshakeChan: make(chan struct{}),
+ logger: utils.DefaultLogger.WithPrefix("client"),
+ }
+ return c, nil
+}
+
+func (c *client) dial(ctx context.Context) error {
+ c.logger.Infof("Starting new connection to %s (%s -> %s), source connection ID %s, destination connection ID %s, version %s", c.tlsConf.ServerName, c.sconn.LocalAddr(), c.sconn.RemoteAddr(), c.srcConnID, c.destConnID, c.version)
+
+ c.conn = newClientConnection(
+ c.sconn,
+ c.packetHandlers,
+ c.destConnID,
+ c.srcConnID,
+ c.config,
+ c.tlsConf,
+ c.initialPacketNumber,
+ c.use0RTT,
+ c.hasNegotiatedVersion,
+ c.tracer,
+ c.tracingID,
+ c.logger,
+ c.version,
+ )
+ c.packetHandlers.Add(c.srcConnID, c.conn)
+
+ errorChan := make(chan error, 1)
+ go func() {
+ err := c.conn.run() // returns as soon as the connection is closed
+
+ if e := (&errCloseForRecreating{}); !errors.As(err, &e) && c.createdPacketConn {
+ c.packetHandlers.Destroy()
+ }
+ errorChan <- err
+ }()
+
+ // only set when we're using 0-RTT
+ // Otherwise, earlyConnChan will be nil. Receiving from a nil chan blocks forever.
+ var earlyConnChan <-chan struct{}
+ if c.use0RTT {
+ earlyConnChan = c.conn.earlyConnReady()
+ }
+
+ select {
+ case <-ctx.Done():
+ c.conn.shutdown()
+ return ctx.Err()
+ case err := <-errorChan:
+ var recreateErr *errCloseForRecreating
+ if errors.As(err, &recreateErr) {
+ c.initialPacketNumber = recreateErr.nextPacketNumber
+ c.version = recreateErr.nextVersion
+ c.hasNegotiatedVersion = true
+ return c.dial(ctx)
+ }
+ return err
+ case <-earlyConnChan:
+ // ready to send 0-RTT data
+ return nil
+ case <-c.conn.HandshakeComplete().Done():
+ // handshake successfully completed
+ return nil
+ }
+}
diff --git a/vendor/github.com/quic-go/quic-go/closed_conn.go b/vendor/github.com/quic-go/quic-go/closed_conn.go
new file mode 100644
index 0000000000..73904b8468
--- /dev/null
+++ b/vendor/github.com/quic-go/quic-go/closed_conn.go
@@ -0,0 +1,64 @@
+package quic
+
+import (
+ "math/bits"
+ "net"
+
+ "github.com/quic-go/quic-go/internal/protocol"
+ "github.com/quic-go/quic-go/internal/utils"
+)
+
+// A closedLocalConn is a connection that we closed locally.
+// When receiving packets for such a connection, we need to retransmit the packet containing the CONNECTION_CLOSE frame,
+// with an exponential backoff.
+type closedLocalConn struct {
+ counter uint32
+ perspective protocol.Perspective
+ logger utils.Logger
+
+ sendPacket func(net.Addr, *packetInfo)
+}
+
+var _ packetHandler = &closedLocalConn{}
+
+// newClosedLocalConn creates a new closedLocalConn and runs it.
+func newClosedLocalConn(sendPacket func(net.Addr, *packetInfo), pers protocol.Perspective, logger utils.Logger) packetHandler {
+ return &closedLocalConn{
+ sendPacket: sendPacket,
+ perspective: pers,
+ logger: logger,
+ }
+}
+
+func (c *closedLocalConn) handlePacket(p *receivedPacket) {
+ c.counter++
+ // exponential backoff
+ // only send a CONNECTION_CLOSE for the 1st, 2nd, 4th, 8th, 16th, ... packet arriving
+ if bits.OnesCount32(c.counter) != 1 {
+ return
+ }
+ c.logger.Debugf("Received %d packets after sending CONNECTION_CLOSE. Retransmitting.", c.counter)
+ c.sendPacket(p.remoteAddr, p.info)
+}
+
+func (c *closedLocalConn) shutdown() {}
+func (c *closedLocalConn) destroy(error) {}
+func (c *closedLocalConn) getPerspective() protocol.Perspective { return c.perspective }
+
+// A closedRemoteConn is a connection that was closed remotely.
+// For such a connection, we might receive reordered packets that were sent before the CONNECTION_CLOSE.
+// We can just ignore those packets.
+type closedRemoteConn struct {
+ perspective protocol.Perspective
+}
+
+var _ packetHandler = &closedRemoteConn{}
+
+func newClosedRemoteConn(pers protocol.Perspective) packetHandler {
+ return &closedRemoteConn{perspective: pers}
+}
+
+func (s *closedRemoteConn) handlePacket(*receivedPacket) {}
+func (s *closedRemoteConn) shutdown() {}
+func (s *closedRemoteConn) destroy(error) {}
+func (s *closedRemoteConn) getPerspective() protocol.Perspective { return s.perspective }
diff --git a/vendor/github.com/quic-go/quic-go/codecov.yml b/vendor/github.com/quic-go/quic-go/codecov.yml
new file mode 100644
index 0000000000..074d983252
--- /dev/null
+++ b/vendor/github.com/quic-go/quic-go/codecov.yml
@@ -0,0 +1,22 @@
+coverage:
+ round: nearest
+ ignore:
+ - streams_map_incoming_bidi.go
+ - streams_map_incoming_uni.go
+ - streams_map_outgoing_bidi.go
+ - streams_map_outgoing_uni.go
+ - http3/gzip_reader.go
+ - interop/
+ - internal/ackhandler/packet_linkedlist.go
+ - internal/utils/byteinterval_linkedlist.go
+ - internal/utils/newconnectionid_linkedlist.go
+ - internal/utils/packetinterval_linkedlist.go
+ - internal/utils/linkedlist/linkedlist.go
+ - logging/null_tracer.go
+ - fuzzing/
+ - metrics/
+ status:
+ project:
+ default:
+ threshold: 0.5
+ patch: false
diff --git a/vendor/github.com/quic-go/quic-go/config.go b/vendor/github.com/quic-go/quic-go/config.go
new file mode 100644
index 0000000000..3ead9b7a1c
--- /dev/null
+++ b/vendor/github.com/quic-go/quic-go/config.go
@@ -0,0 +1,141 @@
+package quic
+
+import (
+ "errors"
+ "net"
+ "time"
+
+ "github.com/quic-go/quic-go/internal/protocol"
+ "github.com/quic-go/quic-go/internal/utils"
+)
+
+// Clone clones a Config
+func (c *Config) Clone() *Config {
+ copy := *c
+ return ©
+}
+
+func (c *Config) handshakeTimeout() time.Duration {
+ return utils.Max(protocol.DefaultHandshakeTimeout, 2*c.HandshakeIdleTimeout)
+}
+
+func validateConfig(config *Config) error {
+ if config == nil {
+ return nil
+ }
+ if config.MaxIncomingStreams > 1<<60 {
+ return errors.New("invalid value for Config.MaxIncomingStreams")
+ }
+ if config.MaxIncomingUniStreams > 1<<60 {
+ return errors.New("invalid value for Config.MaxIncomingUniStreams")
+ }
+ return nil
+}
+
+// populateServerConfig populates fields in the quic.Config with their default values, if none are set
+// it may be called with nil
+func populateServerConfig(config *Config) *Config {
+ config = populateConfig(config, protocol.DefaultConnectionIDLength)
+ if config.MaxTokenAge == 0 {
+ config.MaxTokenAge = protocol.TokenValidity
+ }
+ if config.MaxRetryTokenAge == 0 {
+ config.MaxRetryTokenAge = protocol.RetryTokenValidity
+ }
+ if config.RequireAddressValidation == nil {
+ config.RequireAddressValidation = func(net.Addr) bool { return false }
+ }
+ return config
+}
+
+// populateClientConfig populates fields in the quic.Config with their default values, if none are set
+// it may be called with nil
+func populateClientConfig(config *Config, createdPacketConn bool) *Config {
+ defaultConnIDLen := protocol.DefaultConnectionIDLength
+ if createdPacketConn {
+ defaultConnIDLen = 0
+ }
+
+ config = populateConfig(config, defaultConnIDLen)
+ return config
+}
+
+func populateConfig(config *Config, defaultConnIDLen int) *Config {
+ if config == nil {
+ config = &Config{}
+ }
+ versions := config.Versions
+ if len(versions) == 0 {
+ versions = protocol.SupportedVersions
+ }
+ conIDLen := config.ConnectionIDLength
+ if config.ConnectionIDLength == 0 {
+ conIDLen = defaultConnIDLen
+ }
+ handshakeIdleTimeout := protocol.DefaultHandshakeIdleTimeout
+ if config.HandshakeIdleTimeout != 0 {
+ handshakeIdleTimeout = config.HandshakeIdleTimeout
+ }
+ idleTimeout := protocol.DefaultIdleTimeout
+ if config.MaxIdleTimeout != 0 {
+ idleTimeout = config.MaxIdleTimeout
+ }
+ initialStreamReceiveWindow := config.InitialStreamReceiveWindow
+ if initialStreamReceiveWindow == 0 {
+ initialStreamReceiveWindow = protocol.DefaultInitialMaxStreamData
+ }
+ maxStreamReceiveWindow := config.MaxStreamReceiveWindow
+ if maxStreamReceiveWindow == 0 {
+ maxStreamReceiveWindow = protocol.DefaultMaxReceiveStreamFlowControlWindow
+ }
+ initialConnectionReceiveWindow := config.InitialConnectionReceiveWindow
+ if initialConnectionReceiveWindow == 0 {
+ initialConnectionReceiveWindow = protocol.DefaultInitialMaxData
+ }
+ maxConnectionReceiveWindow := config.MaxConnectionReceiveWindow
+ if maxConnectionReceiveWindow == 0 {
+ maxConnectionReceiveWindow = protocol.DefaultMaxReceiveConnectionFlowControlWindow
+ }
+ maxIncomingStreams := config.MaxIncomingStreams
+ if maxIncomingStreams == 0 {
+ maxIncomingStreams = protocol.DefaultMaxIncomingStreams
+ } else if maxIncomingStreams < 0 {
+ maxIncomingStreams = 0
+ }
+ maxIncomingUniStreams := config.MaxIncomingUniStreams
+ if maxIncomingUniStreams == 0 {
+ maxIncomingUniStreams = protocol.DefaultMaxIncomingUniStreams
+ } else if maxIncomingUniStreams < 0 {
+ maxIncomingUniStreams = 0
+ }
+ connIDGenerator := config.ConnectionIDGenerator
+ if connIDGenerator == nil {
+ connIDGenerator = &protocol.DefaultConnectionIDGenerator{ConnLen: conIDLen}
+ }
+
+ return &Config{
+ Versions: versions,
+ HandshakeIdleTimeout: handshakeIdleTimeout,
+ MaxIdleTimeout: idleTimeout,
+ MaxTokenAge: config.MaxTokenAge,
+ MaxRetryTokenAge: config.MaxRetryTokenAge,
+ RequireAddressValidation: config.RequireAddressValidation,
+ KeepAlivePeriod: config.KeepAlivePeriod,
+ InitialStreamReceiveWindow: initialStreamReceiveWindow,
+ MaxStreamReceiveWindow: maxStreamReceiveWindow,
+ InitialConnectionReceiveWindow: initialConnectionReceiveWindow,
+ MaxConnectionReceiveWindow: maxConnectionReceiveWindow,
+ AllowConnectionWindowIncrease: config.AllowConnectionWindowIncrease,
+ MaxIncomingStreams: maxIncomingStreams,
+ MaxIncomingUniStreams: maxIncomingUniStreams,
+ ConnectionIDLength: conIDLen,
+ ConnectionIDGenerator: connIDGenerator,
+ StatelessResetKey: config.StatelessResetKey,
+ TokenStore: config.TokenStore,
+ EnableDatagrams: config.EnableDatagrams,
+ DisablePathMTUDiscovery: config.DisablePathMTUDiscovery,
+ DisableVersionNegotiationPackets: config.DisableVersionNegotiationPackets,
+ Allow0RTT: config.Allow0RTT,
+ Tracer: config.Tracer,
+ }
+}
diff --git a/vendor/github.com/quic-go/quic-go/conn_id_generator.go b/vendor/github.com/quic-go/quic-go/conn_id_generator.go
new file mode 100644
index 0000000000..2d28dc619c
--- /dev/null
+++ b/vendor/github.com/quic-go/quic-go/conn_id_generator.go
@@ -0,0 +1,139 @@
+package quic
+
+import (
+ "fmt"
+
+ "github.com/quic-go/quic-go/internal/protocol"
+ "github.com/quic-go/quic-go/internal/qerr"
+ "github.com/quic-go/quic-go/internal/utils"
+ "github.com/quic-go/quic-go/internal/wire"
+)
+
+type connIDGenerator struct {
+ generator ConnectionIDGenerator
+ highestSeq uint64
+
+ activeSrcConnIDs map[uint64]protocol.ConnectionID
+ initialClientDestConnID *protocol.ConnectionID // nil for the client
+
+ addConnectionID func(protocol.ConnectionID)
+ getStatelessResetToken func(protocol.ConnectionID) protocol.StatelessResetToken
+ removeConnectionID func(protocol.ConnectionID)
+ retireConnectionID func(protocol.ConnectionID)
+ replaceWithClosed func([]protocol.ConnectionID, protocol.Perspective, []byte)
+ queueControlFrame func(wire.Frame)
+}
+
+func newConnIDGenerator(
+ initialConnectionID protocol.ConnectionID,
+ initialClientDestConnID *protocol.ConnectionID, // nil for the client
+ addConnectionID func(protocol.ConnectionID),
+ getStatelessResetToken func(protocol.ConnectionID) protocol.StatelessResetToken,
+ removeConnectionID func(protocol.ConnectionID),
+ retireConnectionID func(protocol.ConnectionID),
+ replaceWithClosed func([]protocol.ConnectionID, protocol.Perspective, []byte),
+ queueControlFrame func(wire.Frame),
+ generator ConnectionIDGenerator,
+) *connIDGenerator {
+ m := &connIDGenerator{
+ generator: generator,
+ activeSrcConnIDs: make(map[uint64]protocol.ConnectionID),
+ addConnectionID: addConnectionID,
+ getStatelessResetToken: getStatelessResetToken,
+ removeConnectionID: removeConnectionID,
+ retireConnectionID: retireConnectionID,
+ replaceWithClosed: replaceWithClosed,
+ queueControlFrame: queueControlFrame,
+ }
+ m.activeSrcConnIDs[0] = initialConnectionID
+ m.initialClientDestConnID = initialClientDestConnID
+ return m
+}
+
+func (m *connIDGenerator) SetMaxActiveConnIDs(limit uint64) error {
+ if m.generator.ConnectionIDLen() == 0 {
+ return nil
+ }
+ // The active_connection_id_limit transport parameter is the number of
+ // connection IDs the peer will store. This limit includes the connection ID
+ // used during the handshake, and the one sent in the preferred_address
+ // transport parameter.
+ // We currently don't send the preferred_address transport parameter,
+ // so we can issue (limit - 1) connection IDs.
+ for i := uint64(len(m.activeSrcConnIDs)); i < utils.Min(limit, protocol.MaxIssuedConnectionIDs); i++ {
+ if err := m.issueNewConnID(); err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
+func (m *connIDGenerator) Retire(seq uint64, sentWithDestConnID protocol.ConnectionID) error {
+ if seq > m.highestSeq {
+ return &qerr.TransportError{
+ ErrorCode: qerr.ProtocolViolation,
+ ErrorMessage: fmt.Sprintf("retired connection ID %d (highest issued: %d)", seq, m.highestSeq),
+ }
+ }
+ connID, ok := m.activeSrcConnIDs[seq]
+ // We might already have deleted this connection ID, if this is a duplicate frame.
+ if !ok {
+ return nil
+ }
+ if connID == sentWithDestConnID {
+ return &qerr.TransportError{
+ ErrorCode: qerr.ProtocolViolation,
+ ErrorMessage: fmt.Sprintf("retired connection ID %d (%s), which was used as the Destination Connection ID on this packet", seq, connID),
+ }
+ }
+ m.retireConnectionID(connID)
+ delete(m.activeSrcConnIDs, seq)
+ // Don't issue a replacement for the initial connection ID.
+ if seq == 0 {
+ return nil
+ }
+ return m.issueNewConnID()
+}
+
+func (m *connIDGenerator) issueNewConnID() error {
+ connID, err := m.generator.GenerateConnectionID()
+ if err != nil {
+ return err
+ }
+ m.activeSrcConnIDs[m.highestSeq+1] = connID
+ m.addConnectionID(connID)
+ m.queueControlFrame(&wire.NewConnectionIDFrame{
+ SequenceNumber: m.highestSeq + 1,
+ ConnectionID: connID,
+ StatelessResetToken: m.getStatelessResetToken(connID),
+ })
+ m.highestSeq++
+ return nil
+}
+
+func (m *connIDGenerator) SetHandshakeComplete() {
+ if m.initialClientDestConnID != nil {
+ m.retireConnectionID(*m.initialClientDestConnID)
+ m.initialClientDestConnID = nil
+ }
+}
+
+func (m *connIDGenerator) RemoveAll() {
+ if m.initialClientDestConnID != nil {
+ m.removeConnectionID(*m.initialClientDestConnID)
+ }
+ for _, connID := range m.activeSrcConnIDs {
+ m.removeConnectionID(connID)
+ }
+}
+
+func (m *connIDGenerator) ReplaceWithClosed(pers protocol.Perspective, connClose []byte) {
+ connIDs := make([]protocol.ConnectionID, 0, len(m.activeSrcConnIDs)+1)
+ if m.initialClientDestConnID != nil {
+ connIDs = append(connIDs, *m.initialClientDestConnID)
+ }
+ for _, connID := range m.activeSrcConnIDs {
+ connIDs = append(connIDs, connID)
+ }
+ m.replaceWithClosed(connIDs, pers, connClose)
+}
diff --git a/vendor/github.com/quic-go/quic-go/conn_id_manager.go b/vendor/github.com/quic-go/quic-go/conn_id_manager.go
new file mode 100644
index 0000000000..ba65aec043
--- /dev/null
+++ b/vendor/github.com/quic-go/quic-go/conn_id_manager.go
@@ -0,0 +1,214 @@
+package quic
+
+import (
+ "fmt"
+
+ "github.com/quic-go/quic-go/internal/protocol"
+ "github.com/quic-go/quic-go/internal/qerr"
+ "github.com/quic-go/quic-go/internal/utils"
+ list "github.com/quic-go/quic-go/internal/utils/linkedlist"
+ "github.com/quic-go/quic-go/internal/wire"
+)
+
+type newConnID struct {
+ SequenceNumber uint64
+ ConnectionID protocol.ConnectionID
+ StatelessResetToken protocol.StatelessResetToken
+}
+
+type connIDManager struct {
+ queue list.List[newConnID]
+
+ handshakeComplete bool
+ activeSequenceNumber uint64
+ highestRetired uint64
+ activeConnectionID protocol.ConnectionID
+ activeStatelessResetToken *protocol.StatelessResetToken
+
+ // We change the connection ID after sending on average
+ // protocol.PacketsPerConnectionID packets. The actual value is randomized
+ // hide the packet loss rate from on-path observers.
+ rand utils.Rand
+ packetsSinceLastChange uint32
+ packetsPerConnectionID uint32
+
+ addStatelessResetToken func(protocol.StatelessResetToken)
+ removeStatelessResetToken func(protocol.StatelessResetToken)
+ queueControlFrame func(wire.Frame)
+}
+
+func newConnIDManager(
+ initialDestConnID protocol.ConnectionID,
+ addStatelessResetToken func(protocol.StatelessResetToken),
+ removeStatelessResetToken func(protocol.StatelessResetToken),
+ queueControlFrame func(wire.Frame),
+) *connIDManager {
+ return &connIDManager{
+ activeConnectionID: initialDestConnID,
+ addStatelessResetToken: addStatelessResetToken,
+ removeStatelessResetToken: removeStatelessResetToken,
+ queueControlFrame: queueControlFrame,
+ }
+}
+
+func (h *connIDManager) AddFromPreferredAddress(connID protocol.ConnectionID, resetToken protocol.StatelessResetToken) error {
+ return h.addConnectionID(1, connID, resetToken)
+}
+
+func (h *connIDManager) Add(f *wire.NewConnectionIDFrame) error {
+ if err := h.add(f); err != nil {
+ return err
+ }
+ if h.queue.Len() >= protocol.MaxActiveConnectionIDs {
+ return &qerr.TransportError{ErrorCode: qerr.ConnectionIDLimitError}
+ }
+ return nil
+}
+
+func (h *connIDManager) add(f *wire.NewConnectionIDFrame) error {
+ // If the NEW_CONNECTION_ID frame is reordered, such that its sequence number is smaller than the currently active
+ // connection ID or if it was already retired, send the RETIRE_CONNECTION_ID frame immediately.
+ if f.SequenceNumber < h.activeSequenceNumber || f.SequenceNumber < h.highestRetired {
+ h.queueControlFrame(&wire.RetireConnectionIDFrame{
+ SequenceNumber: f.SequenceNumber,
+ })
+ return nil
+ }
+
+ // Retire elements in the queue.
+ // Doesn't retire the active connection ID.
+ if f.RetirePriorTo > h.highestRetired {
+ var next *list.Element[newConnID]
+ for el := h.queue.Front(); el != nil; el = next {
+ if el.Value.SequenceNumber >= f.RetirePriorTo {
+ break
+ }
+ next = el.Next()
+ h.queueControlFrame(&wire.RetireConnectionIDFrame{
+ SequenceNumber: el.Value.SequenceNumber,
+ })
+ h.queue.Remove(el)
+ }
+ h.highestRetired = f.RetirePriorTo
+ }
+
+ if f.SequenceNumber == h.activeSequenceNumber {
+ return nil
+ }
+
+ if err := h.addConnectionID(f.SequenceNumber, f.ConnectionID, f.StatelessResetToken); err != nil {
+ return err
+ }
+
+ // Retire the active connection ID, if necessary.
+ if h.activeSequenceNumber < f.RetirePriorTo {
+ // The queue is guaranteed to have at least one element at this point.
+ h.updateConnectionID()
+ }
+ return nil
+}
+
+func (h *connIDManager) addConnectionID(seq uint64, connID protocol.ConnectionID, resetToken protocol.StatelessResetToken) error {
+ // insert a new element at the end
+ if h.queue.Len() == 0 || h.queue.Back().Value.SequenceNumber < seq {
+ h.queue.PushBack(newConnID{
+ SequenceNumber: seq,
+ ConnectionID: connID,
+ StatelessResetToken: resetToken,
+ })
+ return nil
+ }
+ // insert a new element somewhere in the middle
+ for el := h.queue.Front(); el != nil; el = el.Next() {
+ if el.Value.SequenceNumber == seq {
+ if el.Value.ConnectionID != connID {
+ return fmt.Errorf("received conflicting connection IDs for sequence number %d", seq)
+ }
+ if el.Value.StatelessResetToken != resetToken {
+ return fmt.Errorf("received conflicting stateless reset tokens for sequence number %d", seq)
+ }
+ break
+ }
+ if el.Value.SequenceNumber > seq {
+ h.queue.InsertBefore(newConnID{
+ SequenceNumber: seq,
+ ConnectionID: connID,
+ StatelessResetToken: resetToken,
+ }, el)
+ break
+ }
+ }
+ return nil
+}
+
+func (h *connIDManager) updateConnectionID() {
+ h.queueControlFrame(&wire.RetireConnectionIDFrame{
+ SequenceNumber: h.activeSequenceNumber,
+ })
+ h.highestRetired = utils.Max(h.highestRetired, h.activeSequenceNumber)
+ if h.activeStatelessResetToken != nil {
+ h.removeStatelessResetToken(*h.activeStatelessResetToken)
+ }
+
+ front := h.queue.Remove(h.queue.Front())
+ h.activeSequenceNumber = front.SequenceNumber
+ h.activeConnectionID = front.ConnectionID
+ h.activeStatelessResetToken = &front.StatelessResetToken
+ h.packetsSinceLastChange = 0
+ h.packetsPerConnectionID = protocol.PacketsPerConnectionID/2 + uint32(h.rand.Int31n(protocol.PacketsPerConnectionID))
+ h.addStatelessResetToken(*h.activeStatelessResetToken)
+}
+
+func (h *connIDManager) Close() {
+ if h.activeStatelessResetToken != nil {
+ h.removeStatelessResetToken(*h.activeStatelessResetToken)
+ }
+}
+
+// is called when the server performs a Retry
+// and when the server changes the connection ID in the first Initial sent
+func (h *connIDManager) ChangeInitialConnID(newConnID protocol.ConnectionID) {
+ if h.activeSequenceNumber != 0 {
+ panic("expected first connection ID to have sequence number 0")
+ }
+ h.activeConnectionID = newConnID
+}
+
+// is called when the server provides a stateless reset token in the transport parameters
+func (h *connIDManager) SetStatelessResetToken(token protocol.StatelessResetToken) {
+ if h.activeSequenceNumber != 0 {
+ panic("expected first connection ID to have sequence number 0")
+ }
+ h.activeStatelessResetToken = &token
+ h.addStatelessResetToken(token)
+}
+
+func (h *connIDManager) SentPacket() {
+ h.packetsSinceLastChange++
+}
+
+func (h *connIDManager) shouldUpdateConnID() bool {
+ if !h.handshakeComplete {
+ return false
+ }
+ // initiate the first change as early as possible (after handshake completion)
+ if h.queue.Len() > 0 && h.activeSequenceNumber == 0 {
+ return true
+ }
+ // For later changes, only change if
+ // 1. The queue of connection IDs is filled more than 50%.
+ // 2. We sent at least PacketsPerConnectionID packets
+ return 2*h.queue.Len() >= protocol.MaxActiveConnectionIDs &&
+ h.packetsSinceLastChange >= h.packetsPerConnectionID
+}
+
+func (h *connIDManager) Get() protocol.ConnectionID {
+ if h.shouldUpdateConnID() {
+ h.updateConnectionID()
+ }
+ return h.activeConnectionID
+}
+
+func (h *connIDManager) SetHandshakeComplete() {
+ h.handshakeComplete = true
+}
diff --git a/vendor/github.com/quic-go/quic-go/connection.go b/vendor/github.com/quic-go/quic-go/connection.go
new file mode 100644
index 0000000000..18a02655dc
--- /dev/null
+++ b/vendor/github.com/quic-go/quic-go/connection.go
@@ -0,0 +1,2185 @@
+package quic
+
+import (
+ "bytes"
+ "context"
+ "crypto/tls"
+ "errors"
+ "fmt"
+ "io"
+ "net"
+ "reflect"
+ "sync"
+ "sync/atomic"
+ "time"
+
+ "github.com/quic-go/quic-go/internal/ackhandler"
+ "github.com/quic-go/quic-go/internal/flowcontrol"
+ "github.com/quic-go/quic-go/internal/handshake"
+ "github.com/quic-go/quic-go/internal/logutils"
+ "github.com/quic-go/quic-go/internal/protocol"
+ "github.com/quic-go/quic-go/internal/qerr"
+ "github.com/quic-go/quic-go/internal/utils"
+ "github.com/quic-go/quic-go/internal/wire"
+ "github.com/quic-go/quic-go/logging"
+)
+
+type unpacker interface {
+ UnpackLongHeader(hdr *wire.Header, rcvTime time.Time, data []byte, v protocol.VersionNumber) (*unpackedPacket, error)
+ UnpackShortHeader(rcvTime time.Time, data []byte) (protocol.PacketNumber, protocol.PacketNumberLen, protocol.KeyPhaseBit, []byte, error)
+}
+
+type streamGetter interface {
+ GetOrOpenReceiveStream(protocol.StreamID) (receiveStreamI, error)
+ GetOrOpenSendStream(protocol.StreamID) (sendStreamI, error)
+}
+
+type streamManager interface {
+ GetOrOpenSendStream(protocol.StreamID) (sendStreamI, error)
+ GetOrOpenReceiveStream(protocol.StreamID) (receiveStreamI, error)
+ OpenStream() (Stream, error)
+ OpenUniStream() (SendStream, error)
+ OpenStreamSync(context.Context) (Stream, error)
+ OpenUniStreamSync(context.Context) (SendStream, error)
+ AcceptStream(context.Context) (Stream, error)
+ AcceptUniStream(context.Context) (ReceiveStream, error)
+ DeleteStream(protocol.StreamID) error
+ UpdateLimits(*wire.TransportParameters)
+ HandleMaxStreamsFrame(*wire.MaxStreamsFrame)
+ CloseWithError(error)
+ ResetFor0RTT()
+ UseResetMaps()
+}
+
+type cryptoStreamHandler interface {
+ RunHandshake()
+ ChangeConnectionID(protocol.ConnectionID)
+ SetLargest1RTTAcked(protocol.PacketNumber) error
+ SetHandshakeConfirmed()
+ GetSessionTicket() ([]byte, error)
+ io.Closer
+ ConnectionState() handshake.ConnectionState
+}
+
+type packetInfo struct {
+ addr net.IP
+ ifIndex uint32
+}
+
+type receivedPacket struct {
+ buffer *packetBuffer
+
+ remoteAddr net.Addr
+ rcvTime time.Time
+ data []byte
+
+ ecn protocol.ECN
+
+ info *packetInfo
+}
+
+func (p *receivedPacket) Size() protocol.ByteCount { return protocol.ByteCount(len(p.data)) }
+
+func (p *receivedPacket) Clone() *receivedPacket {
+ return &receivedPacket{
+ remoteAddr: p.remoteAddr,
+ rcvTime: p.rcvTime,
+ data: p.data,
+ buffer: p.buffer,
+ ecn: p.ecn,
+ info: p.info,
+ }
+}
+
+type connRunner interface {
+ Add(protocol.ConnectionID, packetHandler) bool
+ GetStatelessResetToken(protocol.ConnectionID) protocol.StatelessResetToken
+ Retire(protocol.ConnectionID)
+ Remove(protocol.ConnectionID)
+ ReplaceWithClosed([]protocol.ConnectionID, protocol.Perspective, []byte)
+ AddResetToken(protocol.StatelessResetToken, packetHandler)
+ RemoveResetToken(protocol.StatelessResetToken)
+}
+
+type handshakeRunner struct {
+ onReceivedParams func(*wire.TransportParameters)
+ onError func(error)
+ dropKeys func(protocol.EncryptionLevel)
+ onHandshakeComplete func()
+}
+
+func (r *handshakeRunner) OnReceivedParams(tp *wire.TransportParameters) { r.onReceivedParams(tp) }
+func (r *handshakeRunner) OnError(e error) { r.onError(e) }
+func (r *handshakeRunner) DropKeys(el protocol.EncryptionLevel) { r.dropKeys(el) }
+func (r *handshakeRunner) OnHandshakeComplete() { r.onHandshakeComplete() }
+
+type closeError struct {
+ err error
+ remote bool
+ immediate bool
+}
+
+type errCloseForRecreating struct {
+ nextPacketNumber protocol.PacketNumber
+ nextVersion protocol.VersionNumber
+}
+
+func (e *errCloseForRecreating) Error() string {
+ return "closing connection in order to recreate it"
+}
+
+var connTracingID uint64 // to be accessed atomically
+func nextConnTracingID() uint64 { return atomic.AddUint64(&connTracingID, 1) }
+
+// A Connection is a QUIC connection
+type connection struct {
+ // Destination connection ID used during the handshake.
+ // Used to check source connection ID on incoming packets.
+ handshakeDestConnID protocol.ConnectionID
+ // Set for the client. Destination connection ID used on the first Initial sent.
+ origDestConnID protocol.ConnectionID
+ retrySrcConnID *protocol.ConnectionID // only set for the client (and if a Retry was performed)
+
+ srcConnIDLen int
+
+ perspective protocol.Perspective
+ version protocol.VersionNumber
+ config *Config
+
+ conn sendConn
+ sendQueue sender
+
+ streamsMap streamManager
+ connIDManager *connIDManager
+ connIDGenerator *connIDGenerator
+
+ rttStats *utils.RTTStats
+
+ cryptoStreamManager *cryptoStreamManager
+ sentPacketHandler ackhandler.SentPacketHandler
+ receivedPacketHandler ackhandler.ReceivedPacketHandler
+ retransmissionQueue *retransmissionQueue
+ framer framer
+ windowUpdateQueue *windowUpdateQueue
+ connFlowController flowcontrol.ConnectionFlowController
+ tokenStoreKey string // only set for the client
+ tokenGenerator *handshake.TokenGenerator // only set for the server
+
+ unpacker unpacker
+ frameParser wire.FrameParser
+ packer packer
+ mtuDiscoverer mtuDiscoverer // initialized when the handshake completes
+
+ oneRTTStream cryptoStream // only set for the server
+ cryptoStreamHandler cryptoStreamHandler
+
+ receivedPackets chan *receivedPacket
+ sendingScheduled chan struct{}
+
+ closeOnce sync.Once
+ // closeChan is used to notify the run loop that it should terminate
+ closeChan chan closeError
+
+ ctx context.Context
+ ctxCancel context.CancelFunc
+ handshakeCtx context.Context
+ handshakeCtxCancel context.CancelFunc
+
+ undecryptablePackets []*receivedPacket // undecryptable packets, waiting for a change in encryption level
+ undecryptablePacketsToProcess []*receivedPacket
+
+ clientHelloWritten <-chan *wire.TransportParameters
+ earlyConnReadyChan chan struct{}
+ handshakeCompleteChan chan struct{} // is closed when the handshake completes
+ sentFirstPacket bool
+ handshakeComplete bool
+ handshakeConfirmed bool
+
+ receivedRetry bool
+ versionNegotiated bool
+ receivedFirstPacket bool
+
+ idleTimeout time.Duration
+ creationTime time.Time
+ // The idle timeout is set based on the max of the time we received the last packet...
+ lastPacketReceivedTime time.Time
+ // ... and the time we sent a new ack-eliciting packet after receiving a packet.
+ firstAckElicitingPacketAfterIdleSentTime time.Time
+ // pacingDeadline is the time when the next packet should be sent
+ pacingDeadline time.Time
+
+ peerParams *wire.TransportParameters
+
+ timer connectionTimer
+ // keepAlivePingSent stores whether a keep alive PING is in flight.
+ // It is reset as soon as we receive a packet from the peer.
+ keepAlivePingSent bool
+ keepAliveInterval time.Duration
+
+ datagramQueue *datagramQueue
+
+ connStateMutex sync.Mutex
+ connState ConnectionState
+
+ logID string
+ tracer logging.ConnectionTracer
+ logger utils.Logger
+}
+
+var (
+ _ Connection = &connection{}
+ _ EarlyConnection = &connection{}
+ _ streamSender = &connection{}
+)
+
+var newConnection = func(
+ conn sendConn,
+ runner connRunner,
+ origDestConnID protocol.ConnectionID,
+ retrySrcConnID *protocol.ConnectionID,
+ clientDestConnID protocol.ConnectionID,
+ destConnID protocol.ConnectionID,
+ srcConnID protocol.ConnectionID,
+ statelessResetToken protocol.StatelessResetToken,
+ conf *Config,
+ tlsConf *tls.Config,
+ tokenGenerator *handshake.TokenGenerator,
+ clientAddressValidated bool,
+ tracer logging.ConnectionTracer,
+ tracingID uint64,
+ logger utils.Logger,
+ v protocol.VersionNumber,
+) quicConn {
+ s := &connection{
+ conn: conn,
+ config: conf,
+ handshakeDestConnID: destConnID,
+ srcConnIDLen: srcConnID.Len(),
+ tokenGenerator: tokenGenerator,
+ oneRTTStream: newCryptoStream(),
+ perspective: protocol.PerspectiveServer,
+ handshakeCompleteChan: make(chan struct{}),
+ tracer: tracer,
+ logger: logger,
+ version: v,
+ }
+ if origDestConnID.Len() > 0 {
+ s.logID = origDestConnID.String()
+ } else {
+ s.logID = destConnID.String()
+ }
+ s.connIDManager = newConnIDManager(
+ destConnID,
+ func(token protocol.StatelessResetToken) { runner.AddResetToken(token, s) },
+ runner.RemoveResetToken,
+ s.queueControlFrame,
+ )
+ s.connIDGenerator = newConnIDGenerator(
+ srcConnID,
+ &clientDestConnID,
+ func(connID protocol.ConnectionID) { runner.Add(connID, s) },
+ runner.GetStatelessResetToken,
+ runner.Remove,
+ runner.Retire,
+ runner.ReplaceWithClosed,
+ s.queueControlFrame,
+ s.config.ConnectionIDGenerator,
+ )
+ s.preSetup()
+ s.ctx, s.ctxCancel = context.WithCancel(context.WithValue(context.Background(), ConnectionTracingKey, tracingID))
+ s.sentPacketHandler, s.receivedPacketHandler = ackhandler.NewAckHandler(
+ 0,
+ getMaxPacketSize(s.conn.RemoteAddr()),
+ s.rttStats,
+ clientAddressValidated,
+ s.perspective,
+ s.tracer,
+ s.logger,
+ )
+ initialStream := newCryptoStream()
+ handshakeStream := newCryptoStream()
+ params := &wire.TransportParameters{
+ InitialMaxStreamDataBidiLocal: protocol.ByteCount(s.config.InitialStreamReceiveWindow),
+ InitialMaxStreamDataBidiRemote: protocol.ByteCount(s.config.InitialStreamReceiveWindow),
+ InitialMaxStreamDataUni: protocol.ByteCount(s.config.InitialStreamReceiveWindow),
+ InitialMaxData: protocol.ByteCount(s.config.InitialConnectionReceiveWindow),
+ MaxIdleTimeout: s.config.MaxIdleTimeout,
+ MaxBidiStreamNum: protocol.StreamNum(s.config.MaxIncomingStreams),
+ MaxUniStreamNum: protocol.StreamNum(s.config.MaxIncomingUniStreams),
+ MaxAckDelay: protocol.MaxAckDelayInclGranularity,
+ AckDelayExponent: protocol.AckDelayExponent,
+ DisableActiveMigration: true,
+ StatelessResetToken: &statelessResetToken,
+ OriginalDestinationConnectionID: origDestConnID,
+ ActiveConnectionIDLimit: protocol.MaxActiveConnectionIDs,
+ InitialSourceConnectionID: srcConnID,
+ RetrySourceConnectionID: retrySrcConnID,
+ }
+ if s.config.EnableDatagrams {
+ params.MaxDatagramFrameSize = protocol.MaxDatagramFrameSize
+ } else {
+ params.MaxDatagramFrameSize = protocol.InvalidByteCount
+ }
+ if s.tracer != nil {
+ s.tracer.SentTransportParameters(params)
+ }
+ var allow0RTT func() bool
+ if conf.Allow0RTT != nil {
+ allow0RTT = func() bool { return conf.Allow0RTT(conn.RemoteAddr()) }
+ }
+ cs := handshake.NewCryptoSetupServer(
+ initialStream,
+ handshakeStream,
+ clientDestConnID,
+ conn.LocalAddr(),
+ conn.RemoteAddr(),
+ params,
+ &handshakeRunner{
+ onReceivedParams: s.handleTransportParameters,
+ onError: s.closeLocal,
+ dropKeys: s.dropEncryptionLevel,
+ onHandshakeComplete: func() {
+ runner.Retire(clientDestConnID)
+ close(s.handshakeCompleteChan)
+ },
+ },
+ tlsConf,
+ allow0RTT,
+ s.rttStats,
+ tracer,
+ logger,
+ s.version,
+ )
+ s.cryptoStreamHandler = cs
+ s.packer = newPacketPacker(srcConnID, s.connIDManager.Get, initialStream, handshakeStream, s.sentPacketHandler, s.retransmissionQueue, s.RemoteAddr(), cs, s.framer, s.receivedPacketHandler, s.datagramQueue, s.perspective)
+ s.unpacker = newPacketUnpacker(cs, s.srcConnIDLen)
+ s.cryptoStreamManager = newCryptoStreamManager(cs, initialStream, handshakeStream, s.oneRTTStream)
+ return s
+}
+
+// declare this as a variable, such that we can it mock it in the tests
+var newClientConnection = func(
+ conn sendConn,
+ runner connRunner,
+ destConnID protocol.ConnectionID,
+ srcConnID protocol.ConnectionID,
+ conf *Config,
+ tlsConf *tls.Config,
+ initialPacketNumber protocol.PacketNumber,
+ enable0RTT bool,
+ hasNegotiatedVersion bool,
+ tracer logging.ConnectionTracer,
+ tracingID uint64,
+ logger utils.Logger,
+ v protocol.VersionNumber,
+) quicConn {
+ s := &connection{
+ conn: conn,
+ config: conf,
+ origDestConnID: destConnID,
+ handshakeDestConnID: destConnID,
+ srcConnIDLen: srcConnID.Len(),
+ perspective: protocol.PerspectiveClient,
+ handshakeCompleteChan: make(chan struct{}),
+ logID: destConnID.String(),
+ logger: logger,
+ tracer: tracer,
+ versionNegotiated: hasNegotiatedVersion,
+ version: v,
+ }
+ s.connIDManager = newConnIDManager(
+ destConnID,
+ func(token protocol.StatelessResetToken) { runner.AddResetToken(token, s) },
+ runner.RemoveResetToken,
+ s.queueControlFrame,
+ )
+ s.connIDGenerator = newConnIDGenerator(
+ srcConnID,
+ nil,
+ func(connID protocol.ConnectionID) { runner.Add(connID, s) },
+ runner.GetStatelessResetToken,
+ runner.Remove,
+ runner.Retire,
+ runner.ReplaceWithClosed,
+ s.queueControlFrame,
+ s.config.ConnectionIDGenerator,
+ )
+ s.preSetup()
+ s.ctx, s.ctxCancel = context.WithCancel(context.WithValue(context.Background(), ConnectionTracingKey, tracingID))
+ s.sentPacketHandler, s.receivedPacketHandler = ackhandler.NewAckHandler(
+ initialPacketNumber,
+ getMaxPacketSize(s.conn.RemoteAddr()),
+ s.rttStats,
+ false, /* has no effect */
+ s.perspective,
+ s.tracer,
+ s.logger,
+ )
+ initialStream := newCryptoStream()
+ handshakeStream := newCryptoStream()
+ params := &wire.TransportParameters{
+ InitialMaxStreamDataBidiRemote: protocol.ByteCount(s.config.InitialStreamReceiveWindow),
+ InitialMaxStreamDataBidiLocal: protocol.ByteCount(s.config.InitialStreamReceiveWindow),
+ InitialMaxStreamDataUni: protocol.ByteCount(s.config.InitialStreamReceiveWindow),
+ InitialMaxData: protocol.ByteCount(s.config.InitialConnectionReceiveWindow),
+ MaxIdleTimeout: s.config.MaxIdleTimeout,
+ MaxBidiStreamNum: protocol.StreamNum(s.config.MaxIncomingStreams),
+ MaxUniStreamNum: protocol.StreamNum(s.config.MaxIncomingUniStreams),
+ MaxAckDelay: protocol.MaxAckDelayInclGranularity,
+ AckDelayExponent: protocol.AckDelayExponent,
+ DisableActiveMigration: true,
+ ActiveConnectionIDLimit: protocol.MaxActiveConnectionIDs,
+ InitialSourceConnectionID: srcConnID,
+ }
+ if s.config.EnableDatagrams {
+ params.MaxDatagramFrameSize = protocol.MaxDatagramFrameSize
+ } else {
+ params.MaxDatagramFrameSize = protocol.InvalidByteCount
+ }
+ if s.tracer != nil {
+ s.tracer.SentTransportParameters(params)
+ }
+ cs, clientHelloWritten := handshake.NewCryptoSetupClient(
+ initialStream,
+ handshakeStream,
+ destConnID,
+ conn.LocalAddr(),
+ conn.RemoteAddr(),
+ params,
+ &handshakeRunner{
+ onReceivedParams: s.handleTransportParameters,
+ onError: s.closeLocal,
+ dropKeys: s.dropEncryptionLevel,
+ onHandshakeComplete: func() { close(s.handshakeCompleteChan) },
+ },
+ tlsConf,
+ enable0RTT,
+ s.rttStats,
+ tracer,
+ logger,
+ s.version,
+ )
+ s.clientHelloWritten = clientHelloWritten
+ s.cryptoStreamHandler = cs
+ s.cryptoStreamManager = newCryptoStreamManager(cs, initialStream, handshakeStream, newCryptoStream())
+ s.unpacker = newPacketUnpacker(cs, s.srcConnIDLen)
+ s.packer = newPacketPacker(srcConnID, s.connIDManager.Get, initialStream, handshakeStream, s.sentPacketHandler, s.retransmissionQueue, s.RemoteAddr(), cs, s.framer, s.receivedPacketHandler, s.datagramQueue, s.perspective)
+ if len(tlsConf.ServerName) > 0 {
+ s.tokenStoreKey = tlsConf.ServerName
+ } else {
+ s.tokenStoreKey = conn.RemoteAddr().String()
+ }
+ if s.config.TokenStore != nil {
+ if token := s.config.TokenStore.Pop(s.tokenStoreKey); token != nil {
+ s.packer.SetToken(token.data)
+ }
+ }
+ return s
+}
+
+func (s *connection) preSetup() {
+ s.sendQueue = newSendQueue(s.conn)
+ s.retransmissionQueue = newRetransmissionQueue()
+ s.frameParser = wire.NewFrameParser(s.config.EnableDatagrams)
+ s.rttStats = &utils.RTTStats{}
+ s.connFlowController = flowcontrol.NewConnectionFlowController(
+ protocol.ByteCount(s.config.InitialConnectionReceiveWindow),
+ protocol.ByteCount(s.config.MaxConnectionReceiveWindow),
+ s.onHasConnectionWindowUpdate,
+ func(size protocol.ByteCount) bool {
+ if s.config.AllowConnectionWindowIncrease == nil {
+ return true
+ }
+ return s.config.AllowConnectionWindowIncrease(s, uint64(size))
+ },
+ s.rttStats,
+ s.logger,
+ )
+ s.earlyConnReadyChan = make(chan struct{})
+ s.streamsMap = newStreamsMap(
+ s,
+ s.newFlowController,
+ uint64(s.config.MaxIncomingStreams),
+ uint64(s.config.MaxIncomingUniStreams),
+ s.perspective,
+ )
+ s.framer = newFramer(s.streamsMap)
+ s.receivedPackets = make(chan *receivedPacket, protocol.MaxConnUnprocessedPackets)
+ s.closeChan = make(chan closeError, 1)
+ s.sendingScheduled = make(chan struct{}, 1)
+ s.handshakeCtx, s.handshakeCtxCancel = context.WithCancel(context.Background())
+
+ now := time.Now()
+ s.lastPacketReceivedTime = now
+ s.creationTime = now
+
+ s.windowUpdateQueue = newWindowUpdateQueue(s.streamsMap, s.connFlowController, s.framer.QueueControlFrame)
+ s.datagramQueue = newDatagramQueue(s.scheduleSending, s.logger)
+ s.connState.Version = s.version
+}
+
+// run the connection main loop
+func (s *connection) run() error {
+ defer s.ctxCancel()
+
+ s.timer = *newTimer()
+
+ handshaking := make(chan struct{})
+ go func() {
+ defer close(handshaking)
+ s.cryptoStreamHandler.RunHandshake()
+ }()
+ go func() {
+ if err := s.sendQueue.Run(); err != nil {
+ s.destroyImpl(err)
+ }
+ }()
+
+ if s.perspective == protocol.PerspectiveClient {
+ select {
+ case zeroRTTParams := <-s.clientHelloWritten:
+ s.scheduleSending()
+ if zeroRTTParams != nil {
+ s.restoreTransportParameters(zeroRTTParams)
+ close(s.earlyConnReadyChan)
+ }
+ case closeErr := <-s.closeChan:
+ // put the close error back into the channel, so that the run loop can receive it
+ s.closeChan <- closeErr
+ }
+ }
+
+ var (
+ closeErr closeError
+ sendQueueAvailable <-chan struct{}
+ )
+
+runLoop:
+ for {
+ // Close immediately if requested
+ select {
+ case closeErr = <-s.closeChan:
+ break runLoop
+ case <-s.handshakeCompleteChan:
+ s.handleHandshakeComplete()
+ default:
+ }
+
+ s.maybeResetTimer()
+
+ var processedUndecryptablePacket bool
+ if len(s.undecryptablePacketsToProcess) > 0 {
+ queue := s.undecryptablePacketsToProcess
+ s.undecryptablePacketsToProcess = nil
+ for _, p := range queue {
+ if processed := s.handlePacketImpl(p); processed {
+ processedUndecryptablePacket = true
+ }
+ // Don't set timers and send packets if the packet made us close the connection.
+ select {
+ case closeErr = <-s.closeChan:
+ break runLoop
+ default:
+ }
+ }
+ }
+ // If we processed any undecryptable packets, jump to the resetting of the timers directly.
+ if !processedUndecryptablePacket {
+ select {
+ case closeErr = <-s.closeChan:
+ break runLoop
+ case <-s.timer.Chan():
+ s.timer.SetRead()
+ // We do all the interesting stuff after the switch statement, so
+ // nothing to see here.
+ case <-s.sendingScheduled:
+ // We do all the interesting stuff after the switch statement, so
+ // nothing to see here.
+ case <-sendQueueAvailable:
+ case firstPacket := <-s.receivedPackets:
+ wasProcessed := s.handlePacketImpl(firstPacket)
+ // Don't set timers and send packets if the packet made us close the connection.
+ select {
+ case closeErr = <-s.closeChan:
+ break runLoop
+ default:
+ }
+ if s.handshakeComplete {
+ // Now process all packets in the receivedPackets channel.
+ // Limit the number of packets to the length of the receivedPackets channel,
+ // so we eventually get a chance to send out an ACK when receiving a lot of packets.
+ numPackets := len(s.receivedPackets)
+ receiveLoop:
+ for i := 0; i < numPackets; i++ {
+ select {
+ case p := <-s.receivedPackets:
+ if processed := s.handlePacketImpl(p); processed {
+ wasProcessed = true
+ }
+ select {
+ case closeErr = <-s.closeChan:
+ break runLoop
+ default:
+ }
+ default:
+ break receiveLoop
+ }
+ }
+ }
+ // Only reset the timers if this packet was actually processed.
+ // This avoids modifying any state when handling undecryptable packets,
+ // which could be injected by an attacker.
+ if !wasProcessed {
+ continue
+ }
+ case <-s.handshakeCompleteChan:
+ s.handleHandshakeComplete()
+ }
+ }
+
+ now := time.Now()
+ if timeout := s.sentPacketHandler.GetLossDetectionTimeout(); !timeout.IsZero() && timeout.Before(now) {
+ // This could cause packets to be retransmitted.
+ // Check it before trying to send packets.
+ if err := s.sentPacketHandler.OnLossDetectionTimeout(); err != nil {
+ s.closeLocal(err)
+ }
+ }
+
+ if keepAliveTime := s.nextKeepAliveTime(); !keepAliveTime.IsZero() && !now.Before(keepAliveTime) {
+ // send a PING frame since there is no activity in the connection
+ s.logger.Debugf("Sending a keep-alive PING to keep the connection alive.")
+ s.framer.QueueControlFrame(&wire.PingFrame{})
+ s.keepAlivePingSent = true
+ } else if !s.handshakeComplete && now.Sub(s.creationTime) >= s.config.handshakeTimeout() {
+ s.destroyImpl(qerr.ErrHandshakeTimeout)
+ continue
+ } else {
+ idleTimeoutStartTime := s.idleTimeoutStartTime()
+ if (!s.handshakeComplete && now.Sub(idleTimeoutStartTime) >= s.config.HandshakeIdleTimeout) ||
+ (s.handshakeComplete && now.Sub(idleTimeoutStartTime) >= s.idleTimeout) {
+ s.destroyImpl(qerr.ErrIdleTimeout)
+ continue
+ }
+ }
+
+ if s.sendQueue.WouldBlock() {
+ // The send queue is still busy sending out packets.
+ // Wait until there's space to enqueue new packets.
+ sendQueueAvailable = s.sendQueue.Available()
+ continue
+ }
+ if err := s.sendPackets(); err != nil {
+ s.closeLocal(err)
+ }
+ if s.sendQueue.WouldBlock() {
+ sendQueueAvailable = s.sendQueue.Available()
+ } else {
+ sendQueueAvailable = nil
+ }
+ }
+
+ s.cryptoStreamHandler.Close()
+ <-handshaking
+ s.handleCloseError(&closeErr)
+ if e := (&errCloseForRecreating{}); !errors.As(closeErr.err, &e) && s.tracer != nil {
+ s.tracer.Close()
+ }
+ s.logger.Infof("Connection %s closed.", s.logID)
+ s.sendQueue.Close()
+ s.timer.Stop()
+ return closeErr.err
+}
+
+// blocks until the early connection can be used
+func (s *connection) earlyConnReady() <-chan struct{} {
+ return s.earlyConnReadyChan
+}
+
+func (s *connection) HandshakeComplete() context.Context {
+ return s.handshakeCtx
+}
+
+func (s *connection) Context() context.Context {
+ return s.ctx
+}
+
+func (s *connection) supportsDatagrams() bool {
+ return s.peerParams.MaxDatagramFrameSize > 0
+}
+
+func (s *connection) ConnectionState() ConnectionState {
+ s.connStateMutex.Lock()
+ defer s.connStateMutex.Unlock()
+ s.connState.TLS = s.cryptoStreamHandler.ConnectionState()
+ return s.connState
+}
+
+// Time when the next keep-alive packet should be sent.
+// It returns a zero time if no keep-alive should be sent.
+func (s *connection) nextKeepAliveTime() time.Time {
+ if s.config.KeepAlivePeriod == 0 || s.keepAlivePingSent || !s.firstAckElicitingPacketAfterIdleSentTime.IsZero() {
+ return time.Time{}
+ }
+ return s.lastPacketReceivedTime.Add(s.keepAliveInterval)
+}
+
+func (s *connection) maybeResetTimer() {
+ var deadline time.Time
+ if !s.handshakeComplete {
+ deadline = utils.MinTime(
+ s.creationTime.Add(s.config.handshakeTimeout()),
+ s.idleTimeoutStartTime().Add(s.config.HandshakeIdleTimeout),
+ )
+ } else {
+ if keepAliveTime := s.nextKeepAliveTime(); !keepAliveTime.IsZero() {
+ deadline = keepAliveTime
+ } else {
+ deadline = s.idleTimeoutStartTime().Add(s.idleTimeout)
+ }
+ }
+
+ s.timer.SetTimer(
+ deadline,
+ s.receivedPacketHandler.GetAlarmTimeout(),
+ s.sentPacketHandler.GetLossDetectionTimeout(),
+ s.pacingDeadline,
+ )
+}
+
+func (s *connection) idleTimeoutStartTime() time.Time {
+ return utils.MaxTime(s.lastPacketReceivedTime, s.firstAckElicitingPacketAfterIdleSentTime)
+}
+
+func (s *connection) handleHandshakeComplete() {
+ s.handshakeComplete = true
+ s.handshakeCompleteChan = nil // prevent this case from ever being selected again
+ defer s.handshakeCtxCancel()
+ // Once the handshake completes, we have derived 1-RTT keys.
+ // There's no point in queueing undecryptable packets for later decryption any more.
+ s.undecryptablePackets = nil
+
+ s.connIDManager.SetHandshakeComplete()
+ s.connIDGenerator.SetHandshakeComplete()
+
+ if s.perspective == protocol.PerspectiveClient {
+ s.applyTransportParameters()
+ return
+ }
+
+ s.handleHandshakeConfirmed()
+
+ ticket, err := s.cryptoStreamHandler.GetSessionTicket()
+ if err != nil {
+ s.closeLocal(err)
+ }
+ if ticket != nil {
+ s.oneRTTStream.Write(ticket)
+ for s.oneRTTStream.HasData() {
+ s.queueControlFrame(s.oneRTTStream.PopCryptoFrame(protocol.MaxPostHandshakeCryptoFrameSize))
+ }
+ }
+ token, err := s.tokenGenerator.NewToken(s.conn.RemoteAddr())
+ if err != nil {
+ s.closeLocal(err)
+ }
+ s.queueControlFrame(&wire.NewTokenFrame{Token: token})
+ s.queueControlFrame(&wire.HandshakeDoneFrame{})
+}
+
+func (s *connection) handleHandshakeConfirmed() {
+ s.handshakeConfirmed = true
+ s.sentPacketHandler.SetHandshakeConfirmed()
+ s.cryptoStreamHandler.SetHandshakeConfirmed()
+
+ if !s.config.DisablePathMTUDiscovery {
+ maxPacketSize := s.peerParams.MaxUDPPayloadSize
+ if maxPacketSize == 0 {
+ maxPacketSize = protocol.MaxByteCount
+ }
+ maxPacketSize = utils.Min(maxPacketSize, protocol.MaxPacketBufferSize)
+ s.mtuDiscoverer = newMTUDiscoverer(
+ s.rttStats,
+ getMaxPacketSize(s.conn.RemoteAddr()),
+ maxPacketSize,
+ func(size protocol.ByteCount) {
+ s.sentPacketHandler.SetMaxDatagramSize(size)
+ s.packer.SetMaxPacketSize(size)
+ },
+ )
+ }
+}
+
+func (s *connection) handlePacketImpl(rp *receivedPacket) bool {
+ s.sentPacketHandler.ReceivedBytes(rp.Size())
+
+ if wire.IsVersionNegotiationPacket(rp.data) {
+ s.handleVersionNegotiationPacket(rp)
+ return false
+ }
+
+ var counter uint8
+ var lastConnID protocol.ConnectionID
+ var processed bool
+ data := rp.data
+ p := rp
+ for len(data) > 0 {
+ var destConnID protocol.ConnectionID
+ if counter > 0 {
+ p = p.Clone()
+ p.data = data
+
+ var err error
+ destConnID, err = wire.ParseConnectionID(p.data, s.srcConnIDLen)
+ if err != nil {
+ if s.tracer != nil {
+ s.tracer.DroppedPacket(logging.PacketTypeNotDetermined, protocol.ByteCount(len(data)), logging.PacketDropHeaderParseError)
+ }
+ s.logger.Debugf("error parsing packet, couldn't parse connection ID: %s", err)
+ break
+ }
+ if destConnID != lastConnID {
+ if s.tracer != nil {
+ s.tracer.DroppedPacket(logging.PacketTypeNotDetermined, protocol.ByteCount(len(data)), logging.PacketDropUnknownConnectionID)
+ }
+ s.logger.Debugf("coalesced packet has different destination connection ID: %s, expected %s", destConnID, lastConnID)
+ break
+ }
+ }
+
+ if wire.IsLongHeaderPacket(p.data[0]) {
+ hdr, packetData, rest, err := wire.ParsePacket(p.data)
+ if err != nil {
+ if s.tracer != nil {
+ dropReason := logging.PacketDropHeaderParseError
+ if err == wire.ErrUnsupportedVersion {
+ dropReason = logging.PacketDropUnsupportedVersion
+ }
+ s.tracer.DroppedPacket(logging.PacketTypeNotDetermined, protocol.ByteCount(len(data)), dropReason)
+ }
+ s.logger.Debugf("error parsing packet: %s", err)
+ break
+ }
+ lastConnID = hdr.DestConnectionID
+
+ if hdr.Version != s.version {
+ if s.tracer != nil {
+ s.tracer.DroppedPacket(logging.PacketTypeFromHeader(hdr), protocol.ByteCount(len(data)), logging.PacketDropUnexpectedVersion)
+ }
+ s.logger.Debugf("Dropping packet with version %x. Expected %x.", hdr.Version, s.version)
+ break
+ }
+
+ if counter > 0 {
+ p.buffer.Split()
+ }
+ counter++
+
+ // only log if this actually a coalesced packet
+ if s.logger.Debug() && (counter > 1 || len(rest) > 0) {
+ s.logger.Debugf("Parsed a coalesced packet. Part %d: %d bytes. Remaining: %d bytes.", counter, len(packetData), len(rest))
+ }
+
+ p.data = packetData
+
+ if wasProcessed := s.handleLongHeaderPacket(p, hdr); wasProcessed {
+ processed = true
+ }
+ data = rest
+ } else {
+ if counter > 0 {
+ p.buffer.Split()
+ }
+ processed = s.handleShortHeaderPacket(p, destConnID)
+ break
+ }
+ }
+
+ p.buffer.MaybeRelease()
+ return processed
+}
+
+func (s *connection) handleShortHeaderPacket(p *receivedPacket, destConnID protocol.ConnectionID) bool {
+ var wasQueued bool
+
+ defer func() {
+ // Put back the packet buffer if the packet wasn't queued for later decryption.
+ if !wasQueued {
+ p.buffer.Decrement()
+ }
+ }()
+
+ pn, pnLen, keyPhase, data, err := s.unpacker.UnpackShortHeader(p.rcvTime, p.data)
+ if err != nil {
+ wasQueued = s.handleUnpackError(err, p, logging.PacketType1RTT)
+ return false
+ }
+
+ if s.logger.Debug() {
+ s.logger.Debugf("<- Reading packet %d (%d bytes) for connection %s, 1-RTT", pn, p.Size(), destConnID)
+ wire.LogShortHeader(s.logger, destConnID, pn, pnLen, keyPhase)
+ }
+
+ if s.receivedPacketHandler.IsPotentiallyDuplicate(pn, protocol.Encryption1RTT) {
+ s.logger.Debugf("Dropping (potentially) duplicate packet.")
+ if s.tracer != nil {
+ s.tracer.DroppedPacket(logging.PacketType1RTT, p.Size(), logging.PacketDropDuplicate)
+ }
+ return false
+ }
+
+ var log func([]logging.Frame)
+ if s.tracer != nil {
+ log = func(frames []logging.Frame) {
+ s.tracer.ReceivedShortHeaderPacket(
+ &logging.ShortHeader{
+ DestConnectionID: destConnID,
+ PacketNumber: pn,
+ PacketNumberLen: pnLen,
+ KeyPhase: keyPhase,
+ },
+ p.Size(),
+ frames,
+ )
+ }
+ }
+ if err := s.handleUnpackedShortHeaderPacket(destConnID, pn, data, p.ecn, p.rcvTime, log); err != nil {
+ s.closeLocal(err)
+ return false
+ }
+ return true
+}
+
+func (s *connection) handleLongHeaderPacket(p *receivedPacket, hdr *wire.Header) bool /* was the packet successfully processed */ {
+ var wasQueued bool
+
+ defer func() {
+ // Put back the packet buffer if the packet wasn't queued for later decryption.
+ if !wasQueued {
+ p.buffer.Decrement()
+ }
+ }()
+
+ if hdr.Type == protocol.PacketTypeRetry {
+ return s.handleRetryPacket(hdr, p.data)
+ }
+
+ // The server can change the source connection ID with the first Handshake packet.
+ // After this, all packets with a different source connection have to be ignored.
+ if s.receivedFirstPacket && hdr.Type == protocol.PacketTypeInitial && hdr.SrcConnectionID != s.handshakeDestConnID {
+ if s.tracer != nil {
+ s.tracer.DroppedPacket(logging.PacketTypeInitial, p.Size(), logging.PacketDropUnknownConnectionID)
+ }
+ s.logger.Debugf("Dropping Initial packet (%d bytes) with unexpected source connection ID: %s (expected %s)", p.Size(), hdr.SrcConnectionID, s.handshakeDestConnID)
+ return false
+ }
+ // drop 0-RTT packets, if we are a client
+ if s.perspective == protocol.PerspectiveClient && hdr.Type == protocol.PacketType0RTT {
+ if s.tracer != nil {
+ s.tracer.DroppedPacket(logging.PacketType0RTT, p.Size(), logging.PacketDropKeyUnavailable)
+ }
+ return false
+ }
+
+ packet, err := s.unpacker.UnpackLongHeader(hdr, p.rcvTime, p.data, s.version)
+ if err != nil {
+ wasQueued = s.handleUnpackError(err, p, logging.PacketTypeFromHeader(hdr))
+ return false
+ }
+
+ if s.logger.Debug() {
+ s.logger.Debugf("<- Reading packet %d (%d bytes) for connection %s, %s", packet.hdr.PacketNumber, p.Size(), hdr.DestConnectionID, packet.encryptionLevel)
+ packet.hdr.Log(s.logger)
+ }
+
+ if s.receivedPacketHandler.IsPotentiallyDuplicate(packet.hdr.PacketNumber, packet.encryptionLevel) {
+ s.logger.Debugf("Dropping (potentially) duplicate packet.")
+ if s.tracer != nil {
+ s.tracer.DroppedPacket(logging.PacketTypeFromHeader(hdr), p.Size(), logging.PacketDropDuplicate)
+ }
+ return false
+ }
+
+ if err := s.handleUnpackedLongHeaderPacket(packet, p.ecn, p.rcvTime, p.Size()); err != nil {
+ s.closeLocal(err)
+ return false
+ }
+ return true
+}
+
+func (s *connection) handleUnpackError(err error, p *receivedPacket, pt logging.PacketType) (wasQueued bool) {
+ switch err {
+ case handshake.ErrKeysDropped:
+ if s.tracer != nil {
+ s.tracer.DroppedPacket(pt, p.Size(), logging.PacketDropKeyUnavailable)
+ }
+ s.logger.Debugf("Dropping %s packet (%d bytes) because we already dropped the keys.", pt, p.Size())
+ case handshake.ErrKeysNotYetAvailable:
+ // Sealer for this encryption level not yet available.
+ // Try again later.
+ s.tryQueueingUndecryptablePacket(p, pt)
+ return true
+ case wire.ErrInvalidReservedBits:
+ s.closeLocal(&qerr.TransportError{
+ ErrorCode: qerr.ProtocolViolation,
+ ErrorMessage: err.Error(),
+ })
+ case handshake.ErrDecryptionFailed:
+ // This might be a packet injected by an attacker. Drop it.
+ if s.tracer != nil {
+ s.tracer.DroppedPacket(pt, p.Size(), logging.PacketDropPayloadDecryptError)
+ }
+ s.logger.Debugf("Dropping %s packet (%d bytes) that could not be unpacked. Error: %s", pt, p.Size(), err)
+ default:
+ var headerErr *headerParseError
+ if errors.As(err, &headerErr) {
+ // This might be a packet injected by an attacker. Drop it.
+ if s.tracer != nil {
+ s.tracer.DroppedPacket(pt, p.Size(), logging.PacketDropHeaderParseError)
+ }
+ s.logger.Debugf("Dropping %s packet (%d bytes) for which we couldn't unpack the header. Error: %s", pt, p.Size(), err)
+ } else {
+ // This is an error returned by the AEAD (other than ErrDecryptionFailed).
+ // For example, a PROTOCOL_VIOLATION due to key updates.
+ s.closeLocal(err)
+ }
+ }
+ return false
+}
+
+func (s *connection) handleRetryPacket(hdr *wire.Header, data []byte) bool /* was this a valid Retry */ {
+ if s.perspective == protocol.PerspectiveServer {
+ if s.tracer != nil {
+ s.tracer.DroppedPacket(logging.PacketTypeRetry, protocol.ByteCount(len(data)), logging.PacketDropUnexpectedPacket)
+ }
+ s.logger.Debugf("Ignoring Retry.")
+ return false
+ }
+ if s.receivedFirstPacket {
+ if s.tracer != nil {
+ s.tracer.DroppedPacket(logging.PacketTypeRetry, protocol.ByteCount(len(data)), logging.PacketDropUnexpectedPacket)
+ }
+ s.logger.Debugf("Ignoring Retry, since we already received a packet.")
+ return false
+ }
+ destConnID := s.connIDManager.Get()
+ if hdr.SrcConnectionID == destConnID {
+ if s.tracer != nil {
+ s.tracer.DroppedPacket(logging.PacketTypeRetry, protocol.ByteCount(len(data)), logging.PacketDropUnexpectedPacket)
+ }
+ s.logger.Debugf("Ignoring Retry, since the server didn't change the Source Connection ID.")
+ return false
+ }
+ // If a token is already set, this means that we already received a Retry from the server.
+ // Ignore this Retry packet.
+ if s.receivedRetry {
+ s.logger.Debugf("Ignoring Retry, since a Retry was already received.")
+ return false
+ }
+
+ tag := handshake.GetRetryIntegrityTag(data[:len(data)-16], destConnID, hdr.Version)
+ if !bytes.Equal(data[len(data)-16:], tag[:]) {
+ if s.tracer != nil {
+ s.tracer.DroppedPacket(logging.PacketTypeRetry, protocol.ByteCount(len(data)), logging.PacketDropPayloadDecryptError)
+ }
+ s.logger.Debugf("Ignoring spoofed Retry. Integrity Tag doesn't match.")
+ return false
+ }
+
+ if s.logger.Debug() {
+ s.logger.Debugf("<- Received Retry:")
+ (&wire.ExtendedHeader{Header: *hdr}).Log(s.logger)
+ s.logger.Debugf("Switching destination connection ID to: %s", hdr.SrcConnectionID)
+ }
+ if s.tracer != nil {
+ s.tracer.ReceivedRetry(hdr)
+ }
+ newDestConnID := hdr.SrcConnectionID
+ s.receivedRetry = true
+ if err := s.sentPacketHandler.ResetForRetry(); err != nil {
+ s.closeLocal(err)
+ return false
+ }
+ s.handshakeDestConnID = newDestConnID
+ s.retrySrcConnID = &newDestConnID
+ s.cryptoStreamHandler.ChangeConnectionID(newDestConnID)
+ s.packer.SetToken(hdr.Token)
+ s.connIDManager.ChangeInitialConnID(newDestConnID)
+ s.scheduleSending()
+ return true
+}
+
+func (s *connection) handleVersionNegotiationPacket(p *receivedPacket) {
+ if s.perspective == protocol.PerspectiveServer || // servers never receive version negotiation packets
+ s.receivedFirstPacket || s.versionNegotiated { // ignore delayed / duplicated version negotiation packets
+ if s.tracer != nil {
+ s.tracer.DroppedPacket(logging.PacketTypeVersionNegotiation, p.Size(), logging.PacketDropUnexpectedPacket)
+ }
+ return
+ }
+
+ src, dest, supportedVersions, err := wire.ParseVersionNegotiationPacket(p.data)
+ if err != nil {
+ if s.tracer != nil {
+ s.tracer.DroppedPacket(logging.PacketTypeVersionNegotiation, p.Size(), logging.PacketDropHeaderParseError)
+ }
+ s.logger.Debugf("Error parsing Version Negotiation packet: %s", err)
+ return
+ }
+
+ for _, v := range supportedVersions {
+ if v == s.version {
+ if s.tracer != nil {
+ s.tracer.DroppedPacket(logging.PacketTypeVersionNegotiation, p.Size(), logging.PacketDropUnexpectedVersion)
+ }
+ // The Version Negotiation packet contains the version that we offered.
+ // This might be a packet sent by an attacker, or it was corrupted.
+ return
+ }
+ }
+
+ s.logger.Infof("Received a Version Negotiation packet. Supported Versions: %s", supportedVersions)
+ if s.tracer != nil {
+ s.tracer.ReceivedVersionNegotiationPacket(dest, src, supportedVersions)
+ }
+ newVersion, ok := protocol.ChooseSupportedVersion(s.config.Versions, supportedVersions)
+ if !ok {
+ s.destroyImpl(&VersionNegotiationError{
+ Ours: s.config.Versions,
+ Theirs: supportedVersions,
+ })
+ s.logger.Infof("No compatible QUIC version found.")
+ return
+ }
+ if s.tracer != nil {
+ s.tracer.NegotiatedVersion(newVersion, s.config.Versions, supportedVersions)
+ }
+
+ s.logger.Infof("Switching to QUIC version %s.", newVersion)
+ nextPN, _ := s.sentPacketHandler.PeekPacketNumber(protocol.EncryptionInitial)
+ s.destroyImpl(&errCloseForRecreating{
+ nextPacketNumber: nextPN,
+ nextVersion: newVersion,
+ })
+}
+
+func (s *connection) handleUnpackedLongHeaderPacket(
+ packet *unpackedPacket,
+ ecn protocol.ECN,
+ rcvTime time.Time,
+ packetSize protocol.ByteCount, // only for logging
+) error {
+ if !s.receivedFirstPacket {
+ s.receivedFirstPacket = true
+ if !s.versionNegotiated && s.tracer != nil {
+ var clientVersions, serverVersions []protocol.VersionNumber
+ switch s.perspective {
+ case protocol.PerspectiveClient:
+ clientVersions = s.config.Versions
+ case protocol.PerspectiveServer:
+ serverVersions = s.config.Versions
+ }
+ s.tracer.NegotiatedVersion(s.version, clientVersions, serverVersions)
+ }
+ // The server can change the source connection ID with the first Handshake packet.
+ if s.perspective == protocol.PerspectiveClient && packet.hdr.SrcConnectionID != s.handshakeDestConnID {
+ cid := packet.hdr.SrcConnectionID
+ s.logger.Debugf("Received first packet. Switching destination connection ID to: %s", cid)
+ s.handshakeDestConnID = cid
+ s.connIDManager.ChangeInitialConnID(cid)
+ }
+ // We create the connection as soon as we receive the first packet from the client.
+ // We do that before authenticating the packet.
+ // That means that if the source connection ID was corrupted,
+ // we might have created a connection with an incorrect source connection ID.
+ // Once we authenticate the first packet, we need to update it.
+ if s.perspective == protocol.PerspectiveServer {
+ if packet.hdr.SrcConnectionID != s.handshakeDestConnID {
+ s.handshakeDestConnID = packet.hdr.SrcConnectionID
+ s.connIDManager.ChangeInitialConnID(packet.hdr.SrcConnectionID)
+ }
+ if s.tracer != nil {
+ s.tracer.StartedConnection(
+ s.conn.LocalAddr(),
+ s.conn.RemoteAddr(),
+ packet.hdr.SrcConnectionID,
+ packet.hdr.DestConnectionID,
+ )
+ }
+ }
+ }
+
+ s.lastPacketReceivedTime = rcvTime
+ s.firstAckElicitingPacketAfterIdleSentTime = time.Time{}
+ s.keepAlivePingSent = false
+
+ var log func([]logging.Frame)
+ if s.tracer != nil {
+ log = func(frames []logging.Frame) {
+ s.tracer.ReceivedLongHeaderPacket(packet.hdr, packetSize, frames)
+ }
+ }
+ isAckEliciting, err := s.handleFrames(packet.data, packet.hdr.DestConnectionID, packet.encryptionLevel, log)
+ if err != nil {
+ return err
+ }
+ return s.receivedPacketHandler.ReceivedPacket(packet.hdr.PacketNumber, ecn, packet.encryptionLevel, rcvTime, isAckEliciting)
+}
+
+func (s *connection) handleUnpackedShortHeaderPacket(
+ destConnID protocol.ConnectionID,
+ pn protocol.PacketNumber,
+ data []byte,
+ ecn protocol.ECN,
+ rcvTime time.Time,
+ log func([]logging.Frame),
+) error {
+ s.lastPacketReceivedTime = rcvTime
+ s.firstAckElicitingPacketAfterIdleSentTime = time.Time{}
+ s.keepAlivePingSent = false
+
+ isAckEliciting, err := s.handleFrames(data, destConnID, protocol.Encryption1RTT, log)
+ if err != nil {
+ return err
+ }
+ return s.receivedPacketHandler.ReceivedPacket(pn, ecn, protocol.Encryption1RTT, rcvTime, isAckEliciting)
+}
+
+func (s *connection) handleFrames(
+ data []byte,
+ destConnID protocol.ConnectionID,
+ encLevel protocol.EncryptionLevel,
+ log func([]logging.Frame),
+) (isAckEliciting bool, _ error) {
+ // Only used for tracing.
+ // If we're not tracing, this slice will always remain empty.
+ var frames []wire.Frame
+ for len(data) > 0 {
+ l, frame, err := s.frameParser.ParseNext(data, encLevel, s.version)
+ if err != nil {
+ return false, err
+ }
+ data = data[l:]
+ if frame == nil {
+ break
+ }
+ if ackhandler.IsFrameAckEliciting(frame) {
+ isAckEliciting = true
+ }
+ // Only process frames now if we're not logging.
+ // If we're logging, we need to make sure that the packet_received event is logged first.
+ if log == nil {
+ if err := s.handleFrame(frame, encLevel, destConnID); err != nil {
+ return false, err
+ }
+ } else {
+ frames = append(frames, frame)
+ }
+ }
+
+ if log != nil {
+ fs := make([]logging.Frame, len(frames))
+ for i, frame := range frames {
+ fs[i] = logutils.ConvertFrame(frame)
+ }
+ log(fs)
+ for _, frame := range frames {
+ if err := s.handleFrame(frame, encLevel, destConnID); err != nil {
+ return false, err
+ }
+ }
+ }
+ return
+}
+
+func (s *connection) handleFrame(f wire.Frame, encLevel protocol.EncryptionLevel, destConnID protocol.ConnectionID) error {
+ var err error
+ wire.LogFrame(s.logger, f, false)
+ switch frame := f.(type) {
+ case *wire.CryptoFrame:
+ err = s.handleCryptoFrame(frame, encLevel)
+ case *wire.StreamFrame:
+ err = s.handleStreamFrame(frame)
+ case *wire.AckFrame:
+ err = s.handleAckFrame(frame, encLevel)
+ wire.PutAckFrame(frame)
+ case *wire.ConnectionCloseFrame:
+ s.handleConnectionCloseFrame(frame)
+ case *wire.ResetStreamFrame:
+ err = s.handleResetStreamFrame(frame)
+ case *wire.MaxDataFrame:
+ s.handleMaxDataFrame(frame)
+ case *wire.MaxStreamDataFrame:
+ err = s.handleMaxStreamDataFrame(frame)
+ case *wire.MaxStreamsFrame:
+ s.handleMaxStreamsFrame(frame)
+ case *wire.DataBlockedFrame:
+ case *wire.StreamDataBlockedFrame:
+ case *wire.StreamsBlockedFrame:
+ case *wire.StopSendingFrame:
+ err = s.handleStopSendingFrame(frame)
+ case *wire.PingFrame:
+ case *wire.PathChallengeFrame:
+ s.handlePathChallengeFrame(frame)
+ case *wire.PathResponseFrame:
+ // since we don't send PATH_CHALLENGEs, we don't expect PATH_RESPONSEs
+ err = errors.New("unexpected PATH_RESPONSE frame")
+ case *wire.NewTokenFrame:
+ err = s.handleNewTokenFrame(frame)
+ case *wire.NewConnectionIDFrame:
+ err = s.handleNewConnectionIDFrame(frame)
+ case *wire.RetireConnectionIDFrame:
+ err = s.handleRetireConnectionIDFrame(frame, destConnID)
+ case *wire.HandshakeDoneFrame:
+ err = s.handleHandshakeDoneFrame()
+ case *wire.DatagramFrame:
+ err = s.handleDatagramFrame(frame)
+ default:
+ err = fmt.Errorf("unexpected frame type: %s", reflect.ValueOf(&frame).Elem().Type().Name())
+ }
+ return err
+}
+
+// handlePacket is called by the server with a new packet
+func (s *connection) handlePacket(p *receivedPacket) {
+ // Discard packets once the amount of queued packets is larger than
+ // the channel size, protocol.MaxConnUnprocessedPackets
+ select {
+ case s.receivedPackets <- p:
+ default:
+ if s.tracer != nil {
+ s.tracer.DroppedPacket(logging.PacketTypeNotDetermined, p.Size(), logging.PacketDropDOSPrevention)
+ }
+ }
+}
+
+func (s *connection) handleConnectionCloseFrame(frame *wire.ConnectionCloseFrame) {
+ if frame.IsApplicationError {
+ s.closeRemote(&qerr.ApplicationError{
+ Remote: true,
+ ErrorCode: qerr.ApplicationErrorCode(frame.ErrorCode),
+ ErrorMessage: frame.ReasonPhrase,
+ })
+ return
+ }
+ s.closeRemote(&qerr.TransportError{
+ Remote: true,
+ ErrorCode: qerr.TransportErrorCode(frame.ErrorCode),
+ FrameType: frame.FrameType,
+ ErrorMessage: frame.ReasonPhrase,
+ })
+}
+
+func (s *connection) handleCryptoFrame(frame *wire.CryptoFrame, encLevel protocol.EncryptionLevel) error {
+ encLevelChanged, err := s.cryptoStreamManager.HandleCryptoFrame(frame, encLevel)
+ if err != nil {
+ return err
+ }
+ if encLevelChanged {
+ // Queue all packets for decryption that have been undecryptable so far.
+ s.undecryptablePacketsToProcess = s.undecryptablePackets
+ s.undecryptablePackets = nil
+ }
+ return nil
+}
+
+func (s *connection) handleStreamFrame(frame *wire.StreamFrame) error {
+ str, err := s.streamsMap.GetOrOpenReceiveStream(frame.StreamID)
+ if err != nil {
+ return err
+ }
+ if str == nil {
+ // Stream is closed and already garbage collected
+ // ignore this StreamFrame
+ return nil
+ }
+ return str.handleStreamFrame(frame)
+}
+
+func (s *connection) handleMaxDataFrame(frame *wire.MaxDataFrame) {
+ s.connFlowController.UpdateSendWindow(frame.MaximumData)
+}
+
+func (s *connection) handleMaxStreamDataFrame(frame *wire.MaxStreamDataFrame) error {
+ str, err := s.streamsMap.GetOrOpenSendStream(frame.StreamID)
+ if err != nil {
+ return err
+ }
+ if str == nil {
+ // stream is closed and already garbage collected
+ return nil
+ }
+ str.updateSendWindow(frame.MaximumStreamData)
+ return nil
+}
+
+func (s *connection) handleMaxStreamsFrame(frame *wire.MaxStreamsFrame) {
+ s.streamsMap.HandleMaxStreamsFrame(frame)
+}
+
+func (s *connection) handleResetStreamFrame(frame *wire.ResetStreamFrame) error {
+ str, err := s.streamsMap.GetOrOpenReceiveStream(frame.StreamID)
+ if err != nil {
+ return err
+ }
+ if str == nil {
+ // stream is closed and already garbage collected
+ return nil
+ }
+ return str.handleResetStreamFrame(frame)
+}
+
+func (s *connection) handleStopSendingFrame(frame *wire.StopSendingFrame) error {
+ str, err := s.streamsMap.GetOrOpenSendStream(frame.StreamID)
+ if err != nil {
+ return err
+ }
+ if str == nil {
+ // stream is closed and already garbage collected
+ return nil
+ }
+ str.handleStopSendingFrame(frame)
+ return nil
+}
+
+func (s *connection) handlePathChallengeFrame(frame *wire.PathChallengeFrame) {
+ s.queueControlFrame(&wire.PathResponseFrame{Data: frame.Data})
+}
+
+func (s *connection) handleNewTokenFrame(frame *wire.NewTokenFrame) error {
+ if s.perspective == protocol.PerspectiveServer {
+ return &qerr.TransportError{
+ ErrorCode: qerr.ProtocolViolation,
+ ErrorMessage: "received NEW_TOKEN frame from the client",
+ }
+ }
+ if s.config.TokenStore != nil {
+ s.config.TokenStore.Put(s.tokenStoreKey, &ClientToken{data: frame.Token})
+ }
+ return nil
+}
+
+func (s *connection) handleNewConnectionIDFrame(f *wire.NewConnectionIDFrame) error {
+ return s.connIDManager.Add(f)
+}
+
+func (s *connection) handleRetireConnectionIDFrame(f *wire.RetireConnectionIDFrame, destConnID protocol.ConnectionID) error {
+ return s.connIDGenerator.Retire(f.SequenceNumber, destConnID)
+}
+
+func (s *connection) handleHandshakeDoneFrame() error {
+ if s.perspective == protocol.PerspectiveServer {
+ return &qerr.TransportError{
+ ErrorCode: qerr.ProtocolViolation,
+ ErrorMessage: "received a HANDSHAKE_DONE frame",
+ }
+ }
+ if !s.handshakeConfirmed {
+ s.handleHandshakeConfirmed()
+ }
+ return nil
+}
+
+func (s *connection) handleAckFrame(frame *wire.AckFrame, encLevel protocol.EncryptionLevel) error {
+ acked1RTTPacket, err := s.sentPacketHandler.ReceivedAck(frame, encLevel, s.lastPacketReceivedTime)
+ if err != nil {
+ return err
+ }
+ if !acked1RTTPacket {
+ return nil
+ }
+ if s.perspective == protocol.PerspectiveClient && !s.handshakeConfirmed {
+ s.handleHandshakeConfirmed()
+ }
+ return s.cryptoStreamHandler.SetLargest1RTTAcked(frame.LargestAcked())
+}
+
+func (s *connection) handleDatagramFrame(f *wire.DatagramFrame) error {
+ if f.Length(s.version) > protocol.MaxDatagramFrameSize {
+ return &qerr.TransportError{
+ ErrorCode: qerr.ProtocolViolation,
+ ErrorMessage: "DATAGRAM frame too large",
+ }
+ }
+ s.datagramQueue.HandleDatagramFrame(f)
+ return nil
+}
+
+// closeLocal closes the connection and send a CONNECTION_CLOSE containing the error
+func (s *connection) closeLocal(e error) {
+ s.closeOnce.Do(func() {
+ if e == nil {
+ s.logger.Infof("Closing connection.")
+ } else {
+ s.logger.Errorf("Closing connection with error: %s", e)
+ }
+ s.closeChan <- closeError{err: e, immediate: false, remote: false}
+ })
+}
+
+// destroy closes the connection without sending the error on the wire
+func (s *connection) destroy(e error) {
+ s.destroyImpl(e)
+ <-s.ctx.Done()
+}
+
+func (s *connection) destroyImpl(e error) {
+ s.closeOnce.Do(func() {
+ if nerr, ok := e.(net.Error); ok && nerr.Timeout() {
+ s.logger.Errorf("Destroying connection: %s", e)
+ } else {
+ s.logger.Errorf("Destroying connection with error: %s", e)
+ }
+ s.closeChan <- closeError{err: e, immediate: true, remote: false}
+ })
+}
+
+func (s *connection) closeRemote(e error) {
+ s.closeOnce.Do(func() {
+ s.logger.Errorf("Peer closed connection with error: %s", e)
+ s.closeChan <- closeError{err: e, immediate: true, remote: true}
+ })
+}
+
+// Close the connection. It sends a NO_ERROR application error.
+// It waits until the run loop has stopped before returning
+func (s *connection) shutdown() {
+ s.closeLocal(nil)
+ <-s.ctx.Done()
+}
+
+func (s *connection) CloseWithError(code ApplicationErrorCode, desc string) error {
+ s.closeLocal(&qerr.ApplicationError{
+ ErrorCode: code,
+ ErrorMessage: desc,
+ })
+ <-s.ctx.Done()
+ return nil
+}
+
+func (s *connection) handleCloseError(closeErr *closeError) {
+ e := closeErr.err
+ if e == nil {
+ e = &qerr.ApplicationError{}
+ } else {
+ defer func() {
+ closeErr.err = e
+ }()
+ }
+
+ var (
+ statelessResetErr *StatelessResetError
+ versionNegotiationErr *VersionNegotiationError
+ recreateErr *errCloseForRecreating
+ applicationErr *ApplicationError
+ transportErr *TransportError
+ )
+ switch {
+ case errors.Is(e, qerr.ErrIdleTimeout),
+ errors.Is(e, qerr.ErrHandshakeTimeout),
+ errors.As(e, &statelessResetErr),
+ errors.As(e, &versionNegotiationErr),
+ errors.As(e, &recreateErr),
+ errors.As(e, &applicationErr),
+ errors.As(e, &transportErr):
+ default:
+ e = &qerr.TransportError{
+ ErrorCode: qerr.InternalError,
+ ErrorMessage: e.Error(),
+ }
+ }
+
+ s.streamsMap.CloseWithError(e)
+ s.connIDManager.Close()
+ if s.datagramQueue != nil {
+ s.datagramQueue.CloseWithError(e)
+ }
+
+ if s.tracer != nil && !errors.As(e, &recreateErr) {
+ s.tracer.ClosedConnection(e)
+ }
+
+ // If this is a remote close we're done here
+ if closeErr.remote {
+ s.connIDGenerator.ReplaceWithClosed(s.perspective, nil)
+ return
+ }
+ if closeErr.immediate {
+ s.connIDGenerator.RemoveAll()
+ return
+ }
+ // Don't send out any CONNECTION_CLOSE if this is an error that occurred
+ // before we even sent out the first packet.
+ if s.perspective == protocol.PerspectiveClient && !s.sentFirstPacket {
+ s.connIDGenerator.RemoveAll()
+ return
+ }
+ connClosePacket, err := s.sendConnectionClose(e)
+ if err != nil {
+ s.logger.Debugf("Error sending CONNECTION_CLOSE: %s", err)
+ }
+ s.connIDGenerator.ReplaceWithClosed(s.perspective, connClosePacket)
+}
+
+func (s *connection) dropEncryptionLevel(encLevel protocol.EncryptionLevel) {
+ s.sentPacketHandler.DropPackets(encLevel)
+ s.receivedPacketHandler.DropPackets(encLevel)
+ if s.tracer != nil {
+ s.tracer.DroppedEncryptionLevel(encLevel)
+ }
+ if encLevel == protocol.Encryption0RTT {
+ s.streamsMap.ResetFor0RTT()
+ if err := s.connFlowController.Reset(); err != nil {
+ s.closeLocal(err)
+ }
+ if err := s.framer.Handle0RTTRejection(); err != nil {
+ s.closeLocal(err)
+ }
+ }
+}
+
+// is called for the client, when restoring transport parameters saved for 0-RTT
+func (s *connection) restoreTransportParameters(params *wire.TransportParameters) {
+ if s.logger.Debug() {
+ s.logger.Debugf("Restoring Transport Parameters: %s", params)
+ }
+
+ s.peerParams = params
+ s.connIDGenerator.SetMaxActiveConnIDs(params.ActiveConnectionIDLimit)
+ s.connFlowController.UpdateSendWindow(params.InitialMaxData)
+ s.streamsMap.UpdateLimits(params)
+ s.connStateMutex.Lock()
+ s.connState.SupportsDatagrams = s.supportsDatagrams()
+ s.connStateMutex.Unlock()
+}
+
+func (s *connection) handleTransportParameters(params *wire.TransportParameters) {
+ if err := s.checkTransportParameters(params); err != nil {
+ s.closeLocal(&qerr.TransportError{
+ ErrorCode: qerr.TransportParameterError,
+ ErrorMessage: err.Error(),
+ })
+ }
+ s.peerParams = params
+ // On the client side we have to wait for handshake completion.
+ // During a 0-RTT connection, we are only allowed to use the new transport parameters for 1-RTT packets.
+ if s.perspective == protocol.PerspectiveServer {
+ s.applyTransportParameters()
+ // On the server side, the early connection is ready as soon as we processed
+ // the client's transport parameters.
+ close(s.earlyConnReadyChan)
+ }
+
+ s.connStateMutex.Lock()
+ s.connState.SupportsDatagrams = s.supportsDatagrams()
+ s.connStateMutex.Unlock()
+}
+
+func (s *connection) checkTransportParameters(params *wire.TransportParameters) error {
+ if s.logger.Debug() {
+ s.logger.Debugf("Processed Transport Parameters: %s", params)
+ }
+ if s.tracer != nil {
+ s.tracer.ReceivedTransportParameters(params)
+ }
+
+ // check the initial_source_connection_id
+ if params.InitialSourceConnectionID != s.handshakeDestConnID {
+ return fmt.Errorf("expected initial_source_connection_id to equal %s, is %s", s.handshakeDestConnID, params.InitialSourceConnectionID)
+ }
+
+ if s.perspective == protocol.PerspectiveServer {
+ return nil
+ }
+ // check the original_destination_connection_id
+ if params.OriginalDestinationConnectionID != s.origDestConnID {
+ return fmt.Errorf("expected original_destination_connection_id to equal %s, is %s", s.origDestConnID, params.OriginalDestinationConnectionID)
+ }
+ if s.retrySrcConnID != nil { // a Retry was performed
+ if params.RetrySourceConnectionID == nil {
+ return errors.New("missing retry_source_connection_id")
+ }
+ if *params.RetrySourceConnectionID != *s.retrySrcConnID {
+ return fmt.Errorf("expected retry_source_connection_id to equal %s, is %s", s.retrySrcConnID, *params.RetrySourceConnectionID)
+ }
+ } else if params.RetrySourceConnectionID != nil {
+ return errors.New("received retry_source_connection_id, although no Retry was performed")
+ }
+ return nil
+}
+
+func (s *connection) applyTransportParameters() {
+ params := s.peerParams
+ // Our local idle timeout will always be > 0.
+ s.idleTimeout = utils.MinNonZeroDuration(s.config.MaxIdleTimeout, params.MaxIdleTimeout)
+ s.keepAliveInterval = utils.Min(s.config.KeepAlivePeriod, utils.Min(s.idleTimeout/2, protocol.MaxKeepAliveInterval))
+ s.streamsMap.UpdateLimits(params)
+ s.packer.HandleTransportParameters(params)
+ s.frameParser.SetAckDelayExponent(params.AckDelayExponent)
+ s.connFlowController.UpdateSendWindow(params.InitialMaxData)
+ s.rttStats.SetMaxAckDelay(params.MaxAckDelay)
+ s.connIDGenerator.SetMaxActiveConnIDs(params.ActiveConnectionIDLimit)
+ if params.StatelessResetToken != nil {
+ s.connIDManager.SetStatelessResetToken(*params.StatelessResetToken)
+ }
+ // We don't support connection migration yet, so we don't have any use for the preferred_address.
+ if params.PreferredAddress != nil {
+ // Retire the connection ID.
+ s.connIDManager.AddFromPreferredAddress(params.PreferredAddress.ConnectionID, params.PreferredAddress.StatelessResetToken)
+ }
+}
+
+func (s *connection) sendPackets() error {
+ s.pacingDeadline = time.Time{}
+
+ var sentPacket bool // only used in for packets sent in send mode SendAny
+ for {
+ sendMode := s.sentPacketHandler.SendMode()
+ if sendMode == ackhandler.SendAny && s.handshakeComplete && !s.sentPacketHandler.HasPacingBudget() {
+ deadline := s.sentPacketHandler.TimeUntilSend()
+ if deadline.IsZero() {
+ deadline = deadlineSendImmediately
+ }
+ s.pacingDeadline = deadline
+ // Allow sending of an ACK if we're pacing limit (if we haven't sent out a packet yet).
+ // This makes sure that a peer that is mostly receiving data (and thus has an inaccurate cwnd estimate)
+ // sends enough ACKs to allow its peer to utilize the bandwidth.
+ if sentPacket {
+ return nil
+ }
+ sendMode = ackhandler.SendAck
+ }
+ switch sendMode {
+ case ackhandler.SendNone:
+ return nil
+ case ackhandler.SendAck:
+ // If we already sent packets, and the send mode switches to SendAck,
+ // as we've just become congestion limited.
+ // There's no need to try to send an ACK at this moment.
+ if sentPacket {
+ return nil
+ }
+ // We can at most send a single ACK only packet.
+ // There will only be a new ACK after receiving new packets.
+ // SendAck is only returned when we're congestion limited, so we don't need to set the pacinggs timer.
+ return s.maybeSendAckOnlyPacket()
+ case ackhandler.SendPTOInitial:
+ if err := s.sendProbePacket(protocol.EncryptionInitial); err != nil {
+ return err
+ }
+ case ackhandler.SendPTOHandshake:
+ if err := s.sendProbePacket(protocol.EncryptionHandshake); err != nil {
+ return err
+ }
+ case ackhandler.SendPTOAppData:
+ if err := s.sendProbePacket(protocol.Encryption1RTT); err != nil {
+ return err
+ }
+ case ackhandler.SendAny:
+ sent, err := s.sendPacket()
+ if err != nil || !sent {
+ return err
+ }
+ sentPacket = true
+ default:
+ return fmt.Errorf("BUG: invalid send mode %d", sendMode)
+ }
+ // Prioritize receiving of packets over sending out more packets.
+ if len(s.receivedPackets) > 0 {
+ s.pacingDeadline = deadlineSendImmediately
+ return nil
+ }
+ if s.sendQueue.WouldBlock() {
+ return nil
+ }
+ }
+}
+
+func (s *connection) maybeSendAckOnlyPacket() error {
+ if !s.handshakeConfirmed {
+ packet, err := s.packer.PackCoalescedPacket(true, s.version)
+ if err != nil {
+ return err
+ }
+ if packet == nil {
+ return nil
+ }
+ s.sendPackedCoalescedPacket(packet, time.Now())
+ return nil
+ }
+
+ now := time.Now()
+ p, buffer, err := s.packer.PackPacket(true, now, s.version)
+ if err != nil {
+ if err == errNothingToPack {
+ return nil
+ }
+ return err
+ }
+ s.logShortHeaderPacket(p.DestConnID, p.Ack, p.Frames, p.PacketNumber, p.PacketNumberLen, p.KeyPhase, buffer.Len(), false)
+ s.sendPackedShortHeaderPacket(buffer, p.Packet, now)
+ return nil
+}
+
+func (s *connection) sendProbePacket(encLevel protocol.EncryptionLevel) error {
+ // Queue probe packets until we actually send out a packet,
+ // or until there are no more packets to queue.
+ var packet *coalescedPacket
+ for {
+ if wasQueued := s.sentPacketHandler.QueueProbePacket(encLevel); !wasQueued {
+ break
+ }
+ var err error
+ packet, err = s.packer.MaybePackProbePacket(encLevel, s.version)
+ if err != nil {
+ return err
+ }
+ if packet != nil {
+ break
+ }
+ }
+ if packet == nil {
+ //nolint:exhaustive // Cannot send probe packets for 0-RTT.
+ switch encLevel {
+ case protocol.EncryptionInitial:
+ s.retransmissionQueue.AddInitial(&wire.PingFrame{})
+ case protocol.EncryptionHandshake:
+ s.retransmissionQueue.AddHandshake(&wire.PingFrame{})
+ case protocol.Encryption1RTT:
+ s.retransmissionQueue.AddAppData(&wire.PingFrame{})
+ default:
+ panic("unexpected encryption level")
+ }
+ var err error
+ packet, err = s.packer.MaybePackProbePacket(encLevel, s.version)
+ if err != nil {
+ return err
+ }
+ }
+ if packet == nil || (len(packet.longHdrPackets) == 0 && packet.shortHdrPacket == nil) {
+ return fmt.Errorf("connection BUG: couldn't pack %s probe packet", encLevel)
+ }
+ s.sendPackedCoalescedPacket(packet, time.Now())
+ return nil
+}
+
+func (s *connection) sendPacket() (bool, error) {
+ if isBlocked, offset := s.connFlowController.IsNewlyBlocked(); isBlocked {
+ s.framer.QueueControlFrame(&wire.DataBlockedFrame{MaximumData: offset})
+ }
+ s.windowUpdateQueue.QueueAll()
+
+ now := time.Now()
+ if !s.handshakeConfirmed {
+ packet, err := s.packer.PackCoalescedPacket(false, s.version)
+ if err != nil || packet == nil {
+ return false, err
+ }
+ s.sentFirstPacket = true
+ s.sendPackedCoalescedPacket(packet, now)
+ return true, nil
+ } else if !s.config.DisablePathMTUDiscovery && s.mtuDiscoverer.ShouldSendProbe(now) {
+ ping, size := s.mtuDiscoverer.GetPing()
+ p, buffer, err := s.packer.PackMTUProbePacket(ping, size, now, s.version)
+ if err != nil {
+ return false, err
+ }
+ s.logShortHeaderPacket(p.DestConnID, p.Ack, p.Frames, p.PacketNumber, p.PacketNumberLen, p.KeyPhase, buffer.Len(), false)
+ s.sendPackedShortHeaderPacket(buffer, p.Packet, now)
+ return true, nil
+ }
+ p, buffer, err := s.packer.PackPacket(false, now, s.version)
+ if err != nil {
+ if err == errNothingToPack {
+ return false, nil
+ }
+ return false, err
+ }
+ s.logShortHeaderPacket(p.DestConnID, p.Ack, p.Frames, p.PacketNumber, p.PacketNumberLen, p.KeyPhase, buffer.Len(), false)
+ s.sendPackedShortHeaderPacket(buffer, p.Packet, now)
+ return true, nil
+}
+
+func (s *connection) sendPackedShortHeaderPacket(buffer *packetBuffer, p *ackhandler.Packet, now time.Time) {
+ if s.firstAckElicitingPacketAfterIdleSentTime.IsZero() && ackhandler.HasAckElicitingFrames(p.Frames) {
+ s.firstAckElicitingPacketAfterIdleSentTime = now
+ }
+
+ s.sentPacketHandler.SentPacket(p)
+ s.connIDManager.SentPacket()
+ s.sendQueue.Send(buffer)
+}
+
+func (s *connection) sendPackedCoalescedPacket(packet *coalescedPacket, now time.Time) {
+ s.logCoalescedPacket(packet)
+ for _, p := range packet.longHdrPackets {
+ if s.firstAckElicitingPacketAfterIdleSentTime.IsZero() && p.IsAckEliciting() {
+ s.firstAckElicitingPacketAfterIdleSentTime = now
+ }
+ s.sentPacketHandler.SentPacket(p.ToAckHandlerPacket(now, s.retransmissionQueue))
+ }
+ if p := packet.shortHdrPacket; p != nil {
+ if s.firstAckElicitingPacketAfterIdleSentTime.IsZero() && p.IsAckEliciting() {
+ s.firstAckElicitingPacketAfterIdleSentTime = now
+ }
+ s.sentPacketHandler.SentPacket(p.Packet)
+ }
+ s.connIDManager.SentPacket()
+ s.sendQueue.Send(packet.buffer)
+}
+
+func (s *connection) sendConnectionClose(e error) ([]byte, error) {
+ var packet *coalescedPacket
+ var err error
+ var transportErr *qerr.TransportError
+ var applicationErr *qerr.ApplicationError
+ if errors.As(e, &transportErr) {
+ packet, err = s.packer.PackConnectionClose(transportErr, s.version)
+ } else if errors.As(e, &applicationErr) {
+ packet, err = s.packer.PackApplicationClose(applicationErr, s.version)
+ } else {
+ packet, err = s.packer.PackConnectionClose(&qerr.TransportError{
+ ErrorCode: qerr.InternalError,
+ ErrorMessage: fmt.Sprintf("connection BUG: unspecified error type (msg: %s)", e.Error()),
+ }, s.version)
+ }
+ if err != nil {
+ return nil, err
+ }
+ s.logCoalescedPacket(packet)
+ return packet.buffer.Data, s.conn.Write(packet.buffer.Data)
+}
+
+func (s *connection) logLongHeaderPacket(p *longHeaderPacket) {
+ // quic-go logging
+ if s.logger.Debug() {
+ p.header.Log(s.logger)
+ if p.ack != nil {
+ wire.LogFrame(s.logger, p.ack, true)
+ }
+ for _, frame := range p.frames {
+ wire.LogFrame(s.logger, frame.Frame, true)
+ }
+ }
+
+ // tracing
+ if s.tracer != nil {
+ frames := make([]logging.Frame, 0, len(p.frames))
+ for _, f := range p.frames {
+ frames = append(frames, logutils.ConvertFrame(f.Frame))
+ }
+ var ack *logging.AckFrame
+ if p.ack != nil {
+ ack = logutils.ConvertAckFrame(p.ack)
+ }
+ s.tracer.SentLongHeaderPacket(p.header, p.length, ack, frames)
+ }
+}
+
+func (s *connection) logShortHeaderPacket(
+ destConnID protocol.ConnectionID,
+ ackFrame *wire.AckFrame,
+ frames []*ackhandler.Frame,
+ pn protocol.PacketNumber,
+ pnLen protocol.PacketNumberLen,
+ kp protocol.KeyPhaseBit,
+ size protocol.ByteCount,
+ isCoalesced bool,
+) {
+ if s.logger.Debug() && !isCoalesced {
+ s.logger.Debugf("-> Sending packet %d (%d bytes) for connection %s, 1-RTT", pn, size, s.logID)
+ }
+ // quic-go logging
+ if s.logger.Debug() {
+ wire.LogShortHeader(s.logger, destConnID, pn, pnLen, kp)
+ if ackFrame != nil {
+ wire.LogFrame(s.logger, ackFrame, true)
+ }
+ for _, frame := range frames {
+ wire.LogFrame(s.logger, frame.Frame, true)
+ }
+ }
+
+ // tracing
+ if s.tracer != nil {
+ fs := make([]logging.Frame, 0, len(frames))
+ for _, f := range frames {
+ fs = append(fs, logutils.ConvertFrame(f.Frame))
+ }
+ var ack *logging.AckFrame
+ if ackFrame != nil {
+ ack = logutils.ConvertAckFrame(ackFrame)
+ }
+ s.tracer.SentShortHeaderPacket(
+ &logging.ShortHeader{
+ DestConnectionID: destConnID,
+ PacketNumber: pn,
+ PacketNumberLen: pnLen,
+ KeyPhase: kp,
+ },
+ size,
+ ack,
+ fs,
+ )
+ }
+}
+
+func (s *connection) logCoalescedPacket(packet *coalescedPacket) {
+ if s.logger.Debug() {
+ if len(packet.longHdrPackets) > 1 {
+ s.logger.Debugf("-> Sending coalesced packet (%d parts, %d bytes) for connection %s", len(packet.longHdrPackets), packet.buffer.Len(), s.logID)
+ } else {
+ s.logger.Debugf("-> Sending packet %d (%d bytes) for connection %s, %s", packet.longHdrPackets[0].header.PacketNumber, packet.buffer.Len(), s.logID, packet.longHdrPackets[0].EncryptionLevel())
+ }
+ }
+ for _, p := range packet.longHdrPackets {
+ s.logLongHeaderPacket(p)
+ }
+ if p := packet.shortHdrPacket; p != nil {
+ s.logShortHeaderPacket(p.DestConnID, p.Ack, p.Frames, p.PacketNumber, p.PacketNumberLen, p.KeyPhase, p.Length, true)
+ }
+}
+
+// AcceptStream returns the next stream openend by the peer
+func (s *connection) AcceptStream(ctx context.Context) (Stream, error) {
+ return s.streamsMap.AcceptStream(ctx)
+}
+
+func (s *connection) AcceptUniStream(ctx context.Context) (ReceiveStream, error) {
+ return s.streamsMap.AcceptUniStream(ctx)
+}
+
+// OpenStream opens a stream
+func (s *connection) OpenStream() (Stream, error) {
+ return s.streamsMap.OpenStream()
+}
+
+func (s *connection) OpenStreamSync(ctx context.Context) (Stream, error) {
+ return s.streamsMap.OpenStreamSync(ctx)
+}
+
+func (s *connection) OpenUniStream() (SendStream, error) {
+ return s.streamsMap.OpenUniStream()
+}
+
+func (s *connection) OpenUniStreamSync(ctx context.Context) (SendStream, error) {
+ return s.streamsMap.OpenUniStreamSync(ctx)
+}
+
+func (s *connection) newFlowController(id protocol.StreamID) flowcontrol.StreamFlowController {
+ initialSendWindow := s.peerParams.InitialMaxStreamDataUni
+ if id.Type() == protocol.StreamTypeBidi {
+ if id.InitiatedBy() == s.perspective {
+ initialSendWindow = s.peerParams.InitialMaxStreamDataBidiRemote
+ } else {
+ initialSendWindow = s.peerParams.InitialMaxStreamDataBidiLocal
+ }
+ }
+ return flowcontrol.NewStreamFlowController(
+ id,
+ s.connFlowController,
+ protocol.ByteCount(s.config.InitialStreamReceiveWindow),
+ protocol.ByteCount(s.config.MaxStreamReceiveWindow),
+ initialSendWindow,
+ s.onHasStreamWindowUpdate,
+ s.rttStats,
+ s.logger,
+ )
+}
+
+// scheduleSending signals that we have data for sending
+func (s *connection) scheduleSending() {
+ select {
+ case s.sendingScheduled <- struct{}{}:
+ default:
+ }
+}
+
+// tryQueueingUndecryptablePacket queues a packet for which we're missing the decryption keys.
+// The logging.PacketType is only used for logging purposes.
+func (s *connection) tryQueueingUndecryptablePacket(p *receivedPacket, pt logging.PacketType) {
+ if s.handshakeComplete {
+ panic("shouldn't queue undecryptable packets after handshake completion")
+ }
+ if len(s.undecryptablePackets)+1 > protocol.MaxUndecryptablePackets {
+ if s.tracer != nil {
+ s.tracer.DroppedPacket(pt, p.Size(), logging.PacketDropDOSPrevention)
+ }
+ s.logger.Infof("Dropping undecryptable packet (%d bytes). Undecryptable packet queue full.", p.Size())
+ return
+ }
+ s.logger.Infof("Queueing packet (%d bytes) for later decryption", p.Size())
+ if s.tracer != nil {
+ s.tracer.BufferedPacket(pt, p.Size())
+ }
+ s.undecryptablePackets = append(s.undecryptablePackets, p)
+}
+
+func (s *connection) queueControlFrame(f wire.Frame) {
+ s.framer.QueueControlFrame(f)
+ s.scheduleSending()
+}
+
+func (s *connection) onHasStreamWindowUpdate(id protocol.StreamID) {
+ s.windowUpdateQueue.AddStream(id)
+ s.scheduleSending()
+}
+
+func (s *connection) onHasConnectionWindowUpdate() {
+ s.windowUpdateQueue.AddConnection()
+ s.scheduleSending()
+}
+
+func (s *connection) onHasStreamData(id protocol.StreamID) {
+ s.framer.AddActiveStream(id)
+ s.scheduleSending()
+}
+
+func (s *connection) onStreamCompleted(id protocol.StreamID) {
+ if err := s.streamsMap.DeleteStream(id); err != nil {
+ s.closeLocal(err)
+ }
+}
+
+func (s *connection) SendMessage(p []byte) error {
+ if !s.supportsDatagrams() {
+ return errors.New("datagram support disabled")
+ }
+
+ f := &wire.DatagramFrame{DataLenPresent: true}
+ if protocol.ByteCount(len(p)) > f.MaxDataLen(s.peerParams.MaxDatagramFrameSize, s.version) {
+ return errors.New("message too large")
+ }
+ f.Data = make([]byte, len(p))
+ copy(f.Data, p)
+ return s.datagramQueue.AddAndWait(f)
+}
+
+func (s *connection) ReceiveMessage() ([]byte, error) {
+ if !s.config.EnableDatagrams {
+ return nil, errors.New("datagram support disabled")
+ }
+ return s.datagramQueue.Receive()
+}
+
+func (s *connection) LocalAddr() net.Addr {
+ return s.conn.LocalAddr()
+}
+
+func (s *connection) RemoteAddr() net.Addr {
+ return s.conn.RemoteAddr()
+}
+
+func (s *connection) getPerspective() protocol.Perspective {
+ return s.perspective
+}
+
+func (s *connection) GetVersion() protocol.VersionNumber {
+ return s.version
+}
+
+func (s *connection) NextConnection() Connection {
+ <-s.HandshakeComplete().Done()
+ s.streamsMap.UseResetMaps()
+ return s
+}
diff --git a/vendor/github.com/quic-go/quic-go/connection_timer.go b/vendor/github.com/quic-go/quic-go/connection_timer.go
new file mode 100644
index 0000000000..171fdd0138
--- /dev/null
+++ b/vendor/github.com/quic-go/quic-go/connection_timer.go
@@ -0,0 +1,51 @@
+package quic
+
+import (
+ "time"
+
+ "github.com/quic-go/quic-go/internal/utils"
+)
+
+var deadlineSendImmediately = time.Time{}.Add(42 * time.Millisecond) // any value > time.Time{} and before time.Now() is fine
+
+type connectionTimer struct {
+ timer *utils.Timer
+ last time.Time
+}
+
+func newTimer() *connectionTimer {
+ return &connectionTimer{timer: utils.NewTimer()}
+}
+
+func (t *connectionTimer) SetRead() {
+ if deadline := t.timer.Deadline(); deadline != deadlineSendImmediately {
+ t.last = deadline
+ }
+ t.timer.SetRead()
+}
+
+func (t *connectionTimer) Chan() <-chan time.Time {
+ return t.timer.Chan()
+}
+
+// SetTimer resets the timer.
+// It makes sure that the deadline is strictly increasing.
+// This prevents busy-looping in cases where the timer fires, but we can't actually send out a packet.
+// This doesn't apply to the pacing deadline, which can be set multiple times to deadlineSendImmediately.
+func (t *connectionTimer) SetTimer(idleTimeoutOrKeepAlive, ackAlarm, lossTime, pacing time.Time) {
+ deadline := idleTimeoutOrKeepAlive
+ if !ackAlarm.IsZero() && ackAlarm.Before(deadline) && ackAlarm.After(t.last) {
+ deadline = ackAlarm
+ }
+ if !lossTime.IsZero() && lossTime.Before(deadline) && lossTime.After(t.last) {
+ deadline = lossTime
+ }
+ if !pacing.IsZero() && pacing.Before(deadline) {
+ deadline = pacing
+ }
+ t.timer.Reset(deadline)
+}
+
+func (t *connectionTimer) Stop() {
+ t.timer.Stop()
+}
diff --git a/vendor/github.com/quic-go/quic-go/crypto_stream.go b/vendor/github.com/quic-go/quic-go/crypto_stream.go
new file mode 100644
index 0000000000..f10e91202f
--- /dev/null
+++ b/vendor/github.com/quic-go/quic-go/crypto_stream.go
@@ -0,0 +1,115 @@
+package quic
+
+import (
+ "fmt"
+ "io"
+
+ "github.com/quic-go/quic-go/internal/protocol"
+ "github.com/quic-go/quic-go/internal/qerr"
+ "github.com/quic-go/quic-go/internal/utils"
+ "github.com/quic-go/quic-go/internal/wire"
+)
+
+type cryptoStream interface {
+ // for receiving data
+ HandleCryptoFrame(*wire.CryptoFrame) error
+ GetCryptoData() []byte
+ Finish() error
+ // for sending data
+ io.Writer
+ HasData() bool
+ PopCryptoFrame(protocol.ByteCount) *wire.CryptoFrame
+}
+
+type cryptoStreamImpl struct {
+ queue *frameSorter
+ msgBuf []byte
+
+ highestOffset protocol.ByteCount
+ finished bool
+
+ writeOffset protocol.ByteCount
+ writeBuf []byte
+}
+
+func newCryptoStream() cryptoStream {
+ return &cryptoStreamImpl{queue: newFrameSorter()}
+}
+
+func (s *cryptoStreamImpl) HandleCryptoFrame(f *wire.CryptoFrame) error {
+ highestOffset := f.Offset + protocol.ByteCount(len(f.Data))
+ if maxOffset := highestOffset; maxOffset > protocol.MaxCryptoStreamOffset {
+ return &qerr.TransportError{
+ ErrorCode: qerr.CryptoBufferExceeded,
+ ErrorMessage: fmt.Sprintf("received invalid offset %d on crypto stream, maximum allowed %d", maxOffset, protocol.MaxCryptoStreamOffset),
+ }
+ }
+ if s.finished {
+ if highestOffset > s.highestOffset {
+ // reject crypto data received after this stream was already finished
+ return &qerr.TransportError{
+ ErrorCode: qerr.ProtocolViolation,
+ ErrorMessage: "received crypto data after change of encryption level",
+ }
+ }
+ // ignore data with a smaller offset than the highest received
+ // could e.g. be a retransmission
+ return nil
+ }
+ s.highestOffset = utils.Max(s.highestOffset, highestOffset)
+ if err := s.queue.Push(f.Data, f.Offset, nil); err != nil {
+ return err
+ }
+ for {
+ _, data, _ := s.queue.Pop()
+ if data == nil {
+ return nil
+ }
+ s.msgBuf = append(s.msgBuf, data...)
+ }
+}
+
+// GetCryptoData retrieves data that was received in CRYPTO frames
+func (s *cryptoStreamImpl) GetCryptoData() []byte {
+ if len(s.msgBuf) < 4 {
+ return nil
+ }
+ msgLen := 4 + int(s.msgBuf[1])<<16 + int(s.msgBuf[2])<<8 + int(s.msgBuf[3])
+ if len(s.msgBuf) < msgLen {
+ return nil
+ }
+ msg := make([]byte, msgLen)
+ copy(msg, s.msgBuf[:msgLen])
+ s.msgBuf = s.msgBuf[msgLen:]
+ return msg
+}
+
+func (s *cryptoStreamImpl) Finish() error {
+ if s.queue.HasMoreData() {
+ return &qerr.TransportError{
+ ErrorCode: qerr.ProtocolViolation,
+ ErrorMessage: "encryption level changed, but crypto stream has more data to read",
+ }
+ }
+ s.finished = true
+ return nil
+}
+
+// Writes writes data that should be sent out in CRYPTO frames
+func (s *cryptoStreamImpl) Write(p []byte) (int, error) {
+ s.writeBuf = append(s.writeBuf, p...)
+ return len(p), nil
+}
+
+func (s *cryptoStreamImpl) HasData() bool {
+ return len(s.writeBuf) > 0
+}
+
+func (s *cryptoStreamImpl) PopCryptoFrame(maxLen protocol.ByteCount) *wire.CryptoFrame {
+ f := &wire.CryptoFrame{Offset: s.writeOffset}
+ n := utils.Min(f.MaxDataLen(maxLen), protocol.ByteCount(len(s.writeBuf)))
+ f.Data = s.writeBuf[:n]
+ s.writeBuf = s.writeBuf[n:]
+ s.writeOffset += n
+ return f
+}
diff --git a/vendor/github.com/quic-go/quic-go/crypto_stream_manager.go b/vendor/github.com/quic-go/quic-go/crypto_stream_manager.go
new file mode 100644
index 0000000000..91946acfa5
--- /dev/null
+++ b/vendor/github.com/quic-go/quic-go/crypto_stream_manager.go
@@ -0,0 +1,61 @@
+package quic
+
+import (
+ "fmt"
+
+ "github.com/quic-go/quic-go/internal/protocol"
+ "github.com/quic-go/quic-go/internal/wire"
+)
+
+type cryptoDataHandler interface {
+ HandleMessage([]byte, protocol.EncryptionLevel) bool
+}
+
+type cryptoStreamManager struct {
+ cryptoHandler cryptoDataHandler
+
+ initialStream cryptoStream
+ handshakeStream cryptoStream
+ oneRTTStream cryptoStream
+}
+
+func newCryptoStreamManager(
+ cryptoHandler cryptoDataHandler,
+ initialStream cryptoStream,
+ handshakeStream cryptoStream,
+ oneRTTStream cryptoStream,
+) *cryptoStreamManager {
+ return &cryptoStreamManager{
+ cryptoHandler: cryptoHandler,
+ initialStream: initialStream,
+ handshakeStream: handshakeStream,
+ oneRTTStream: oneRTTStream,
+ }
+}
+
+func (m *cryptoStreamManager) HandleCryptoFrame(frame *wire.CryptoFrame, encLevel protocol.EncryptionLevel) (bool /* encryption level changed */, error) {
+ var str cryptoStream
+ //nolint:exhaustive // CRYPTO frames cannot be sent in 0-RTT packets.
+ switch encLevel {
+ case protocol.EncryptionInitial:
+ str = m.initialStream
+ case protocol.EncryptionHandshake:
+ str = m.handshakeStream
+ case protocol.Encryption1RTT:
+ str = m.oneRTTStream
+ default:
+ return false, fmt.Errorf("received CRYPTO frame with unexpected encryption level: %s", encLevel)
+ }
+ if err := str.HandleCryptoFrame(frame); err != nil {
+ return false, err
+ }
+ for {
+ data := str.GetCryptoData()
+ if data == nil {
+ return false, nil
+ }
+ if encLevelFinished := m.cryptoHandler.HandleMessage(data, encLevel); encLevelFinished {
+ return true, str.Finish()
+ }
+ }
+}
diff --git a/vendor/github.com/quic-go/quic-go/datagram_queue.go b/vendor/github.com/quic-go/quic-go/datagram_queue.go
new file mode 100644
index 0000000000..58aad3b8f1
--- /dev/null
+++ b/vendor/github.com/quic-go/quic-go/datagram_queue.go
@@ -0,0 +1,99 @@
+package quic
+
+import (
+ "github.com/quic-go/quic-go/internal/protocol"
+ "github.com/quic-go/quic-go/internal/utils"
+ "github.com/quic-go/quic-go/internal/wire"
+)
+
+type datagramQueue struct {
+ sendQueue chan *wire.DatagramFrame
+ nextFrame *wire.DatagramFrame
+ rcvQueue chan []byte
+
+ closeErr error
+ closed chan struct{}
+
+ hasData func()
+
+ dequeued chan struct{}
+
+ logger utils.Logger
+}
+
+func newDatagramQueue(hasData func(), logger utils.Logger) *datagramQueue {
+ return &datagramQueue{
+ hasData: hasData,
+ sendQueue: make(chan *wire.DatagramFrame, 1),
+ rcvQueue: make(chan []byte, protocol.DatagramRcvQueueLen),
+ dequeued: make(chan struct{}),
+ closed: make(chan struct{}),
+ logger: logger,
+ }
+}
+
+// AddAndWait queues a new DATAGRAM frame for sending.
+// It blocks until the frame has been dequeued.
+func (h *datagramQueue) AddAndWait(f *wire.DatagramFrame) error {
+ select {
+ case h.sendQueue <- f:
+ h.hasData()
+ case <-h.closed:
+ return h.closeErr
+ }
+
+ select {
+ case <-h.dequeued:
+ return nil
+ case <-h.closed:
+ return h.closeErr
+ }
+}
+
+// Peek gets the next DATAGRAM frame for sending.
+// If actually sent out, Pop needs to be called before the next call to Peek.
+func (h *datagramQueue) Peek() *wire.DatagramFrame {
+ if h.nextFrame != nil {
+ return h.nextFrame
+ }
+ select {
+ case h.nextFrame = <-h.sendQueue:
+ h.dequeued <- struct{}{}
+ default:
+ return nil
+ }
+ return h.nextFrame
+}
+
+func (h *datagramQueue) Pop() {
+ if h.nextFrame == nil {
+ panic("datagramQueue BUG: Pop called for nil frame")
+ }
+ h.nextFrame = nil
+}
+
+// HandleDatagramFrame handles a received DATAGRAM frame.
+func (h *datagramQueue) HandleDatagramFrame(f *wire.DatagramFrame) {
+ data := make([]byte, len(f.Data))
+ copy(data, f.Data)
+ select {
+ case h.rcvQueue <- data:
+ default:
+ h.logger.Debugf("Discarding DATAGRAM frame (%d bytes payload)", len(f.Data))
+ }
+}
+
+// Receive gets a received DATAGRAM frame.
+func (h *datagramQueue) Receive() ([]byte, error) {
+ select {
+ case data := <-h.rcvQueue:
+ return data, nil
+ case <-h.closed:
+ return nil, h.closeErr
+ }
+}
+
+func (h *datagramQueue) CloseWithError(e error) {
+ h.closeErr = e
+ close(h.closed)
+}
diff --git a/vendor/github.com/quic-go/quic-go/errors.go b/vendor/github.com/quic-go/quic-go/errors.go
new file mode 100644
index 0000000000..c9fb0a07b0
--- /dev/null
+++ b/vendor/github.com/quic-go/quic-go/errors.go
@@ -0,0 +1,63 @@
+package quic
+
+import (
+ "fmt"
+
+ "github.com/quic-go/quic-go/internal/qerr"
+)
+
+type (
+ TransportError = qerr.TransportError
+ ApplicationError = qerr.ApplicationError
+ VersionNegotiationError = qerr.VersionNegotiationError
+ StatelessResetError = qerr.StatelessResetError
+ IdleTimeoutError = qerr.IdleTimeoutError
+ HandshakeTimeoutError = qerr.HandshakeTimeoutError
+)
+
+type (
+ TransportErrorCode = qerr.TransportErrorCode
+ ApplicationErrorCode = qerr.ApplicationErrorCode
+ StreamErrorCode = qerr.StreamErrorCode
+)
+
+const (
+ NoError = qerr.NoError
+ InternalError = qerr.InternalError
+ ConnectionRefused = qerr.ConnectionRefused
+ FlowControlError = qerr.FlowControlError
+ StreamLimitError = qerr.StreamLimitError
+ StreamStateError = qerr.StreamStateError
+ FinalSizeError = qerr.FinalSizeError
+ FrameEncodingError = qerr.FrameEncodingError
+ TransportParameterError = qerr.TransportParameterError
+ ConnectionIDLimitError = qerr.ConnectionIDLimitError
+ ProtocolViolation = qerr.ProtocolViolation
+ InvalidToken = qerr.InvalidToken
+ ApplicationErrorErrorCode = qerr.ApplicationErrorErrorCode
+ CryptoBufferExceeded = qerr.CryptoBufferExceeded
+ KeyUpdateError = qerr.KeyUpdateError
+ AEADLimitReached = qerr.AEADLimitReached
+ NoViablePathError = qerr.NoViablePathError
+)
+
+// A StreamError is used for Stream.CancelRead and Stream.CancelWrite.
+// It is also returned from Stream.Read and Stream.Write if the peer canceled reading or writing.
+type StreamError struct {
+ StreamID StreamID
+ ErrorCode StreamErrorCode
+ Remote bool
+}
+
+func (e *StreamError) Is(target error) bool {
+ _, ok := target.(*StreamError)
+ return ok
+}
+
+func (e *StreamError) Error() string {
+ pers := "local"
+ if e.Remote {
+ pers = "remote"
+ }
+ return fmt.Sprintf("stream %d canceled by %s with error code %d", e.StreamID, pers, e.ErrorCode)
+}
diff --git a/vendor/github.com/quic-go/quic-go/frame_sorter.go b/vendor/github.com/quic-go/quic-go/frame_sorter.go
new file mode 100644
index 0000000000..bee0abadb5
--- /dev/null
+++ b/vendor/github.com/quic-go/quic-go/frame_sorter.go
@@ -0,0 +1,237 @@
+package quic
+
+import (
+ "errors"
+ "sync"
+
+ "github.com/quic-go/quic-go/internal/protocol"
+ list "github.com/quic-go/quic-go/internal/utils/linkedlist"
+)
+
+// byteInterval is an interval from one ByteCount to the other
+type byteInterval struct {
+ Start protocol.ByteCount
+ End protocol.ByteCount
+}
+
+var byteIntervalElementPool sync.Pool
+
+func init() {
+ byteIntervalElementPool = *list.NewPool[byteInterval]()
+}
+
+type frameSorterEntry struct {
+ Data []byte
+ DoneCb func()
+}
+
+type frameSorter struct {
+ queue map[protocol.ByteCount]frameSorterEntry
+ readPos protocol.ByteCount
+ gaps *list.List[byteInterval]
+}
+
+var errDuplicateStreamData = errors.New("duplicate stream data")
+
+func newFrameSorter() *frameSorter {
+ s := frameSorter{
+ gaps: list.NewWithPool[byteInterval](&byteIntervalElementPool),
+ queue: make(map[protocol.ByteCount]frameSorterEntry),
+ }
+ s.gaps.PushFront(byteInterval{Start: 0, End: protocol.MaxByteCount})
+ return &s
+}
+
+func (s *frameSorter) Push(data []byte, offset protocol.ByteCount, doneCb func()) error {
+ err := s.push(data, offset, doneCb)
+ if err == errDuplicateStreamData {
+ if doneCb != nil {
+ doneCb()
+ }
+ return nil
+ }
+ return err
+}
+
+func (s *frameSorter) push(data []byte, offset protocol.ByteCount, doneCb func()) error {
+ if len(data) == 0 {
+ return errDuplicateStreamData
+ }
+
+ start := offset
+ end := offset + protocol.ByteCount(len(data))
+
+ if end <= s.gaps.Front().Value.Start {
+ return errDuplicateStreamData
+ }
+
+ startGap, startsInGap := s.findStartGap(start)
+ endGap, endsInGap := s.findEndGap(startGap, end)
+
+ startGapEqualsEndGap := startGap == endGap
+
+ if (startGapEqualsEndGap && end <= startGap.Value.Start) ||
+ (!startGapEqualsEndGap && startGap.Value.End >= endGap.Value.Start && end <= startGap.Value.Start) {
+ return errDuplicateStreamData
+ }
+
+ startGapNext := startGap.Next()
+ startGapEnd := startGap.Value.End // save it, in case startGap is modified
+ endGapStart := endGap.Value.Start // save it, in case endGap is modified
+ endGapEnd := endGap.Value.End // save it, in case endGap is modified
+ var adjustedStartGapEnd bool
+ var wasCut bool
+
+ pos := start
+ var hasReplacedAtLeastOne bool
+ for {
+ oldEntry, ok := s.queue[pos]
+ if !ok {
+ break
+ }
+ oldEntryLen := protocol.ByteCount(len(oldEntry.Data))
+ if end-pos > oldEntryLen || (hasReplacedAtLeastOne && end-pos == oldEntryLen) {
+ // The existing frame is shorter than the new frame. Replace it.
+ delete(s.queue, pos)
+ pos += oldEntryLen
+ hasReplacedAtLeastOne = true
+ if oldEntry.DoneCb != nil {
+ oldEntry.DoneCb()
+ }
+ } else {
+ if !hasReplacedAtLeastOne {
+ return errDuplicateStreamData
+ }
+ // The existing frame is longer than the new frame.
+ // Cut the new frame such that the end aligns with the start of the existing frame.
+ data = data[:pos-start]
+ end = pos
+ wasCut = true
+ break
+ }
+ }
+
+ if !startsInGap && !hasReplacedAtLeastOne {
+ // cut the frame, such that it starts at the start of the gap
+ data = data[startGap.Value.Start-start:]
+ start = startGap.Value.Start
+ wasCut = true
+ }
+ if start <= startGap.Value.Start {
+ if end >= startGap.Value.End {
+ // The frame covers the whole startGap. Delete the gap.
+ s.gaps.Remove(startGap)
+ } else {
+ startGap.Value.Start = end
+ }
+ } else if !hasReplacedAtLeastOne {
+ startGap.Value.End = start
+ adjustedStartGapEnd = true
+ }
+
+ if !startGapEqualsEndGap {
+ s.deleteConsecutive(startGapEnd)
+ var nextGap *list.Element[byteInterval]
+ for gap := startGapNext; gap.Value.End < endGapStart; gap = nextGap {
+ nextGap = gap.Next()
+ s.deleteConsecutive(gap.Value.End)
+ s.gaps.Remove(gap)
+ }
+ }
+
+ if !endsInGap && start != endGapEnd && end > endGapEnd {
+ // cut the frame, such that it ends at the end of the gap
+ data = data[:endGapEnd-start]
+ end = endGapEnd
+ wasCut = true
+ }
+ if end == endGapEnd {
+ if !startGapEqualsEndGap {
+ // The frame covers the whole endGap. Delete the gap.
+ s.gaps.Remove(endGap)
+ }
+ } else {
+ if startGapEqualsEndGap && adjustedStartGapEnd {
+ // The frame split the existing gap into two.
+ s.gaps.InsertAfter(byteInterval{Start: end, End: startGapEnd}, startGap)
+ } else if !startGapEqualsEndGap {
+ endGap.Value.Start = end
+ }
+ }
+
+ if wasCut && len(data) < protocol.MinStreamFrameBufferSize {
+ newData := make([]byte, len(data))
+ copy(newData, data)
+ data = newData
+ if doneCb != nil {
+ doneCb()
+ doneCb = nil
+ }
+ }
+
+ if s.gaps.Len() > protocol.MaxStreamFrameSorterGaps {
+ return errors.New("too many gaps in received data")
+ }
+
+ s.queue[start] = frameSorterEntry{Data: data, DoneCb: doneCb}
+ return nil
+}
+
+func (s *frameSorter) findStartGap(offset protocol.ByteCount) (*list.Element[byteInterval], bool) {
+ for gap := s.gaps.Front(); gap != nil; gap = gap.Next() {
+ if offset >= gap.Value.Start && offset <= gap.Value.End {
+ return gap, true
+ }
+ if offset < gap.Value.Start {
+ return gap, false
+ }
+ }
+ panic("no gap found")
+}
+
+func (s *frameSorter) findEndGap(startGap *list.Element[byteInterval], offset protocol.ByteCount) (*list.Element[byteInterval], bool) {
+ for gap := startGap; gap != nil; gap = gap.Next() {
+ if offset >= gap.Value.Start && offset < gap.Value.End {
+ return gap, true
+ }
+ if offset < gap.Value.Start {
+ return gap.Prev(), false
+ }
+ }
+ panic("no gap found")
+}
+
+// deleteConsecutive deletes consecutive frames from the queue, starting at pos
+func (s *frameSorter) deleteConsecutive(pos protocol.ByteCount) {
+ for {
+ oldEntry, ok := s.queue[pos]
+ if !ok {
+ break
+ }
+ oldEntryLen := protocol.ByteCount(len(oldEntry.Data))
+ delete(s.queue, pos)
+ if oldEntry.DoneCb != nil {
+ oldEntry.DoneCb()
+ }
+ pos += oldEntryLen
+ }
+}
+
+func (s *frameSorter) Pop() (protocol.ByteCount, []byte, func()) {
+ entry, ok := s.queue[s.readPos]
+ if !ok {
+ return s.readPos, nil, nil
+ }
+ delete(s.queue, s.readPos)
+ offset := s.readPos
+ s.readPos += protocol.ByteCount(len(entry.Data))
+ if s.gaps.Front().Value.End <= s.readPos {
+ panic("frame sorter BUG: read position higher than a gap")
+ }
+ return offset, entry.Data, entry.DoneCb
+}
+
+// HasMoreData says if there is any more data queued at *any* offset.
+func (s *frameSorter) HasMoreData() bool {
+ return len(s.queue) > 0
+}
diff --git a/vendor/github.com/quic-go/quic-go/framer.go b/vendor/github.com/quic-go/quic-go/framer.go
new file mode 100644
index 0000000000..0b2059164a
--- /dev/null
+++ b/vendor/github.com/quic-go/quic-go/framer.go
@@ -0,0 +1,168 @@
+package quic
+
+import (
+ "errors"
+ "sync"
+
+ "github.com/quic-go/quic-go/internal/ackhandler"
+ "github.com/quic-go/quic-go/internal/protocol"
+ "github.com/quic-go/quic-go/internal/wire"
+ "github.com/quic-go/quic-go/quicvarint"
+)
+
+type framer interface {
+ HasData() bool
+
+ QueueControlFrame(wire.Frame)
+ AppendControlFrames([]*ackhandler.Frame, protocol.ByteCount, protocol.VersionNumber) ([]*ackhandler.Frame, protocol.ByteCount)
+
+ AddActiveStream(protocol.StreamID)
+ AppendStreamFrames([]*ackhandler.Frame, protocol.ByteCount, protocol.VersionNumber) ([]*ackhandler.Frame, protocol.ByteCount)
+
+ Handle0RTTRejection() error
+}
+
+type framerI struct {
+ mutex sync.Mutex
+
+ streamGetter streamGetter
+
+ activeStreams map[protocol.StreamID]struct{}
+ streamQueue []protocol.StreamID
+
+ controlFrameMutex sync.Mutex
+ controlFrames []wire.Frame
+}
+
+var _ framer = &framerI{}
+
+func newFramer(streamGetter streamGetter) framer {
+ return &framerI{
+ streamGetter: streamGetter,
+ activeStreams: make(map[protocol.StreamID]struct{}),
+ }
+}
+
+func (f *framerI) HasData() bool {
+ f.mutex.Lock()
+ hasData := len(f.streamQueue) > 0
+ f.mutex.Unlock()
+ if hasData {
+ return true
+ }
+ f.controlFrameMutex.Lock()
+ hasData = len(f.controlFrames) > 0
+ f.controlFrameMutex.Unlock()
+ return hasData
+}
+
+func (f *framerI) QueueControlFrame(frame wire.Frame) {
+ f.controlFrameMutex.Lock()
+ f.controlFrames = append(f.controlFrames, frame)
+ f.controlFrameMutex.Unlock()
+}
+
+func (f *framerI) AppendControlFrames(frames []*ackhandler.Frame, maxLen protocol.ByteCount, v protocol.VersionNumber) ([]*ackhandler.Frame, protocol.ByteCount) {
+ var length protocol.ByteCount
+ f.controlFrameMutex.Lock()
+ for len(f.controlFrames) > 0 {
+ frame := f.controlFrames[len(f.controlFrames)-1]
+ frameLen := frame.Length(v)
+ if length+frameLen > maxLen {
+ break
+ }
+ af := ackhandler.GetFrame()
+ af.Frame = frame
+ frames = append(frames, af)
+ length += frameLen
+ f.controlFrames = f.controlFrames[:len(f.controlFrames)-1]
+ }
+ f.controlFrameMutex.Unlock()
+ return frames, length
+}
+
+func (f *framerI) AddActiveStream(id protocol.StreamID) {
+ f.mutex.Lock()
+ if _, ok := f.activeStreams[id]; !ok {
+ f.streamQueue = append(f.streamQueue, id)
+ f.activeStreams[id] = struct{}{}
+ }
+ f.mutex.Unlock()
+}
+
+func (f *framerI) AppendStreamFrames(frames []*ackhandler.Frame, maxLen protocol.ByteCount, v protocol.VersionNumber) ([]*ackhandler.Frame, protocol.ByteCount) {
+ var length protocol.ByteCount
+ var lastFrame *ackhandler.Frame
+ f.mutex.Lock()
+ // pop STREAM frames, until less than MinStreamFrameSize bytes are left in the packet
+ numActiveStreams := len(f.streamQueue)
+ for i := 0; i < numActiveStreams; i++ {
+ if protocol.MinStreamFrameSize+length > maxLen {
+ break
+ }
+ id := f.streamQueue[0]
+ f.streamQueue = f.streamQueue[1:]
+ // This should never return an error. Better check it anyway.
+ // The stream will only be in the streamQueue, if it enqueued itself there.
+ str, err := f.streamGetter.GetOrOpenSendStream(id)
+ // The stream can be nil if it completed after it said it had data.
+ if str == nil || err != nil {
+ delete(f.activeStreams, id)
+ continue
+ }
+ remainingLen := maxLen - length
+ // For the last STREAM frame, we'll remove the DataLen field later.
+ // Therefore, we can pretend to have more bytes available when popping
+ // the STREAM frame (which will always have the DataLen set).
+ remainingLen += quicvarint.Len(uint64(remainingLen))
+ frame, hasMoreData := str.popStreamFrame(remainingLen, v)
+ if hasMoreData { // put the stream back in the queue (at the end)
+ f.streamQueue = append(f.streamQueue, id)
+ } else { // no more data to send. Stream is not active any more
+ delete(f.activeStreams, id)
+ }
+ // The frame can be nil
+ // * if the receiveStream was canceled after it said it had data
+ // * the remaining size doesn't allow us to add another STREAM frame
+ if frame == nil {
+ continue
+ }
+ frames = append(frames, frame)
+ length += frame.Length(v)
+ lastFrame = frame
+ }
+ f.mutex.Unlock()
+ if lastFrame != nil {
+ lastFrameLen := lastFrame.Length(v)
+ // account for the smaller size of the last STREAM frame
+ lastFrame.Frame.(*wire.StreamFrame).DataLenPresent = false
+ length += lastFrame.Length(v) - lastFrameLen
+ }
+ return frames, length
+}
+
+func (f *framerI) Handle0RTTRejection() error {
+ f.mutex.Lock()
+ defer f.mutex.Unlock()
+
+ f.controlFrameMutex.Lock()
+ f.streamQueue = f.streamQueue[:0]
+ for id := range f.activeStreams {
+ delete(f.activeStreams, id)
+ }
+ var j int
+ for i, frame := range f.controlFrames {
+ switch frame.(type) {
+ case *wire.MaxDataFrame, *wire.MaxStreamDataFrame, *wire.MaxStreamsFrame:
+ return errors.New("didn't expect MAX_DATA / MAX_STREAM_DATA / MAX_STREAMS frame to be sent in 0-RTT")
+ case *wire.DataBlockedFrame, *wire.StreamDataBlockedFrame, *wire.StreamsBlockedFrame:
+ continue
+ default:
+ f.controlFrames[j] = f.controlFrames[i]
+ j++
+ }
+ }
+ f.controlFrames = f.controlFrames[:j]
+ f.controlFrameMutex.Unlock()
+ return nil
+}
diff --git a/vendor/github.com/quic-go/quic-go/interface.go b/vendor/github.com/quic-go/quic-go/interface.go
new file mode 100644
index 0000000000..e55f258e59
--- /dev/null
+++ b/vendor/github.com/quic-go/quic-go/interface.go
@@ -0,0 +1,363 @@
+package quic
+
+import (
+ "context"
+ "errors"
+ "io"
+ "net"
+ "time"
+
+ "github.com/quic-go/quic-go/internal/handshake"
+ "github.com/quic-go/quic-go/internal/protocol"
+ "github.com/quic-go/quic-go/logging"
+)
+
+// The StreamID is the ID of a QUIC stream.
+type StreamID = protocol.StreamID
+
+// A VersionNumber is a QUIC version number.
+type VersionNumber = protocol.VersionNumber
+
+const (
+ // VersionDraft29 is IETF QUIC draft-29
+ VersionDraft29 = protocol.VersionDraft29
+ // Version1 is RFC 9000
+ Version1 = protocol.Version1
+ Version2 = protocol.Version2
+)
+
+// A ClientToken is a token received by the client.
+// It can be used to skip address validation on future connection attempts.
+type ClientToken struct {
+ data []byte
+}
+
+type TokenStore interface {
+ // Pop searches for a ClientToken associated with the given key.
+ // Since tokens are not supposed to be reused, it must remove the token from the cache.
+ // It returns nil when no token is found.
+ Pop(key string) (token *ClientToken)
+
+ // Put adds a token to the cache with the given key. It might get called
+ // multiple times in a connection.
+ Put(key string, token *ClientToken)
+}
+
+// Err0RTTRejected is the returned from:
+// * Open{Uni}Stream{Sync}
+// * Accept{Uni}Stream
+// * Stream.Read and Stream.Write
+// when the server rejects a 0-RTT connection attempt.
+var Err0RTTRejected = errors.New("0-RTT rejected")
+
+// ConnectionTracingKey can be used to associate a ConnectionTracer with a Connection.
+// It is set on the Connection.Context() context,
+// as well as on the context passed to logging.Tracer.NewConnectionTracer.
+var ConnectionTracingKey = connTracingCtxKey{}
+
+type connTracingCtxKey struct{}
+
+// Stream is the interface implemented by QUIC streams
+// In addition to the errors listed on the Connection,
+// calls to stream functions can return a StreamError if the stream is canceled.
+type Stream interface {
+ ReceiveStream
+ SendStream
+ // SetDeadline sets the read and write deadlines associated
+ // with the connection. It is equivalent to calling both
+ // SetReadDeadline and SetWriteDeadline.
+ SetDeadline(t time.Time) error
+}
+
+// A ReceiveStream is a unidirectional Receive Stream.
+type ReceiveStream interface {
+ // StreamID returns the stream ID.
+ StreamID() StreamID
+ // Read reads data from the stream.
+ // Read can be made to time out and return a net.Error with Timeout() == true
+ // after a fixed time limit; see SetDeadline and SetReadDeadline.
+ // If the stream was canceled by the peer, the error implements the StreamError
+ // interface, and Canceled() == true.
+ // If the connection was closed due to a timeout, the error satisfies
+ // the net.Error interface, and Timeout() will be true.
+ io.Reader
+ // CancelRead aborts receiving on this stream.
+ // It will ask the peer to stop transmitting stream data.
+ // Read will unblock immediately, and future Read calls will fail.
+ // When called multiple times or after reading the io.EOF it is a no-op.
+ CancelRead(StreamErrorCode)
+ // SetReadDeadline sets the deadline for future Read calls and
+ // any currently-blocked Read call.
+ // A zero value for t means Read will not time out.
+
+ SetReadDeadline(t time.Time) error
+}
+
+// A SendStream is a unidirectional Send Stream.
+type SendStream interface {
+ // StreamID returns the stream ID.
+ StreamID() StreamID
+ // Write writes data to the stream.
+ // Write can be made to time out and return a net.Error with Timeout() == true
+ // after a fixed time limit; see SetDeadline and SetWriteDeadline.
+ // If the stream was canceled by the peer, the error implements the StreamError
+ // interface, and Canceled() == true.
+ // If the connection was closed due to a timeout, the error satisfies
+ // the net.Error interface, and Timeout() will be true.
+ io.Writer
+ // Close closes the write-direction of the stream.
+ // Future calls to Write are not permitted after calling Close.
+ // It must not be called concurrently with Write.
+ // It must not be called after calling CancelWrite.
+ io.Closer
+ // CancelWrite aborts sending on this stream.
+ // Data already written, but not yet delivered to the peer is not guaranteed to be delivered reliably.
+ // Write will unblock immediately, and future calls to Write will fail.
+ // When called multiple times or after closing the stream it is a no-op.
+ CancelWrite(StreamErrorCode)
+ // The Context is canceled as soon as the write-side of the stream is closed.
+ // This happens when Close() or CancelWrite() is called, or when the peer
+ // cancels the read-side of their stream.
+ Context() context.Context
+ // SetWriteDeadline sets the deadline for future Write calls
+ // and any currently-blocked Write call.
+ // Even if write times out, it may return n > 0, indicating that
+ // some data was successfully written.
+ // A zero value for t means Write will not time out.
+ SetWriteDeadline(t time.Time) error
+}
+
+// A Connection is a QUIC connection between two peers.
+// Calls to the connection (and to streams) can return the following types of errors:
+// * ApplicationError: for errors triggered by the application running on top of QUIC
+// * TransportError: for errors triggered by the QUIC transport (in many cases a misbehaving peer)
+// * IdleTimeoutError: when the peer goes away unexpectedly (this is a net.Error timeout error)
+// * HandshakeTimeoutError: when the cryptographic handshake takes too long (this is a net.Error timeout error)
+// * StatelessResetError: when we receive a stateless reset (this is a net.Error temporary error)
+// * VersionNegotiationError: returned by the client, when there's no version overlap between the peers
+type Connection interface {
+ // AcceptStream returns the next stream opened by the peer, blocking until one is available.
+ // If the connection was closed due to a timeout, the error satisfies
+ // the net.Error interface, and Timeout() will be true.
+ AcceptStream(context.Context) (Stream, error)
+ // AcceptUniStream returns the next unidirectional stream opened by the peer, blocking until one is available.
+ // If the connection was closed due to a timeout, the error satisfies
+ // the net.Error interface, and Timeout() will be true.
+ AcceptUniStream(context.Context) (ReceiveStream, error)
+ // OpenStream opens a new bidirectional QUIC stream.
+ // There is no signaling to the peer about new streams:
+ // The peer can only accept the stream after data has been sent on the stream.
+ // If the error is non-nil, it satisfies the net.Error interface.
+ // When reaching the peer's stream limit, err.Temporary() will be true.
+ // If the connection was closed due to a timeout, Timeout() will be true.
+ OpenStream() (Stream, error)
+ // OpenStreamSync opens a new bidirectional QUIC stream.
+ // It blocks until a new stream can be opened.
+ // If the error is non-nil, it satisfies the net.Error interface.
+ // If the connection was closed due to a timeout, Timeout() will be true.
+ OpenStreamSync(context.Context) (Stream, error)
+ // OpenUniStream opens a new outgoing unidirectional QUIC stream.
+ // If the error is non-nil, it satisfies the net.Error interface.
+ // When reaching the peer's stream limit, Temporary() will be true.
+ // If the connection was closed due to a timeout, Timeout() will be true.
+ OpenUniStream() (SendStream, error)
+ // OpenUniStreamSync opens a new outgoing unidirectional QUIC stream.
+ // It blocks until a new stream can be opened.
+ // If the error is non-nil, it satisfies the net.Error interface.
+ // If the connection was closed due to a timeout, Timeout() will be true.
+ OpenUniStreamSync(context.Context) (SendStream, error)
+ // LocalAddr returns the local address.
+ LocalAddr() net.Addr
+ // RemoteAddr returns the address of the peer.
+ RemoteAddr() net.Addr
+ // CloseWithError closes the connection with an error.
+ // The error string will be sent to the peer.
+ CloseWithError(ApplicationErrorCode, string) error
+ // Context returns a context that is cancelled when the connection is closed.
+ Context() context.Context
+ // ConnectionState returns basic details about the QUIC connection.
+ // Warning: This API should not be considered stable and might change soon.
+ ConnectionState() ConnectionState
+
+ // SendMessage sends a message as a datagram, as specified in RFC 9221.
+ SendMessage([]byte) error
+ // ReceiveMessage gets a message received in a datagram, as specified in RFC 9221.
+ ReceiveMessage() ([]byte, error)
+}
+
+// An EarlyConnection is a connection that is handshaking.
+// Data sent during the handshake is encrypted using the forward secure keys.
+// When using client certificates, the client's identity is only verified
+// after completion of the handshake.
+type EarlyConnection interface {
+ Connection
+
+ // HandshakeComplete blocks until the handshake completes (or fails).
+ // Data sent before completion of the handshake is encrypted with 1-RTT keys.
+ // Note that the client's identity hasn't been verified yet.
+ HandshakeComplete() context.Context
+
+ NextConnection() Connection
+}
+
+// StatelessResetKey is a key used to derive stateless reset tokens.
+type StatelessResetKey [32]byte
+
+// A ConnectionID is a QUIC Connection ID, as defined in RFC 9000.
+// It is not able to handle QUIC Connection IDs longer than 20 bytes,
+// as they are allowed by RFC 8999.
+type ConnectionID = protocol.ConnectionID
+
+// ConnectionIDFromBytes interprets b as a Connection ID. It panics if b is
+// longer than 20 bytes.
+func ConnectionIDFromBytes(b []byte) ConnectionID {
+ return protocol.ParseConnectionID(b)
+}
+
+// A ConnectionIDGenerator is an interface that allows clients to implement their own format
+// for the Connection IDs that servers/clients use as SrcConnectionID in QUIC packets.
+//
+// Connection IDs generated by an implementation should always produce IDs of constant size.
+type ConnectionIDGenerator interface {
+ // GenerateConnectionID generates a new ConnectionID.
+ // Generated ConnectionIDs should be unique and observers should not be able to correlate two ConnectionIDs.
+ GenerateConnectionID() (ConnectionID, error)
+
+ // ConnectionIDLen tells what is the length of the ConnectionIDs generated by the implementation of
+ // this interface.
+ // Effectively, this means that implementations of ConnectionIDGenerator must always return constant-size
+ // connection IDs. Valid lengths are between 0 and 20 and calls to GenerateConnectionID.
+ // 0-length ConnectionsIDs can be used when an endpoint (server or client) does not require multiplexing connections
+ // in the presence of a connection migration environment.
+ ConnectionIDLen() int
+}
+
+// Config contains all configuration data needed for a QUIC server or client.
+type Config struct {
+ // The QUIC versions that can be negotiated.
+ // If not set, it uses all versions available.
+ Versions []VersionNumber
+ // The length of the connection ID in bytes.
+ // It can be 0, or any value between 4 and 18.
+ // If not set, the interpretation depends on where the Config is used:
+ // If used for dialing an address, a 0 byte connection ID will be used.
+ // If used for a server, or dialing on a packet conn, a 4 byte connection ID will be used.
+ // When dialing on a packet conn, the ConnectionIDLength value must be the same for every Dial call.
+ ConnectionIDLength int
+ // An optional ConnectionIDGenerator to be used for ConnectionIDs generated during the lifecycle of a QUIC connection.
+ // The goal is to give some control on how connection IDs, which can be useful in some scenarios, in particular for servers.
+ // By default, if not provided, random connection IDs with the length given by ConnectionIDLength is used.
+ // Otherwise, if one is provided, then ConnectionIDLength is ignored.
+ ConnectionIDGenerator ConnectionIDGenerator
+ // HandshakeIdleTimeout is the idle timeout before completion of the handshake.
+ // Specifically, if we don't receive any packet from the peer within this time, the connection attempt is aborted.
+ // If this value is zero, the timeout is set to 5 seconds.
+ HandshakeIdleTimeout time.Duration
+ // MaxIdleTimeout is the maximum duration that may pass without any incoming network activity.
+ // The actual value for the idle timeout is the minimum of this value and the peer's.
+ // This value only applies after the handshake has completed.
+ // If the timeout is exceeded, the connection is closed.
+ // If this value is zero, the timeout is set to 30 seconds.
+ MaxIdleTimeout time.Duration
+ // RequireAddressValidation determines if a QUIC Retry packet is sent.
+ // This allows the server to verify the client's address, at the cost of increasing the handshake latency by 1 RTT.
+ // See https://datatracker.ietf.org/doc/html/rfc9000#section-8 for details.
+ // If not set, every client is forced to prove its remote address.
+ RequireAddressValidation func(net.Addr) bool
+ // MaxRetryTokenAge is the maximum age of a Retry token.
+ // If not set, it defaults to 5 seconds. Only valid for a server.
+ MaxRetryTokenAge time.Duration
+ // MaxTokenAge is the maximum age of the token presented during the handshake,
+ // for tokens that were issued on a previous connection.
+ // If not set, it defaults to 24 hours. Only valid for a server.
+ MaxTokenAge time.Duration
+ // The TokenStore stores tokens received from the server.
+ // Tokens are used to skip address validation on future connection attempts.
+ // The key used to store tokens is the ServerName from the tls.Config, if set
+ // otherwise the token is associated with the server's IP address.
+ TokenStore TokenStore
+ // InitialStreamReceiveWindow is the initial size of the stream-level flow control window for receiving data.
+ // If the application is consuming data quickly enough, the flow control auto-tuning algorithm
+ // will increase the window up to MaxStreamReceiveWindow.
+ // If this value is zero, it will default to 512 KB.
+ InitialStreamReceiveWindow uint64
+ // MaxStreamReceiveWindow is the maximum stream-level flow control window for receiving data.
+ // If this value is zero, it will default to 6 MB.
+ MaxStreamReceiveWindow uint64
+ // InitialConnectionReceiveWindow is the initial size of the stream-level flow control window for receiving data.
+ // If the application is consuming data quickly enough, the flow control auto-tuning algorithm
+ // will increase the window up to MaxConnectionReceiveWindow.
+ // If this value is zero, it will default to 512 KB.
+ InitialConnectionReceiveWindow uint64
+ // MaxConnectionReceiveWindow is the connection-level flow control window for receiving data.
+ // If this value is zero, it will default to 15 MB.
+ MaxConnectionReceiveWindow uint64
+ // AllowConnectionWindowIncrease is called every time the connection flow controller attempts
+ // to increase the connection flow control window.
+ // If set, the caller can prevent an increase of the window. Typically, it would do so to
+ // limit the memory usage.
+ // To avoid deadlocks, it is not valid to call other functions on the connection or on streams
+ // in this callback.
+ AllowConnectionWindowIncrease func(conn Connection, delta uint64) bool
+ // MaxIncomingStreams is the maximum number of concurrent bidirectional streams that a peer is allowed to open.
+ // Values above 2^60 are invalid.
+ // If not set, it will default to 100.
+ // If set to a negative value, it doesn't allow any bidirectional streams.
+ MaxIncomingStreams int64
+ // MaxIncomingUniStreams is the maximum number of concurrent unidirectional streams that a peer is allowed to open.
+ // Values above 2^60 are invalid.
+ // If not set, it will default to 100.
+ // If set to a negative value, it doesn't allow any unidirectional streams.
+ MaxIncomingUniStreams int64
+ // The StatelessResetKey is used to generate stateless reset tokens.
+ // If no key is configured, sending of stateless resets is disabled.
+ StatelessResetKey *StatelessResetKey
+ // KeepAlivePeriod defines whether this peer will periodically send a packet to keep the connection alive.
+ // If set to 0, then no keep alive is sent. Otherwise, the keep alive is sent on that period (or at most
+ // every half of MaxIdleTimeout, whichever is smaller).
+ KeepAlivePeriod time.Duration
+ // DisablePathMTUDiscovery disables Path MTU Discovery (RFC 8899).
+ // Packets will then be at most 1252 (IPv4) / 1232 (IPv6) bytes in size.
+ // Note that if Path MTU discovery is causing issues on your system, please open a new issue
+ DisablePathMTUDiscovery bool
+ // DisableVersionNegotiationPackets disables the sending of Version Negotiation packets.
+ // This can be useful if version information is exchanged out-of-band.
+ // It has no effect for a client.
+ DisableVersionNegotiationPackets bool
+ // Allow0RTT allows the application to decide if a 0-RTT connection attempt should be accepted.
+ // When set, 0-RTT is enabled. When not set, 0-RTT is disabled.
+ // Only valid for the server.
+ // Warning: This API should not be considered stable and might change soon.
+ Allow0RTT func(net.Addr) bool
+ // Enable QUIC datagram support (RFC 9221).
+ EnableDatagrams bool
+ Tracer logging.Tracer
+}
+
+// ConnectionState records basic details about a QUIC connection
+type ConnectionState struct {
+ TLS handshake.ConnectionState
+ SupportsDatagrams bool
+ Version VersionNumber
+}
+
+// A Listener for incoming QUIC connections
+type Listener interface {
+ // Close the server. All active connections will be closed.
+ Close() error
+ // Addr returns the local network addr that the server is listening on.
+ Addr() net.Addr
+ // Accept returns new connections. It should be called in a loop.
+ Accept(context.Context) (Connection, error)
+}
+
+// An EarlyListener listens for incoming QUIC connections,
+// and returns them before the handshake completes.
+type EarlyListener interface {
+ // Close the server. All active connections will be closed.
+ Close() error
+ // Addr returns the local network addr that the server is listening on.
+ Addr() net.Addr
+ // Accept returns new early connections. It should be called in a loop.
+ Accept(context.Context) (EarlyConnection, error)
+}
diff --git a/vendor/github.com/quic-go/quic-go/internal/ackhandler/ack_eliciting.go b/vendor/github.com/quic-go/quic-go/internal/ackhandler/ack_eliciting.go
new file mode 100644
index 0000000000..4bab419013
--- /dev/null
+++ b/vendor/github.com/quic-go/quic-go/internal/ackhandler/ack_eliciting.go
@@ -0,0 +1,20 @@
+package ackhandler
+
+import "github.com/quic-go/quic-go/internal/wire"
+
+// IsFrameAckEliciting returns true if the frame is ack-eliciting.
+func IsFrameAckEliciting(f wire.Frame) bool {
+ _, isAck := f.(*wire.AckFrame)
+ _, isConnectionClose := f.(*wire.ConnectionCloseFrame)
+ return !isAck && !isConnectionClose
+}
+
+// HasAckElicitingFrames returns true if at least one frame is ack-eliciting.
+func HasAckElicitingFrames(fs []*Frame) bool {
+ for _, f := range fs {
+ if IsFrameAckEliciting(f.Frame) {
+ return true
+ }
+ }
+ return false
+}
diff --git a/vendor/github.com/quic-go/quic-go/internal/ackhandler/ackhandler.go b/vendor/github.com/quic-go/quic-go/internal/ackhandler/ackhandler.go
new file mode 100644
index 0000000000..2c7cc4fcf0
--- /dev/null
+++ b/vendor/github.com/quic-go/quic-go/internal/ackhandler/ackhandler.go
@@ -0,0 +1,23 @@
+package ackhandler
+
+import (
+ "github.com/quic-go/quic-go/internal/protocol"
+ "github.com/quic-go/quic-go/internal/utils"
+ "github.com/quic-go/quic-go/logging"
+)
+
+// NewAckHandler creates a new SentPacketHandler and a new ReceivedPacketHandler.
+// clientAddressValidated indicates whether the address was validated beforehand by an address validation token.
+// clientAddressValidated has no effect for a client.
+func NewAckHandler(
+ initialPacketNumber protocol.PacketNumber,
+ initialMaxDatagramSize protocol.ByteCount,
+ rttStats *utils.RTTStats,
+ clientAddressValidated bool,
+ pers protocol.Perspective,
+ tracer logging.ConnectionTracer,
+ logger utils.Logger,
+) (SentPacketHandler, ReceivedPacketHandler) {
+ sph := newSentPacketHandler(initialPacketNumber, initialMaxDatagramSize, rttStats, clientAddressValidated, pers, tracer, logger)
+ return sph, newReceivedPacketHandler(sph, rttStats, logger)
+}
diff --git a/vendor/github.com/quic-go/quic-go/internal/ackhandler/frame.go b/vendor/github.com/quic-go/quic-go/internal/ackhandler/frame.go
new file mode 100644
index 0000000000..deb23cfcb1
--- /dev/null
+++ b/vendor/github.com/quic-go/quic-go/internal/ackhandler/frame.go
@@ -0,0 +1,29 @@
+package ackhandler
+
+import (
+ "sync"
+
+ "github.com/quic-go/quic-go/internal/wire"
+)
+
+type Frame struct {
+ wire.Frame // nil if the frame has already been acknowledged in another packet
+ OnLost func(wire.Frame)
+ OnAcked func(wire.Frame)
+}
+
+var framePool = sync.Pool{New: func() any { return &Frame{} }}
+
+func GetFrame() *Frame {
+ f := framePool.Get().(*Frame)
+ f.OnLost = nil
+ f.OnAcked = nil
+ return f
+}
+
+func putFrame(f *Frame) {
+ f.Frame = nil
+ f.OnLost = nil
+ f.OnAcked = nil
+ framePool.Put(f)
+}
diff --git a/vendor/github.com/quic-go/quic-go/internal/ackhandler/interfaces.go b/vendor/github.com/quic-go/quic-go/internal/ackhandler/interfaces.go
new file mode 100644
index 0000000000..5924f84bda
--- /dev/null
+++ b/vendor/github.com/quic-go/quic-go/internal/ackhandler/interfaces.go
@@ -0,0 +1,52 @@
+package ackhandler
+
+import (
+ "time"
+
+ "github.com/quic-go/quic-go/internal/protocol"
+ "github.com/quic-go/quic-go/internal/wire"
+)
+
+// SentPacketHandler handles ACKs received for outgoing packets
+type SentPacketHandler interface {
+ // SentPacket may modify the packet
+ SentPacket(packet *Packet)
+ ReceivedAck(ackFrame *wire.AckFrame, encLevel protocol.EncryptionLevel, recvTime time.Time) (bool /* 1-RTT packet acked */, error)
+ ReceivedBytes(protocol.ByteCount)
+ DropPackets(protocol.EncryptionLevel)
+ ResetForRetry() error
+ SetHandshakeConfirmed()
+
+ // The SendMode determines if and what kind of packets can be sent.
+ SendMode() SendMode
+ // TimeUntilSend is the time when the next packet should be sent.
+ // It is used for pacing packets.
+ TimeUntilSend() time.Time
+ // HasPacingBudget says if the pacer allows sending of a (full size) packet at this moment.
+ HasPacingBudget() bool
+ SetMaxDatagramSize(count protocol.ByteCount)
+
+ // only to be called once the handshake is complete
+ QueueProbePacket(protocol.EncryptionLevel) bool /* was a packet queued */
+
+ PeekPacketNumber(protocol.EncryptionLevel) (protocol.PacketNumber, protocol.PacketNumberLen)
+ PopPacketNumber(protocol.EncryptionLevel) protocol.PacketNumber
+
+ GetLossDetectionTimeout() time.Time
+ OnLossDetectionTimeout() error
+}
+
+type sentPacketTracker interface {
+ GetLowestPacketNotConfirmedAcked() protocol.PacketNumber
+ ReceivedPacket(protocol.EncryptionLevel)
+}
+
+// ReceivedPacketHandler handles ACKs needed to send for incoming packets
+type ReceivedPacketHandler interface {
+ IsPotentiallyDuplicate(protocol.PacketNumber, protocol.EncryptionLevel) bool
+ ReceivedPacket(pn protocol.PacketNumber, ecn protocol.ECN, encLevel protocol.EncryptionLevel, rcvTime time.Time, shouldInstigateAck bool) error
+ DropPackets(protocol.EncryptionLevel)
+
+ GetAlarmTimeout() time.Time
+ GetAckFrame(encLevel protocol.EncryptionLevel, onlyIfQueued bool) *wire.AckFrame
+}
diff --git a/vendor/github.com/quic-go/quic-go/internal/ackhandler/mockgen.go b/vendor/github.com/quic-go/quic-go/internal/ackhandler/mockgen.go
new file mode 100644
index 0000000000..366e5520da
--- /dev/null
+++ b/vendor/github.com/quic-go/quic-go/internal/ackhandler/mockgen.go
@@ -0,0 +1,3 @@
+package ackhandler
+
+//go:generate sh -c "../../mockgen_private.sh ackhandler mock_sent_packet_tracker_test.go github.com/quic-go/quic-go/internal/ackhandler sentPacketTracker"
diff --git a/vendor/github.com/quic-go/quic-go/internal/ackhandler/packet.go b/vendor/github.com/quic-go/quic-go/internal/ackhandler/packet.go
new file mode 100644
index 0000000000..394ee40a98
--- /dev/null
+++ b/vendor/github.com/quic-go/quic-go/internal/ackhandler/packet.go
@@ -0,0 +1,55 @@
+package ackhandler
+
+import (
+ "sync"
+ "time"
+
+ "github.com/quic-go/quic-go/internal/protocol"
+)
+
+// A Packet is a packet
+type Packet struct {
+ PacketNumber protocol.PacketNumber
+ Frames []*Frame
+ LargestAcked protocol.PacketNumber // InvalidPacketNumber if the packet doesn't contain an ACK
+ Length protocol.ByteCount
+ EncryptionLevel protocol.EncryptionLevel
+ SendTime time.Time
+
+ IsPathMTUProbePacket bool // We don't report the loss of Path MTU probe packets to the congestion controller.
+
+ includedInBytesInFlight bool
+ declaredLost bool
+ skippedPacket bool
+}
+
+func (p *Packet) outstanding() bool {
+ return !p.declaredLost && !p.skippedPacket && !p.IsPathMTUProbePacket
+}
+
+var packetPool = sync.Pool{New: func() any { return &Packet{} }}
+
+func GetPacket() *Packet {
+ p := packetPool.Get().(*Packet)
+ p.PacketNumber = 0
+ p.Frames = nil
+ p.LargestAcked = 0
+ p.Length = 0
+ p.EncryptionLevel = protocol.EncryptionLevel(0)
+ p.SendTime = time.Time{}
+ p.IsPathMTUProbePacket = false
+ p.includedInBytesInFlight = false
+ p.declaredLost = false
+ p.skippedPacket = false
+ return p
+}
+
+// We currently only return Packets back into the pool when they're acknowledged (not when they're lost).
+// This simplifies the code, and gives the vast majority of the performance benefit we can gain from using the pool.
+func putPacket(p *Packet) {
+ for _, f := range p.Frames {
+ putFrame(f)
+ }
+ p.Frames = nil
+ packetPool.Put(p)
+}
diff --git a/vendor/github.com/quic-go/quic-go/internal/ackhandler/packet_number_generator.go b/vendor/github.com/quic-go/quic-go/internal/ackhandler/packet_number_generator.go
new file mode 100644
index 0000000000..9cf20a0b00
--- /dev/null
+++ b/vendor/github.com/quic-go/quic-go/internal/ackhandler/packet_number_generator.go
@@ -0,0 +1,76 @@
+package ackhandler
+
+import (
+ "github.com/quic-go/quic-go/internal/protocol"
+ "github.com/quic-go/quic-go/internal/utils"
+)
+
+type packetNumberGenerator interface {
+ Peek() protocol.PacketNumber
+ Pop() protocol.PacketNumber
+}
+
+type sequentialPacketNumberGenerator struct {
+ next protocol.PacketNumber
+}
+
+var _ packetNumberGenerator = &sequentialPacketNumberGenerator{}
+
+func newSequentialPacketNumberGenerator(initial protocol.PacketNumber) packetNumberGenerator {
+ return &sequentialPacketNumberGenerator{next: initial}
+}
+
+func (p *sequentialPacketNumberGenerator) Peek() protocol.PacketNumber {
+ return p.next
+}
+
+func (p *sequentialPacketNumberGenerator) Pop() protocol.PacketNumber {
+ next := p.next
+ p.next++
+ return next
+}
+
+// The skippingPacketNumberGenerator generates the packet number for the next packet
+// it randomly skips a packet number every averagePeriod packets (on average).
+// It is guaranteed to never skip two consecutive packet numbers.
+type skippingPacketNumberGenerator struct {
+ period protocol.PacketNumber
+ maxPeriod protocol.PacketNumber
+
+ next protocol.PacketNumber
+ nextToSkip protocol.PacketNumber
+
+ rng utils.Rand
+}
+
+var _ packetNumberGenerator = &skippingPacketNumberGenerator{}
+
+func newSkippingPacketNumberGenerator(initial, initialPeriod, maxPeriod protocol.PacketNumber) packetNumberGenerator {
+ g := &skippingPacketNumberGenerator{
+ next: initial,
+ period: initialPeriod,
+ maxPeriod: maxPeriod,
+ }
+ g.generateNewSkip()
+ return g
+}
+
+func (p *skippingPacketNumberGenerator) Peek() protocol.PacketNumber {
+ return p.next
+}
+
+func (p *skippingPacketNumberGenerator) Pop() protocol.PacketNumber {
+ next := p.next
+ p.next++ // generate a new packet number for the next packet
+ if p.next == p.nextToSkip {
+ p.next++
+ p.generateNewSkip()
+ }
+ return next
+}
+
+func (p *skippingPacketNumberGenerator) generateNewSkip() {
+ // make sure that there are never two consecutive packet numbers that are skipped
+ p.nextToSkip = p.next + 2 + protocol.PacketNumber(p.rng.Int31n(int32(2*p.period)))
+ p.period = utils.Min(2*p.period, p.maxPeriod)
+}
diff --git a/vendor/github.com/quic-go/quic-go/internal/ackhandler/received_packet_handler.go b/vendor/github.com/quic-go/quic-go/internal/ackhandler/received_packet_handler.go
new file mode 100644
index 0000000000..3675694f4f
--- /dev/null
+++ b/vendor/github.com/quic-go/quic-go/internal/ackhandler/received_packet_handler.go
@@ -0,0 +1,137 @@
+package ackhandler
+
+import (
+ "fmt"
+ "time"
+
+ "github.com/quic-go/quic-go/internal/protocol"
+ "github.com/quic-go/quic-go/internal/utils"
+ "github.com/quic-go/quic-go/internal/wire"
+)
+
+type receivedPacketHandler struct {
+ sentPackets sentPacketTracker
+
+ initialPackets *receivedPacketTracker
+ handshakePackets *receivedPacketTracker
+ appDataPackets *receivedPacketTracker
+
+ lowest1RTTPacket protocol.PacketNumber
+}
+
+var _ ReceivedPacketHandler = &receivedPacketHandler{}
+
+func newReceivedPacketHandler(
+ sentPackets sentPacketTracker,
+ rttStats *utils.RTTStats,
+ logger utils.Logger,
+) ReceivedPacketHandler {
+ return &receivedPacketHandler{
+ sentPackets: sentPackets,
+ initialPackets: newReceivedPacketTracker(rttStats, logger),
+ handshakePackets: newReceivedPacketTracker(rttStats, logger),
+ appDataPackets: newReceivedPacketTracker(rttStats, logger),
+ lowest1RTTPacket: protocol.InvalidPacketNumber,
+ }
+}
+
+func (h *receivedPacketHandler) ReceivedPacket(
+ pn protocol.PacketNumber,
+ ecn protocol.ECN,
+ encLevel protocol.EncryptionLevel,
+ rcvTime time.Time,
+ shouldInstigateAck bool,
+) error {
+ h.sentPackets.ReceivedPacket(encLevel)
+ switch encLevel {
+ case protocol.EncryptionInitial:
+ return h.initialPackets.ReceivedPacket(pn, ecn, rcvTime, shouldInstigateAck)
+ case protocol.EncryptionHandshake:
+ return h.handshakePackets.ReceivedPacket(pn, ecn, rcvTime, shouldInstigateAck)
+ case protocol.Encryption0RTT:
+ if h.lowest1RTTPacket != protocol.InvalidPacketNumber && pn > h.lowest1RTTPacket {
+ return fmt.Errorf("received packet number %d on a 0-RTT packet after receiving %d on a 1-RTT packet", pn, h.lowest1RTTPacket)
+ }
+ return h.appDataPackets.ReceivedPacket(pn, ecn, rcvTime, shouldInstigateAck)
+ case protocol.Encryption1RTT:
+ if h.lowest1RTTPacket == protocol.InvalidPacketNumber || pn < h.lowest1RTTPacket {
+ h.lowest1RTTPacket = pn
+ }
+ if err := h.appDataPackets.ReceivedPacket(pn, ecn, rcvTime, shouldInstigateAck); err != nil {
+ return err
+ }
+ h.appDataPackets.IgnoreBelow(h.sentPackets.GetLowestPacketNotConfirmedAcked())
+ return nil
+ default:
+ panic(fmt.Sprintf("received packet with unknown encryption level: %s", encLevel))
+ }
+}
+
+func (h *receivedPacketHandler) DropPackets(encLevel protocol.EncryptionLevel) {
+ //nolint:exhaustive // 1-RTT packet number space is never dropped.
+ switch encLevel {
+ case protocol.EncryptionInitial:
+ h.initialPackets = nil
+ case protocol.EncryptionHandshake:
+ h.handshakePackets = nil
+ case protocol.Encryption0RTT:
+ // Nothing to do here.
+ // If we are rejecting 0-RTT, no 0-RTT packets will have been decrypted.
+ default:
+ panic(fmt.Sprintf("Cannot drop keys for encryption level %s", encLevel))
+ }
+}
+
+func (h *receivedPacketHandler) GetAlarmTimeout() time.Time {
+ var initialAlarm, handshakeAlarm time.Time
+ if h.initialPackets != nil {
+ initialAlarm = h.initialPackets.GetAlarmTimeout()
+ }
+ if h.handshakePackets != nil {
+ handshakeAlarm = h.handshakePackets.GetAlarmTimeout()
+ }
+ oneRTTAlarm := h.appDataPackets.GetAlarmTimeout()
+ return utils.MinNonZeroTime(utils.MinNonZeroTime(initialAlarm, handshakeAlarm), oneRTTAlarm)
+}
+
+func (h *receivedPacketHandler) GetAckFrame(encLevel protocol.EncryptionLevel, onlyIfQueued bool) *wire.AckFrame {
+ var ack *wire.AckFrame
+ //nolint:exhaustive // 0-RTT packets can't contain ACK frames.
+ switch encLevel {
+ case protocol.EncryptionInitial:
+ if h.initialPackets != nil {
+ ack = h.initialPackets.GetAckFrame(onlyIfQueued)
+ }
+ case protocol.EncryptionHandshake:
+ if h.handshakePackets != nil {
+ ack = h.handshakePackets.GetAckFrame(onlyIfQueued)
+ }
+ case protocol.Encryption1RTT:
+ // 0-RTT packets can't contain ACK frames
+ return h.appDataPackets.GetAckFrame(onlyIfQueued)
+ default:
+ return nil
+ }
+ // For Initial and Handshake ACKs, the delay time is ignored by the receiver.
+ // Set it to 0 in order to save bytes.
+ if ack != nil {
+ ack.DelayTime = 0
+ }
+ return ack
+}
+
+func (h *receivedPacketHandler) IsPotentiallyDuplicate(pn protocol.PacketNumber, encLevel protocol.EncryptionLevel) bool {
+ switch encLevel {
+ case protocol.EncryptionInitial:
+ if h.initialPackets != nil {
+ return h.initialPackets.IsPotentiallyDuplicate(pn)
+ }
+ case protocol.EncryptionHandshake:
+ if h.handshakePackets != nil {
+ return h.handshakePackets.IsPotentiallyDuplicate(pn)
+ }
+ case protocol.Encryption0RTT, protocol.Encryption1RTT:
+ return h.appDataPackets.IsPotentiallyDuplicate(pn)
+ }
+ panic("unexpected encryption level")
+}
diff --git a/vendor/github.com/quic-go/quic-go/internal/ackhandler/received_packet_history.go b/vendor/github.com/quic-go/quic-go/internal/ackhandler/received_packet_history.go
new file mode 100644
index 0000000000..3143bfe120
--- /dev/null
+++ b/vendor/github.com/quic-go/quic-go/internal/ackhandler/received_packet_history.go
@@ -0,0 +1,151 @@
+package ackhandler
+
+import (
+ "sync"
+
+ "github.com/quic-go/quic-go/internal/protocol"
+ list "github.com/quic-go/quic-go/internal/utils/linkedlist"
+ "github.com/quic-go/quic-go/internal/wire"
+)
+
+// interval is an interval from one PacketNumber to the other
+type interval struct {
+ Start protocol.PacketNumber
+ End protocol.PacketNumber
+}
+
+var intervalElementPool sync.Pool
+
+func init() {
+ intervalElementPool = *list.NewPool[interval]()
+}
+
+// The receivedPacketHistory stores if a packet number has already been received.
+// It generates ACK ranges which can be used to assemble an ACK frame.
+// It does not store packet contents.
+type receivedPacketHistory struct {
+ ranges *list.List[interval]
+
+ deletedBelow protocol.PacketNumber
+}
+
+func newReceivedPacketHistory() *receivedPacketHistory {
+ return &receivedPacketHistory{
+ ranges: list.NewWithPool[interval](&intervalElementPool),
+ }
+}
+
+// ReceivedPacket registers a packet with PacketNumber p and updates the ranges
+func (h *receivedPacketHistory) ReceivedPacket(p protocol.PacketNumber) bool /* is a new packet (and not a duplicate / delayed packet) */ {
+ // ignore delayed packets, if we already deleted the range
+ if p < h.deletedBelow {
+ return false
+ }
+ isNew := h.addToRanges(p)
+ h.maybeDeleteOldRanges()
+ return isNew
+}
+
+func (h *receivedPacketHistory) addToRanges(p protocol.PacketNumber) bool /* is a new packet (and not a duplicate / delayed packet) */ {
+ if h.ranges.Len() == 0 {
+ h.ranges.PushBack(interval{Start: p, End: p})
+ return true
+ }
+
+ for el := h.ranges.Back(); el != nil; el = el.Prev() {
+ // p already included in an existing range. Nothing to do here
+ if p >= el.Value.Start && p <= el.Value.End {
+ return false
+ }
+
+ if el.Value.End == p-1 { // extend a range at the end
+ el.Value.End = p
+ return true
+ }
+ if el.Value.Start == p+1 { // extend a range at the beginning
+ el.Value.Start = p
+
+ prev := el.Prev()
+ if prev != nil && prev.Value.End+1 == el.Value.Start { // merge two ranges
+ prev.Value.End = el.Value.End
+ h.ranges.Remove(el)
+ }
+ return true
+ }
+
+ // create a new range at the end
+ if p > el.Value.End {
+ h.ranges.InsertAfter(interval{Start: p, End: p}, el)
+ return true
+ }
+ }
+
+ // create a new range at the beginning
+ h.ranges.InsertBefore(interval{Start: p, End: p}, h.ranges.Front())
+ return true
+}
+
+// Delete old ranges, if we're tracking more than 500 of them.
+// This is a DoS defense against a peer that sends us too many gaps.
+func (h *receivedPacketHistory) maybeDeleteOldRanges() {
+ for h.ranges.Len() > protocol.MaxNumAckRanges {
+ h.ranges.Remove(h.ranges.Front())
+ }
+}
+
+// DeleteBelow deletes all entries below (but not including) p
+func (h *receivedPacketHistory) DeleteBelow(p protocol.PacketNumber) {
+ if p < h.deletedBelow {
+ return
+ }
+ h.deletedBelow = p
+
+ nextEl := h.ranges.Front()
+ for el := h.ranges.Front(); nextEl != nil; el = nextEl {
+ nextEl = el.Next()
+
+ if el.Value.End < p { // delete a whole range
+ h.ranges.Remove(el)
+ } else if p > el.Value.Start && p <= el.Value.End {
+ el.Value.Start = p
+ return
+ } else { // no ranges affected. Nothing to do
+ return
+ }
+ }
+}
+
+// AppendAckRanges appends to a slice of all AckRanges that can be used in an AckFrame
+func (h *receivedPacketHistory) AppendAckRanges(ackRanges []wire.AckRange) []wire.AckRange {
+ if h.ranges.Len() > 0 {
+ for el := h.ranges.Back(); el != nil; el = el.Prev() {
+ ackRanges = append(ackRanges, wire.AckRange{Smallest: el.Value.Start, Largest: el.Value.End})
+ }
+ }
+ return ackRanges
+}
+
+func (h *receivedPacketHistory) GetHighestAckRange() wire.AckRange {
+ ackRange := wire.AckRange{}
+ if h.ranges.Len() > 0 {
+ r := h.ranges.Back().Value
+ ackRange.Smallest = r.Start
+ ackRange.Largest = r.End
+ }
+ return ackRange
+}
+
+func (h *receivedPacketHistory) IsPotentiallyDuplicate(p protocol.PacketNumber) bool {
+ if p < h.deletedBelow {
+ return true
+ }
+ for el := h.ranges.Back(); el != nil; el = el.Prev() {
+ if p > el.Value.End {
+ return false
+ }
+ if p <= el.Value.End && p >= el.Value.Start {
+ return true
+ }
+ }
+ return false
+}
diff --git a/vendor/github.com/quic-go/quic-go/internal/ackhandler/received_packet_tracker.go b/vendor/github.com/quic-go/quic-go/internal/ackhandler/received_packet_tracker.go
new file mode 100644
index 0000000000..7132ccaade
--- /dev/null
+++ b/vendor/github.com/quic-go/quic-go/internal/ackhandler/received_packet_tracker.go
@@ -0,0 +1,194 @@
+package ackhandler
+
+import (
+ "fmt"
+ "time"
+
+ "github.com/quic-go/quic-go/internal/protocol"
+ "github.com/quic-go/quic-go/internal/utils"
+ "github.com/quic-go/quic-go/internal/wire"
+)
+
+// number of ack-eliciting packets received before sending an ack.
+const packetsBeforeAck = 2
+
+type receivedPacketTracker struct {
+ largestObserved protocol.PacketNumber
+ ignoreBelow protocol.PacketNumber
+ largestObservedReceivedTime time.Time
+ ect0, ect1, ecnce uint64
+
+ packetHistory *receivedPacketHistory
+
+ maxAckDelay time.Duration
+ rttStats *utils.RTTStats
+
+ hasNewAck bool // true as soon as we received an ack-eliciting new packet
+ ackQueued bool // true once we received more than 2 (or later in the connection 10) ack-eliciting packets
+
+ ackElicitingPacketsReceivedSinceLastAck int
+ ackAlarm time.Time
+ lastAck *wire.AckFrame
+
+ logger utils.Logger
+}
+
+func newReceivedPacketTracker(
+ rttStats *utils.RTTStats,
+ logger utils.Logger,
+) *receivedPacketTracker {
+ return &receivedPacketTracker{
+ packetHistory: newReceivedPacketHistory(),
+ maxAckDelay: protocol.MaxAckDelay,
+ rttStats: rttStats,
+ logger: logger,
+ }
+}
+
+func (h *receivedPacketTracker) ReceivedPacket(packetNumber protocol.PacketNumber, ecn protocol.ECN, rcvTime time.Time, shouldInstigateAck bool) error {
+ if isNew := h.packetHistory.ReceivedPacket(packetNumber); !isNew {
+ return fmt.Errorf("recevedPacketTracker BUG: ReceivedPacket called for old / duplicate packet %d", packetNumber)
+ }
+
+ isMissing := h.isMissing(packetNumber)
+ if packetNumber >= h.largestObserved {
+ h.largestObserved = packetNumber
+ h.largestObservedReceivedTime = rcvTime
+ }
+
+ if shouldInstigateAck {
+ h.hasNewAck = true
+ }
+ if shouldInstigateAck {
+ h.maybeQueueAck(packetNumber, rcvTime, isMissing)
+ }
+ switch ecn {
+ case protocol.ECNNon:
+ case protocol.ECT0:
+ h.ect0++
+ case protocol.ECT1:
+ h.ect1++
+ case protocol.ECNCE:
+ h.ecnce++
+ }
+ return nil
+}
+
+// IgnoreBelow sets a lower limit for acknowledging packets.
+// Packets with packet numbers smaller than p will not be acked.
+func (h *receivedPacketTracker) IgnoreBelow(p protocol.PacketNumber) {
+ if p <= h.ignoreBelow {
+ return
+ }
+ h.ignoreBelow = p
+ h.packetHistory.DeleteBelow(p)
+ if h.logger.Debug() {
+ h.logger.Debugf("\tIgnoring all packets below %d.", p)
+ }
+}
+
+// isMissing says if a packet was reported missing in the last ACK.
+func (h *receivedPacketTracker) isMissing(p protocol.PacketNumber) bool {
+ if h.lastAck == nil || p < h.ignoreBelow {
+ return false
+ }
+ return p < h.lastAck.LargestAcked() && !h.lastAck.AcksPacket(p)
+}
+
+func (h *receivedPacketTracker) hasNewMissingPackets() bool {
+ if h.lastAck == nil {
+ return false
+ }
+ highestRange := h.packetHistory.GetHighestAckRange()
+ return highestRange.Smallest > h.lastAck.LargestAcked()+1 && highestRange.Len() == 1
+}
+
+// maybeQueueAck queues an ACK, if necessary.
+func (h *receivedPacketTracker) maybeQueueAck(pn protocol.PacketNumber, rcvTime time.Time, wasMissing bool) {
+ // always acknowledge the first packet
+ if h.lastAck == nil {
+ if !h.ackQueued {
+ h.logger.Debugf("\tQueueing ACK because the first packet should be acknowledged.")
+ }
+ h.ackQueued = true
+ return
+ }
+
+ if h.ackQueued {
+ return
+ }
+
+ h.ackElicitingPacketsReceivedSinceLastAck++
+
+ // Send an ACK if this packet was reported missing in an ACK sent before.
+ // Ack decimation with reordering relies on the timer to send an ACK, but if
+ // missing packets we reported in the previous ack, send an ACK immediately.
+ if wasMissing {
+ if h.logger.Debug() {
+ h.logger.Debugf("\tQueueing ACK because packet %d was missing before.", pn)
+ }
+ h.ackQueued = true
+ }
+
+ // send an ACK every 2 ack-eliciting packets
+ if h.ackElicitingPacketsReceivedSinceLastAck >= packetsBeforeAck {
+ if h.logger.Debug() {
+ h.logger.Debugf("\tQueueing ACK because packet %d packets were received after the last ACK (using initial threshold: %d).", h.ackElicitingPacketsReceivedSinceLastAck, packetsBeforeAck)
+ }
+ h.ackQueued = true
+ } else if h.ackAlarm.IsZero() {
+ if h.logger.Debug() {
+ h.logger.Debugf("\tSetting ACK timer to max ack delay: %s", h.maxAckDelay)
+ }
+ h.ackAlarm = rcvTime.Add(h.maxAckDelay)
+ }
+
+ // Queue an ACK if there are new missing packets to report.
+ if h.hasNewMissingPackets() {
+ h.logger.Debugf("\tQueuing ACK because there's a new missing packet to report.")
+ h.ackQueued = true
+ }
+
+ if h.ackQueued {
+ // cancel the ack alarm
+ h.ackAlarm = time.Time{}
+ }
+}
+
+func (h *receivedPacketTracker) GetAckFrame(onlyIfQueued bool) *wire.AckFrame {
+ if !h.hasNewAck {
+ return nil
+ }
+ now := time.Now()
+ if onlyIfQueued {
+ if !h.ackQueued && (h.ackAlarm.IsZero() || h.ackAlarm.After(now)) {
+ return nil
+ }
+ if h.logger.Debug() && !h.ackQueued && !h.ackAlarm.IsZero() {
+ h.logger.Debugf("Sending ACK because the ACK timer expired.")
+ }
+ }
+
+ ack := wire.GetAckFrame()
+ ack.DelayTime = utils.Max(0, now.Sub(h.largestObservedReceivedTime))
+ ack.ECT0 = h.ect0
+ ack.ECT1 = h.ect1
+ ack.ECNCE = h.ecnce
+ ack.AckRanges = h.packetHistory.AppendAckRanges(ack.AckRanges)
+
+ if h.lastAck != nil {
+ wire.PutAckFrame(h.lastAck)
+ }
+ h.lastAck = ack
+ h.ackAlarm = time.Time{}
+ h.ackQueued = false
+ h.hasNewAck = false
+ h.ackElicitingPacketsReceivedSinceLastAck = 0
+ return ack
+}
+
+func (h *receivedPacketTracker) GetAlarmTimeout() time.Time { return h.ackAlarm }
+
+func (h *receivedPacketTracker) IsPotentiallyDuplicate(pn protocol.PacketNumber) bool {
+ return h.packetHistory.IsPotentiallyDuplicate(pn)
+}
diff --git a/vendor/github.com/quic-go/quic-go/internal/ackhandler/send_mode.go b/vendor/github.com/quic-go/quic-go/internal/ackhandler/send_mode.go
new file mode 100644
index 0000000000..3d5fe560fc
--- /dev/null
+++ b/vendor/github.com/quic-go/quic-go/internal/ackhandler/send_mode.go
@@ -0,0 +1,40 @@
+package ackhandler
+
+import "fmt"
+
+// The SendMode says what kind of packets can be sent.
+type SendMode uint8
+
+const (
+ // SendNone means that no packets should be sent
+ SendNone SendMode = iota
+ // SendAck means an ACK-only packet should be sent
+ SendAck
+ // SendPTOInitial means that an Initial probe packet should be sent
+ SendPTOInitial
+ // SendPTOHandshake means that a Handshake probe packet should be sent
+ SendPTOHandshake
+ // SendPTOAppData means that an Application data probe packet should be sent
+ SendPTOAppData
+ // SendAny means that any packet should be sent
+ SendAny
+)
+
+func (s SendMode) String() string {
+ switch s {
+ case SendNone:
+ return "none"
+ case SendAck:
+ return "ack"
+ case SendPTOInitial:
+ return "pto (Initial)"
+ case SendPTOHandshake:
+ return "pto (Handshake)"
+ case SendPTOAppData:
+ return "pto (Application Data)"
+ case SendAny:
+ return "any"
+ default:
+ return fmt.Sprintf("invalid send mode: %d", s)
+ }
+}
diff --git a/vendor/github.com/quic-go/quic-go/internal/ackhandler/sent_packet_handler.go b/vendor/github.com/quic-go/quic-go/internal/ackhandler/sent_packet_handler.go
new file mode 100644
index 0000000000..732bbc3a1d
--- /dev/null
+++ b/vendor/github.com/quic-go/quic-go/internal/ackhandler/sent_packet_handler.go
@@ -0,0 +1,861 @@
+package ackhandler
+
+import (
+ "errors"
+ "fmt"
+ "time"
+
+ "github.com/quic-go/quic-go/internal/congestion"
+ "github.com/quic-go/quic-go/internal/protocol"
+ "github.com/quic-go/quic-go/internal/qerr"
+ "github.com/quic-go/quic-go/internal/utils"
+ "github.com/quic-go/quic-go/internal/wire"
+ "github.com/quic-go/quic-go/logging"
+)
+
+const (
+ // Maximum reordering in time space before time based loss detection considers a packet lost.
+ // Specified as an RTT multiplier.
+ timeThreshold = 9.0 / 8
+ // Maximum reordering in packets before packet threshold loss detection considers a packet lost.
+ packetThreshold = 3
+ // Before validating the client's address, the server won't send more than 3x bytes than it received.
+ amplificationFactor = 3
+ // We use Retry packets to derive an RTT estimate. Make sure we don't set the RTT to a super low value yet.
+ minRTTAfterRetry = 5 * time.Millisecond
+ // The PTO duration uses exponential backoff, but is truncated to a maximum value, as allowed by RFC 8961, section 4.4.
+ maxPTODuration = 60 * time.Second
+)
+
+type packetNumberSpace struct {
+ history *sentPacketHistory
+ pns packetNumberGenerator
+
+ lossTime time.Time
+ lastAckElicitingPacketTime time.Time
+
+ largestAcked protocol.PacketNumber
+ largestSent protocol.PacketNumber
+}
+
+func newPacketNumberSpace(initialPN protocol.PacketNumber, skipPNs bool, rttStats *utils.RTTStats) *packetNumberSpace {
+ var pns packetNumberGenerator
+ if skipPNs {
+ pns = newSkippingPacketNumberGenerator(initialPN, protocol.SkipPacketInitialPeriod, protocol.SkipPacketMaxPeriod)
+ } else {
+ pns = newSequentialPacketNumberGenerator(initialPN)
+ }
+ return &packetNumberSpace{
+ history: newSentPacketHistory(rttStats),
+ pns: pns,
+ largestSent: protocol.InvalidPacketNumber,
+ largestAcked: protocol.InvalidPacketNumber,
+ }
+}
+
+type sentPacketHandler struct {
+ initialPackets *packetNumberSpace
+ handshakePackets *packetNumberSpace
+ appDataPackets *packetNumberSpace
+
+ // Do we know that the peer completed address validation yet?
+ // Always true for the server.
+ peerCompletedAddressValidation bool
+ bytesReceived protocol.ByteCount
+ bytesSent protocol.ByteCount
+ // Have we validated the peer's address yet?
+ // Always true for the client.
+ peerAddressValidated bool
+
+ handshakeConfirmed bool
+
+ // lowestNotConfirmedAcked is the lowest packet number that we sent an ACK for, but haven't received confirmation, that this ACK actually arrived
+ // example: we send an ACK for packets 90-100 with packet number 20
+ // once we receive an ACK from the peer for packet 20, the lowestNotConfirmedAcked is 101
+ // Only applies to the application-data packet number space.
+ lowestNotConfirmedAcked protocol.PacketNumber
+
+ ackedPackets []*Packet // to avoid allocations in detectAndRemoveAckedPackets
+
+ bytesInFlight protocol.ByteCount
+
+ congestion congestion.SendAlgorithmWithDebugInfos
+ rttStats *utils.RTTStats
+
+ // The number of times a PTO has been sent without receiving an ack.
+ ptoCount uint32
+ ptoMode SendMode
+ // The number of PTO probe packets that should be sent.
+ // Only applies to the application-data packet number space.
+ numProbesToSend int
+
+ // The alarm timeout
+ alarm time.Time
+
+ perspective protocol.Perspective
+
+ tracer logging.ConnectionTracer
+ logger utils.Logger
+}
+
+var (
+ _ SentPacketHandler = &sentPacketHandler{}
+ _ sentPacketTracker = &sentPacketHandler{}
+)
+
+// clientAddressValidated indicates whether the address was validated beforehand by an address validation token.
+// If the address was validated, the amplification limit doesn't apply. It has no effect for a client.
+func newSentPacketHandler(
+ initialPN protocol.PacketNumber,
+ initialMaxDatagramSize protocol.ByteCount,
+ rttStats *utils.RTTStats,
+ clientAddressValidated bool,
+ pers protocol.Perspective,
+ tracer logging.ConnectionTracer,
+ logger utils.Logger,
+) *sentPacketHandler {
+ congestion := congestion.NewCubicSender(
+ congestion.DefaultClock{},
+ rttStats,
+ initialMaxDatagramSize,
+ true, // use Reno
+ tracer,
+ )
+
+ return &sentPacketHandler{
+ peerCompletedAddressValidation: pers == protocol.PerspectiveServer,
+ peerAddressValidated: pers == protocol.PerspectiveClient || clientAddressValidated,
+ initialPackets: newPacketNumberSpace(initialPN, false, rttStats),
+ handshakePackets: newPacketNumberSpace(0, false, rttStats),
+ appDataPackets: newPacketNumberSpace(0, true, rttStats),
+ rttStats: rttStats,
+ congestion: congestion,
+ perspective: pers,
+ tracer: tracer,
+ logger: logger,
+ }
+}
+
+func (h *sentPacketHandler) DropPackets(encLevel protocol.EncryptionLevel) {
+ if h.perspective == protocol.PerspectiveClient && encLevel == protocol.EncryptionInitial {
+ // This function is called when the crypto setup seals a Handshake packet.
+ // If this Handshake packet is coalesced behind an Initial packet, we would drop the Initial packet number space
+ // before SentPacket() was called for that Initial packet.
+ return
+ }
+ h.dropPackets(encLevel)
+}
+
+func (h *sentPacketHandler) removeFromBytesInFlight(p *Packet) {
+ if p.includedInBytesInFlight {
+ if p.Length > h.bytesInFlight {
+ panic("negative bytes_in_flight")
+ }
+ h.bytesInFlight -= p.Length
+ p.includedInBytesInFlight = false
+ }
+}
+
+func (h *sentPacketHandler) dropPackets(encLevel protocol.EncryptionLevel) {
+ // The server won't await address validation after the handshake is confirmed.
+ // This applies even if we didn't receive an ACK for a Handshake packet.
+ if h.perspective == protocol.PerspectiveClient && encLevel == protocol.EncryptionHandshake {
+ h.peerCompletedAddressValidation = true
+ }
+ // remove outstanding packets from bytes_in_flight
+ if encLevel == protocol.EncryptionInitial || encLevel == protocol.EncryptionHandshake {
+ pnSpace := h.getPacketNumberSpace(encLevel)
+ pnSpace.history.Iterate(func(p *Packet) (bool, error) {
+ h.removeFromBytesInFlight(p)
+ return true, nil
+ })
+ }
+ // drop the packet history
+ //nolint:exhaustive // Not every packet number space can be dropped.
+ switch encLevel {
+ case protocol.EncryptionInitial:
+ h.initialPackets = nil
+ case protocol.EncryptionHandshake:
+ h.handshakePackets = nil
+ case protocol.Encryption0RTT:
+ // This function is only called when 0-RTT is rejected,
+ // and not when the client drops 0-RTT keys when the handshake completes.
+ // When 0-RTT is rejected, all application data sent so far becomes invalid.
+ // Delete the packets from the history and remove them from bytes_in_flight.
+ h.appDataPackets.history.Iterate(func(p *Packet) (bool, error) {
+ if p.EncryptionLevel != protocol.Encryption0RTT {
+ return false, nil
+ }
+ h.removeFromBytesInFlight(p)
+ h.appDataPackets.history.Remove(p.PacketNumber)
+ return true, nil
+ })
+ default:
+ panic(fmt.Sprintf("Cannot drop keys for encryption level %s", encLevel))
+ }
+ if h.tracer != nil && h.ptoCount != 0 {
+ h.tracer.UpdatedPTOCount(0)
+ }
+ h.ptoCount = 0
+ h.numProbesToSend = 0
+ h.ptoMode = SendNone
+ h.setLossDetectionTimer()
+}
+
+func (h *sentPacketHandler) ReceivedBytes(n protocol.ByteCount) {
+ wasAmplificationLimit := h.isAmplificationLimited()
+ h.bytesReceived += n
+ if wasAmplificationLimit && !h.isAmplificationLimited() {
+ h.setLossDetectionTimer()
+ }
+}
+
+func (h *sentPacketHandler) ReceivedPacket(l protocol.EncryptionLevel) {
+ if h.perspective == protocol.PerspectiveServer && l == protocol.EncryptionHandshake && !h.peerAddressValidated {
+ h.peerAddressValidated = true
+ h.setLossDetectionTimer()
+ }
+}
+
+func (h *sentPacketHandler) packetsInFlight() int {
+ packetsInFlight := h.appDataPackets.history.Len()
+ if h.handshakePackets != nil {
+ packetsInFlight += h.handshakePackets.history.Len()
+ }
+ if h.initialPackets != nil {
+ packetsInFlight += h.initialPackets.history.Len()
+ }
+ return packetsInFlight
+}
+
+func (h *sentPacketHandler) SentPacket(p *Packet) {
+ h.bytesSent += p.Length
+ // For the client, drop the Initial packet number space when the first Handshake packet is sent.
+ if h.perspective == protocol.PerspectiveClient && p.EncryptionLevel == protocol.EncryptionHandshake && h.initialPackets != nil {
+ h.dropPackets(protocol.EncryptionInitial)
+ }
+ isAckEliciting := h.sentPacketImpl(p)
+ if isAckEliciting {
+ h.getPacketNumberSpace(p.EncryptionLevel).history.SentAckElicitingPacket(p)
+ } else {
+ h.getPacketNumberSpace(p.EncryptionLevel).history.SentNonAckElicitingPacket(p.PacketNumber, p.EncryptionLevel, p.SendTime)
+ putPacket(p)
+ p = nil //nolint:ineffassign // This is just to be on the safe side.
+ }
+ if h.tracer != nil && isAckEliciting {
+ h.tracer.UpdatedMetrics(h.rttStats, h.congestion.GetCongestionWindow(), h.bytesInFlight, h.packetsInFlight())
+ }
+ if isAckEliciting || !h.peerCompletedAddressValidation {
+ h.setLossDetectionTimer()
+ }
+}
+
+func (h *sentPacketHandler) getPacketNumberSpace(encLevel protocol.EncryptionLevel) *packetNumberSpace {
+ switch encLevel {
+ case protocol.EncryptionInitial:
+ return h.initialPackets
+ case protocol.EncryptionHandshake:
+ return h.handshakePackets
+ case protocol.Encryption0RTT, protocol.Encryption1RTT:
+ return h.appDataPackets
+ default:
+ panic("invalid packet number space")
+ }
+}
+
+func (h *sentPacketHandler) sentPacketImpl(packet *Packet) bool /* is ack-eliciting */ {
+ pnSpace := h.getPacketNumberSpace(packet.EncryptionLevel)
+
+ if h.logger.Debug() && pnSpace.history.HasOutstandingPackets() {
+ for p := utils.Max(0, pnSpace.largestSent+1); p < packet.PacketNumber; p++ {
+ h.logger.Debugf("Skipping packet number %d", p)
+ }
+ }
+
+ pnSpace.largestSent = packet.PacketNumber
+ isAckEliciting := len(packet.Frames) > 0
+
+ if isAckEliciting {
+ pnSpace.lastAckElicitingPacketTime = packet.SendTime
+ packet.includedInBytesInFlight = true
+ h.bytesInFlight += packet.Length
+ if h.numProbesToSend > 0 {
+ h.numProbesToSend--
+ }
+ }
+ h.congestion.OnPacketSent(packet.SendTime, h.bytesInFlight, packet.PacketNumber, packet.Length, isAckEliciting)
+
+ return isAckEliciting
+}
+
+func (h *sentPacketHandler) ReceivedAck(ack *wire.AckFrame, encLevel protocol.EncryptionLevel, rcvTime time.Time) (bool /* contained 1-RTT packet */, error) {
+ pnSpace := h.getPacketNumberSpace(encLevel)
+
+ largestAcked := ack.LargestAcked()
+ if largestAcked > pnSpace.largestSent {
+ return false, &qerr.TransportError{
+ ErrorCode: qerr.ProtocolViolation,
+ ErrorMessage: "received ACK for an unsent packet",
+ }
+ }
+
+ pnSpace.largestAcked = utils.Max(pnSpace.largestAcked, largestAcked)
+
+ // Servers complete address validation when a protected packet is received.
+ if h.perspective == protocol.PerspectiveClient && !h.peerCompletedAddressValidation &&
+ (encLevel == protocol.EncryptionHandshake || encLevel == protocol.Encryption1RTT) {
+ h.peerCompletedAddressValidation = true
+ h.logger.Debugf("Peer doesn't await address validation any longer.")
+ // Make sure that the timer is reset, even if this ACK doesn't acknowledge any (ack-eliciting) packets.
+ h.setLossDetectionTimer()
+ }
+
+ priorInFlight := h.bytesInFlight
+ ackedPackets, err := h.detectAndRemoveAckedPackets(ack, encLevel)
+ if err != nil || len(ackedPackets) == 0 {
+ return false, err
+ }
+ // update the RTT, if the largest acked is newly acknowledged
+ if len(ackedPackets) > 0 {
+ if p := ackedPackets[len(ackedPackets)-1]; p.PacketNumber == ack.LargestAcked() {
+ // don't use the ack delay for Initial and Handshake packets
+ var ackDelay time.Duration
+ if encLevel == protocol.Encryption1RTT {
+ ackDelay = utils.Min(ack.DelayTime, h.rttStats.MaxAckDelay())
+ }
+ h.rttStats.UpdateRTT(rcvTime.Sub(p.SendTime), ackDelay, rcvTime)
+ if h.logger.Debug() {
+ h.logger.Debugf("\tupdated RTT: %s (σ: %s)", h.rttStats.SmoothedRTT(), h.rttStats.MeanDeviation())
+ }
+ h.congestion.MaybeExitSlowStart()
+ }
+ }
+ if err := h.detectLostPackets(rcvTime, encLevel); err != nil {
+ return false, err
+ }
+ var acked1RTTPacket bool
+ for _, p := range ackedPackets {
+ if p.includedInBytesInFlight && !p.declaredLost {
+ h.congestion.OnPacketAcked(p.PacketNumber, p.Length, priorInFlight, rcvTime)
+ }
+ if p.EncryptionLevel == protocol.Encryption1RTT {
+ acked1RTTPacket = true
+ }
+ h.removeFromBytesInFlight(p)
+ putPacket(p)
+ }
+ // After this point, we must not use ackedPackets any longer!
+ // We've already returned the buffers.
+ ackedPackets = nil //nolint:ineffassign // This is just to be on the safe side.
+
+ // Reset the pto_count unless the client is unsure if the server has validated the client's address.
+ if h.peerCompletedAddressValidation {
+ if h.tracer != nil && h.ptoCount != 0 {
+ h.tracer.UpdatedPTOCount(0)
+ }
+ h.ptoCount = 0
+ }
+ h.numProbesToSend = 0
+
+ if h.tracer != nil {
+ h.tracer.UpdatedMetrics(h.rttStats, h.congestion.GetCongestionWindow(), h.bytesInFlight, h.packetsInFlight())
+ }
+
+ pnSpace.history.DeleteOldPackets(rcvTime)
+ h.setLossDetectionTimer()
+ return acked1RTTPacket, nil
+}
+
+func (h *sentPacketHandler) GetLowestPacketNotConfirmedAcked() protocol.PacketNumber {
+ return h.lowestNotConfirmedAcked
+}
+
+// Packets are returned in ascending packet number order.
+func (h *sentPacketHandler) detectAndRemoveAckedPackets(ack *wire.AckFrame, encLevel protocol.EncryptionLevel) ([]*Packet, error) {
+ pnSpace := h.getPacketNumberSpace(encLevel)
+ h.ackedPackets = h.ackedPackets[:0]
+ ackRangeIndex := 0
+ lowestAcked := ack.LowestAcked()
+ largestAcked := ack.LargestAcked()
+ err := pnSpace.history.Iterate(func(p *Packet) (bool, error) {
+ // Ignore packets below the lowest acked
+ if p.PacketNumber < lowestAcked {
+ return true, nil
+ }
+ // Break after largest acked is reached
+ if p.PacketNumber > largestAcked {
+ return false, nil
+ }
+
+ if ack.HasMissingRanges() {
+ ackRange := ack.AckRanges[len(ack.AckRanges)-1-ackRangeIndex]
+
+ for p.PacketNumber > ackRange.Largest && ackRangeIndex < len(ack.AckRanges)-1 {
+ ackRangeIndex++
+ ackRange = ack.AckRanges[len(ack.AckRanges)-1-ackRangeIndex]
+ }
+
+ if p.PacketNumber < ackRange.Smallest { // packet not contained in ACK range
+ return true, nil
+ }
+ if p.PacketNumber > ackRange.Largest {
+ return false, fmt.Errorf("BUG: ackhandler would have acked wrong packet %d, while evaluating range %d -> %d", p.PacketNumber, ackRange.Smallest, ackRange.Largest)
+ }
+ }
+ if p.skippedPacket {
+ return false, &qerr.TransportError{
+ ErrorCode: qerr.ProtocolViolation,
+ ErrorMessage: fmt.Sprintf("received an ACK for skipped packet number: %d (%s)", p.PacketNumber, encLevel),
+ }
+ }
+ h.ackedPackets = append(h.ackedPackets, p)
+ return true, nil
+ })
+ if h.logger.Debug() && len(h.ackedPackets) > 0 {
+ pns := make([]protocol.PacketNumber, len(h.ackedPackets))
+ for i, p := range h.ackedPackets {
+ pns[i] = p.PacketNumber
+ }
+ h.logger.Debugf("\tnewly acked packets (%d): %d", len(pns), pns)
+ }
+
+ for _, p := range h.ackedPackets {
+ if p.LargestAcked != protocol.InvalidPacketNumber && encLevel == protocol.Encryption1RTT {
+ h.lowestNotConfirmedAcked = utils.Max(h.lowestNotConfirmedAcked, p.LargestAcked+1)
+ }
+
+ for _, f := range p.Frames {
+ if f.OnAcked != nil {
+ f.OnAcked(f.Frame)
+ }
+ }
+ if err := pnSpace.history.Remove(p.PacketNumber); err != nil {
+ return nil, err
+ }
+ if h.tracer != nil {
+ h.tracer.AcknowledgedPacket(encLevel, p.PacketNumber)
+ }
+ }
+
+ return h.ackedPackets, err
+}
+
+func (h *sentPacketHandler) getLossTimeAndSpace() (time.Time, protocol.EncryptionLevel) {
+ var encLevel protocol.EncryptionLevel
+ var lossTime time.Time
+
+ if h.initialPackets != nil {
+ lossTime = h.initialPackets.lossTime
+ encLevel = protocol.EncryptionInitial
+ }
+ if h.handshakePackets != nil && (lossTime.IsZero() || (!h.handshakePackets.lossTime.IsZero() && h.handshakePackets.lossTime.Before(lossTime))) {
+ lossTime = h.handshakePackets.lossTime
+ encLevel = protocol.EncryptionHandshake
+ }
+ if lossTime.IsZero() || (!h.appDataPackets.lossTime.IsZero() && h.appDataPackets.lossTime.Before(lossTime)) {
+ lossTime = h.appDataPackets.lossTime
+ encLevel = protocol.Encryption1RTT
+ }
+ return lossTime, encLevel
+}
+
+func (h *sentPacketHandler) getScaledPTO(includeMaxAckDelay bool) time.Duration {
+ pto := h.rttStats.PTO(includeMaxAckDelay) << h.ptoCount
+ if pto > maxPTODuration || pto <= 0 {
+ return maxPTODuration
+ }
+ return pto
+}
+
+// same logic as getLossTimeAndSpace, but for lastAckElicitingPacketTime instead of lossTime
+func (h *sentPacketHandler) getPTOTimeAndSpace() (pto time.Time, encLevel protocol.EncryptionLevel, ok bool) {
+ // We only send application data probe packets once the handshake is confirmed,
+ // because before that, we don't have the keys to decrypt ACKs sent in 1-RTT packets.
+ if !h.handshakeConfirmed && !h.hasOutstandingCryptoPackets() {
+ if h.peerCompletedAddressValidation {
+ return
+ }
+ t := time.Now().Add(h.getScaledPTO(false))
+ if h.initialPackets != nil {
+ return t, protocol.EncryptionInitial, true
+ }
+ return t, protocol.EncryptionHandshake, true
+ }
+
+ if h.initialPackets != nil {
+ encLevel = protocol.EncryptionInitial
+ if t := h.initialPackets.lastAckElicitingPacketTime; !t.IsZero() {
+ pto = t.Add(h.getScaledPTO(false))
+ }
+ }
+ if h.handshakePackets != nil && !h.handshakePackets.lastAckElicitingPacketTime.IsZero() {
+ t := h.handshakePackets.lastAckElicitingPacketTime.Add(h.getScaledPTO(false))
+ if pto.IsZero() || (!t.IsZero() && t.Before(pto)) {
+ pto = t
+ encLevel = protocol.EncryptionHandshake
+ }
+ }
+ if h.handshakeConfirmed && !h.appDataPackets.lastAckElicitingPacketTime.IsZero() {
+ t := h.appDataPackets.lastAckElicitingPacketTime.Add(h.getScaledPTO(true))
+ if pto.IsZero() || (!t.IsZero() && t.Before(pto)) {
+ pto = t
+ encLevel = protocol.Encryption1RTT
+ }
+ }
+ return pto, encLevel, true
+}
+
+func (h *sentPacketHandler) hasOutstandingCryptoPackets() bool {
+ if h.initialPackets != nil && h.initialPackets.history.HasOutstandingPackets() {
+ return true
+ }
+ if h.handshakePackets != nil && h.handshakePackets.history.HasOutstandingPackets() {
+ return true
+ }
+ return false
+}
+
+func (h *sentPacketHandler) hasOutstandingPackets() bool {
+ return h.appDataPackets.history.HasOutstandingPackets() || h.hasOutstandingCryptoPackets()
+}
+
+func (h *sentPacketHandler) setLossDetectionTimer() {
+ oldAlarm := h.alarm // only needed in case tracing is enabled
+ lossTime, encLevel := h.getLossTimeAndSpace()
+ if !lossTime.IsZero() {
+ // Early retransmit timer or time loss detection.
+ h.alarm = lossTime
+ if h.tracer != nil && h.alarm != oldAlarm {
+ h.tracer.SetLossTimer(logging.TimerTypeACK, encLevel, h.alarm)
+ }
+ return
+ }
+
+ // Cancel the alarm if amplification limited.
+ if h.isAmplificationLimited() {
+ h.alarm = time.Time{}
+ if !oldAlarm.IsZero() {
+ h.logger.Debugf("Canceling loss detection timer. Amplification limited.")
+ if h.tracer != nil {
+ h.tracer.LossTimerCanceled()
+ }
+ }
+ return
+ }
+
+ // Cancel the alarm if no packets are outstanding
+ if !h.hasOutstandingPackets() && h.peerCompletedAddressValidation {
+ h.alarm = time.Time{}
+ if !oldAlarm.IsZero() {
+ h.logger.Debugf("Canceling loss detection timer. No packets in flight.")
+ if h.tracer != nil {
+ h.tracer.LossTimerCanceled()
+ }
+ }
+ return
+ }
+
+ // PTO alarm
+ ptoTime, encLevel, ok := h.getPTOTimeAndSpace()
+ if !ok {
+ if !oldAlarm.IsZero() {
+ h.alarm = time.Time{}
+ h.logger.Debugf("Canceling loss detection timer. No PTO needed..")
+ if h.tracer != nil {
+ h.tracer.LossTimerCanceled()
+ }
+ }
+ return
+ }
+ h.alarm = ptoTime
+ if h.tracer != nil && h.alarm != oldAlarm {
+ h.tracer.SetLossTimer(logging.TimerTypePTO, encLevel, h.alarm)
+ }
+}
+
+func (h *sentPacketHandler) detectLostPackets(now time.Time, encLevel protocol.EncryptionLevel) error {
+ pnSpace := h.getPacketNumberSpace(encLevel)
+ pnSpace.lossTime = time.Time{}
+
+ maxRTT := float64(utils.Max(h.rttStats.LatestRTT(), h.rttStats.SmoothedRTT()))
+ lossDelay := time.Duration(timeThreshold * maxRTT)
+
+ // Minimum time of granularity before packets are deemed lost.
+ lossDelay = utils.Max(lossDelay, protocol.TimerGranularity)
+
+ // Packets sent before this time are deemed lost.
+ lostSendTime := now.Add(-lossDelay)
+
+ priorInFlight := h.bytesInFlight
+ return pnSpace.history.Iterate(func(p *Packet) (bool, error) {
+ if p.PacketNumber > pnSpace.largestAcked {
+ return false, nil
+ }
+ if p.declaredLost || p.skippedPacket {
+ return true, nil
+ }
+
+ var packetLost bool
+ if p.SendTime.Before(lostSendTime) {
+ packetLost = true
+ if h.logger.Debug() {
+ h.logger.Debugf("\tlost packet %d (time threshold)", p.PacketNumber)
+ }
+ if h.tracer != nil {
+ h.tracer.LostPacket(p.EncryptionLevel, p.PacketNumber, logging.PacketLossTimeThreshold)
+ }
+ } else if pnSpace.largestAcked >= p.PacketNumber+packetThreshold {
+ packetLost = true
+ if h.logger.Debug() {
+ h.logger.Debugf("\tlost packet %d (reordering threshold)", p.PacketNumber)
+ }
+ if h.tracer != nil {
+ h.tracer.LostPacket(p.EncryptionLevel, p.PacketNumber, logging.PacketLossReorderingThreshold)
+ }
+ } else if pnSpace.lossTime.IsZero() {
+ // Note: This conditional is only entered once per call
+ lossTime := p.SendTime.Add(lossDelay)
+ if h.logger.Debug() {
+ h.logger.Debugf("\tsetting loss timer for packet %d (%s) to %s (in %s)", p.PacketNumber, encLevel, lossDelay, lossTime)
+ }
+ pnSpace.lossTime = lossTime
+ }
+ if packetLost {
+ p = pnSpace.history.DeclareLost(p)
+ // the bytes in flight need to be reduced no matter if the frames in this packet will be retransmitted
+ h.removeFromBytesInFlight(p)
+ h.queueFramesForRetransmission(p)
+ if !p.IsPathMTUProbePacket {
+ h.congestion.OnPacketLost(p.PacketNumber, p.Length, priorInFlight)
+ }
+ }
+ return true, nil
+ })
+}
+
+func (h *sentPacketHandler) OnLossDetectionTimeout() error {
+ defer h.setLossDetectionTimer()
+ earliestLossTime, encLevel := h.getLossTimeAndSpace()
+ if !earliestLossTime.IsZero() {
+ if h.logger.Debug() {
+ h.logger.Debugf("Loss detection alarm fired in loss timer mode. Loss time: %s", earliestLossTime)
+ }
+ if h.tracer != nil {
+ h.tracer.LossTimerExpired(logging.TimerTypeACK, encLevel)
+ }
+ // Early retransmit or time loss detection
+ return h.detectLostPackets(time.Now(), encLevel)
+ }
+
+ // PTO
+ // When all outstanding are acknowledged, the alarm is canceled in
+ // setLossDetectionTimer. This doesn't reset the timer in the session though.
+ // When OnAlarm is called, we therefore need to make sure that there are
+ // actually packets outstanding.
+ if h.bytesInFlight == 0 && !h.peerCompletedAddressValidation {
+ h.ptoCount++
+ h.numProbesToSend++
+ if h.initialPackets != nil {
+ h.ptoMode = SendPTOInitial
+ } else if h.handshakePackets != nil {
+ h.ptoMode = SendPTOHandshake
+ } else {
+ return errors.New("sentPacketHandler BUG: PTO fired, but bytes_in_flight is 0 and Initial and Handshake already dropped")
+ }
+ return nil
+ }
+
+ _, encLevel, ok := h.getPTOTimeAndSpace()
+ if !ok {
+ return nil
+ }
+ if ps := h.getPacketNumberSpace(encLevel); !ps.history.HasOutstandingPackets() && !h.peerCompletedAddressValidation {
+ return nil
+ }
+ h.ptoCount++
+ if h.logger.Debug() {
+ h.logger.Debugf("Loss detection alarm for %s fired in PTO mode. PTO count: %d", encLevel, h.ptoCount)
+ }
+ if h.tracer != nil {
+ h.tracer.LossTimerExpired(logging.TimerTypePTO, encLevel)
+ h.tracer.UpdatedPTOCount(h.ptoCount)
+ }
+ h.numProbesToSend += 2
+ //nolint:exhaustive // We never arm a PTO timer for 0-RTT packets.
+ switch encLevel {
+ case protocol.EncryptionInitial:
+ h.ptoMode = SendPTOInitial
+ case protocol.EncryptionHandshake:
+ h.ptoMode = SendPTOHandshake
+ case protocol.Encryption1RTT:
+ // skip a packet number in order to elicit an immediate ACK
+ _ = h.PopPacketNumber(protocol.Encryption1RTT)
+ h.ptoMode = SendPTOAppData
+ default:
+ return fmt.Errorf("PTO timer in unexpected encryption level: %s", encLevel)
+ }
+ return nil
+}
+
+func (h *sentPacketHandler) GetLossDetectionTimeout() time.Time {
+ return h.alarm
+}
+
+func (h *sentPacketHandler) PeekPacketNumber(encLevel protocol.EncryptionLevel) (protocol.PacketNumber, protocol.PacketNumberLen) {
+ pnSpace := h.getPacketNumberSpace(encLevel)
+
+ var lowestUnacked protocol.PacketNumber
+ if p := pnSpace.history.FirstOutstanding(); p != nil {
+ lowestUnacked = p.PacketNumber
+ } else {
+ lowestUnacked = pnSpace.largestAcked + 1
+ }
+
+ pn := pnSpace.pns.Peek()
+ return pn, protocol.GetPacketNumberLengthForHeader(pn, lowestUnacked)
+}
+
+func (h *sentPacketHandler) PopPacketNumber(encLevel protocol.EncryptionLevel) protocol.PacketNumber {
+ return h.getPacketNumberSpace(encLevel).pns.Pop()
+}
+
+func (h *sentPacketHandler) SendMode() SendMode {
+ numTrackedPackets := h.appDataPackets.history.Len()
+ if h.initialPackets != nil {
+ numTrackedPackets += h.initialPackets.history.Len()
+ }
+ if h.handshakePackets != nil {
+ numTrackedPackets += h.handshakePackets.history.Len()
+ }
+
+ if h.isAmplificationLimited() {
+ h.logger.Debugf("Amplification window limited. Received %d bytes, already sent out %d bytes", h.bytesReceived, h.bytesSent)
+ return SendNone
+ }
+ // Don't send any packets if we're keeping track of the maximum number of packets.
+ // Note that since MaxOutstandingSentPackets is smaller than MaxTrackedSentPackets,
+ // we will stop sending out new data when reaching MaxOutstandingSentPackets,
+ // but still allow sending of retransmissions and ACKs.
+ if numTrackedPackets >= protocol.MaxTrackedSentPackets {
+ if h.logger.Debug() {
+ h.logger.Debugf("Limited by the number of tracked packets: tracking %d packets, maximum %d", numTrackedPackets, protocol.MaxTrackedSentPackets)
+ }
+ return SendNone
+ }
+ if h.numProbesToSend > 0 {
+ return h.ptoMode
+ }
+ // Only send ACKs if we're congestion limited.
+ if !h.congestion.CanSend(h.bytesInFlight) {
+ if h.logger.Debug() {
+ h.logger.Debugf("Congestion limited: bytes in flight %d, window %d", h.bytesInFlight, h.congestion.GetCongestionWindow())
+ }
+ return SendAck
+ }
+ if numTrackedPackets >= protocol.MaxOutstandingSentPackets {
+ if h.logger.Debug() {
+ h.logger.Debugf("Max outstanding limited: tracking %d packets, maximum: %d", numTrackedPackets, protocol.MaxOutstandingSentPackets)
+ }
+ return SendAck
+ }
+ return SendAny
+}
+
+func (h *sentPacketHandler) TimeUntilSend() time.Time {
+ return h.congestion.TimeUntilSend(h.bytesInFlight)
+}
+
+func (h *sentPacketHandler) HasPacingBudget() bool {
+ return h.congestion.HasPacingBudget()
+}
+
+func (h *sentPacketHandler) SetMaxDatagramSize(s protocol.ByteCount) {
+ h.congestion.SetMaxDatagramSize(s)
+}
+
+func (h *sentPacketHandler) isAmplificationLimited() bool {
+ if h.peerAddressValidated {
+ return false
+ }
+ return h.bytesSent >= amplificationFactor*h.bytesReceived
+}
+
+func (h *sentPacketHandler) QueueProbePacket(encLevel protocol.EncryptionLevel) bool {
+ pnSpace := h.getPacketNumberSpace(encLevel)
+ p := pnSpace.history.FirstOutstanding()
+ if p == nil {
+ return false
+ }
+ h.queueFramesForRetransmission(p)
+ // TODO: don't declare the packet lost here.
+ // Keep track of acknowledged frames instead.
+ h.removeFromBytesInFlight(p)
+ pnSpace.history.DeclareLost(p)
+ return true
+}
+
+func (h *sentPacketHandler) queueFramesForRetransmission(p *Packet) {
+ if len(p.Frames) == 0 {
+ panic("no frames")
+ }
+ for _, f := range p.Frames {
+ f.OnLost(f.Frame)
+ }
+ p.Frames = nil
+}
+
+func (h *sentPacketHandler) ResetForRetry() error {
+ h.bytesInFlight = 0
+ var firstPacketSendTime time.Time
+ h.initialPackets.history.Iterate(func(p *Packet) (bool, error) {
+ if firstPacketSendTime.IsZero() {
+ firstPacketSendTime = p.SendTime
+ }
+ if p.declaredLost || p.skippedPacket {
+ return true, nil
+ }
+ h.queueFramesForRetransmission(p)
+ return true, nil
+ })
+ // All application data packets sent at this point are 0-RTT packets.
+ // In the case of a Retry, we can assume that the server dropped all of them.
+ h.appDataPackets.history.Iterate(func(p *Packet) (bool, error) {
+ if !p.declaredLost && !p.skippedPacket {
+ h.queueFramesForRetransmission(p)
+ }
+ return true, nil
+ })
+
+ // Only use the Retry to estimate the RTT if we didn't send any retransmission for the Initial.
+ // Otherwise, we don't know which Initial the Retry was sent in response to.
+ if h.ptoCount == 0 {
+ // Don't set the RTT to a value lower than 5ms here.
+ now := time.Now()
+ h.rttStats.UpdateRTT(utils.Max(minRTTAfterRetry, now.Sub(firstPacketSendTime)), 0, now)
+ if h.logger.Debug() {
+ h.logger.Debugf("\tupdated RTT: %s (σ: %s)", h.rttStats.SmoothedRTT(), h.rttStats.MeanDeviation())
+ }
+ if h.tracer != nil {
+ h.tracer.UpdatedMetrics(h.rttStats, h.congestion.GetCongestionWindow(), h.bytesInFlight, h.packetsInFlight())
+ }
+ }
+ h.initialPackets = newPacketNumberSpace(h.initialPackets.pns.Pop(), false, h.rttStats)
+ h.appDataPackets = newPacketNumberSpace(h.appDataPackets.pns.Pop(), true, h.rttStats)
+ oldAlarm := h.alarm
+ h.alarm = time.Time{}
+ if h.tracer != nil {
+ h.tracer.UpdatedPTOCount(0)
+ if !oldAlarm.IsZero() {
+ h.tracer.LossTimerCanceled()
+ }
+ }
+ h.ptoCount = 0
+ return nil
+}
+
+func (h *sentPacketHandler) SetHandshakeConfirmed() {
+ h.handshakeConfirmed = true
+ // We don't send PTOs for application data packets before the handshake completes.
+ // Make sure the timer is armed now, if necessary.
+ h.setLossDetectionTimer()
+}
diff --git a/vendor/github.com/quic-go/quic-go/internal/ackhandler/sent_packet_history.go b/vendor/github.com/quic-go/quic-go/internal/ackhandler/sent_packet_history.go
new file mode 100644
index 0000000000..0647839914
--- /dev/null
+++ b/vendor/github.com/quic-go/quic-go/internal/ackhandler/sent_packet_history.go
@@ -0,0 +1,163 @@
+package ackhandler
+
+import (
+ "fmt"
+ "sync"
+ "time"
+
+ "github.com/quic-go/quic-go/internal/protocol"
+ "github.com/quic-go/quic-go/internal/utils"
+ list "github.com/quic-go/quic-go/internal/utils/linkedlist"
+)
+
+type sentPacketHistory struct {
+ rttStats *utils.RTTStats
+ outstandingPacketList *list.List[*Packet]
+ etcPacketList *list.List[*Packet]
+ packetMap map[protocol.PacketNumber]*list.Element[*Packet]
+ highestSent protocol.PacketNumber
+}
+
+var packetElementPool sync.Pool
+
+func init() {
+ packetElementPool = *list.NewPool[*Packet]()
+}
+
+func newSentPacketHistory(rttStats *utils.RTTStats) *sentPacketHistory {
+ return &sentPacketHistory{
+ rttStats: rttStats,
+ outstandingPacketList: list.NewWithPool[*Packet](&packetElementPool),
+ etcPacketList: list.NewWithPool[*Packet](&packetElementPool),
+ packetMap: make(map[protocol.PacketNumber]*list.Element[*Packet]),
+ highestSent: protocol.InvalidPacketNumber,
+ }
+}
+
+func (h *sentPacketHistory) SentNonAckElicitingPacket(pn protocol.PacketNumber, encLevel protocol.EncryptionLevel, t time.Time) {
+ h.registerSentPacket(pn, encLevel, t)
+}
+
+func (h *sentPacketHistory) SentAckElicitingPacket(p *Packet) {
+ h.registerSentPacket(p.PacketNumber, p.EncryptionLevel, p.SendTime)
+
+ var el *list.Element[*Packet]
+ if p.outstanding() {
+ el = h.outstandingPacketList.PushBack(p)
+ } else {
+ el = h.etcPacketList.PushBack(p)
+ }
+ h.packetMap[p.PacketNumber] = el
+}
+
+func (h *sentPacketHistory) registerSentPacket(pn protocol.PacketNumber, encLevel protocol.EncryptionLevel, t time.Time) {
+ if pn <= h.highestSent {
+ panic("non-sequential packet number use")
+ }
+ // Skipped packet numbers.
+ for p := h.highestSent + 1; p < pn; p++ {
+ el := h.etcPacketList.PushBack(&Packet{
+ PacketNumber: p,
+ EncryptionLevel: encLevel,
+ SendTime: t,
+ skippedPacket: true,
+ })
+ h.packetMap[p] = el
+ }
+ h.highestSent = pn
+}
+
+// Iterate iterates through all packets.
+func (h *sentPacketHistory) Iterate(cb func(*Packet) (cont bool, err error)) error {
+ cont := true
+ outstandingEl := h.outstandingPacketList.Front()
+ etcEl := h.etcPacketList.Front()
+ var el *list.Element[*Packet]
+ // whichever has the next packet number is returned first
+ for cont {
+ if outstandingEl == nil || (etcEl != nil && etcEl.Value.PacketNumber < outstandingEl.Value.PacketNumber) {
+ el = etcEl
+ } else {
+ el = outstandingEl
+ }
+ if el == nil {
+ return nil
+ }
+ if el == outstandingEl {
+ outstandingEl = outstandingEl.Next()
+ } else {
+ etcEl = etcEl.Next()
+ }
+ var err error
+ cont, err = cb(el.Value)
+ if err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
+// FirstOutstanding returns the first outstanding packet.
+func (h *sentPacketHistory) FirstOutstanding() *Packet {
+ el := h.outstandingPacketList.Front()
+ if el == nil {
+ return nil
+ }
+ return el.Value
+}
+
+func (h *sentPacketHistory) Len() int {
+ return len(h.packetMap)
+}
+
+func (h *sentPacketHistory) Remove(p protocol.PacketNumber) error {
+ el, ok := h.packetMap[p]
+ if !ok {
+ return fmt.Errorf("packet %d not found in sent packet history", p)
+ }
+ el.List().Remove(el)
+ delete(h.packetMap, p)
+ return nil
+}
+
+func (h *sentPacketHistory) HasOutstandingPackets() bool {
+ return h.outstandingPacketList.Len() > 0
+}
+
+func (h *sentPacketHistory) DeleteOldPackets(now time.Time) {
+ maxAge := 3 * h.rttStats.PTO(false)
+ var nextEl *list.Element[*Packet]
+ // we don't iterate outstandingPacketList, as we should not delete outstanding packets.
+ // being outstanding for more than 3*PTO should only happen in the case of drastic RTT changes.
+ for el := h.etcPacketList.Front(); el != nil; el = nextEl {
+ nextEl = el.Next()
+ p := el.Value
+ if p.SendTime.After(now.Add(-maxAge)) {
+ break
+ }
+ delete(h.packetMap, p.PacketNumber)
+ h.etcPacketList.Remove(el)
+ }
+}
+
+func (h *sentPacketHistory) DeclareLost(p *Packet) *Packet {
+ el, ok := h.packetMap[p.PacketNumber]
+ if !ok {
+ return nil
+ }
+ el.List().Remove(el)
+ p.declaredLost = true
+ // move it to the correct position in the etc list (based on the packet number)
+ for el = h.etcPacketList.Back(); el != nil; el = el.Prev() {
+ if el.Value.PacketNumber < p.PacketNumber {
+ break
+ }
+ }
+ if el == nil {
+ el = h.etcPacketList.PushFront(p)
+ } else {
+ el = h.etcPacketList.InsertAfter(p, el)
+ }
+ h.packetMap[p.PacketNumber] = el
+ return el.Value
+}
diff --git a/vendor/github.com/quic-go/quic-go/internal/congestion/bandwidth.go b/vendor/github.com/quic-go/quic-go/internal/congestion/bandwidth.go
new file mode 100644
index 0000000000..1d03abbb8a
--- /dev/null
+++ b/vendor/github.com/quic-go/quic-go/internal/congestion/bandwidth.go
@@ -0,0 +1,25 @@
+package congestion
+
+import (
+ "math"
+ "time"
+
+ "github.com/quic-go/quic-go/internal/protocol"
+)
+
+// Bandwidth of a connection
+type Bandwidth uint64
+
+const infBandwidth Bandwidth = math.MaxUint64
+
+const (
+ // BitsPerSecond is 1 bit per second
+ BitsPerSecond Bandwidth = 1
+ // BytesPerSecond is 1 byte per second
+ BytesPerSecond = 8 * BitsPerSecond
+)
+
+// BandwidthFromDelta calculates the bandwidth from a number of bytes and a time delta
+func BandwidthFromDelta(bytes protocol.ByteCount, delta time.Duration) Bandwidth {
+ return Bandwidth(bytes) * Bandwidth(time.Second) / Bandwidth(delta) * BytesPerSecond
+}
diff --git a/vendor/github.com/quic-go/quic-go/internal/congestion/clock.go b/vendor/github.com/quic-go/quic-go/internal/congestion/clock.go
new file mode 100644
index 0000000000..405fae70f9
--- /dev/null
+++ b/vendor/github.com/quic-go/quic-go/internal/congestion/clock.go
@@ -0,0 +1,18 @@
+package congestion
+
+import "time"
+
+// A Clock returns the current time
+type Clock interface {
+ Now() time.Time
+}
+
+// DefaultClock implements the Clock interface using the Go stdlib clock.
+type DefaultClock struct{}
+
+var _ Clock = DefaultClock{}
+
+// Now gets the current time
+func (DefaultClock) Now() time.Time {
+ return time.Now()
+}
diff --git a/vendor/github.com/quic-go/quic-go/internal/congestion/cubic.go b/vendor/github.com/quic-go/quic-go/internal/congestion/cubic.go
new file mode 100644
index 0000000000..a73cf82aa5
--- /dev/null
+++ b/vendor/github.com/quic-go/quic-go/internal/congestion/cubic.go
@@ -0,0 +1,214 @@
+package congestion
+
+import (
+ "math"
+ "time"
+
+ "github.com/quic-go/quic-go/internal/protocol"
+ "github.com/quic-go/quic-go/internal/utils"
+)
+
+// This cubic implementation is based on the one found in Chromiums's QUIC
+// implementation, in the files net/quic/congestion_control/cubic.{hh,cc}.
+
+// Constants based on TCP defaults.
+// The following constants are in 2^10 fractions of a second instead of ms to
+// allow a 10 shift right to divide.
+
+// 1024*1024^3 (first 1024 is from 0.100^3)
+// where 0.100 is 100 ms which is the scaling round trip time.
+const (
+ cubeScale = 40
+ cubeCongestionWindowScale = 410
+ cubeFactor protocol.ByteCount = 1 << cubeScale / cubeCongestionWindowScale / maxDatagramSize
+ // TODO: when re-enabling cubic, make sure to use the actual packet size here
+ maxDatagramSize = protocol.ByteCount(protocol.InitialPacketSizeIPv4)
+)
+
+const defaultNumConnections = 1
+
+// Default Cubic backoff factor
+const beta float32 = 0.7
+
+// Additional backoff factor when loss occurs in the concave part of the Cubic
+// curve. This additional backoff factor is expected to give up bandwidth to
+// new concurrent flows and speed up convergence.
+const betaLastMax float32 = 0.85
+
+// Cubic implements the cubic algorithm from TCP
+type Cubic struct {
+ clock Clock
+
+ // Number of connections to simulate.
+ numConnections int
+
+ // Time when this cycle started, after last loss event.
+ epoch time.Time
+
+ // Max congestion window used just before last loss event.
+ // Note: to improve fairness to other streams an additional back off is
+ // applied to this value if the new value is below our latest value.
+ lastMaxCongestionWindow protocol.ByteCount
+
+ // Number of acked bytes since the cycle started (epoch).
+ ackedBytesCount protocol.ByteCount
+
+ // TCP Reno equivalent congestion window in packets.
+ estimatedTCPcongestionWindow protocol.ByteCount
+
+ // Origin point of cubic function.
+ originPointCongestionWindow protocol.ByteCount
+
+ // Time to origin point of cubic function in 2^10 fractions of a second.
+ timeToOriginPoint uint32
+
+ // Last congestion window in packets computed by cubic function.
+ lastTargetCongestionWindow protocol.ByteCount
+}
+
+// NewCubic returns a new Cubic instance
+func NewCubic(clock Clock) *Cubic {
+ c := &Cubic{
+ clock: clock,
+ numConnections: defaultNumConnections,
+ }
+ c.Reset()
+ return c
+}
+
+// Reset is called after a timeout to reset the cubic state
+func (c *Cubic) Reset() {
+ c.epoch = time.Time{}
+ c.lastMaxCongestionWindow = 0
+ c.ackedBytesCount = 0
+ c.estimatedTCPcongestionWindow = 0
+ c.originPointCongestionWindow = 0
+ c.timeToOriginPoint = 0
+ c.lastTargetCongestionWindow = 0
+}
+
+func (c *Cubic) alpha() float32 {
+ // TCPFriendly alpha is described in Section 3.3 of the CUBIC paper. Note that
+ // beta here is a cwnd multiplier, and is equal to 1-beta from the paper.
+ // We derive the equivalent alpha for an N-connection emulation as:
+ b := c.beta()
+ return 3 * float32(c.numConnections) * float32(c.numConnections) * (1 - b) / (1 + b)
+}
+
+func (c *Cubic) beta() float32 {
+ // kNConnectionBeta is the backoff factor after loss for our N-connection
+ // emulation, which emulates the effective backoff of an ensemble of N
+ // TCP-Reno connections on a single loss event. The effective multiplier is
+ // computed as:
+ return (float32(c.numConnections) - 1 + beta) / float32(c.numConnections)
+}
+
+func (c *Cubic) betaLastMax() float32 {
+ // betaLastMax is the additional backoff factor after loss for our
+ // N-connection emulation, which emulates the additional backoff of
+ // an ensemble of N TCP-Reno connections on a single loss event. The
+ // effective multiplier is computed as:
+ return (float32(c.numConnections) - 1 + betaLastMax) / float32(c.numConnections)
+}
+
+// OnApplicationLimited is called on ack arrival when sender is unable to use
+// the available congestion window. Resets Cubic state during quiescence.
+func (c *Cubic) OnApplicationLimited() {
+ // When sender is not using the available congestion window, the window does
+ // not grow. But to be RTT-independent, Cubic assumes that the sender has been
+ // using the entire window during the time since the beginning of the current
+ // "epoch" (the end of the last loss recovery period). Since
+ // application-limited periods break this assumption, we reset the epoch when
+ // in such a period. This reset effectively freezes congestion window growth
+ // through application-limited periods and allows Cubic growth to continue
+ // when the entire window is being used.
+ c.epoch = time.Time{}
+}
+
+// CongestionWindowAfterPacketLoss computes a new congestion window to use after
+// a loss event. Returns the new congestion window in packets. The new
+// congestion window is a multiplicative decrease of our current window.
+func (c *Cubic) CongestionWindowAfterPacketLoss(currentCongestionWindow protocol.ByteCount) protocol.ByteCount {
+ if currentCongestionWindow+maxDatagramSize < c.lastMaxCongestionWindow {
+ // We never reached the old max, so assume we are competing with another
+ // flow. Use our extra back off factor to allow the other flow to go up.
+ c.lastMaxCongestionWindow = protocol.ByteCount(c.betaLastMax() * float32(currentCongestionWindow))
+ } else {
+ c.lastMaxCongestionWindow = currentCongestionWindow
+ }
+ c.epoch = time.Time{} // Reset time.
+ return protocol.ByteCount(float32(currentCongestionWindow) * c.beta())
+}
+
+// CongestionWindowAfterAck computes a new congestion window to use after a received ACK.
+// Returns the new congestion window in packets. The new congestion window
+// follows a cubic function that depends on the time passed since last
+// packet loss.
+func (c *Cubic) CongestionWindowAfterAck(
+ ackedBytes protocol.ByteCount,
+ currentCongestionWindow protocol.ByteCount,
+ delayMin time.Duration,
+ eventTime time.Time,
+) protocol.ByteCount {
+ c.ackedBytesCount += ackedBytes
+
+ if c.epoch.IsZero() {
+ // First ACK after a loss event.
+ c.epoch = eventTime // Start of epoch.
+ c.ackedBytesCount = ackedBytes // Reset count.
+ // Reset estimated_tcp_congestion_window_ to be in sync with cubic.
+ c.estimatedTCPcongestionWindow = currentCongestionWindow
+ if c.lastMaxCongestionWindow <= currentCongestionWindow {
+ c.timeToOriginPoint = 0
+ c.originPointCongestionWindow = currentCongestionWindow
+ } else {
+ c.timeToOriginPoint = uint32(math.Cbrt(float64(cubeFactor * (c.lastMaxCongestionWindow - currentCongestionWindow))))
+ c.originPointCongestionWindow = c.lastMaxCongestionWindow
+ }
+ }
+
+ // Change the time unit from microseconds to 2^10 fractions per second. Take
+ // the round trip time in account. This is done to allow us to use shift as a
+ // divide operator.
+ elapsedTime := int64(eventTime.Add(delayMin).Sub(c.epoch)/time.Microsecond) << 10 / (1000 * 1000)
+
+ // Right-shifts of negative, signed numbers have implementation-dependent
+ // behavior, so force the offset to be positive, as is done in the kernel.
+ offset := int64(c.timeToOriginPoint) - elapsedTime
+ if offset < 0 {
+ offset = -offset
+ }
+
+ deltaCongestionWindow := protocol.ByteCount(cubeCongestionWindowScale*offset*offset*offset) * maxDatagramSize >> cubeScale
+ var targetCongestionWindow protocol.ByteCount
+ if elapsedTime > int64(c.timeToOriginPoint) {
+ targetCongestionWindow = c.originPointCongestionWindow + deltaCongestionWindow
+ } else {
+ targetCongestionWindow = c.originPointCongestionWindow - deltaCongestionWindow
+ }
+ // Limit the CWND increase to half the acked bytes.
+ targetCongestionWindow = utils.Min(targetCongestionWindow, currentCongestionWindow+c.ackedBytesCount/2)
+
+ // Increase the window by approximately Alpha * 1 MSS of bytes every
+ // time we ack an estimated tcp window of bytes. For small
+ // congestion windows (less than 25), the formula below will
+ // increase slightly slower than linearly per estimated tcp window
+ // of bytes.
+ c.estimatedTCPcongestionWindow += protocol.ByteCount(float32(c.ackedBytesCount) * c.alpha() * float32(maxDatagramSize) / float32(c.estimatedTCPcongestionWindow))
+ c.ackedBytesCount = 0
+
+ // We have a new cubic congestion window.
+ c.lastTargetCongestionWindow = targetCongestionWindow
+
+ // Compute target congestion_window based on cubic target and estimated TCP
+ // congestion_window, use highest (fastest).
+ if targetCongestionWindow < c.estimatedTCPcongestionWindow {
+ targetCongestionWindow = c.estimatedTCPcongestionWindow
+ }
+ return targetCongestionWindow
+}
+
+// SetNumConnections sets the number of emulated connections
+func (c *Cubic) SetNumConnections(n int) {
+ c.numConnections = n
+}
diff --git a/vendor/github.com/quic-go/quic-go/internal/congestion/cubic_sender.go b/vendor/github.com/quic-go/quic-go/internal/congestion/cubic_sender.go
new file mode 100644
index 0000000000..dac3118e3d
--- /dev/null
+++ b/vendor/github.com/quic-go/quic-go/internal/congestion/cubic_sender.go
@@ -0,0 +1,316 @@
+package congestion
+
+import (
+ "fmt"
+ "time"
+
+ "github.com/quic-go/quic-go/internal/protocol"
+ "github.com/quic-go/quic-go/internal/utils"
+ "github.com/quic-go/quic-go/logging"
+)
+
+const (
+ // maxDatagramSize is the default maximum packet size used in the Linux TCP implementation.
+ // Used in QUIC for congestion window computations in bytes.
+ initialMaxDatagramSize = protocol.ByteCount(protocol.InitialPacketSizeIPv4)
+ maxBurstPackets = 3
+ renoBeta = 0.7 // Reno backoff factor.
+ minCongestionWindowPackets = 2
+ initialCongestionWindow = 32
+)
+
+type cubicSender struct {
+ hybridSlowStart HybridSlowStart
+ rttStats *utils.RTTStats
+ cubic *Cubic
+ pacer *pacer
+ clock Clock
+
+ reno bool
+
+ // Track the largest packet that has been sent.
+ largestSentPacketNumber protocol.PacketNumber
+
+ // Track the largest packet that has been acked.
+ largestAckedPacketNumber protocol.PacketNumber
+
+ // Track the largest packet number outstanding when a CWND cutback occurs.
+ largestSentAtLastCutback protocol.PacketNumber
+
+ // Whether the last loss event caused us to exit slowstart.
+ // Used for stats collection of slowstartPacketsLost
+ lastCutbackExitedSlowstart bool
+
+ // Congestion window in bytes.
+ congestionWindow protocol.ByteCount
+
+ // Slow start congestion window in bytes, aka ssthresh.
+ slowStartThreshold protocol.ByteCount
+
+ // ACK counter for the Reno implementation.
+ numAckedPackets uint64
+
+ initialCongestionWindow protocol.ByteCount
+ initialMaxCongestionWindow protocol.ByteCount
+
+ maxDatagramSize protocol.ByteCount
+
+ lastState logging.CongestionState
+ tracer logging.ConnectionTracer
+}
+
+var (
+ _ SendAlgorithm = &cubicSender{}
+ _ SendAlgorithmWithDebugInfos = &cubicSender{}
+)
+
+// NewCubicSender makes a new cubic sender
+func NewCubicSender(
+ clock Clock,
+ rttStats *utils.RTTStats,
+ initialMaxDatagramSize protocol.ByteCount,
+ reno bool,
+ tracer logging.ConnectionTracer,
+) *cubicSender {
+ return newCubicSender(
+ clock,
+ rttStats,
+ reno,
+ initialMaxDatagramSize,
+ initialCongestionWindow*initialMaxDatagramSize,
+ protocol.MaxCongestionWindowPackets*initialMaxDatagramSize,
+ tracer,
+ )
+}
+
+func newCubicSender(
+ clock Clock,
+ rttStats *utils.RTTStats,
+ reno bool,
+ initialMaxDatagramSize,
+ initialCongestionWindow,
+ initialMaxCongestionWindow protocol.ByteCount,
+ tracer logging.ConnectionTracer,
+) *cubicSender {
+ c := &cubicSender{
+ rttStats: rttStats,
+ largestSentPacketNumber: protocol.InvalidPacketNumber,
+ largestAckedPacketNumber: protocol.InvalidPacketNumber,
+ largestSentAtLastCutback: protocol.InvalidPacketNumber,
+ initialCongestionWindow: initialCongestionWindow,
+ initialMaxCongestionWindow: initialMaxCongestionWindow,
+ congestionWindow: initialCongestionWindow,
+ slowStartThreshold: protocol.MaxByteCount,
+ cubic: NewCubic(clock),
+ clock: clock,
+ reno: reno,
+ tracer: tracer,
+ maxDatagramSize: initialMaxDatagramSize,
+ }
+ c.pacer = newPacer(c.BandwidthEstimate)
+ if c.tracer != nil {
+ c.lastState = logging.CongestionStateSlowStart
+ c.tracer.UpdatedCongestionState(logging.CongestionStateSlowStart)
+ }
+ return c
+}
+
+// TimeUntilSend returns when the next packet should be sent.
+func (c *cubicSender) TimeUntilSend(_ protocol.ByteCount) time.Time {
+ return c.pacer.TimeUntilSend()
+}
+
+func (c *cubicSender) HasPacingBudget() bool {
+ return c.pacer.Budget(c.clock.Now()) >= c.maxDatagramSize
+}
+
+func (c *cubicSender) maxCongestionWindow() protocol.ByteCount {
+ return c.maxDatagramSize * protocol.MaxCongestionWindowPackets
+}
+
+func (c *cubicSender) minCongestionWindow() protocol.ByteCount {
+ return c.maxDatagramSize * minCongestionWindowPackets
+}
+
+func (c *cubicSender) OnPacketSent(
+ sentTime time.Time,
+ _ protocol.ByteCount,
+ packetNumber protocol.PacketNumber,
+ bytes protocol.ByteCount,
+ isRetransmittable bool,
+) {
+ c.pacer.SentPacket(sentTime, bytes)
+ if !isRetransmittable {
+ return
+ }
+ c.largestSentPacketNumber = packetNumber
+ c.hybridSlowStart.OnPacketSent(packetNumber)
+}
+
+func (c *cubicSender) CanSend(bytesInFlight protocol.ByteCount) bool {
+ return bytesInFlight < c.GetCongestionWindow()
+}
+
+func (c *cubicSender) InRecovery() bool {
+ return c.largestAckedPacketNumber != protocol.InvalidPacketNumber && c.largestAckedPacketNumber <= c.largestSentAtLastCutback
+}
+
+func (c *cubicSender) InSlowStart() bool {
+ return c.GetCongestionWindow() < c.slowStartThreshold
+}
+
+func (c *cubicSender) GetCongestionWindow() protocol.ByteCount {
+ return c.congestionWindow
+}
+
+func (c *cubicSender) MaybeExitSlowStart() {
+ if c.InSlowStart() &&
+ c.hybridSlowStart.ShouldExitSlowStart(c.rttStats.LatestRTT(), c.rttStats.MinRTT(), c.GetCongestionWindow()/c.maxDatagramSize) {
+ // exit slow start
+ c.slowStartThreshold = c.congestionWindow
+ c.maybeTraceStateChange(logging.CongestionStateCongestionAvoidance)
+ }
+}
+
+func (c *cubicSender) OnPacketAcked(
+ ackedPacketNumber protocol.PacketNumber,
+ ackedBytes protocol.ByteCount,
+ priorInFlight protocol.ByteCount,
+ eventTime time.Time,
+) {
+ c.largestAckedPacketNumber = utils.Max(ackedPacketNumber, c.largestAckedPacketNumber)
+ if c.InRecovery() {
+ return
+ }
+ c.maybeIncreaseCwnd(ackedPacketNumber, ackedBytes, priorInFlight, eventTime)
+ if c.InSlowStart() {
+ c.hybridSlowStart.OnPacketAcked(ackedPacketNumber)
+ }
+}
+
+func (c *cubicSender) OnPacketLost(packetNumber protocol.PacketNumber, lostBytes, priorInFlight protocol.ByteCount) {
+ // TCP NewReno (RFC6582) says that once a loss occurs, any losses in packets
+ // already sent should be treated as a single loss event, since it's expected.
+ if packetNumber <= c.largestSentAtLastCutback {
+ return
+ }
+ c.lastCutbackExitedSlowstart = c.InSlowStart()
+ c.maybeTraceStateChange(logging.CongestionStateRecovery)
+
+ if c.reno {
+ c.congestionWindow = protocol.ByteCount(float64(c.congestionWindow) * renoBeta)
+ } else {
+ c.congestionWindow = c.cubic.CongestionWindowAfterPacketLoss(c.congestionWindow)
+ }
+ if minCwnd := c.minCongestionWindow(); c.congestionWindow < minCwnd {
+ c.congestionWindow = minCwnd
+ }
+ c.slowStartThreshold = c.congestionWindow
+ c.largestSentAtLastCutback = c.largestSentPacketNumber
+ // reset packet count from congestion avoidance mode. We start
+ // counting again when we're out of recovery.
+ c.numAckedPackets = 0
+}
+
+// Called when we receive an ack. Normal TCP tracks how many packets one ack
+// represents, but quic has a separate ack for each packet.
+func (c *cubicSender) maybeIncreaseCwnd(
+ _ protocol.PacketNumber,
+ ackedBytes protocol.ByteCount,
+ priorInFlight protocol.ByteCount,
+ eventTime time.Time,
+) {
+ // Do not increase the congestion window unless the sender is close to using
+ // the current window.
+ if !c.isCwndLimited(priorInFlight) {
+ c.cubic.OnApplicationLimited()
+ c.maybeTraceStateChange(logging.CongestionStateApplicationLimited)
+ return
+ }
+ if c.congestionWindow >= c.maxCongestionWindow() {
+ return
+ }
+ if c.InSlowStart() {
+ // TCP slow start, exponential growth, increase by one for each ACK.
+ c.congestionWindow += c.maxDatagramSize
+ c.maybeTraceStateChange(logging.CongestionStateSlowStart)
+ return
+ }
+ // Congestion avoidance
+ c.maybeTraceStateChange(logging.CongestionStateCongestionAvoidance)
+ if c.reno {
+ // Classic Reno congestion avoidance.
+ c.numAckedPackets++
+ if c.numAckedPackets >= uint64(c.congestionWindow/c.maxDatagramSize) {
+ c.congestionWindow += c.maxDatagramSize
+ c.numAckedPackets = 0
+ }
+ } else {
+ c.congestionWindow = utils.Min(c.maxCongestionWindow(), c.cubic.CongestionWindowAfterAck(ackedBytes, c.congestionWindow, c.rttStats.MinRTT(), eventTime))
+ }
+}
+
+func (c *cubicSender) isCwndLimited(bytesInFlight protocol.ByteCount) bool {
+ congestionWindow := c.GetCongestionWindow()
+ if bytesInFlight >= congestionWindow {
+ return true
+ }
+ availableBytes := congestionWindow - bytesInFlight
+ slowStartLimited := c.InSlowStart() && bytesInFlight > congestionWindow/2
+ return slowStartLimited || availableBytes <= maxBurstPackets*c.maxDatagramSize
+}
+
+// BandwidthEstimate returns the current bandwidth estimate
+func (c *cubicSender) BandwidthEstimate() Bandwidth {
+ srtt := c.rttStats.SmoothedRTT()
+ if srtt == 0 {
+ // If we haven't measured an rtt, the bandwidth estimate is unknown.
+ return infBandwidth
+ }
+ return BandwidthFromDelta(c.GetCongestionWindow(), srtt)
+}
+
+// OnRetransmissionTimeout is called on an retransmission timeout
+func (c *cubicSender) OnRetransmissionTimeout(packetsRetransmitted bool) {
+ c.largestSentAtLastCutback = protocol.InvalidPacketNumber
+ if !packetsRetransmitted {
+ return
+ }
+ c.hybridSlowStart.Restart()
+ c.cubic.Reset()
+ c.slowStartThreshold = c.congestionWindow / 2
+ c.congestionWindow = c.minCongestionWindow()
+}
+
+// OnConnectionMigration is called when the connection is migrated (?)
+func (c *cubicSender) OnConnectionMigration() {
+ c.hybridSlowStart.Restart()
+ c.largestSentPacketNumber = protocol.InvalidPacketNumber
+ c.largestAckedPacketNumber = protocol.InvalidPacketNumber
+ c.largestSentAtLastCutback = protocol.InvalidPacketNumber
+ c.lastCutbackExitedSlowstart = false
+ c.cubic.Reset()
+ c.numAckedPackets = 0
+ c.congestionWindow = c.initialCongestionWindow
+ c.slowStartThreshold = c.initialMaxCongestionWindow
+}
+
+func (c *cubicSender) maybeTraceStateChange(new logging.CongestionState) {
+ if c.tracer == nil || new == c.lastState {
+ return
+ }
+ c.tracer.UpdatedCongestionState(new)
+ c.lastState = new
+}
+
+func (c *cubicSender) SetMaxDatagramSize(s protocol.ByteCount) {
+ if s < c.maxDatagramSize {
+ panic(fmt.Sprintf("congestion BUG: decreased max datagram size from %d to %d", c.maxDatagramSize, s))
+ }
+ cwndIsMinCwnd := c.congestionWindow == c.minCongestionWindow()
+ c.maxDatagramSize = s
+ if cwndIsMinCwnd {
+ c.congestionWindow = c.minCongestionWindow()
+ }
+ c.pacer.SetMaxDatagramSize(s)
+}
diff --git a/vendor/github.com/quic-go/quic-go/internal/congestion/hybrid_slow_start.go b/vendor/github.com/quic-go/quic-go/internal/congestion/hybrid_slow_start.go
new file mode 100644
index 0000000000..b2f7c908ed
--- /dev/null
+++ b/vendor/github.com/quic-go/quic-go/internal/congestion/hybrid_slow_start.go
@@ -0,0 +1,113 @@
+package congestion
+
+import (
+ "time"
+
+ "github.com/quic-go/quic-go/internal/protocol"
+ "github.com/quic-go/quic-go/internal/utils"
+)
+
+// Note(pwestin): the magic clamping numbers come from the original code in
+// tcp_cubic.c.
+const hybridStartLowWindow = protocol.ByteCount(16)
+
+// Number of delay samples for detecting the increase of delay.
+const hybridStartMinSamples = uint32(8)
+
+// Exit slow start if the min rtt has increased by more than 1/8th.
+const hybridStartDelayFactorExp = 3 // 2^3 = 8
+// The original paper specifies 2 and 8ms, but those have changed over time.
+const (
+ hybridStartDelayMinThresholdUs = int64(4000)
+ hybridStartDelayMaxThresholdUs = int64(16000)
+)
+
+// HybridSlowStart implements the TCP hybrid slow start algorithm
+type HybridSlowStart struct {
+ endPacketNumber protocol.PacketNumber
+ lastSentPacketNumber protocol.PacketNumber
+ started bool
+ currentMinRTT time.Duration
+ rttSampleCount uint32
+ hystartFound bool
+}
+
+// StartReceiveRound is called for the start of each receive round (burst) in the slow start phase.
+func (s *HybridSlowStart) StartReceiveRound(lastSent protocol.PacketNumber) {
+ s.endPacketNumber = lastSent
+ s.currentMinRTT = 0
+ s.rttSampleCount = 0
+ s.started = true
+}
+
+// IsEndOfRound returns true if this ack is the last packet number of our current slow start round.
+func (s *HybridSlowStart) IsEndOfRound(ack protocol.PacketNumber) bool {
+ return s.endPacketNumber < ack
+}
+
+// ShouldExitSlowStart should be called on every new ack frame, since a new
+// RTT measurement can be made then.
+// rtt: the RTT for this ack packet.
+// minRTT: is the lowest delay (RTT) we have seen during the session.
+// congestionWindow: the congestion window in packets.
+func (s *HybridSlowStart) ShouldExitSlowStart(latestRTT time.Duration, minRTT time.Duration, congestionWindow protocol.ByteCount) bool {
+ if !s.started {
+ // Time to start the hybrid slow start.
+ s.StartReceiveRound(s.lastSentPacketNumber)
+ }
+ if s.hystartFound {
+ return true
+ }
+ // Second detection parameter - delay increase detection.
+ // Compare the minimum delay (s.currentMinRTT) of the current
+ // burst of packets relative to the minimum delay during the session.
+ // Note: we only look at the first few(8) packets in each burst, since we
+ // only want to compare the lowest RTT of the burst relative to previous
+ // bursts.
+ s.rttSampleCount++
+ if s.rttSampleCount <= hybridStartMinSamples {
+ if s.currentMinRTT == 0 || s.currentMinRTT > latestRTT {
+ s.currentMinRTT = latestRTT
+ }
+ }
+ // We only need to check this once per round.
+ if s.rttSampleCount == hybridStartMinSamples {
+ // Divide minRTT by 8 to get a rtt increase threshold for exiting.
+ minRTTincreaseThresholdUs := int64(minRTT / time.Microsecond >> hybridStartDelayFactorExp)
+ // Ensure the rtt threshold is never less than 2ms or more than 16ms.
+ minRTTincreaseThresholdUs = utils.Min(minRTTincreaseThresholdUs, hybridStartDelayMaxThresholdUs)
+ minRTTincreaseThreshold := time.Duration(utils.Max(minRTTincreaseThresholdUs, hybridStartDelayMinThresholdUs)) * time.Microsecond
+
+ if s.currentMinRTT > (minRTT + minRTTincreaseThreshold) {
+ s.hystartFound = true
+ }
+ }
+ // Exit from slow start if the cwnd is greater than 16 and
+ // increasing delay is found.
+ return congestionWindow >= hybridStartLowWindow && s.hystartFound
+}
+
+// OnPacketSent is called when a packet was sent
+func (s *HybridSlowStart) OnPacketSent(packetNumber protocol.PacketNumber) {
+ s.lastSentPacketNumber = packetNumber
+}
+
+// OnPacketAcked gets invoked after ShouldExitSlowStart, so it's best to end
+// the round when the final packet of the burst is received and start it on
+// the next incoming ack.
+func (s *HybridSlowStart) OnPacketAcked(ackedPacketNumber protocol.PacketNumber) {
+ if s.IsEndOfRound(ackedPacketNumber) {
+ s.started = false
+ }
+}
+
+// Started returns true if started
+func (s *HybridSlowStart) Started() bool {
+ return s.started
+}
+
+// Restart the slow start phase
+func (s *HybridSlowStart) Restart() {
+ s.started = false
+ s.hystartFound = false
+}
diff --git a/vendor/github.com/quic-go/quic-go/internal/congestion/interface.go b/vendor/github.com/quic-go/quic-go/internal/congestion/interface.go
new file mode 100644
index 0000000000..5db3ebae0c
--- /dev/null
+++ b/vendor/github.com/quic-go/quic-go/internal/congestion/interface.go
@@ -0,0 +1,28 @@
+package congestion
+
+import (
+ "time"
+
+ "github.com/quic-go/quic-go/internal/protocol"
+)
+
+// A SendAlgorithm performs congestion control
+type SendAlgorithm interface {
+ TimeUntilSend(bytesInFlight protocol.ByteCount) time.Time
+ HasPacingBudget() bool
+ OnPacketSent(sentTime time.Time, bytesInFlight protocol.ByteCount, packetNumber protocol.PacketNumber, bytes protocol.ByteCount, isRetransmittable bool)
+ CanSend(bytesInFlight protocol.ByteCount) bool
+ MaybeExitSlowStart()
+ OnPacketAcked(number protocol.PacketNumber, ackedBytes protocol.ByteCount, priorInFlight protocol.ByteCount, eventTime time.Time)
+ OnPacketLost(number protocol.PacketNumber, lostBytes protocol.ByteCount, priorInFlight protocol.ByteCount)
+ OnRetransmissionTimeout(packetsRetransmitted bool)
+ SetMaxDatagramSize(protocol.ByteCount)
+}
+
+// A SendAlgorithmWithDebugInfos is a SendAlgorithm that exposes some debug infos
+type SendAlgorithmWithDebugInfos interface {
+ SendAlgorithm
+ InSlowStart() bool
+ InRecovery() bool
+ GetCongestionWindow() protocol.ByteCount
+}
diff --git a/vendor/github.com/quic-go/quic-go/internal/congestion/pacer.go b/vendor/github.com/quic-go/quic-go/internal/congestion/pacer.go
new file mode 100644
index 0000000000..a5861062e1
--- /dev/null
+++ b/vendor/github.com/quic-go/quic-go/internal/congestion/pacer.go
@@ -0,0 +1,77 @@
+package congestion
+
+import (
+ "math"
+ "time"
+
+ "github.com/quic-go/quic-go/internal/protocol"
+ "github.com/quic-go/quic-go/internal/utils"
+)
+
+const maxBurstSizePackets = 10
+
+// The pacer implements a token bucket pacing algorithm.
+type pacer struct {
+ budgetAtLastSent protocol.ByteCount
+ maxDatagramSize protocol.ByteCount
+ lastSentTime time.Time
+ getAdjustedBandwidth func() uint64 // in bytes/s
+}
+
+func newPacer(getBandwidth func() Bandwidth) *pacer {
+ p := &pacer{
+ maxDatagramSize: initialMaxDatagramSize,
+ getAdjustedBandwidth: func() uint64 {
+ // Bandwidth is in bits/s. We need the value in bytes/s.
+ bw := uint64(getBandwidth() / BytesPerSecond)
+ // Use a slightly higher value than the actual measured bandwidth.
+ // RTT variations then won't result in under-utilization of the congestion window.
+ // Ultimately, this will result in sending packets as acknowledgments are received rather than when timers fire,
+ // provided the congestion window is fully utilized and acknowledgments arrive at regular intervals.
+ return bw * 5 / 4
+ },
+ }
+ p.budgetAtLastSent = p.maxBurstSize()
+ return p
+}
+
+func (p *pacer) SentPacket(sendTime time.Time, size protocol.ByteCount) {
+ budget := p.Budget(sendTime)
+ if size > budget {
+ p.budgetAtLastSent = 0
+ } else {
+ p.budgetAtLastSent = budget - size
+ }
+ p.lastSentTime = sendTime
+}
+
+func (p *pacer) Budget(now time.Time) protocol.ByteCount {
+ if p.lastSentTime.IsZero() {
+ return p.maxBurstSize()
+ }
+ budget := p.budgetAtLastSent + (protocol.ByteCount(p.getAdjustedBandwidth())*protocol.ByteCount(now.Sub(p.lastSentTime).Nanoseconds()))/1e9
+ return utils.Min(p.maxBurstSize(), budget)
+}
+
+func (p *pacer) maxBurstSize() protocol.ByteCount {
+ return utils.Max(
+ protocol.ByteCount(uint64((protocol.MinPacingDelay+protocol.TimerGranularity).Nanoseconds())*p.getAdjustedBandwidth())/1e9,
+ maxBurstSizePackets*p.maxDatagramSize,
+ )
+}
+
+// TimeUntilSend returns when the next packet should be sent.
+// It returns the zero value of time.Time if a packet can be sent immediately.
+func (p *pacer) TimeUntilSend() time.Time {
+ if p.budgetAtLastSent >= p.maxDatagramSize {
+ return time.Time{}
+ }
+ return p.lastSentTime.Add(utils.Max(
+ protocol.MinPacingDelay,
+ time.Duration(math.Ceil(float64(p.maxDatagramSize-p.budgetAtLastSent)*1e9/float64(p.getAdjustedBandwidth())))*time.Nanosecond,
+ ))
+}
+
+func (p *pacer) SetMaxDatagramSize(s protocol.ByteCount) {
+ p.maxDatagramSize = s
+}
diff --git a/vendor/github.com/quic-go/quic-go/internal/flowcontrol/base_flow_controller.go b/vendor/github.com/quic-go/quic-go/internal/flowcontrol/base_flow_controller.go
new file mode 100644
index 0000000000..f3f24a60ed
--- /dev/null
+++ b/vendor/github.com/quic-go/quic-go/internal/flowcontrol/base_flow_controller.go
@@ -0,0 +1,125 @@
+package flowcontrol
+
+import (
+ "sync"
+ "time"
+
+ "github.com/quic-go/quic-go/internal/protocol"
+ "github.com/quic-go/quic-go/internal/utils"
+)
+
+type baseFlowController struct {
+ // for sending data
+ bytesSent protocol.ByteCount
+ sendWindow protocol.ByteCount
+ lastBlockedAt protocol.ByteCount
+
+ // for receiving data
+ //nolint:structcheck // The mutex is used both by the stream and the connection flow controller
+ mutex sync.Mutex
+ bytesRead protocol.ByteCount
+ highestReceived protocol.ByteCount
+ receiveWindow protocol.ByteCount
+ receiveWindowSize protocol.ByteCount
+ maxReceiveWindowSize protocol.ByteCount
+
+ allowWindowIncrease func(size protocol.ByteCount) bool
+
+ epochStartTime time.Time
+ epochStartOffset protocol.ByteCount
+ rttStats *utils.RTTStats
+
+ logger utils.Logger
+}
+
+// IsNewlyBlocked says if it is newly blocked by flow control.
+// For every offset, it only returns true once.
+// If it is blocked, the offset is returned.
+func (c *baseFlowController) IsNewlyBlocked() (bool, protocol.ByteCount) {
+ if c.sendWindowSize() != 0 || c.sendWindow == c.lastBlockedAt {
+ return false, 0
+ }
+ c.lastBlockedAt = c.sendWindow
+ return true, c.sendWindow
+}
+
+func (c *baseFlowController) AddBytesSent(n protocol.ByteCount) {
+ c.bytesSent += n
+}
+
+// UpdateSendWindow is called after receiving a MAX_{STREAM_}DATA frame.
+func (c *baseFlowController) UpdateSendWindow(offset protocol.ByteCount) {
+ if offset > c.sendWindow {
+ c.sendWindow = offset
+ }
+}
+
+func (c *baseFlowController) sendWindowSize() protocol.ByteCount {
+ // this only happens during connection establishment, when data is sent before we receive the peer's transport parameters
+ if c.bytesSent > c.sendWindow {
+ return 0
+ }
+ return c.sendWindow - c.bytesSent
+}
+
+// needs to be called with locked mutex
+func (c *baseFlowController) addBytesRead(n protocol.ByteCount) {
+ // pretend we sent a WindowUpdate when reading the first byte
+ // this way auto-tuning of the window size already works for the first WindowUpdate
+ if c.bytesRead == 0 {
+ c.startNewAutoTuningEpoch(time.Now())
+ }
+ c.bytesRead += n
+}
+
+func (c *baseFlowController) hasWindowUpdate() bool {
+ bytesRemaining := c.receiveWindow - c.bytesRead
+ // update the window when more than the threshold was consumed
+ return bytesRemaining <= protocol.ByteCount(float64(c.receiveWindowSize)*(1-protocol.WindowUpdateThreshold))
+}
+
+// getWindowUpdate updates the receive window, if necessary
+// it returns the new offset
+func (c *baseFlowController) getWindowUpdate() protocol.ByteCount {
+ if !c.hasWindowUpdate() {
+ return 0
+ }
+
+ c.maybeAdjustWindowSize()
+ c.receiveWindow = c.bytesRead + c.receiveWindowSize
+ return c.receiveWindow
+}
+
+// maybeAdjustWindowSize increases the receiveWindowSize if we're sending updates too often.
+// For details about auto-tuning, see https://docs.google.com/document/d/1SExkMmGiz8VYzV3s9E35JQlJ73vhzCekKkDi85F1qCE/edit?usp=sharing.
+func (c *baseFlowController) maybeAdjustWindowSize() {
+ bytesReadInEpoch := c.bytesRead - c.epochStartOffset
+ // don't do anything if less than half the window has been consumed
+ if bytesReadInEpoch <= c.receiveWindowSize/2 {
+ return
+ }
+ rtt := c.rttStats.SmoothedRTT()
+ if rtt == 0 {
+ return
+ }
+
+ fraction := float64(bytesReadInEpoch) / float64(c.receiveWindowSize)
+ now := time.Now()
+ if now.Sub(c.epochStartTime) < time.Duration(4*fraction*float64(rtt)) {
+ // window is consumed too fast, try to increase the window size
+ newSize := utils.Min(2*c.receiveWindowSize, c.maxReceiveWindowSize)
+ if newSize > c.receiveWindowSize && (c.allowWindowIncrease == nil || c.allowWindowIncrease(newSize-c.receiveWindowSize)) {
+ c.receiveWindowSize = newSize
+ }
+ }
+ c.startNewAutoTuningEpoch(now)
+}
+
+func (c *baseFlowController) startNewAutoTuningEpoch(now time.Time) {
+ c.epochStartTime = now
+ c.epochStartOffset = c.bytesRead
+}
+
+func (c *baseFlowController) checkFlowControlViolation() bool {
+ return c.highestReceived > c.receiveWindow
+}
diff --git a/vendor/github.com/quic-go/quic-go/internal/flowcontrol/connection_flow_controller.go b/vendor/github.com/quic-go/quic-go/internal/flowcontrol/connection_flow_controller.go
new file mode 100644
index 0000000000..13e69d6c43
--- /dev/null
+++ b/vendor/github.com/quic-go/quic-go/internal/flowcontrol/connection_flow_controller.go
@@ -0,0 +1,112 @@
+package flowcontrol
+
+import (
+ "errors"
+ "fmt"
+ "time"
+
+ "github.com/quic-go/quic-go/internal/protocol"
+ "github.com/quic-go/quic-go/internal/qerr"
+ "github.com/quic-go/quic-go/internal/utils"
+)
+
+type connectionFlowController struct {
+ baseFlowController
+
+ queueWindowUpdate func()
+}
+
+var _ ConnectionFlowController = &connectionFlowController{}
+
+// NewConnectionFlowController gets a new flow controller for the connection
+// It is created before we receive the peer's transport parameters, thus it starts with a sendWindow of 0.
+func NewConnectionFlowController(
+ receiveWindow protocol.ByteCount,
+ maxReceiveWindow protocol.ByteCount,
+ queueWindowUpdate func(),
+ allowWindowIncrease func(size protocol.ByteCount) bool,
+ rttStats *utils.RTTStats,
+ logger utils.Logger,
+) ConnectionFlowController {
+ return &connectionFlowController{
+ baseFlowController: baseFlowController{
+ rttStats: rttStats,
+ receiveWindow: receiveWindow,
+ receiveWindowSize: receiveWindow,
+ maxReceiveWindowSize: maxReceiveWindow,
+ allowWindowIncrease: allowWindowIncrease,
+ logger: logger,
+ },
+ queueWindowUpdate: queueWindowUpdate,
+ }
+}
+
+func (c *connectionFlowController) SendWindowSize() protocol.ByteCount {
+ return c.baseFlowController.sendWindowSize()
+}
+
+// IncrementHighestReceived adds an increment to the highestReceived value
+func (c *connectionFlowController) IncrementHighestReceived(increment protocol.ByteCount) error {
+ c.mutex.Lock()
+ defer c.mutex.Unlock()
+
+ c.highestReceived += increment
+ if c.checkFlowControlViolation() {
+ return &qerr.TransportError{
+ ErrorCode: qerr.FlowControlError,
+ ErrorMessage: fmt.Sprintf("received %d bytes for the connection, allowed %d bytes", c.highestReceived, c.receiveWindow),
+ }
+ }
+ return nil
+}
+
+func (c *connectionFlowController) AddBytesRead(n protocol.ByteCount) {
+ c.mutex.Lock()
+ c.baseFlowController.addBytesRead(n)
+ shouldQueueWindowUpdate := c.hasWindowUpdate()
+ c.mutex.Unlock()
+ if shouldQueueWindowUpdate {
+ c.queueWindowUpdate()
+ }
+}
+
+func (c *connectionFlowController) GetWindowUpdate() protocol.ByteCount {
+ c.mutex.Lock()
+ oldWindowSize := c.receiveWindowSize
+ offset := c.baseFlowController.getWindowUpdate()
+ if oldWindowSize < c.receiveWindowSize {
+ c.logger.Debugf("Increasing receive flow control window for the connection to %d kB", c.receiveWindowSize/(1<<10))
+ }
+ c.mutex.Unlock()
+ return offset
+}
+
+// EnsureMinimumWindowSize sets a minimum window size
+// it should make sure that the connection-level window is increased when a stream-level window grows
+func (c *connectionFlowController) EnsureMinimumWindowSize(inc protocol.ByteCount) {
+ c.mutex.Lock()
+ if inc > c.receiveWindowSize {
+ c.logger.Debugf("Increasing receive flow control window for the connection to %d kB, in response to stream flow control window increase", c.receiveWindowSize/(1<<10))
+ newSize := utils.Min(inc, c.maxReceiveWindowSize)
+ if delta := newSize - c.receiveWindowSize; delta > 0 && c.allowWindowIncrease(delta) {
+ c.receiveWindowSize = newSize
+ }
+ c.startNewAutoTuningEpoch(time.Now())
+ }
+ c.mutex.Unlock()
+}
+
+// Reset rests the flow controller. This happens when 0-RTT is rejected.
+// All stream data is invalidated, it's if we had never opened a stream and never sent any data.
+// At that point, we only have sent stream data, but we didn't have the keys to open 1-RTT keys yet.
+func (c *connectionFlowController) Reset() error {
+ c.mutex.Lock()
+ defer c.mutex.Unlock()
+
+ if c.bytesRead > 0 || c.highestReceived > 0 || !c.epochStartTime.IsZero() {
+ return errors.New("flow controller reset after reading data")
+ }
+ c.bytesSent = 0
+ c.lastBlockedAt = 0
+ return nil
+}
diff --git a/vendor/github.com/quic-go/quic-go/internal/flowcontrol/interface.go b/vendor/github.com/quic-go/quic-go/internal/flowcontrol/interface.go
new file mode 100644
index 0000000000..946519d520
--- /dev/null
+++ b/vendor/github.com/quic-go/quic-go/internal/flowcontrol/interface.go
@@ -0,0 +1,42 @@
+package flowcontrol
+
+import "github.com/quic-go/quic-go/internal/protocol"
+
+type flowController interface {
+ // for sending
+ SendWindowSize() protocol.ByteCount
+ UpdateSendWindow(protocol.ByteCount)
+ AddBytesSent(protocol.ByteCount)
+ // for receiving
+ AddBytesRead(protocol.ByteCount)
+ GetWindowUpdate() protocol.ByteCount // returns 0 if no update is necessary
+ IsNewlyBlocked() (bool, protocol.ByteCount)
+}
+
+// A StreamFlowController is a flow controller for a QUIC stream.
+type StreamFlowController interface {
+ flowController
+ // for receiving
+ // UpdateHighestReceived should be called when a new highest offset is received
+ // final has to be to true if this is the final offset of the stream,
+ // as contained in a STREAM frame with FIN bit, and the RESET_STREAM frame
+ UpdateHighestReceived(offset protocol.ByteCount, final bool) error
+ // Abandon should be called when reading from the stream is aborted early,
+ // and there won't be any further calls to AddBytesRead.
+ Abandon()
+}
+
+// The ConnectionFlowController is the flow controller for the connection.
+type ConnectionFlowController interface {
+ flowController
+ Reset() error
+}
+
+type connectionFlowControllerI interface {
+ ConnectionFlowController
+ // The following two methods are not supposed to be called from outside this packet, but are needed internally
+ // for sending
+ EnsureMinimumWindowSize(protocol.ByteCount)
+ // for receiving
+ IncrementHighestReceived(protocol.ByteCount) error
+}
diff --git a/vendor/github.com/quic-go/quic-go/internal/flowcontrol/stream_flow_controller.go b/vendor/github.com/quic-go/quic-go/internal/flowcontrol/stream_flow_controller.go
new file mode 100644
index 0000000000..1770a9c848
--- /dev/null
+++ b/vendor/github.com/quic-go/quic-go/internal/flowcontrol/stream_flow_controller.go
@@ -0,0 +1,149 @@
+package flowcontrol
+
+import (
+ "fmt"
+
+ "github.com/quic-go/quic-go/internal/protocol"
+ "github.com/quic-go/quic-go/internal/qerr"
+ "github.com/quic-go/quic-go/internal/utils"
+)
+
+type streamFlowController struct {
+ baseFlowController
+
+ streamID protocol.StreamID
+
+ queueWindowUpdate func()
+
+ connection connectionFlowControllerI
+
+ receivedFinalOffset bool
+}
+
+var _ StreamFlowController = &streamFlowController{}
+
+// NewStreamFlowController gets a new flow controller for a stream
+func NewStreamFlowController(
+ streamID protocol.StreamID,
+ cfc ConnectionFlowController,
+ receiveWindow protocol.ByteCount,
+ maxReceiveWindow protocol.ByteCount,
+ initialSendWindow protocol.ByteCount,
+ queueWindowUpdate func(protocol.StreamID),
+ rttStats *utils.RTTStats,
+ logger utils.Logger,
+) StreamFlowController {
+ return &streamFlowController{
+ streamID: streamID,
+ connection: cfc.(connectionFlowControllerI),
+ queueWindowUpdate: func() { queueWindowUpdate(streamID) },
+ baseFlowController: baseFlowController{
+ rttStats: rttStats,
+ receiveWindow: receiveWindow,
+ receiveWindowSize: receiveWindow,
+ maxReceiveWindowSize: maxReceiveWindow,
+ sendWindow: initialSendWindow,
+ logger: logger,
+ },
+ }
+}
+
+// UpdateHighestReceived updates the highestReceived value, if the offset is higher.
+func (c *streamFlowController) UpdateHighestReceived(offset protocol.ByteCount, final bool) error {
+ // If the final offset for this stream is already known, check for consistency.
+ if c.receivedFinalOffset {
+ // If we receive another final offset, check that it's the same.
+ if final && offset != c.highestReceived {
+ return &qerr.TransportError{
+ ErrorCode: qerr.FinalSizeError,
+ ErrorMessage: fmt.Sprintf("received inconsistent final offset for stream %d (old: %d, new: %d bytes)", c.streamID, c.highestReceived, offset),
+ }
+ }
+ // Check that the offset is below the final offset.
+ if offset > c.highestReceived {
+ return &qerr.TransportError{
+ ErrorCode: qerr.FinalSizeError,
+ ErrorMessage: fmt.Sprintf("received offset %d for stream %d, but final offset was already received at %d", offset, c.streamID, c.highestReceived),
+ }
+ }
+ }
+
+ if final {
+ c.receivedFinalOffset = true
+ }
+ if offset == c.highestReceived {
+ return nil
+ }
+ // A higher offset was received before.
+ // This can happen due to reordering.
+ if offset <= c.highestReceived {
+ if final {
+ return &qerr.TransportError{
+ ErrorCode: qerr.FinalSizeError,
+ ErrorMessage: fmt.Sprintf("received final offset %d for stream %d, but already received offset %d before", offset, c.streamID, c.highestReceived),
+ }
+ }
+ return nil
+ }
+
+ increment := offset - c.highestReceived
+ c.highestReceived = offset
+ if c.checkFlowControlViolation() {
+ return &qerr.TransportError{
+ ErrorCode: qerr.FlowControlError,
+ ErrorMessage: fmt.Sprintf("received %d bytes on stream %d, allowed %d bytes", offset, c.streamID, c.receiveWindow),
+ }
+ }
+ return c.connection.IncrementHighestReceived(increment)
+}
+
+func (c *streamFlowController) AddBytesRead(n protocol.ByteCount) {
+ c.mutex.Lock()
+ c.baseFlowController.addBytesRead(n)
+ shouldQueueWindowUpdate := c.shouldQueueWindowUpdate()
+ c.mutex.Unlock()
+ if shouldQueueWindowUpdate {
+ c.queueWindowUpdate()
+ }
+ c.connection.AddBytesRead(n)
+}
+
+func (c *streamFlowController) Abandon() {
+ c.mutex.Lock()
+ unread := c.highestReceived - c.bytesRead
+ c.mutex.Unlock()
+ if unread > 0 {
+ c.connection.AddBytesRead(unread)
+ }
+}
+
+func (c *streamFlowController) AddBytesSent(n protocol.ByteCount) {
+ c.baseFlowController.AddBytesSent(n)
+ c.connection.AddBytesSent(n)
+}
+
+func (c *streamFlowController) SendWindowSize() protocol.ByteCount {
+ return utils.Min(c.baseFlowController.sendWindowSize(), c.connection.SendWindowSize())
+}
+
+func (c *streamFlowController) shouldQueueWindowUpdate() bool {
+ return !c.receivedFinalOffset && c.hasWindowUpdate()
+}
+
+func (c *streamFlowController) GetWindowUpdate() protocol.ByteCount {
+ // If we already received the final offset for this stream, the peer won't need any additional flow control credit.
+ if c.receivedFinalOffset {
+ return 0
+ }
+
+ // Don't use defer for unlocking the mutex here, GetWindowUpdate() is called frequently and defer shows up in the profiler
+ c.mutex.Lock()
+ oldWindowSize := c.receiveWindowSize
+ offset := c.baseFlowController.getWindowUpdate()
+ if c.receiveWindowSize > oldWindowSize { // auto-tuning enlarged the window size
+ c.logger.Debugf("Increasing receive flow control window for stream %d to %d kB", c.streamID, c.receiveWindowSize/(1<<10))
+ c.connection.EnsureMinimumWindowSize(protocol.ByteCount(float64(c.receiveWindowSize) * protocol.ConnectionFlowControlMultiplier))
+ }
+ c.mutex.Unlock()
+ return offset
+}
diff --git a/vendor/github.com/quic-go/quic-go/internal/handshake/aead.go b/vendor/github.com/quic-go/quic-go/internal/handshake/aead.go
new file mode 100644
index 0000000000..410745f1a8
--- /dev/null
+++ b/vendor/github.com/quic-go/quic-go/internal/handshake/aead.go
@@ -0,0 +1,161 @@
+package handshake
+
+import (
+ "crypto/cipher"
+ "encoding/binary"
+
+ "github.com/quic-go/quic-go/internal/protocol"
+ "github.com/quic-go/quic-go/internal/qtls"
+ "github.com/quic-go/quic-go/internal/utils"
+)
+
+func createAEAD(suite *qtls.CipherSuiteTLS13, trafficSecret []byte, v protocol.VersionNumber) cipher.AEAD {
+ keyLabel := hkdfLabelKeyV1
+ ivLabel := hkdfLabelIVV1
+ if v == protocol.Version2 {
+ keyLabel = hkdfLabelKeyV2
+ ivLabel = hkdfLabelIVV2
+ }
+ key := hkdfExpandLabel(suite.Hash, trafficSecret, []byte{}, keyLabel, suite.KeyLen)
+ iv := hkdfExpandLabel(suite.Hash, trafficSecret, []byte{}, ivLabel, suite.IVLen())
+ return suite.AEAD(key, iv)
+}
+
+type longHeaderSealer struct {
+ aead cipher.AEAD
+ headerProtector headerProtector
+
+ // use a single slice to avoid allocations
+ nonceBuf []byte
+}
+
+var _ LongHeaderSealer = &longHeaderSealer{}
+
+func newLongHeaderSealer(aead cipher.AEAD, headerProtector headerProtector) LongHeaderSealer {
+ return &longHeaderSealer{
+ aead: aead,
+ headerProtector: headerProtector,
+ nonceBuf: make([]byte, aead.NonceSize()),
+ }
+}
+
+func (s *longHeaderSealer) Seal(dst, src []byte, pn protocol.PacketNumber, ad []byte) []byte {
+ binary.BigEndian.PutUint64(s.nonceBuf[len(s.nonceBuf)-8:], uint64(pn))
+ // The AEAD we're using here will be the qtls.aeadAESGCM13.
+ // It uses the nonce provided here and XOR it with the IV.
+ return s.aead.Seal(dst, s.nonceBuf, src, ad)
+}
+
+func (s *longHeaderSealer) EncryptHeader(sample []byte, firstByte *byte, pnBytes []byte) {
+ s.headerProtector.EncryptHeader(sample, firstByte, pnBytes)
+}
+
+func (s *longHeaderSealer) Overhead() int {
+ return s.aead.Overhead()
+}
+
+type longHeaderOpener struct {
+ aead cipher.AEAD
+ headerProtector headerProtector
+ highestRcvdPN protocol.PacketNumber // highest packet number received (which could be successfully unprotected)
+
+ // use a single slice to avoid allocations
+ nonceBuf []byte
+}
+
+var _ LongHeaderOpener = &longHeaderOpener{}
+
+func newLongHeaderOpener(aead cipher.AEAD, headerProtector headerProtector) LongHeaderOpener {
+ return &longHeaderOpener{
+ aead: aead,
+ headerProtector: headerProtector,
+ nonceBuf: make([]byte, aead.NonceSize()),
+ }
+}
+
+func (o *longHeaderOpener) DecodePacketNumber(wirePN protocol.PacketNumber, wirePNLen protocol.PacketNumberLen) protocol.PacketNumber {
+ return protocol.DecodePacketNumber(wirePNLen, o.highestRcvdPN, wirePN)
+}
+
+func (o *longHeaderOpener) Open(dst, src []byte, pn protocol.PacketNumber, ad []byte) ([]byte, error) {
+ binary.BigEndian.PutUint64(o.nonceBuf[len(o.nonceBuf)-8:], uint64(pn))
+ // The AEAD we're using here will be the qtls.aeadAESGCM13.
+ // It uses the nonce provided here and XOR it with the IV.
+ dec, err := o.aead.Open(dst, o.nonceBuf, src, ad)
+ if err == nil {
+ o.highestRcvdPN = utils.Max(o.highestRcvdPN, pn)
+ } else {
+ err = ErrDecryptionFailed
+ }
+ return dec, err
+}
+
+func (o *longHeaderOpener) DecryptHeader(sample []byte, firstByte *byte, pnBytes []byte) {
+ o.headerProtector.DecryptHeader(sample, firstByte, pnBytes)
+}
+
+type handshakeSealer struct {
+ LongHeaderSealer
+
+ dropInitialKeys func()
+ dropped bool
+}
+
+func newHandshakeSealer(
+ aead cipher.AEAD,
+ headerProtector headerProtector,
+ dropInitialKeys func(),
+ perspective protocol.Perspective,
+) LongHeaderSealer {
+ sealer := newLongHeaderSealer(aead, headerProtector)
+ // The client drops Initial keys when sending the first Handshake packet.
+ if perspective == protocol.PerspectiveServer {
+ return sealer
+ }
+ return &handshakeSealer{
+ LongHeaderSealer: sealer,
+ dropInitialKeys: dropInitialKeys,
+ }
+}
+
+func (s *handshakeSealer) Seal(dst, src []byte, pn protocol.PacketNumber, ad []byte) []byte {
+ data := s.LongHeaderSealer.Seal(dst, src, pn, ad)
+ if !s.dropped {
+ s.dropInitialKeys()
+ s.dropped = true
+ }
+ return data
+}
+
+type handshakeOpener struct {
+ LongHeaderOpener
+
+ dropInitialKeys func()
+ dropped bool
+}
+
+func newHandshakeOpener(
+ aead cipher.AEAD,
+ headerProtector headerProtector,
+ dropInitialKeys func(),
+ perspective protocol.Perspective,
+) LongHeaderOpener {
+ opener := newLongHeaderOpener(aead, headerProtector)
+ // The server drops Initial keys when first successfully processing a Handshake packet.
+ if perspective == protocol.PerspectiveClient {
+ return opener
+ }
+ return &handshakeOpener{
+ LongHeaderOpener: opener,
+ dropInitialKeys: dropInitialKeys,
+ }
+}
+
+func (o *handshakeOpener) Open(dst, src []byte, pn protocol.PacketNumber, ad []byte) ([]byte, error) {
+ dec, err := o.LongHeaderOpener.Open(dst, src, pn, ad)
+ if err == nil && !o.dropped {
+ o.dropInitialKeys()
+ o.dropped = true
+ }
+ return dec, err
+}
diff --git a/vendor/github.com/quic-go/quic-go/internal/handshake/crypto_setup.go b/vendor/github.com/quic-go/quic-go/internal/handshake/crypto_setup.go
new file mode 100644
index 0000000000..f9665b6199
--- /dev/null
+++ b/vendor/github.com/quic-go/quic-go/internal/handshake/crypto_setup.go
@@ -0,0 +1,837 @@
+package handshake
+
+import (
+ "bytes"
+ "crypto/tls"
+ "errors"
+ "fmt"
+ "io"
+ "math"
+ "net"
+ "sync"
+ "time"
+
+ "github.com/quic-go/quic-go/internal/protocol"
+ "github.com/quic-go/quic-go/internal/qerr"
+ "github.com/quic-go/quic-go/internal/qtls"
+ "github.com/quic-go/quic-go/internal/utils"
+ "github.com/quic-go/quic-go/internal/wire"
+ "github.com/quic-go/quic-go/logging"
+ "github.com/quic-go/quic-go/quicvarint"
+)
+
+// TLS unexpected_message alert
+const alertUnexpectedMessage uint8 = 10
+
+type messageType uint8
+
+// TLS handshake message types.
+const (
+ typeClientHello messageType = 1
+ typeServerHello messageType = 2
+ typeNewSessionTicket messageType = 4
+ typeEncryptedExtensions messageType = 8
+ typeCertificate messageType = 11
+ typeCertificateRequest messageType = 13
+ typeCertificateVerify messageType = 15
+ typeFinished messageType = 20
+)
+
+func (m messageType) String() string {
+ switch m {
+ case typeClientHello:
+ return "ClientHello"
+ case typeServerHello:
+ return "ServerHello"
+ case typeNewSessionTicket:
+ return "NewSessionTicket"
+ case typeEncryptedExtensions:
+ return "EncryptedExtensions"
+ case typeCertificate:
+ return "Certificate"
+ case typeCertificateRequest:
+ return "CertificateRequest"
+ case typeCertificateVerify:
+ return "CertificateVerify"
+ case typeFinished:
+ return "Finished"
+ default:
+ return fmt.Sprintf("unknown message type: %d", m)
+ }
+}
+
+const clientSessionStateRevision = 3
+
+type conn struct {
+ localAddr, remoteAddr net.Addr
+ version protocol.VersionNumber
+}
+
+var _ ConnWithVersion = &conn{}
+
+func newConn(local, remote net.Addr, version protocol.VersionNumber) ConnWithVersion {
+ return &conn{
+ localAddr: local,
+ remoteAddr: remote,
+ version: version,
+ }
+}
+
+var _ net.Conn = &conn{}
+
+func (c *conn) Read([]byte) (int, error) { return 0, nil }
+func (c *conn) Write([]byte) (int, error) { return 0, nil }
+func (c *conn) Close() error { return nil }
+func (c *conn) RemoteAddr() net.Addr { return c.remoteAddr }
+func (c *conn) LocalAddr() net.Addr { return c.localAddr }
+func (c *conn) SetReadDeadline(time.Time) error { return nil }
+func (c *conn) SetWriteDeadline(time.Time) error { return nil }
+func (c *conn) SetDeadline(time.Time) error { return nil }
+func (c *conn) GetQUICVersion() protocol.VersionNumber { return c.version }
+
+type cryptoSetup struct {
+ tlsConf *tls.Config
+ extraConf *qtls.ExtraConfig
+ conn *qtls.Conn
+
+ version protocol.VersionNumber
+
+ messageChan chan []byte
+ isReadingHandshakeMessage chan struct{}
+ readFirstHandshakeMessage bool
+
+ ourParams *wire.TransportParameters
+ peerParams *wire.TransportParameters
+ paramsChan <-chan []byte
+
+ runner handshakeRunner
+
+ alertChan chan uint8
+ // handshakeDone is closed as soon as the go routine running qtls.Handshake() returns
+ handshakeDone chan struct{}
+ // is closed when Close() is called
+ closeChan chan struct{}
+
+ zeroRTTParameters *wire.TransportParameters
+ clientHelloWritten bool
+ clientHelloWrittenChan chan struct{} // is closed as soon as the ClientHello is written
+ zeroRTTParametersChan chan<- *wire.TransportParameters
+ allow0RTT func() bool
+
+ rttStats *utils.RTTStats
+
+ tracer logging.ConnectionTracer
+ logger utils.Logger
+
+ perspective protocol.Perspective
+
+ mutex sync.Mutex // protects all members below
+
+ handshakeCompleteTime time.Time
+
+ readEncLevel protocol.EncryptionLevel
+ writeEncLevel protocol.EncryptionLevel
+
+ zeroRTTOpener LongHeaderOpener // only set for the server
+ zeroRTTSealer LongHeaderSealer // only set for the client
+
+ initialStream io.Writer
+ initialOpener LongHeaderOpener
+ initialSealer LongHeaderSealer
+
+ handshakeStream io.Writer
+ handshakeOpener LongHeaderOpener
+ handshakeSealer LongHeaderSealer
+
+ aead *updatableAEAD
+ has1RTTSealer bool
+ has1RTTOpener bool
+}
+
+var (
+ _ qtls.RecordLayer = &cryptoSetup{}
+ _ CryptoSetup = &cryptoSetup{}
+)
+
+// NewCryptoSetupClient creates a new crypto setup for the client
+func NewCryptoSetupClient(
+ initialStream io.Writer,
+ handshakeStream io.Writer,
+ connID protocol.ConnectionID,
+ localAddr net.Addr,
+ remoteAddr net.Addr,
+ tp *wire.TransportParameters,
+ runner handshakeRunner,
+ tlsConf *tls.Config,
+ enable0RTT bool,
+ rttStats *utils.RTTStats,
+ tracer logging.ConnectionTracer,
+ logger utils.Logger,
+ version protocol.VersionNumber,
+) (CryptoSetup, <-chan *wire.TransportParameters /* ClientHello written. Receive nil for non-0-RTT */) {
+ cs, clientHelloWritten := newCryptoSetup(
+ initialStream,
+ handshakeStream,
+ connID,
+ tp,
+ runner,
+ tlsConf,
+ enable0RTT,
+ rttStats,
+ tracer,
+ logger,
+ protocol.PerspectiveClient,
+ version,
+ )
+ cs.conn = qtls.Client(newConn(localAddr, remoteAddr, version), cs.tlsConf, cs.extraConf)
+ return cs, clientHelloWritten
+}
+
+// NewCryptoSetupServer creates a new crypto setup for the server
+func NewCryptoSetupServer(
+ initialStream io.Writer,
+ handshakeStream io.Writer,
+ connID protocol.ConnectionID,
+ localAddr net.Addr,
+ remoteAddr net.Addr,
+ tp *wire.TransportParameters,
+ runner handshakeRunner,
+ tlsConf *tls.Config,
+ allow0RTT func() bool,
+ rttStats *utils.RTTStats,
+ tracer logging.ConnectionTracer,
+ logger utils.Logger,
+ version protocol.VersionNumber,
+) CryptoSetup {
+ cs, _ := newCryptoSetup(
+ initialStream,
+ handshakeStream,
+ connID,
+ tp,
+ runner,
+ tlsConf,
+ allow0RTT != nil,
+ rttStats,
+ tracer,
+ logger,
+ protocol.PerspectiveServer,
+ version,
+ )
+ cs.allow0RTT = allow0RTT
+ cs.conn = qtls.Server(newConn(localAddr, remoteAddr, version), cs.tlsConf, cs.extraConf)
+ return cs
+}
+
+func newCryptoSetup(
+ initialStream io.Writer,
+ handshakeStream io.Writer,
+ connID protocol.ConnectionID,
+ tp *wire.TransportParameters,
+ runner handshakeRunner,
+ tlsConf *tls.Config,
+ enable0RTT bool,
+ rttStats *utils.RTTStats,
+ tracer logging.ConnectionTracer,
+ logger utils.Logger,
+ perspective protocol.Perspective,
+ version protocol.VersionNumber,
+) (*cryptoSetup, <-chan *wire.TransportParameters /* ClientHello written. Receive nil for non-0-RTT */) {
+ initialSealer, initialOpener := NewInitialAEAD(connID, perspective, version)
+ if tracer != nil {
+ tracer.UpdatedKeyFromTLS(protocol.EncryptionInitial, protocol.PerspectiveClient)
+ tracer.UpdatedKeyFromTLS(protocol.EncryptionInitial, protocol.PerspectiveServer)
+ }
+ extHandler := newExtensionHandler(tp.Marshal(perspective), perspective, version)
+ zeroRTTParametersChan := make(chan *wire.TransportParameters, 1)
+ cs := &cryptoSetup{
+ tlsConf: tlsConf,
+ initialStream: initialStream,
+ initialSealer: initialSealer,
+ initialOpener: initialOpener,
+ handshakeStream: handshakeStream,
+ aead: newUpdatableAEAD(rttStats, tracer, logger, version),
+ readEncLevel: protocol.EncryptionInitial,
+ writeEncLevel: protocol.EncryptionInitial,
+ runner: runner,
+ ourParams: tp,
+ paramsChan: extHandler.TransportParameters(),
+ rttStats: rttStats,
+ tracer: tracer,
+ logger: logger,
+ perspective: perspective,
+ handshakeDone: make(chan struct{}),
+ alertChan: make(chan uint8),
+ clientHelloWrittenChan: make(chan struct{}),
+ zeroRTTParametersChan: zeroRTTParametersChan,
+ messageChan: make(chan []byte, 100),
+ isReadingHandshakeMessage: make(chan struct{}),
+ closeChan: make(chan struct{}),
+ version: version,
+ }
+ var maxEarlyData uint32
+ if enable0RTT {
+ maxEarlyData = math.MaxUint32
+ }
+ cs.extraConf = &qtls.ExtraConfig{
+ GetExtensions: extHandler.GetExtensions,
+ ReceivedExtensions: extHandler.ReceivedExtensions,
+ AlternativeRecordLayer: cs,
+ EnforceNextProtoSelection: true,
+ MaxEarlyData: maxEarlyData,
+ Accept0RTT: cs.accept0RTT,
+ Rejected0RTT: cs.rejected0RTT,
+ Enable0RTT: enable0RTT,
+ GetAppDataForSessionState: cs.marshalDataForSessionState,
+ SetAppDataFromSessionState: cs.handleDataFromSessionState,
+ }
+ return cs, zeroRTTParametersChan
+}
+
+func (h *cryptoSetup) ChangeConnectionID(id protocol.ConnectionID) {
+ initialSealer, initialOpener := NewInitialAEAD(id, h.perspective, h.version)
+ h.initialSealer = initialSealer
+ h.initialOpener = initialOpener
+ if h.tracer != nil {
+ h.tracer.UpdatedKeyFromTLS(protocol.EncryptionInitial, protocol.PerspectiveClient)
+ h.tracer.UpdatedKeyFromTLS(protocol.EncryptionInitial, protocol.PerspectiveServer)
+ }
+}
+
+func (h *cryptoSetup) SetLargest1RTTAcked(pn protocol.PacketNumber) error {
+ return h.aead.SetLargestAcked(pn)
+}
+
+func (h *cryptoSetup) RunHandshake() {
+ // Handle errors that might occur when HandleData() is called.
+ handshakeComplete := make(chan struct{})
+ handshakeErrChan := make(chan error, 1)
+ go func() {
+ defer close(h.handshakeDone)
+ if err := h.conn.Handshake(); err != nil {
+ handshakeErrChan <- err
+ return
+ }
+ close(handshakeComplete)
+ }()
+
+ if h.perspective == protocol.PerspectiveClient {
+ select {
+ case err := <-handshakeErrChan:
+ h.onError(0, err.Error())
+ return
+ case <-h.clientHelloWrittenChan:
+ }
+ }
+
+ select {
+ case <-handshakeComplete: // return when the handshake is done
+ h.mutex.Lock()
+ h.handshakeCompleteTime = time.Now()
+ h.mutex.Unlock()
+ h.runner.OnHandshakeComplete()
+ case <-h.closeChan:
+ // wait until the Handshake() go routine has returned
+ <-h.handshakeDone
+ case alert := <-h.alertChan:
+ handshakeErr := <-handshakeErrChan
+ h.onError(alert, handshakeErr.Error())
+ }
+}
+
+func (h *cryptoSetup) onError(alert uint8, message string) {
+ var err error
+ if alert == 0 {
+ err = &qerr.TransportError{ErrorCode: qerr.InternalError, ErrorMessage: message}
+ } else {
+ err = qerr.NewLocalCryptoError(alert, message)
+ }
+ h.runner.OnError(err)
+}
+
+// Close closes the crypto setup.
+// It aborts the handshake, if it is still running.
+// It must only be called once.
+func (h *cryptoSetup) Close() error {
+ close(h.closeChan)
+ // wait until qtls.Handshake() actually returned
+ <-h.handshakeDone
+ return nil
+}
+
+// handleMessage handles a TLS handshake message.
+// It is called by the crypto streams when a new message is available.
+// It returns if it is done with messages on the same encryption level.
+func (h *cryptoSetup) HandleMessage(data []byte, encLevel protocol.EncryptionLevel) bool /* stream finished */ {
+ msgType := messageType(data[0])
+ h.logger.Debugf("Received %s message (%d bytes, encryption level: %s)", msgType, len(data), encLevel)
+ if err := h.checkEncryptionLevel(msgType, encLevel); err != nil {
+ h.onError(alertUnexpectedMessage, err.Error())
+ return false
+ }
+ h.messageChan <- data
+ if encLevel == protocol.Encryption1RTT {
+ h.handlePostHandshakeMessage()
+ return false
+ }
+readLoop:
+ for {
+ select {
+ case data := <-h.paramsChan:
+ if data == nil {
+ h.onError(0x6d, "missing quic_transport_parameters extension")
+ } else {
+ h.handleTransportParameters(data)
+ }
+ case <-h.isReadingHandshakeMessage:
+ break readLoop
+ case <-h.handshakeDone:
+ break readLoop
+ case <-h.closeChan:
+ break readLoop
+ }
+ }
+ // We're done with the Initial encryption level after processing a ClientHello / ServerHello,
+ // but only if a handshake opener and sealer was created.
+ // Otherwise, a HelloRetryRequest was performed.
+ // We're done with the Handshake encryption level after processing the Finished message.
+ return ((msgType == typeClientHello || msgType == typeServerHello) && h.handshakeOpener != nil && h.handshakeSealer != nil) ||
+ msgType == typeFinished
+}
+
+func (h *cryptoSetup) checkEncryptionLevel(msgType messageType, encLevel protocol.EncryptionLevel) error {
+ var expected protocol.EncryptionLevel
+ switch msgType {
+ case typeClientHello,
+ typeServerHello:
+ expected = protocol.EncryptionInitial
+ case typeEncryptedExtensions,
+ typeCertificate,
+ typeCertificateRequest,
+ typeCertificateVerify,
+ typeFinished:
+ expected = protocol.EncryptionHandshake
+ case typeNewSessionTicket:
+ expected = protocol.Encryption1RTT
+ default:
+ return fmt.Errorf("unexpected handshake message: %d", msgType)
+ }
+ if encLevel != expected {
+ return fmt.Errorf("expected handshake message %s to have encryption level %s, has %s", msgType, expected, encLevel)
+ }
+ return nil
+}
+
+func (h *cryptoSetup) handleTransportParameters(data []byte) {
+ var tp wire.TransportParameters
+ if err := tp.Unmarshal(data, h.perspective.Opposite()); err != nil {
+ h.runner.OnError(&qerr.TransportError{
+ ErrorCode: qerr.TransportParameterError,
+ ErrorMessage: err.Error(),
+ })
+ }
+ h.peerParams = &tp
+ h.runner.OnReceivedParams(h.peerParams)
+}
+
+// must be called after receiving the transport parameters
+func (h *cryptoSetup) marshalDataForSessionState() []byte {
+ b := make([]byte, 0, 256)
+ b = quicvarint.Append(b, clientSessionStateRevision)
+ b = quicvarint.Append(b, uint64(h.rttStats.SmoothedRTT().Microseconds()))
+ return h.peerParams.MarshalForSessionTicket(b)
+}
+
+func (h *cryptoSetup) handleDataFromSessionState(data []byte) {
+ tp, err := h.handleDataFromSessionStateImpl(data)
+ if err != nil {
+ h.logger.Debugf("Restoring of transport parameters from session ticket failed: %s", err.Error())
+ return
+ }
+ h.zeroRTTParameters = tp
+}
+
+func (h *cryptoSetup) handleDataFromSessionStateImpl(data []byte) (*wire.TransportParameters, error) {
+ r := bytes.NewReader(data)
+ ver, err := quicvarint.Read(r)
+ if err != nil {
+ return nil, err
+ }
+ if ver != clientSessionStateRevision {
+ return nil, fmt.Errorf("mismatching version. Got %d, expected %d", ver, clientSessionStateRevision)
+ }
+ rtt, err := quicvarint.Read(r)
+ if err != nil {
+ return nil, err
+ }
+ h.rttStats.SetInitialRTT(time.Duration(rtt) * time.Microsecond)
+ var tp wire.TransportParameters
+ if err := tp.UnmarshalFromSessionTicket(r); err != nil {
+ return nil, err
+ }
+ return &tp, nil
+}
+
+// only valid for the server
+func (h *cryptoSetup) GetSessionTicket() ([]byte, error) {
+ var appData []byte
+ // Save transport parameters to the session ticket if we're allowing 0-RTT.
+ if h.extraConf.MaxEarlyData > 0 {
+ appData = (&sessionTicket{
+ Parameters: h.ourParams,
+ RTT: h.rttStats.SmoothedRTT(),
+ }).Marshal()
+ }
+ return h.conn.GetSessionTicket(appData)
+}
+
+// accept0RTT is called for the server when receiving the client's session ticket.
+// It decides whether to accept 0-RTT.
+func (h *cryptoSetup) accept0RTT(sessionTicketData []byte) bool {
+ var t sessionTicket
+ if err := t.Unmarshal(sessionTicketData); err != nil {
+ h.logger.Debugf("Unmarshalling transport parameters from session ticket failed: %s", err.Error())
+ return false
+ }
+ valid := h.ourParams.ValidFor0RTT(t.Parameters)
+ if !valid {
+ h.logger.Debugf("Transport parameters changed. Rejecting 0-RTT.")
+ return false
+ }
+ if !h.allow0RTT() {
+ h.logger.Debugf("0-RTT not allowed. Rejecting 0-RTT.")
+ return false
+ }
+ h.logger.Debugf("Accepting 0-RTT. Restoring RTT from session ticket: %s", t.RTT)
+ h.rttStats.SetInitialRTT(t.RTT)
+ return true
+}
+
+// rejected0RTT is called for the client when the server rejects 0-RTT.
+func (h *cryptoSetup) rejected0RTT() {
+ h.logger.Debugf("0-RTT was rejected. Dropping 0-RTT keys.")
+
+ h.mutex.Lock()
+ had0RTTKeys := h.zeroRTTSealer != nil
+ h.zeroRTTSealer = nil
+ h.mutex.Unlock()
+
+ if had0RTTKeys {
+ h.runner.DropKeys(protocol.Encryption0RTT)
+ }
+}
+
+func (h *cryptoSetup) handlePostHandshakeMessage() {
+ // make sure the handshake has already completed
+ <-h.handshakeDone
+
+ done := make(chan struct{})
+ defer close(done)
+
+ // h.alertChan is an unbuffered channel.
+ // If an error occurs during conn.HandlePostHandshakeMessage,
+ // it will be sent on this channel.
+ // Read it from a go-routine so that HandlePostHandshakeMessage doesn't deadlock.
+ alertChan := make(chan uint8, 1)
+ go func() {
+ <-h.isReadingHandshakeMessage
+ select {
+ case alert := <-h.alertChan:
+ alertChan <- alert
+ case <-done:
+ }
+ }()
+
+ if err := h.conn.HandlePostHandshakeMessage(); err != nil {
+ select {
+ case <-h.closeChan:
+ case alert := <-alertChan:
+ h.onError(alert, err.Error())
+ }
+ }
+}
+
+// ReadHandshakeMessage is called by TLS.
+// It blocks until a new handshake message is available.
+func (h *cryptoSetup) ReadHandshakeMessage() ([]byte, error) {
+ if !h.readFirstHandshakeMessage {
+ h.readFirstHandshakeMessage = true
+ } else {
+ select {
+ case h.isReadingHandshakeMessage <- struct{}{}:
+ case <-h.closeChan:
+ return nil, errors.New("error while handling the handshake message")
+ }
+ }
+ select {
+ case msg := <-h.messageChan:
+ return msg, nil
+ case <-h.closeChan:
+ return nil, errors.New("error while handling the handshake message")
+ }
+}
+
+func (h *cryptoSetup) SetReadKey(encLevel qtls.EncryptionLevel, suite *qtls.CipherSuiteTLS13, trafficSecret []byte) {
+ h.mutex.Lock()
+ switch encLevel {
+ case qtls.Encryption0RTT:
+ if h.perspective == protocol.PerspectiveClient {
+ panic("Received 0-RTT read key for the client")
+ }
+ h.zeroRTTOpener = newLongHeaderOpener(
+ createAEAD(suite, trafficSecret, h.version),
+ newHeaderProtector(suite, trafficSecret, true, h.version),
+ )
+ h.mutex.Unlock()
+ if h.logger.Debug() {
+ h.logger.Debugf("Installed 0-RTT Read keys (using %s)", tls.CipherSuiteName(suite.ID))
+ }
+ if h.tracer != nil {
+ h.tracer.UpdatedKeyFromTLS(protocol.Encryption0RTT, h.perspective.Opposite())
+ }
+ return
+ case qtls.EncryptionHandshake:
+ h.readEncLevel = protocol.EncryptionHandshake
+ h.handshakeOpener = newHandshakeOpener(
+ createAEAD(suite, trafficSecret, h.version),
+ newHeaderProtector(suite, trafficSecret, true, h.version),
+ h.dropInitialKeys,
+ h.perspective,
+ )
+ if h.logger.Debug() {
+ h.logger.Debugf("Installed Handshake Read keys (using %s)", tls.CipherSuiteName(suite.ID))
+ }
+ case qtls.EncryptionApplication:
+ h.readEncLevel = protocol.Encryption1RTT
+ h.aead.SetReadKey(suite, trafficSecret)
+ h.has1RTTOpener = true
+ if h.logger.Debug() {
+ h.logger.Debugf("Installed 1-RTT Read keys (using %s)", tls.CipherSuiteName(suite.ID))
+ }
+ default:
+ panic("unexpected read encryption level")
+ }
+ h.mutex.Unlock()
+ if h.tracer != nil {
+ h.tracer.UpdatedKeyFromTLS(h.readEncLevel, h.perspective.Opposite())
+ }
+}
+
+func (h *cryptoSetup) SetWriteKey(encLevel qtls.EncryptionLevel, suite *qtls.CipherSuiteTLS13, trafficSecret []byte) {
+ h.mutex.Lock()
+ switch encLevel {
+ case qtls.Encryption0RTT:
+ if h.perspective == protocol.PerspectiveServer {
+ panic("Received 0-RTT write key for the server")
+ }
+ h.zeroRTTSealer = newLongHeaderSealer(
+ createAEAD(suite, trafficSecret, h.version),
+ newHeaderProtector(suite, trafficSecret, true, h.version),
+ )
+ h.mutex.Unlock()
+ if h.logger.Debug() {
+ h.logger.Debugf("Installed 0-RTT Write keys (using %s)", tls.CipherSuiteName(suite.ID))
+ }
+ if h.tracer != nil {
+ h.tracer.UpdatedKeyFromTLS(protocol.Encryption0RTT, h.perspective)
+ }
+ return
+ case qtls.EncryptionHandshake:
+ h.writeEncLevel = protocol.EncryptionHandshake
+ h.handshakeSealer = newHandshakeSealer(
+ createAEAD(suite, trafficSecret, h.version),
+ newHeaderProtector(suite, trafficSecret, true, h.version),
+ h.dropInitialKeys,
+ h.perspective,
+ )
+ if h.logger.Debug() {
+ h.logger.Debugf("Installed Handshake Write keys (using %s)", tls.CipherSuiteName(suite.ID))
+ }
+ case qtls.EncryptionApplication:
+ h.writeEncLevel = protocol.Encryption1RTT
+ h.aead.SetWriteKey(suite, trafficSecret)
+ h.has1RTTSealer = true
+ if h.logger.Debug() {
+ h.logger.Debugf("Installed 1-RTT Write keys (using %s)", tls.CipherSuiteName(suite.ID))
+ }
+ if h.zeroRTTSealer != nil {
+ h.zeroRTTSealer = nil
+ h.logger.Debugf("Dropping 0-RTT keys.")
+ if h.tracer != nil {
+ h.tracer.DroppedEncryptionLevel(protocol.Encryption0RTT)
+ }
+ }
+ default:
+ panic("unexpected write encryption level")
+ }
+ h.mutex.Unlock()
+ if h.tracer != nil {
+ h.tracer.UpdatedKeyFromTLS(h.writeEncLevel, h.perspective)
+ }
+}
+
+// WriteRecord is called when TLS writes data
+func (h *cryptoSetup) WriteRecord(p []byte) (int, error) {
+ h.mutex.Lock()
+ defer h.mutex.Unlock()
+
+ //nolint:exhaustive // LS records can only be written for Initial and Handshake.
+ switch h.writeEncLevel {
+ case protocol.EncryptionInitial:
+ // assume that the first WriteRecord call contains the ClientHello
+ n, err := h.initialStream.Write(p)
+ if !h.clientHelloWritten && h.perspective == protocol.PerspectiveClient {
+ h.clientHelloWritten = true
+ close(h.clientHelloWrittenChan)
+ if h.zeroRTTSealer != nil && h.zeroRTTParameters != nil {
+ h.logger.Debugf("Doing 0-RTT.")
+ h.zeroRTTParametersChan <- h.zeroRTTParameters
+ } else {
+ h.logger.Debugf("Not doing 0-RTT.")
+ h.zeroRTTParametersChan <- nil
+ }
+ }
+ return n, err
+ case protocol.EncryptionHandshake:
+ return h.handshakeStream.Write(p)
+ default:
+ panic(fmt.Sprintf("unexpected write encryption level: %s", h.writeEncLevel))
+ }
+}
+
+func (h *cryptoSetup) SendAlert(alert uint8) {
+ select {
+ case h.alertChan <- alert:
+ case <-h.closeChan:
+ // no need to send an alert when we've already closed
+ }
+}
+
+// used a callback in the handshakeSealer and handshakeOpener
+func (h *cryptoSetup) dropInitialKeys() {
+ h.mutex.Lock()
+ h.initialOpener = nil
+ h.initialSealer = nil
+ h.mutex.Unlock()
+ h.runner.DropKeys(protocol.EncryptionInitial)
+ h.logger.Debugf("Dropping Initial keys.")
+}
+
+func (h *cryptoSetup) SetHandshakeConfirmed() {
+ h.aead.SetHandshakeConfirmed()
+ // drop Handshake keys
+ var dropped bool
+ h.mutex.Lock()
+ if h.handshakeOpener != nil {
+ h.handshakeOpener = nil
+ h.handshakeSealer = nil
+ dropped = true
+ }
+ h.mutex.Unlock()
+ if dropped {
+ h.runner.DropKeys(protocol.EncryptionHandshake)
+ h.logger.Debugf("Dropping Handshake keys.")
+ }
+}
+
+func (h *cryptoSetup) GetInitialSealer() (LongHeaderSealer, error) {
+ h.mutex.Lock()
+ defer h.mutex.Unlock()
+
+ if h.initialSealer == nil {
+ return nil, ErrKeysDropped
+ }
+ return h.initialSealer, nil
+}
+
+func (h *cryptoSetup) Get0RTTSealer() (LongHeaderSealer, error) {
+ h.mutex.Lock()
+ defer h.mutex.Unlock()
+
+ if h.zeroRTTSealer == nil {
+ return nil, ErrKeysDropped
+ }
+ return h.zeroRTTSealer, nil
+}
+
+func (h *cryptoSetup) GetHandshakeSealer() (LongHeaderSealer, error) {
+ h.mutex.Lock()
+ defer h.mutex.Unlock()
+
+ if h.handshakeSealer == nil {
+ if h.initialSealer == nil {
+ return nil, ErrKeysDropped
+ }
+ return nil, ErrKeysNotYetAvailable
+ }
+ return h.handshakeSealer, nil
+}
+
+func (h *cryptoSetup) Get1RTTSealer() (ShortHeaderSealer, error) {
+ h.mutex.Lock()
+ defer h.mutex.Unlock()
+
+ if !h.has1RTTSealer {
+ return nil, ErrKeysNotYetAvailable
+ }
+ return h.aead, nil
+}
+
+func (h *cryptoSetup) GetInitialOpener() (LongHeaderOpener, error) {
+ h.mutex.Lock()
+ defer h.mutex.Unlock()
+
+ if h.initialOpener == nil {
+ return nil, ErrKeysDropped
+ }
+ return h.initialOpener, nil
+}
+
+func (h *cryptoSetup) Get0RTTOpener() (LongHeaderOpener, error) {
+ h.mutex.Lock()
+ defer h.mutex.Unlock()
+
+ if h.zeroRTTOpener == nil {
+ if h.initialOpener != nil {
+ return nil, ErrKeysNotYetAvailable
+ }
+ // if the initial opener is also not available, the keys were already dropped
+ return nil, ErrKeysDropped
+ }
+ return h.zeroRTTOpener, nil
+}
+
+func (h *cryptoSetup) GetHandshakeOpener() (LongHeaderOpener, error) {
+ h.mutex.Lock()
+ defer h.mutex.Unlock()
+
+ if h.handshakeOpener == nil {
+ if h.initialOpener != nil {
+ return nil, ErrKeysNotYetAvailable
+ }
+ // if the initial opener is also not available, the keys were already dropped
+ return nil, ErrKeysDropped
+ }
+ return h.handshakeOpener, nil
+}
+
+func (h *cryptoSetup) Get1RTTOpener() (ShortHeaderOpener, error) {
+ h.mutex.Lock()
+ defer h.mutex.Unlock()
+
+ if h.zeroRTTOpener != nil && time.Since(h.handshakeCompleteTime) > 3*h.rttStats.PTO(true) {
+ h.zeroRTTOpener = nil
+ h.logger.Debugf("Dropping 0-RTT keys.")
+ if h.tracer != nil {
+ h.tracer.DroppedEncryptionLevel(protocol.Encryption0RTT)
+ }
+ }
+
+ if !h.has1RTTOpener {
+ return nil, ErrKeysNotYetAvailable
+ }
+ return h.aead, nil
+}
+
+func (h *cryptoSetup) ConnectionState() ConnectionState {
+ return qtls.GetConnectionState(h.conn)
+}
diff --git a/vendor/github.com/quic-go/quic-go/internal/handshake/header_protector.go b/vendor/github.com/quic-go/quic-go/internal/handshake/header_protector.go
new file mode 100644
index 0000000000..274fb30cbd
--- /dev/null
+++ b/vendor/github.com/quic-go/quic-go/internal/handshake/header_protector.go
@@ -0,0 +1,136 @@
+package handshake
+
+import (
+ "crypto/aes"
+ "crypto/cipher"
+ "crypto/tls"
+ "encoding/binary"
+ "fmt"
+
+ "golang.org/x/crypto/chacha20"
+
+ "github.com/quic-go/quic-go/internal/protocol"
+ "github.com/quic-go/quic-go/internal/qtls"
+)
+
+type headerProtector interface {
+ EncryptHeader(sample []byte, firstByte *byte, hdrBytes []byte)
+ DecryptHeader(sample []byte, firstByte *byte, hdrBytes []byte)
+}
+
+func hkdfHeaderProtectionLabel(v protocol.VersionNumber) string {
+ if v == protocol.Version2 {
+ return "quicv2 hp"
+ }
+ return "quic hp"
+}
+
+func newHeaderProtector(suite *qtls.CipherSuiteTLS13, trafficSecret []byte, isLongHeader bool, v protocol.VersionNumber) headerProtector {
+ hkdfLabel := hkdfHeaderProtectionLabel(v)
+ switch suite.ID {
+ case tls.TLS_AES_128_GCM_SHA256, tls.TLS_AES_256_GCM_SHA384:
+ return newAESHeaderProtector(suite, trafficSecret, isLongHeader, hkdfLabel)
+ case tls.TLS_CHACHA20_POLY1305_SHA256:
+ return newChaChaHeaderProtector(suite, trafficSecret, isLongHeader, hkdfLabel)
+ default:
+ panic(fmt.Sprintf("Invalid cipher suite id: %d", suite.ID))
+ }
+}
+
+type aesHeaderProtector struct {
+ mask []byte
+ block cipher.Block
+ isLongHeader bool
+}
+
+var _ headerProtector = &aesHeaderProtector{}
+
+func newAESHeaderProtector(suite *qtls.CipherSuiteTLS13, trafficSecret []byte, isLongHeader bool, hkdfLabel string) headerProtector {
+ hpKey := hkdfExpandLabel(suite.Hash, trafficSecret, []byte{}, hkdfLabel, suite.KeyLen)
+ block, err := aes.NewCipher(hpKey)
+ if err != nil {
+ panic(fmt.Sprintf("error creating new AES cipher: %s", err))
+ }
+ return &aesHeaderProtector{
+ block: block,
+ mask: make([]byte, block.BlockSize()),
+ isLongHeader: isLongHeader,
+ }
+}
+
+func (p *aesHeaderProtector) DecryptHeader(sample []byte, firstByte *byte, hdrBytes []byte) {
+ p.apply(sample, firstByte, hdrBytes)
+}
+
+func (p *aesHeaderProtector) EncryptHeader(sample []byte, firstByte *byte, hdrBytes []byte) {
+ p.apply(sample, firstByte, hdrBytes)
+}
+
+func (p *aesHeaderProtector) apply(sample []byte, firstByte *byte, hdrBytes []byte) {
+ if len(sample) != len(p.mask) {
+ panic("invalid sample size")
+ }
+ p.block.Encrypt(p.mask, sample)
+ if p.isLongHeader {
+ *firstByte ^= p.mask[0] & 0xf
+ } else {
+ *firstByte ^= p.mask[0] & 0x1f
+ }
+ for i := range hdrBytes {
+ hdrBytes[i] ^= p.mask[i+1]
+ }
+}
+
+type chachaHeaderProtector struct {
+ mask [5]byte
+
+ key [32]byte
+ isLongHeader bool
+}
+
+var _ headerProtector = &chachaHeaderProtector{}
+
+func newChaChaHeaderProtector(suite *qtls.CipherSuiteTLS13, trafficSecret []byte, isLongHeader bool, hkdfLabel string) headerProtector {
+ hpKey := hkdfExpandLabel(suite.Hash, trafficSecret, []byte{}, hkdfLabel, suite.KeyLen)
+
+ p := &chachaHeaderProtector{
+ isLongHeader: isLongHeader,
+ }
+ copy(p.key[:], hpKey)
+ return p
+}
+
+func (p *chachaHeaderProtector) DecryptHeader(sample []byte, firstByte *byte, hdrBytes []byte) {
+ p.apply(sample, firstByte, hdrBytes)
+}
+
+func (p *chachaHeaderProtector) EncryptHeader(sample []byte, firstByte *byte, hdrBytes []byte) {
+ p.apply(sample, firstByte, hdrBytes)
+}
+
+func (p *chachaHeaderProtector) apply(sample []byte, firstByte *byte, hdrBytes []byte) {
+ if len(sample) != 16 {
+ panic("invalid sample size")
+ }
+ for i := 0; i < 5; i++ {
+ p.mask[i] = 0
+ }
+ cipher, err := chacha20.NewUnauthenticatedCipher(p.key[:], sample[4:])
+ if err != nil {
+ panic(err)
+ }
+ cipher.SetCounter(binary.LittleEndian.Uint32(sample[:4]))
+ cipher.XORKeyStream(p.mask[:], p.mask[:])
+ p.applyMask(firstByte, hdrBytes)
+}
+
+func (p *chachaHeaderProtector) applyMask(firstByte *byte, hdrBytes []byte) {
+ if p.isLongHeader {
+ *firstByte ^= p.mask[0] & 0xf
+ } else {
+ *firstByte ^= p.mask[0] & 0x1f
+ }
+ for i := range hdrBytes {
+ hdrBytes[i] ^= p.mask[i+1]
+ }
+}
diff --git a/vendor/github.com/quic-go/quic-go/internal/handshake/hkdf.go b/vendor/github.com/quic-go/quic-go/internal/handshake/hkdf.go
new file mode 100644
index 0000000000..c4fd86c57b
--- /dev/null
+++ b/vendor/github.com/quic-go/quic-go/internal/handshake/hkdf.go
@@ -0,0 +1,29 @@
+package handshake
+
+import (
+ "crypto"
+ "encoding/binary"
+
+ "golang.org/x/crypto/hkdf"
+)
+
+// hkdfExpandLabel HKDF expands a label.
+// Since this implementation avoids using a cryptobyte.Builder, it is about 15% faster than the
+// hkdfExpandLabel in the standard library.
+func hkdfExpandLabel(hash crypto.Hash, secret, context []byte, label string, length int) []byte {
+ b := make([]byte, 3, 3+6+len(label)+1+len(context))
+ binary.BigEndian.PutUint16(b, uint16(length))
+ b[2] = uint8(6 + len(label))
+ b = append(b, []byte("tls13 ")...)
+ b = append(b, []byte(label)...)
+ b = b[:3+6+len(label)+1]
+ b[3+6+len(label)] = uint8(len(context))
+ b = append(b, context...)
+
+ out := make([]byte, length)
+ n, err := hkdf.Expand(hash.New, secret, b).Read(out)
+ if err != nil || n != length {
+ panic("quic: HKDF-Expand-Label invocation failed unexpectedly")
+ }
+ return out
+}
diff --git a/vendor/github.com/quic-go/quic-go/internal/handshake/initial_aead.go b/vendor/github.com/quic-go/quic-go/internal/handshake/initial_aead.go
new file mode 100644
index 0000000000..3967fdb83a
--- /dev/null
+++ b/vendor/github.com/quic-go/quic-go/internal/handshake/initial_aead.go
@@ -0,0 +1,81 @@
+package handshake
+
+import (
+ "crypto"
+ "crypto/tls"
+
+ "golang.org/x/crypto/hkdf"
+
+ "github.com/quic-go/quic-go/internal/protocol"
+ "github.com/quic-go/quic-go/internal/qtls"
+)
+
+var (
+ quicSaltOld = []byte{0xaf, 0xbf, 0xec, 0x28, 0x99, 0x93, 0xd2, 0x4c, 0x9e, 0x97, 0x86, 0xf1, 0x9c, 0x61, 0x11, 0xe0, 0x43, 0x90, 0xa8, 0x99}
+ quicSaltV1 = []byte{0x38, 0x76, 0x2c, 0xf7, 0xf5, 0x59, 0x34, 0xb3, 0x4d, 0x17, 0x9a, 0xe6, 0xa4, 0xc8, 0x0c, 0xad, 0xcc, 0xbb, 0x7f, 0x0a}
+ quicSaltV2 = []byte{0x0d, 0xed, 0xe3, 0xde, 0xf7, 0x00, 0xa6, 0xdb, 0x81, 0x93, 0x81, 0xbe, 0x6e, 0x26, 0x9d, 0xcb, 0xf9, 0xbd, 0x2e, 0xd9}
+)
+
+const (
+ hkdfLabelKeyV1 = "quic key"
+ hkdfLabelKeyV2 = "quicv2 key"
+ hkdfLabelIVV1 = "quic iv"
+ hkdfLabelIVV2 = "quicv2 iv"
+)
+
+func getSalt(v protocol.VersionNumber) []byte {
+ if v == protocol.Version2 {
+ return quicSaltV2
+ }
+ if v == protocol.Version1 {
+ return quicSaltV1
+ }
+ return quicSaltOld
+}
+
+var initialSuite = &qtls.CipherSuiteTLS13{
+ ID: tls.TLS_AES_128_GCM_SHA256,
+ KeyLen: 16,
+ AEAD: qtls.AEADAESGCMTLS13,
+ Hash: crypto.SHA256,
+}
+
+// NewInitialAEAD creates a new AEAD for Initial encryption / decryption.
+func NewInitialAEAD(connID protocol.ConnectionID, pers protocol.Perspective, v protocol.VersionNumber) (LongHeaderSealer, LongHeaderOpener) {
+ clientSecret, serverSecret := computeSecrets(connID, v)
+ var mySecret, otherSecret []byte
+ if pers == protocol.PerspectiveClient {
+ mySecret = clientSecret
+ otherSecret = serverSecret
+ } else {
+ mySecret = serverSecret
+ otherSecret = clientSecret
+ }
+ myKey, myIV := computeInitialKeyAndIV(mySecret, v)
+ otherKey, otherIV := computeInitialKeyAndIV(otherSecret, v)
+
+ encrypter := qtls.AEADAESGCMTLS13(myKey, myIV)
+ decrypter := qtls.AEADAESGCMTLS13(otherKey, otherIV)
+
+ return newLongHeaderSealer(encrypter, newHeaderProtector(initialSuite, mySecret, true, v)),
+ newLongHeaderOpener(decrypter, newAESHeaderProtector(initialSuite, otherSecret, true, hkdfHeaderProtectionLabel(v)))
+}
+
+func computeSecrets(connID protocol.ConnectionID, v protocol.VersionNumber) (clientSecret, serverSecret []byte) {
+ initialSecret := hkdf.Extract(crypto.SHA256.New, connID.Bytes(), getSalt(v))
+ clientSecret = hkdfExpandLabel(crypto.SHA256, initialSecret, []byte{}, "client in", crypto.SHA256.Size())
+ serverSecret = hkdfExpandLabel(crypto.SHA256, initialSecret, []byte{}, "server in", crypto.SHA256.Size())
+ return
+}
+
+func computeInitialKeyAndIV(secret []byte, v protocol.VersionNumber) (key, iv []byte) {
+ keyLabel := hkdfLabelKeyV1
+ ivLabel := hkdfLabelIVV1
+ if v == protocol.Version2 {
+ keyLabel = hkdfLabelKeyV2
+ ivLabel = hkdfLabelIVV2
+ }
+ key = hkdfExpandLabel(crypto.SHA256, secret, []byte{}, keyLabel, 16)
+ iv = hkdfExpandLabel(crypto.SHA256, secret, []byte{}, ivLabel, 12)
+ return
+}
diff --git a/vendor/github.com/quic-go/quic-go/internal/handshake/interface.go b/vendor/github.com/quic-go/quic-go/internal/handshake/interface.go
new file mode 100644
index 0000000000..e7baea9065
--- /dev/null
+++ b/vendor/github.com/quic-go/quic-go/internal/handshake/interface.go
@@ -0,0 +1,102 @@
+package handshake
+
+import (
+ "errors"
+ "io"
+ "net"
+ "time"
+
+ "github.com/quic-go/quic-go/internal/protocol"
+ "github.com/quic-go/quic-go/internal/qtls"
+ "github.com/quic-go/quic-go/internal/wire"
+)
+
+var (
+ // ErrKeysNotYetAvailable is returned when an opener or a sealer is requested for an encryption level,
+ // but the corresponding opener has not yet been initialized
+ // This can happen when packets arrive out of order.
+ ErrKeysNotYetAvailable = errors.New("CryptoSetup: keys at this encryption level not yet available")
+ // ErrKeysDropped is returned when an opener or a sealer is requested for an encryption level,
+ // but the corresponding keys have already been dropped.
+ ErrKeysDropped = errors.New("CryptoSetup: keys were already dropped")
+ // ErrDecryptionFailed is returned when the AEAD fails to open the packet.
+ ErrDecryptionFailed = errors.New("decryption failed")
+)
+
+// ConnectionState contains information about the state of the connection.
+type ConnectionState = qtls.ConnectionState
+
+type headerDecryptor interface {
+ DecryptHeader(sample []byte, firstByte *byte, pnBytes []byte)
+}
+
+// LongHeaderOpener opens a long header packet
+type LongHeaderOpener interface {
+ headerDecryptor
+ DecodePacketNumber(wirePN protocol.PacketNumber, wirePNLen protocol.PacketNumberLen) protocol.PacketNumber
+ Open(dst, src []byte, pn protocol.PacketNumber, associatedData []byte) ([]byte, error)
+}
+
+// ShortHeaderOpener opens a short header packet
+type ShortHeaderOpener interface {
+ headerDecryptor
+ DecodePacketNumber(wirePN protocol.PacketNumber, wirePNLen protocol.PacketNumberLen) protocol.PacketNumber
+ Open(dst, src []byte, rcvTime time.Time, pn protocol.PacketNumber, kp protocol.KeyPhaseBit, associatedData []byte) ([]byte, error)
+}
+
+// LongHeaderSealer seals a long header packet
+type LongHeaderSealer interface {
+ Seal(dst, src []byte, packetNumber protocol.PacketNumber, associatedData []byte) []byte
+ EncryptHeader(sample []byte, firstByte *byte, pnBytes []byte)
+ Overhead() int
+}
+
+// ShortHeaderSealer seals a short header packet
+type ShortHeaderSealer interface {
+ LongHeaderSealer
+ KeyPhase() protocol.KeyPhaseBit
+}
+
+// A tlsExtensionHandler sends and received the QUIC TLS extension.
+type tlsExtensionHandler interface {
+ GetExtensions(msgType uint8) []qtls.Extension
+ ReceivedExtensions(msgType uint8, exts []qtls.Extension)
+ TransportParameters() <-chan []byte
+}
+
+type handshakeRunner interface {
+ OnReceivedParams(*wire.TransportParameters)
+ OnHandshakeComplete()
+ OnError(error)
+ DropKeys(protocol.EncryptionLevel)
+}
+
+// CryptoSetup handles the handshake and protecting / unprotecting packets
+type CryptoSetup interface {
+ RunHandshake()
+ io.Closer
+ ChangeConnectionID(protocol.ConnectionID)
+ GetSessionTicket() ([]byte, error)
+
+ HandleMessage([]byte, protocol.EncryptionLevel) bool
+ SetLargest1RTTAcked(protocol.PacketNumber) error
+ SetHandshakeConfirmed()
+ ConnectionState() ConnectionState
+
+ GetInitialOpener() (LongHeaderOpener, error)
+ GetHandshakeOpener() (LongHeaderOpener, error)
+ Get0RTTOpener() (LongHeaderOpener, error)
+ Get1RTTOpener() (ShortHeaderOpener, error)
+
+ GetInitialSealer() (LongHeaderSealer, error)
+ GetHandshakeSealer() (LongHeaderSealer, error)
+ Get0RTTSealer() (LongHeaderSealer, error)
+ Get1RTTSealer() (ShortHeaderSealer, error)
+}
+
+// ConnWithVersion is the connection used in the ClientHelloInfo.
+// It can be used to determine the QUIC version in use.
+type ConnWithVersion interface {
+ net.Conn
+ GetQUICVersion() protocol.VersionNumber
+}
diff --git a/vendor/github.com/quic-go/quic-go/internal/handshake/mockgen.go b/vendor/github.com/quic-go/quic-go/internal/handshake/mockgen.go
new file mode 100644
index 0000000000..f91e7e8a03
--- /dev/null
+++ b/vendor/github.com/quic-go/quic-go/internal/handshake/mockgen.go
@@ -0,0 +1,3 @@
+package handshake
+
+//go:generate sh -c "../../mockgen_private.sh handshake mock_handshake_runner_test.go github.com/quic-go/quic-go/internal/handshake handshakeRunner"
diff --git a/vendor/github.com/quic-go/quic-go/internal/handshake/retry.go b/vendor/github.com/quic-go/quic-go/internal/handshake/retry.go
new file mode 100644
index 0000000000..ff14f7e0d2
--- /dev/null
+++ b/vendor/github.com/quic-go/quic-go/internal/handshake/retry.go
@@ -0,0 +1,70 @@
+package handshake
+
+import (
+ "bytes"
+ "crypto/aes"
+ "crypto/cipher"
+ "fmt"
+ "sync"
+
+ "github.com/quic-go/quic-go/internal/protocol"
+)
+
+var (
+ retryAEADdraft29 cipher.AEAD // used for QUIC draft versions up to 34
+ retryAEADv1 cipher.AEAD // used for QUIC v1 (RFC 9000)
+ retryAEADv2 cipher.AEAD // used for QUIC v2
+)
+
+func init() {
+ retryAEADdraft29 = initAEAD([16]byte{0xcc, 0xce, 0x18, 0x7e, 0xd0, 0x9a, 0x09, 0xd0, 0x57, 0x28, 0x15, 0x5a, 0x6c, 0xb9, 0x6b, 0xe1})
+ retryAEADv1 = initAEAD([16]byte{0xbe, 0x0c, 0x69, 0x0b, 0x9f, 0x66, 0x57, 0x5a, 0x1d, 0x76, 0x6b, 0x54, 0xe3, 0x68, 0xc8, 0x4e})
+ retryAEADv2 = initAEAD([16]byte{0x8f, 0xb4, 0xb0, 0x1b, 0x56, 0xac, 0x48, 0xe2, 0x60, 0xfb, 0xcb, 0xce, 0xad, 0x7c, 0xcc, 0x92})
+}
+
+func initAEAD(key [16]byte) cipher.AEAD {
+ aes, err := aes.NewCipher(key[:])
+ if err != nil {
+ panic(err)
+ }
+ aead, err := cipher.NewGCM(aes)
+ if err != nil {
+ panic(err)
+ }
+ return aead
+}
+
+var (
+ retryBuf bytes.Buffer
+ retryMutex sync.Mutex
+ retryNonceDraft29 = [12]byte{0xe5, 0x49, 0x30, 0xf9, 0x7f, 0x21, 0x36, 0xf0, 0x53, 0x0a, 0x8c, 0x1c}
+ retryNonceV1 = [12]byte{0x46, 0x15, 0x99, 0xd3, 0x5d, 0x63, 0x2b, 0xf2, 0x23, 0x98, 0x25, 0xbb}
+ retryNonceV2 = [12]byte{0xd8, 0x69, 0x69, 0xbc, 0x2d, 0x7c, 0x6d, 0x99, 0x90, 0xef, 0xb0, 0x4a}
+)
+
+// GetRetryIntegrityTag calculates the integrity tag on a Retry packet
+func GetRetryIntegrityTag(retry []byte, origDestConnID protocol.ConnectionID, version protocol.VersionNumber) *[16]byte {
+ retryMutex.Lock()
+ defer retryMutex.Unlock()
+
+ retryBuf.WriteByte(uint8(origDestConnID.Len()))
+ retryBuf.Write(origDestConnID.Bytes())
+ retryBuf.Write(retry)
+ defer retryBuf.Reset()
+
+ var tag [16]byte
+ var sealed []byte
+ //nolint:exhaustive // These are all the versions we support
+ switch version {
+ case protocol.Version1:
+ sealed = retryAEADv1.Seal(tag[:0], retryNonceV1[:], nil, retryBuf.Bytes())
+ case protocol.Version2:
+ sealed = retryAEADv2.Seal(tag[:0], retryNonceV2[:], nil, retryBuf.Bytes())
+ default:
+ sealed = retryAEADdraft29.Seal(tag[:0], retryNonceDraft29[:], nil, retryBuf.Bytes())
+ }
+ if len(sealed) != 16 {
+ panic(fmt.Sprintf("unexpected Retry integrity tag length: %d", len(sealed)))
+ }
+ return &tag
+}
diff --git a/vendor/github.com/quic-go/quic-go/internal/handshake/session_ticket.go b/vendor/github.com/quic-go/quic-go/internal/handshake/session_ticket.go
new file mode 100644
index 0000000000..56bcbcd5d0
--- /dev/null
+++ b/vendor/github.com/quic-go/quic-go/internal/handshake/session_ticket.go
@@ -0,0 +1,47 @@
+package handshake
+
+import (
+ "bytes"
+ "errors"
+ "fmt"
+ "time"
+
+ "github.com/quic-go/quic-go/internal/wire"
+ "github.com/quic-go/quic-go/quicvarint"
+)
+
+const sessionTicketRevision = 2
+
+type sessionTicket struct {
+ Parameters *wire.TransportParameters
+ RTT time.Duration // to be encoded in mus
+}
+
+func (t *sessionTicket) Marshal() []byte {
+ b := make([]byte, 0, 256)
+ b = quicvarint.Append(b, sessionTicketRevision)
+ b = quicvarint.Append(b, uint64(t.RTT.Microseconds()))
+ return t.Parameters.MarshalForSessionTicket(b)
+}
+
+func (t *sessionTicket) Unmarshal(b []byte) error {
+ r := bytes.NewReader(b)
+ rev, err := quicvarint.Read(r)
+ if err != nil {
+ return errors.New("failed to read session ticket revision")
+ }
+ if rev != sessionTicketRevision {
+ return fmt.Errorf("unknown session ticket revision: %d", rev)
+ }
+ rtt, err := quicvarint.Read(r)
+ if err != nil {
+ return errors.New("failed to read RTT")
+ }
+ var tp wire.TransportParameters
+ if err := tp.UnmarshalFromSessionTicket(r); err != nil {
+ return fmt.Errorf("unmarshaling transport parameters from session ticket failed: %s", err.Error())
+ }
+ t.Parameters = &tp
+ t.RTT = time.Duration(rtt) * time.Microsecond
+ return nil
+}
diff --git a/vendor/github.com/quic-go/quic-go/internal/handshake/tls_extension_handler.go b/vendor/github.com/quic-go/quic-go/internal/handshake/tls_extension_handler.go
new file mode 100644
index 0000000000..ec6431bdac
--- /dev/null
+++ b/vendor/github.com/quic-go/quic-go/internal/handshake/tls_extension_handler.go
@@ -0,0 +1,68 @@
+package handshake
+
+import (
+ "github.com/quic-go/quic-go/internal/protocol"
+ "github.com/quic-go/quic-go/internal/qtls"
+)
+
+const (
+ quicTLSExtensionTypeOldDrafts = 0xffa5
+ quicTLSExtensionType = 0x39
+)
+
+type extensionHandler struct {
+ ourParams []byte
+ paramsChan chan []byte
+
+ extensionType uint16
+
+ perspective protocol.Perspective
+}
+
+var _ tlsExtensionHandler = &extensionHandler{}
+
+// newExtensionHandler creates a new extension handler
+func newExtensionHandler(params []byte, pers protocol.Perspective, v protocol.VersionNumber) tlsExtensionHandler {
+ et := uint16(quicTLSExtensionType)
+ if v != protocol.Version1 {
+ et = quicTLSExtensionTypeOldDrafts
+ }
+ return &extensionHandler{
+ ourParams: params,
+ paramsChan: make(chan []byte),
+ perspective: pers,
+ extensionType: et,
+ }
+}
+
+func (h *extensionHandler) GetExtensions(msgType uint8) []qtls.Extension {
+ if (h.perspective == protocol.PerspectiveClient && messageType(msgType) != typeClientHello) ||
+ (h.perspective == protocol.PerspectiveServer && messageType(msgType) != typeEncryptedExtensions) {
+ return nil
+ }
+ return []qtls.Extension{{
+ Type: h.extensionType,
+ Data: h.ourParams,
+ }}
+}
+
+func (h *extensionHandler) ReceivedExtensions(msgType uint8, exts []qtls.Extension) {
+ if (h.perspective == protocol.PerspectiveClient && messageType(msgType) != typeEncryptedExtensions) ||
+ (h.perspective == protocol.PerspectiveServer && messageType(msgType) != typeClientHello) {
+ return
+ }
+
+ var data []byte
+ for _, ext := range exts {
+ if ext.Type == h.extensionType {
+ data = ext.Data
+ break
+ }
+ }
+
+ h.paramsChan <- data
+}
+
+func (h *extensionHandler) TransportParameters() <-chan []byte {
+ return h.paramsChan
+}
diff --git a/vendor/github.com/quic-go/quic-go/internal/handshake/token_generator.go b/vendor/github.com/quic-go/quic-go/internal/handshake/token_generator.go
new file mode 100644
index 0000000000..e5e90bb3ba
--- /dev/null
+++ b/vendor/github.com/quic-go/quic-go/internal/handshake/token_generator.go
@@ -0,0 +1,127 @@
+package handshake
+
+import (
+ "bytes"
+ "encoding/asn1"
+ "fmt"
+ "io"
+ "net"
+ "time"
+
+ "github.com/quic-go/quic-go/internal/protocol"
+)
+
+const (
+ tokenPrefixIP byte = iota
+ tokenPrefixString
+)
+
+// A Token is derived from the client address and can be used to verify the ownership of this address.
+type Token struct {
+ IsRetryToken bool
+ SentTime time.Time
+ encodedRemoteAddr []byte
+ // only set for retry tokens
+ OriginalDestConnectionID protocol.ConnectionID
+ RetrySrcConnectionID protocol.ConnectionID
+}
+
+// ValidateRemoteAddr validates the address, but does not check expiration
+func (t *Token) ValidateRemoteAddr(addr net.Addr) bool {
+ return bytes.Equal(encodeRemoteAddr(addr), t.encodedRemoteAddr)
+}
+
+// token is the struct that is used for ASN1 serialization and deserialization
+type token struct {
+ IsRetryToken bool
+ RemoteAddr []byte
+ Timestamp int64
+ OriginalDestConnectionID []byte
+ RetrySrcConnectionID []byte
+}
+
+// A TokenGenerator generates tokens
+type TokenGenerator struct {
+ tokenProtector tokenProtector
+}
+
+// NewTokenGenerator initializes a new TookenGenerator
+func NewTokenGenerator(rand io.Reader) (*TokenGenerator, error) {
+ tokenProtector, err := newTokenProtector(rand)
+ if err != nil {
+ return nil, err
+ }
+ return &TokenGenerator{
+ tokenProtector: tokenProtector,
+ }, nil
+}
+
+// NewRetryToken generates a new token for a Retry for a given source address
+func (g *TokenGenerator) NewRetryToken(
+ raddr net.Addr,
+ origDestConnID protocol.ConnectionID,
+ retrySrcConnID protocol.ConnectionID,
+) ([]byte, error) {
+ data, err := asn1.Marshal(token{
+ IsRetryToken: true,
+ RemoteAddr: encodeRemoteAddr(raddr),
+ OriginalDestConnectionID: origDestConnID.Bytes(),
+ RetrySrcConnectionID: retrySrcConnID.Bytes(),
+ Timestamp: time.Now().UnixNano(),
+ })
+ if err != nil {
+ return nil, err
+ }
+ return g.tokenProtector.NewToken(data)
+}
+
+// NewToken generates a new token to be sent in a NEW_TOKEN frame
+func (g *TokenGenerator) NewToken(raddr net.Addr) ([]byte, error) {
+ data, err := asn1.Marshal(token{
+ RemoteAddr: encodeRemoteAddr(raddr),
+ Timestamp: time.Now().UnixNano(),
+ })
+ if err != nil {
+ return nil, err
+ }
+ return g.tokenProtector.NewToken(data)
+}
+
+// DecodeToken decodes a token
+func (g *TokenGenerator) DecodeToken(encrypted []byte) (*Token, error) {
+ // if the client didn't send any token, DecodeToken will be called with a nil-slice
+ if len(encrypted) == 0 {
+ return nil, nil
+ }
+
+ data, err := g.tokenProtector.DecodeToken(encrypted)
+ if err != nil {
+ return nil, err
+ }
+ t := &token{}
+ rest, err := asn1.Unmarshal(data, t)
+ if err != nil {
+ return nil, err
+ }
+ if len(rest) != 0 {
+ return nil, fmt.Errorf("rest when unpacking token: %d", len(rest))
+ }
+ token := &Token{
+ IsRetryToken: t.IsRetryToken,
+ SentTime: time.Unix(0, t.Timestamp),
+ encodedRemoteAddr: t.RemoteAddr,
+ }
+ if t.IsRetryToken {
+ token.OriginalDestConnectionID = protocol.ParseConnectionID(t.OriginalDestConnectionID)
+ token.RetrySrcConnectionID = protocol.ParseConnectionID(t.RetrySrcConnectionID)
+ }
+ return token, nil
+}
+
+// encodeRemoteAddr encodes a remote address such that it can be saved in the token
+func encodeRemoteAddr(remoteAddr net.Addr) []byte {
+ if udpAddr, ok := remoteAddr.(*net.UDPAddr); ok {
+ return append([]byte{tokenPrefixIP}, udpAddr.IP...)
+ }
+ return append([]byte{tokenPrefixString}, []byte(remoteAddr.String())...)
+}
diff --git a/vendor/github.com/quic-go/quic-go/internal/handshake/token_protector.go b/vendor/github.com/quic-go/quic-go/internal/handshake/token_protector.go
new file mode 100644
index 0000000000..650f230b20
--- /dev/null
+++ b/vendor/github.com/quic-go/quic-go/internal/handshake/token_protector.go
@@ -0,0 +1,89 @@
+package handshake
+
+import (
+ "crypto/aes"
+ "crypto/cipher"
+ "crypto/sha256"
+ "fmt"
+ "io"
+
+ "golang.org/x/crypto/hkdf"
+)
+
+// TokenProtector is used to create and verify a token
+type tokenProtector interface {
+ // NewToken creates a new token
+ NewToken([]byte) ([]byte, error)
+ // DecodeToken decodes a token
+ DecodeToken([]byte) ([]byte, error)
+}
+
+const (
+ tokenSecretSize = 32
+ tokenNonceSize = 32
+)
+
+// tokenProtector is used to create and verify a token
+type tokenProtectorImpl struct {
+ rand io.Reader
+ secret []byte
+}
+
+// newTokenProtector creates a source for source address tokens
+func newTokenProtector(rand io.Reader) (tokenProtector, error) {
+ secret := make([]byte, tokenSecretSize)
+ if _, err := rand.Read(secret); err != nil {
+ return nil, err
+ }
+ return &tokenProtectorImpl{
+ rand: rand,
+ secret: secret,
+ }, nil
+}
+
+// NewToken encodes data into a new token.
+func (s *tokenProtectorImpl) NewToken(data []byte) ([]byte, error) {
+ nonce := make([]byte, tokenNonceSize)
+ if _, err := s.rand.Read(nonce); err != nil {
+ return nil, err
+ }
+ aead, aeadNonce, err := s.createAEAD(nonce)
+ if err != nil {
+ return nil, err
+ }
+ return append(nonce, aead.Seal(nil, aeadNonce, data, nil)...), nil
+}
+
+// DecodeToken decodes a token.
+func (s *tokenProtectorImpl) DecodeToken(p []byte) ([]byte, error) {
+ if len(p) < tokenNonceSize {
+ return nil, fmt.Errorf("token too short: %d", len(p))
+ }
+ nonce := p[:tokenNonceSize]
+ aead, aeadNonce, err := s.createAEAD(nonce)
+ if err != nil {
+ return nil, err
+ }
+ return aead.Open(nil, aeadNonce, p[tokenNonceSize:], nil)
+}
+
+func (s *tokenProtectorImpl) createAEAD(nonce []byte) (cipher.AEAD, []byte, error) {
+ h := hkdf.New(sha256.New, s.secret, nonce, []byte("quic-go token source"))
+ key := make([]byte, 32) // use a 32 byte key, in order to select AES-256
+ if _, err := io.ReadFull(h, key); err != nil {
+ return nil, nil, err
+ }
+ aeadNonce := make([]byte, 12)
+ if _, err := io.ReadFull(h, aeadNonce); err != nil {
+ return nil, nil, err
+ }
+ c, err := aes.NewCipher(key)
+ if err != nil {
+ return nil, nil, err
+ }
+ aead, err := cipher.NewGCM(c)
+ if err != nil {
+ return nil, nil, err
+ }
+ return aead, aeadNonce, nil
+}
diff --git a/vendor/github.com/quic-go/quic-go/internal/handshake/updatable_aead.go b/vendor/github.com/quic-go/quic-go/internal/handshake/updatable_aead.go
new file mode 100644
index 0000000000..89a9dcd620
--- /dev/null
+++ b/vendor/github.com/quic-go/quic-go/internal/handshake/updatable_aead.go
@@ -0,0 +1,323 @@
+package handshake
+
+import (
+ "crypto"
+ "crypto/cipher"
+ "crypto/tls"
+ "encoding/binary"
+ "fmt"
+ "time"
+
+ "github.com/quic-go/quic-go/internal/protocol"
+ "github.com/quic-go/quic-go/internal/qerr"
+ "github.com/quic-go/quic-go/internal/qtls"
+ "github.com/quic-go/quic-go/internal/utils"
+ "github.com/quic-go/quic-go/logging"
+)
+
+// KeyUpdateInterval is the maximum number of packets we send or receive before initiating a key update.
+// It's a package-level variable to allow modifying it for testing purposes.
+var KeyUpdateInterval uint64 = protocol.KeyUpdateInterval
+
+type updatableAEAD struct {
+ suite *qtls.CipherSuiteTLS13
+
+ keyPhase protocol.KeyPhase
+ largestAcked protocol.PacketNumber
+ firstPacketNumber protocol.PacketNumber
+ handshakeConfirmed bool
+
+ keyUpdateInterval uint64
+ invalidPacketLimit uint64
+ invalidPacketCount uint64
+
+ // Time when the keys should be dropped. Keys are dropped on the next call to Open().
+ prevRcvAEADExpiry time.Time
+ prevRcvAEAD cipher.AEAD
+
+ firstRcvdWithCurrentKey protocol.PacketNumber
+ firstSentWithCurrentKey protocol.PacketNumber
+ highestRcvdPN protocol.PacketNumber // highest packet number received (which could be successfully unprotected)
+ numRcvdWithCurrentKey uint64
+ numSentWithCurrentKey uint64
+ rcvAEAD cipher.AEAD
+ sendAEAD cipher.AEAD
+ // caches cipher.AEAD.Overhead(). This speeds up calls to Overhead().
+ aeadOverhead int
+
+ nextRcvAEAD cipher.AEAD
+ nextSendAEAD cipher.AEAD
+ nextRcvTrafficSecret []byte
+ nextSendTrafficSecret []byte
+
+ headerDecrypter headerProtector
+ headerEncrypter headerProtector
+
+ rttStats *utils.RTTStats
+
+ tracer logging.ConnectionTracer
+ logger utils.Logger
+ version protocol.VersionNumber
+
+ // use a single slice to avoid allocations
+ nonceBuf []byte
+}
+
+var (
+ _ ShortHeaderOpener = &updatableAEAD{}
+ _ ShortHeaderSealer = &updatableAEAD{}
+)
+
+func newUpdatableAEAD(rttStats *utils.RTTStats, tracer logging.ConnectionTracer, logger utils.Logger, version protocol.VersionNumber) *updatableAEAD {
+ return &updatableAEAD{
+ firstPacketNumber: protocol.InvalidPacketNumber,
+ largestAcked: protocol.InvalidPacketNumber,
+ firstRcvdWithCurrentKey: protocol.InvalidPacketNumber,
+ firstSentWithCurrentKey: protocol.InvalidPacketNumber,
+ keyUpdateInterval: KeyUpdateInterval,
+ rttStats: rttStats,
+ tracer: tracer,
+ logger: logger,
+ version: version,
+ }
+}
+
+func (a *updatableAEAD) rollKeys() {
+ if a.prevRcvAEAD != nil {
+ a.logger.Debugf("Dropping key phase %d ahead of scheduled time. Drop time was: %s", a.keyPhase-1, a.prevRcvAEADExpiry)
+ if a.tracer != nil {
+ a.tracer.DroppedKey(a.keyPhase - 1)
+ }
+ a.prevRcvAEADExpiry = time.Time{}
+ }
+
+ a.keyPhase++
+ a.firstRcvdWithCurrentKey = protocol.InvalidPacketNumber
+ a.firstSentWithCurrentKey = protocol.InvalidPacketNumber
+ a.numRcvdWithCurrentKey = 0
+ a.numSentWithCurrentKey = 0
+ a.prevRcvAEAD = a.rcvAEAD
+ a.rcvAEAD = a.nextRcvAEAD
+ a.sendAEAD = a.nextSendAEAD
+
+ a.nextRcvTrafficSecret = a.getNextTrafficSecret(a.suite.Hash, a.nextRcvTrafficSecret)
+ a.nextSendTrafficSecret = a.getNextTrafficSecret(a.suite.Hash, a.nextSendTrafficSecret)
+ a.nextRcvAEAD = createAEAD(a.suite, a.nextRcvTrafficSecret, a.version)
+ a.nextSendAEAD = createAEAD(a.suite, a.nextSendTrafficSecret, a.version)
+}
+
+func (a *updatableAEAD) startKeyDropTimer(now time.Time) {
+ d := 3 * a.rttStats.PTO(true)
+ a.logger.Debugf("Starting key drop timer to drop key phase %d (in %s)", a.keyPhase-1, d)
+ a.prevRcvAEADExpiry = now.Add(d)
+}
+
+func (a *updatableAEAD) getNextTrafficSecret(hash crypto.Hash, ts []byte) []byte {
+ return hkdfExpandLabel(hash, ts, []byte{}, "quic ku", hash.Size())
+}
+
+// For the client, this function is called before SetWriteKey.
+// For the server, this function is called after SetWriteKey.
+func (a *updatableAEAD) SetReadKey(suite *qtls.CipherSuiteTLS13, trafficSecret []byte) {
+ a.rcvAEAD = createAEAD(suite, trafficSecret, a.version)
+ a.headerDecrypter = newHeaderProtector(suite, trafficSecret, false, a.version)
+ if a.suite == nil {
+ a.setAEADParameters(a.rcvAEAD, suite)
+ }
+
+ a.nextRcvTrafficSecret = a.getNextTrafficSecret(suite.Hash, trafficSecret)
+ a.nextRcvAEAD = createAEAD(suite, a.nextRcvTrafficSecret, a.version)
+}
+
+// For the client, this function is called after SetReadKey.
+// For the server, this function is called before SetWriteKey.
+func (a *updatableAEAD) SetWriteKey(suite *qtls.CipherSuiteTLS13, trafficSecret []byte) {
+ a.sendAEAD = createAEAD(suite, trafficSecret, a.version)
+ a.headerEncrypter = newHeaderProtector(suite, trafficSecret, false, a.version)
+ if a.suite == nil {
+ a.setAEADParameters(a.sendAEAD, suite)
+ }
+
+ a.nextSendTrafficSecret = a.getNextTrafficSecret(suite.Hash, trafficSecret)
+ a.nextSendAEAD = createAEAD(suite, a.nextSendTrafficSecret, a.version)
+}
+
+func (a *updatableAEAD) setAEADParameters(aead cipher.AEAD, suite *qtls.CipherSuiteTLS13) {
+ a.nonceBuf = make([]byte, aead.NonceSize())
+ a.aeadOverhead = aead.Overhead()
+ a.suite = suite
+ switch suite.ID {
+ case tls.TLS_AES_128_GCM_SHA256, tls.TLS_AES_256_GCM_SHA384:
+ a.invalidPacketLimit = protocol.InvalidPacketLimitAES
+ case tls.TLS_CHACHA20_POLY1305_SHA256:
+ a.invalidPacketLimit = protocol.InvalidPacketLimitChaCha
+ default:
+ panic(fmt.Sprintf("unknown cipher suite %d", suite.ID))
+ }
+}
+
+func (a *updatableAEAD) DecodePacketNumber(wirePN protocol.PacketNumber, wirePNLen protocol.PacketNumberLen) protocol.PacketNumber {
+ return protocol.DecodePacketNumber(wirePNLen, a.highestRcvdPN, wirePN)
+}
+
+func (a *updatableAEAD) Open(dst, src []byte, rcvTime time.Time, pn protocol.PacketNumber, kp protocol.KeyPhaseBit, ad []byte) ([]byte, error) {
+ dec, err := a.open(dst, src, rcvTime, pn, kp, ad)
+ if err == ErrDecryptionFailed {
+ a.invalidPacketCount++
+ if a.invalidPacketCount >= a.invalidPacketLimit {
+ return nil, &qerr.TransportError{ErrorCode: qerr.AEADLimitReached}
+ }
+ }
+ if err == nil {
+ a.highestRcvdPN = utils.Max(a.highestRcvdPN, pn)
+ }
+ return dec, err
+}
+
+func (a *updatableAEAD) open(dst, src []byte, rcvTime time.Time, pn protocol.PacketNumber, kp protocol.KeyPhaseBit, ad []byte) ([]byte, error) {
+ if a.prevRcvAEAD != nil && !a.prevRcvAEADExpiry.IsZero() && rcvTime.After(a.prevRcvAEADExpiry) {
+ a.prevRcvAEAD = nil
+ a.logger.Debugf("Dropping key phase %d", a.keyPhase-1)
+ a.prevRcvAEADExpiry = time.Time{}
+ if a.tracer != nil {
+ a.tracer.DroppedKey(a.keyPhase - 1)
+ }
+ }
+ binary.BigEndian.PutUint64(a.nonceBuf[len(a.nonceBuf)-8:], uint64(pn))
+ if kp != a.keyPhase.Bit() {
+ if a.keyPhase > 0 && a.firstRcvdWithCurrentKey == protocol.InvalidPacketNumber || pn < a.firstRcvdWithCurrentKey {
+ if a.prevRcvAEAD == nil {
+ return nil, ErrKeysDropped
+ }
+ // we updated the key, but the peer hasn't updated yet
+ dec, err := a.prevRcvAEAD.Open(dst, a.nonceBuf, src, ad)
+ if err != nil {
+ err = ErrDecryptionFailed
+ }
+ return dec, err
+ }
+ // try opening the packet with the next key phase
+ dec, err := a.nextRcvAEAD.Open(dst, a.nonceBuf, src, ad)
+ if err != nil {
+ return nil, ErrDecryptionFailed
+ }
+ // Opening succeeded. Check if the peer was allowed to update.
+ if a.keyPhase > 0 && a.firstSentWithCurrentKey == protocol.InvalidPacketNumber {
+ return nil, &qerr.TransportError{
+ ErrorCode: qerr.KeyUpdateError,
+ ErrorMessage: "keys updated too quickly",
+ }
+ }
+ a.rollKeys()
+ a.logger.Debugf("Peer updated keys to %d", a.keyPhase)
+ // The peer initiated this key update. It's safe to drop the keys for the previous generation now.
+ // Start a timer to drop the previous key generation.
+ a.startKeyDropTimer(rcvTime)
+ if a.tracer != nil {
+ a.tracer.UpdatedKey(a.keyPhase, true)
+ }
+ a.firstRcvdWithCurrentKey = pn
+ return dec, err
+ }
+ // The AEAD we're using here will be the qtls.aeadAESGCM13.
+ // It uses the nonce provided here and XOR it with the IV.
+ dec, err := a.rcvAEAD.Open(dst, a.nonceBuf, src, ad)
+ if err != nil {
+ return dec, ErrDecryptionFailed
+ }
+ a.numRcvdWithCurrentKey++
+ if a.firstRcvdWithCurrentKey == protocol.InvalidPacketNumber {
+ // We initiated the key updated, and now we received the first packet protected with the new key phase.
+ // Therefore, we are certain that the peer rolled its keys as well. Start a timer to drop the old keys.
+ if a.keyPhase > 0 {
+ a.logger.Debugf("Peer confirmed key update to phase %d", a.keyPhase)
+ a.startKeyDropTimer(rcvTime)
+ }
+ a.firstRcvdWithCurrentKey = pn
+ }
+ return dec, err
+}
+
+func (a *updatableAEAD) Seal(dst, src []byte, pn protocol.PacketNumber, ad []byte) []byte {
+ if a.firstSentWithCurrentKey == protocol.InvalidPacketNumber {
+ a.firstSentWithCurrentKey = pn
+ }
+ if a.firstPacketNumber == protocol.InvalidPacketNumber {
+ a.firstPacketNumber = pn
+ }
+ a.numSentWithCurrentKey++
+ binary.BigEndian.PutUint64(a.nonceBuf[len(a.nonceBuf)-8:], uint64(pn))
+ // The AEAD we're using here will be the qtls.aeadAESGCM13.
+ // It uses the nonce provided here and XOR it with the IV.
+ return a.sendAEAD.Seal(dst, a.nonceBuf, src, ad)
+}
+
+func (a *updatableAEAD) SetLargestAcked(pn protocol.PacketNumber) error {
+ if a.firstSentWithCurrentKey != protocol.InvalidPacketNumber &&
+ pn >= a.firstSentWithCurrentKey && a.numRcvdWithCurrentKey == 0 {
+ return &qerr.TransportError{
+ ErrorCode: qerr.KeyUpdateError,
+ ErrorMessage: fmt.Sprintf("received ACK for key phase %d, but peer didn't update keys", a.keyPhase),
+ }
+ }
+ a.largestAcked = pn
+ return nil
+}
+
+func (a *updatableAEAD) SetHandshakeConfirmed() {
+ a.handshakeConfirmed = true
+}
+
+func (a *updatableAEAD) updateAllowed() bool {
+ if !a.handshakeConfirmed {
+ return false
+ }
+ // the first key update is allowed as soon as the handshake is confirmed
+ return a.keyPhase == 0 ||
+ // subsequent key updates as soon as a packet sent with that key phase has been acknowledged
+ (a.firstSentWithCurrentKey != protocol.InvalidPacketNumber &&
+ a.largestAcked != protocol.InvalidPacketNumber &&
+ a.largestAcked >= a.firstSentWithCurrentKey)
+}
+
+func (a *updatableAEAD) shouldInitiateKeyUpdate() bool {
+ if !a.updateAllowed() {
+ return false
+ }
+ if a.numRcvdWithCurrentKey >= a.keyUpdateInterval {
+ a.logger.Debugf("Received %d packets with current key phase. Initiating key update to the next key phase: %d", a.numRcvdWithCurrentKey, a.keyPhase+1)
+ return true
+ }
+ if a.numSentWithCurrentKey >= a.keyUpdateInterval {
+ a.logger.Debugf("Sent %d packets with current key phase. Initiating key update to the next key phase: %d", a.numSentWithCurrentKey, a.keyPhase+1)
+ return true
+ }
+ return false
+}
+
+func (a *updatableAEAD) KeyPhase() protocol.KeyPhaseBit {
+ if a.shouldInitiateKeyUpdate() {
+ a.rollKeys()
+ a.logger.Debugf("Initiating key update to key phase %d", a.keyPhase)
+ if a.tracer != nil {
+ a.tracer.UpdatedKey(a.keyPhase, false)
+ }
+ }
+ return a.keyPhase.Bit()
+}
+
+func (a *updatableAEAD) Overhead() int {
+ return a.aeadOverhead
+}
+
+func (a *updatableAEAD) EncryptHeader(sample []byte, firstByte *byte, hdrBytes []byte) {
+ a.headerEncrypter.EncryptHeader(sample, firstByte, hdrBytes)
+}
+
+func (a *updatableAEAD) DecryptHeader(sample []byte, firstByte *byte, hdrBytes []byte) {
+ a.headerDecrypter.DecryptHeader(sample, firstByte, hdrBytes)
+}
+
+func (a *updatableAEAD) FirstPacketNumber() protocol.PacketNumber {
+ return a.firstPacketNumber
+}
diff --git a/vendor/github.com/quic-go/quic-go/internal/logutils/frame.go b/vendor/github.com/quic-go/quic-go/internal/logutils/frame.go
new file mode 100644
index 0000000000..a6032fc20d
--- /dev/null
+++ b/vendor/github.com/quic-go/quic-go/internal/logutils/frame.go
@@ -0,0 +1,50 @@
+package logutils
+
+import (
+ "github.com/quic-go/quic-go/internal/protocol"
+ "github.com/quic-go/quic-go/internal/wire"
+ "github.com/quic-go/quic-go/logging"
+)
+
+// ConvertFrame converts a wire.Frame into a logging.Frame.
+// This makes it possible for external packages to access the frames.
+// Furthermore, it removes the data slices from CRYPTO and STREAM frames.
+func ConvertFrame(frame wire.Frame) logging.Frame {
+ switch f := frame.(type) {
+ case *wire.AckFrame:
+ // We use a pool for ACK frames.
+ // Implementations of the tracer interface may hold on to frames, so we need to make a copy here.
+ return ConvertAckFrame(f)
+ case *wire.CryptoFrame:
+ return &logging.CryptoFrame{
+ Offset: f.Offset,
+ Length: protocol.ByteCount(len(f.Data)),
+ }
+ case *wire.StreamFrame:
+ return &logging.StreamFrame{
+ StreamID: f.StreamID,
+ Offset: f.Offset,
+ Length: f.DataLen(),
+ Fin: f.Fin,
+ }
+ case *wire.DatagramFrame:
+ return &logging.DatagramFrame{
+ Length: logging.ByteCount(len(f.Data)),
+ }
+ default:
+ return logging.Frame(frame)
+ }
+}
+
+func ConvertAckFrame(f *wire.AckFrame) *logging.AckFrame {
+ ranges := make([]wire.AckRange, 0, len(f.AckRanges))
+ ranges = append(ranges, f.AckRanges...)
+ ack := &logging.AckFrame{
+ AckRanges: ranges,
+ DelayTime: f.DelayTime,
+ ECNCE: f.ECNCE,
+ ECT0: f.ECT0,
+ ECT1: f.ECT1,
+ }
+ return ack
+}
diff --git a/vendor/github.com/quic-go/quic-go/internal/protocol/connection_id.go b/vendor/github.com/quic-go/quic-go/internal/protocol/connection_id.go
new file mode 100644
index 0000000000..77259b5fa5
--- /dev/null
+++ b/vendor/github.com/quic-go/quic-go/internal/protocol/connection_id.go
@@ -0,0 +1,116 @@
+package protocol
+
+import (
+ "crypto/rand"
+ "errors"
+ "fmt"
+ "io"
+)
+
+var ErrInvalidConnectionIDLen = errors.New("invalid Connection ID length")
+
+// An ArbitraryLenConnectionID is a QUIC Connection ID able to represent Connection IDs according to RFC 8999.
+// Future QUIC versions might allow connection ID lengths up to 255 bytes, while QUIC v1
+// restricts the length to 20 bytes.
+type ArbitraryLenConnectionID []byte
+
+func (c ArbitraryLenConnectionID) Len() int {
+ return len(c)
+}
+
+func (c ArbitraryLenConnectionID) Bytes() []byte {
+ return c
+}
+
+func (c ArbitraryLenConnectionID) String() string {
+ if c.Len() == 0 {
+ return "(empty)"
+ }
+ return fmt.Sprintf("%x", c.Bytes())
+}
+
+const maxConnectionIDLen = 20
+
+// A ConnectionID in QUIC
+type ConnectionID struct {
+ b [20]byte
+ l uint8
+}
+
+// GenerateConnectionID generates a connection ID using cryptographic random
+func GenerateConnectionID(l int) (ConnectionID, error) {
+ var c ConnectionID
+ c.l = uint8(l)
+ _, err := rand.Read(c.b[:l])
+ return c, err
+}
+
+// ParseConnectionID interprets b as a Connection ID.
+// It panics if b is longer than 20 bytes.
+func ParseConnectionID(b []byte) ConnectionID {
+ if len(b) > maxConnectionIDLen {
+ panic("invalid conn id length")
+ }
+ var c ConnectionID
+ c.l = uint8(len(b))
+ copy(c.b[:c.l], b)
+ return c
+}
+
+// GenerateConnectionIDForInitial generates a connection ID for the Initial packet.
+// It uses a length randomly chosen between 8 and 20 bytes.
+func GenerateConnectionIDForInitial() (ConnectionID, error) {
+ r := make([]byte, 1)
+ if _, err := rand.Read(r); err != nil {
+ return ConnectionID{}, err
+ }
+ l := MinConnectionIDLenInitial + int(r[0])%(maxConnectionIDLen-MinConnectionIDLenInitial+1)
+ return GenerateConnectionID(l)
+}
+
+// ReadConnectionID reads a connection ID of length len from the given io.Reader.
+// It returns io.EOF if there are not enough bytes to read.
+func ReadConnectionID(r io.Reader, l int) (ConnectionID, error) {
+ var c ConnectionID
+ if l == 0 {
+ return c, nil
+ }
+ if l > maxConnectionIDLen {
+ return c, ErrInvalidConnectionIDLen
+ }
+ c.l = uint8(l)
+ _, err := io.ReadFull(r, c.b[:l])
+ if err == io.ErrUnexpectedEOF {
+ return c, io.EOF
+ }
+ return c, err
+}
+
+// Len returns the length of the connection ID in bytes
+func (c ConnectionID) Len() int {
+ return int(c.l)
+}
+
+// Bytes returns the byte representation
+func (c ConnectionID) Bytes() []byte {
+ return c.b[:c.l]
+}
+
+func (c ConnectionID) String() string {
+ if c.Len() == 0 {
+ return "(empty)"
+ }
+ return fmt.Sprintf("%x", c.Bytes())
+}
+
+type DefaultConnectionIDGenerator struct {
+ ConnLen int
+}
+
+func (d *DefaultConnectionIDGenerator) GenerateConnectionID() (ConnectionID, error) {
+ return GenerateConnectionID(d.ConnLen)
+}
+
+func (d *DefaultConnectionIDGenerator) ConnectionIDLen() int {
+ return d.ConnLen
+}
diff --git a/vendor/github.com/quic-go/quic-go/internal/protocol/encryption_level.go b/vendor/github.com/quic-go/quic-go/internal/protocol/encryption_level.go
new file mode 100644
index 0000000000..32d38ab1e8
--- /dev/null
+++ b/vendor/github.com/quic-go/quic-go/internal/protocol/encryption_level.go
@@ -0,0 +1,30 @@
+package protocol
+
+// EncryptionLevel is the encryption level
+// Default value is Unencrypted
+type EncryptionLevel uint8
+
+const (
+ // EncryptionInitial is the Initial encryption level
+ EncryptionInitial EncryptionLevel = 1 + iota
+ // EncryptionHandshake is the Handshake encryption level
+ EncryptionHandshake
+ // Encryption0RTT is the 0-RTT encryption level
+ Encryption0RTT
+ // Encryption1RTT is the 1-RTT encryption level
+ Encryption1RTT
+)
+
+func (e EncryptionLevel) String() string {
+ switch e {
+ case EncryptionInitial:
+ return "Initial"
+ case EncryptionHandshake:
+ return "Handshake"
+ case Encryption0RTT:
+ return "0-RTT"
+ case Encryption1RTT:
+ return "1-RTT"
+ }
+ return "unknown"
+}
diff --git a/vendor/github.com/quic-go/quic-go/internal/protocol/key_phase.go b/vendor/github.com/quic-go/quic-go/internal/protocol/key_phase.go
new file mode 100644
index 0000000000..edd740cf64
--- /dev/null
+++ b/vendor/github.com/quic-go/quic-go/internal/protocol/key_phase.go
@@ -0,0 +1,36 @@
+package protocol
+
+// KeyPhase is the key phase
+type KeyPhase uint64
+
+// Bit determines the key phase bit
+func (p KeyPhase) Bit() KeyPhaseBit {
+ if p%2 == 0 {
+ return KeyPhaseZero
+ }
+ return KeyPhaseOne
+}
+
+// KeyPhaseBit is the key phase bit
+type KeyPhaseBit uint8
+
+const (
+ // KeyPhaseUndefined is an undefined key phase
+ KeyPhaseUndefined KeyPhaseBit = iota
+ // KeyPhaseZero is key phase 0
+ KeyPhaseZero
+ // KeyPhaseOne is key phase 1
+ KeyPhaseOne
+)
+
+func (p KeyPhaseBit) String() string {
+ //nolint:exhaustive
+ switch p {
+ case KeyPhaseZero:
+ return "0"
+ case KeyPhaseOne:
+ return "1"
+ default:
+ return "undefined"
+ }
+}
diff --git a/vendor/github.com/quic-go/quic-go/internal/protocol/packet_number.go b/vendor/github.com/quic-go/quic-go/internal/protocol/packet_number.go
new file mode 100644
index 0000000000..bd34016195
--- /dev/null
+++ b/vendor/github.com/quic-go/quic-go/internal/protocol/packet_number.go
@@ -0,0 +1,79 @@
+package protocol
+
+// A PacketNumber in QUIC
+type PacketNumber int64
+
+// InvalidPacketNumber is a packet number that is never sent.
+// In QUIC, 0 is a valid packet number.
+const InvalidPacketNumber PacketNumber = -1
+
+// PacketNumberLen is the length of the packet number in bytes
+type PacketNumberLen uint8
+
+const (
+ // PacketNumberLen1 is a packet number length of 1 byte
+ PacketNumberLen1 PacketNumberLen = 1
+ // PacketNumberLen2 is a packet number length of 2 bytes
+ PacketNumberLen2 PacketNumberLen = 2
+ // PacketNumberLen3 is a packet number length of 3 bytes
+ PacketNumberLen3 PacketNumberLen = 3
+ // PacketNumberLen4 is a packet number length of 4 bytes
+ PacketNumberLen4 PacketNumberLen = 4
+)
+
+// DecodePacketNumber calculates the packet number based on the received packet number, its length and the last seen packet number
+func DecodePacketNumber(
+ packetNumberLength PacketNumberLen,
+ lastPacketNumber PacketNumber,
+ wirePacketNumber PacketNumber,
+) PacketNumber {
+ var epochDelta PacketNumber
+ switch packetNumberLength {
+ case PacketNumberLen1:
+ epochDelta = PacketNumber(1) << 8
+ case PacketNumberLen2:
+ epochDelta = PacketNumber(1) << 16
+ case PacketNumberLen3:
+ epochDelta = PacketNumber(1) << 24
+ case PacketNumberLen4:
+ epochDelta = PacketNumber(1) << 32
+ }
+ epoch := lastPacketNumber & ^(epochDelta - 1)
+ var prevEpochBegin PacketNumber
+ if epoch > epochDelta {
+ prevEpochBegin = epoch - epochDelta
+ }
+ nextEpochBegin := epoch + epochDelta
+ return closestTo(
+ lastPacketNumber+1,
+ epoch+wirePacketNumber,
+ closestTo(lastPacketNumber+1, prevEpochBegin+wirePacketNumber, nextEpochBegin+wirePacketNumber),
+ )
+}
+
+func closestTo(target, a, b PacketNumber) PacketNumber {
+ if delta(target, a) < delta(target, b) {
+ return a
+ }
+ return b
+}
+
+func delta(a, b PacketNumber) PacketNumber {
+ if a < b {
+ return b - a
+ }
+ return a - b
+}
+
+// GetPacketNumberLengthForHeader gets the length of the packet number for the public header
+// it never chooses a PacketNumberLen of 1 byte, since this is too short under certain circumstances
+func GetPacketNumberLengthForHeader(packetNumber, leastUnacked PacketNumber) PacketNumberLen {
+ diff := uint64(packetNumber - leastUnacked)
+ if diff < (1 << (16 - 1)) {
+ return PacketNumberLen2
+ }
+ if diff < (1 << (24 - 1)) {
+ return PacketNumberLen3
+ }
+ return PacketNumberLen4
+}
diff --git a/vendor/github.com/quic-go/quic-go/internal/protocol/params.go b/vendor/github.com/quic-go/quic-go/internal/protocol/params.go
new file mode 100644
index 0000000000..60c8677944
--- /dev/null
+++ b/vendor/github.com/quic-go/quic-go/internal/protocol/params.go
@@ -0,0 +1,193 @@
+package protocol
+
+import "time"
+
+// DesiredReceiveBufferSize is the kernel UDP receive buffer size that we'd like to use.
+const DesiredReceiveBufferSize = (1 << 20) * 2 // 2 MB
+
+// InitialPacketSizeIPv4 is the maximum packet size that we use for sending IPv4 packets.
+const InitialPacketSizeIPv4 = 1252
+
+// InitialPacketSizeIPv6 is the maximum packet size that we use for sending IPv6 packets.
+const InitialPacketSizeIPv6 = 1232
+
+// MaxCongestionWindowPackets is the maximum congestion window in packet.
+const MaxCongestionWindowPackets = 10000
+
+// MaxUndecryptablePackets limits the number of undecryptable packets that are queued in the connection.
+const MaxUndecryptablePackets = 32
+
+// ConnectionFlowControlMultiplier determines how much larger the connection flow control windows needs to be relative to any stream's flow control window
+// This is the value that Chromium is using
+const ConnectionFlowControlMultiplier = 1.5
+
+// DefaultInitialMaxStreamData is the default initial stream-level flow control window for receiving data
+const DefaultInitialMaxStreamData = (1 << 10) * 512 // 512 kb
+
+// DefaultInitialMaxData is the connection-level flow control window for receiving data
+const DefaultInitialMaxData = ConnectionFlowControlMultiplier * DefaultInitialMaxStreamData
+
+// DefaultMaxReceiveStreamFlowControlWindow is the default maximum stream-level flow control window for receiving data
+const DefaultMaxReceiveStreamFlowControlWindow = 6 * (1 << 20) // 6 MB
+
+// DefaultMaxReceiveConnectionFlowControlWindow is the default connection-level flow control window for receiving data
+const DefaultMaxReceiveConnectionFlowControlWindow = 15 * (1 << 20) // 15 MB
+
+// WindowUpdateThreshold is the fraction of the receive window that has to be consumed before an higher offset is advertised to the client
+const WindowUpdateThreshold = 0.25
+
+// DefaultMaxIncomingStreams is the maximum number of streams that a peer may open
+const DefaultMaxIncomingStreams = 100
+
+// DefaultMaxIncomingUniStreams is the maximum number of unidirectional streams that a peer may open
+const DefaultMaxIncomingUniStreams = 100
+
+// MaxServerUnprocessedPackets is the max number of packets stored in the server that are not yet processed.
+const MaxServerUnprocessedPackets = 1024
+
+// MaxConnUnprocessedPackets is the max number of packets stored in each connection that are not yet processed.
+const MaxConnUnprocessedPackets = 256
+
+// SkipPacketInitialPeriod is the initial period length used for packet number skipping to prevent an Optimistic ACK attack.
+// Every time a packet number is skipped, the period is doubled, up to SkipPacketMaxPeriod.
+const SkipPacketInitialPeriod PacketNumber = 256
+
+// SkipPacketMaxPeriod is the maximum period length used for packet number skipping.
+const SkipPacketMaxPeriod PacketNumber = 128 * 1024
+
+// MaxAcceptQueueSize is the maximum number of connections that the server queues for accepting.
+// If the queue is full, new connection attempts will be rejected.
+const MaxAcceptQueueSize = 32
+
+// TokenValidity is the duration that a (non-retry) token is considered valid
+const TokenValidity = 24 * time.Hour
+
+// RetryTokenValidity is the duration that a retry token is considered valid
+const RetryTokenValidity = 10 * time.Second
+
+// MaxOutstandingSentPackets is maximum number of packets saved for retransmission.
+// When reached, it imposes a soft limit on sending new packets:
+// Sending ACKs and retransmission is still allowed, but now new regular packets can be sent.
+const MaxOutstandingSentPackets = 2 * MaxCongestionWindowPackets
+
+// MaxTrackedSentPackets is maximum number of sent packets saved for retransmission.
+// When reached, no more packets will be sent.
+// This value *must* be larger than MaxOutstandingSentPackets.
+const MaxTrackedSentPackets = MaxOutstandingSentPackets * 5 / 4
+
+// MaxNonAckElicitingAcks is the maximum number of packets containing an ACK,
+// but no ack-eliciting frames, that we send in a row
+const MaxNonAckElicitingAcks = 19
+
+// MaxStreamFrameSorterGaps is the maximum number of gaps between received StreamFrames
+// prevents DoS attacks against the streamFrameSorter
+const MaxStreamFrameSorterGaps = 1000
+
+// MinStreamFrameBufferSize is the minimum data length of a received STREAM frame
+// that we use the buffer for. This protects against a DoS where an attacker would send us
+// very small STREAM frames to consume a lot of memory.
+const MinStreamFrameBufferSize = 128
+
+// MinCoalescedPacketSize is the minimum size of a coalesced packet that we pack.
+// If a packet has less than this number of bytes, we won't coalesce any more packets onto it.
+const MinCoalescedPacketSize = 128
+
+// MaxCryptoStreamOffset is the maximum offset allowed on any of the crypto streams.
+// This limits the size of the ClientHello and Certificates that can be received.
+const MaxCryptoStreamOffset = 16 * (1 << 10)
+
+// MinRemoteIdleTimeout is the minimum value that we accept for the remote idle timeout
+const MinRemoteIdleTimeout = 5 * time.Second
+
+// DefaultIdleTimeout is the default idle timeout
+const DefaultIdleTimeout = 30 * time.Second
+
+// DefaultHandshakeIdleTimeout is the default idle timeout used before handshake completion.
+const DefaultHandshakeIdleTimeout = 5 * time.Second
+
+// DefaultHandshakeTimeout is the default timeout for a connection until the crypto handshake succeeds.
+const DefaultHandshakeTimeout = 10 * time.Second
+
+// MaxKeepAliveInterval is the maximum time until we send a packet to keep a connection alive.
+// It should be shorter than the time that NATs clear their mapping.
+const MaxKeepAliveInterval = 20 * time.Second
+
+// RetiredConnectionIDDeleteTimeout is the time we keep closed connections around in order to retransmit the CONNECTION_CLOSE.
+// after this time all information about the old connection will be deleted
+const RetiredConnectionIDDeleteTimeout = 5 * time.Second
+
+// MinStreamFrameSize is the minimum size that has to be left in a packet, so that we add another STREAM frame.
+// This avoids splitting up STREAM frames into small pieces, which has 2 advantages:
+// 1. it reduces the framing overhead
+// 2. it reduces the head-of-line blocking, when a packet is lost
+const MinStreamFrameSize ByteCount = 128
+
+// MaxPostHandshakeCryptoFrameSize is the maximum size of CRYPTO frames
+// we send after the handshake completes.
+const MaxPostHandshakeCryptoFrameSize = 1000
+
+// MaxAckFrameSize is the maximum size for an ACK frame that we write
+// Due to the varint encoding, ACK frames can grow (almost) indefinitely large.
+// The MaxAckFrameSize should be large enough to encode many ACK range,
+// but must ensure that a maximum size ACK frame fits into one packet.
+const MaxAckFrameSize ByteCount = 1000
+
+// MaxDatagramFrameSize is the maximum size of a DATAGRAM frame (RFC 9221).
+// The size is chosen such that a DATAGRAM frame fits into a QUIC packet.
+const MaxDatagramFrameSize ByteCount = 1200
+
+// DatagramRcvQueueLen is the length of the receive queue for DATAGRAM frames (RFC 9221)
+const DatagramRcvQueueLen = 128
+
+// MaxNumAckRanges is the maximum number of ACK ranges that we send in an ACK frame.
+// It also serves as a limit for the packet history.
+// If at any point we keep track of more ranges, old ranges are discarded.
+const MaxNumAckRanges = 32
+
+// MinPacingDelay is the minimum duration that is used for packet pacing
+// If the packet packing frequency is higher, multiple packets might be sent at once.
+// Example: For a packet pacing delay of 200μs, we would send 5 packets at once, wait for 1ms, and so forth.
+const MinPacingDelay = time.Millisecond
+
+// DefaultConnectionIDLength is the connection ID length that is used for multiplexed connections
+// if no other value is configured.
+const DefaultConnectionIDLength = 4
+
+// MaxActiveConnectionIDs is the number of connection IDs that we're storing.
+const MaxActiveConnectionIDs = 4
+
+// MaxIssuedConnectionIDs is the maximum number of connection IDs that we're issuing at the same time.
+const MaxIssuedConnectionIDs = 6
+
+// PacketsPerConnectionID is the number of packets we send using one connection ID.
+// If the peer provices us with enough new connection IDs, we switch to a new connection ID.
+const PacketsPerConnectionID = 10000
+
+// AckDelayExponent is the ack delay exponent used when sending ACKs.
+const AckDelayExponent = 3
+
+// Estimated timer granularity.
+// The loss detection timer will not be set to a value smaller than granularity.
+const TimerGranularity = time.Millisecond
+
+// MaxAckDelay is the maximum time by which we delay sending ACKs.
+const MaxAckDelay = 25 * time.Millisecond
+
+// MaxAckDelayInclGranularity is the max_ack_delay including the timer granularity.
+// This is the value that should be advertised to the peer.
+const MaxAckDelayInclGranularity = MaxAckDelay + TimerGranularity
+
+// KeyUpdateInterval is the maximum number of packets we send or receive before initiating a key update.
+const KeyUpdateInterval = 100 * 1000
+
+// Max0RTTQueueingDuration is the maximum time that we store 0-RTT packets in order to wait for the corresponding Initial to be received.
+const Max0RTTQueueingDuration = 100 * time.Millisecond
+
+// Max0RTTQueues is the maximum number of connections that we buffer 0-RTT packets for.
+const Max0RTTQueues = 32
+
+// Max0RTTQueueLen is the maximum number of 0-RTT packets that we buffer for each connection.
+// When a new connection is created, all buffered packets are passed to the connection immediately.
+// To avoid blocking, this value has to be smaller than MaxConnUnprocessedPackets.
+// To avoid packets being dropped as undecryptable by the connection, this value has to be smaller than MaxUndecryptablePackets.
+const Max0RTTQueueLen = 31
diff --git a/vendor/github.com/quic-go/quic-go/internal/protocol/perspective.go b/vendor/github.com/quic-go/quic-go/internal/protocol/perspective.go
new file mode 100644
index 0000000000..43358fecb4
--- /dev/null
+++ b/vendor/github.com/quic-go/quic-go/internal/protocol/perspective.go
@@ -0,0 +1,26 @@
+package protocol
+
+// Perspective determines if we're acting as a server or a client
+type Perspective int
+
+// the perspectives
+const (
+ PerspectiveServer Perspective = 1
+ PerspectiveClient Perspective = 2
+)
+
+// Opposite returns the perspective of the peer
+func (p Perspective) Opposite() Perspective {
+ return 3 - p
+}
+
+func (p Perspective) String() string {
+ switch p {
+ case PerspectiveServer:
+ return "Server"
+ case PerspectiveClient:
+ return "Client"
+ default:
+ return "invalid perspective"
+ }
+}
diff --git a/vendor/github.com/quic-go/quic-go/internal/protocol/protocol.go b/vendor/github.com/quic-go/quic-go/internal/protocol/protocol.go
new file mode 100644
index 0000000000..8241e2741e
--- /dev/null
+++ b/vendor/github.com/quic-go/quic-go/internal/protocol/protocol.go
@@ -0,0 +1,97 @@
+package protocol
+
+import (
+ "fmt"
+ "time"
+)
+
+// The PacketType is the Long Header Type
+type PacketType uint8
+
+const (
+ // PacketTypeInitial is the packet type of an Initial packet
+ PacketTypeInitial PacketType = 1 + iota
+ // PacketTypeRetry is the packet type of a Retry packet
+ PacketTypeRetry
+ // PacketTypeHandshake is the packet type of a Handshake packet
+ PacketTypeHandshake
+ // PacketType0RTT is the packet type of a 0-RTT packet
+ PacketType0RTT
+)
+
+func (t PacketType) String() string {
+ switch t {
+ case PacketTypeInitial:
+ return "Initial"
+ case PacketTypeRetry:
+ return "Retry"
+ case PacketTypeHandshake:
+ return "Handshake"
+ case PacketType0RTT:
+ return "0-RTT Protected"
+ default:
+ return fmt.Sprintf("unknown packet type: %d", t)
+ }
+}
+
+type ECN uint8
+
+const (
+ ECNNon ECN = iota // 00
+ ECT1 // 01
+ ECT0 // 10
+ ECNCE // 11
+)
+
+// A ByteCount in QUIC
+type ByteCount int64
+
+// MaxByteCount is the maximum value of a ByteCount
+const MaxByteCount = ByteCount(1<<62 - 1)
+
+// InvalidByteCount is an invalid byte count
+const InvalidByteCount ByteCount = -1
+
+// A StatelessResetToken is a stateless reset token.
+type StatelessResetToken [16]byte
+
+// MaxPacketBufferSize maximum packet size of any QUIC packet, based on
+// ethernet's max size, minus the IP and UDP headers. IPv6 has a 40 byte header,
+// UDP adds an additional 8 bytes. This is a total overhead of 48 bytes.
+// Ethernet's max packet size is 1500 bytes, 1500 - 48 = 1452.
+const MaxPacketBufferSize ByteCount = 1452
+
+// MinInitialPacketSize is the minimum size an Initial packet is required to have.
+const MinInitialPacketSize = 1200
+
+// MinUnknownVersionPacketSize is the minimum size a packet with an unknown version
+// needs to have in order to trigger a Version Negotiation packet.
+const MinUnknownVersionPacketSize = MinInitialPacketSize
+
+// MinStatelessResetSize is the minimum size of a stateless reset packet that we send
+const MinStatelessResetSize = 1 /* first byte */ + 20 /* max. conn ID length */ + 4 /* max. packet number length */ + 1 /* min. payload length */ + 16 /* token */
+
+// MinConnectionIDLenInitial is the minimum length of the destination connection ID on an Initial packet.
+const MinConnectionIDLenInitial = 8
+
+// DefaultAckDelayExponent is the default ack delay exponent
+const DefaultAckDelayExponent = 3
+
+// MaxAckDelayExponent is the maximum ack delay exponent
+const MaxAckDelayExponent = 20
+
+// DefaultMaxAckDelay is the default max_ack_delay
+const DefaultMaxAckDelay = 25 * time.Millisecond
+
+// MaxMaxAckDelay is the maximum max_ack_delay
+const MaxMaxAckDelay = (1<<14 - 1) * time.Millisecond
+
+// MaxConnIDLen is the maximum length of the connection ID
+const MaxConnIDLen = 20
+
+// InvalidPacketLimitAES is the maximum number of packets that we can fail to decrypt when using
+// AEAD_AES_128_GCM or AEAD_AES_265_GCM.
+const InvalidPacketLimitAES = 1 << 52
+
+// InvalidPacketLimitChaCha is the maximum number of packets that we can fail to decrypt when using AEAD_CHACHA20_POLY1305.
+const InvalidPacketLimitChaCha = 1 << 36
diff --git a/vendor/github.com/quic-go/quic-go/internal/protocol/stream.go b/vendor/github.com/quic-go/quic-go/internal/protocol/stream.go
new file mode 100644
index 0000000000..ad7de864b8
--- /dev/null
+++ b/vendor/github.com/quic-go/quic-go/internal/protocol/stream.go
@@ -0,0 +1,76 @@
+package protocol
+
+// StreamType encodes if this is a unidirectional or bidirectional stream
+type StreamType uint8
+
+const (
+ // StreamTypeUni is a unidirectional stream
+ StreamTypeUni StreamType = iota
+ // StreamTypeBidi is a bidirectional stream
+ StreamTypeBidi
+)
+
+// InvalidPacketNumber is a stream ID that is invalid.
+// The first valid stream ID in QUIC is 0.
+const InvalidStreamID StreamID = -1
+
+// StreamNum is the stream number
+type StreamNum int64
+
+const (
+ // InvalidStreamNum is an invalid stream number.
+ InvalidStreamNum = -1
+ // MaxStreamCount is the maximum stream count value that can be sent in MAX_STREAMS frames
+ // and as the stream count in the transport parameters
+ MaxStreamCount StreamNum = 1 << 60
+)
+
+// StreamID calculates the stream ID.
+func (s StreamNum) StreamID(stype StreamType, pers Perspective) StreamID {
+ if s == 0 {
+ return InvalidStreamID
+ }
+ var first StreamID
+ switch stype {
+ case StreamTypeBidi:
+ switch pers {
+ case PerspectiveClient:
+ first = 0
+ case PerspectiveServer:
+ first = 1
+ }
+ case StreamTypeUni:
+ switch pers {
+ case PerspectiveClient:
+ first = 2
+ case PerspectiveServer:
+ first = 3
+ }
+ }
+ return first + 4*StreamID(s-1)
+}
+
+// A StreamID in QUIC
+type StreamID int64
+
+// InitiatedBy says if the stream was initiated by the client or by the server
+func (s StreamID) InitiatedBy() Perspective {
+ if s%2 == 0 {
+ return PerspectiveClient
+ }
+ return PerspectiveServer
+}
+
+// Type says if this is a unidirectional or bidirectional stream
+func (s StreamID) Type() StreamType {
+ if s%4 >= 2 {
+ return StreamTypeUni
+ }
+ return StreamTypeBidi
+}
+
+// StreamNum returns how many streams in total are below this
+// Example: for stream 9 it returns 3 (i.e. streams 1, 5 and 9)
+func (s StreamID) StreamNum() StreamNum {
+ return StreamNum(s/4) + 1
+}
diff --git a/vendor/github.com/quic-go/quic-go/internal/protocol/version.go b/vendor/github.com/quic-go/quic-go/internal/protocol/version.go
new file mode 100644
index 0000000000..2ae7a1154e
--- /dev/null
+++ b/vendor/github.com/quic-go/quic-go/internal/protocol/version.go
@@ -0,0 +1,114 @@
+package protocol
+
+import (
+ "crypto/rand"
+ "encoding/binary"
+ "fmt"
+ "math"
+)
+
+// VersionNumber is a version number as int
+type VersionNumber uint32
+
+// gQUIC version range as defined in the wiki: https://github.com/quicwg/base-drafts/wiki/QUIC-Versions
+const (
+ gquicVersion0 = 0x51303030
+ maxGquicVersion = 0x51303439
+)
+
+// The version numbers, making grepping easier
+const (
+ VersionTLS VersionNumber = 0x1
+ VersionWhatever VersionNumber = math.MaxUint32 - 1 // for when the version doesn't matter
+ VersionUnknown VersionNumber = math.MaxUint32
+ VersionDraft29 VersionNumber = 0xff00001d
+ Version1 VersionNumber = 0x1
+ Version2 VersionNumber = 0x6b3343cf
+)
+
+// SupportedVersions lists the versions that the server supports
+// must be in sorted descending order
+var SupportedVersions = []VersionNumber{Version1, Version2, VersionDraft29}
+
+// IsValidVersion says if the version is known to quic-go
+func IsValidVersion(v VersionNumber) bool {
+ return v == VersionTLS || IsSupportedVersion(SupportedVersions, v)
+}
+
+func (vn VersionNumber) String() string {
+ // For releases, VersionTLS will be set to a draft version.
+ // A switch statement can't contain duplicate cases.
+ if vn == VersionTLS && VersionTLS != VersionDraft29 && VersionTLS != Version1 {
+ return "TLS dev version (WIP)"
+ }
+ //nolint:exhaustive
+ switch vn {
+ case VersionWhatever:
+ return "whatever"
+ case VersionUnknown:
+ return "unknown"
+ case VersionDraft29:
+ return "draft-29"
+ case Version1:
+ return "v1"
+ case Version2:
+ return "v2"
+ default:
+ if vn.isGQUIC() {
+ return fmt.Sprintf("gQUIC %d", vn.toGQUICVersion())
+ }
+ return fmt.Sprintf("%#x", uint32(vn))
+ }
+}
+
+func (vn VersionNumber) isGQUIC() bool {
+ return vn > gquicVersion0 && vn <= maxGquicVersion
+}
+
+func (vn VersionNumber) toGQUICVersion() int {
+ return int(10*(vn-gquicVersion0)/0x100) + int(vn%0x10)
+}
+
+// IsSupportedVersion returns true if the server supports this version
+func IsSupportedVersion(supported []VersionNumber, v VersionNumber) bool {
+ for _, t := range supported {
+ if t == v {
+ return true
+ }
+ }
+ return false
+}
+
+// ChooseSupportedVersion finds the best version in the overlap of ours and theirs
+// ours is a slice of versions that we support, sorted by our preference (descending)
+// theirs is a slice of versions offered by the peer. The order does not matter.
+// The bool returned indicates if a matching version was found.
+func ChooseSupportedVersion(ours, theirs []VersionNumber) (VersionNumber, bool) {
+ for _, ourVer := range ours {
+ for _, theirVer := range theirs {
+ if ourVer == theirVer {
+ return ourVer, true
+ }
+ }
+ }
+ return 0, false
+}
+
+// generateReservedVersion generates a reserved version number (v & 0x0f0f0f0f == 0x0a0a0a0a)
+func generateReservedVersion() VersionNumber {
+ b := make([]byte, 4)
+ _, _ = rand.Read(b) // ignore the error here. Failure to read random data doesn't break anything
+ return VersionNumber((binary.BigEndian.Uint32(b) | 0x0a0a0a0a) & 0xfafafafa)
+}
+
+// GetGreasedVersions adds one reserved version number to a slice of version numbers, at a random position
+func GetGreasedVersions(supported []VersionNumber) []VersionNumber {
+ b := make([]byte, 1)
+ _, _ = rand.Read(b) // ignore the error here. Failure to read random data doesn't break anything
+ randPos := int(b[0]) % (len(supported) + 1)
+ greased := make([]VersionNumber, len(supported)+1)
+ copy(greased, supported[:randPos])
+ greased[randPos] = generateReservedVersion()
+ copy(greased[randPos+1:], supported[randPos:])
+ return greased
+}
diff --git a/vendor/github.com/quic-go/quic-go/internal/qerr/error_codes.go b/vendor/github.com/quic-go/quic-go/internal/qerr/error_codes.go
new file mode 100644
index 0000000000..cc846df6a7
--- /dev/null
+++ b/vendor/github.com/quic-go/quic-go/internal/qerr/error_codes.go
@@ -0,0 +1,88 @@
+package qerr
+
+import (
+ "fmt"
+
+ "github.com/quic-go/quic-go/internal/qtls"
+)
+
+// TransportErrorCode is a QUIC transport error.
+type TransportErrorCode uint64
+
+// The error codes defined by QUIC
+const (
+ NoError TransportErrorCode = 0x0
+ InternalError TransportErrorCode = 0x1
+ ConnectionRefused TransportErrorCode = 0x2
+ FlowControlError TransportErrorCode = 0x3
+ StreamLimitError TransportErrorCode = 0x4
+ StreamStateError TransportErrorCode = 0x5
+ FinalSizeError TransportErrorCode = 0x6
+ FrameEncodingError TransportErrorCode = 0x7
+ TransportParameterError TransportErrorCode = 0x8
+ ConnectionIDLimitError TransportErrorCode = 0x9
+ ProtocolViolation TransportErrorCode = 0xa
+ InvalidToken TransportErrorCode = 0xb
+ ApplicationErrorErrorCode TransportErrorCode = 0xc
+ CryptoBufferExceeded TransportErrorCode = 0xd
+ KeyUpdateError TransportErrorCode = 0xe
+ AEADLimitReached TransportErrorCode = 0xf
+ NoViablePathError TransportErrorCode = 0x10
+)
+
+func (e TransportErrorCode) IsCryptoError() bool {
+ return e >= 0x100 && e < 0x200
+}
+
+// Message is a description of the error.
+// It only returns a non-empty string for crypto errors.
+func (e TransportErrorCode) Message() string {
+ if !e.IsCryptoError() {
+ return ""
+ }
+ return qtls.Alert(e - 0x100).Error()
+}
+
+func (e TransportErrorCode) String() string {
+ switch e {
+ case NoError:
+ return "NO_ERROR"
+ case InternalError:
+ return "INTERNAL_ERROR"
+ case ConnectionRefused:
+ return "CONNECTION_REFUSED"
+ case FlowControlError:
+ return "FLOW_CONTROL_ERROR"
+ case StreamLimitError:
+ return "STREAM_LIMIT_ERROR"
+ case StreamStateError:
+ return "STREAM_STATE_ERROR"
+ case FinalSizeError:
+ return "FINAL_SIZE_ERROR"
+ case FrameEncodingError:
+ return "FRAME_ENCODING_ERROR"
+ case TransportParameterError:
+ return "TRANSPORT_PARAMETER_ERROR"
+ case ConnectionIDLimitError:
+ return "CONNECTION_ID_LIMIT_ERROR"
+ case ProtocolViolation:
+ return "PROTOCOL_VIOLATION"
+ case InvalidToken:
+ return "INVALID_TOKEN"
+ case ApplicationErrorErrorCode:
+ return "APPLICATION_ERROR"
+ case CryptoBufferExceeded:
+ return "CRYPTO_BUFFER_EXCEEDED"
+ case KeyUpdateError:
+ return "KEY_UPDATE_ERROR"
+ case AEADLimitReached:
+ return "AEAD_LIMIT_REACHED"
+ case NoViablePathError:
+ return "NO_VIABLE_PATH"
+ default:
+ if e.IsCryptoError() {
+ return fmt.Sprintf("CRYPTO_ERROR %#x", uint16(e))
+ }
+ return fmt.Sprintf("unknown error code: %#x", uint16(e))
+ }
+}
diff --git a/vendor/github.com/quic-go/quic-go/internal/qerr/errors.go b/vendor/github.com/quic-go/quic-go/internal/qerr/errors.go
new file mode 100644
index 0000000000..26ea344521
--- /dev/null
+++ b/vendor/github.com/quic-go/quic-go/internal/qerr/errors.go
@@ -0,0 +1,131 @@
+package qerr
+
+import (
+ "fmt"
+ "net"
+
+ "github.com/quic-go/quic-go/internal/protocol"
+)
+
+var (
+ ErrHandshakeTimeout = &HandshakeTimeoutError{}
+ ErrIdleTimeout = &IdleTimeoutError{}
+)
+
+type TransportError struct {
+ Remote bool
+ FrameType uint64
+ ErrorCode TransportErrorCode
+ ErrorMessage string
+}
+
+var _ error = &TransportError{}
+
+// NewLocalCryptoError create a new TransportError instance for a crypto error
+func NewLocalCryptoError(tlsAlert uint8, errorMessage string) *TransportError {
+ return &TransportError{
+ ErrorCode: 0x100 + TransportErrorCode(tlsAlert),
+ ErrorMessage: errorMessage,
+ }
+}
+
+func (e *TransportError) Error() string {
+ str := fmt.Sprintf("%s (%s)", e.ErrorCode.String(), getRole(e.Remote))
+ if e.FrameType != 0 {
+ str += fmt.Sprintf(" (frame type: %#x)", e.FrameType)
+ }
+ msg := e.ErrorMessage
+ if len(msg) == 0 {
+ msg = e.ErrorCode.Message()
+ }
+ if len(msg) == 0 {
+ return str
+ }
+ return str + ": " + msg
+}
+
+func (e *TransportError) Is(target error) bool {
+ return target == net.ErrClosed
+}
+
+// An ApplicationErrorCode is an application-defined error code.
+type ApplicationErrorCode uint64
+
+func (e *ApplicationError) Is(target error) bool {
+ return target == net.ErrClosed
+}
+
+// A StreamErrorCode is an error code used to cancel streams.
+type StreamErrorCode uint64
+
+type ApplicationError struct {
+ Remote bool
+ ErrorCode ApplicationErrorCode
+ ErrorMessage string
+}
+
+var _ error = &ApplicationError{}
+
+func (e *ApplicationError) Error() string {
+ if len(e.ErrorMessage) == 0 {
+ return fmt.Sprintf("Application error %#x (%s)", e.ErrorCode, getRole(e.Remote))
+ }
+ return fmt.Sprintf("Application error %#x (%s): %s", e.ErrorCode, getRole(e.Remote), e.ErrorMessage)
+}
+
+type IdleTimeoutError struct{}
+
+var _ error = &IdleTimeoutError{}
+
+func (e *IdleTimeoutError) Timeout() bool { return true }
+func (e *IdleTimeoutError) Temporary() bool { return false }
+func (e *IdleTimeoutError) Error() string { return "timeout: no recent network activity" }
+func (e *IdleTimeoutError) Is(target error) bool { return target == net.ErrClosed }
+
+type HandshakeTimeoutError struct{}
+
+var _ error = &HandshakeTimeoutError{}
+
+func (e *HandshakeTimeoutError) Timeout() bool { return true }
+func (e *HandshakeTimeoutError) Temporary() bool { return false }
+func (e *HandshakeTimeoutError) Error() string { return "timeout: handshake did not complete in time" }
+func (e *HandshakeTimeoutError) Is(target error) bool { return target == net.ErrClosed }
+
+// A VersionNegotiationError occurs when the client and the server can't agree on a QUIC version.
+type VersionNegotiationError struct {
+ Ours []protocol.VersionNumber
+ Theirs []protocol.VersionNumber
+}
+
+func (e *VersionNegotiationError) Error() string {
+ return fmt.Sprintf("no compatible QUIC version found (we support %s, server offered %s)", e.Ours, e.Theirs)
+}
+
+func (e *VersionNegotiationError) Is(target error) bool {
+ return target == net.ErrClosed
+}
+
+// A StatelessResetError occurs when we receive a stateless reset.
+type StatelessResetError struct {
+ Token protocol.StatelessResetToken
+}
+
+var _ net.Error = &StatelessResetError{}
+
+func (e *StatelessResetError) Error() string {
+ return fmt.Sprintf("received a stateless reset with token %x", e.Token)
+}
+
+func (e *StatelessResetError) Is(target error) bool {
+ return target == net.ErrClosed
+}
+
+func (e *StatelessResetError) Timeout() bool { return false }
+func (e *StatelessResetError) Temporary() bool { return true }
+
+func getRole(remote bool) string {
+ if remote {
+ return "remote"
+ }
+ return "local"
+}
diff --git a/vendor/github.com/quic-go/quic-go/internal/qtls/go118.go b/vendor/github.com/quic-go/quic-go/internal/qtls/go118.go
new file mode 100644
index 0000000000..e47dfe4c27
--- /dev/null
+++ b/vendor/github.com/quic-go/quic-go/internal/qtls/go118.go
@@ -0,0 +1,99 @@
+//go:build go1.18 && !go1.19
+
+package qtls
+
+import (
+ "crypto"
+ "crypto/cipher"
+ "crypto/tls"
+ "net"
+ "unsafe"
+
+ "github.com/quic-go/qtls-go1-18"
+)
+
+type (
+ // Alert is a TLS alert
+ Alert = qtls.Alert
+ // A Certificate is qtls.Certificate.
+ Certificate = qtls.Certificate
+ // CertificateRequestInfo contains inforamtion about a certificate request.
+ CertificateRequestInfo = qtls.CertificateRequestInfo
+ // A CipherSuiteTLS13 is a cipher suite for TLS 1.3
+ CipherSuiteTLS13 = qtls.CipherSuiteTLS13
+ // ClientHelloInfo contains information about a ClientHello.
+ ClientHelloInfo = qtls.ClientHelloInfo
+ // ClientSessionCache is a cache used for session resumption.
+ ClientSessionCache = qtls.ClientSessionCache
+ // ClientSessionState is a state needed for session resumption.
+ ClientSessionState = qtls.ClientSessionState
+ // A Config is a qtls.Config.
+ Config = qtls.Config
+ // A Conn is a qtls.Conn.
+ Conn = qtls.Conn
+ // ConnectionState contains information about the state of the connection.
+ ConnectionState = qtls.ConnectionStateWith0RTT
+ // EncryptionLevel is the encryption level of a message.
+ EncryptionLevel = qtls.EncryptionLevel
+ // Extension is a TLS extension
+ Extension = qtls.Extension
+ // ExtraConfig is the qtls.ExtraConfig
+ ExtraConfig = qtls.ExtraConfig
+ // RecordLayer is a qtls RecordLayer.
+ RecordLayer = qtls.RecordLayer
+)
+
+const (
+ // EncryptionHandshake is the Handshake encryption level
+ EncryptionHandshake = qtls.EncryptionHandshake
+ // Encryption0RTT is the 0-RTT encryption level
+ Encryption0RTT = qtls.Encryption0RTT
+ // EncryptionApplication is the application data encryption level
+ EncryptionApplication = qtls.EncryptionApplication
+)
+
+// AEADAESGCMTLS13 creates a new AES-GCM AEAD for TLS 1.3
+func AEADAESGCMTLS13(key, fixedNonce []byte) cipher.AEAD {
+ return qtls.AEADAESGCMTLS13(key, fixedNonce)
+}
+
+// Client returns a new TLS client side connection.
+func Client(conn net.Conn, config *Config, extraConfig *ExtraConfig) *Conn {
+ return qtls.Client(conn, config, extraConfig)
+}
+
+// Server returns a new TLS server side connection.
+func Server(conn net.Conn, config *Config, extraConfig *ExtraConfig) *Conn {
+ return qtls.Server(conn, config, extraConfig)
+}
+
+func GetConnectionState(conn *Conn) ConnectionState {
+ return conn.ConnectionStateWith0RTT()
+}
+
+// ToTLSConnectionState extracts the tls.ConnectionState
+func ToTLSConnectionState(cs ConnectionState) tls.ConnectionState {
+ return cs.ConnectionState
+}
+
+type cipherSuiteTLS13 struct {
+ ID uint16
+ KeyLen int
+ AEAD func(key, fixedNonce []byte) cipher.AEAD
+ Hash crypto.Hash
+}
+
+//go:linkname cipherSuiteTLS13ByID github.com/quic-go/qtls-go1-18.cipherSuiteTLS13ByID
+func cipherSuiteTLS13ByID(id uint16) *cipherSuiteTLS13
+
+// CipherSuiteTLS13ByID gets a TLS 1.3 cipher suite.
+func CipherSuiteTLS13ByID(id uint16) *CipherSuiteTLS13 {
+ val := cipherSuiteTLS13ByID(id)
+ cs := (*cipherSuiteTLS13)(unsafe.Pointer(val))
+ return &qtls.CipherSuiteTLS13{
+ ID: cs.ID,
+ KeyLen: cs.KeyLen,
+ AEAD: cs.AEAD,
+ Hash: cs.Hash,
+ }
+}
diff --git a/vendor/github.com/quic-go/quic-go/internal/qtls/go119.go b/vendor/github.com/quic-go/quic-go/internal/qtls/go119.go
new file mode 100644
index 0000000000..6c804ccef6
--- /dev/null
+++ b/vendor/github.com/quic-go/quic-go/internal/qtls/go119.go
@@ -0,0 +1,99 @@
+//go:build go1.19 && !go1.20
+
+package qtls
+
+import (
+ "crypto"
+ "crypto/cipher"
+ "crypto/tls"
+ "net"
+ "unsafe"
+
+ "github.com/quic-go/qtls-go1-19"
+)
+
+type (
+ // Alert is a TLS alert
+ Alert = qtls.Alert
+ // A Certificate is qtls.Certificate.
+ Certificate = qtls.Certificate
+ // CertificateRequestInfo contains information about a certificate request.
+ CertificateRequestInfo = qtls.CertificateRequestInfo
+ // A CipherSuiteTLS13 is a cipher suite for TLS 1.3
+ CipherSuiteTLS13 = qtls.CipherSuiteTLS13
+ // ClientHelloInfo contains information about a ClientHello.
+ ClientHelloInfo = qtls.ClientHelloInfo
+ // ClientSessionCache is a cache used for session resumption.
+ ClientSessionCache = qtls.ClientSessionCache
+ // ClientSessionState is a state needed for session resumption.
+ ClientSessionState = qtls.ClientSessionState
+ // A Config is a qtls.Config.
+ Config = qtls.Config
+ // A Conn is a qtls.Conn.
+ Conn = qtls.Conn
+ // ConnectionState contains information about the state of the connection.
+ ConnectionState = qtls.ConnectionStateWith0RTT
+ // EncryptionLevel is the encryption level of a message.
+ EncryptionLevel = qtls.EncryptionLevel
+ // Extension is a TLS extension
+ Extension = qtls.Extension
+ // ExtraConfig is the qtls.ExtraConfig
+ ExtraConfig = qtls.ExtraConfig
+ // RecordLayer is a qtls RecordLayer.
+ RecordLayer = qtls.RecordLayer
+)
+
+const (
+ // EncryptionHandshake is the Handshake encryption level
+ EncryptionHandshake = qtls.EncryptionHandshake
+ // Encryption0RTT is the 0-RTT encryption level
+ Encryption0RTT = qtls.Encryption0RTT
+ // EncryptionApplication is the application data encryption level
+ EncryptionApplication = qtls.EncryptionApplication
+)
+
+// AEADAESGCMTLS13 creates a new AES-GCM AEAD for TLS 1.3
+func AEADAESGCMTLS13(key, fixedNonce []byte) cipher.AEAD {
+ return qtls.AEADAESGCMTLS13(key, fixedNonce)
+}
+
+// Client returns a new TLS client side connection.
+func Client(conn net.Conn, config *Config, extraConfig *ExtraConfig) *Conn {
+ return qtls.Client(conn, config, extraConfig)
+}
+
+// Server returns a new TLS server side connection.
+func Server(conn net.Conn, config *Config, extraConfig *ExtraConfig) *Conn {
+ return qtls.Server(conn, config, extraConfig)
+}
+
+func GetConnectionState(conn *Conn) ConnectionState {
+ return conn.ConnectionStateWith0RTT()
+}
+
+// ToTLSConnectionState extracts the tls.ConnectionState
+func ToTLSConnectionState(cs ConnectionState) tls.ConnectionState {
+ return cs.ConnectionState
+}
+
+type cipherSuiteTLS13 struct {
+ ID uint16
+ KeyLen int
+ AEAD func(key, fixedNonce []byte) cipher.AEAD
+ Hash crypto.Hash
+}
+
+//go:linkname cipherSuiteTLS13ByID github.com/quic-go/qtls-go1-19.cipherSuiteTLS13ByID
+func cipherSuiteTLS13ByID(id uint16) *cipherSuiteTLS13
+
+// CipherSuiteTLS13ByID gets a TLS 1.3 cipher suite.
+func CipherSuiteTLS13ByID(id uint16) *CipherSuiteTLS13 {
+ val := cipherSuiteTLS13ByID(id)
+ cs := (*cipherSuiteTLS13)(unsafe.Pointer(val))
+ return &qtls.CipherSuiteTLS13{
+ ID: cs.ID,
+ KeyLen: cs.KeyLen,
+ AEAD: cs.AEAD,
+ Hash: cs.Hash,
+ }
+}
diff --git a/vendor/github.com/quic-go/quic-go/internal/qtls/go120.go b/vendor/github.com/quic-go/quic-go/internal/qtls/go120.go
new file mode 100644
index 0000000000..b9baa52fe3
--- /dev/null
+++ b/vendor/github.com/quic-go/quic-go/internal/qtls/go120.go
@@ -0,0 +1,99 @@
+//go:build go1.20
+
+package qtls
+
+import (
+ "crypto"
+ "crypto/cipher"
+ "crypto/tls"
+ "net"
+ "unsafe"
+
+ "github.com/quic-go/qtls-go1-20"
+)
+
+type (
+ // Alert is a TLS alert
+ Alert = qtls.Alert
+ // A Certificate is qtls.Certificate.
+ Certificate = qtls.Certificate
+ // CertificateRequestInfo contains information about a certificate request.
+ CertificateRequestInfo = qtls.CertificateRequestInfo
+ // A CipherSuiteTLS13 is a cipher suite for TLS 1.3
+ CipherSuiteTLS13 = qtls.CipherSuiteTLS13
+ // ClientHelloInfo contains information about a ClientHello.
+ ClientHelloInfo = qtls.ClientHelloInfo
+ // ClientSessionCache is a cache used for session resumption.
+ ClientSessionCache = qtls.ClientSessionCache
+ // ClientSessionState is a state needed for session resumption.
+ ClientSessionState = qtls.ClientSessionState
+ // A Config is a qtls.Config.
+ Config = qtls.Config
+ // A Conn is a qtls.Conn.
+ Conn = qtls.Conn
+ // ConnectionState contains information about the state of the connection.
+ ConnectionState = qtls.ConnectionStateWith0RTT
+ // EncryptionLevel is the encryption level of a message.
+ EncryptionLevel = qtls.EncryptionLevel
+ // Extension is a TLS extension
+ Extension = qtls.Extension
+ // ExtraConfig is the qtls.ExtraConfig
+ ExtraConfig = qtls.ExtraConfig
+ // RecordLayer is a qtls RecordLayer.
+ RecordLayer = qtls.RecordLayer
+)
+
+const (
+ // EncryptionHandshake is the Handshake encryption level
+ EncryptionHandshake = qtls.EncryptionHandshake
+ // Encryption0RTT is the 0-RTT encryption level
+ Encryption0RTT = qtls.Encryption0RTT
+ // EncryptionApplication is the application data encryption level
+ EncryptionApplication = qtls.EncryptionApplication
+)
+
+// AEADAESGCMTLS13 creates a new AES-GCM AEAD for TLS 1.3
+func AEADAESGCMTLS13(key, fixedNonce []byte) cipher.AEAD {
+ return qtls.AEADAESGCMTLS13(key, fixedNonce)
+}
+
+// Client returns a new TLS client side connection.
+func Client(conn net.Conn, config *Config, extraConfig *ExtraConfig) *Conn {
+ return qtls.Client(conn, config, extraConfig)
+}
+
+// Server returns a new TLS server side connection.
+func Server(conn net.Conn, config *Config, extraConfig *ExtraConfig) *Conn {
+ return qtls.Server(conn, config, extraConfig)
+}
+
+func GetConnectionState(conn *Conn) ConnectionState {
+ return conn.ConnectionStateWith0RTT()
+}
+
+// ToTLSConnectionState extracts the tls.ConnectionState
+func ToTLSConnectionState(cs ConnectionState) tls.ConnectionState {
+ return cs.ConnectionState
+}
+
+type cipherSuiteTLS13 struct {
+ ID uint16
+ KeyLen int
+ AEAD func(key, fixedNonce []byte) cipher.AEAD
+ Hash crypto.Hash
+}
+
+//go:linkname cipherSuiteTLS13ByID github.com/quic-go/qtls-go1-20.cipherSuiteTLS13ByID
+func cipherSuiteTLS13ByID(id uint16) *cipherSuiteTLS13
+
+// CipherSuiteTLS13ByID gets a TLS 1.3 cipher suite.
+func CipherSuiteTLS13ByID(id uint16) *CipherSuiteTLS13 {
+ val := cipherSuiteTLS13ByID(id)
+ cs := (*cipherSuiteTLS13)(unsafe.Pointer(val))
+ return &qtls.CipherSuiteTLS13{
+ ID: cs.ID,
+ KeyLen: cs.KeyLen,
+ AEAD: cs.AEAD,
+ Hash: cs.Hash,
+ }
+}
diff --git a/vendor/github.com/quic-go/quic-go/internal/qtls/go121.go b/vendor/github.com/quic-go/quic-go/internal/qtls/go121.go
new file mode 100644
index 0000000000..b33406397b
--- /dev/null
+++ b/vendor/github.com/quic-go/quic-go/internal/qtls/go121.go
@@ -0,0 +1,5 @@
+//go:build go1.21
+
+package qtls
+
+var _ int = "The version of quic-go you're using can't be built on Go 1.21 yet. For more details, please see https://github.com/quic-go/quic-go/wiki/quic-go-and-Go-versions."
diff --git a/vendor/github.com/quic-go/quic-go/internal/qtls/go_oldversion.go b/vendor/github.com/quic-go/quic-go/internal/qtls/go_oldversion.go
new file mode 100644
index 0000000000..f433b328ce
--- /dev/null
+++ b/vendor/github.com/quic-go/quic-go/internal/qtls/go_oldversion.go
@@ -0,0 +1,5 @@
+//go:build !go1.18
+
+package qtls
+
+var _ int = "The version of quic-go you're using can't be built using outdated Go versions. For more details, please see https://github.com/quic-go/quic-go/wiki/quic-go-and-Go-versions."
diff --git a/vendor/github.com/quic-go/quic-go/internal/utils/atomic_bool.go b/vendor/github.com/quic-go/quic-go/internal/utils/atomic_bool.go
new file mode 100644
index 0000000000..cf4642504e
--- /dev/null
+++ b/vendor/github.com/quic-go/quic-go/internal/utils/atomic_bool.go
@@ -0,0 +1,22 @@
+package utils
+
+import "sync/atomic"
+
+// An AtomicBool is an atomic bool
+type AtomicBool struct {
+ v int32
+}
+
+// Set sets the value
+func (a *AtomicBool) Set(value bool) {
+ var n int32
+ if value {
+ n = 1
+ }
+ atomic.StoreInt32(&a.v, n)
+}
+
+// Get gets the value
+func (a *AtomicBool) Get() bool {
+ return atomic.LoadInt32(&a.v) != 0
+}
diff --git a/vendor/github.com/quic-go/quic-go/internal/utils/buffered_write_closer.go b/vendor/github.com/quic-go/quic-go/internal/utils/buffered_write_closer.go
new file mode 100644
index 0000000000..b5b9d6fc7d
--- /dev/null
+++ b/vendor/github.com/quic-go/quic-go/internal/utils/buffered_write_closer.go
@@ -0,0 +1,26 @@
+package utils
+
+import (
+ "bufio"
+ "io"
+)
+
+type bufferedWriteCloser struct {
+ *bufio.Writer
+ io.Closer
+}
+
+// NewBufferedWriteCloser creates an io.WriteCloser from a bufio.Writer and an io.Closer
+func NewBufferedWriteCloser(writer *bufio.Writer, closer io.Closer) io.WriteCloser {
+ return &bufferedWriteCloser{
+ Writer: writer,
+ Closer: closer,
+ }
+}
+
+func (h bufferedWriteCloser) Close() error {
+ if err := h.Writer.Flush(); err != nil {
+ return err
+ }
+ return h.Closer.Close()
+}
diff --git a/vendor/github.com/quic-go/quic-go/internal/utils/byteorder.go b/vendor/github.com/quic-go/quic-go/internal/utils/byteorder.go
new file mode 100644
index 0000000000..a9b715e2f1
--- /dev/null
+++ b/vendor/github.com/quic-go/quic-go/internal/utils/byteorder.go
@@ -0,0 +1,21 @@
+package utils
+
+import (
+ "bytes"
+ "io"
+)
+
+// A ByteOrder specifies how to convert byte sequences into 16-, 32-, or 64-bit unsigned integers.
+type ByteOrder interface {
+ Uint32([]byte) uint32
+ Uint24([]byte) uint32
+ Uint16([]byte) uint16
+
+ ReadUint32(io.ByteReader) (uint32, error)
+ ReadUint24(io.ByteReader) (uint32, error)
+ ReadUint16(io.ByteReader) (uint16, error)
+
+ WriteUint32(*bytes.Buffer, uint32)
+ WriteUint24(*bytes.Buffer, uint32)
+ WriteUint16(*bytes.Buffer, uint16)
+}
diff --git a/vendor/github.com/quic-go/quic-go/internal/utils/byteorder_big_endian.go b/vendor/github.com/quic-go/quic-go/internal/utils/byteorder_big_endian.go
new file mode 100644
index 0000000000..834a711b9e
--- /dev/null
+++ b/vendor/github.com/quic-go/quic-go/internal/utils/byteorder_big_endian.go
@@ -0,0 +1,103 @@
+package utils
+
+import (
+ "bytes"
+ "encoding/binary"
+ "io"
+)
+
+// BigEndian is the big-endian implementation of ByteOrder.
+var BigEndian ByteOrder = bigEndian{}
+
+type bigEndian struct{}
+
+var _ ByteOrder = &bigEndian{}
+
+// ReadUintN reads N bytes
+func (bigEndian) ReadUintN(b io.ByteReader, length uint8) (uint64, error) {
+ var res uint64
+ for i := uint8(0); i < length; i++ {
+ bt, err := b.ReadByte()
+ if err != nil {
+ return 0, err
+ }
+ res ^= uint64(bt) << ((length - 1 - i) * 8)
+ }
+ return res, nil
+}
+
+// ReadUint32 reads a uint32
+func (bigEndian) ReadUint32(b io.ByteReader) (uint32, error) {
+ var b1, b2, b3, b4 uint8
+ var err error
+ if b4, err = b.ReadByte(); err != nil {
+ return 0, err
+ }
+ if b3, err = b.ReadByte(); err != nil {
+ return 0, err
+ }
+ if b2, err = b.ReadByte(); err != nil {
+ return 0, err
+ }
+ if b1, err = b.ReadByte(); err != nil {
+ return 0, err
+ }
+ return uint32(b1) + uint32(b2)<<8 + uint32(b3)<<16 + uint32(b4)<<24, nil
+}
+
+// ReadUint24 reads a uint24
+func (bigEndian) ReadUint24(b io.ByteReader) (uint32, error) {
+ var b1, b2, b3 uint8
+ var err error
+ if b3, err = b.ReadByte(); err != nil {
+ return 0, err
+ }
+ if b2, err = b.ReadByte(); err != nil {
+ return 0, err
+ }
+ if b1, err = b.ReadByte(); err != nil {
+ return 0, err
+ }
+ return uint32(b1) + uint32(b2)<<8 + uint32(b3)<<16, nil
+}
+
+// ReadUint16 reads a uint16
+func (bigEndian) ReadUint16(b io.ByteReader) (uint16, error) {
+ var b1, b2 uint8
+ var err error
+ if b2, err = b.ReadByte(); err != nil {
+ return 0, err
+ }
+ if b1, err = b.ReadByte(); err != nil {
+ return 0, err
+ }
+ return uint16(b1) + uint16(b2)<<8, nil
+}
+
+func (bigEndian) Uint32(b []byte) uint32 {
+ return binary.BigEndian.Uint32(b)
+}
+
+func (bigEndian) Uint24(b []byte) uint32 {
+ _ = b[2] // bounds check hint to compiler; see golang.org/issue/14808
+ return uint32(b[2]) | uint32(b[1])<<8 | uint32(b[0])<<16
+}
+
+func (bigEndian) Uint16(b []byte) uint16 {
+ return binary.BigEndian.Uint16(b)
+}
+
+// WriteUint32 writes a uint32
+func (bigEndian) WriteUint32(b *bytes.Buffer, i uint32) {
+ b.Write([]byte{uint8(i >> 24), uint8(i >> 16), uint8(i >> 8), uint8(i)})
+}
+
+// WriteUint24 writes a uint24
+func (bigEndian) WriteUint24(b *bytes.Buffer, i uint32) {
+ b.Write([]byte{uint8(i >> 16), uint8(i >> 8), uint8(i)})
+}
+
+// WriteUint16 writes a uint16
+func (bigEndian) WriteUint16(b *bytes.Buffer, i uint16) {
+ b.Write([]byte{uint8(i >> 8), uint8(i)})
+}
diff --git a/vendor/github.com/quic-go/quic-go/internal/utils/ip.go b/vendor/github.com/quic-go/quic-go/internal/utils/ip.go
new file mode 100644
index 0000000000..7ac7ffec11
--- /dev/null
+++ b/vendor/github.com/quic-go/quic-go/internal/utils/ip.go
@@ -0,0 +1,10 @@
+package utils
+
+import "net"
+
+func IsIPv4(ip net.IP) bool {
+ // If ip is not an IPv4 address, To4 returns nil.
+ // Note that there might be some corner cases, where this is not correct.
+ // See https://stackoverflow.com/questions/22751035/golang-distinguish-ipv4-ipv6.
+ return ip.To4() != nil
+}
diff --git a/vendor/github.com/quic-go/quic-go/internal/utils/linkedlist/README.md b/vendor/github.com/quic-go/quic-go/internal/utils/linkedlist/README.md
new file mode 100644
index 0000000000..66482f4fb1
--- /dev/null
+++ b/vendor/github.com/quic-go/quic-go/internal/utils/linkedlist/README.md
@@ -0,0 +1,6 @@
+# Usage
+
+This is the Go standard library implementation of a linked list
+(https://golang.org/src/container/list/list.go), with the following modifications:
+* it uses Go generics
+* it allows passing in a `sync.Pool` (via the `NewWithPool` constructor) to reduce allocations of `Element` structs
diff --git a/vendor/github.com/quic-go/quic-go/internal/utils/linkedlist/linkedlist.go b/vendor/github.com/quic-go/quic-go/internal/utils/linkedlist/linkedlist.go
new file mode 100644
index 0000000000..804a34444a
--- /dev/null
+++ b/vendor/github.com/quic-go/quic-go/internal/utils/linkedlist/linkedlist.go
@@ -0,0 +1,264 @@
+// Copyright 2009 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Package list implements a doubly linked list.
+//
+// To iterate over a list (where l is a *List[T]):
+//
+// for e := l.Front(); e != nil; e = e.Next() {
+// // do something with e.Value
+// }
+package list
+
+import "sync"
+
+func NewPool[T any]() *sync.Pool {
+ return &sync.Pool{New: func() any { return &Element[T]{} }}
+}
+
+// Element is an element of a linked list.
+type Element[T any] struct {
+ // Next and previous pointers in the doubly-linked list of elements.
+ // To simplify the implementation, internally a list l is implemented
+ // as a ring, such that &l.root is both the next element of the last
+ // list element (l.Back()) and the previous element of the first list
+ // element (l.Front()).
+ next, prev *Element[T]
+
+ // The list to which this element belongs.
+ list *List[T]
+
+ // The value stored with this element.
+ Value T
+}
+
+// Next returns the next list element or nil.
+func (e *Element[T]) Next() *Element[T] {
+ if p := e.next; e.list != nil && p != &e.list.root {
+ return p
+ }
+ return nil
+}
+
+// Prev returns the previous list element or nil.
+func (e *Element[T]) Prev() *Element[T] {
+ if p := e.prev; e.list != nil && p != &e.list.root {
+ return p
+ }
+ return nil
+}
+
+func (e *Element[T]) List() *List[T] {
+ return e.list
+}
+
+// List represents a doubly linked list.
+// The zero value for List is an empty list ready to use.
+type List[T any] struct {
+ root Element[T] // sentinel list element, only &root, root.prev, and root.next are used
+ len int // current list length excluding (this) sentinel element
+
+ pool *sync.Pool
+}
+
+// Init initializes or clears list l.
+func (l *List[T]) Init() *List[T] {
+ l.root.next = &l.root
+ l.root.prev = &l.root
+ l.len = 0
+ return l
+}
+
+// New returns an initialized list.
+func New[T any]() *List[T] { return new(List[T]).Init() }
+
+// NewWithPool returns an initialized list, using a sync.Pool for list elements.
+func NewWithPool[T any](pool *sync.Pool) *List[T] {
+ l := &List[T]{pool: pool}
+ return l.Init()
+}
+
+// Len returns the number of elements of list l.
+// The complexity is O(1).
+func (l *List[T]) Len() int { return l.len }
+
+// Front returns the first element of list l or nil if the list is empty.
+func (l *List[T]) Front() *Element[T] {
+ if l.len == 0 {
+ return nil
+ }
+ return l.root.next
+}
+
+// Back returns the last element of list l or nil if the list is empty.
+func (l *List[T]) Back() *Element[T] {
+ if l.len == 0 {
+ return nil
+ }
+ return l.root.prev
+}
+
+// lazyInit lazily initializes a zero List value.
+func (l *List[T]) lazyInit() {
+ if l.root.next == nil {
+ l.Init()
+ }
+}
+
+// insert inserts e after at, increments l.len, and returns e.
+func (l *List[T]) insert(e, at *Element[T]) *Element[T] {
+ e.prev = at
+ e.next = at.next
+ e.prev.next = e
+ e.next.prev = e
+ e.list = l
+ l.len++
+ return e
+}
+
+// insertValue is a convenience wrapper for insert(&Element{Value: v}, at).
+func (l *List[T]) insertValue(v T, at *Element[T]) *Element[T] {
+ var e *Element[T]
+ if l.pool != nil {
+ e = l.pool.Get().(*Element[T])
+ } else {
+ e = &Element[T]{}
+ }
+ e.Value = v
+ return l.insert(e, at)
+}
+
+// remove removes e from its list, decrements l.len
+func (l *List[T]) remove(e *Element[T]) {
+ e.prev.next = e.next
+ e.next.prev = e.prev
+ e.next = nil // avoid memory leaks
+ e.prev = nil // avoid memory leaks
+ e.list = nil
+ if l.pool != nil {
+ l.pool.Put(e)
+ }
+ l.len--
+}
+
+// move moves e to next to at.
+func (l *List[T]) move(e, at *Element[T]) {
+ if e == at {
+ return
+ }
+ e.prev.next = e.next
+ e.next.prev = e.prev
+
+ e.prev = at
+ e.next = at.next
+ e.prev.next = e
+ e.next.prev = e
+}
+
+// Remove removes e from l if e is an element of list l.
+// It returns the element value e.Value.
+// The element must not be nil.
+func (l *List[T]) Remove(e *Element[T]) T {
+ v := e.Value
+ if e.list == l {
+ // if e.list == l, l must have been initialized when e was inserted
+ // in l or l == nil (e is a zero Element) and l.remove will crash
+ l.remove(e)
+ }
+ return v
+}
+
+// PushFront inserts a new element e with value v at the front of list l and returns e.
+func (l *List[T]) PushFront(v T) *Element[T] {
+ l.lazyInit()
+ return l.insertValue(v, &l.root)
+}
+
+// PushBack inserts a new element e with value v at the back of list l and returns e.
+func (l *List[T]) PushBack(v T) *Element[T] {
+ l.lazyInit()
+ return l.insertValue(v, l.root.prev)
+}
+
+// InsertBefore inserts a new element e with value v immediately before mark and returns e.
+// If mark is not an element of l, the list is not modified.
+// The mark must not be nil.
+func (l *List[T]) InsertBefore(v T, mark *Element[T]) *Element[T] {
+ if mark.list != l {
+ return nil
+ }
+ // see comment in List.Remove about initialization of l
+ return l.insertValue(v, mark.prev)
+}
+
+// InsertAfter inserts a new element e with value v immediately after mark and returns e.
+// If mark is not an element of l, the list is not modified.
+// The mark must not be nil.
+func (l *List[T]) InsertAfter(v T, mark *Element[T]) *Element[T] {
+ if mark.list != l {
+ return nil
+ }
+ // see comment in List.Remove about initialization of l
+ return l.insertValue(v, mark)
+}
+
+// MoveToFront moves element e to the front of list l.
+// If e is not an element of l, the list is not modified.
+// The element must not be nil.
+func (l *List[T]) MoveToFront(e *Element[T]) {
+ if e.list != l || l.root.next == e {
+ return
+ }
+ // see comment in List.Remove about initialization of l
+ l.move(e, &l.root)
+}
+
+// MoveToBack moves element e to the back of list l.
+// If e is not an element of l, the list is not modified.
+// The element must not be nil.
+func (l *List[T]) MoveToBack(e *Element[T]) {
+ if e.list != l || l.root.prev == e {
+ return
+ }
+ // see comment in List.Remove about initialization of l
+ l.move(e, l.root.prev)
+}
+
+// MoveBefore moves element e to its new position before mark.
+// If e or mark is not an element of l, or e == mark, the list is not modified.
+// The element and mark must not be nil.
+func (l *List[T]) MoveBefore(e, mark *Element[T]) {
+ if e.list != l || e == mark || mark.list != l {
+ return
+ }
+ l.move(e, mark.prev)
+}
+
+// MoveAfter moves element e to its new position after mark.
+// If e or mark is not an element of l, or e == mark, the list is not modified.
+// The element and mark must not be nil.
+func (l *List[T]) MoveAfter(e, mark *Element[T]) {
+ if e.list != l || e == mark || mark.list != l {
+ return
+ }
+ l.move(e, mark)
+}
+
+// PushBackList inserts a copy of another list at the back of list l.
+// The lists l and other may be the same. They must not be nil.
+func (l *List[T]) PushBackList(other *List[T]) {
+ l.lazyInit()
+ for i, e := other.Len(), other.Front(); i > 0; i, e = i-1, e.Next() {
+ l.insertValue(e.Value, l.root.prev)
+ }
+}
+
+// PushFrontList inserts a copy of another list at the front of list l.
+// The lists l and other may be the same. They must not be nil.
+func (l *List[T]) PushFrontList(other *List[T]) {
+ l.lazyInit()
+ for i, e := other.Len(), other.Back(); i > 0; i, e = i-1, e.Prev() {
+ l.insertValue(e.Value, &l.root)
+ }
+}
diff --git a/vendor/github.com/quic-go/quic-go/internal/utils/log.go b/vendor/github.com/quic-go/quic-go/internal/utils/log.go
new file mode 100644
index 0000000000..89b52c0d9a
--- /dev/null
+++ b/vendor/github.com/quic-go/quic-go/internal/utils/log.go
@@ -0,0 +1,131 @@
+package utils
+
+import (
+ "fmt"
+ "log"
+ "os"
+ "strings"
+ "time"
+)
+
+// LogLevel of quic-go
+type LogLevel uint8
+
+const (
+ // LogLevelNothing disables
+ LogLevelNothing LogLevel = iota
+ // LogLevelError enables err logs
+ LogLevelError
+ // LogLevelInfo enables info logs (e.g. packets)
+ LogLevelInfo
+ // LogLevelDebug enables debug logs (e.g. packet contents)
+ LogLevelDebug
+)
+
+const logEnv = "QUIC_GO_LOG_LEVEL"
+
+// A Logger logs.
+type Logger interface {
+ SetLogLevel(LogLevel)
+ SetLogTimeFormat(format string)
+ WithPrefix(prefix string) Logger
+ Debug() bool
+
+ Errorf(format string, args ...interface{})
+ Infof(format string, args ...interface{})
+ Debugf(format string, args ...interface{})
+}
+
+// DefaultLogger is used by quic-go for logging.
+var DefaultLogger Logger
+
+type defaultLogger struct {
+ prefix string
+
+ logLevel LogLevel
+ timeFormat string
+}
+
+var _ Logger = &defaultLogger{}
+
+// SetLogLevel sets the log level
+func (l *defaultLogger) SetLogLevel(level LogLevel) {
+ l.logLevel = level
+}
+
+// SetLogTimeFormat sets the format of the timestamp
+// an empty string disables the logging of timestamps
+func (l *defaultLogger) SetLogTimeFormat(format string) {
+ log.SetFlags(0) // disable timestamp logging done by the log package
+ l.timeFormat = format
+}
+
+// Debugf logs something
+func (l *defaultLogger) Debugf(format string, args ...interface{}) {
+ if l.logLevel == LogLevelDebug {
+ l.logMessage(format, args...)
+ }
+}
+
+// Infof logs something
+func (l *defaultLogger) Infof(format string, args ...interface{}) {
+ if l.logLevel >= LogLevelInfo {
+ l.logMessage(format, args...)
+ }
+}
+
+// Errorf logs something
+func (l *defaultLogger) Errorf(format string, args ...interface{}) {
+ if l.logLevel >= LogLevelError {
+ l.logMessage(format, args...)
+ }
+}
+
+func (l *defaultLogger) logMessage(format string, args ...interface{}) {
+ var pre string
+
+ if len(l.timeFormat) > 0 {
+ pre = time.Now().Format(l.timeFormat) + " "
+ }
+ if len(l.prefix) > 0 {
+ pre += l.prefix + " "
+ }
+ log.Printf(pre+format, args...)
+}
+
+func (l *defaultLogger) WithPrefix(prefix string) Logger {
+ if len(l.prefix) > 0 {
+ prefix = l.prefix + " " + prefix
+ }
+ return &defaultLogger{
+ logLevel: l.logLevel,
+ timeFormat: l.timeFormat,
+ prefix: prefix,
+ }
+}
+
+// Debug returns true if the log level is LogLevelDebug
+func (l *defaultLogger) Debug() bool {
+ return l.logLevel == LogLevelDebug
+}
+
+func init() {
+ DefaultLogger = &defaultLogger{}
+ DefaultLogger.SetLogLevel(readLoggingEnv())
+}
+
+func readLoggingEnv() LogLevel {
+ switch strings.ToLower(os.Getenv(logEnv)) {
+ case "":
+ return LogLevelNothing
+ case "debug":
+ return LogLevelDebug
+ case "info":
+ return LogLevelInfo
+ case "error":
+ return LogLevelError
+ default:
+ fmt.Fprintln(os.Stderr, "invalid quic-go log level, see https://github.com/quic-go/quic-go/wiki/Logging")
+ return LogLevelNothing
+ }
+}
diff --git a/vendor/github.com/quic-go/quic-go/internal/utils/minmax.go b/vendor/github.com/quic-go/quic-go/internal/utils/minmax.go
new file mode 100644
index 0000000000..d191f75158
--- /dev/null
+++ b/vendor/github.com/quic-go/quic-go/internal/utils/minmax.go
@@ -0,0 +1,72 @@
+package utils
+
+import (
+ "math"
+ "time"
+
+ "golang.org/x/exp/constraints"
+)
+
+// InfDuration is a duration of infinite length
+const InfDuration = time.Duration(math.MaxInt64)
+
+func Max[T constraints.Ordered](a, b T) T {
+ if a < b {
+ return b
+ }
+ return a
+}
+
+func Min[T constraints.Ordered](a, b T) T {
+ if a < b {
+ return a
+ }
+ return b
+}
+
+// MinNonZeroDuration return the minimum duration that's not zero.
+func MinNonZeroDuration(a, b time.Duration) time.Duration {
+ if a == 0 {
+ return b
+ }
+ if b == 0 {
+ return a
+ }
+ return Min(a, b)
+}
+
+// AbsDuration returns the absolute value of a time duration
+func AbsDuration(d time.Duration) time.Duration {
+ if d >= 0 {
+ return d
+ }
+ return -d
+}
+
+// MinTime returns the earlier time
+func MinTime(a, b time.Time) time.Time {
+ if a.After(b) {
+ return b
+ }
+ return a
+}
+
+// MinNonZeroTime returns the earlist time that is not time.Time{}
+// If both a and b are time.Time{}, it returns time.Time{}
+func MinNonZeroTime(a, b time.Time) time.Time {
+ if a.IsZero() {
+ return b
+ }
+ if b.IsZero() {
+ return a
+ }
+ return MinTime(a, b)
+}
+
+// MaxTime returns the later time
+func MaxTime(a, b time.Time) time.Time {
+ if a.After(b) {
+ return a
+ }
+ return b
+}
diff --git a/vendor/github.com/quic-go/quic-go/internal/utils/rand.go b/vendor/github.com/quic-go/quic-go/internal/utils/rand.go
new file mode 100644
index 0000000000..30069144a2
--- /dev/null
+++ b/vendor/github.com/quic-go/quic-go/internal/utils/rand.go
@@ -0,0 +1,29 @@
+package utils
+
+import (
+ "crypto/rand"
+ "encoding/binary"
+)
+
+// Rand is a wrapper around crypto/rand that adds some convenience functions known from math/rand.
+type Rand struct {
+ buf [4]byte
+}
+
+func (r *Rand) Int31() int32 {
+ rand.Read(r.buf[:])
+ return int32(binary.BigEndian.Uint32(r.buf[:]) & ^uint32(1<<31))
+}
+
+// copied from the standard library math/rand implementation of Int63n
+func (r *Rand) Int31n(n int32) int32 {
+ if n&(n-1) == 0 { // n is power of two, can mask
+ return r.Int31() & (n - 1)
+ }
+ max := int32((1 << 31) - 1 - (1<<31)%uint32(n))
+ v := r.Int31()
+ for v > max {
+ v = r.Int31()
+ }
+ return v % n
+}
diff --git a/vendor/github.com/quic-go/quic-go/internal/utils/rtt_stats.go b/vendor/github.com/quic-go/quic-go/internal/utils/rtt_stats.go
new file mode 100644
index 0000000000..527539e1e2
--- /dev/null
+++ b/vendor/github.com/quic-go/quic-go/internal/utils/rtt_stats.go
@@ -0,0 +1,127 @@
+package utils
+
+import (
+ "time"
+
+ "github.com/quic-go/quic-go/internal/protocol"
+)
+
+const (
+ rttAlpha = 0.125
+ oneMinusAlpha = 1 - rttAlpha
+ rttBeta = 0.25
+ oneMinusBeta = 1 - rttBeta
+ // The default RTT used before an RTT sample is taken.
+ defaultInitialRTT = 100 * time.Millisecond
+)
+
+// RTTStats provides round-trip statistics
+type RTTStats struct {
+ hasMeasurement bool
+
+ minRTT time.Duration
+ latestRTT time.Duration
+ smoothedRTT time.Duration
+ meanDeviation time.Duration
+
+ maxAckDelay time.Duration
+}
+
+// NewRTTStats makes a properly initialized RTTStats object
+func NewRTTStats() *RTTStats {
+ return &RTTStats{}
+}
+
+// MinRTT Returns the minRTT for the entire connection.
+// May return Zero if no valid updates have occurred.
+func (r *RTTStats) MinRTT() time.Duration { return r.minRTT }
+
+// LatestRTT returns the most recent rtt measurement.
+// May return Zero if no valid updates have occurred.
+func (r *RTTStats) LatestRTT() time.Duration { return r.latestRTT }
+
+// SmoothedRTT returns the smoothed RTT for the connection.
+// May return Zero if no valid updates have occurred.
+func (r *RTTStats) SmoothedRTT() time.Duration { return r.smoothedRTT }
+
+// MeanDeviation gets the mean deviation
+func (r *RTTStats) MeanDeviation() time.Duration { return r.meanDeviation }
+
+// MaxAckDelay gets the max_ack_delay advertised by the peer
+func (r *RTTStats) MaxAckDelay() time.Duration { return r.maxAckDelay }
+
+// PTO gets the probe timeout duration.
+func (r *RTTStats) PTO(includeMaxAckDelay bool) time.Duration {
+ if r.SmoothedRTT() == 0 {
+ return 2 * defaultInitialRTT
+ }
+ pto := r.SmoothedRTT() + Max(4*r.MeanDeviation(), protocol.TimerGranularity)
+ if includeMaxAckDelay {
+ pto += r.MaxAckDelay()
+ }
+ return pto
+}
+
+// UpdateRTT updates the RTT based on a new sample.
+func (r *RTTStats) UpdateRTT(sendDelta, ackDelay time.Duration, now time.Time) {
+ if sendDelta == InfDuration || sendDelta <= 0 {
+ return
+ }
+
+ // Update r.minRTT first. r.minRTT does not use an rttSample corrected for
+ // ackDelay but the raw observed sendDelta, since poor clock granularity at
+ // the client may cause a high ackDelay to result in underestimation of the
+ // r.minRTT.
+ if r.minRTT == 0 || r.minRTT > sendDelta {
+ r.minRTT = sendDelta
+ }
+
+ // Correct for ackDelay if information received from the peer results in a
+ // an RTT sample at least as large as minRTT. Otherwise, only use the
+ // sendDelta.
+ sample := sendDelta
+ if sample-r.minRTT >= ackDelay {
+ sample -= ackDelay
+ }
+ r.latestRTT = sample
+ // First time call.
+ if !r.hasMeasurement {
+ r.hasMeasurement = true
+ r.smoothedRTT = sample
+ r.meanDeviation = sample / 2
+ } else {
+ r.meanDeviation = time.Duration(oneMinusBeta*float32(r.meanDeviation/time.Microsecond)+rttBeta*float32(AbsDuration(r.smoothedRTT-sample)/time.Microsecond)) * time.Microsecond
+ r.smoothedRTT = time.Duration((float32(r.smoothedRTT/time.Microsecond)*oneMinusAlpha)+(float32(sample/time.Microsecond)*rttAlpha)) * time.Microsecond
+ }
+}
+
+// SetMaxAckDelay sets the max_ack_delay
+func (r *RTTStats) SetMaxAckDelay(mad time.Duration) {
+ r.maxAckDelay = mad
+}
+
+// SetInitialRTT sets the initial RTT.
+// It is used during the 0-RTT handshake when restoring the RTT stats from the session state.
+func (r *RTTStats) SetInitialRTT(t time.Duration) {
+ if r.hasMeasurement {
+ panic("initial RTT set after first measurement")
+ }
+ r.smoothedRTT = t
+ r.latestRTT = t
+}
+
+// OnConnectionMigration is called when connection migrates and rtt measurement needs to be reset.
+func (r *RTTStats) OnConnectionMigration() {
+ r.latestRTT = 0
+ r.minRTT = 0
+ r.smoothedRTT = 0
+ r.meanDeviation = 0
+}
+
+// ExpireSmoothedMetrics causes the smoothed_rtt to be increased to the latest_rtt if the latest_rtt
+// is larger. The mean deviation is increased to the most recent deviation if
+// it's larger.
+func (r *RTTStats) ExpireSmoothedMetrics() {
+ r.meanDeviation = Max(r.meanDeviation, AbsDuration(r.smoothedRTT-r.latestRTT))
+ r.smoothedRTT = Max(r.smoothedRTT, r.latestRTT)
+}
diff --git a/vendor/github.com/quic-go/quic-go/internal/utils/timer.go b/vendor/github.com/quic-go/quic-go/internal/utils/timer.go
new file mode 100644
index 0000000000..361106c8a9
--- /dev/null
+++ b/vendor/github.com/quic-go/quic-go/internal/utils/timer.go
@@ -0,0 +1,57 @@
+package utils
+
+import (
+ "math"
+ "time"
+)
+
+// A Timer wrapper that behaves correctly when resetting
+type Timer struct {
+ t *time.Timer
+ read bool
+ deadline time.Time
+}
+
+// NewTimer creates a new timer that is not set
+func NewTimer() *Timer {
+ return &Timer{t: time.NewTimer(time.Duration(math.MaxInt64))}
+}
+
+// Chan returns the channel of the wrapped timer
+func (t *Timer) Chan() <-chan time.Time {
+ return t.t.C
+}
+
+// Reset the timer, no matter whether the value was read or not
+func (t *Timer) Reset(deadline time.Time) {
+ if deadline.Equal(t.deadline) && !t.read {
+ // No need to reset the timer
+ return
+ }
+
+ // We need to drain the timer if the value from its channel was not read yet.
+ // See https://groups.google.com/forum/#!topic/golang-dev/c9UUfASVPoU
+ if !t.t.Stop() && !t.read {
+ <-t.t.C
+ }
+ if !deadline.IsZero() {
+ t.t.Reset(time.Until(deadline))
+ }
+
+ t.read = false
+ t.deadline = deadline
+}
+
+// SetRead should be called after the value from the chan was read
+func (t *Timer) SetRead() {
+ t.read = true
+}
+
+func (t *Timer) Deadline() time.Time {
+ return t.deadline
+}
+
+// Stop stops the timer
+func (t *Timer) Stop() {
+ t.t.Stop()
+}
diff --git a/vendor/github.com/quic-go/quic-go/internal/wire/ack_frame.go b/vendor/github.com/quic-go/quic-go/internal/wire/ack_frame.go
new file mode 100644
index 0000000000..5b01649a32
--- /dev/null
+++ b/vendor/github.com/quic-go/quic-go/internal/wire/ack_frame.go
@@ -0,0 +1,251 @@
+package wire
+
+import (
+ "bytes"
+ "errors"
+ "sort"
+ "time"
+
+ "github.com/quic-go/quic-go/internal/protocol"
+ "github.com/quic-go/quic-go/internal/utils"
+ "github.com/quic-go/quic-go/quicvarint"
+)
+
+var errInvalidAckRanges = errors.New("AckFrame: ACK frame contains invalid ACK ranges")
+
+// An AckFrame is an ACK frame
+type AckFrame struct {
+ AckRanges []AckRange // has to be ordered. The highest ACK range goes first, the lowest ACK range goes last
+ DelayTime time.Duration
+
+ ECT0, ECT1, ECNCE uint64
+}
+
+// parseAckFrame reads an ACK frame
+func parseAckFrame(r *bytes.Reader, ackDelayExponent uint8, _ protocol.VersionNumber) (*AckFrame, error) {
+ typeByte, err := r.ReadByte()
+ if err != nil {
+ return nil, err
+ }
+ ecn := typeByte&0x1 > 0
+
+ frame := GetAckFrame()
+
+ la, err := quicvarint.Read(r)
+ if err != nil {
+ return nil, err
+ }
+ largestAcked := protocol.PacketNumber(la)
+ delay, err := quicvarint.Read(r)
+ if err != nil {
+ return nil, err
+ }
+
+ delayTime := time.Duration(delay*1< largestAcked {
+ return nil, errors.New("invalid first ACK range")
+ }
+ smallest := largestAcked - ackBlock
+
+ // read all the other ACK ranges
+ frame.AckRanges = append(frame.AckRanges, AckRange{Smallest: smallest, Largest: largestAcked})
+ for i := uint64(0); i < numBlocks; i++ {
+ g, err := quicvarint.Read(r)
+ if err != nil {
+ return nil, err
+ }
+ gap := protocol.PacketNumber(g)
+ if smallest < gap+2 {
+ return nil, errInvalidAckRanges
+ }
+ largest := smallest - gap - 2
+
+ ab, err := quicvarint.Read(r)
+ if err != nil {
+ return nil, err
+ }
+ ackBlock := protocol.PacketNumber(ab)
+
+ if ackBlock > largest {
+ return nil, errInvalidAckRanges
+ }
+ smallest = largest - ackBlock
+ frame.AckRanges = append(frame.AckRanges, AckRange{Smallest: smallest, Largest: largest})
+ }
+
+ if !frame.validateAckRanges() {
+ return nil, errInvalidAckRanges
+ }
+
+ // parse (and skip) the ECN section
+ if ecn {
+ for i := 0; i < 3; i++ {
+ if _, err := quicvarint.Read(r); err != nil {
+ return nil, err
+ }
+ }
+ }
+
+ return frame, nil
+}
+
+// Append appends an ACK frame.
+func (f *AckFrame) Append(b []byte, _ protocol.VersionNumber) ([]byte, error) {
+ hasECN := f.ECT0 > 0 || f.ECT1 > 0 || f.ECNCE > 0
+ if hasECN {
+ b = append(b, 0b11)
+ } else {
+ b = append(b, 0b10)
+ }
+ b = quicvarint.Append(b, uint64(f.LargestAcked()))
+ b = quicvarint.Append(b, encodeAckDelay(f.DelayTime))
+
+ numRanges := f.numEncodableAckRanges()
+ b = quicvarint.Append(b, uint64(numRanges-1))
+
+ // write the first range
+ _, firstRange := f.encodeAckRange(0)
+ b = quicvarint.Append(b, firstRange)
+
+ // write all the other range
+ for i := 1; i < numRanges; i++ {
+ gap, len := f.encodeAckRange(i)
+ b = quicvarint.Append(b, gap)
+ b = quicvarint.Append(b, len)
+ }
+
+ if hasECN {
+ b = quicvarint.Append(b, f.ECT0)
+ b = quicvarint.Append(b, f.ECT1)
+ b = quicvarint.Append(b, f.ECNCE)
+ }
+ return b, nil
+}
+
+// Length of a written frame
+func (f *AckFrame) Length(_ protocol.VersionNumber) protocol.ByteCount {
+ largestAcked := f.AckRanges[0].Largest
+ numRanges := f.numEncodableAckRanges()
+
+ length := 1 + quicvarint.Len(uint64(largestAcked)) + quicvarint.Len(encodeAckDelay(f.DelayTime))
+
+ length += quicvarint.Len(uint64(numRanges - 1))
+ lowestInFirstRange := f.AckRanges[0].Smallest
+ length += quicvarint.Len(uint64(largestAcked - lowestInFirstRange))
+
+ for i := 1; i < numRanges; i++ {
+ gap, len := f.encodeAckRange(i)
+ length += quicvarint.Len(gap)
+ length += quicvarint.Len(len)
+ }
+ if f.ECT0 > 0 || f.ECT1 > 0 || f.ECNCE > 0 {
+ length += quicvarint.Len(f.ECT0)
+ length += quicvarint.Len(f.ECT1)
+ length += quicvarint.Len(f.ECNCE)
+ }
+ return length
+}
+
+// gets the number of ACK ranges that can be encoded
+// such that the resulting frame is smaller than the maximum ACK frame size
+func (f *AckFrame) numEncodableAckRanges() int {
+ length := 1 + quicvarint.Len(uint64(f.LargestAcked())) + quicvarint.Len(encodeAckDelay(f.DelayTime))
+ length += 2 // assume that the number of ranges will consume 2 bytes
+ for i := 1; i < len(f.AckRanges); i++ {
+ gap, len := f.encodeAckRange(i)
+ rangeLen := quicvarint.Len(gap) + quicvarint.Len(len)
+ if length+rangeLen > protocol.MaxAckFrameSize {
+ // Writing range i would exceed the MaxAckFrameSize.
+ // So encode one range less than that.
+ return i - 1
+ }
+ length += rangeLen
+ }
+ return len(f.AckRanges)
+}
+
+func (f *AckFrame) encodeAckRange(i int) (uint64 /* gap */, uint64 /* length */) {
+ if i == 0 {
+ return 0, uint64(f.AckRanges[0].Largest - f.AckRanges[0].Smallest)
+ }
+ return uint64(f.AckRanges[i-1].Smallest - f.AckRanges[i].Largest - 2),
+ uint64(f.AckRanges[i].Largest - f.AckRanges[i].Smallest)
+}
+
+// HasMissingRanges returns if this frame reports any missing packets
+func (f *AckFrame) HasMissingRanges() bool {
+ return len(f.AckRanges) > 1
+}
+
+func (f *AckFrame) validateAckRanges() bool {
+ if len(f.AckRanges) == 0 {
+ return false
+ }
+
+ // check the validity of every single ACK range
+ for _, ackRange := range f.AckRanges {
+ if ackRange.Smallest > ackRange.Largest {
+ return false
+ }
+ }
+
+ // check the consistency for ACK with multiple NACK ranges
+ for i, ackRange := range f.AckRanges {
+ if i == 0 {
+ continue
+ }
+ lastAckRange := f.AckRanges[i-1]
+ if lastAckRange.Smallest <= ackRange.Smallest {
+ return false
+ }
+ if lastAckRange.Smallest <= ackRange.Largest+1 {
+ return false
+ }
+ }
+
+ return true
+}
+
+// LargestAcked is the largest acked packet number
+func (f *AckFrame) LargestAcked() protocol.PacketNumber {
+ return f.AckRanges[0].Largest
+}
+
+// LowestAcked is the lowest acked packet number
+func (f *AckFrame) LowestAcked() protocol.PacketNumber {
+ return f.AckRanges[len(f.AckRanges)-1].Smallest
+}
+
+// AcksPacket determines if this ACK frame acks a certain packet number
+func (f *AckFrame) AcksPacket(p protocol.PacketNumber) bool {
+ if p < f.LowestAcked() || p > f.LargestAcked() {
+ return false
+ }
+
+ i := sort.Search(len(f.AckRanges), func(i int) bool {
+ return p >= f.AckRanges[i].Smallest
+ })
+ // i will always be < len(f.AckRanges), since we checked above that p is not bigger than the largest acked
+ return p <= f.AckRanges[i].Largest
+}
+
+func encodeAckDelay(delay time.Duration) uint64 {
+ return uint64(delay.Nanoseconds() / (1000 * (1 << protocol.AckDelayExponent)))
+}
diff --git a/vendor/github.com/quic-go/quic-go/internal/wire/ack_frame_pool.go b/vendor/github.com/quic-go/quic-go/internal/wire/ack_frame_pool.go
new file mode 100644
index 0000000000..a0c6a21d7d
--- /dev/null
+++ b/vendor/github.com/quic-go/quic-go/internal/wire/ack_frame_pool.go
@@ -0,0 +1,24 @@
+package wire
+
+import "sync"
+
+var ackFramePool = sync.Pool{New: func() any {
+ return &AckFrame{}
+}}
+
+func GetAckFrame() *AckFrame {
+ f := ackFramePool.Get().(*AckFrame)
+ f.AckRanges = f.AckRanges[:0]
+ f.ECNCE = 0
+ f.ECT0 = 0
+ f.ECT1 = 0
+ f.DelayTime = 0
+ return f
+}
+
+func PutAckFrame(f *AckFrame) {
+ if cap(f.AckRanges) > 4 {
+ return
+ }
+ ackFramePool.Put(f)
+}
diff --git a/vendor/github.com/quic-go/quic-go/internal/wire/ack_range.go b/vendor/github.com/quic-go/quic-go/internal/wire/ack_range.go
new file mode 100644
index 0000000000..03a1235ee0
--- /dev/null
+++ b/vendor/github.com/quic-go/quic-go/internal/wire/ack_range.go
@@ -0,0 +1,14 @@
+package wire
+
+import "github.com/quic-go/quic-go/internal/protocol"
+
+// AckRange is an ACK range
+type AckRange struct {
+ Smallest protocol.PacketNumber
+ Largest protocol.PacketNumber
+}
+
+// Len returns the number of packets contained in this ACK range
+func (r AckRange) Len() protocol.PacketNumber {
+ return r.Largest - r.Smallest + 1
+}
diff --git a/vendor/github.com/quic-go/quic-go/internal/wire/connection_close_frame.go b/vendor/github.com/quic-go/quic-go/internal/wire/connection_close_frame.go
new file mode 100644
index 0000000000..de2283b3b1
--- /dev/null
+++ b/vendor/github.com/quic-go/quic-go/internal/wire/connection_close_frame.go
@@ -0,0 +1,83 @@
+package wire
+
+import (
+ "bytes"
+ "io"
+
+ "github.com/quic-go/quic-go/internal/protocol"
+ "github.com/quic-go/quic-go/quicvarint"
+)
+
+// A ConnectionCloseFrame is a CONNECTION_CLOSE frame
+type ConnectionCloseFrame struct {
+ IsApplicationError bool
+ ErrorCode uint64
+ FrameType uint64
+ ReasonPhrase string
+}
+
+func parseConnectionCloseFrame(r *bytes.Reader, _ protocol.VersionNumber) (*ConnectionCloseFrame, error) {
+ typeByte, err := r.ReadByte()
+ if err != nil {
+ return nil, err
+ }
+
+ f := &ConnectionCloseFrame{IsApplicationError: typeByte == 0x1d}
+ ec, err := quicvarint.Read(r)
+ if err != nil {
+ return nil, err
+ }
+ f.ErrorCode = ec
+ // read the Frame Type, if this is not an application error
+ if !f.IsApplicationError {
+ ft, err := quicvarint.Read(r)
+ if err != nil {
+ return nil, err
+ }
+ f.FrameType = ft
+ }
+ var reasonPhraseLen uint64
+ reasonPhraseLen, err = quicvarint.Read(r)
+ if err != nil {
+ return nil, err
+ }
+ // shortcut to prevent the unnecessary allocation of dataLen bytes
+ // if the dataLen is larger than the remaining length of the packet
+ // reading the whole reason phrase would result in EOF when attempting to READ
+ if int(reasonPhraseLen) > r.Len() {
+ return nil, io.EOF
+ }
+
+ reasonPhrase := make([]byte, reasonPhraseLen)
+ if _, err := io.ReadFull(r, reasonPhrase); err != nil {
+ // this should never happen, since we already checked the reasonPhraseLen earlier
+ return nil, err
+ }
+ f.ReasonPhrase = string(reasonPhrase)
+ return f, nil
+}
+
+// Length of a written frame
+func (f *ConnectionCloseFrame) Length(protocol.VersionNumber) protocol.ByteCount {
+ length := 1 + quicvarint.Len(f.ErrorCode) + quicvarint.Len(uint64(len(f.ReasonPhrase))) + protocol.ByteCount(len(f.ReasonPhrase))
+ if !f.IsApplicationError {
+ length += quicvarint.Len(f.FrameType) // for the frame type
+ }
+ return length
+}
+
+func (f *ConnectionCloseFrame) Append(b []byte, _ protocol.VersionNumber) ([]byte, error) {
+ if f.IsApplicationError {
+ b = append(b, 0x1d)
+ } else {
+ b = append(b, 0x1c)
+ }
+
+ b = quicvarint.Append(b, f.ErrorCode)
+ if !f.IsApplicationError {
+ b = quicvarint.Append(b, f.FrameType)
+ }
+ b = quicvarint.Append(b, uint64(len(f.ReasonPhrase)))
+ b = append(b, []byte(f.ReasonPhrase)...)
+ return b, nil
+}
diff --git a/vendor/github.com/quic-go/quic-go/internal/wire/crypto_frame.go b/vendor/github.com/quic-go/quic-go/internal/wire/crypto_frame.go
new file mode 100644
index 0000000000..99ffb21d0a
--- /dev/null
+++ b/vendor/github.com/quic-go/quic-go/internal/wire/crypto_frame.go
@@ -0,0 +1,102 @@
+package wire
+
+import (
+ "bytes"
+ "io"
+
+ "github.com/quic-go/quic-go/internal/protocol"
+ "github.com/quic-go/quic-go/quicvarint"
+)
+
+// A CryptoFrame is a CRYPTO frame
+type CryptoFrame struct {
+ Offset protocol.ByteCount
+ Data []byte
+}
+
+func parseCryptoFrame(r *bytes.Reader, _ protocol.VersionNumber) (*CryptoFrame, error) {
+ if _, err := r.ReadByte(); err != nil {
+ return nil, err
+ }
+
+ frame := &CryptoFrame{}
+ offset, err := quicvarint.Read(r)
+ if err != nil {
+ return nil, err
+ }
+ frame.Offset = protocol.ByteCount(offset)
+ dataLen, err := quicvarint.Read(r)
+ if err != nil {
+ return nil, err
+ }
+ if dataLen > uint64(r.Len()) {
+ return nil, io.EOF
+ }
+ if dataLen != 0 {
+ frame.Data = make([]byte, dataLen)
+ if _, err := io.ReadFull(r, frame.Data); err != nil {
+ // this should never happen, since we already checked the dataLen earlier
+ return nil, err
+ }
+ }
+ return frame, nil
+}
+
+func (f *CryptoFrame) Append(b []byte, _ protocol.VersionNumber) ([]byte, error) {
+ b = append(b, 0x6)
+ b = quicvarint.Append(b, uint64(f.Offset))
+ b = quicvarint.Append(b, uint64(len(f.Data)))
+ b = append(b, f.Data...)
+ return b, nil
+}
+
+// Length of a written frame
+func (f *CryptoFrame) Length(_ protocol.VersionNumber) protocol.ByteCount {
+ return 1 + quicvarint.Len(uint64(f.Offset)) + quicvarint.Len(uint64(len(f.Data))) + protocol.ByteCount(len(f.Data))
+}
+
+// MaxDataLen returns the maximum data length
+func (f *CryptoFrame) MaxDataLen(maxSize protocol.ByteCount) protocol.ByteCount {
+ // pretend that the data size will be 1 bytes
+ // if it turns out that varint encoding the length will consume 2 bytes, we need to adjust the data length afterwards
+ headerLen := 1 + quicvarint.Len(uint64(f.Offset)) + 1
+ if headerLen > maxSize {
+ return 0
+ }
+ maxDataLen := maxSize - headerLen
+ if quicvarint.Len(uint64(maxDataLen)) != 1 {
+ maxDataLen--
+ }
+ return maxDataLen
+}
+
+// MaybeSplitOffFrame splits a frame such that it is not bigger than n bytes.
+// It returns if the frame was actually split.
+// The frame might not be split if:
+// * the size is large enough to fit the whole frame
+// * the size is too small to fit even a 1-byte frame. In that case, the frame returned is nil.
+func (f *CryptoFrame) MaybeSplitOffFrame(maxSize protocol.ByteCount, version protocol.VersionNumber) (*CryptoFrame, bool /* was splitting required */) {
+ if f.Length(version) <= maxSize {
+ return nil, false
+ }
+
+ n := f.MaxDataLen(maxSize)
+ if n == 0 {
+ return nil, true
+ }
+
+ newLen := protocol.ByteCount(len(f.Data)) - n
+
+ new := &CryptoFrame{}
+ new.Offset = f.Offset
+ new.Data = make([]byte, newLen)
+
+ // swap the data slices
+ new.Data, f.Data = f.Data, new.Data
+
+ copy(f.Data, new.Data[n:])
+ new.Data = new.Data[:n]
+ f.Offset += n
+
+ return new, true
+}
diff --git a/vendor/github.com/quic-go/quic-go/internal/wire/data_blocked_frame.go b/vendor/github.com/quic-go/quic-go/internal/wire/data_blocked_frame.go
new file mode 100644
index 0000000000..b567af8a4b
--- /dev/null
+++ b/vendor/github.com/quic-go/quic-go/internal/wire/data_blocked_frame.go
@@ -0,0 +1,37 @@
+package wire
+
+import (
+ "bytes"
+
+ "github.com/quic-go/quic-go/internal/protocol"
+ "github.com/quic-go/quic-go/quicvarint"
+)
+
+// A DataBlockedFrame is a DATA_BLOCKED frame
+type DataBlockedFrame struct {
+ MaximumData protocol.ByteCount
+}
+
+func parseDataBlockedFrame(r *bytes.Reader, _ protocol.VersionNumber) (*DataBlockedFrame, error) {
+ if _, err := r.ReadByte(); err != nil {
+ return nil, err
+ }
+ offset, err := quicvarint.Read(r)
+ if err != nil {
+ return nil, err
+ }
+ return &DataBlockedFrame{
+ MaximumData: protocol.ByteCount(offset),
+ }, nil
+}
+
+func (f *DataBlockedFrame) Append(b []byte, version protocol.VersionNumber) ([]byte, error) {
+ b = append(b, 0x14)
+ b = quicvarint.Append(b, uint64(f.MaximumData))
+ return b, nil
+}
+
+// Length of a written frame
+func (f *DataBlockedFrame) Length(version protocol.VersionNumber) protocol.ByteCount {
+ return 1 + quicvarint.Len(uint64(f.MaximumData))
+}
diff --git a/vendor/github.com/quic-go/quic-go/internal/wire/datagram_frame.go b/vendor/github.com/quic-go/quic-go/internal/wire/datagram_frame.go
new file mode 100644
index 0000000000..756a23ffdc
--- /dev/null
+++ b/vendor/github.com/quic-go/quic-go/internal/wire/datagram_frame.go
@@ -0,0 +1,85 @@
+package wire
+
+import (
+ "bytes"
+ "io"
+
+ "github.com/quic-go/quic-go/internal/protocol"
+ "github.com/quic-go/quic-go/quicvarint"
+)
+
+// A DatagramFrame is a DATAGRAM frame
+type DatagramFrame struct {
+ DataLenPresent bool
+ Data []byte
+}
+
+func parseDatagramFrame(r *bytes.Reader, _ protocol.VersionNumber) (*DatagramFrame, error) {
+ typeByte, err := r.ReadByte()
+ if err != nil {
+ return nil, err
+ }
+
+ f := &DatagramFrame{}
+ f.DataLenPresent = typeByte&0x1 > 0
+
+ var length uint64
+ if f.DataLenPresent {
+ var err error
+ len, err := quicvarint.Read(r)
+ if err != nil {
+ return nil, err
+ }
+ if len > uint64(r.Len()) {
+ return nil, io.EOF
+ }
+ length = len
+ } else {
+ length = uint64(r.Len())
+ }
+ f.Data = make([]byte, length)
+ if _, err := io.ReadFull(r, f.Data); err != nil {
+ return nil, err
+ }
+ return f, nil
+}
+
+func (f *DatagramFrame) Append(b []byte, _ protocol.VersionNumber) ([]byte, error) {
+ typeByte := uint8(0x30)
+ if f.DataLenPresent {
+ typeByte ^= 0b1
+ }
+ b = append(b, typeByte)
+ if f.DataLenPresent {
+ b = quicvarint.Append(b, uint64(len(f.Data)))
+ }
+ b = append(b, f.Data...)
+ return b, nil
+}
+
+// MaxDataLen returns the maximum data length
+func (f *DatagramFrame) MaxDataLen(maxSize protocol.ByteCount, version protocol.VersionNumber) protocol.ByteCount {
+ headerLen := protocol.ByteCount(1)
+ if f.DataLenPresent {
+ // pretend that the data size will be 1 bytes
+ // if it turns out that varint encoding the length will consume 2 bytes, we need to adjust the data length afterwards
+ headerLen++
+ }
+ if headerLen > maxSize {
+ return 0
+ }
+ maxDataLen := maxSize - headerLen
+ if f.DataLenPresent && quicvarint.Len(uint64(maxDataLen)) != 1 {
+ maxDataLen--
+ }
+ return maxDataLen
+}
+
+// Length of a written frame
+func (f *DatagramFrame) Length(_ protocol.VersionNumber) protocol.ByteCount {
+ length := 1 + protocol.ByteCount(len(f.Data))
+ if f.DataLenPresent {
+ length += quicvarint.Len(uint64(len(f.Data)))
+ }
+ return length
+}
diff --git a/vendor/github.com/quic-go/quic-go/internal/wire/extended_header.go b/vendor/github.com/quic-go/quic-go/internal/wire/extended_header.go
new file mode 100644
index 0000000000..d10820d6d9
--- /dev/null
+++ b/vendor/github.com/quic-go/quic-go/internal/wire/extended_header.go
@@ -0,0 +1,210 @@
+package wire
+
+import (
+ "bytes"
+ "encoding/binary"
+ "errors"
+ "fmt"
+ "io"
+
+ "github.com/quic-go/quic-go/internal/protocol"
+ "github.com/quic-go/quic-go/internal/utils"
+ "github.com/quic-go/quic-go/quicvarint"
+)
+
+// ErrInvalidReservedBits is returned when the reserved bits are incorrect.
+// When this error is returned, parsing continues, and an ExtendedHeader is returned.
+// This is necessary because we need to decrypt the packet in that case,
+// in order to avoid a timing side-channel.
+var ErrInvalidReservedBits = errors.New("invalid reserved bits")
+
+// ExtendedHeader is the header of a QUIC packet.
+type ExtendedHeader struct {
+ Header
+
+ typeByte byte
+
+ KeyPhase protocol.KeyPhaseBit
+
+ PacketNumberLen protocol.PacketNumberLen
+ PacketNumber protocol.PacketNumber
+
+ parsedLen protocol.ByteCount
+}
+
+func (h *ExtendedHeader) parse(b *bytes.Reader, v protocol.VersionNumber) (bool /* reserved bits valid */, error) {
+ startLen := b.Len()
+ // read the (now unencrypted) first byte
+ var err error
+ h.typeByte, err = b.ReadByte()
+ if err != nil {
+ return false, err
+ }
+ if _, err := b.Seek(int64(h.Header.ParsedLen())-1, io.SeekCurrent); err != nil {
+ return false, err
+ }
+ reservedBitsValid, err := h.parseLongHeader(b, v)
+ if err != nil {
+ return false, err
+ }
+ h.parsedLen = protocol.ByteCount(startLen - b.Len())
+ return reservedBitsValid, err
+}
+
+func (h *ExtendedHeader) parseLongHeader(b *bytes.Reader, _ protocol.VersionNumber) (bool /* reserved bits valid */, error) {
+ if err := h.readPacketNumber(b); err != nil {
+ return false, err
+ }
+ if h.typeByte&0xc != 0 {
+ return false, nil
+ }
+ return true, nil
+}
+
+func (h *ExtendedHeader) readPacketNumber(b *bytes.Reader) error {
+ h.PacketNumberLen = protocol.PacketNumberLen(h.typeByte&0x3) + 1
+ switch h.PacketNumberLen {
+ case protocol.PacketNumberLen1:
+ n, err := b.ReadByte()
+ if err != nil {
+ return err
+ }
+ h.PacketNumber = protocol.PacketNumber(n)
+ case protocol.PacketNumberLen2:
+ n, err := utils.BigEndian.ReadUint16(b)
+ if err != nil {
+ return err
+ }
+ h.PacketNumber = protocol.PacketNumber(n)
+ case protocol.PacketNumberLen3:
+ n, err := utils.BigEndian.ReadUint24(b)
+ if err != nil {
+ return err
+ }
+ h.PacketNumber = protocol.PacketNumber(n)
+ case protocol.PacketNumberLen4:
+ n, err := utils.BigEndian.ReadUint32(b)
+ if err != nil {
+ return err
+ }
+ h.PacketNumber = protocol.PacketNumber(n)
+ default:
+ return fmt.Errorf("invalid packet number length: %d", h.PacketNumberLen)
+ }
+ return nil
+}
+
+// Append appends the Header.
+func (h *ExtendedHeader) Append(b []byte, v protocol.VersionNumber) ([]byte, error) {
+ if h.DestConnectionID.Len() > protocol.MaxConnIDLen {
+ return nil, fmt.Errorf("invalid connection ID length: %d bytes", h.DestConnectionID.Len())
+ }
+ if h.SrcConnectionID.Len() > protocol.MaxConnIDLen {
+ return nil, fmt.Errorf("invalid connection ID length: %d bytes", h.SrcConnectionID.Len())
+ }
+
+ var packetType uint8
+ if v == protocol.Version2 {
+ //nolint:exhaustive
+ switch h.Type {
+ case protocol.PacketTypeInitial:
+ packetType = 0b01
+ case protocol.PacketType0RTT:
+ packetType = 0b10
+ case protocol.PacketTypeHandshake:
+ packetType = 0b11
+ case protocol.PacketTypeRetry:
+ packetType = 0b00
+ }
+ } else {
+ //nolint:exhaustive
+ switch h.Type {
+ case protocol.PacketTypeInitial:
+ packetType = 0b00
+ case protocol.PacketType0RTT:
+ packetType = 0b01
+ case protocol.PacketTypeHandshake:
+ packetType = 0b10
+ case protocol.PacketTypeRetry:
+ packetType = 0b11
+ }
+ }
+ firstByte := 0xc0 | packetType<<4
+ if h.Type != protocol.PacketTypeRetry {
+ // Retry packets don't have a packet number
+ firstByte |= uint8(h.PacketNumberLen - 1)
+ }
+
+ b = append(b, firstByte)
+ b = append(b, make([]byte, 4)...)
+ binary.BigEndian.PutUint32(b[len(b)-4:], uint32(h.Version))
+ b = append(b, uint8(h.DestConnectionID.Len()))
+ b = append(b, h.DestConnectionID.Bytes()...)
+ b = append(b, uint8(h.SrcConnectionID.Len()))
+ b = append(b, h.SrcConnectionID.Bytes()...)
+
+ //nolint:exhaustive
+ switch h.Type {
+ case protocol.PacketTypeRetry:
+ b = append(b, h.Token...)
+ return b, nil
+ case protocol.PacketTypeInitial:
+ b = quicvarint.Append(b, uint64(len(h.Token)))
+ b = append(b, h.Token...)
+ }
+ b = quicvarint.AppendWithLen(b, uint64(h.Length), 2)
+ return appendPacketNumber(b, h.PacketNumber, h.PacketNumberLen)
+}
+
+// ParsedLen returns the number of bytes that were consumed when parsing the header
+func (h *ExtendedHeader) ParsedLen() protocol.ByteCount {
+ return h.parsedLen
+}
+
+// GetLength determines the length of the Header.
+func (h *ExtendedHeader) GetLength(_ protocol.VersionNumber) protocol.ByteCount {
+ length := 1 /* type byte */ + 4 /* version */ + 1 /* dest conn ID len */ + protocol.ByteCount(h.DestConnectionID.Len()) + 1 /* src conn ID len */ + protocol.ByteCount(h.SrcConnectionID.Len()) + protocol.ByteCount(h.PacketNumberLen) + 2 /* length */
+ if h.Type == protocol.PacketTypeInitial {
+ length += quicvarint.Len(uint64(len(h.Token))) + protocol.ByteCount(len(h.Token))
+ }
+ return length
+}
+
+// Log logs the Header
+func (h *ExtendedHeader) Log(logger utils.Logger) {
+ var token string
+ if h.Type == protocol.PacketTypeInitial || h.Type == protocol.PacketTypeRetry {
+ if len(h.Token) == 0 {
+ token = "Token: (empty), "
+ } else {
+ token = fmt.Sprintf("Token: %#x, ", h.Token)
+ }
+ if h.Type == protocol.PacketTypeRetry {
+ logger.Debugf("\tLong Header{Type: %s, DestConnectionID: %s, SrcConnectionID: %s, %sVersion: %s}", h.Type, h.DestConnectionID, h.SrcConnectionID, token, h.Version)
+ return
+ }
+ }
+ logger.Debugf("\tLong Header{Type: %s, DestConnectionID: %s, SrcConnectionID: %s, %sPacketNumber: %d, PacketNumberLen: %d, Length: %d, Version: %s}", h.Type, h.DestConnectionID, h.SrcConnectionID, token, h.PacketNumber, h.PacketNumberLen, h.Length, h.Version)
+}
+
+func appendPacketNumber(b []byte, pn protocol.PacketNumber, pnLen protocol.PacketNumberLen) ([]byte, error) {
+ switch pnLen {
+ case protocol.PacketNumberLen1:
+ b = append(b, uint8(pn))
+ case protocol.PacketNumberLen2:
+ buf := make([]byte, 2)
+ binary.BigEndian.PutUint16(buf, uint16(pn))
+ b = append(b, buf...)
+ case protocol.PacketNumberLen3:
+ buf := make([]byte, 4)
+ binary.BigEndian.PutUint32(buf, uint32(pn))
+ b = append(b, buf[1:]...)
+ case protocol.PacketNumberLen4:
+ buf := make([]byte, 4)
+ binary.BigEndian.PutUint32(buf, uint32(pn))
+ b = append(b, buf...)
+ default:
+ return nil, fmt.Errorf("invalid packet number length: %d", pnLen)
+ }
+ return b, nil
+}
diff --git a/vendor/github.com/quic-go/quic-go/internal/wire/frame_parser.go b/vendor/github.com/quic-go/quic-go/internal/wire/frame_parser.go
new file mode 100644
index 0000000000..ec744d903b
--- /dev/null
+++ b/vendor/github.com/quic-go/quic-go/internal/wire/frame_parser.go
@@ -0,0 +1,154 @@
+package wire
+
+import (
+ "bytes"
+ "errors"
+ "fmt"
+ "reflect"
+
+ "github.com/quic-go/quic-go/internal/protocol"
+ "github.com/quic-go/quic-go/internal/qerr"
+)
+
+type frameParser struct {
+ r bytes.Reader // cached bytes.Reader, so we don't have to repeatedly allocate them
+
+ ackDelayExponent uint8
+
+ supportsDatagrams bool
+}
+
+var _ FrameParser = &frameParser{}
+
+// NewFrameParser creates a new frame parser.
+func NewFrameParser(supportsDatagrams bool) *frameParser {
+ return &frameParser{
+ r: *bytes.NewReader(nil),
+ supportsDatagrams: supportsDatagrams,
+ }
+}
+
+// ParseNext parses the next frame.
+// It skips PADDING frames.
+func (p *frameParser) ParseNext(data []byte, encLevel protocol.EncryptionLevel, v protocol.VersionNumber) (int, Frame, error) {
+ startLen := len(data)
+ p.r.Reset(data)
+ frame, err := p.parseNext(&p.r, encLevel, v)
+ n := startLen - p.r.Len()
+ p.r.Reset(nil)
+ return n, frame, err
+}
+
+func (p *frameParser) parseNext(r *bytes.Reader, encLevel protocol.EncryptionLevel, v protocol.VersionNumber) (Frame, error) {
+ for r.Len() != 0 {
+ typeByte, _ := p.r.ReadByte()
+ if typeByte == 0x0 { // PADDING frame
+ continue
+ }
+ r.UnreadByte()
+
+ f, err := p.parseFrame(r, typeByte, encLevel, v)
+ if err != nil {
+ return nil, &qerr.TransportError{
+ FrameType: uint64(typeByte),
+ ErrorCode: qerr.FrameEncodingError,
+ ErrorMessage: err.Error(),
+ }
+ }
+ return f, nil
+ }
+ return nil, nil
+}
+
+func (p *frameParser) parseFrame(r *bytes.Reader, typeByte byte, encLevel protocol.EncryptionLevel, v protocol.VersionNumber) (Frame, error) {
+ var frame Frame
+ var err error
+ if typeByte&0xf8 == 0x8 {
+ frame, err = parseStreamFrame(r, v)
+ } else {
+ switch typeByte {
+ case 0x1:
+ frame, err = parsePingFrame(r, v)
+ case 0x2, 0x3:
+ ackDelayExponent := p.ackDelayExponent
+ if encLevel != protocol.Encryption1RTT {
+ ackDelayExponent = protocol.DefaultAckDelayExponent
+ }
+ frame, err = parseAckFrame(r, ackDelayExponent, v)
+ case 0x4:
+ frame, err = parseResetStreamFrame(r, v)
+ case 0x5:
+ frame, err = parseStopSendingFrame(r, v)
+ case 0x6:
+ frame, err = parseCryptoFrame(r, v)
+ case 0x7:
+ frame, err = parseNewTokenFrame(r, v)
+ case 0x10:
+ frame, err = parseMaxDataFrame(r, v)
+ case 0x11:
+ frame, err = parseMaxStreamDataFrame(r, v)
+ case 0x12, 0x13:
+ frame, err = parseMaxStreamsFrame(r, v)
+ case 0x14:
+ frame, err = parseDataBlockedFrame(r, v)
+ case 0x15:
+ frame, err = parseStreamDataBlockedFrame(r, v)
+ case 0x16, 0x17:
+ frame, err = parseStreamsBlockedFrame(r, v)
+ case 0x18:
+ frame, err = parseNewConnectionIDFrame(r, v)
+ case 0x19:
+ frame, err = parseRetireConnectionIDFrame(r, v)
+ case 0x1a:
+ frame, err = parsePathChallengeFrame(r, v)
+ case 0x1b:
+ frame, err = parsePathResponseFrame(r, v)
+ case 0x1c, 0x1d:
+ frame, err = parseConnectionCloseFrame(r, v)
+ case 0x1e:
+ frame, err = parseHandshakeDoneFrame(r, v)
+ case 0x30, 0x31:
+ if p.supportsDatagrams {
+ frame, err = parseDatagramFrame(r, v)
+ break
+ }
+ fallthrough
+ default:
+ err = errors.New("unknown frame type")
+ }
+ }
+ if err != nil {
+ return nil, err
+ }
+ if !p.isAllowedAtEncLevel(frame, encLevel) {
+ return nil, fmt.Errorf("%s not allowed at encryption level %s", reflect.TypeOf(frame).Elem().Name(), encLevel)
+ }
+ return frame, nil
+}
+
+func (p *frameParser) isAllowedAtEncLevel(f Frame, encLevel protocol.EncryptionLevel) bool {
+ switch encLevel {
+ case protocol.EncryptionInitial, protocol.EncryptionHandshake:
+ switch f.(type) {
+ case *CryptoFrame, *AckFrame, *ConnectionCloseFrame, *PingFrame:
+ return true
+ default:
+ return false
+ }
+ case protocol.Encryption0RTT:
+ switch f.(type) {
+ case *CryptoFrame, *AckFrame, *ConnectionCloseFrame, *NewTokenFrame, *PathResponseFrame, *RetireConnectionIDFrame:
+ return false
+ default:
+ return true
+ }
+ case protocol.Encryption1RTT:
+ return true
+ default:
+ panic("unknown encryption level")
+ }
+}
+
+func (p *frameParser) SetAckDelayExponent(exp uint8) {
+ p.ackDelayExponent = exp
+}
diff --git a/vendor/github.com/quic-go/quic-go/internal/wire/handshake_done_frame.go b/vendor/github.com/quic-go/quic-go/internal/wire/handshake_done_frame.go
new file mode 100644
index 0000000000..7bbc0e8887
--- /dev/null
+++ b/vendor/github.com/quic-go/quic-go/internal/wire/handshake_done_frame.go
@@ -0,0 +1,27 @@
+package wire
+
+import (
+ "bytes"
+
+ "github.com/quic-go/quic-go/internal/protocol"
+)
+
+// A HandshakeDoneFrame is a HANDSHAKE_DONE frame
+type HandshakeDoneFrame struct{}
+
+// ParseHandshakeDoneFrame parses a HandshakeDone frame
+func parseHandshakeDoneFrame(r *bytes.Reader, _ protocol.VersionNumber) (*HandshakeDoneFrame, error) {
+ if _, err := r.ReadByte(); err != nil {
+ return nil, err
+ }
+ return &HandshakeDoneFrame{}, nil
+}
+
+func (f *HandshakeDoneFrame) Append(b []byte, _ protocol.VersionNumber) ([]byte, error) {
+ return append(b, 0x1e), nil
+}
+
+// Length of a written frame
+func (f *HandshakeDoneFrame) Length(_ protocol.VersionNumber) protocol.ByteCount {
+ return 1
+}
diff --git a/vendor/github.com/quic-go/quic-go/internal/wire/header.go b/vendor/github.com/quic-go/quic-go/internal/wire/header.go
new file mode 100644
index 0000000000..4d3c5049a6
--- /dev/null
+++ b/vendor/github.com/quic-go/quic-go/internal/wire/header.go
@@ -0,0 +1,296 @@
+package wire
+
+import (
+ "bytes"
+ "encoding/binary"
+ "errors"
+ "fmt"
+ "io"
+
+ "github.com/quic-go/quic-go/internal/protocol"
+ "github.com/quic-go/quic-go/internal/utils"
+ "github.com/quic-go/quic-go/quicvarint"
+)
+
+// ParseConnectionID parses the destination connection ID of a packet.
+// It uses the data slice for the connection ID.
+// That means that the connection ID must not be used after the packet buffer is released.
+func ParseConnectionID(data []byte, shortHeaderConnIDLen int) (protocol.ConnectionID, error) {
+ if len(data) == 0 {
+ return protocol.ConnectionID{}, io.EOF
+ }
+ if !IsLongHeaderPacket(data[0]) {
+ if len(data) < shortHeaderConnIDLen+1 {
+ return protocol.ConnectionID{}, io.EOF
+ }
+ return protocol.ParseConnectionID(data[1 : 1+shortHeaderConnIDLen]), nil
+ }
+ if len(data) < 6 {
+ return protocol.ConnectionID{}, io.EOF
+ }
+ destConnIDLen := int(data[5])
+ if destConnIDLen > protocol.MaxConnIDLen {
+ return protocol.ConnectionID{}, protocol.ErrInvalidConnectionIDLen
+ }
+ if len(data) < 6+destConnIDLen {
+ return protocol.ConnectionID{}, io.EOF
+ }
+ return protocol.ParseConnectionID(data[6 : 6+destConnIDLen]), nil
+}
+
+// ParseArbitraryLenConnectionIDs parses the most general form of a Long Header packet,
+// using only the version-independent packet format as described in Section 5.1 of RFC 8999:
+// https://datatracker.ietf.org/doc/html/rfc8999#section-5.1.
+// This function should only be called on Long Header packets for which we don't support the version.
+func ParseArbitraryLenConnectionIDs(data []byte) (bytesParsed int, dest, src protocol.ArbitraryLenConnectionID, _ error) {
+ r := bytes.NewReader(data)
+ remaining := r.Len()
+ src, dest, err := parseArbitraryLenConnectionIDs(r)
+ return remaining - r.Len(), src, dest, err
+}
+
+func parseArbitraryLenConnectionIDs(r *bytes.Reader) (dest, src protocol.ArbitraryLenConnectionID, _ error) {
+ r.Seek(5, io.SeekStart) // skip first byte and version field
+ destConnIDLen, err := r.ReadByte()
+ if err != nil {
+ return nil, nil, err
+ }
+ destConnID := make(protocol.ArbitraryLenConnectionID, destConnIDLen)
+ if _, err := io.ReadFull(r, destConnID); err != nil {
+ if err == io.ErrUnexpectedEOF {
+ err = io.EOF
+ }
+ return nil, nil, err
+ }
+ srcConnIDLen, err := r.ReadByte()
+ if err != nil {
+ return nil, nil, err
+ }
+ srcConnID := make(protocol.ArbitraryLenConnectionID, srcConnIDLen)
+ if _, err := io.ReadFull(r, srcConnID); err != nil {
+ if err == io.ErrUnexpectedEOF {
+ err = io.EOF
+ }
+ return nil, nil, err
+ }
+ return destConnID, srcConnID, nil
+}
+
+// IsLongHeaderPacket says if this is a Long Header packet
+func IsLongHeaderPacket(firstByte byte) bool {
+ return firstByte&0x80 > 0
+}
+
+// ParseVersion parses the QUIC version.
+// It should only be called for Long Header packets (Short Header packets don't contain a version number).
+func ParseVersion(data []byte) (protocol.VersionNumber, error) {
+ if len(data) < 5 {
+ return 0, io.EOF
+ }
+ return protocol.VersionNumber(binary.BigEndian.Uint32(data[1:5])), nil
+}
+
+// IsVersionNegotiationPacket says if this is a version negotiation packet
+func IsVersionNegotiationPacket(b []byte) bool {
+ if len(b) < 5 {
+ return false
+ }
+ return IsLongHeaderPacket(b[0]) && b[1] == 0 && b[2] == 0 && b[3] == 0 && b[4] == 0
+}
+
+// Is0RTTPacket says if this is a 0-RTT packet.
+// A packet sent with a version we don't understand can never be a 0-RTT packet.
+func Is0RTTPacket(b []byte) bool {
+ if len(b) < 5 {
+ return false
+ }
+ if !IsLongHeaderPacket(b[0]) {
+ return false
+ }
+ version := protocol.VersionNumber(binary.BigEndian.Uint32(b[1:5]))
+ if !protocol.IsSupportedVersion(protocol.SupportedVersions, version) {
+ return false
+ }
+ if version == protocol.Version2 {
+ return b[0]>>4&0b11 == 0b10
+ }
+ return b[0]>>4&0b11 == 0b01
+}
+
+var ErrUnsupportedVersion = errors.New("unsupported version")
+
+// The Header is the version independent part of the header
+type Header struct {
+ typeByte byte
+ Type protocol.PacketType
+
+ Version protocol.VersionNumber
+ SrcConnectionID protocol.ConnectionID
+ DestConnectionID protocol.ConnectionID
+
+ Length protocol.ByteCount
+
+ Token []byte
+
+ parsedLen protocol.ByteCount // how many bytes were read while parsing this header
+}
+
+// ParsePacket parses a packet.
+// If the packet has a long header, the packet is cut according to the length field.
+// If we understand the version, the packet is header up unto the packet number.
+// Otherwise, only the invariant part of the header is parsed.
+func ParsePacket(data []byte) (*Header, []byte, []byte, error) {
+ if len(data) == 0 || !IsLongHeaderPacket(data[0]) {
+ return nil, nil, nil, errors.New("not a long header packet")
+ }
+ hdr, err := parseHeader(bytes.NewReader(data))
+ if err != nil {
+ if err == ErrUnsupportedVersion {
+ return hdr, nil, nil, ErrUnsupportedVersion
+ }
+ return nil, nil, nil, err
+ }
+ if protocol.ByteCount(len(data)) < hdr.ParsedLen()+hdr.Length {
+ return nil, nil, nil, fmt.Errorf("packet length (%d bytes) is smaller than the expected length (%d bytes)", len(data)-int(hdr.ParsedLen()), hdr.Length)
+ }
+ packetLen := int(hdr.ParsedLen() + hdr.Length)
+ return hdr, data[:packetLen], data[packetLen:], nil
+}
+
+// ParseHeader parses the header.
+// For short header packets: up to the packet number.
+// For long header packets:
+// * if we understand the version: up to the packet number
+// * if not, only the invariant part of the header
+func parseHeader(b *bytes.Reader) (*Header, error) {
+ startLen := b.Len()
+ typeByte, err := b.ReadByte()
+ if err != nil {
+ return nil, err
+ }
+
+ h := &Header{typeByte: typeByte}
+ err = h.parseLongHeader(b)
+ h.parsedLen = protocol.ByteCount(startLen - b.Len())
+ return h, err
+}
+
+func (h *Header) parseLongHeader(b *bytes.Reader) error {
+ v, err := utils.BigEndian.ReadUint32(b)
+ if err != nil {
+ return err
+ }
+ h.Version = protocol.VersionNumber(v)
+ if h.Version != 0 && h.typeByte&0x40 == 0 {
+ return errors.New("not a QUIC packet")
+ }
+ destConnIDLen, err := b.ReadByte()
+ if err != nil {
+ return err
+ }
+ h.DestConnectionID, err = protocol.ReadConnectionID(b, int(destConnIDLen))
+ if err != nil {
+ return err
+ }
+ srcConnIDLen, err := b.ReadByte()
+ if err != nil {
+ return err
+ }
+ h.SrcConnectionID, err = protocol.ReadConnectionID(b, int(srcConnIDLen))
+ if err != nil {
+ return err
+ }
+ if h.Version == 0 { // version negotiation packet
+ return nil
+ }
+ // If we don't understand the version, we have no idea how to interpret the rest of the bytes
+ if !protocol.IsSupportedVersion(protocol.SupportedVersions, h.Version) {
+ return ErrUnsupportedVersion
+ }
+
+ if h.Version == protocol.Version2 {
+ switch h.typeByte >> 4 & 0b11 {
+ case 0b00:
+ h.Type = protocol.PacketTypeRetry
+ case 0b01:
+ h.Type = protocol.PacketTypeInitial
+ case 0b10:
+ h.Type = protocol.PacketType0RTT
+ case 0b11:
+ h.Type = protocol.PacketTypeHandshake
+ }
+ } else {
+ switch h.typeByte >> 4 & 0b11 {
+ case 0b00:
+ h.Type = protocol.PacketTypeInitial
+ case 0b01:
+ h.Type = protocol.PacketType0RTT
+ case 0b10:
+ h.Type = protocol.PacketTypeHandshake
+ case 0b11:
+ h.Type = protocol.PacketTypeRetry
+ }
+ }
+
+ if h.Type == protocol.PacketTypeRetry {
+ tokenLen := b.Len() - 16
+ if tokenLen <= 0 {
+ return io.EOF
+ }
+ h.Token = make([]byte, tokenLen)
+ if _, err := io.ReadFull(b, h.Token); err != nil {
+ return err
+ }
+ _, err := b.Seek(16, io.SeekCurrent)
+ return err
+ }
+
+ if h.Type == protocol.PacketTypeInitial {
+ tokenLen, err := quicvarint.Read(b)
+ if err != nil {
+ return err
+ }
+ if tokenLen > uint64(b.Len()) {
+ return io.EOF
+ }
+ h.Token = make([]byte, tokenLen)
+ if _, err := io.ReadFull(b, h.Token); err != nil {
+ return err
+ }
+ }
+
+ pl, err := quicvarint.Read(b)
+ if err != nil {
+ return err
+ }
+ h.Length = protocol.ByteCount(pl)
+ return nil
+}
+
+// ParsedLen returns the number of bytes that were consumed when parsing the header
+func (h *Header) ParsedLen() protocol.ByteCount {
+ return h.parsedLen
+}
+
+// ParseExtended parses the version dependent part of the header.
+// The Reader has to be set such that it points to the first byte of the header.
+func (h *Header) ParseExtended(b *bytes.Reader, ver protocol.VersionNumber) (*ExtendedHeader, error) {
+ extHdr := h.toExtendedHeader()
+ reservedBitsValid, err := extHdr.parse(b, ver)
+ if err != nil {
+ return nil, err
+ }
+ if !reservedBitsValid {
+ return extHdr, ErrInvalidReservedBits
+ }
+ return extHdr, nil
+}
+
+func (h *Header) toExtendedHeader() *ExtendedHeader {
+ return &ExtendedHeader{Header: *h}
+}
+
+// PacketType is the type of the packet, for logging purposes
+func (h *Header) PacketType() string {
+ return h.Type.String()
+}
diff --git a/vendor/github.com/quic-go/quic-go/internal/wire/interface.go b/vendor/github.com/quic-go/quic-go/internal/wire/interface.go
new file mode 100644
index 0000000000..7e0f9a03e0
--- /dev/null
+++ b/vendor/github.com/quic-go/quic-go/internal/wire/interface.go
@@ -0,0 +1,17 @@
+package wire
+
+import (
+ "github.com/quic-go/quic-go/internal/protocol"
+)
+
+// A Frame in QUIC
+type Frame interface {
+ Append(b []byte, version protocol.VersionNumber) ([]byte, error)
+ Length(version protocol.VersionNumber) protocol.ByteCount
+}
+
+// A FrameParser parses QUIC frames, one by one.
+type FrameParser interface {
+ ParseNext([]byte, protocol.EncryptionLevel, protocol.VersionNumber) (int, Frame, error)
+ SetAckDelayExponent(uint8)
+}
diff --git a/vendor/github.com/quic-go/quic-go/internal/wire/log.go b/vendor/github.com/quic-go/quic-go/internal/wire/log.go
new file mode 100644
index 0000000000..ec7d45d861
--- /dev/null
+++ b/vendor/github.com/quic-go/quic-go/internal/wire/log.go
@@ -0,0 +1,72 @@
+package wire
+
+import (
+ "fmt"
+ "strings"
+
+ "github.com/quic-go/quic-go/internal/protocol"
+ "github.com/quic-go/quic-go/internal/utils"
+)
+
+// LogFrame logs a frame, either sent or received
+func LogFrame(logger utils.Logger, frame Frame, sent bool) {
+ if !logger.Debug() {
+ return
+ }
+ dir := "<-"
+ if sent {
+ dir = "->"
+ }
+ switch f := frame.(type) {
+ case *CryptoFrame:
+ dataLen := protocol.ByteCount(len(f.Data))
+ logger.Debugf("\t%s &wire.CryptoFrame{Offset: %d, Data length: %d, Offset + Data length: %d}", dir, f.Offset, dataLen, f.Offset+dataLen)
+ case *StreamFrame:
+ logger.Debugf("\t%s &wire.StreamFrame{StreamID: %d, Fin: %t, Offset: %d, Data length: %d, Offset + Data length: %d}", dir, f.StreamID, f.Fin, f.Offset, f.DataLen(), f.Offset+f.DataLen())
+ case *ResetStreamFrame:
+ logger.Debugf("\t%s &wire.ResetStreamFrame{StreamID: %d, ErrorCode: %#x, FinalSize: %d}", dir, f.StreamID, f.ErrorCode, f.FinalSize)
+ case *AckFrame:
+ hasECN := f.ECT0 > 0 || f.ECT1 > 0 || f.ECNCE > 0
+ var ecn string
+ if hasECN {
+ ecn = fmt.Sprintf(", ECT0: %d, ECT1: %d, CE: %d", f.ECT0, f.ECT1, f.ECNCE)
+ }
+ if len(f.AckRanges) > 1 {
+ ackRanges := make([]string, len(f.AckRanges))
+ for i, r := range f.AckRanges {
+ ackRanges[i] = fmt.Sprintf("{Largest: %d, Smallest: %d}", r.Largest, r.Smallest)
+ }
+ logger.Debugf("\t%s &wire.AckFrame{LargestAcked: %d, LowestAcked: %d, AckRanges: {%s}, DelayTime: %s%s}", dir, f.LargestAcked(), f.LowestAcked(), strings.Join(ackRanges, ", "), f.DelayTime.String(), ecn)
+ } else {
+ logger.Debugf("\t%s &wire.AckFrame{LargestAcked: %d, LowestAcked: %d, DelayTime: %s%s}", dir, f.LargestAcked(), f.LowestAcked(), f.DelayTime.String(), ecn)
+ }
+ case *MaxDataFrame:
+ logger.Debugf("\t%s &wire.MaxDataFrame{MaximumData: %d}", dir, f.MaximumData)
+ case *MaxStreamDataFrame:
+ logger.Debugf("\t%s &wire.MaxStreamDataFrame{StreamID: %d, MaximumStreamData: %d}", dir, f.StreamID, f.MaximumStreamData)
+ case *DataBlockedFrame:
+ logger.Debugf("\t%s &wire.DataBlockedFrame{MaximumData: %d}", dir, f.MaximumData)
+ case *StreamDataBlockedFrame:
+ logger.Debugf("\t%s &wire.StreamDataBlockedFrame{StreamID: %d, MaximumStreamData: %d}", dir, f.StreamID, f.MaximumStreamData)
+ case *MaxStreamsFrame:
+ switch f.Type {
+ case protocol.StreamTypeUni:
+ logger.Debugf("\t%s &wire.MaxStreamsFrame{Type: uni, MaxStreamNum: %d}", dir, f.MaxStreamNum)
+ case protocol.StreamTypeBidi:
+ logger.Debugf("\t%s &wire.MaxStreamsFrame{Type: bidi, MaxStreamNum: %d}", dir, f.MaxStreamNum)
+ }
+ case *StreamsBlockedFrame:
+ switch f.Type {
+ case protocol.StreamTypeUni:
+ logger.Debugf("\t%s &wire.StreamsBlockedFrame{Type: uni, MaxStreams: %d}", dir, f.StreamLimit)
+ case protocol.StreamTypeBidi:
+ logger.Debugf("\t%s &wire.StreamsBlockedFrame{Type: bidi, MaxStreams: %d}", dir, f.StreamLimit)
+ }
+ case *NewConnectionIDFrame:
+ logger.Debugf("\t%s &wire.NewConnectionIDFrame{SequenceNumber: %d, ConnectionID: %s, StatelessResetToken: %#x}", dir, f.SequenceNumber, f.ConnectionID, f.StatelessResetToken)
+ case *NewTokenFrame:
+ logger.Debugf("\t%s &wire.NewTokenFrame{Token: %#x}", dir, f.Token)
+ default:
+ logger.Debugf("\t%s %#v", dir, frame)
+ }
+}
diff --git a/vendor/github.com/quic-go/quic-go/internal/wire/max_data_frame.go b/vendor/github.com/quic-go/quic-go/internal/wire/max_data_frame.go
new file mode 100644
index 0000000000..427c811017
--- /dev/null
+++ b/vendor/github.com/quic-go/quic-go/internal/wire/max_data_frame.go
@@ -0,0 +1,40 @@
+package wire
+
+import (
+ "bytes"
+
+ "github.com/quic-go/quic-go/internal/protocol"
+ "github.com/quic-go/quic-go/quicvarint"
+)
+
+// A MaxDataFrame carries flow control information for the connection
+type MaxDataFrame struct {
+ MaximumData protocol.ByteCount
+}
+
+// parseMaxDataFrame parses a MAX_DATA frame
+func parseMaxDataFrame(r *bytes.Reader, _ protocol.VersionNumber) (*MaxDataFrame, error) {
+ if _, err := r.ReadByte(); err != nil {
+ return nil, err
+ }
+
+ frame := &MaxDataFrame{}
+ byteOffset, err := quicvarint.Read(r)
+ if err != nil {
+ return nil, err
+ }
+ frame.MaximumData = protocol.ByteCount(byteOffset)
+ return frame, nil
+}
+
+// Write writes a MAX_STREAM_DATA frame
+func (f *MaxDataFrame) Append(b []byte, _ protocol.VersionNumber) ([]byte, error) {
+ b = append(b, 0x10)
+ b = quicvarint.Append(b, uint64(f.MaximumData))
+ return b, nil
+}
+
+// Length of a written frame
+func (f *MaxDataFrame) Length(_ protocol.VersionNumber) protocol.ByteCount {
+ return 1 + quicvarint.Len(uint64(f.MaximumData))
+}
diff --git a/vendor/github.com/quic-go/quic-go/internal/wire/max_stream_data_frame.go b/vendor/github.com/quic-go/quic-go/internal/wire/max_stream_data_frame.go
new file mode 100644
index 0000000000..4218c09bd4
--- /dev/null
+++ b/vendor/github.com/quic-go/quic-go/internal/wire/max_stream_data_frame.go
@@ -0,0 +1,46 @@
+package wire
+
+import (
+ "bytes"
+
+ "github.com/quic-go/quic-go/internal/protocol"
+ "github.com/quic-go/quic-go/quicvarint"
+)
+
+// A MaxStreamDataFrame is a MAX_STREAM_DATA frame
+type MaxStreamDataFrame struct {
+ StreamID protocol.StreamID
+ MaximumStreamData protocol.ByteCount
+}
+
+func parseMaxStreamDataFrame(r *bytes.Reader, _ protocol.VersionNumber) (*MaxStreamDataFrame, error) {
+ if _, err := r.ReadByte(); err != nil {
+ return nil, err
+ }
+
+ sid, err := quicvarint.Read(r)
+ if err != nil {
+ return nil, err
+ }
+ offset, err := quicvarint.Read(r)
+ if err != nil {
+ return nil, err
+ }
+
+ return &MaxStreamDataFrame{
+ StreamID: protocol.StreamID(sid),
+ MaximumStreamData: protocol.ByteCount(offset),
+ }, nil
+}
+
+func (f *MaxStreamDataFrame) Append(b []byte, version protocol.VersionNumber) ([]byte, error) {
+ b = append(b, 0x11)
+ b = quicvarint.Append(b, uint64(f.StreamID))
+ b = quicvarint.Append(b, uint64(f.MaximumStreamData))
+ return b, nil
+}
+
+// Length of a written frame
+func (f *MaxStreamDataFrame) Length(version protocol.VersionNumber) protocol.ByteCount {
+ return 1 + quicvarint.Len(uint64(f.StreamID)) + quicvarint.Len(uint64(f.MaximumStreamData))
+}
diff --git a/vendor/github.com/quic-go/quic-go/internal/wire/max_streams_frame.go b/vendor/github.com/quic-go/quic-go/internal/wire/max_streams_frame.go
new file mode 100644
index 0000000000..f417127c4e
--- /dev/null
+++ b/vendor/github.com/quic-go/quic-go/internal/wire/max_streams_frame.go
@@ -0,0 +1,55 @@
+package wire
+
+import (
+ "bytes"
+ "fmt"
+
+ "github.com/quic-go/quic-go/internal/protocol"
+ "github.com/quic-go/quic-go/quicvarint"
+)
+
+// A MaxStreamsFrame is a MAX_STREAMS frame
+type MaxStreamsFrame struct {
+ Type protocol.StreamType
+ MaxStreamNum protocol.StreamNum
+}
+
+func parseMaxStreamsFrame(r *bytes.Reader, _ protocol.VersionNumber) (*MaxStreamsFrame, error) {
+ typeByte, err := r.ReadByte()
+ if err != nil {
+ return nil, err
+ }
+
+ f := &MaxStreamsFrame{}
+ switch typeByte {
+ case 0x12:
+ f.Type = protocol.StreamTypeBidi
+ case 0x13:
+ f.Type = protocol.StreamTypeUni
+ }
+ streamID, err := quicvarint.Read(r)
+ if err != nil {
+ return nil, err
+ }
+ f.MaxStreamNum = protocol.StreamNum(streamID)
+ if f.MaxStreamNum > protocol.MaxStreamCount {
+ return nil, fmt.Errorf("%d exceeds the maximum stream count", f.MaxStreamNum)
+ }
+ return f, nil
+}
+
+func (f *MaxStreamsFrame) Append(b []byte, _ protocol.VersionNumber) ([]byte, error) {
+ switch f.Type {
+ case protocol.StreamTypeBidi:
+ b = append(b, 0x12)
+ case protocol.StreamTypeUni:
+ b = append(b, 0x13)
+ }
+ b = quicvarint.Append(b, uint64(f.MaxStreamNum))
+ return b, nil
+}
+
+// Length of a written frame
+func (f *MaxStreamsFrame) Length(protocol.VersionNumber) protocol.ByteCount {
+ return 1 + quicvarint.Len(uint64(f.MaxStreamNum))
+}
diff --git a/vendor/github.com/quic-go/quic-go/internal/wire/new_connection_id_frame.go b/vendor/github.com/quic-go/quic-go/internal/wire/new_connection_id_frame.go
new file mode 100644
index 0000000000..5f6ab99807
--- /dev/null
+++ b/vendor/github.com/quic-go/quic-go/internal/wire/new_connection_id_frame.go
@@ -0,0 +1,77 @@
+package wire
+
+import (
+ "bytes"
+ "fmt"
+ "io"
+
+ "github.com/quic-go/quic-go/internal/protocol"
+ "github.com/quic-go/quic-go/quicvarint"
+)
+
+// A NewConnectionIDFrame is a NEW_CONNECTION_ID frame
+type NewConnectionIDFrame struct {
+ SequenceNumber uint64
+ RetirePriorTo uint64
+ ConnectionID protocol.ConnectionID
+ StatelessResetToken protocol.StatelessResetToken
+}
+
+func parseNewConnectionIDFrame(r *bytes.Reader, _ protocol.VersionNumber) (*NewConnectionIDFrame, error) {
+ if _, err := r.ReadByte(); err != nil {
+ return nil, err
+ }
+
+ seq, err := quicvarint.Read(r)
+ if err != nil {
+ return nil, err
+ }
+ ret, err := quicvarint.Read(r)
+ if err != nil {
+ return nil, err
+ }
+ if ret > seq {
+ //nolint:stylecheck
+ return nil, fmt.Errorf("Retire Prior To value (%d) larger than Sequence Number (%d)", ret, seq)
+ }
+ connIDLen, err := r.ReadByte()
+ if err != nil {
+ return nil, err
+ }
+ connID, err := protocol.ReadConnectionID(r, int(connIDLen))
+ if err != nil {
+ return nil, err
+ }
+ frame := &NewConnectionIDFrame{
+ SequenceNumber: seq,
+ RetirePriorTo: ret,
+ ConnectionID: connID,
+ }
+ if _, err := io.ReadFull(r, frame.StatelessResetToken[:]); err != nil {
+ if err == io.ErrUnexpectedEOF {
+ return nil, io.EOF
+ }
+ return nil, err
+ }
+
+ return frame, nil
+}
+
+func (f *NewConnectionIDFrame) Append(b []byte, _ protocol.VersionNumber) ([]byte, error) {
+ b = append(b, 0x18)
+ b = quicvarint.Append(b, f.SequenceNumber)
+ b = quicvarint.Append(b, f.RetirePriorTo)
+ connIDLen := f.ConnectionID.Len()
+ if connIDLen > protocol.MaxConnIDLen {
+ return nil, fmt.Errorf("invalid connection ID length: %d", connIDLen)
+ }
+ b = append(b, uint8(connIDLen))
+ b = append(b, f.ConnectionID.Bytes()...)
+ b = append(b, f.StatelessResetToken[:]...)
+ return b, nil
+}
+
+// Length of a written frame
+func (f *NewConnectionIDFrame) Length(protocol.VersionNumber) protocol.ByteCount {
+ return 1 + quicvarint.Len(f.SequenceNumber) + quicvarint.Len(f.RetirePriorTo) + 1 /* connection ID length */ + protocol.ByteCount(f.ConnectionID.Len()) + 16
+}
diff --git a/vendor/github.com/quic-go/quic-go/internal/wire/new_token_frame.go b/vendor/github.com/quic-go/quic-go/internal/wire/new_token_frame.go
new file mode 100644
index 0000000000..cc1d581966
--- /dev/null
+++ b/vendor/github.com/quic-go/quic-go/internal/wire/new_token_frame.go
@@ -0,0 +1,48 @@
+package wire
+
+import (
+ "bytes"
+ "errors"
+ "io"
+
+ "github.com/quic-go/quic-go/internal/protocol"
+ "github.com/quic-go/quic-go/quicvarint"
+)
+
+// A NewTokenFrame is a NEW_TOKEN frame
+type NewTokenFrame struct {
+ Token []byte
+}
+
+func parseNewTokenFrame(r *bytes.Reader, _ protocol.VersionNumber) (*NewTokenFrame, error) {
+ if _, err := r.ReadByte(); err != nil {
+ return nil, err
+ }
+ tokenLen, err := quicvarint.Read(r)
+ if err != nil {
+ return nil, err
+ }
+ if uint64(r.Len()) < tokenLen {
+ return nil, io.EOF
+ }
+ if tokenLen == 0 {
+ return nil, errors.New("token must not be empty")
+ }
+ token := make([]byte, int(tokenLen))
+ if _, err := io.ReadFull(r, token); err != nil {
+ return nil, err
+ }
+ return &NewTokenFrame{Token: token}, nil
+}
+
+func (f *NewTokenFrame) Append(b []byte, _ protocol.VersionNumber) ([]byte, error) {
+ b = append(b, 0x7)
+ b = quicvarint.Append(b, uint64(len(f.Token)))
+ b = append(b, f.Token...)
+ return b, nil
+}
+
+// Length of a written frame
+func (f *NewTokenFrame) Length(protocol.VersionNumber) protocol.ByteCount {
+ return 1 + quicvarint.Len(uint64(len(f.Token))) + protocol.ByteCount(len(f.Token))
+}
diff --git a/vendor/github.com/quic-go/quic-go/internal/wire/path_challenge_frame.go b/vendor/github.com/quic-go/quic-go/internal/wire/path_challenge_frame.go
new file mode 100644
index 0000000000..5d32865e24
--- /dev/null
+++ b/vendor/github.com/quic-go/quic-go/internal/wire/path_challenge_frame.go
@@ -0,0 +1,38 @@
+package wire
+
+import (
+ "bytes"
+ "io"
+
+ "github.com/quic-go/quic-go/internal/protocol"
+)
+
+// A PathChallengeFrame is a PATH_CHALLENGE frame
+type PathChallengeFrame struct {
+ Data [8]byte
+}
+
+func parsePathChallengeFrame(r *bytes.Reader, _ protocol.VersionNumber) (*PathChallengeFrame, error) {
+ if _, err := r.ReadByte(); err != nil {
+ return nil, err
+ }
+ frame := &PathChallengeFrame{}
+ if _, err := io.ReadFull(r, frame.Data[:]); err != nil {
+ if err == io.ErrUnexpectedEOF {
+ return nil, io.EOF
+ }
+ return nil, err
+ }
+ return frame, nil
+}
+
+func (f *PathChallengeFrame) Append(b []byte, _ protocol.VersionNumber) ([]byte, error) {
+ b = append(b, 0x1a)
+ b = append(b, f.Data[:]...)
+ return b, nil
+}
+
+// Length of a written frame
+func (f *PathChallengeFrame) Length(_ protocol.VersionNumber) protocol.ByteCount {
+ return 1 + 8
+}
diff --git a/vendor/github.com/quic-go/quic-go/internal/wire/path_response_frame.go b/vendor/github.com/quic-go/quic-go/internal/wire/path_response_frame.go
new file mode 100644
index 0000000000..5c49e12273
--- /dev/null
+++ b/vendor/github.com/quic-go/quic-go/internal/wire/path_response_frame.go
@@ -0,0 +1,38 @@
+package wire
+
+import (
+ "bytes"
+ "io"
+
+ "github.com/quic-go/quic-go/internal/protocol"
+)
+
+// A PathResponseFrame is a PATH_RESPONSE frame
+type PathResponseFrame struct {
+ Data [8]byte
+}
+
+func parsePathResponseFrame(r *bytes.Reader, _ protocol.VersionNumber) (*PathResponseFrame, error) {
+ if _, err := r.ReadByte(); err != nil {
+ return nil, err
+ }
+ frame := &PathResponseFrame{}
+ if _, err := io.ReadFull(r, frame.Data[:]); err != nil {
+ if err == io.ErrUnexpectedEOF {
+ return nil, io.EOF
+ }
+ return nil, err
+ }
+ return frame, nil
+}
+
+func (f *PathResponseFrame) Append(b []byte, _ protocol.VersionNumber) ([]byte, error) {
+ b = append(b, 0x1b)
+ b = append(b, f.Data[:]...)
+ return b, nil
+}
+
+// Length of a written frame
+func (f *PathResponseFrame) Length(_ protocol.VersionNumber) protocol.ByteCount {
+ return 1 + 8
+}
diff --git a/vendor/github.com/quic-go/quic-go/internal/wire/ping_frame.go b/vendor/github.com/quic-go/quic-go/internal/wire/ping_frame.go
new file mode 100644
index 0000000000..ba32d16704
--- /dev/null
+++ b/vendor/github.com/quic-go/quic-go/internal/wire/ping_frame.go
@@ -0,0 +1,26 @@
+package wire
+
+import (
+ "bytes"
+
+ "github.com/quic-go/quic-go/internal/protocol"
+)
+
+// A PingFrame is a PING frame
+type PingFrame struct{}
+
+func parsePingFrame(r *bytes.Reader, _ protocol.VersionNumber) (*PingFrame, error) {
+ if _, err := r.ReadByte(); err != nil {
+ return nil, err
+ }
+ return &PingFrame{}, nil
+}
+
+func (f *PingFrame) Append(b []byte, _ protocol.VersionNumber) ([]byte, error) {
+ return append(b, 0x1), nil
+}
+
+// Length of a written frame
+func (f *PingFrame) Length(_ protocol.VersionNumber) protocol.ByteCount {
+ return 1
+}
diff --git a/vendor/github.com/quic-go/quic-go/internal/wire/pool.go b/vendor/github.com/quic-go/quic-go/internal/wire/pool.go
new file mode 100644
index 0000000000..18ab437937
--- /dev/null
+++ b/vendor/github.com/quic-go/quic-go/internal/wire/pool.go
@@ -0,0 +1,33 @@
+package wire
+
+import (
+ "sync"
+
+ "github.com/quic-go/quic-go/internal/protocol"
+)
+
+var pool sync.Pool
+
+func init() {
+ pool.New = func() interface{} {
+ return &StreamFrame{
+ Data: make([]byte, 0, protocol.MaxPacketBufferSize),
+ fromPool: true,
+ }
+ }
+}
+
+func GetStreamFrame() *StreamFrame {
+ f := pool.Get().(*StreamFrame)
+ return f
+}
+
+func putStreamFrame(f *StreamFrame) {
+ if !f.fromPool {
+ return
+ }
+ if protocol.ByteCount(cap(f.Data)) != protocol.MaxPacketBufferSize {
+ panic("wire.PutStreamFrame called with packet of wrong size!")
+ }
+ pool.Put(f)
+}
diff --git a/vendor/github.com/quic-go/quic-go/internal/wire/reset_stream_frame.go b/vendor/github.com/quic-go/quic-go/internal/wire/reset_stream_frame.go
new file mode 100644
index 0000000000..462138130e
--- /dev/null
+++ b/vendor/github.com/quic-go/quic-go/internal/wire/reset_stream_frame.go
@@ -0,0 +1,58 @@
+package wire
+
+import (
+ "bytes"
+
+ "github.com/quic-go/quic-go/internal/protocol"
+ "github.com/quic-go/quic-go/internal/qerr"
+ "github.com/quic-go/quic-go/quicvarint"
+)
+
+// A ResetStreamFrame is a RESET_STREAM frame in QUIC
+type ResetStreamFrame struct {
+ StreamID protocol.StreamID
+ ErrorCode qerr.StreamErrorCode
+ FinalSize protocol.ByteCount
+}
+
+func parseResetStreamFrame(r *bytes.Reader, _ protocol.VersionNumber) (*ResetStreamFrame, error) {
+ if _, err := r.ReadByte(); err != nil { // read the TypeByte
+ return nil, err
+ }
+
+ var streamID protocol.StreamID
+ var byteOffset protocol.ByteCount
+ sid, err := quicvarint.Read(r)
+ if err != nil {
+ return nil, err
+ }
+ streamID = protocol.StreamID(sid)
+ errorCode, err := quicvarint.Read(r)
+ if err != nil {
+ return nil, err
+ }
+ bo, err := quicvarint.Read(r)
+ if err != nil {
+ return nil, err
+ }
+ byteOffset = protocol.ByteCount(bo)
+
+ return &ResetStreamFrame{
+ StreamID: streamID,
+ ErrorCode: qerr.StreamErrorCode(errorCode),
+ FinalSize: byteOffset,
+ }, nil
+}
+
+func (f *ResetStreamFrame) Append(b []byte, _ protocol.VersionNumber) ([]byte, error) {
+ b = append(b, 0x4)
+ b = quicvarint.Append(b, uint64(f.StreamID))
+ b = quicvarint.Append(b, uint64(f.ErrorCode))
+ b = quicvarint.Append(b, uint64(f.FinalSize))
+ return b, nil
+}
+
+// Length of a written frame
+func (f *ResetStreamFrame) Length(version protocol.VersionNumber) protocol.ByteCount {
+ return 1 + quicvarint.Len(uint64(f.StreamID)) + quicvarint.Len(uint64(f.ErrorCode)) + quicvarint.Len(uint64(f.FinalSize))
+}
diff --git a/vendor/github.com/quic-go/quic-go/internal/wire/retire_connection_id_frame.go b/vendor/github.com/quic-go/quic-go/internal/wire/retire_connection_id_frame.go
new file mode 100644
index 0000000000..3e4f58ac39
--- /dev/null
+++ b/vendor/github.com/quic-go/quic-go/internal/wire/retire_connection_id_frame.go
@@ -0,0 +1,36 @@
+package wire
+
+import (
+ "bytes"
+
+ "github.com/quic-go/quic-go/internal/protocol"
+ "github.com/quic-go/quic-go/quicvarint"
+)
+
+// A RetireConnectionIDFrame is a RETIRE_CONNECTION_ID frame
+type RetireConnectionIDFrame struct {
+ SequenceNumber uint64
+}
+
+func parseRetireConnectionIDFrame(r *bytes.Reader, _ protocol.VersionNumber) (*RetireConnectionIDFrame, error) {
+ if _, err := r.ReadByte(); err != nil {
+ return nil, err
+ }
+
+ seq, err := quicvarint.Read(r)
+ if err != nil {
+ return nil, err
+ }
+ return &RetireConnectionIDFrame{SequenceNumber: seq}, nil
+}
+
+func (f *RetireConnectionIDFrame) Append(b []byte, _ protocol.VersionNumber) ([]byte, error) {
+ b = append(b, 0x19)
+ b = quicvarint.Append(b, f.SequenceNumber)
+ return b, nil
+}
+
+// Length of a written frame
+func (f *RetireConnectionIDFrame) Length(protocol.VersionNumber) protocol.ByteCount {
+ return 1 + quicvarint.Len(f.SequenceNumber)
+}
diff --git a/vendor/github.com/quic-go/quic-go/internal/wire/short_header.go b/vendor/github.com/quic-go/quic-go/internal/wire/short_header.go
new file mode 100644
index 0000000000..69aa834118
--- /dev/null
+++ b/vendor/github.com/quic-go/quic-go/internal/wire/short_header.go
@@ -0,0 +1,73 @@
+package wire
+
+import (
+ "errors"
+ "fmt"
+ "io"
+
+ "github.com/quic-go/quic-go/internal/protocol"
+ "github.com/quic-go/quic-go/internal/utils"
+)
+
+// ParseShortHeader parses a short header packet.
+// It must be called after header protection was removed.
+// Otherwise, the check for the reserved bits will (most likely) fail.
+func ParseShortHeader(data []byte, connIDLen int) (length int, _ protocol.PacketNumber, _ protocol.PacketNumberLen, _ protocol.KeyPhaseBit, _ error) {
+ if len(data) == 0 {
+ return 0, 0, 0, 0, io.EOF
+ }
+ if data[0]&0x80 > 0 {
+ return 0, 0, 0, 0, errors.New("not a short header packet")
+ }
+ if data[0]&0x40 == 0 {
+ return 0, 0, 0, 0, errors.New("not a QUIC packet")
+ }
+ pnLen := protocol.PacketNumberLen(data[0]&0b11) + 1
+ if len(data) < 1+int(pnLen)+connIDLen {
+ return 0, 0, 0, 0, io.EOF
+ }
+
+ pos := 1 + connIDLen
+ var pn protocol.PacketNumber
+ switch pnLen {
+ case protocol.PacketNumberLen1:
+ pn = protocol.PacketNumber(data[pos])
+ case protocol.PacketNumberLen2:
+ pn = protocol.PacketNumber(utils.BigEndian.Uint16(data[pos : pos+2]))
+ case protocol.PacketNumberLen3:
+ pn = protocol.PacketNumber(utils.BigEndian.Uint24(data[pos : pos+3]))
+ case protocol.PacketNumberLen4:
+ pn = protocol.PacketNumber(utils.BigEndian.Uint32(data[pos : pos+4]))
+ default:
+ return 0, 0, 0, 0, fmt.Errorf("invalid packet number length: %d", pnLen)
+ }
+ kp := protocol.KeyPhaseZero
+ if data[0]&0b100 > 0 {
+ kp = protocol.KeyPhaseOne
+ }
+
+ var err error
+ if data[0]&0x18 != 0 {
+ err = ErrInvalidReservedBits
+ }
+ return 1 + connIDLen + int(pnLen), pn, pnLen, kp, err
+}
+
+// AppendShortHeader writes a short header.
+func AppendShortHeader(b []byte, connID protocol.ConnectionID, pn protocol.PacketNumber, pnLen protocol.PacketNumberLen, kp protocol.KeyPhaseBit) ([]byte, error) {
+ typeByte := 0x40 | uint8(pnLen-1)
+ if kp == protocol.KeyPhaseOne {
+ typeByte |= byte(1 << 2)
+ }
+ b = append(b, typeByte)
+ b = append(b, connID.Bytes()...)
+ return appendPacketNumber(b, pn, pnLen)
+}
+
+func ShortHeaderLen(dest protocol.ConnectionID, pnLen protocol.PacketNumberLen) protocol.ByteCount {
+ return 1 + protocol.ByteCount(dest.Len()) + protocol.ByteCount(pnLen)
+}
+
+func LogShortHeader(logger utils.Logger, dest protocol.ConnectionID, pn protocol.PacketNumber, pnLen protocol.PacketNumberLen, kp protocol.KeyPhaseBit) {
+ logger.Debugf("\tShort Header{DestConnectionID: %s, PacketNumber: %d, PacketNumberLen: %d, KeyPhase: %s}", dest, pn, pnLen, kp)
+}
diff --git a/vendor/github.com/quic-go/quic-go/internal/wire/stop_sending_frame.go b/vendor/github.com/quic-go/quic-go/internal/wire/stop_sending_frame.go
new file mode 100644
index 0000000000..e47a0f4a81
--- /dev/null
+++ b/vendor/github.com/quic-go/quic-go/internal/wire/stop_sending_frame.go
@@ -0,0 +1,48 @@
+package wire
+
+import (
+ "bytes"
+
+ "github.com/quic-go/quic-go/internal/protocol"
+ "github.com/quic-go/quic-go/internal/qerr"
+ "github.com/quic-go/quic-go/quicvarint"
+)
+
+// A StopSendingFrame is a STOP_SENDING frame
+type StopSendingFrame struct {
+ StreamID protocol.StreamID
+ ErrorCode qerr.StreamErrorCode
+}
+
+// parseStopSendingFrame parses a STOP_SENDING frame
+func parseStopSendingFrame(r *bytes.Reader, _ protocol.VersionNumber) (*StopSendingFrame, error) {
+ if _, err := r.ReadByte(); err != nil {
+ return nil, err
+ }
+
+ streamID, err := quicvarint.Read(r)
+ if err != nil {
+ return nil, err
+ }
+ errorCode, err := quicvarint.Read(r)
+ if err != nil {
+ return nil, err
+ }
+
+ return &StopSendingFrame{
+ StreamID: protocol.StreamID(streamID),
+ ErrorCode: qerr.StreamErrorCode(errorCode),
+ }, nil
+}
+
+// Length of a written frame
+func (f *StopSendingFrame) Length(_ protocol.VersionNumber) protocol.ByteCount {
+ return 1 + quicvarint.Len(uint64(f.StreamID)) + quicvarint.Len(uint64(f.ErrorCode))
+}
+
+func (f *StopSendingFrame) Append(b []byte, _ protocol.VersionNumber) ([]byte, error) {
+ b = append(b, 0x5)
+ b = quicvarint.Append(b, uint64(f.StreamID))
+ b = quicvarint.Append(b, uint64(f.ErrorCode))
+ return b, nil
+}
diff --git a/vendor/github.com/quic-go/quic-go/internal/wire/stream_data_blocked_frame.go b/vendor/github.com/quic-go/quic-go/internal/wire/stream_data_blocked_frame.go
new file mode 100644
index 0000000000..2d3fb07e0e
--- /dev/null
+++ b/vendor/github.com/quic-go/quic-go/internal/wire/stream_data_blocked_frame.go
@@ -0,0 +1,46 @@
+package wire
+
+import (
+ "bytes"
+
+ "github.com/quic-go/quic-go/internal/protocol"
+ "github.com/quic-go/quic-go/quicvarint"
+)
+
+// A StreamDataBlockedFrame is a STREAM_DATA_BLOCKED frame
+type StreamDataBlockedFrame struct {
+ StreamID protocol.StreamID
+ MaximumStreamData protocol.ByteCount
+}
+
+func parseStreamDataBlockedFrame(r *bytes.Reader, _ protocol.VersionNumber) (*StreamDataBlockedFrame, error) {
+ if _, err := r.ReadByte(); err != nil {
+ return nil, err
+ }
+
+ sid, err := quicvarint.Read(r)
+ if err != nil {
+ return nil, err
+ }
+ offset, err := quicvarint.Read(r)
+ if err != nil {
+ return nil, err
+ }
+
+ return &StreamDataBlockedFrame{
+ StreamID: protocol.StreamID(sid),
+ MaximumStreamData: protocol.ByteCount(offset),
+ }, nil
+}
+
+func (f *StreamDataBlockedFrame) Append(b []byte, _ protocol.VersionNumber) ([]byte, error) {
+ b = append(b, 0x15)
+ b = quicvarint.Append(b, uint64(f.StreamID))
+ b = quicvarint.Append(b, uint64(f.MaximumStreamData))
+ return b, nil
+}
+
+// Length of a written frame
+func (f *StreamDataBlockedFrame) Length(version protocol.VersionNumber) protocol.ByteCount {
+ return 1 + quicvarint.Len(uint64(f.StreamID)) + quicvarint.Len(uint64(f.MaximumStreamData))
+}
diff --git a/vendor/github.com/quic-go/quic-go/internal/wire/stream_frame.go b/vendor/github.com/quic-go/quic-go/internal/wire/stream_frame.go
new file mode 100644
index 0000000000..ebf3101c40
--- /dev/null
+++ b/vendor/github.com/quic-go/quic-go/internal/wire/stream_frame.go
@@ -0,0 +1,189 @@
+package wire
+
+import (
+ "bytes"
+ "errors"
+ "io"
+
+ "github.com/quic-go/quic-go/internal/protocol"
+ "github.com/quic-go/quic-go/quicvarint"
+)
+
+// A StreamFrame of QUIC
+type StreamFrame struct {
+ StreamID protocol.StreamID
+ Offset protocol.ByteCount
+ Data []byte
+ Fin bool
+ DataLenPresent bool
+
+ fromPool bool
+}
+
+func parseStreamFrame(r *bytes.Reader, _ protocol.VersionNumber) (*StreamFrame, error) {
+ typeByte, err := r.ReadByte()
+ if err != nil {
+ return nil, err
+ }
+
+ hasOffset := typeByte&0b100 > 0
+ fin := typeByte&0b1 > 0
+ hasDataLen := typeByte&0b10 > 0
+
+ streamID, err := quicvarint.Read(r)
+ if err != nil {
+ return nil, err
+ }
+ var offset uint64
+ if hasOffset {
+ offset, err = quicvarint.Read(r)
+ if err != nil {
+ return nil, err
+ }
+ }
+
+ var dataLen uint64
+ if hasDataLen {
+ var err error
+ dataLen, err = quicvarint.Read(r)
+ if err != nil {
+ return nil, err
+ }
+ } else {
+ // The rest of the packet is data
+ dataLen = uint64(r.Len())
+ }
+
+ var frame *StreamFrame
+ if dataLen < protocol.MinStreamFrameBufferSize {
+ frame = &StreamFrame{Data: make([]byte, dataLen)}
+ } else {
+ frame = GetStreamFrame()
+ // The STREAM frame can't be larger than the StreamFrame we obtained from the buffer,
+ // since those StreamFrames have a buffer length of the maximum packet size.
+ if dataLen > uint64(cap(frame.Data)) {
+ return nil, io.EOF
+ }
+ frame.Data = frame.Data[:dataLen]
+ }
+
+ frame.StreamID = protocol.StreamID(streamID)
+ frame.Offset = protocol.ByteCount(offset)
+ frame.Fin = fin
+ frame.DataLenPresent = hasDataLen
+
+ if dataLen != 0 {
+ if _, err := io.ReadFull(r, frame.Data); err != nil {
+ return nil, err
+ }
+ }
+ if frame.Offset+frame.DataLen() > protocol.MaxByteCount {
+ return nil, errors.New("stream data overflows maximum offset")
+ }
+ return frame, nil
+}
+
+// Write writes a STREAM frame
+func (f *StreamFrame) Append(b []byte, _ protocol.VersionNumber) ([]byte, error) {
+ if len(f.Data) == 0 && !f.Fin {
+ return nil, errors.New("StreamFrame: attempting to write empty frame without FIN")
+ }
+
+ typeByte := byte(0x8)
+ if f.Fin {
+ typeByte ^= 0b1
+ }
+ hasOffset := f.Offset != 0
+ if f.DataLenPresent {
+ typeByte ^= 0b10
+ }
+ if hasOffset {
+ typeByte ^= 0b100
+ }
+ b = append(b, typeByte)
+ b = quicvarint.Append(b, uint64(f.StreamID))
+ if hasOffset {
+ b = quicvarint.Append(b, uint64(f.Offset))
+ }
+ if f.DataLenPresent {
+ b = quicvarint.Append(b, uint64(f.DataLen()))
+ }
+ b = append(b, f.Data...)
+ return b, nil
+}
+
+// Length returns the total length of the STREAM frame
+func (f *StreamFrame) Length(version protocol.VersionNumber) protocol.ByteCount {
+ length := 1 + quicvarint.Len(uint64(f.StreamID))
+ if f.Offset != 0 {
+ length += quicvarint.Len(uint64(f.Offset))
+ }
+ if f.DataLenPresent {
+ length += quicvarint.Len(uint64(f.DataLen()))
+ }
+ return length + f.DataLen()
+}
+
+// DataLen gives the length of data in bytes
+func (f *StreamFrame) DataLen() protocol.ByteCount {
+ return protocol.ByteCount(len(f.Data))
+}
+
+// MaxDataLen returns the maximum data length
+// If 0 is returned, writing will fail (a STREAM frame must contain at least 1 byte of data).
+func (f *StreamFrame) MaxDataLen(maxSize protocol.ByteCount, version protocol.VersionNumber) protocol.ByteCount {
+ headerLen := 1 + quicvarint.Len(uint64(f.StreamID))
+ if f.Offset != 0 {
+ headerLen += quicvarint.Len(uint64(f.Offset))
+ }
+ if f.DataLenPresent {
+ // pretend that the data size will be 1 bytes
+ // if it turns out that varint encoding the length will consume 2 bytes, we need to adjust the data length afterwards
+ headerLen++
+ }
+ if headerLen > maxSize {
+ return 0
+ }
+ maxDataLen := maxSize - headerLen
+ if f.DataLenPresent && quicvarint.Len(uint64(maxDataLen)) != 1 {
+ maxDataLen--
+ }
+ return maxDataLen
+}
+
+// MaybeSplitOffFrame splits a frame such that it is not bigger than n bytes.
+// It returns if the frame was actually split.
+// The frame might not be split if:
+// * the size is large enough to fit the whole frame
+// * the size is too small to fit even a 1-byte frame. In that case, the frame returned is nil.
+func (f *StreamFrame) MaybeSplitOffFrame(maxSize protocol.ByteCount, version protocol.VersionNumber) (*StreamFrame, bool /* was splitting required */) {
+ if maxSize >= f.Length(version) {
+ return nil, false
+ }
+
+ n := f.MaxDataLen(maxSize, version)
+ if n == 0 {
+ return nil, true
+ }
+
+ new := GetStreamFrame()
+ new.StreamID = f.StreamID
+ new.Offset = f.Offset
+ new.Fin = false
+ new.DataLenPresent = f.DataLenPresent
+
+ // swap the data slices
+ new.Data, f.Data = f.Data, new.Data
+ new.fromPool, f.fromPool = f.fromPool, new.fromPool
+
+ f.Data = f.Data[:protocol.ByteCount(len(new.Data))-n]
+ copy(f.Data, new.Data[n:])
+ new.Data = new.Data[:n]
+ f.Offset += n
+
+ return new, true
+}
+
+func (f *StreamFrame) PutBack() {
+ putStreamFrame(f)
+}
diff --git a/vendor/github.com/quic-go/quic-go/internal/wire/streams_blocked_frame.go b/vendor/github.com/quic-go/quic-go/internal/wire/streams_blocked_frame.go
new file mode 100644
index 0000000000..5e556cb896
--- /dev/null
+++ b/vendor/github.com/quic-go/quic-go/internal/wire/streams_blocked_frame.go
@@ -0,0 +1,55 @@
+package wire
+
+import (
+ "bytes"
+ "fmt"
+
+ "github.com/quic-go/quic-go/internal/protocol"
+ "github.com/quic-go/quic-go/quicvarint"
+)
+
+// A StreamsBlockedFrame is a STREAMS_BLOCKED frame
+type StreamsBlockedFrame struct {
+ Type protocol.StreamType
+ StreamLimit protocol.StreamNum
+}
+
+func parseStreamsBlockedFrame(r *bytes.Reader, _ protocol.VersionNumber) (*StreamsBlockedFrame, error) {
+ typeByte, err := r.ReadByte()
+ if err != nil {
+ return nil, err
+ }
+
+ f := &StreamsBlockedFrame{}
+ switch typeByte {
+ case 0x16:
+ f.Type = protocol.StreamTypeBidi
+ case 0x17:
+ f.Type = protocol.StreamTypeUni
+ }
+ streamLimit, err := quicvarint.Read(r)
+ if err != nil {
+ return nil, err
+ }
+ f.StreamLimit = protocol.StreamNum(streamLimit)
+ if f.StreamLimit > protocol.MaxStreamCount {
+ return nil, fmt.Errorf("%d exceeds the maximum stream count", f.StreamLimit)
+ }
+ return f, nil
+}
+
+func (f *StreamsBlockedFrame) Append(b []byte, _ protocol.VersionNumber) ([]byte, error) {
+ switch f.Type {
+ case protocol.StreamTypeBidi:
+ b = append(b, 0x16)
+ case protocol.StreamTypeUni:
+ b = append(b, 0x17)
+ }
+ b = quicvarint.Append(b, uint64(f.StreamLimit))
+ return b, nil
+}
+
+// Length of a written frame
+func (f *StreamsBlockedFrame) Length(_ protocol.VersionNumber) protocol.ByteCount {
+ return 1 + quicvarint.Len(uint64(f.StreamLimit))
+}
diff --git a/vendor/github.com/quic-go/quic-go/internal/wire/transport_parameters.go b/vendor/github.com/quic-go/quic-go/internal/wire/transport_parameters.go
new file mode 100644
index 0000000000..a64638cbdc
--- /dev/null
+++ b/vendor/github.com/quic-go/quic-go/internal/wire/transport_parameters.go
@@ -0,0 +1,484 @@
+package wire
+
+import (
+ "bytes"
+ "encoding/binary"
+ "errors"
+ "fmt"
+ "io"
+ "math/rand"
+ "net"
+ "sort"
+ "time"
+
+ "github.com/quic-go/quic-go/internal/protocol"
+ "github.com/quic-go/quic-go/internal/qerr"
+ "github.com/quic-go/quic-go/internal/utils"
+ "github.com/quic-go/quic-go/quicvarint"
+)
+
+const transportParameterMarshalingVersion = 1
+
+func init() {
+ rand.Seed(time.Now().UTC().UnixNano())
+}
+
+type transportParameterID uint64
+
+const (
+ originalDestinationConnectionIDParameterID transportParameterID = 0x0
+ maxIdleTimeoutParameterID transportParameterID = 0x1
+ statelessResetTokenParameterID transportParameterID = 0x2
+ maxUDPPayloadSizeParameterID transportParameterID = 0x3
+ initialMaxDataParameterID transportParameterID = 0x4
+ initialMaxStreamDataBidiLocalParameterID transportParameterID = 0x5
+ initialMaxStreamDataBidiRemoteParameterID transportParameterID = 0x6
+ initialMaxStreamDataUniParameterID transportParameterID = 0x7
+ initialMaxStreamsBidiParameterID transportParameterID = 0x8
+ initialMaxStreamsUniParameterID transportParameterID = 0x9
+ ackDelayExponentParameterID transportParameterID = 0xa
+ maxAckDelayParameterID transportParameterID = 0xb
+ disableActiveMigrationParameterID transportParameterID = 0xc
+ preferredAddressParameterID transportParameterID = 0xd
+ activeConnectionIDLimitParameterID transportParameterID = 0xe
+ initialSourceConnectionIDParameterID transportParameterID = 0xf
+ retrySourceConnectionIDParameterID transportParameterID = 0x10
+ // RFC 9221
+ maxDatagramFrameSizeParameterID transportParameterID = 0x20
+)
+
+// PreferredAddress is the value encoding in the preferred_address transport parameter
+type PreferredAddress struct {
+ IPv4 net.IP
+ IPv4Port uint16
+ IPv6 net.IP
+ IPv6Port uint16
+ ConnectionID protocol.ConnectionID
+ StatelessResetToken protocol.StatelessResetToken
+}
+
+// TransportParameters are parameters sent to the peer during the handshake
+type TransportParameters struct {
+ InitialMaxStreamDataBidiLocal protocol.ByteCount
+ InitialMaxStreamDataBidiRemote protocol.ByteCount
+ InitialMaxStreamDataUni protocol.ByteCount
+ InitialMaxData protocol.ByteCount
+
+ MaxAckDelay time.Duration
+ AckDelayExponent uint8
+
+ DisableActiveMigration bool
+
+ MaxUDPPayloadSize protocol.ByteCount
+
+ MaxUniStreamNum protocol.StreamNum
+ MaxBidiStreamNum protocol.StreamNum
+
+ MaxIdleTimeout time.Duration
+
+ PreferredAddress *PreferredAddress
+
+ OriginalDestinationConnectionID protocol.ConnectionID
+ InitialSourceConnectionID protocol.ConnectionID
+ RetrySourceConnectionID *protocol.ConnectionID // use a pointer here to distinguish zero-length connection IDs from missing transport parameters
+
+ StatelessResetToken *protocol.StatelessResetToken
+ ActiveConnectionIDLimit uint64
+
+ MaxDatagramFrameSize protocol.ByteCount
+}
+
+// Unmarshal the transport parameters
+func (p *TransportParameters) Unmarshal(data []byte, sentBy protocol.Perspective) error {
+ if err := p.unmarshal(bytes.NewReader(data), sentBy, false); err != nil {
+ return &qerr.TransportError{
+ ErrorCode: qerr.TransportParameterError,
+ ErrorMessage: err.Error(),
+ }
+ }
+ return nil
+}
+
+func (p *TransportParameters) unmarshal(r *bytes.Reader, sentBy protocol.Perspective, fromSessionTicket bool) error {
+ // needed to check that every parameter is only sent at most once
+ var parameterIDs []transportParameterID
+
+ var (
+ readOriginalDestinationConnectionID bool
+ readInitialSourceConnectionID bool
+ )
+
+ p.AckDelayExponent = protocol.DefaultAckDelayExponent
+ p.MaxAckDelay = protocol.DefaultMaxAckDelay
+ p.MaxDatagramFrameSize = protocol.InvalidByteCount
+
+ for r.Len() > 0 {
+ paramIDInt, err := quicvarint.Read(r)
+ if err != nil {
+ return err
+ }
+ paramID := transportParameterID(paramIDInt)
+ paramLen, err := quicvarint.Read(r)
+ if err != nil {
+ return err
+ }
+ if uint64(r.Len()) < paramLen {
+ return fmt.Errorf("remaining length (%d) smaller than parameter length (%d)", r.Len(), paramLen)
+ }
+ parameterIDs = append(parameterIDs, paramID)
+ switch paramID {
+ case maxIdleTimeoutParameterID,
+ maxUDPPayloadSizeParameterID,
+ initialMaxDataParameterID,
+ initialMaxStreamDataBidiLocalParameterID,
+ initialMaxStreamDataBidiRemoteParameterID,
+ initialMaxStreamDataUniParameterID,
+ initialMaxStreamsBidiParameterID,
+ initialMaxStreamsUniParameterID,
+ maxAckDelayParameterID,
+ activeConnectionIDLimitParameterID,
+ maxDatagramFrameSizeParameterID,
+ ackDelayExponentParameterID:
+ if err := p.readNumericTransportParameter(r, paramID, int(paramLen)); err != nil {
+ return err
+ }
+ case preferredAddressParameterID:
+ if sentBy == protocol.PerspectiveClient {
+ return errors.New("client sent a preferred_address")
+ }
+ if err := p.readPreferredAddress(r, int(paramLen)); err != nil {
+ return err
+ }
+ case disableActiveMigrationParameterID:
+ if paramLen != 0 {
+ return fmt.Errorf("wrong length for disable_active_migration: %d (expected empty)", paramLen)
+ }
+ p.DisableActiveMigration = true
+ case statelessResetTokenParameterID:
+ if sentBy == protocol.PerspectiveClient {
+ return errors.New("client sent a stateless_reset_token")
+ }
+ if paramLen != 16 {
+ return fmt.Errorf("wrong length for stateless_reset_token: %d (expected 16)", paramLen)
+ }
+ var token protocol.StatelessResetToken
+ r.Read(token[:])
+ p.StatelessResetToken = &token
+ case originalDestinationConnectionIDParameterID:
+ if sentBy == protocol.PerspectiveClient {
+ return errors.New("client sent an original_destination_connection_id")
+ }
+ p.OriginalDestinationConnectionID, _ = protocol.ReadConnectionID(r, int(paramLen))
+ readOriginalDestinationConnectionID = true
+ case initialSourceConnectionIDParameterID:
+ p.InitialSourceConnectionID, _ = protocol.ReadConnectionID(r, int(paramLen))
+ readInitialSourceConnectionID = true
+ case retrySourceConnectionIDParameterID:
+ if sentBy == protocol.PerspectiveClient {
+ return errors.New("client sent a retry_source_connection_id")
+ }
+ connID, _ := protocol.ReadConnectionID(r, int(paramLen))
+ p.RetrySourceConnectionID = &connID
+ default:
+ r.Seek(int64(paramLen), io.SeekCurrent)
+ }
+ }
+
+ if !fromSessionTicket {
+ if sentBy == protocol.PerspectiveServer && !readOriginalDestinationConnectionID {
+ return errors.New("missing original_destination_connection_id")
+ }
+ if p.MaxUDPPayloadSize == 0 {
+ p.MaxUDPPayloadSize = protocol.MaxByteCount
+ }
+ if !readInitialSourceConnectionID {
+ return errors.New("missing initial_source_connection_id")
+ }
+ }
+
+ // check that every transport parameter was sent at most once
+ sort.Slice(parameterIDs, func(i, j int) bool { return parameterIDs[i] < parameterIDs[j] })
+ for i := 0; i < len(parameterIDs)-1; i++ {
+ if parameterIDs[i] == parameterIDs[i+1] {
+ return fmt.Errorf("received duplicate transport parameter %#x", parameterIDs[i])
+ }
+ }
+
+ return nil
+}
+
+func (p *TransportParameters) readPreferredAddress(r *bytes.Reader, expectedLen int) error {
+ remainingLen := r.Len()
+ pa := &PreferredAddress{}
+ ipv4 := make([]byte, 4)
+ if _, err := io.ReadFull(r, ipv4); err != nil {
+ return err
+ }
+ pa.IPv4 = net.IP(ipv4)
+ port, err := utils.BigEndian.ReadUint16(r)
+ if err != nil {
+ return err
+ }
+ pa.IPv4Port = port
+ ipv6 := make([]byte, 16)
+ if _, err := io.ReadFull(r, ipv6); err != nil {
+ return err
+ }
+ pa.IPv6 = net.IP(ipv6)
+ port, err = utils.BigEndian.ReadUint16(r)
+ if err != nil {
+ return err
+ }
+ pa.IPv6Port = port
+ connIDLen, err := r.ReadByte()
+ if err != nil {
+ return err
+ }
+ if connIDLen == 0 || connIDLen > protocol.MaxConnIDLen {
+ return fmt.Errorf("invalid connection ID length: %d", connIDLen)
+ }
+ connID, err := protocol.ReadConnectionID(r, int(connIDLen))
+ if err != nil {
+ return err
+ }
+ pa.ConnectionID = connID
+ if _, err := io.ReadFull(r, pa.StatelessResetToken[:]); err != nil {
+ return err
+ }
+ if bytesRead := remainingLen - r.Len(); bytesRead != expectedLen {
+ return fmt.Errorf("expected preferred_address to be %d long, read %d bytes", expectedLen, bytesRead)
+ }
+ p.PreferredAddress = pa
+ return nil
+}
+
+func (p *TransportParameters) readNumericTransportParameter(
+ r *bytes.Reader,
+ paramID transportParameterID,
+ expectedLen int,
+) error {
+ remainingLen := r.Len()
+ val, err := quicvarint.Read(r)
+ if err != nil {
+ return fmt.Errorf("error while reading transport parameter %d: %s", paramID, err)
+ }
+ if remainingLen-r.Len() != expectedLen {
+ return fmt.Errorf("inconsistent transport parameter length for transport parameter %#x", paramID)
+ }
+ //nolint:exhaustive // This only covers the numeric transport parameters.
+ switch paramID {
+ case initialMaxStreamDataBidiLocalParameterID:
+ p.InitialMaxStreamDataBidiLocal = protocol.ByteCount(val)
+ case initialMaxStreamDataBidiRemoteParameterID:
+ p.InitialMaxStreamDataBidiRemote = protocol.ByteCount(val)
+ case initialMaxStreamDataUniParameterID:
+ p.InitialMaxStreamDataUni = protocol.ByteCount(val)
+ case initialMaxDataParameterID:
+ p.InitialMaxData = protocol.ByteCount(val)
+ case initialMaxStreamsBidiParameterID:
+ p.MaxBidiStreamNum = protocol.StreamNum(val)
+ if p.MaxBidiStreamNum > protocol.MaxStreamCount {
+ return fmt.Errorf("initial_max_streams_bidi too large: %d (maximum %d)", p.MaxBidiStreamNum, protocol.MaxStreamCount)
+ }
+ case initialMaxStreamsUniParameterID:
+ p.MaxUniStreamNum = protocol.StreamNum(val)
+ if p.MaxUniStreamNum > protocol.MaxStreamCount {
+ return fmt.Errorf("initial_max_streams_uni too large: %d (maximum %d)", p.MaxUniStreamNum, protocol.MaxStreamCount)
+ }
+ case maxIdleTimeoutParameterID:
+ p.MaxIdleTimeout = utils.Max(protocol.MinRemoteIdleTimeout, time.Duration(val)*time.Millisecond)
+ case maxUDPPayloadSizeParameterID:
+ if val < 1200 {
+ return fmt.Errorf("invalid value for max_packet_size: %d (minimum 1200)", val)
+ }
+ p.MaxUDPPayloadSize = protocol.ByteCount(val)
+ case ackDelayExponentParameterID:
+ if val > protocol.MaxAckDelayExponent {
+ return fmt.Errorf("invalid value for ack_delay_exponent: %d (maximum %d)", val, protocol.MaxAckDelayExponent)
+ }
+ p.AckDelayExponent = uint8(val)
+ case maxAckDelayParameterID:
+ if val > uint64(protocol.MaxMaxAckDelay/time.Millisecond) {
+ return fmt.Errorf("invalid value for max_ack_delay: %dms (maximum %dms)", val, protocol.MaxMaxAckDelay/time.Millisecond)
+ }
+ p.MaxAckDelay = time.Duration(val) * time.Millisecond
+ case activeConnectionIDLimitParameterID:
+ if val < 2 {
+ return fmt.Errorf("invalid value for active_connection_id_limit: %d (minimum 2)", val)
+ }
+ p.ActiveConnectionIDLimit = val
+ case maxDatagramFrameSizeParameterID:
+ p.MaxDatagramFrameSize = protocol.ByteCount(val)
+ default:
+ return fmt.Errorf("TransportParameter BUG: transport parameter %d not found", paramID)
+ }
+ return nil
+}
+
+// Marshal the transport parameters
+func (p *TransportParameters) Marshal(pers protocol.Perspective) []byte {
+ // Typical Transport Parameters consume around 110 bytes, depending on the exact values,
+ // especially the lengths of the Connection IDs.
+ // Allocate 256 bytes, so we won't have to grow the slice in any case.
+ b := make([]byte, 0, 256)
+
+ // add a greased value
+ b = quicvarint.Append(b, uint64(27+31*rand.Intn(100)))
+ length := rand.Intn(16)
+ b = quicvarint.Append(b, uint64(length))
+ b = b[:len(b)+length]
+ rand.Read(b[len(b)-length:])
+
+ // initial_max_stream_data_bidi_local
+ b = p.marshalVarintParam(b, initialMaxStreamDataBidiLocalParameterID, uint64(p.InitialMaxStreamDataBidiLocal))
+ // initial_max_stream_data_bidi_remote
+ b = p.marshalVarintParam(b, initialMaxStreamDataBidiRemoteParameterID, uint64(p.InitialMaxStreamDataBidiRemote))
+ // initial_max_stream_data_uni
+ b = p.marshalVarintParam(b, initialMaxStreamDataUniParameterID, uint64(p.InitialMaxStreamDataUni))
+ // initial_max_data
+ b = p.marshalVarintParam(b, initialMaxDataParameterID, uint64(p.InitialMaxData))
+ // initial_max_bidi_streams
+ b = p.marshalVarintParam(b, initialMaxStreamsBidiParameterID, uint64(p.MaxBidiStreamNum))
+ // initial_max_uni_streams
+ b = p.marshalVarintParam(b, initialMaxStreamsUniParameterID, uint64(p.MaxUniStreamNum))
+ // idle_timeout
+ b = p.marshalVarintParam(b, maxIdleTimeoutParameterID, uint64(p.MaxIdleTimeout/time.Millisecond))
+ // max_packet_size
+ b = p.marshalVarintParam(b, maxUDPPayloadSizeParameterID, uint64(protocol.MaxPacketBufferSize))
+ // max_ack_delay
+ // Only send it if is different from the default value.
+ if p.MaxAckDelay != protocol.DefaultMaxAckDelay {
+ b = p.marshalVarintParam(b, maxAckDelayParameterID, uint64(p.MaxAckDelay/time.Millisecond))
+ }
+ // ack_delay_exponent
+ // Only send it if is different from the default value.
+ if p.AckDelayExponent != protocol.DefaultAckDelayExponent {
+ b = p.marshalVarintParam(b, ackDelayExponentParameterID, uint64(p.AckDelayExponent))
+ }
+ // disable_active_migration
+ if p.DisableActiveMigration {
+ b = quicvarint.Append(b, uint64(disableActiveMigrationParameterID))
+ b = quicvarint.Append(b, 0)
+ }
+ if pers == protocol.PerspectiveServer {
+ // stateless_reset_token
+ if p.StatelessResetToken != nil {
+ b = quicvarint.Append(b, uint64(statelessResetTokenParameterID))
+ b = quicvarint.Append(b, 16)
+ b = append(b, p.StatelessResetToken[:]...)
+ }
+ // original_destination_connection_id
+ b = quicvarint.Append(b, uint64(originalDestinationConnectionIDParameterID))
+ b = quicvarint.Append(b, uint64(p.OriginalDestinationConnectionID.Len()))
+ b = append(b, p.OriginalDestinationConnectionID.Bytes()...)
+ // preferred_address
+ if p.PreferredAddress != nil {
+ b = quicvarint.Append(b, uint64(preferredAddressParameterID))
+ b = quicvarint.Append(b, 4+2+16+2+1+uint64(p.PreferredAddress.ConnectionID.Len())+16)
+ ipv4 := p.PreferredAddress.IPv4
+ b = append(b, ipv4[len(ipv4)-4:]...)
+ b = append(b, []byte{0, 0}...)
+ binary.BigEndian.PutUint16(b[len(b)-2:], p.PreferredAddress.IPv4Port)
+ b = append(b, p.PreferredAddress.IPv6...)
+ b = append(b, []byte{0, 0}...)
+ binary.BigEndian.PutUint16(b[len(b)-2:], p.PreferredAddress.IPv6Port)
+ b = append(b, uint8(p.PreferredAddress.ConnectionID.Len()))
+ b = append(b, p.PreferredAddress.ConnectionID.Bytes()...)
+ b = append(b, p.PreferredAddress.StatelessResetToken[:]...)
+ }
+ }
+ // active_connection_id_limit
+ b = p.marshalVarintParam(b, activeConnectionIDLimitParameterID, p.ActiveConnectionIDLimit)
+ // initial_source_connection_id
+ b = quicvarint.Append(b, uint64(initialSourceConnectionIDParameterID))
+ b = quicvarint.Append(b, uint64(p.InitialSourceConnectionID.Len()))
+ b = append(b, p.InitialSourceConnectionID.Bytes()...)
+ // retry_source_connection_id
+ if pers == protocol.PerspectiveServer && p.RetrySourceConnectionID != nil {
+ b = quicvarint.Append(b, uint64(retrySourceConnectionIDParameterID))
+ b = quicvarint.Append(b, uint64(p.RetrySourceConnectionID.Len()))
+ b = append(b, p.RetrySourceConnectionID.Bytes()...)
+ }
+ if p.MaxDatagramFrameSize != protocol.InvalidByteCount {
+ b = p.marshalVarintParam(b, maxDatagramFrameSizeParameterID, uint64(p.MaxDatagramFrameSize))
+ }
+ return b
+}
+
+func (p *TransportParameters) marshalVarintParam(b []byte, id transportParameterID, val uint64) []byte {
+ b = quicvarint.Append(b, uint64(id))
+ b = quicvarint.Append(b, uint64(quicvarint.Len(val)))
+ return quicvarint.Append(b, val)
+}
+
+// MarshalForSessionTicket marshals the transport parameters we save in the session ticket.
+// When sending a 0-RTT enabled TLS session tickets, we need to save the transport parameters.
+// The client will remember the transport parameters used in the last session,
+// and apply those to the 0-RTT data it sends.
+// Saving the transport parameters in the ticket gives the server the option to reject 0-RTT
+// if the transport parameters changed.
+// Since the session ticket is encrypted, the serialization format is defined by the server.
+// For convenience, we use the same format that we also use for sending the transport parameters.
+func (p *TransportParameters) MarshalForSessionTicket(b []byte) []byte {
+ b = quicvarint.Append(b, transportParameterMarshalingVersion)
+
+ // initial_max_stream_data_bidi_local
+ b = p.marshalVarintParam(b, initialMaxStreamDataBidiLocalParameterID, uint64(p.InitialMaxStreamDataBidiLocal))
+ // initial_max_stream_data_bidi_remote
+ b = p.marshalVarintParam(b, initialMaxStreamDataBidiRemoteParameterID, uint64(p.InitialMaxStreamDataBidiRemote))
+ // initial_max_stream_data_uni
+ b = p.marshalVarintParam(b, initialMaxStreamDataUniParameterID, uint64(p.InitialMaxStreamDataUni))
+ // initial_max_data
+ b = p.marshalVarintParam(b, initialMaxDataParameterID, uint64(p.InitialMaxData))
+ // initial_max_bidi_streams
+ b = p.marshalVarintParam(b, initialMaxStreamsBidiParameterID, uint64(p.MaxBidiStreamNum))
+ // initial_max_uni_streams
+ b = p.marshalVarintParam(b, initialMaxStreamsUniParameterID, uint64(p.MaxUniStreamNum))
+ // active_connection_id_limit
+ return p.marshalVarintParam(b, activeConnectionIDLimitParameterID, p.ActiveConnectionIDLimit)
+}
+
+// UnmarshalFromSessionTicket unmarshals transport parameters from a session ticket.
+func (p *TransportParameters) UnmarshalFromSessionTicket(r *bytes.Reader) error {
+ version, err := quicvarint.Read(r)
+ if err != nil {
+ return err
+ }
+ if version != transportParameterMarshalingVersion {
+ return fmt.Errorf("unknown transport parameter marshaling version: %d", version)
+ }
+ return p.unmarshal(r, protocol.PerspectiveServer, true)
+}
+
+// ValidFor0RTT checks if the transport parameters match those saved in the session ticket.
+func (p *TransportParameters) ValidFor0RTT(saved *TransportParameters) bool {
+ return p.InitialMaxStreamDataBidiLocal >= saved.InitialMaxStreamDataBidiLocal &&
+ p.InitialMaxStreamDataBidiRemote >= saved.InitialMaxStreamDataBidiRemote &&
+ p.InitialMaxStreamDataUni >= saved.InitialMaxStreamDataUni &&
+ p.InitialMaxData >= saved.InitialMaxData &&
+ p.MaxBidiStreamNum >= saved.MaxBidiStreamNum &&
+ p.MaxUniStreamNum >= saved.MaxUniStreamNum &&
+ p.ActiveConnectionIDLimit == saved.ActiveConnectionIDLimit
+}
+
+// String returns a string representation, intended for logging.
+func (p *TransportParameters) String() string {
+ logString := "&wire.TransportParameters{OriginalDestinationConnectionID: %s, InitialSourceConnectionID: %s, "
+ logParams := []interface{}{p.OriginalDestinationConnectionID, p.InitialSourceConnectionID}
+ if p.RetrySourceConnectionID != nil {
+ logString += "RetrySourceConnectionID: %s, "
+ logParams = append(logParams, p.RetrySourceConnectionID)
+ }
+ logString += "InitialMaxStreamDataBidiLocal: %d, InitialMaxStreamDataBidiRemote: %d, InitialMaxStreamDataUni: %d, InitialMaxData: %d, MaxBidiStreamNum: %d, MaxUniStreamNum: %d, MaxIdleTimeout: %s, AckDelayExponent: %d, MaxAckDelay: %s, ActiveConnectionIDLimit: %d"
+ logParams = append(logParams, []interface{}{p.InitialMaxStreamDataBidiLocal, p.InitialMaxStreamDataBidiRemote, p.InitialMaxStreamDataUni, p.InitialMaxData, p.MaxBidiStreamNum, p.MaxUniStreamNum, p.MaxIdleTimeout, p.AckDelayExponent, p.MaxAckDelay, p.ActiveConnectionIDLimit}...)
+ if p.StatelessResetToken != nil { // the client never sends a stateless reset token
+ logString += ", StatelessResetToken: %#x"
+ logParams = append(logParams, *p.StatelessResetToken)
+ }
+ if p.MaxDatagramFrameSize != protocol.InvalidByteCount {
+ logString += ", MaxDatagramFrameSize: %d"
+ logParams = append(logParams, p.MaxDatagramFrameSize)
+ }
+ logString += "}"
+ return fmt.Sprintf(logString, logParams...)
+}
diff --git a/vendor/github.com/quic-go/quic-go/internal/wire/version_negotiation.go b/vendor/github.com/quic-go/quic-go/internal/wire/version_negotiation.go
new file mode 100644
index 0000000000..3dc621135b
--- /dev/null
+++ b/vendor/github.com/quic-go/quic-go/internal/wire/version_negotiation.go
@@ -0,0 +1,53 @@
+package wire
+
+import (
+ "bytes"
+ "crypto/rand"
+ "encoding/binary"
+ "errors"
+
+ "github.com/quic-go/quic-go/internal/protocol"
+ "github.com/quic-go/quic-go/internal/utils"
+)
+
+// ParseVersionNegotiationPacket parses a Version Negotiation packet.
+func ParseVersionNegotiationPacket(b []byte) (dest, src protocol.ArbitraryLenConnectionID, _ []protocol.VersionNumber, _ error) {
+ n, dest, src, err := ParseArbitraryLenConnectionIDs(b)
+ if err != nil {
+ return nil, nil, nil, err
+ }
+ b = b[n:]
+ if len(b) == 0 {
+ //nolint:stylecheck
+ return nil, nil, nil, errors.New("Version Negotiation packet has empty version list")
+ }
+ if len(b)%4 != 0 {
+ //nolint:stylecheck
+ return nil, nil, nil, errors.New("Version Negotiation packet has a version list with an invalid length")
+ }
+ versions := make([]protocol.VersionNumber, len(b)/4)
+ for i := 0; len(b) > 0; i++ {
+ versions[i] = protocol.VersionNumber(binary.BigEndian.Uint32(b[:4]))
+ b = b[4:]
+ }
+ return dest, src, versions, nil
+}
+
+// ComposeVersionNegotiation composes a Version Negotiation
+func ComposeVersionNegotiation(destConnID, srcConnID protocol.ArbitraryLenConnectionID, versions []protocol.VersionNumber) []byte {
+ greasedVersions := protocol.GetGreasedVersions(versions)
+ expectedLen := 1 /* type byte */ + 4 /* version field */ + 1 /* dest connection ID length field */ + destConnID.Len() + 1 /* src connection ID length field */ + srcConnID.Len() + len(greasedVersions)*4
+ buf := bytes.NewBuffer(make([]byte, 0, expectedLen))
+ r := make([]byte, 1)
+ _, _ = rand.Read(r) // ignore the error here. It is not critical to have perfect random here.
+ buf.WriteByte(r[0] | 0x80)
+ utils.BigEndian.WriteUint32(buf, 0) // version 0
+ buf.WriteByte(uint8(destConnID.Len()))
+ buf.Write(destConnID.Bytes())
+ buf.WriteByte(uint8(srcConnID.Len()))
+ buf.Write(srcConnID.Bytes())
+ for _, v := range greasedVersions {
+ utils.BigEndian.WriteUint32(buf, uint32(v))
+ }
+ return buf.Bytes()
+}
diff --git a/vendor/github.com/quic-go/quic-go/logging/frame.go b/vendor/github.com/quic-go/quic-go/logging/frame.go
new file mode 100644
index 0000000000..9a055db359
--- /dev/null
+++ b/vendor/github.com/quic-go/quic-go/logging/frame.go
@@ -0,0 +1,66 @@
+package logging
+
+import "github.com/quic-go/quic-go/internal/wire"
+
+// A Frame is a QUIC frame
+type Frame interface{}
+
+// The AckRange is used within the AckFrame.
+// It is a range of packet numbers that is being acknowledged.
+type AckRange = wire.AckRange
+
+type (
+ // An AckFrame is an ACK frame.
+ AckFrame = wire.AckFrame
+ // A ConnectionCloseFrame is a CONNECTION_CLOSE frame.
+ ConnectionCloseFrame = wire.ConnectionCloseFrame
+ // A DataBlockedFrame is a DATA_BLOCKED frame.
+ DataBlockedFrame = wire.DataBlockedFrame
+ // A HandshakeDoneFrame is a HANDSHAKE_DONE frame.
+ HandshakeDoneFrame = wire.HandshakeDoneFrame
+ // A MaxDataFrame is a MAX_DATA frame.
+ MaxDataFrame = wire.MaxDataFrame
+ // A MaxStreamDataFrame is a MAX_STREAM_DATA frame.
+ MaxStreamDataFrame = wire.MaxStreamDataFrame
+ // A MaxStreamsFrame is a MAX_STREAMS_FRAME.
+ MaxStreamsFrame = wire.MaxStreamsFrame
+ // A NewConnectionIDFrame is a NEW_CONNECTION_ID frame.
+ NewConnectionIDFrame = wire.NewConnectionIDFrame
+ // A NewTokenFrame is a NEW_TOKEN frame.
+ NewTokenFrame = wire.NewTokenFrame
+ // A PathChallengeFrame is a PATH_CHALLENGE frame.
+ PathChallengeFrame = wire.PathChallengeFrame
+ // A PathResponseFrame is a PATH_RESPONSE frame.
+ PathResponseFrame = wire.PathResponseFrame
+ // A PingFrame is a PING frame.
+ PingFrame = wire.PingFrame
+ // A ResetStreamFrame is a RESET_STREAM frame.
+ ResetStreamFrame = wire.ResetStreamFrame
+ // A RetireConnectionIDFrame is a RETIRE_CONNECTION_ID frame.
+ RetireConnectionIDFrame = wire.RetireConnectionIDFrame
+ // A StopSendingFrame is a STOP_SENDING frame.
+ StopSendingFrame = wire.StopSendingFrame
+ // A StreamsBlockedFrame is a STREAMS_BLOCKED frame.
+ StreamsBlockedFrame = wire.StreamsBlockedFrame
+ // A StreamDataBlockedFrame is a STREAM_DATA_BLOCKED frame.
+ StreamDataBlockedFrame = wire.StreamDataBlockedFrame
+)
+
+// A CryptoFrame is a CRYPTO frame.
+type CryptoFrame struct {
+ Offset ByteCount
+ Length ByteCount
+}
+
+// A StreamFrame is a STREAM frame.
+type StreamFrame struct {
+ StreamID StreamID
+ Offset ByteCount
+ Length ByteCount
+ Fin bool
+}
+
+// A DatagramFrame is a DATAGRAM frame.
+type DatagramFrame struct {
+ Length ByteCount
+}
diff --git a/vendor/github.com/quic-go/quic-go/logging/interface.go b/vendor/github.com/quic-go/quic-go/logging/interface.go
new file mode 100644
index 0000000000..efcef151e1
--- /dev/null
+++ b/vendor/github.com/quic-go/quic-go/logging/interface.go
@@ -0,0 +1,146 @@
+// Package logging defines a logging interface for quic-go.
+// This package should not be considered stable
+package logging
+
+import (
+ "context"
+ "net"
+ "time"
+
+ "github.com/quic-go/quic-go/internal/protocol"
+ "github.com/quic-go/quic-go/internal/qerr"
+ "github.com/quic-go/quic-go/internal/utils"
+ "github.com/quic-go/quic-go/internal/wire"
+)
+
+type (
+ // A ByteCount is used to count bytes.
+ ByteCount = protocol.ByteCount
+ // A ConnectionID is a QUIC Connection ID.
+ ConnectionID = protocol.ConnectionID
+ // An ArbitraryLenConnectionID is a QUIC Connection ID that can be up to 255 bytes long.
+ ArbitraryLenConnectionID = protocol.ArbitraryLenConnectionID
+ // The EncryptionLevel is the encryption level of a packet.
+ EncryptionLevel = protocol.EncryptionLevel
+ // The KeyPhase is the key phase of the 1-RTT keys.
+ KeyPhase = protocol.KeyPhase
+ // The KeyPhaseBit is the value of the key phase bit of the 1-RTT packets.
+ KeyPhaseBit = protocol.KeyPhaseBit
+ // The PacketNumber is the packet number of a packet.
+ PacketNumber = protocol.PacketNumber
+ // The Perspective is the role of a QUIC endpoint (client or server).
+ Perspective = protocol.Perspective
+ // A StatelessResetToken is a stateless reset token.
+ StatelessResetToken = protocol.StatelessResetToken
+ // The StreamID is the stream ID.
+ StreamID = protocol.StreamID
+ // The StreamNum is the number of the stream.
+ StreamNum = protocol.StreamNum
+ // The StreamType is the type of the stream (unidirectional or bidirectional).
+ StreamType = protocol.StreamType
+ // The VersionNumber is the QUIC version.
+ VersionNumber = protocol.VersionNumber
+
+ // The Header is the QUIC packet header, before removing header protection.
+ Header = wire.Header
+ // The ExtendedHeader is the QUIC Long Header packet header, after removing header protection.
+ ExtendedHeader = wire.ExtendedHeader
+ // The TransportParameters are QUIC transport parameters.
+ TransportParameters = wire.TransportParameters
+ // The PreferredAddress is the preferred address sent in the transport parameters.
+ PreferredAddress = wire.PreferredAddress
+
+ // A TransportError is a transport-level error code.
+ TransportError = qerr.TransportErrorCode
+ // An ApplicationError is an application-defined error code.
+ ApplicationError = qerr.TransportErrorCode
+
+ // The RTTStats contain statistics used by the congestion controller.
+ RTTStats = utils.RTTStats
+)
+
+const (
+ // KeyPhaseZero is key phase bit 0
+ KeyPhaseZero KeyPhaseBit = protocol.KeyPhaseZero
+ // KeyPhaseOne is key phase bit 1
+ KeyPhaseOne KeyPhaseBit = protocol.KeyPhaseOne
+)
+
+const (
+ // PerspectiveServer is used for a QUIC server
+ PerspectiveServer Perspective = protocol.PerspectiveServer
+ // PerspectiveClient is used for a QUIC client
+ PerspectiveClient Perspective = protocol.PerspectiveClient
+)
+
+const (
+ // EncryptionInitial is the Initial encryption level
+ EncryptionInitial EncryptionLevel = protocol.EncryptionInitial
+ // EncryptionHandshake is the Handshake encryption level
+ EncryptionHandshake EncryptionLevel = protocol.EncryptionHandshake
+ // Encryption1RTT is the 1-RTT encryption level
+ Encryption1RTT EncryptionLevel = protocol.Encryption1RTT
+ // Encryption0RTT is the 0-RTT encryption level
+ Encryption0RTT EncryptionLevel = protocol.Encryption0RTT
+)
+
+const (
+ // StreamTypeUni is a unidirectional stream
+ StreamTypeUni = protocol.StreamTypeUni
+ // StreamTypeBidi is a bidirectional stream
+ StreamTypeBidi = protocol.StreamTypeBidi
+)
+
+// The ShortHeader is the QUIC Short Header packet header, after removing header protection.
+type ShortHeader struct {
+ DestConnectionID ConnectionID
+ PacketNumber PacketNumber
+ PacketNumberLen protocol.PacketNumberLen
+ KeyPhase KeyPhaseBit
+}
+
+// A Tracer traces events.
+type Tracer interface {
+ // TracerForConnection requests a new tracer for a connection.
+ // The ODCID is the original destination connection ID:
+ // The destination connection ID that the client used on the first Initial packet it sent on this connection.
+ // If nil is returned, tracing will be disabled for this connection.
+ TracerForConnection(ctx context.Context, p Perspective, odcid ConnectionID) ConnectionTracer
+
+ SentPacket(net.Addr, *Header, ByteCount, []Frame)
+ SentVersionNegotiationPacket(_ net.Addr, dest, src ArbitraryLenConnectionID, _ []VersionNumber)
+ DroppedPacket(net.Addr, PacketType, ByteCount, PacketDropReason)
+}
+
+// A ConnectionTracer records events.
+type ConnectionTracer interface {
+ StartedConnection(local, remote net.Addr, srcConnID, destConnID ConnectionID)
+ NegotiatedVersion(chosen VersionNumber, clientVersions, serverVersions []VersionNumber)
+ ClosedConnection(error)
+ SentTransportParameters(*TransportParameters)
+ ReceivedTransportParameters(*TransportParameters)
+ RestoredTransportParameters(parameters *TransportParameters) // for 0-RTT
+ SentLongHeaderPacket(hdr *ExtendedHeader, size ByteCount, ack *AckFrame, frames []Frame)
+ SentShortHeaderPacket(hdr *ShortHeader, size ByteCount, ack *AckFrame, frames []Frame)
+ ReceivedVersionNegotiationPacket(dest, src ArbitraryLenConnectionID, _ []VersionNumber)
+ ReceivedRetry(*Header)
+ ReceivedLongHeaderPacket(hdr *ExtendedHeader, size ByteCount, frames []Frame)
+ ReceivedShortHeaderPacket(hdr *ShortHeader, size ByteCount, frames []Frame)
+ BufferedPacket(PacketType, ByteCount)
+ DroppedPacket(PacketType, ByteCount, PacketDropReason)
+ UpdatedMetrics(rttStats *RTTStats, cwnd, bytesInFlight ByteCount, packetsInFlight int)
+ AcknowledgedPacket(EncryptionLevel, PacketNumber)
+ LostPacket(EncryptionLevel, PacketNumber, PacketLossReason)
+ UpdatedCongestionState(CongestionState)
+ UpdatedPTOCount(value uint32)
+ UpdatedKeyFromTLS(EncryptionLevel, Perspective)
+ UpdatedKey(generation KeyPhase, remote bool)
+ DroppedEncryptionLevel(EncryptionLevel)
+ DroppedKey(generation KeyPhase)
+ SetLossTimer(TimerType, EncryptionLevel, time.Time)
+ LossTimerExpired(TimerType, EncryptionLevel)
+ LossTimerCanceled()
+ // Close is called when the connection is closed.
+ Close()
+ Debug(name, msg string)
+}
diff --git a/vendor/github.com/quic-go/quic-go/logging/mockgen.go b/vendor/github.com/quic-go/quic-go/logging/mockgen.go
new file mode 100644
index 0000000000..d509167996
--- /dev/null
+++ b/vendor/github.com/quic-go/quic-go/logging/mockgen.go
@@ -0,0 +1,4 @@
+package logging
+
+//go:generate sh -c "go run github.com/golang/mock/mockgen -package logging -self_package github.com/quic-go/quic-go/logging -destination mock_connection_tracer_test.go github.com/quic-go/quic-go/logging ConnectionTracer"
+//go:generate sh -c "go run github.com/golang/mock/mockgen -package logging -self_package github.com/quic-go/quic-go/logging -destination mock_tracer_test.go github.com/quic-go/quic-go/logging Tracer"
diff --git a/vendor/github.com/quic-go/quic-go/logging/multiplex.go b/vendor/github.com/quic-go/quic-go/logging/multiplex.go
new file mode 100644
index 0000000000..8e85db494a
--- /dev/null
+++ b/vendor/github.com/quic-go/quic-go/logging/multiplex.go
@@ -0,0 +1,237 @@
+package logging
+
+import (
+ "context"
+ "net"
+ "time"
+)
+
+type tracerMultiplexer struct {
+ tracers []Tracer
+}
+
+var _ Tracer = &tracerMultiplexer{}
+
+// NewMultiplexedTracer creates a new tracer that multiplexes events to multiple tracers.
+func NewMultiplexedTracer(tracers ...Tracer) Tracer {
+ if len(tracers) == 0 {
+ return nil
+ }
+ if len(tracers) == 1 {
+ return tracers[0]
+ }
+ return &tracerMultiplexer{tracers}
+}
+
+func (m *tracerMultiplexer) TracerForConnection(ctx context.Context, p Perspective, odcid ConnectionID) ConnectionTracer {
+ var connTracers []ConnectionTracer
+ for _, t := range m.tracers {
+ if ct := t.TracerForConnection(ctx, p, odcid); ct != nil {
+ connTracers = append(connTracers, ct)
+ }
+ }
+ return NewMultiplexedConnectionTracer(connTracers...)
+}
+
+func (m *tracerMultiplexer) SentPacket(remote net.Addr, hdr *Header, size ByteCount, frames []Frame) {
+ for _, t := range m.tracers {
+ t.SentPacket(remote, hdr, size, frames)
+ }
+}
+
+func (m *tracerMultiplexer) SentVersionNegotiationPacket(remote net.Addr, dest, src ArbitraryLenConnectionID, versions []VersionNumber) {
+ for _, t := range m.tracers {
+ t.SentVersionNegotiationPacket(remote, dest, src, versions)
+ }
+}
+
+func (m *tracerMultiplexer) DroppedPacket(remote net.Addr, typ PacketType, size ByteCount, reason PacketDropReason) {
+ for _, t := range m.tracers {
+ t.DroppedPacket(remote, typ, size, reason)
+ }
+}
+
+type connTracerMultiplexer struct {
+ tracers []ConnectionTracer
+}
+
+var _ ConnectionTracer = &connTracerMultiplexer{}
+
+// NewMultiplexedConnectionTracer creates a new connection tracer that multiplexes events to multiple tracers.
+func NewMultiplexedConnectionTracer(tracers ...ConnectionTracer) ConnectionTracer {
+ if len(tracers) == 0 {
+ return nil
+ }
+ if len(tracers) == 1 {
+ return tracers[0]
+ }
+ return &connTracerMultiplexer{tracers: tracers}
+}
+
+func (m *connTracerMultiplexer) StartedConnection(local, remote net.Addr, srcConnID, destConnID ConnectionID) {
+ for _, t := range m.tracers {
+ t.StartedConnection(local, remote, srcConnID, destConnID)
+ }
+}
+
+func (m *connTracerMultiplexer) NegotiatedVersion(chosen VersionNumber, clientVersions, serverVersions []VersionNumber) {
+ for _, t := range m.tracers {
+ t.NegotiatedVersion(chosen, clientVersions, serverVersions)
+ }
+}
+
+func (m *connTracerMultiplexer) ClosedConnection(e error) {
+ for _, t := range m.tracers {
+ t.ClosedConnection(e)
+ }
+}
+
+func (m *connTracerMultiplexer) SentTransportParameters(tp *TransportParameters) {
+ for _, t := range m.tracers {
+ t.SentTransportParameters(tp)
+ }
+}
+
+func (m *connTracerMultiplexer) ReceivedTransportParameters(tp *TransportParameters) {
+ for _, t := range m.tracers {
+ t.ReceivedTransportParameters(tp)
+ }
+}
+
+func (m *connTracerMultiplexer) RestoredTransportParameters(tp *TransportParameters) {
+ for _, t := range m.tracers {
+ t.RestoredTransportParameters(tp)
+ }
+}
+
+func (m *connTracerMultiplexer) SentLongHeaderPacket(hdr *ExtendedHeader, size ByteCount, ack *AckFrame, frames []Frame) {
+ for _, t := range m.tracers {
+ t.SentLongHeaderPacket(hdr, size, ack, frames)
+ }
+}
+
+func (m *connTracerMultiplexer) SentShortHeaderPacket(hdr *ShortHeader, size ByteCount, ack *AckFrame, frames []Frame) {
+ for _, t := range m.tracers {
+ t.SentShortHeaderPacket(hdr, size, ack, frames)
+ }
+}
+
+func (m *connTracerMultiplexer) ReceivedVersionNegotiationPacket(dest, src ArbitraryLenConnectionID, versions []VersionNumber) {
+ for _, t := range m.tracers {
+ t.ReceivedVersionNegotiationPacket(dest, src, versions)
+ }
+}
+
+func (m *connTracerMultiplexer) ReceivedRetry(hdr *Header) {
+ for _, t := range m.tracers {
+ t.ReceivedRetry(hdr)
+ }
+}
+
+func (m *connTracerMultiplexer) ReceivedLongHeaderPacket(hdr *ExtendedHeader, size ByteCount, frames []Frame) {
+ for _, t := range m.tracers {
+ t.ReceivedLongHeaderPacket(hdr, size, frames)
+ }
+}
+
+func (m *connTracerMultiplexer) ReceivedShortHeaderPacket(hdr *ShortHeader, size ByteCount, frames []Frame) {
+ for _, t := range m.tracers {
+ t.ReceivedShortHeaderPacket(hdr, size, frames)
+ }
+}
+
+func (m *connTracerMultiplexer) BufferedPacket(typ PacketType, size ByteCount) {
+ for _, t := range m.tracers {
+ t.BufferedPacket(typ, size)
+ }
+}
+
+func (m *connTracerMultiplexer) DroppedPacket(typ PacketType, size ByteCount, reason PacketDropReason) {
+ for _, t := range m.tracers {
+ t.DroppedPacket(typ, size, reason)
+ }
+}
+
+func (m *connTracerMultiplexer) UpdatedCongestionState(state CongestionState) {
+ for _, t := range m.tracers {
+ t.UpdatedCongestionState(state)
+ }
+}
+
+func (m *connTracerMultiplexer) UpdatedMetrics(rttStats *RTTStats, cwnd, bytesInFLight ByteCount, packetsInFlight int) {
+ for _, t := range m.tracers {
+ t.UpdatedMetrics(rttStats, cwnd, bytesInFLight, packetsInFlight)
+ }
+}
+
+func (m *connTracerMultiplexer) AcknowledgedPacket(encLevel EncryptionLevel, pn PacketNumber) {
+ for _, t := range m.tracers {
+ t.AcknowledgedPacket(encLevel, pn)
+ }
+}
+
+func (m *connTracerMultiplexer) LostPacket(encLevel EncryptionLevel, pn PacketNumber, reason PacketLossReason) {
+ for _, t := range m.tracers {
+ t.LostPacket(encLevel, pn, reason)
+ }
+}
+
+func (m *connTracerMultiplexer) UpdatedPTOCount(value uint32) {
+ for _, t := range m.tracers {
+ t.UpdatedPTOCount(value)
+ }
+}
+
+func (m *connTracerMultiplexer) UpdatedKeyFromTLS(encLevel EncryptionLevel, perspective Perspective) {
+ for _, t := range m.tracers {
+ t.UpdatedKeyFromTLS(encLevel, perspective)
+ }
+}
+
+func (m *connTracerMultiplexer) UpdatedKey(generation KeyPhase, remote bool) {
+ for _, t := range m.tracers {
+ t.UpdatedKey(generation, remote)
+ }
+}
+
+func (m *connTracerMultiplexer) DroppedEncryptionLevel(encLevel EncryptionLevel) {
+ for _, t := range m.tracers {
+ t.DroppedEncryptionLevel(encLevel)
+ }
+}
+
+func (m *connTracerMultiplexer) DroppedKey(generation KeyPhase) {
+ for _, t := range m.tracers {
+ t.DroppedKey(generation)
+ }
+}
+
+func (m *connTracerMultiplexer) SetLossTimer(typ TimerType, encLevel EncryptionLevel, exp time.Time) {
+ for _, t := range m.tracers {
+ t.SetLossTimer(typ, encLevel, exp)
+ }
+}
+
+func (m *connTracerMultiplexer) LossTimerExpired(typ TimerType, encLevel EncryptionLevel) {
+ for _, t := range m.tracers {
+ t.LossTimerExpired(typ, encLevel)
+ }
+}
+
+func (m *connTracerMultiplexer) LossTimerCanceled() {
+ for _, t := range m.tracers {
+ t.LossTimerCanceled()
+ }
+}
+
+func (m *connTracerMultiplexer) Debug(name, msg string) {
+ for _, t := range m.tracers {
+ t.Debug(name, msg)
+ }
+}
+
+func (m *connTracerMultiplexer) Close() {
+ for _, t := range m.tracers {
+ t.Close()
+ }
+}
diff --git a/vendor/github.com/quic-go/quic-go/logging/null_tracer.go b/vendor/github.com/quic-go/quic-go/logging/null_tracer.go
new file mode 100644
index 0000000000..38052ae3b3
--- /dev/null
+++ b/vendor/github.com/quic-go/quic-go/logging/null_tracer.go
@@ -0,0 +1,62 @@
+package logging
+
+import (
+ "context"
+ "net"
+ "time"
+)
+
+// The NullTracer is a Tracer that does nothing.
+// It is useful for embedding.
+type NullTracer struct{}
+
+var _ Tracer = &NullTracer{}
+
+func (n NullTracer) TracerForConnection(context.Context, Perspective, ConnectionID) ConnectionTracer {
+ return NullConnectionTracer{}
+}
+func (n NullTracer) SentPacket(net.Addr, *Header, ByteCount, []Frame) {}
+func (n NullTracer) SentVersionNegotiationPacket(_ net.Addr, dest, src ArbitraryLenConnectionID, _ []VersionNumber) {
+}
+func (n NullTracer) DroppedPacket(net.Addr, PacketType, ByteCount, PacketDropReason) {}
+
+// The NullConnectionTracer is a ConnectionTracer that does nothing.
+// It is useful for embedding.
+type NullConnectionTracer struct{}
+
+var _ ConnectionTracer = &NullConnectionTracer{}
+
+func (n NullConnectionTracer) StartedConnection(local, remote net.Addr, srcConnID, destConnID ConnectionID) {
+}
+
+func (n NullConnectionTracer) NegotiatedVersion(chosen VersionNumber, clientVersions, serverVersions []VersionNumber) {
+}
+func (n NullConnectionTracer) ClosedConnection(err error) {}
+func (n NullConnectionTracer) SentTransportParameters(*TransportParameters) {}
+func (n NullConnectionTracer) ReceivedTransportParameters(*TransportParameters) {}
+func (n NullConnectionTracer) RestoredTransportParameters(*TransportParameters) {}
+func (n NullConnectionTracer) SentLongHeaderPacket(*ExtendedHeader, ByteCount, *AckFrame, []Frame) {}
+func (n NullConnectionTracer) SentShortHeaderPacket(*ShortHeader, ByteCount, *AckFrame, []Frame) {}
+func (n NullConnectionTracer) ReceivedVersionNegotiationPacket(dest, src ArbitraryLenConnectionID, _ []VersionNumber) {
+}
+func (n NullConnectionTracer) ReceivedRetry(*Header) {}
+func (n NullConnectionTracer) ReceivedLongHeaderPacket(*ExtendedHeader, ByteCount, []Frame) {}
+func (n NullConnectionTracer) ReceivedShortHeaderPacket(*ShortHeader, ByteCount, []Frame) {}
+func (n NullConnectionTracer) BufferedPacket(PacketType, ByteCount) {}
+func (n NullConnectionTracer) DroppedPacket(PacketType, ByteCount, PacketDropReason) {}
+
+func (n NullConnectionTracer) UpdatedMetrics(rttStats *RTTStats, cwnd, bytesInFlight ByteCount, packetsInFlight int) {
+}
+func (n NullConnectionTracer) AcknowledgedPacket(EncryptionLevel, PacketNumber) {}
+func (n NullConnectionTracer) LostPacket(EncryptionLevel, PacketNumber, PacketLossReason) {}
+func (n NullConnectionTracer) UpdatedCongestionState(CongestionState) {}
+func (n NullConnectionTracer) UpdatedPTOCount(uint32) {}
+func (n NullConnectionTracer) UpdatedKeyFromTLS(EncryptionLevel, Perspective) {}
+func (n NullConnectionTracer) UpdatedKey(keyPhase KeyPhase, remote bool) {}
+func (n NullConnectionTracer) DroppedEncryptionLevel(EncryptionLevel) {}
+func (n NullConnectionTracer) DroppedKey(KeyPhase) {}
+func (n NullConnectionTracer) SetLossTimer(TimerType, EncryptionLevel, time.Time) {}
+func (n NullConnectionTracer) LossTimerExpired(timerType TimerType, level EncryptionLevel) {}
+func (n NullConnectionTracer) LossTimerCanceled() {}
+func (n NullConnectionTracer) Close() {}
+func (n NullConnectionTracer) Debug(name, msg string) {}
diff --git a/vendor/github.com/quic-go/quic-go/logging/packet_header.go b/vendor/github.com/quic-go/quic-go/logging/packet_header.go
new file mode 100644
index 0000000000..6b8df58d8a
--- /dev/null
+++ b/vendor/github.com/quic-go/quic-go/logging/packet_header.go
@@ -0,0 +1,24 @@
+package logging
+
+import (
+ "github.com/quic-go/quic-go/internal/protocol"
+)
+
+// PacketTypeFromHeader determines the packet type from a *wire.Header.
+func PacketTypeFromHeader(hdr *Header) PacketType {
+ if hdr.Version == 0 {
+ return PacketTypeVersionNegotiation
+ }
+ switch hdr.Type {
+ case protocol.PacketTypeInitial:
+ return PacketTypeInitial
+ case protocol.PacketTypeHandshake:
+ return PacketTypeHandshake
+ case protocol.PacketType0RTT:
+ return PacketType0RTT
+ case protocol.PacketTypeRetry:
+ return PacketTypeRetry
+ default:
+ return PacketTypeNotDetermined
+ }
+}
diff --git a/vendor/github.com/quic-go/quic-go/logging/types.go b/vendor/github.com/quic-go/quic-go/logging/types.go
new file mode 100644
index 0000000000..ad80069235
--- /dev/null
+++ b/vendor/github.com/quic-go/quic-go/logging/types.go
@@ -0,0 +1,94 @@
+package logging
+
+// PacketType is the packet type of a QUIC packet
+type PacketType uint8
+
+const (
+ // PacketTypeInitial is the packet type of an Initial packet
+ PacketTypeInitial PacketType = iota
+ // PacketTypeHandshake is the packet type of a Handshake packet
+ PacketTypeHandshake
+ // PacketTypeRetry is the packet type of a Retry packet
+ PacketTypeRetry
+ // PacketType0RTT is the packet type of a 0-RTT packet
+ PacketType0RTT
+ // PacketTypeVersionNegotiation is the packet type of a Version Negotiation packet
+ PacketTypeVersionNegotiation
+ // PacketType1RTT is a 1-RTT packet
+ PacketType1RTT
+ // PacketTypeStatelessReset is a stateless reset
+ PacketTypeStatelessReset
+ // PacketTypeNotDetermined is the packet type when it could not be determined
+ PacketTypeNotDetermined
+)
+
+type PacketLossReason uint8
+
+const (
+ // PacketLossReorderingThreshold: when a packet is deemed lost due to reordering threshold
+ PacketLossReorderingThreshold PacketLossReason = iota
+ // PacketLossTimeThreshold: when a packet is deemed lost due to time threshold
+ PacketLossTimeThreshold
+)
+
+type PacketDropReason uint8
+
+const (
+ // PacketDropKeyUnavailable is used when a packet is dropped because keys are unavailable
+ PacketDropKeyUnavailable PacketDropReason = iota
+ // PacketDropUnknownConnectionID is used when a packet is dropped because the connection ID is unknown
+ PacketDropUnknownConnectionID
+ // PacketDropHeaderParseError is used when a packet is dropped because header parsing failed
+ PacketDropHeaderParseError
+ // PacketDropPayloadDecryptError is used when a packet is dropped because decrypting the payload failed
+ PacketDropPayloadDecryptError
+ // PacketDropProtocolViolation is used when a packet is dropped due to a protocol violation
+ PacketDropProtocolViolation
+ // PacketDropDOSPrevention is used when a packet is dropped to mitigate a DoS attack
+ PacketDropDOSPrevention
+ // PacketDropUnsupportedVersion is used when a packet is dropped because the version is not supported
+ PacketDropUnsupportedVersion
+ // PacketDropUnexpectedPacket is used when an unexpected packet is received
+ PacketDropUnexpectedPacket
+ // PacketDropUnexpectedSourceConnectionID is used when a packet with an unexpected source connection ID is received
+ PacketDropUnexpectedSourceConnectionID
+ // PacketDropUnexpectedVersion is used when a packet with an unexpected version is received
+ PacketDropUnexpectedVersion
+ // PacketDropDuplicate is used when a duplicate packet is received
+ PacketDropDuplicate
+)
+
+// TimerType is the type of the loss detection timer
+type TimerType uint8
+
+const (
+ // TimerTypeACK is the timer type for the early retransmit timer
+ TimerTypeACK TimerType = iota
+ // TimerTypePTO is the timer type for the PTO retransmit timer
+ TimerTypePTO
+)
+
+// TimeoutReason is the reason why a connection is closed
+type TimeoutReason uint8
+
+const (
+ // TimeoutReasonHandshake is used when the connection is closed due to a handshake timeout
+ // This reason is not defined in the qlog draft, but very useful for debugging.
+ TimeoutReasonHandshake TimeoutReason = iota
+ // TimeoutReasonIdle is used when the connection is closed due to an idle timeout
+ // This reason is not defined in the qlog draft, but very useful for debugging.
+ TimeoutReasonIdle
+)
+
+type CongestionState uint8
+
+const (
+ // CongestionStateSlowStart is the slow start phase of Reno / Cubic
+ CongestionStateSlowStart CongestionState = iota
+ // CongestionStateCongestionAvoidance is the slow start phase of Reno / Cubic
+ CongestionStateCongestionAvoidance
+ // CongestionStateRecovery is the recovery phase of Reno / Cubic
+ CongestionStateRecovery
+ // CongestionStateApplicationLimited means that the congestion controller is application limited
+ CongestionStateApplicationLimited
+)
diff --git a/vendor/github.com/quic-go/quic-go/mockgen.go b/vendor/github.com/quic-go/quic-go/mockgen.go
new file mode 100644
index 0000000000..abe1faabc9
--- /dev/null
+++ b/vendor/github.com/quic-go/quic-go/mockgen.go
@@ -0,0 +1,27 @@
+package quic
+
+//go:generate sh -c "./mockgen_private.sh quic mock_send_conn_test.go github.com/quic-go/quic-go sendConn"
+//go:generate sh -c "./mockgen_private.sh quic mock_sender_test.go github.com/quic-go/quic-go sender"
+//go:generate sh -c "./mockgen_private.sh quic mock_stream_internal_test.go github.com/quic-go/quic-go streamI"
+//go:generate sh -c "./mockgen_private.sh quic mock_crypto_stream_test.go github.com/quic-go/quic-go cryptoStream"
+//go:generate sh -c "./mockgen_private.sh quic mock_receive_stream_internal_test.go github.com/quic-go/quic-go receiveStreamI"
+//go:generate sh -c "./mockgen_private.sh quic mock_send_stream_internal_test.go github.com/quic-go/quic-go sendStreamI"
+//go:generate sh -c "./mockgen_private.sh quic mock_stream_sender_test.go github.com/quic-go/quic-go streamSender"
+//go:generate sh -c "./mockgen_private.sh quic mock_stream_getter_test.go github.com/quic-go/quic-go streamGetter"
+//go:generate sh -c "./mockgen_private.sh quic mock_crypto_data_handler_test.go github.com/quic-go/quic-go cryptoDataHandler"
+//go:generate sh -c "./mockgen_private.sh quic mock_frame_source_test.go github.com/quic-go/quic-go frameSource"
+//go:generate sh -c "./mockgen_private.sh quic mock_ack_frame_source_test.go github.com/quic-go/quic-go ackFrameSource"
+//go:generate sh -c "./mockgen_private.sh quic mock_stream_manager_test.go github.com/quic-go/quic-go streamManager"
+//go:generate sh -c "./mockgen_private.sh quic mock_sealing_manager_test.go github.com/quic-go/quic-go sealingManager"
+//go:generate sh -c "./mockgen_private.sh quic mock_unpacker_test.go github.com/quic-go/quic-go unpacker"
+//go:generate sh -c "./mockgen_private.sh quic mock_packer_test.go github.com/quic-go/quic-go packer"
+//go:generate sh -c "./mockgen_private.sh quic mock_mtu_discoverer_test.go github.com/quic-go/quic-go mtuDiscoverer"
+//go:generate sh -c "./mockgen_private.sh quic mock_conn_runner_test.go github.com/quic-go/quic-go connRunner"
+//go:generate sh -c "./mockgen_private.sh quic mock_quic_conn_test.go github.com/quic-go/quic-go quicConn"
+//go:generate sh -c "./mockgen_private.sh quic mock_packet_handler_test.go github.com/quic-go/quic-go packetHandler"
+//go:generate sh -c "./mockgen_private.sh quic mock_unknown_packet_handler_test.go github.com/quic-go/quic-go unknownPacketHandler"
+//go:generate sh -c "./mockgen_private.sh quic mock_packet_handler_manager_test.go github.com/quic-go/quic-go packetHandlerManager"
+//go:generate sh -c "./mockgen_private.sh quic mock_multiplexer_test.go github.com/quic-go/quic-go multiplexer"
+//go:generate sh -c "./mockgen_private.sh quic mock_batch_conn_test.go github.com/quic-go/quic-go batchConn"
+//go:generate sh -c "go run github.com/golang/mock/mockgen -package quic -self_package github.com/quic-go/quic-go -destination mock_token_store_test.go github.com/quic-go/quic-go TokenStore"
+//go:generate sh -c "go run github.com/golang/mock/mockgen -package quic -self_package github.com/quic-go/quic-go -destination mock_packetconn_test.go net PacketConn"
diff --git a/vendor/github.com/quic-go/quic-go/mockgen_private.sh b/vendor/github.com/quic-go/quic-go/mockgen_private.sh
new file mode 100644
index 0000000000..79f63eee3e
--- /dev/null
+++ b/vendor/github.com/quic-go/quic-go/mockgen_private.sh
@@ -0,0 +1,49 @@
+#!/bin/bash
+
+DEST=$2
+PACKAGE=$3
+TMPFILE="mockgen_tmp.go"
+# uppercase the name of the interface
+ORIG_INTERFACE_NAME=$4
+INTERFACE_NAME="$(tr '[:lower:]' '[:upper:]' <<< ${ORIG_INTERFACE_NAME:0:1})${ORIG_INTERFACE_NAME:1}"
+
+# Gather all files that contain interface definitions.
+# These interfaces might be used as embedded interfaces,
+# so we need to pass them to mockgen as aux_files.
+AUX=()
+for f in *.go; do
+ if [[ -z ${f##*_test.go} ]]; then
+ # skip test files
+ continue;
+ fi
+ if $(egrep -qe "type (.*) interface" $f); then
+ AUX+=("github.com/quic-go/quic-go=$f")
+ fi
+done
+
+# Find the file that defines the interface we're mocking.
+for f in *.go; do
+ if [[ -z ${f##*_test.go} ]]; then
+ # skip test files
+ continue;
+ fi
+ INTERFACE=$(sed -n "/^type $ORIG_INTERFACE_NAME interface/,/^}/p" $f)
+ if [[ -n "$INTERFACE" ]]; then
+ SRC=$f
+ break
+ fi
+done
+
+if [[ -z "$INTERFACE" ]]; then
+ echo "Interface $ORIG_INTERFACE_NAME not found."
+ exit 1
+fi
+
+AUX_FILES=$(IFS=, ; echo "${AUX[*]}")
+
+## create a public alias for the interface, so that mockgen can process it
+echo -e "package $1\n" > $TMPFILE
+echo "$INTERFACE" | sed "s/$ORIG_INTERFACE_NAME/$INTERFACE_NAME/" >> $TMPFILE
+go run github.com/golang/mock/mockgen -package $1 -self_package $3 -destination $DEST -source=$TMPFILE -aux_files $AUX_FILES
+sed "s/$TMPFILE/$SRC/" "$DEST" > "$DEST.new" && mv "$DEST.new" "$DEST"
+rm "$TMPFILE"
diff --git a/vendor/github.com/quic-go/quic-go/mtu_discoverer.go b/vendor/github.com/quic-go/quic-go/mtu_discoverer.go
new file mode 100644
index 0000000000..5a8484c76b
--- /dev/null
+++ b/vendor/github.com/quic-go/quic-go/mtu_discoverer.go
@@ -0,0 +1,74 @@
+package quic
+
+import (
+ "time"
+
+ "github.com/quic-go/quic-go/internal/ackhandler"
+ "github.com/quic-go/quic-go/internal/protocol"
+ "github.com/quic-go/quic-go/internal/utils"
+ "github.com/quic-go/quic-go/internal/wire"
+)
+
+type mtuDiscoverer interface {
+ ShouldSendProbe(now time.Time) bool
+ GetPing() (ping ackhandler.Frame, datagramSize protocol.ByteCount)
+}
+
+const (
+ // At some point, we have to stop searching for a higher MTU.
+ // We're happy to send a packet that's 10 bytes smaller than the actual MTU.
+ maxMTUDiff = 20
+ // send a probe packet every mtuProbeDelay RTTs
+ mtuProbeDelay = 5
+)
+
+type mtuFinder struct {
+ lastProbeTime time.Time
+ probeInFlight bool
+ mtuIncreased func(protocol.ByteCount)
+
+ rttStats *utils.RTTStats
+ current protocol.ByteCount
+ max protocol.ByteCount // the maximum value, as advertised by the peer (or our maximum size buffer)
+}
+
+var _ mtuDiscoverer = &mtuFinder{}
+
+func newMTUDiscoverer(rttStats *utils.RTTStats, start, max protocol.ByteCount, mtuIncreased func(protocol.ByteCount)) mtuDiscoverer {
+ return &mtuFinder{
+ current: start,
+ rttStats: rttStats,
+ lastProbeTime: time.Now(), // to make sure the first probe packet is not sent immediately
+ mtuIncreased: mtuIncreased,
+ max: max,
+ }
+}
+
+func (f *mtuFinder) done() bool {
+ return f.max-f.current <= maxMTUDiff+1
+}
+
+func (f *mtuFinder) ShouldSendProbe(now time.Time) bool {
+ if f.probeInFlight || f.done() {
+ return false
+ }
+ return !now.Before(f.lastProbeTime.Add(mtuProbeDelay * f.rttStats.SmoothedRTT()))
+}
+
+func (f *mtuFinder) GetPing() (ackhandler.Frame, protocol.ByteCount) {
+ size := (f.max + f.current) / 2
+ f.lastProbeTime = time.Now()
+ f.probeInFlight = true
+ return ackhandler.Frame{
+ Frame: &wire.PingFrame{},
+ OnLost: func(wire.Frame) {
+ f.probeInFlight = false
+ f.max = size
+ },
+ OnAcked: func(wire.Frame) {
+ f.probeInFlight = false
+ f.current = size
+ f.mtuIncreased(size)
+ },
+ }, size
+}
diff --git a/vendor/github.com/quic-go/quic-go/multiplexer.go b/vendor/github.com/quic-go/quic-go/multiplexer.go
new file mode 100644
index 0000000000..37d4e75cf2
--- /dev/null
+++ b/vendor/github.com/quic-go/quic-go/multiplexer.go
@@ -0,0 +1,106 @@
+package quic
+
+import (
+ "fmt"
+ "net"
+ "sync"
+
+ "github.com/quic-go/quic-go/internal/utils"
+ "github.com/quic-go/quic-go/logging"
+)
+
+var (
+ connMuxerOnce sync.Once
+ connMuxer multiplexer
+)
+
+type indexableConn interface {
+ LocalAddr() net.Addr
+}
+
+type multiplexer interface {
+ AddConn(c net.PacketConn, connIDLen int, statelessResetKey *StatelessResetKey, tracer logging.Tracer) (packetHandlerManager, error)
+ RemoveConn(indexableConn) error
+}
+
+type connManager struct {
+ connIDLen int
+ statelessResetKey *StatelessResetKey
+ tracer logging.Tracer
+ manager packetHandlerManager
+}
+
+// The connMultiplexer listens on multiple net.PacketConns and dispatches
+// incoming packets to the connection handler.
+type connMultiplexer struct {
+ mutex sync.Mutex
+
+ conns map[string] /* LocalAddr().String() */ connManager
+ newPacketHandlerManager func(net.PacketConn, int, *StatelessResetKey, logging.Tracer, utils.Logger) (packetHandlerManager, error) // so it can be replaced in the tests
+
+ logger utils.Logger
+}
+
+var _ multiplexer = &connMultiplexer{}
+
+func getMultiplexer() multiplexer {
+ connMuxerOnce.Do(func() {
+ connMuxer = &connMultiplexer{
+ conns: make(map[string]connManager),
+ logger: utils.DefaultLogger.WithPrefix("muxer"),
+ newPacketHandlerManager: newPacketHandlerMap,
+ }
+ })
+ return connMuxer
+}
+
+func (m *connMultiplexer) AddConn(
+ c net.PacketConn,
+ connIDLen int,
+ statelessResetKey *StatelessResetKey,
+ tracer logging.Tracer,
+) (packetHandlerManager, error) {
+ m.mutex.Lock()
+ defer m.mutex.Unlock()
+
+ addr := c.LocalAddr()
+ connIndex := addr.Network() + " " + addr.String()
+ p, ok := m.conns[connIndex]
+ if !ok {
+ manager, err := m.newPacketHandlerManager(c, connIDLen, statelessResetKey, tracer, m.logger)
+ if err != nil {
+ return nil, err
+ }
+ p = connManager{
+ connIDLen: connIDLen,
+ statelessResetKey: statelessResetKey,
+ manager: manager,
+ tracer: tracer,
+ }
+ m.conns[connIndex] = p
+ } else {
+ if p.connIDLen != connIDLen {
+ return nil, fmt.Errorf("cannot use %d byte connection IDs on a connection that is already using %d byte connction IDs", connIDLen, p.connIDLen)
+ }
+ if statelessResetKey != nil && p.statelessResetKey != statelessResetKey {
+ return nil, fmt.Errorf("cannot use different stateless reset keys on the same packet conn")
+ }
+ if tracer != p.tracer {
+ return nil, fmt.Errorf("cannot use different tracers on the same packet conn")
+ }
+ }
+ return p.manager, nil
+}
+
+func (m *connMultiplexer) RemoveConn(c indexableConn) error {
+ m.mutex.Lock()
+ defer m.mutex.Unlock()
+
+ connIndex := c.LocalAddr().Network() + " " + c.LocalAddr().String()
+ if _, ok := m.conns[connIndex]; !ok {
+ return fmt.Errorf("cannote remove connection, connection is unknown")
+ }
+
+ delete(m.conns, connIndex)
+ return nil
+}
diff --git a/vendor/github.com/quic-go/quic-go/packet_handler_map.go b/vendor/github.com/quic-go/quic-go/packet_handler_map.go
new file mode 100644
index 0000000000..e2bc913ca9
--- /dev/null
+++ b/vendor/github.com/quic-go/quic-go/packet_handler_map.go
@@ -0,0 +1,505 @@
+package quic
+
+import (
+ "crypto/hmac"
+ "crypto/rand"
+ "crypto/sha256"
+ "errors"
+ "fmt"
+ "hash"
+ "io"
+ "log"
+ "net"
+ "os"
+ "strconv"
+ "strings"
+ "sync"
+ "time"
+
+ "github.com/quic-go/quic-go/internal/protocol"
+ "github.com/quic-go/quic-go/internal/utils"
+ "github.com/quic-go/quic-go/internal/wire"
+ "github.com/quic-go/quic-go/logging"
+)
+
+// rawConn is a connection that allow reading of a receivedPacket.
+type rawConn interface {
+ ReadPacket() (*receivedPacket, error)
+ WritePacket(b []byte, addr net.Addr, oob []byte) (int, error)
+ LocalAddr() net.Addr
+ io.Closer
+}
+
+type closePacket struct {
+ payload []byte
+ addr net.Addr
+ info *packetInfo
+}
+
+// The packetHandlerMap stores packetHandlers, identified by connection ID.
+// It is used:
+// * by the server to store connections
+// * when multiplexing outgoing connections to store clients
+type packetHandlerMap struct {
+ mutex sync.Mutex
+
+ conn rawConn
+ connIDLen int
+
+ closeQueue chan closePacket
+
+ handlers map[protocol.ConnectionID]packetHandler
+ resetTokens map[protocol.StatelessResetToken] /* stateless reset token */ packetHandler
+ server unknownPacketHandler
+ numZeroRTTEntries int
+
+ listening chan struct{} // is closed when listen returns
+ closed bool
+
+ deleteRetiredConnsAfter time.Duration
+ zeroRTTQueueDuration time.Duration
+
+ statelessResetEnabled bool
+ statelessResetMutex sync.Mutex
+ statelessResetHasher hash.Hash
+
+ tracer logging.Tracer
+ logger utils.Logger
+}
+
+var _ packetHandlerManager = &packetHandlerMap{}
+
+func setReceiveBuffer(c net.PacketConn, logger utils.Logger) error {
+ conn, ok := c.(interface{ SetReadBuffer(int) error })
+ if !ok {
+ return errors.New("connection doesn't allow setting of receive buffer size. Not a *net.UDPConn?")
+ }
+ size, err := inspectReadBuffer(c)
+ if err != nil {
+ return fmt.Errorf("failed to determine receive buffer size: %w", err)
+ }
+ if size >= protocol.DesiredReceiveBufferSize {
+ logger.Debugf("Conn has receive buffer of %d kiB (wanted: at least %d kiB)", size/1024, protocol.DesiredReceiveBufferSize/1024)
+ return nil
+ }
+ if err := conn.SetReadBuffer(protocol.DesiredReceiveBufferSize); err != nil {
+ return fmt.Errorf("failed to increase receive buffer size: %w", err)
+ }
+ newSize, err := inspectReadBuffer(c)
+ if err != nil {
+ return fmt.Errorf("failed to determine receive buffer size: %w", err)
+ }
+ if newSize == size {
+ return fmt.Errorf("failed to increase receive buffer size (wanted: %d kiB, got %d kiB)", protocol.DesiredReceiveBufferSize/1024, newSize/1024)
+ }
+ if newSize < protocol.DesiredReceiveBufferSize {
+ return fmt.Errorf("failed to sufficiently increase receive buffer size (was: %d kiB, wanted: %d kiB, got: %d kiB)", size/1024, protocol.DesiredReceiveBufferSize/1024, newSize/1024)
+ }
+ logger.Debugf("Increased receive buffer size to %d kiB", newSize/1024)
+ return nil
+}
+
+// only print warnings about the UDP receive buffer size once
+var receiveBufferWarningOnce sync.Once
+
+func newPacketHandlerMap(
+ c net.PacketConn,
+ connIDLen int,
+ statelessResetKey *StatelessResetKey,
+ tracer logging.Tracer,
+ logger utils.Logger,
+) (packetHandlerManager, error) {
+ if err := setReceiveBuffer(c, logger); err != nil {
+ if !strings.Contains(err.Error(), "use of closed network connection") {
+ receiveBufferWarningOnce.Do(func() {
+ if disable, _ := strconv.ParseBool(os.Getenv("QUIC_GO_DISABLE_RECEIVE_BUFFER_WARNING")); disable {
+ return
+ }
+ log.Printf("%s. See https://github.com/quic-go/quic-go/wiki/UDP-Receive-Buffer-Size for details.", err)
+ })
+ }
+ }
+ conn, err := wrapConn(c)
+ if err != nil {
+ return nil, err
+ }
+ m := &packetHandlerMap{
+ conn: conn,
+ connIDLen: connIDLen,
+ listening: make(chan struct{}),
+ handlers: make(map[protocol.ConnectionID]packetHandler),
+ resetTokens: make(map[protocol.StatelessResetToken]packetHandler),
+ deleteRetiredConnsAfter: protocol.RetiredConnectionIDDeleteTimeout,
+ zeroRTTQueueDuration: protocol.Max0RTTQueueingDuration,
+ closeQueue: make(chan closePacket, 4),
+ statelessResetEnabled: statelessResetKey != nil,
+ tracer: tracer,
+ logger: logger,
+ }
+ if m.statelessResetEnabled {
+ m.statelessResetHasher = hmac.New(sha256.New, statelessResetKey[:])
+ }
+ go m.listen()
+ go m.runCloseQueue()
+
+ if logger.Debug() {
+ go m.logUsage()
+ }
+ return m, nil
+}
+
+func (h *packetHandlerMap) logUsage() {
+ ticker := time.NewTicker(2 * time.Second)
+ var printedZero bool
+ for {
+ select {
+ case <-h.listening:
+ return
+ case <-ticker.C:
+ }
+
+ h.mutex.Lock()
+ numHandlers := len(h.handlers)
+ numTokens := len(h.resetTokens)
+ h.mutex.Unlock()
+ // If the number tracked handlers and tokens is zero, only print it a single time.
+ hasZero := numHandlers == 0 && numTokens == 0
+ if !hasZero || (hasZero && !printedZero) {
+ h.logger.Debugf("Tracking %d connection IDs and %d reset tokens.\n", numHandlers, numTokens)
+ printedZero = false
+ if hasZero {
+ printedZero = true
+ }
+ }
+ }
+}
+
+func (h *packetHandlerMap) Add(id protocol.ConnectionID, handler packetHandler) bool /* was added */ {
+ h.mutex.Lock()
+ defer h.mutex.Unlock()
+
+ if _, ok := h.handlers[id]; ok {
+ h.logger.Debugf("Not adding connection ID %s, as it already exists.", id)
+ return false
+ }
+ h.handlers[id] = handler
+ h.logger.Debugf("Adding connection ID %s.", id)
+ return true
+}
+
+func (h *packetHandlerMap) AddWithConnID(clientDestConnID, newConnID protocol.ConnectionID, fn func() packetHandler) bool {
+ h.mutex.Lock()
+ defer h.mutex.Unlock()
+
+ var q *zeroRTTQueue
+ if handler, ok := h.handlers[clientDestConnID]; ok {
+ q, ok = handler.(*zeroRTTQueue)
+ if !ok {
+ h.logger.Debugf("Not adding connection ID %s for a new connection, as it already exists.", clientDestConnID)
+ return false
+ }
+ q.retireTimer.Stop()
+ h.numZeroRTTEntries--
+ if h.numZeroRTTEntries < 0 {
+ panic("number of 0-RTT queues < 0")
+ }
+ }
+ conn := fn()
+ if q != nil {
+ q.EnqueueAll(conn)
+ }
+ h.handlers[clientDestConnID] = conn
+ h.handlers[newConnID] = conn
+ h.logger.Debugf("Adding connection IDs %s and %s for a new connection.", clientDestConnID, newConnID)
+ return true
+}
+
+func (h *packetHandlerMap) Remove(id protocol.ConnectionID) {
+ h.mutex.Lock()
+ delete(h.handlers, id)
+ h.mutex.Unlock()
+ h.logger.Debugf("Removing connection ID %s.", id)
+}
+
+func (h *packetHandlerMap) Retire(id protocol.ConnectionID) {
+ h.logger.Debugf("Retiring connection ID %s in %s.", id, h.deleteRetiredConnsAfter)
+ time.AfterFunc(h.deleteRetiredConnsAfter, func() {
+ h.mutex.Lock()
+ delete(h.handlers, id)
+ h.mutex.Unlock()
+ h.logger.Debugf("Removing connection ID %s after it has been retired.", id)
+ })
+}
+
+// ReplaceWithClosed is called when a connection is closed.
+// Depending on which side closed the connection, we need to:
+// * remote close: absorb delayed packets
+// * local close: retransmit the CONNECTION_CLOSE packet, in case it was lost
+func (h *packetHandlerMap) ReplaceWithClosed(ids []protocol.ConnectionID, pers protocol.Perspective, connClosePacket []byte) {
+ var handler packetHandler
+ if connClosePacket != nil {
+ handler = newClosedLocalConn(
+ func(addr net.Addr, info *packetInfo) {
+ select {
+ case h.closeQueue <- closePacket{payload: connClosePacket, addr: addr, info: info}:
+ default:
+ // Oops, we're backlogged.
+ // Just drop the packet, sending CONNECTION_CLOSE copies is best effort anyway.
+ }
+ },
+ pers,
+ h.logger,
+ )
+ } else {
+ handler = newClosedRemoteConn(pers)
+ }
+
+ h.mutex.Lock()
+ for _, id := range ids {
+ h.handlers[id] = handler
+ }
+ h.mutex.Unlock()
+ h.logger.Debugf("Replacing connection for connection IDs %s with a closed connection.", ids)
+
+ time.AfterFunc(h.deleteRetiredConnsAfter, func() {
+ h.mutex.Lock()
+ handler.shutdown()
+ for _, id := range ids {
+ delete(h.handlers, id)
+ }
+ h.mutex.Unlock()
+ h.logger.Debugf("Removing connection IDs %s for a closed connection after it has been retired.", ids)
+ })
+}
+
+func (h *packetHandlerMap) runCloseQueue() {
+ for {
+ select {
+ case <-h.listening:
+ return
+ case p := <-h.closeQueue:
+ h.conn.WritePacket(p.payload, p.addr, p.info.OOB())
+ }
+ }
+}
+
+func (h *packetHandlerMap) AddResetToken(token protocol.StatelessResetToken, handler packetHandler) {
+ h.mutex.Lock()
+ h.resetTokens[token] = handler
+ h.mutex.Unlock()
+}
+
+func (h *packetHandlerMap) RemoveResetToken(token protocol.StatelessResetToken) {
+ h.mutex.Lock()
+ delete(h.resetTokens, token)
+ h.mutex.Unlock()
+}
+
+func (h *packetHandlerMap) SetServer(s unknownPacketHandler) {
+ h.mutex.Lock()
+ h.server = s
+ h.mutex.Unlock()
+}
+
+func (h *packetHandlerMap) CloseServer() {
+ h.mutex.Lock()
+ if h.server == nil {
+ h.mutex.Unlock()
+ return
+ }
+ h.server = nil
+ var wg sync.WaitGroup
+ for _, handler := range h.handlers {
+ if handler.getPerspective() == protocol.PerspectiveServer {
+ wg.Add(1)
+ go func(handler packetHandler) {
+ // blocks until the CONNECTION_CLOSE has been sent and the run-loop has stopped
+ handler.shutdown()
+ wg.Done()
+ }(handler)
+ }
+ }
+ h.mutex.Unlock()
+ wg.Wait()
+}
+
+// Destroy closes the underlying connection and waits until listen() has returned.
+// It does not close active connections.
+func (h *packetHandlerMap) Destroy() error {
+ if err := h.conn.Close(); err != nil {
+ return err
+ }
+ <-h.listening // wait until listening returns
+ return nil
+}
+
+func (h *packetHandlerMap) close(e error) error {
+ h.mutex.Lock()
+ if h.closed {
+ h.mutex.Unlock()
+ return nil
+ }
+
+ var wg sync.WaitGroup
+ for _, handler := range h.handlers {
+ wg.Add(1)
+ go func(handler packetHandler) {
+ handler.destroy(e)
+ wg.Done()
+ }(handler)
+ }
+
+ if h.server != nil {
+ h.server.setCloseError(e)
+ }
+ h.closed = true
+ h.mutex.Unlock()
+ wg.Wait()
+ return getMultiplexer().RemoveConn(h.conn)
+}
+
+func (h *packetHandlerMap) listen() {
+ defer close(h.listening)
+ for {
+ p, err := h.conn.ReadPacket()
+ //nolint:staticcheck // SA1019 ignore this!
+ // TODO: This code is used to ignore wsa errors on Windows.
+ // Since net.Error.Temporary is deprecated as of Go 1.18, we should find a better solution.
+ // See https://github.com/quic-go/quic-go/issues/1737 for details.
+ if nerr, ok := err.(net.Error); ok && nerr.Temporary() {
+ h.logger.Debugf("Temporary error reading from conn: %w", err)
+ continue
+ }
+ if err != nil {
+ h.close(err)
+ return
+ }
+ h.handlePacket(p)
+ }
+}
+
+func (h *packetHandlerMap) handlePacket(p *receivedPacket) {
+ connID, err := wire.ParseConnectionID(p.data, h.connIDLen)
+ if err != nil {
+ h.logger.Debugf("error parsing connection ID on packet from %s: %s", p.remoteAddr, err)
+ if h.tracer != nil {
+ h.tracer.DroppedPacket(p.remoteAddr, logging.PacketTypeNotDetermined, p.Size(), logging.PacketDropHeaderParseError)
+ }
+ p.buffer.MaybeRelease()
+ return
+ }
+
+ h.mutex.Lock()
+ defer h.mutex.Unlock()
+
+ if isStatelessReset := h.maybeHandleStatelessReset(p.data); isStatelessReset {
+ return
+ }
+
+ if handler, ok := h.handlers[connID]; ok {
+ if ha, ok := handler.(*zeroRTTQueue); ok { // only enqueue 0-RTT packets in the 0-RTT queue
+ if wire.Is0RTTPacket(p.data) {
+ ha.handlePacket(p)
+ return
+ }
+ } else { // existing connection
+ handler.handlePacket(p)
+ return
+ }
+ }
+ if !wire.IsLongHeaderPacket(p.data[0]) {
+ go h.maybeSendStatelessReset(p, connID)
+ return
+ }
+ if h.server == nil { // no server set
+ h.logger.Debugf("received a packet with an unexpected connection ID %s", connID)
+ return
+ }
+ if wire.Is0RTTPacket(p.data) {
+ if h.numZeroRTTEntries >= protocol.Max0RTTQueues {
+ return
+ }
+ h.numZeroRTTEntries++
+ queue := &zeroRTTQueue{queue: make([]*receivedPacket, 0, 8)}
+ h.handlers[connID] = queue
+ queue.retireTimer = time.AfterFunc(h.zeroRTTQueueDuration, func() {
+ h.mutex.Lock()
+ defer h.mutex.Unlock()
+ // The entry might have been replaced by an actual connection.
+ // Only delete it if it's still a 0-RTT queue.
+ if handler, ok := h.handlers[connID]; ok {
+ if q, ok := handler.(*zeroRTTQueue); ok {
+ delete(h.handlers, connID)
+ h.numZeroRTTEntries--
+ if h.numZeroRTTEntries < 0 {
+ panic("number of 0-RTT queues < 0")
+ }
+ q.Clear()
+ if h.logger.Debug() {
+ h.logger.Debugf("Removing 0-RTT queue for %s.", connID)
+ }
+ }
+ }
+ })
+ queue.handlePacket(p)
+ return
+ }
+ h.server.handlePacket(p)
+}
+
+func (h *packetHandlerMap) maybeHandleStatelessReset(data []byte) bool {
+ // stateless resets are always short header packets
+ if wire.IsLongHeaderPacket(data[0]) {
+ return false
+ }
+ if len(data) < 17 /* type byte + 16 bytes for the reset token */ {
+ return false
+ }
+
+ var token protocol.StatelessResetToken
+ copy(token[:], data[len(data)-16:])
+ if sess, ok := h.resetTokens[token]; ok {
+ h.logger.Debugf("Received a stateless reset with token %#x. Closing connection.", token)
+ go sess.destroy(&StatelessResetError{Token: token})
+ return true
+ }
+ return false
+}
+
+func (h *packetHandlerMap) GetStatelessResetToken(connID protocol.ConnectionID) protocol.StatelessResetToken {
+ var token protocol.StatelessResetToken
+ if !h.statelessResetEnabled {
+ // Return a random stateless reset token.
+ // This token will be sent in the server's transport parameters.
+ // By using a random token, an off-path attacker won't be able to disrupt the connection.
+ rand.Read(token[:])
+ return token
+ }
+ h.statelessResetMutex.Lock()
+ h.statelessResetHasher.Write(connID.Bytes())
+ copy(token[:], h.statelessResetHasher.Sum(nil))
+ h.statelessResetHasher.Reset()
+ h.statelessResetMutex.Unlock()
+ return token
+}
+
+func (h *packetHandlerMap) maybeSendStatelessReset(p *receivedPacket, connID protocol.ConnectionID) {
+ defer p.buffer.Release()
+ if !h.statelessResetEnabled {
+ return
+ }
+ // Don't send a stateless reset in response to very small packets.
+ // This includes packets that could be stateless resets.
+ if len(p.data) <= protocol.MinStatelessResetSize {
+ return
+ }
+ token := h.GetStatelessResetToken(connID)
+ h.logger.Debugf("Sending stateless reset to %s (connection ID: %s). Token: %#x", p.remoteAddr, connID, token)
+ data := make([]byte, protocol.MinStatelessResetSize-16, protocol.MinStatelessResetSize)
+ rand.Read(data)
+ data[0] = (data[0] & 0x7f) | 0x40
+ data = append(data, token[:]...)
+ if _, err := h.conn.WritePacket(data, p.remoteAddr, p.info.OOB()); err != nil {
+ h.logger.Debugf("Error sending Stateless Reset: %s", err)
+ }
+}
diff --git a/vendor/github.com/quic-go/quic-go/packet_packer.go b/vendor/github.com/quic-go/quic-go/packet_packer.go
new file mode 100644
index 0000000000..14befd460f
--- /dev/null
+++ b/vendor/github.com/quic-go/quic-go/packet_packer.go
@@ -0,0 +1,968 @@
+package quic
+
+import (
+ "errors"
+ "fmt"
+ "net"
+ "time"
+
+ "github.com/quic-go/quic-go/internal/ackhandler"
+ "github.com/quic-go/quic-go/internal/handshake"
+ "github.com/quic-go/quic-go/internal/protocol"
+ "github.com/quic-go/quic-go/internal/qerr"
+ "github.com/quic-go/quic-go/internal/utils"
+ "github.com/quic-go/quic-go/internal/wire"
+)
+
+var errNothingToPack = errors.New("nothing to pack")
+
+type packer interface {
+ PackCoalescedPacket(onlyAck bool, v protocol.VersionNumber) (*coalescedPacket, error)
+ PackPacket(onlyAck bool, now time.Time, v protocol.VersionNumber) (shortHeaderPacket, *packetBuffer, error)
+ MaybePackProbePacket(protocol.EncryptionLevel, protocol.VersionNumber) (*coalescedPacket, error)
+ PackConnectionClose(*qerr.TransportError, protocol.VersionNumber) (*coalescedPacket, error)
+ PackApplicationClose(*qerr.ApplicationError, protocol.VersionNumber) (*coalescedPacket, error)
+
+ SetMaxPacketSize(protocol.ByteCount)
+ PackMTUProbePacket(ping ackhandler.Frame, size protocol.ByteCount, now time.Time, v protocol.VersionNumber) (shortHeaderPacket, *packetBuffer, error)
+
+ HandleTransportParameters(*wire.TransportParameters)
+ SetToken([]byte)
+}
+
+type sealer interface {
+ handshake.LongHeaderSealer
+}
+
+type payload struct {
+ frames []*ackhandler.Frame
+ ack *wire.AckFrame
+ length protocol.ByteCount
+}
+
+type longHeaderPacket struct {
+ header *wire.ExtendedHeader
+ ack *wire.AckFrame
+ frames []*ackhandler.Frame
+
+ length protocol.ByteCount
+
+ isMTUProbePacket bool
+}
+
+type shortHeaderPacket struct {
+ *ackhandler.Packet
+ // used for logging
+ DestConnID protocol.ConnectionID
+ Ack *wire.AckFrame
+ PacketNumberLen protocol.PacketNumberLen
+ KeyPhase protocol.KeyPhaseBit
+}
+
+func (p *shortHeaderPacket) IsAckEliciting() bool { return ackhandler.HasAckElicitingFrames(p.Frames) }
+
+type coalescedPacket struct {
+ buffer *packetBuffer
+ longHdrPackets []*longHeaderPacket
+ shortHdrPacket *shortHeaderPacket
+}
+
+func (p *longHeaderPacket) EncryptionLevel() protocol.EncryptionLevel {
+ //nolint:exhaustive // Will never be called for Retry packets (and they don't have encrypted data).
+ switch p.header.Type {
+ case protocol.PacketTypeInitial:
+ return protocol.EncryptionInitial
+ case protocol.PacketTypeHandshake:
+ return protocol.EncryptionHandshake
+ case protocol.PacketType0RTT:
+ return protocol.Encryption0RTT
+ default:
+ panic("can't determine encryption level")
+ }
+}
+
+func (p *longHeaderPacket) IsAckEliciting() bool { return ackhandler.HasAckElicitingFrames(p.frames) }
+
+func (p *longHeaderPacket) ToAckHandlerPacket(now time.Time, q *retransmissionQueue) *ackhandler.Packet {
+ largestAcked := protocol.InvalidPacketNumber
+ if p.ack != nil {
+ largestAcked = p.ack.LargestAcked()
+ }
+ encLevel := p.EncryptionLevel()
+ for i := range p.frames {
+ if p.frames[i].OnLost != nil {
+ continue
+ }
+ //nolint:exhaustive // Short header packets are handled separately.
+ switch encLevel {
+ case protocol.EncryptionInitial:
+ p.frames[i].OnLost = q.AddInitial
+ case protocol.EncryptionHandshake:
+ p.frames[i].OnLost = q.AddHandshake
+ case protocol.Encryption0RTT:
+ p.frames[i].OnLost = q.AddAppData
+ }
+ }
+
+ ap := ackhandler.GetPacket()
+ ap.PacketNumber = p.header.PacketNumber
+ ap.LargestAcked = largestAcked
+ ap.Frames = p.frames
+ ap.Length = p.length
+ ap.EncryptionLevel = encLevel
+ ap.SendTime = now
+ ap.IsPathMTUProbePacket = p.isMTUProbePacket
+ return ap
+}
+
+func getMaxPacketSize(addr net.Addr) protocol.ByteCount {
+ maxSize := protocol.ByteCount(protocol.MinInitialPacketSize)
+ // If this is not a UDP address, we don't know anything about the MTU.
+ // Use the minimum size of an Initial packet as the max packet size.
+ if udpAddr, ok := addr.(*net.UDPAddr); ok {
+ if utils.IsIPv4(udpAddr.IP) {
+ maxSize = protocol.InitialPacketSizeIPv4
+ } else {
+ maxSize = protocol.InitialPacketSizeIPv6
+ }
+ }
+ return maxSize
+}
+
+type packetNumberManager interface {
+ PeekPacketNumber(protocol.EncryptionLevel) (protocol.PacketNumber, protocol.PacketNumberLen)
+ PopPacketNumber(protocol.EncryptionLevel) protocol.PacketNumber
+}
+
+type sealingManager interface {
+ GetInitialSealer() (handshake.LongHeaderSealer, error)
+ GetHandshakeSealer() (handshake.LongHeaderSealer, error)
+ Get0RTTSealer() (handshake.LongHeaderSealer, error)
+ Get1RTTSealer() (handshake.ShortHeaderSealer, error)
+}
+
+type frameSource interface {
+ HasData() bool
+ AppendStreamFrames([]*ackhandler.Frame, protocol.ByteCount, protocol.VersionNumber) ([]*ackhandler.Frame, protocol.ByteCount)
+ AppendControlFrames([]*ackhandler.Frame, protocol.ByteCount, protocol.VersionNumber) ([]*ackhandler.Frame, protocol.ByteCount)
+}
+
+type ackFrameSource interface {
+ GetAckFrame(encLevel protocol.EncryptionLevel, onlyIfQueued bool) *wire.AckFrame
+}
+
+type packetPacker struct {
+ srcConnID protocol.ConnectionID
+ getDestConnID func() protocol.ConnectionID
+
+ perspective protocol.Perspective
+ cryptoSetup sealingManager
+
+ initialStream cryptoStream
+ handshakeStream cryptoStream
+
+ token []byte
+
+ pnManager packetNumberManager
+ framer frameSource
+ acks ackFrameSource
+ datagramQueue *datagramQueue
+ retransmissionQueue *retransmissionQueue
+
+ maxPacketSize protocol.ByteCount
+ numNonAckElicitingAcks int
+}
+
+var _ packer = &packetPacker{}
+
+func newPacketPacker(srcConnID protocol.ConnectionID, getDestConnID func() protocol.ConnectionID, initialStream cryptoStream, handshakeStream cryptoStream, packetNumberManager packetNumberManager, retransmissionQueue *retransmissionQueue, remoteAddr net.Addr, cryptoSetup sealingManager, framer frameSource, acks ackFrameSource, datagramQueue *datagramQueue, perspective protocol.Perspective) *packetPacker {
+ return &packetPacker{
+ cryptoSetup: cryptoSetup,
+ getDestConnID: getDestConnID,
+ srcConnID: srcConnID,
+ initialStream: initialStream,
+ handshakeStream: handshakeStream,
+ retransmissionQueue: retransmissionQueue,
+ datagramQueue: datagramQueue,
+ perspective: perspective,
+ framer: framer,
+ acks: acks,
+ pnManager: packetNumberManager,
+ maxPacketSize: getMaxPacketSize(remoteAddr),
+ }
+}
+
+// PackConnectionClose packs a packet that closes the connection with a transport error.
+func (p *packetPacker) PackConnectionClose(e *qerr.TransportError, v protocol.VersionNumber) (*coalescedPacket, error) {
+ var reason string
+ // don't send details of crypto errors
+ if !e.ErrorCode.IsCryptoError() {
+ reason = e.ErrorMessage
+ }
+ return p.packConnectionClose(false, uint64(e.ErrorCode), e.FrameType, reason, v)
+}
+
+// PackApplicationClose packs a packet that closes the connection with an application error.
+func (p *packetPacker) PackApplicationClose(e *qerr.ApplicationError, v protocol.VersionNumber) (*coalescedPacket, error) {
+ return p.packConnectionClose(true, uint64(e.ErrorCode), 0, e.ErrorMessage, v)
+}
+
+func (p *packetPacker) packConnectionClose(
+ isApplicationError bool,
+ errorCode uint64,
+ frameType uint64,
+ reason string,
+ v protocol.VersionNumber,
+) (*coalescedPacket, error) {
+ var sealers [4]sealer
+ var hdrs [3]*wire.ExtendedHeader
+ var payloads [4]payload
+ var size protocol.ByteCount
+ var connID protocol.ConnectionID
+ var oneRTTPacketNumber protocol.PacketNumber
+ var oneRTTPacketNumberLen protocol.PacketNumberLen
+ var keyPhase protocol.KeyPhaseBit // only set for 1-RTT
+ var numLongHdrPackets uint8
+ encLevels := [4]protocol.EncryptionLevel{protocol.EncryptionInitial, protocol.EncryptionHandshake, protocol.Encryption0RTT, protocol.Encryption1RTT}
+ for i, encLevel := range encLevels {
+ if p.perspective == protocol.PerspectiveServer && encLevel == protocol.Encryption0RTT {
+ continue
+ }
+ ccf := &wire.ConnectionCloseFrame{
+ IsApplicationError: isApplicationError,
+ ErrorCode: errorCode,
+ FrameType: frameType,
+ ReasonPhrase: reason,
+ }
+ // don't send application errors in Initial or Handshake packets
+ if isApplicationError && (encLevel == protocol.EncryptionInitial || encLevel == protocol.EncryptionHandshake) {
+ ccf.IsApplicationError = false
+ ccf.ErrorCode = uint64(qerr.ApplicationErrorErrorCode)
+ ccf.ReasonPhrase = ""
+ }
+ pl := payload{
+ frames: []*ackhandler.Frame{{Frame: ccf}},
+ length: ccf.Length(v),
+ }
+
+ var sealer sealer
+ var err error
+ switch encLevel {
+ case protocol.EncryptionInitial:
+ sealer, err = p.cryptoSetup.GetInitialSealer()
+ case protocol.EncryptionHandshake:
+ sealer, err = p.cryptoSetup.GetHandshakeSealer()
+ case protocol.Encryption0RTT:
+ sealer, err = p.cryptoSetup.Get0RTTSealer()
+ case protocol.Encryption1RTT:
+ var s handshake.ShortHeaderSealer
+ s, err = p.cryptoSetup.Get1RTTSealer()
+ if err == nil {
+ keyPhase = s.KeyPhase()
+ }
+ sealer = s
+ }
+ if err == handshake.ErrKeysNotYetAvailable || err == handshake.ErrKeysDropped {
+ continue
+ }
+ if err != nil {
+ return nil, err
+ }
+ sealers[i] = sealer
+ var hdr *wire.ExtendedHeader
+ if encLevel == protocol.Encryption1RTT {
+ connID = p.getDestConnID()
+ oneRTTPacketNumber, oneRTTPacketNumberLen = p.pnManager.PeekPacketNumber(protocol.Encryption1RTT)
+ size += p.shortHeaderPacketLength(connID, oneRTTPacketNumberLen, pl)
+ } else {
+ hdr = p.getLongHeader(encLevel, v)
+ hdrs[i] = hdr
+ size += p.longHeaderPacketLength(hdr, pl, v) + protocol.ByteCount(sealer.Overhead())
+ numLongHdrPackets++
+ }
+ payloads[i] = pl
+ }
+ buffer := getPacketBuffer()
+ packet := &coalescedPacket{
+ buffer: buffer,
+ longHdrPackets: make([]*longHeaderPacket, 0, numLongHdrPackets),
+ }
+ for i, encLevel := range encLevels {
+ if sealers[i] == nil {
+ continue
+ }
+ var paddingLen protocol.ByteCount
+ if encLevel == protocol.EncryptionInitial {
+ paddingLen = p.initialPaddingLen(payloads[i].frames, size)
+ }
+ if encLevel == protocol.Encryption1RTT {
+ ap, ack, err := p.appendShortHeaderPacket(buffer, connID, oneRTTPacketNumber, oneRTTPacketNumberLen, keyPhase, payloads[i], paddingLen, sealers[i], false, v)
+ if err != nil {
+ return nil, err
+ }
+ packet.shortHdrPacket = &shortHeaderPacket{
+ Packet: ap,
+ DestConnID: connID,
+ Ack: ack,
+ PacketNumberLen: oneRTTPacketNumberLen,
+ KeyPhase: keyPhase,
+ }
+ } else {
+ longHdrPacket, err := p.appendLongHeaderPacket(buffer, hdrs[i], payloads[i], paddingLen, encLevel, sealers[i], v)
+ if err != nil {
+ return nil, err
+ }
+ packet.longHdrPackets = append(packet.longHdrPackets, longHdrPacket)
+ }
+ }
+ return packet, nil
+}
+
+// longHeaderPacketLength calculates the length of a serialized long header packet.
+// It takes into account that packets that have a tiny payload need to be padded,
+// such that len(payload) + packet number len >= 4 + AEAD overhead
+func (p *packetPacker) longHeaderPacketLength(hdr *wire.ExtendedHeader, pl payload, v protocol.VersionNumber) protocol.ByteCount {
+ var paddingLen protocol.ByteCount
+ pnLen := protocol.ByteCount(hdr.PacketNumberLen)
+ if pl.length < 4-pnLen {
+ paddingLen = 4 - pnLen - pl.length
+ }
+ return hdr.GetLength(v) + pl.length + paddingLen
+}
+
+// shortHeaderPacketLength calculates the length of a serialized short header packet.
+// It takes into account that packets that have a tiny payload need to be padded,
+// such that len(payload) + packet number len >= 4 + AEAD overhead
+func (p *packetPacker) shortHeaderPacketLength(connID protocol.ConnectionID, pnLen protocol.PacketNumberLen, pl payload) protocol.ByteCount {
+ var paddingLen protocol.ByteCount
+ if pl.length < 4-protocol.ByteCount(pnLen) {
+ paddingLen = 4 - protocol.ByteCount(pnLen) - pl.length
+ }
+ return wire.ShortHeaderLen(connID, pnLen) + pl.length + paddingLen
+}
+
+// size is the expected size of the packet, if no padding was applied.
+func (p *packetPacker) initialPaddingLen(frames []*ackhandler.Frame, size protocol.ByteCount) protocol.ByteCount {
+ // For the server, only ack-eliciting Initial packets need to be padded.
+ if p.perspective == protocol.PerspectiveServer && !ackhandler.HasAckElicitingFrames(frames) {
+ return 0
+ }
+ if size >= p.maxPacketSize {
+ return 0
+ }
+ return p.maxPacketSize - size
+}
+
+// PackCoalescedPacket packs a new packet.
+// It packs an Initial / Handshake if there is data to send in these packet number spaces.
+// It should only be called before the handshake is confirmed.
+func (p *packetPacker) PackCoalescedPacket(onlyAck bool, v protocol.VersionNumber) (*coalescedPacket, error) {
+ maxPacketSize := p.maxPacketSize
+ if p.perspective == protocol.PerspectiveClient {
+ maxPacketSize = protocol.MinInitialPacketSize
+ }
+ var (
+ initialHdr, handshakeHdr, zeroRTTHdr *wire.ExtendedHeader
+ initialPayload, handshakePayload, zeroRTTPayload, oneRTTPayload payload
+ oneRTTPacketNumber protocol.PacketNumber
+ oneRTTPacketNumberLen protocol.PacketNumberLen
+ )
+ // Try packing an Initial packet.
+ initialSealer, err := p.cryptoSetup.GetInitialSealer()
+ if err != nil && err != handshake.ErrKeysDropped {
+ return nil, err
+ }
+ var size protocol.ByteCount
+ if initialSealer != nil {
+ initialHdr, initialPayload = p.maybeGetCryptoPacket(maxPacketSize-protocol.ByteCount(initialSealer.Overhead()), protocol.EncryptionInitial, onlyAck, true, v)
+ if initialPayload.length > 0 {
+ size += p.longHeaderPacketLength(initialHdr, initialPayload, v) + protocol.ByteCount(initialSealer.Overhead())
+ }
+ }
+
+ // Add a Handshake packet.
+ var handshakeSealer sealer
+ if (onlyAck && size == 0) || (!onlyAck && size < maxPacketSize-protocol.MinCoalescedPacketSize) {
+ var err error
+ handshakeSealer, err = p.cryptoSetup.GetHandshakeSealer()
+ if err != nil && err != handshake.ErrKeysDropped && err != handshake.ErrKeysNotYetAvailable {
+ return nil, err
+ }
+ if handshakeSealer != nil {
+ handshakeHdr, handshakePayload = p.maybeGetCryptoPacket(maxPacketSize-size-protocol.ByteCount(handshakeSealer.Overhead()), protocol.EncryptionHandshake, onlyAck, size == 0, v)
+ if handshakePayload.length > 0 {
+ s := p.longHeaderPacketLength(handshakeHdr, handshakePayload, v) + protocol.ByteCount(handshakeSealer.Overhead())
+ size += s
+ }
+ }
+ }
+
+ // Add a 0-RTT / 1-RTT packet.
+ var zeroRTTSealer sealer
+ var oneRTTSealer handshake.ShortHeaderSealer
+ var connID protocol.ConnectionID
+ var kp protocol.KeyPhaseBit
+ if (onlyAck && size == 0) || (!onlyAck && size < maxPacketSize-protocol.MinCoalescedPacketSize) {
+ var err error
+ oneRTTSealer, err = p.cryptoSetup.Get1RTTSealer()
+ if err != nil && err != handshake.ErrKeysDropped && err != handshake.ErrKeysNotYetAvailable {
+ return nil, err
+ }
+ if err == nil { // 1-RTT
+ kp = oneRTTSealer.KeyPhase()
+ connID = p.getDestConnID()
+ oneRTTPacketNumber, oneRTTPacketNumberLen = p.pnManager.PeekPacketNumber(protocol.Encryption1RTT)
+ hdrLen := wire.ShortHeaderLen(connID, oneRTTPacketNumberLen)
+ oneRTTPayload = p.maybeGetShortHeaderPacket(oneRTTSealer, hdrLen, maxPacketSize-size, onlyAck, size == 0, v)
+ if oneRTTPayload.length > 0 {
+ size += p.shortHeaderPacketLength(connID, oneRTTPacketNumberLen, oneRTTPayload) + protocol.ByteCount(oneRTTSealer.Overhead())
+ }
+ } else if p.perspective == protocol.PerspectiveClient { // 0-RTT
+ var err error
+ zeroRTTSealer, err = p.cryptoSetup.Get0RTTSealer()
+ if err != nil && err != handshake.ErrKeysDropped && err != handshake.ErrKeysNotYetAvailable {
+ return nil, err
+ }
+ if zeroRTTSealer != nil {
+ zeroRTTHdr, zeroRTTPayload = p.maybeGetAppDataPacketFor0RTT(zeroRTTSealer, maxPacketSize-size, v)
+ if zeroRTTPayload.length > 0 {
+ size += p.longHeaderPacketLength(zeroRTTHdr, zeroRTTPayload, v) + protocol.ByteCount(zeroRTTSealer.Overhead())
+ }
+ }
+ }
+ }
+
+ if initialPayload.length == 0 && handshakePayload.length == 0 && zeroRTTPayload.length == 0 && oneRTTPayload.length == 0 {
+ return nil, nil
+ }
+
+ buffer := getPacketBuffer()
+ packet := &coalescedPacket{
+ buffer: buffer,
+ longHdrPackets: make([]*longHeaderPacket, 0, 3),
+ }
+ if initialPayload.length > 0 {
+ padding := p.initialPaddingLen(initialPayload.frames, size)
+ cont, err := p.appendLongHeaderPacket(buffer, initialHdr, initialPayload, padding, protocol.EncryptionInitial, initialSealer, v)
+ if err != nil {
+ return nil, err
+ }
+ packet.longHdrPackets = append(packet.longHdrPackets, cont)
+ }
+ if handshakePayload.length > 0 {
+ cont, err := p.appendLongHeaderPacket(buffer, handshakeHdr, handshakePayload, 0, protocol.EncryptionHandshake, handshakeSealer, v)
+ if err != nil {
+ return nil, err
+ }
+ packet.longHdrPackets = append(packet.longHdrPackets, cont)
+ }
+ if zeroRTTPayload.length > 0 {
+ longHdrPacket, err := p.appendLongHeaderPacket(buffer, zeroRTTHdr, zeroRTTPayload, 0, protocol.Encryption0RTT, zeroRTTSealer, v)
+ if err != nil {
+ return nil, err
+ }
+ packet.longHdrPackets = append(packet.longHdrPackets, longHdrPacket)
+ } else if oneRTTPayload.length > 0 {
+ ap, ack, err := p.appendShortHeaderPacket(buffer, connID, oneRTTPacketNumber, oneRTTPacketNumberLen, kp, oneRTTPayload, 0, oneRTTSealer, false, v)
+ if err != nil {
+ return nil, err
+ }
+ packet.shortHdrPacket = &shortHeaderPacket{
+ Packet: ap,
+ DestConnID: connID,
+ Ack: ack,
+ PacketNumberLen: oneRTTPacketNumberLen,
+ KeyPhase: kp,
+ }
+ }
+ return packet, nil
+}
+
+// PackPacket packs a packet in the application data packet number space.
+// It should be called after the handshake is confirmed.
+func (p *packetPacker) PackPacket(onlyAck bool, now time.Time, v protocol.VersionNumber) (shortHeaderPacket, *packetBuffer, error) {
+ sealer, err := p.cryptoSetup.Get1RTTSealer()
+ if err != nil {
+ return shortHeaderPacket{}, nil, err
+ }
+ pn, pnLen := p.pnManager.PeekPacketNumber(protocol.Encryption1RTT)
+ connID := p.getDestConnID()
+ hdrLen := wire.ShortHeaderLen(connID, pnLen)
+ pl := p.maybeGetShortHeaderPacket(sealer, hdrLen, p.maxPacketSize, onlyAck, true, v)
+ if pl.length == 0 {
+ return shortHeaderPacket{}, nil, errNothingToPack
+ }
+ kp := sealer.KeyPhase()
+ buffer := getPacketBuffer()
+ ap, ack, err := p.appendShortHeaderPacket(buffer, connID, pn, pnLen, kp, pl, 0, sealer, false, v)
+ if err != nil {
+ return shortHeaderPacket{}, nil, err
+ }
+ return shortHeaderPacket{
+ Packet: ap,
+ DestConnID: connID,
+ Ack: ack,
+ PacketNumberLen: pnLen,
+ KeyPhase: kp,
+ }, buffer, nil
+}
+
+func (p *packetPacker) maybeGetCryptoPacket(maxPacketSize protocol.ByteCount, encLevel protocol.EncryptionLevel, onlyAck, ackAllowed bool, v protocol.VersionNumber) (*wire.ExtendedHeader, payload) {
+ if onlyAck {
+ if ack := p.acks.GetAckFrame(encLevel, true); ack != nil {
+ return p.getLongHeader(encLevel, v), payload{
+ ack: ack,
+ length: ack.Length(v),
+ }
+ }
+ return nil, payload{}
+ }
+
+ var s cryptoStream
+ var hasRetransmission bool
+ //nolint:exhaustive // Initial and Handshake are the only two encryption levels here.
+ switch encLevel {
+ case protocol.EncryptionInitial:
+ s = p.initialStream
+ hasRetransmission = p.retransmissionQueue.HasInitialData()
+ case protocol.EncryptionHandshake:
+ s = p.handshakeStream
+ hasRetransmission = p.retransmissionQueue.HasHandshakeData()
+ }
+
+ hasData := s.HasData()
+ var ack *wire.AckFrame
+ if ackAllowed {
+ ack = p.acks.GetAckFrame(encLevel, !hasRetransmission && !hasData)
+ }
+ if !hasData && !hasRetransmission && ack == nil {
+ // nothing to send
+ return nil, payload{}
+ }
+
+ var pl payload
+ if ack != nil {
+ pl.ack = ack
+ pl.length = ack.Length(v)
+ maxPacketSize -= pl.length
+ }
+ hdr := p.getLongHeader(encLevel, v)
+ maxPacketSize -= hdr.GetLength(v)
+ if hasRetransmission {
+ for {
+ var f wire.Frame
+ //nolint:exhaustive // 0-RTT packets can't contain any retransmission.s
+ switch encLevel {
+ case protocol.EncryptionInitial:
+ f = p.retransmissionQueue.GetInitialFrame(maxPacketSize, v)
+ case protocol.EncryptionHandshake:
+ f = p.retransmissionQueue.GetHandshakeFrame(maxPacketSize, v)
+ }
+ if f == nil {
+ break
+ }
+ af := ackhandler.GetFrame()
+ af.Frame = f
+ pl.frames = append(pl.frames, af)
+ frameLen := f.Length(v)
+ pl.length += frameLen
+ maxPacketSize -= frameLen
+ }
+ } else if s.HasData() {
+ cf := s.PopCryptoFrame(maxPacketSize)
+ pl.frames = []*ackhandler.Frame{{Frame: cf}}
+ pl.length += cf.Length(v)
+ }
+ return hdr, pl
+}
+
+func (p *packetPacker) maybeGetAppDataPacketFor0RTT(sealer sealer, maxPacketSize protocol.ByteCount, v protocol.VersionNumber) (*wire.ExtendedHeader, payload) {
+ if p.perspective != protocol.PerspectiveClient {
+ return nil, payload{}
+ }
+
+ hdr := p.getLongHeader(protocol.Encryption0RTT, v)
+ maxPayloadSize := maxPacketSize - hdr.GetLength(v) - protocol.ByteCount(sealer.Overhead())
+ return hdr, p.maybeGetAppDataPacket(maxPayloadSize, false, false, v)
+}
+
+func (p *packetPacker) maybeGetShortHeaderPacket(sealer handshake.ShortHeaderSealer, hdrLen protocol.ByteCount, maxPacketSize protocol.ByteCount, onlyAck, ackAllowed bool, v protocol.VersionNumber) payload {
+ maxPayloadSize := maxPacketSize - hdrLen - protocol.ByteCount(sealer.Overhead())
+ return p.maybeGetAppDataPacket(maxPayloadSize, onlyAck, ackAllowed, v)
+}
+
+func (p *packetPacker) maybeGetAppDataPacket(maxPayloadSize protocol.ByteCount, onlyAck, ackAllowed bool, v protocol.VersionNumber) payload {
+ pl := p.composeNextPacket(maxPayloadSize, onlyAck, ackAllowed, v)
+
+ // check if we have anything to send
+ if len(pl.frames) == 0 {
+ if pl.ack == nil {
+ return payload{}
+ }
+ // the packet only contains an ACK
+ if p.numNonAckElicitingAcks >= protocol.MaxNonAckElicitingAcks {
+ ping := &wire.PingFrame{}
+ // don't retransmit the PING frame when it is lost
+ af := ackhandler.GetFrame()
+ af.Frame = ping
+ af.OnLost = func(wire.Frame) {}
+ pl.frames = append(pl.frames, af)
+ pl.length += ping.Length(v)
+ p.numNonAckElicitingAcks = 0
+ } else {
+ p.numNonAckElicitingAcks++
+ }
+ } else {
+ p.numNonAckElicitingAcks = 0
+ }
+ return pl
+}
+
+func (p *packetPacker) composeNextPacket(maxFrameSize protocol.ByteCount, onlyAck, ackAllowed bool, v protocol.VersionNumber) payload {
+ if onlyAck {
+ if ack := p.acks.GetAckFrame(protocol.Encryption1RTT, true); ack != nil {
+ return payload{
+ ack: ack,
+ length: ack.Length(v),
+ }
+ }
+ return payload{}
+ }
+
+ pl := payload{frames: make([]*ackhandler.Frame, 0, 1)}
+
+ hasData := p.framer.HasData()
+ hasRetransmission := p.retransmissionQueue.HasAppData()
+
+ var hasAck bool
+ if ackAllowed {
+ if ack := p.acks.GetAckFrame(protocol.Encryption1RTT, !hasRetransmission && !hasData); ack != nil {
+ pl.ack = ack
+ pl.length += ack.Length(v)
+ hasAck = true
+ }
+ }
+
+ if p.datagramQueue != nil {
+ if f := p.datagramQueue.Peek(); f != nil {
+ size := f.Length(v)
+ if size <= maxFrameSize-pl.length {
+ af := ackhandler.GetFrame()
+ af.Frame = f
+ // set it to a no-op. Then we won't set the default callback, which would retransmit the frame.
+ af.OnLost = func(wire.Frame) {}
+ pl.frames = append(pl.frames, af)
+ pl.length += size
+ p.datagramQueue.Pop()
+ }
+ }
+ }
+
+ if hasAck && !hasData && !hasRetransmission {
+ return pl
+ }
+
+ if hasRetransmission {
+ for {
+ remainingLen := maxFrameSize - pl.length
+ if remainingLen < protocol.MinStreamFrameSize {
+ break
+ }
+ f := p.retransmissionQueue.GetAppDataFrame(remainingLen, v)
+ if f == nil {
+ break
+ }
+ af := ackhandler.GetFrame()
+ af.Frame = f
+ pl.frames = append(pl.frames, af)
+ pl.length += f.Length(v)
+ }
+ }
+
+ if hasData {
+ var lengthAdded protocol.ByteCount
+ pl.frames, lengthAdded = p.framer.AppendControlFrames(pl.frames, maxFrameSize-pl.length, v)
+ pl.length += lengthAdded
+
+ pl.frames, lengthAdded = p.framer.AppendStreamFrames(pl.frames, maxFrameSize-pl.length, v)
+ pl.length += lengthAdded
+ }
+ return pl
+}
+
+func (p *packetPacker) MaybePackProbePacket(encLevel protocol.EncryptionLevel, v protocol.VersionNumber) (*coalescedPacket, error) {
+ if encLevel == protocol.Encryption1RTT {
+ s, err := p.cryptoSetup.Get1RTTSealer()
+ if err != nil {
+ return nil, err
+ }
+ kp := s.KeyPhase()
+ connID := p.getDestConnID()
+ pn, pnLen := p.pnManager.PeekPacketNumber(protocol.Encryption1RTT)
+ hdrLen := wire.ShortHeaderLen(connID, pnLen)
+ pl := p.maybeGetAppDataPacket(p.maxPacketSize-protocol.ByteCount(s.Overhead())-hdrLen, false, true, v)
+ if pl.length == 0 {
+ return nil, nil
+ }
+ buffer := getPacketBuffer()
+ packet := &coalescedPacket{buffer: buffer}
+ ap, ack, err := p.appendShortHeaderPacket(buffer, connID, pn, pnLen, kp, pl, 0, s, false, v)
+ if err != nil {
+ return nil, err
+ }
+ packet.shortHdrPacket = &shortHeaderPacket{
+ Packet: ap,
+ DestConnID: connID,
+ Ack: ack,
+ PacketNumberLen: pnLen,
+ KeyPhase: kp,
+ }
+ return packet, nil
+ }
+
+ var hdr *wire.ExtendedHeader
+ var pl payload
+ var sealer handshake.LongHeaderSealer
+ //nolint:exhaustive // Probe packets are never sent for 0-RTT.
+ switch encLevel {
+ case protocol.EncryptionInitial:
+ var err error
+ sealer, err = p.cryptoSetup.GetInitialSealer()
+ if err != nil {
+ return nil, err
+ }
+ hdr, pl = p.maybeGetCryptoPacket(p.maxPacketSize-protocol.ByteCount(sealer.Overhead()), protocol.EncryptionInitial, false, true, v)
+ case protocol.EncryptionHandshake:
+ var err error
+ sealer, err = p.cryptoSetup.GetHandshakeSealer()
+ if err != nil {
+ return nil, err
+ }
+ hdr, pl = p.maybeGetCryptoPacket(p.maxPacketSize-protocol.ByteCount(sealer.Overhead()), protocol.EncryptionHandshake, false, true, v)
+ default:
+ panic("unknown encryption level")
+ }
+
+ if pl.length == 0 {
+ return nil, nil
+ }
+ buffer := getPacketBuffer()
+ packet := &coalescedPacket{buffer: buffer}
+ size := p.longHeaderPacketLength(hdr, pl, v) + protocol.ByteCount(sealer.Overhead())
+ var padding protocol.ByteCount
+ if encLevel == protocol.EncryptionInitial {
+ padding = p.initialPaddingLen(pl.frames, size)
+ }
+
+ longHdrPacket, err := p.appendLongHeaderPacket(buffer, hdr, pl, padding, encLevel, sealer, v)
+ if err != nil {
+ return nil, err
+ }
+ packet.longHdrPackets = []*longHeaderPacket{longHdrPacket}
+ return packet, nil
+}
+
+func (p *packetPacker) PackMTUProbePacket(ping ackhandler.Frame, size protocol.ByteCount, now time.Time, v protocol.VersionNumber) (shortHeaderPacket, *packetBuffer, error) {
+ pl := payload{
+ frames: []*ackhandler.Frame{&ping},
+ length: ping.Length(v),
+ }
+ buffer := getPacketBuffer()
+ s, err := p.cryptoSetup.Get1RTTSealer()
+ if err != nil {
+ return shortHeaderPacket{}, nil, err
+ }
+ connID := p.getDestConnID()
+ pn, pnLen := p.pnManager.PeekPacketNumber(protocol.Encryption1RTT)
+ padding := size - p.shortHeaderPacketLength(connID, pnLen, pl) - protocol.ByteCount(s.Overhead())
+ kp := s.KeyPhase()
+ ap, ack, err := p.appendShortHeaderPacket(buffer, connID, pn, pnLen, kp, pl, padding, s, true, v)
+ if err != nil {
+ return shortHeaderPacket{}, nil, err
+ }
+ return shortHeaderPacket{
+ Packet: ap,
+ DestConnID: connID,
+ Ack: ack,
+ PacketNumberLen: pnLen,
+ KeyPhase: kp,
+ }, buffer, nil
+}
+
+func (p *packetPacker) getLongHeader(encLevel protocol.EncryptionLevel, v protocol.VersionNumber) *wire.ExtendedHeader {
+ pn, pnLen := p.pnManager.PeekPacketNumber(encLevel)
+ hdr := &wire.ExtendedHeader{
+ PacketNumber: pn,
+ PacketNumberLen: pnLen,
+ }
+ hdr.Version = v
+ hdr.SrcConnectionID = p.srcConnID
+ hdr.DestConnectionID = p.getDestConnID()
+
+ //nolint:exhaustive // 1-RTT packets are not long header packets.
+ switch encLevel {
+ case protocol.EncryptionInitial:
+ hdr.Type = protocol.PacketTypeInitial
+ hdr.Token = p.token
+ case protocol.EncryptionHandshake:
+ hdr.Type = protocol.PacketTypeHandshake
+ case protocol.Encryption0RTT:
+ hdr.Type = protocol.PacketType0RTT
+ }
+ return hdr
+}
+
+func (p *packetPacker) appendLongHeaderPacket(buffer *packetBuffer, header *wire.ExtendedHeader, pl payload, padding protocol.ByteCount, encLevel protocol.EncryptionLevel, sealer sealer, v protocol.VersionNumber) (*longHeaderPacket, error) {
+ var paddingLen protocol.ByteCount
+ pnLen := protocol.ByteCount(header.PacketNumberLen)
+ if pl.length < 4-pnLen {
+ paddingLen = 4 - pnLen - pl.length
+ }
+ paddingLen += padding
+ header.Length = pnLen + protocol.ByteCount(sealer.Overhead()) + pl.length + paddingLen
+
+ startLen := len(buffer.Data)
+ raw := buffer.Data[startLen:]
+ raw, err := header.Append(raw, v)
+ if err != nil {
+ return nil, err
+ }
+ payloadOffset := protocol.ByteCount(len(raw))
+
+ pn := p.pnManager.PopPacketNumber(encLevel)
+ if pn != header.PacketNumber {
+ return nil, errors.New("packetPacker BUG: Peeked and Popped packet numbers do not match")
+ }
+
+ raw, err = p.appendPacketPayload(raw, pl, paddingLen, v)
+ if err != nil {
+ return nil, err
+ }
+ raw = p.encryptPacket(raw, sealer, pn, payloadOffset, pnLen)
+ buffer.Data = buffer.Data[:len(buffer.Data)+len(raw)]
+
+ return &longHeaderPacket{
+ header: header,
+ ack: pl.ack,
+ frames: pl.frames,
+ length: protocol.ByteCount(len(raw)),
+ }, nil
+}
+
+func (p *packetPacker) appendShortHeaderPacket(
+ buffer *packetBuffer,
+ connID protocol.ConnectionID,
+ pn protocol.PacketNumber,
+ pnLen protocol.PacketNumberLen,
+ kp protocol.KeyPhaseBit,
+ pl payload,
+ padding protocol.ByteCount,
+ sealer sealer,
+ isMTUProbePacket bool,
+ v protocol.VersionNumber,
+) (*ackhandler.Packet, *wire.AckFrame, error) {
+ var paddingLen protocol.ByteCount
+ if pl.length < 4-protocol.ByteCount(pnLen) {
+ paddingLen = 4 - protocol.ByteCount(pnLen) - pl.length
+ }
+ paddingLen += padding
+
+ startLen := len(buffer.Data)
+ raw := buffer.Data[startLen:]
+ raw, err := wire.AppendShortHeader(raw, connID, pn, pnLen, kp)
+ if err != nil {
+ return nil, nil, err
+ }
+ payloadOffset := protocol.ByteCount(len(raw))
+
+ if pn != p.pnManager.PopPacketNumber(protocol.Encryption1RTT) {
+ return nil, nil, errors.New("packetPacker BUG: Peeked and Popped packet numbers do not match")
+ }
+
+ raw, err = p.appendPacketPayload(raw, pl, paddingLen, v)
+ if err != nil {
+ return nil, nil, err
+ }
+ if !isMTUProbePacket {
+ if size := protocol.ByteCount(len(raw) + sealer.Overhead()); size > p.maxPacketSize {
+ return nil, nil, fmt.Errorf("PacketPacker BUG: packet too large (%d bytes, allowed %d bytes)", size, p.maxPacketSize)
+ }
+ }
+ raw = p.encryptPacket(raw, sealer, pn, payloadOffset, protocol.ByteCount(pnLen))
+ buffer.Data = buffer.Data[:len(buffer.Data)+len(raw)]
+
+ // create the ackhandler.Packet
+ largestAcked := protocol.InvalidPacketNumber
+ if pl.ack != nil {
+ largestAcked = pl.ack.LargestAcked()
+ }
+ for i := range pl.frames {
+ if pl.frames[i].OnLost != nil {
+ continue
+ }
+ pl.frames[i].OnLost = p.retransmissionQueue.AddAppData
+ }
+
+ ap := ackhandler.GetPacket()
+ ap.PacketNumber = pn
+ ap.LargestAcked = largestAcked
+ ap.Frames = pl.frames
+ ap.Length = protocol.ByteCount(len(raw))
+ ap.EncryptionLevel = protocol.Encryption1RTT
+ ap.SendTime = time.Now()
+ ap.IsPathMTUProbePacket = isMTUProbePacket
+
+ return ap, pl.ack, nil
+}
+
+func (p *packetPacker) appendPacketPayload(raw []byte, pl payload, paddingLen protocol.ByteCount, v protocol.VersionNumber) ([]byte, error) {
+ payloadOffset := len(raw)
+ if pl.ack != nil {
+ var err error
+ raw, err = pl.ack.Append(raw, v)
+ if err != nil {
+ return nil, err
+ }
+ }
+ if paddingLen > 0 {
+ raw = append(raw, make([]byte, paddingLen)...)
+ }
+ for _, frame := range pl.frames {
+ var err error
+ raw, err = frame.Append(raw, v)
+ if err != nil {
+ return nil, err
+ }
+ }
+
+ if payloadSize := protocol.ByteCount(len(raw)-payloadOffset) - paddingLen; payloadSize != pl.length {
+ return nil, fmt.Errorf("PacketPacker BUG: payload size inconsistent (expected %d, got %d bytes)", pl.length, payloadSize)
+ }
+ return raw, nil
+}
+
+func (p *packetPacker) encryptPacket(raw []byte, sealer sealer, pn protocol.PacketNumber, payloadOffset, pnLen protocol.ByteCount) []byte {
+ _ = sealer.Seal(raw[payloadOffset:payloadOffset], raw[payloadOffset:], pn, raw[:payloadOffset])
+ raw = raw[:len(raw)+sealer.Overhead()]
+ // apply header protection
+ pnOffset := payloadOffset - pnLen
+ sealer.EncryptHeader(raw[pnOffset+4:pnOffset+4+16], &raw[0], raw[pnOffset:payloadOffset])
+ return raw
+}
+
+func (p *packetPacker) SetToken(token []byte) {
+ p.token = token
+}
+
+// When a higher MTU is discovered, use it.
+func (p *packetPacker) SetMaxPacketSize(s protocol.ByteCount) {
+ p.maxPacketSize = s
+}
+
+// If the peer sets a max_packet_size that's smaller than the size we're currently using,
+// we need to reduce the size of packets we send.
+func (p *packetPacker) HandleTransportParameters(params *wire.TransportParameters) {
+ if params.MaxUDPPayloadSize != 0 {
+ p.maxPacketSize = utils.Min(p.maxPacketSize, params.MaxUDPPayloadSize)
+ }
+}
diff --git a/vendor/github.com/quic-go/quic-go/packet_unpacker.go b/vendor/github.com/quic-go/quic-go/packet_unpacker.go
new file mode 100644
index 0000000000..103524c7dd
--- /dev/null
+++ b/vendor/github.com/quic-go/quic-go/packet_unpacker.go
@@ -0,0 +1,226 @@
+package quic
+
+import (
+ "bytes"
+ "fmt"
+ "time"
+
+ "github.com/quic-go/quic-go/internal/handshake"
+ "github.com/quic-go/quic-go/internal/protocol"
+ "github.com/quic-go/quic-go/internal/qerr"
+ "github.com/quic-go/quic-go/internal/wire"
+)
+
+type headerDecryptor interface {
+ DecryptHeader(sample []byte, firstByte *byte, pnBytes []byte)
+}
+
+type headerParseError struct {
+ err error
+}
+
+func (e *headerParseError) Unwrap() error {
+ return e.err
+}
+
+func (e *headerParseError) Error() string {
+ return e.err.Error()
+}
+
+type unpackedPacket struct {
+ hdr *wire.ExtendedHeader
+ encryptionLevel protocol.EncryptionLevel
+ data []byte
+}
+
+// The packetUnpacker unpacks QUIC packets.
+type packetUnpacker struct {
+ cs handshake.CryptoSetup
+
+ shortHdrConnIDLen int
+}
+
+var _ unpacker = &packetUnpacker{}
+
+func newPacketUnpacker(cs handshake.CryptoSetup, shortHdrConnIDLen int) *packetUnpacker {
+ return &packetUnpacker{
+ cs: cs,
+ shortHdrConnIDLen: shortHdrConnIDLen,
+ }
+}
+
+// UnpackLongHeader unpacks a Long Header packet.
+// If the reserved bits are invalid, the error is wire.ErrInvalidReservedBits.
+// If any other error occurred when parsing the header, the error is of type headerParseError.
+// If decrypting the payload fails for any reason, the error is the error returned by the AEAD.
+func (u *packetUnpacker) UnpackLongHeader(hdr *wire.Header, rcvTime time.Time, data []byte, v protocol.VersionNumber) (*unpackedPacket, error) {
+ var encLevel protocol.EncryptionLevel
+ var extHdr *wire.ExtendedHeader
+ var decrypted []byte
+ //nolint:exhaustive // Retry packets can't be unpacked.
+ switch hdr.Type {
+ case protocol.PacketTypeInitial:
+ encLevel = protocol.EncryptionInitial
+ opener, err := u.cs.GetInitialOpener()
+ if err != nil {
+ return nil, err
+ }
+ extHdr, decrypted, err = u.unpackLongHeaderPacket(opener, hdr, data, v)
+ if err != nil {
+ return nil, err
+ }
+ case protocol.PacketTypeHandshake:
+ encLevel = protocol.EncryptionHandshake
+ opener, err := u.cs.GetHandshakeOpener()
+ if err != nil {
+ return nil, err
+ }
+ extHdr, decrypted, err = u.unpackLongHeaderPacket(opener, hdr, data, v)
+ if err != nil {
+ return nil, err
+ }
+ case protocol.PacketType0RTT:
+ encLevel = protocol.Encryption0RTT
+ opener, err := u.cs.Get0RTTOpener()
+ if err != nil {
+ return nil, err
+ }
+ extHdr, decrypted, err = u.unpackLongHeaderPacket(opener, hdr, data, v)
+ if err != nil {
+ return nil, err
+ }
+ default:
+ return nil, fmt.Errorf("unknown packet type: %s", hdr.Type)
+ }
+
+ if len(decrypted) == 0 {
+ return nil, &qerr.TransportError{
+ ErrorCode: qerr.ProtocolViolation,
+ ErrorMessage: "empty packet",
+ }
+ }
+
+ return &unpackedPacket{
+ hdr: extHdr,
+ encryptionLevel: encLevel,
+ data: decrypted,
+ }, nil
+}
+
+func (u *packetUnpacker) UnpackShortHeader(rcvTime time.Time, data []byte) (protocol.PacketNumber, protocol.PacketNumberLen, protocol.KeyPhaseBit, []byte, error) {
+ opener, err := u.cs.Get1RTTOpener()
+ if err != nil {
+ return 0, 0, 0, nil, err
+ }
+ pn, pnLen, kp, decrypted, err := u.unpackShortHeaderPacket(opener, rcvTime, data)
+ if err != nil {
+ return 0, 0, 0, nil, err
+ }
+ if len(decrypted) == 0 {
+ return 0, 0, 0, nil, &qerr.TransportError{
+ ErrorCode: qerr.ProtocolViolation,
+ ErrorMessage: "empty packet",
+ }
+ }
+ return pn, pnLen, kp, decrypted, nil
+}
+
+func (u *packetUnpacker) unpackLongHeaderPacket(opener handshake.LongHeaderOpener, hdr *wire.Header, data []byte, v protocol.VersionNumber) (*wire.ExtendedHeader, []byte, error) {
+ extHdr, parseErr := u.unpackLongHeader(opener, hdr, data, v)
+ // If the reserved bits are set incorrectly, we still need to continue unpacking.
+ // This avoids a timing side-channel, which otherwise might allow an attacker
+ // to gain information about the header encryption.
+ if parseErr != nil && parseErr != wire.ErrInvalidReservedBits {
+ return nil, nil, parseErr
+ }
+ extHdrLen := extHdr.ParsedLen()
+ extHdr.PacketNumber = opener.DecodePacketNumber(extHdr.PacketNumber, extHdr.PacketNumberLen)
+ decrypted, err := opener.Open(data[extHdrLen:extHdrLen], data[extHdrLen:], extHdr.PacketNumber, data[:extHdrLen])
+ if err != nil {
+ return nil, nil, err
+ }
+ if parseErr != nil {
+ return nil, nil, parseErr
+ }
+ return extHdr, decrypted, nil
+}
+
+func (u *packetUnpacker) unpackShortHeaderPacket(opener handshake.ShortHeaderOpener, rcvTime time.Time, data []byte) (protocol.PacketNumber, protocol.PacketNumberLen, protocol.KeyPhaseBit, []byte, error) {
+ l, pn, pnLen, kp, parseErr := u.unpackShortHeader(opener, data)
+ // If the reserved bits are set incorrectly, we still need to continue unpacking.
+ // This avoids a timing side-channel, which otherwise might allow an attacker
+ // to gain information about the header encryption.
+ if parseErr != nil && parseErr != wire.ErrInvalidReservedBits {
+ return 0, 0, 0, nil, &headerParseError{parseErr}
+ }
+ pn = opener.DecodePacketNumber(pn, pnLen)
+ decrypted, err := opener.Open(data[l:l], data[l:], rcvTime, pn, kp, data[:l])
+ if err != nil {
+ return 0, 0, 0, nil, err
+ }
+ return pn, pnLen, kp, decrypted, parseErr
+}
+
+func (u *packetUnpacker) unpackShortHeader(hd headerDecryptor, data []byte) (int, protocol.PacketNumber, protocol.PacketNumberLen, protocol.KeyPhaseBit, error) {
+ hdrLen := 1 /* first header byte */ + u.shortHdrConnIDLen
+ if len(data) < hdrLen+4+16 {
+ return 0, 0, 0, 0, fmt.Errorf("packet too small, expected at least 20 bytes after the header, got %d", len(data)-hdrLen)
+ }
+ origPNBytes := make([]byte, 4)
+ copy(origPNBytes, data[hdrLen:hdrLen+4])
+ // 2. decrypt the header, assuming a 4 byte packet number
+ hd.DecryptHeader(
+ data[hdrLen+4:hdrLen+4+16],
+ &data[0],
+ data[hdrLen:hdrLen+4],
+ )
+ // 3. parse the header (and learn the actual length of the packet number)
+ l, pn, pnLen, kp, parseErr := wire.ParseShortHeader(data, u.shortHdrConnIDLen)
+ if parseErr != nil && parseErr != wire.ErrInvalidReservedBits {
+ return l, pn, pnLen, kp, parseErr
+ }
+ // 4. if the packet number is shorter than 4 bytes, replace the remaining bytes with the copy we saved earlier
+ if pnLen != protocol.PacketNumberLen4 {
+ copy(data[hdrLen+int(pnLen):hdrLen+4], origPNBytes[int(pnLen):])
+ }
+ return l, pn, pnLen, kp, parseErr
+}
+
+// The error is either nil, a wire.ErrInvalidReservedBits or of type headerParseError.
+func (u *packetUnpacker) unpackLongHeader(hd headerDecryptor, hdr *wire.Header, data []byte, v protocol.VersionNumber) (*wire.ExtendedHeader, error) {
+ extHdr, err := unpackLongHeader(hd, hdr, data, v)
+ if err != nil && err != wire.ErrInvalidReservedBits {
+ return nil, &headerParseError{err: err}
+ }
+ return extHdr, err
+}
+
+func unpackLongHeader(hd headerDecryptor, hdr *wire.Header, data []byte, v protocol.VersionNumber) (*wire.ExtendedHeader, error) {
+ r := bytes.NewReader(data)
+
+ hdrLen := hdr.ParsedLen()
+ if protocol.ByteCount(len(data)) < hdrLen+4+16 {
+ //nolint:stylecheck
+ return nil, fmt.Errorf("Packet too small. Expected at least 20 bytes after the header, got %d", protocol.ByteCount(len(data))-hdrLen)
+ }
+ // The packet number can be up to 4 bytes long, but we won't know the length until we decrypt it.
+ // 1. save a copy of the 4 bytes
+ origPNBytes := make([]byte, 4)
+ copy(origPNBytes, data[hdrLen:hdrLen+4])
+ // 2. decrypt the header, assuming a 4 byte packet number
+ hd.DecryptHeader(
+ data[hdrLen+4:hdrLen+4+16],
+ &data[0],
+ data[hdrLen:hdrLen+4],
+ )
+ // 3. parse the header (and learn the actual length of the packet number)
+ extHdr, parseErr := hdr.ParseExtended(r, v)
+ if parseErr != nil && parseErr != wire.ErrInvalidReservedBits {
+ return nil, parseErr
+ }
+ // 4. if the packet number is shorter than 4 bytes, replace the remaining bytes with the copy we saved earlier
+ if extHdr.PacketNumberLen != protocol.PacketNumberLen4 {
+ copy(data[extHdr.ParsedLen():hdrLen+4], origPNBytes[int(extHdr.PacketNumberLen):])
+ }
+ return extHdr, parseErr
+}
diff --git a/vendor/github.com/quic-go/quic-go/quicvarint/io.go b/vendor/github.com/quic-go/quic-go/quicvarint/io.go
new file mode 100644
index 0000000000..9368d1c58b
--- /dev/null
+++ b/vendor/github.com/quic-go/quic-go/quicvarint/io.go
@@ -0,0 +1,68 @@
+package quicvarint
+
+import (
+ "bytes"
+ "io"
+)
+
+// Reader implements both the io.ByteReader and io.Reader interfaces.
+type Reader interface {
+ io.ByteReader
+ io.Reader
+}
+
+var _ Reader = &bytes.Reader{}
+
+type byteReader struct {
+ io.Reader
+}
+
+var _ Reader = &byteReader{}
+
+// NewReader returns a Reader for r.
+// If r already implements both io.ByteReader and io.Reader, NewReader returns r.
+// Otherwise, r is wrapped to add the missing interfaces.
+func NewReader(r io.Reader) Reader {
+ if r, ok := r.(Reader); ok {
+ return r
+ }
+ return &byteReader{r}
+}
+
+func (r *byteReader) ReadByte() (byte, error) {
+ var b [1]byte
+ n, err := r.Reader.Read(b[:])
+ if n == 1 && err == io.EOF {
+ err = nil
+ }
+ return b[0], err
+}
+
+// Writer implements both the io.ByteWriter and io.Writer interfaces.
+type Writer interface {
+ io.ByteWriter
+ io.Writer
+}
+
+var _ Writer = &bytes.Buffer{}
+
+type byteWriter struct {
+ io.Writer
+}
+
+var _ Writer = &byteWriter{}
+
+// NewWriter returns a Writer for w.
+// If r already implements both io.ByteWriter and io.Writer, NewWriter returns w.
+// Otherwise, w is wrapped to add the missing interfaces.
+func NewWriter(w io.Writer) Writer {
+ if w, ok := w.(Writer); ok {
+ return w
+ }
+ return &byteWriter{w}
+}
+
+func (w *byteWriter) WriteByte(c byte) error {
+ _, err := w.Writer.Write([]byte{c})
+ return err
+}
diff --git a/vendor/github.com/quic-go/quic-go/quicvarint/varint.go b/vendor/github.com/quic-go/quic-go/quicvarint/varint.go
new file mode 100644
index 0000000000..ea1a9107f5
--- /dev/null
+++ b/vendor/github.com/quic-go/quic-go/quicvarint/varint.go
@@ -0,0 +1,158 @@
+package quicvarint
+
+import (
+ "fmt"
+ "io"
+
+ "github.com/quic-go/quic-go/internal/protocol"
+)
+
+// taken from the QUIC draft
+const (
+ // Min is the minimum value allowed for a QUIC varint.
+ Min = 0
+
+ // Max is the maximum allowed value for a QUIC varint (2^62-1).
+ Max = maxVarInt8
+
+ maxVarInt1 = 63
+ maxVarInt2 = 16383
+ maxVarInt4 = 1073741823
+ maxVarInt8 = 4611686018427387903
+)
+
+// Read reads a number in the QUIC varint format from r.
+func Read(r io.ByteReader) (uint64, error) {
+ firstByte, err := r.ReadByte()
+ if err != nil {
+ return 0, err
+ }
+ // the first two bits of the first byte encode the length
+ len := 1 << ((firstByte & 0xc0) >> 6)
+ b1 := firstByte & (0xff - 0xc0)
+ if len == 1 {
+ return uint64(b1), nil
+ }
+ b2, err := r.ReadByte()
+ if err != nil {
+ return 0, err
+ }
+ if len == 2 {
+ return uint64(b2) + uint64(b1)<<8, nil
+ }
+ b3, err := r.ReadByte()
+ if err != nil {
+ return 0, err
+ }
+ b4, err := r.ReadByte()
+ if err != nil {
+ return 0, err
+ }
+ if len == 4 {
+ return uint64(b4) + uint64(b3)<<8 + uint64(b2)<<16 + uint64(b1)<<24, nil
+ }
+ b5, err := r.ReadByte()
+ if err != nil {
+ return 0, err
+ }
+ b6, err := r.ReadByte()
+ if err != nil {
+ return 0, err
+ }
+ b7, err := r.ReadByte()
+ if err != nil {
+ return 0, err
+ }
+ b8, err := r.ReadByte()
+ if err != nil {
+ return 0, err
+ }
+ return uint64(b8) + uint64(b7)<<8 + uint64(b6)<<16 + uint64(b5)<<24 + uint64(b4)<<32 + uint64(b3)<<40 + uint64(b2)<<48 + uint64(b1)<<56, nil
+}
+
+// Write writes i in the QUIC varint format to w.
+func Write(w Writer, i uint64) {
+ if i <= maxVarInt1 {
+ w.WriteByte(uint8(i))
+ } else if i <= maxVarInt2 {
+ w.Write([]byte{uint8(i>>8) | 0x40, uint8(i)})
+ } else if i <= maxVarInt4 {
+ w.Write([]byte{uint8(i>>24) | 0x80, uint8(i >> 16), uint8(i >> 8), uint8(i)})
+ } else if i <= maxVarInt8 {
+ w.Write([]byte{
+ uint8(i>>56) | 0xc0, uint8(i >> 48), uint8(i >> 40), uint8(i >> 32),
+ uint8(i >> 24), uint8(i >> 16), uint8(i >> 8), uint8(i),
+ })
+ } else {
+ panic(fmt.Sprintf("%#x doesn't fit into 62 bits", i))
+ }
+}
+
+func Append(b []byte, i uint64) []byte {
+ if i <= maxVarInt1 {
+ return append(b, uint8(i))
+ }
+ if i <= maxVarInt2 {
+ return append(b, []byte{uint8(i>>8) | 0x40, uint8(i)}...)
+ }
+ if i <= maxVarInt4 {
+ return append(b, []byte{uint8(i>>24) | 0x80, uint8(i >> 16), uint8(i >> 8), uint8(i)}...)
+ }
+ if i <= maxVarInt8 {
+ return append(b, []byte{
+ uint8(i>>56) | 0xc0, uint8(i >> 48), uint8(i >> 40), uint8(i >> 32),
+ uint8(i >> 24), uint8(i >> 16), uint8(i >> 8), uint8(i),
+ }...)
+ }
+ panic(fmt.Sprintf("%#x doesn't fit into 62 bits", i))
+}
+
+// AppendWithLen append i in the QUIC varint format with the desired length.
+func AppendWithLen(b []byte, i uint64, length protocol.ByteCount) []byte {
+ if length != 1 && length != 2 && length != 4 && length != 8 {
+ panic("invalid varint length")
+ }
+ l := Len(i)
+ if l == length {
+ return Append(b, i)
+ }
+ if l > length {
+ panic(fmt.Sprintf("cannot encode %d in %d bytes", i, length))
+ }
+ if length == 2 {
+ b = append(b, 0b01000000)
+ } else if length == 4 {
+ b = append(b, 0b10000000)
+ } else if length == 8 {
+ b = append(b, 0b11000000)
+ }
+ for j := protocol.ByteCount(1); j < length-l; j++ {
+ b = append(b, 0)
+ }
+ for j := protocol.ByteCount(0); j < l; j++ {
+ b = append(b, uint8(i>>(8*(l-1-j))))
+ }
+ return b
+}
+
+// Len determines the number of bytes that will be needed to write the number i.
+func Len(i uint64) protocol.ByteCount {
+ if i <= maxVarInt1 {
+ return 1
+ }
+ if i <= maxVarInt2 {
+ return 2
+ }
+ if i <= maxVarInt4 {
+ return 4
+ }
+ if i <= maxVarInt8 {
+ return 8
+ }
+ // Don't use a fmt.Sprintf here to format the error message.
+ // The function would then exceed the inlining budget.
+ panic(struct {
+ message string
+ num uint64
+ }{"value doesn't fit into 62 bits: ", i})
+}
diff --git a/vendor/github.com/quic-go/quic-go/receive_stream.go b/vendor/github.com/quic-go/quic-go/receive_stream.go
new file mode 100644
index 0000000000..5d220e2225
--- /dev/null
+++ b/vendor/github.com/quic-go/quic-go/receive_stream.go
@@ -0,0 +1,329 @@
+package quic
+
+import (
+ "fmt"
+ "io"
+ "sync"
+ "time"
+
+ "github.com/quic-go/quic-go/internal/flowcontrol"
+ "github.com/quic-go/quic-go/internal/protocol"
+ "github.com/quic-go/quic-go/internal/qerr"
+ "github.com/quic-go/quic-go/internal/utils"
+ "github.com/quic-go/quic-go/internal/wire"
+)
+
+type receiveStreamI interface {
+ ReceiveStream
+
+ handleStreamFrame(*wire.StreamFrame) error
+ handleResetStreamFrame(*wire.ResetStreamFrame) error
+ closeForShutdown(error)
+ getWindowUpdate() protocol.ByteCount
+}
+
+type receiveStream struct {
+ mutex sync.Mutex
+
+ streamID protocol.StreamID
+
+ sender streamSender
+
+ frameQueue *frameSorter
+ finalOffset protocol.ByteCount
+
+ currentFrame []byte
+ currentFrameDone func()
+ currentFrameIsLast bool // is the currentFrame the last frame on this stream
+ readPosInFrame int
+
+ closeForShutdownErr error
+ cancelReadErr error
+ resetRemotelyErr *StreamError
+
+ closedForShutdown bool // set when CloseForShutdown() is called
+ finRead bool // set once we read a frame with a Fin
+ canceledRead bool // set when CancelRead() is called
+ resetRemotely bool // set when handleResetStreamFrame() is called
+
+ readChan chan struct{}
+ readOnce chan struct{} // cap: 1, to protect against concurrent use of Read
+ deadline time.Time
+
+ flowController flowcontrol.StreamFlowController
+}
+
+var (
+ _ ReceiveStream = &receiveStream{}
+ _ receiveStreamI = &receiveStream{}
+)
+
+func newReceiveStream(
+ streamID protocol.StreamID,
+ sender streamSender,
+ flowController flowcontrol.StreamFlowController,
+) *receiveStream {
+ return &receiveStream{
+ streamID: streamID,
+ sender: sender,
+ flowController: flowController,
+ frameQueue: newFrameSorter(),
+ readChan: make(chan struct{}, 1),
+ readOnce: make(chan struct{}, 1),
+ finalOffset: protocol.MaxByteCount,
+ }
+}
+
+func (s *receiveStream) StreamID() protocol.StreamID {
+ return s.streamID
+}
+
+// Read implements io.Reader. It is not thread safe!
+func (s *receiveStream) Read(p []byte) (int, error) {
+ // Concurrent use of Read is not permitted (and doesn't make any sense),
+ // but sometimes people do it anyway.
+ // Make sure that we only execute one call at any given time to avoid hard to debug failures.
+ s.readOnce <- struct{}{}
+ defer func() { <-s.readOnce }()
+
+ s.mutex.Lock()
+ completed, n, err := s.readImpl(p)
+ s.mutex.Unlock()
+
+ if completed {
+ s.sender.onStreamCompleted(s.streamID)
+ }
+ return n, err
+}
+
+func (s *receiveStream) readImpl(p []byte) (bool /*stream completed */, int, error) {
+ if s.finRead {
+ return false, 0, io.EOF
+ }
+ if s.canceledRead {
+ return false, 0, s.cancelReadErr
+ }
+ if s.resetRemotely {
+ return false, 0, s.resetRemotelyErr
+ }
+ if s.closedForShutdown {
+ return false, 0, s.closeForShutdownErr
+ }
+
+ var bytesRead int
+ var deadlineTimer *utils.Timer
+ for bytesRead < len(p) {
+ if s.currentFrame == nil || s.readPosInFrame >= len(s.currentFrame) {
+ s.dequeueNextFrame()
+ }
+ if s.currentFrame == nil && bytesRead > 0 {
+ return false, bytesRead, s.closeForShutdownErr
+ }
+
+ for {
+ // Stop waiting on errors
+ if s.closedForShutdown {
+ return false, bytesRead, s.closeForShutdownErr
+ }
+ if s.canceledRead {
+ return false, bytesRead, s.cancelReadErr
+ }
+ if s.resetRemotely {
+ return false, bytesRead, s.resetRemotelyErr
+ }
+
+ deadline := s.deadline
+ if !deadline.IsZero() {
+ if !time.Now().Before(deadline) {
+ return false, bytesRead, errDeadline
+ }
+ if deadlineTimer == nil {
+ deadlineTimer = utils.NewTimer()
+ defer deadlineTimer.Stop()
+ }
+ deadlineTimer.Reset(deadline)
+ }
+
+ if s.currentFrame != nil || s.currentFrameIsLast {
+ break
+ }
+
+ s.mutex.Unlock()
+ if deadline.IsZero() {
+ <-s.readChan
+ } else {
+ select {
+ case <-s.readChan:
+ case <-deadlineTimer.Chan():
+ deadlineTimer.SetRead()
+ }
+ }
+ s.mutex.Lock()
+ if s.currentFrame == nil {
+ s.dequeueNextFrame()
+ }
+ }
+
+ if bytesRead > len(p) {
+ return false, bytesRead, fmt.Errorf("BUG: bytesRead (%d) > len(p) (%d) in stream.Read", bytesRead, len(p))
+ }
+ if s.readPosInFrame > len(s.currentFrame) {
+ return false, bytesRead, fmt.Errorf("BUG: readPosInFrame (%d) > frame.DataLen (%d) in stream.Read", s.readPosInFrame, len(s.currentFrame))
+ }
+
+ m := copy(p[bytesRead:], s.currentFrame[s.readPosInFrame:])
+ s.readPosInFrame += m
+ bytesRead += m
+
+ // when a RESET_STREAM was received, the was already informed about the final byteOffset for this stream
+ if !s.resetRemotely {
+ s.flowController.AddBytesRead(protocol.ByteCount(m))
+ }
+
+ if s.readPosInFrame >= len(s.currentFrame) && s.currentFrameIsLast {
+ s.finRead = true
+ return true, bytesRead, io.EOF
+ }
+ }
+ return false, bytesRead, nil
+}
+
+func (s *receiveStream) dequeueNextFrame() {
+ var offset protocol.ByteCount
+ // We're done with the last frame. Release the buffer.
+ if s.currentFrameDone != nil {
+ s.currentFrameDone()
+ }
+ offset, s.currentFrame, s.currentFrameDone = s.frameQueue.Pop()
+ s.currentFrameIsLast = offset+protocol.ByteCount(len(s.currentFrame)) >= s.finalOffset
+ s.readPosInFrame = 0
+}
+
+func (s *receiveStream) CancelRead(errorCode StreamErrorCode) {
+ s.mutex.Lock()
+ completed := s.cancelReadImpl(errorCode)
+ s.mutex.Unlock()
+
+ if completed {
+ s.flowController.Abandon()
+ s.sender.onStreamCompleted(s.streamID)
+ }
+}
+
+func (s *receiveStream) cancelReadImpl(errorCode qerr.StreamErrorCode) bool /* completed */ {
+ if s.finRead || s.canceledRead || s.resetRemotely {
+ return false
+ }
+ s.canceledRead = true
+ s.cancelReadErr = &StreamError{StreamID: s.streamID, ErrorCode: errorCode, Remote: false}
+ s.signalRead()
+ s.sender.queueControlFrame(&wire.StopSendingFrame{
+ StreamID: s.streamID,
+ ErrorCode: errorCode,
+ })
+ // We're done with this stream if the final offset was already received.
+ return s.finalOffset != protocol.MaxByteCount
+}
+
+func (s *receiveStream) handleStreamFrame(frame *wire.StreamFrame) error {
+ s.mutex.Lock()
+ completed, err := s.handleStreamFrameImpl(frame)
+ s.mutex.Unlock()
+
+ if completed {
+ s.flowController.Abandon()
+ s.sender.onStreamCompleted(s.streamID)
+ }
+ return err
+}
+
+func (s *receiveStream) handleStreamFrameImpl(frame *wire.StreamFrame) (bool /* completed */, error) {
+ maxOffset := frame.Offset + frame.DataLen()
+ if err := s.flowController.UpdateHighestReceived(maxOffset, frame.Fin); err != nil {
+ return false, err
+ }
+ var newlyRcvdFinalOffset bool
+ if frame.Fin {
+ newlyRcvdFinalOffset = s.finalOffset == protocol.MaxByteCount
+ s.finalOffset = maxOffset
+ }
+ if s.canceledRead {
+ return newlyRcvdFinalOffset, nil
+ }
+ if err := s.frameQueue.Push(frame.Data, frame.Offset, frame.PutBack); err != nil {
+ return false, err
+ }
+ s.signalRead()
+ return false, nil
+}
+
+func (s *receiveStream) handleResetStreamFrame(frame *wire.ResetStreamFrame) error {
+ s.mutex.Lock()
+ completed, err := s.handleResetStreamFrameImpl(frame)
+ s.mutex.Unlock()
+
+ if completed {
+ s.flowController.Abandon()
+ s.sender.onStreamCompleted(s.streamID)
+ }
+ return err
+}
+
+func (s *receiveStream) handleResetStreamFrameImpl(frame *wire.ResetStreamFrame) (bool /*completed */, error) {
+ if s.closedForShutdown {
+ return false, nil
+ }
+ if err := s.flowController.UpdateHighestReceived(frame.FinalSize, true); err != nil {
+ return false, err
+ }
+ newlyRcvdFinalOffset := s.finalOffset == protocol.MaxByteCount
+ s.finalOffset = frame.FinalSize
+
+ // ignore duplicate RESET_STREAM frames for this stream (after checking their final offset)
+ if s.resetRemotely {
+ return false, nil
+ }
+ s.resetRemotely = true
+ s.resetRemotelyErr = &StreamError{
+ StreamID: s.streamID,
+ ErrorCode: frame.ErrorCode,
+ Remote: true,
+ }
+ s.signalRead()
+ return newlyRcvdFinalOffset, nil
+}
+
+func (s *receiveStream) CloseRemote(offset protocol.ByteCount) {
+ s.handleStreamFrame(&wire.StreamFrame{Fin: true, Offset: offset})
+}
+
+func (s *receiveStream) SetReadDeadline(t time.Time) error {
+ s.mutex.Lock()
+ s.deadline = t
+ s.mutex.Unlock()
+ s.signalRead()
+ return nil
+}
+
+// CloseForShutdown closes a stream abruptly.
+// It makes Read unblock (and return the error) immediately.
+// The peer will NOT be informed about this: the stream is closed without sending a FIN or RESET.
+func (s *receiveStream) closeForShutdown(err error) {
+ s.mutex.Lock()
+ s.closedForShutdown = true
+ s.closeForShutdownErr = err
+ s.mutex.Unlock()
+ s.signalRead()
+}
+
+func (s *receiveStream) getWindowUpdate() protocol.ByteCount {
+ return s.flowController.GetWindowUpdate()
+}
+
+// signalRead performs a non-blocking send on the readChan
+func (s *receiveStream) signalRead() {
+ select {
+ case s.readChan <- struct{}{}:
+ default:
+ }
+}
diff --git a/vendor/github.com/quic-go/quic-go/retransmission_queue.go b/vendor/github.com/quic-go/quic-go/retransmission_queue.go
new file mode 100644
index 0000000000..2ce0b8931a
--- /dev/null
+++ b/vendor/github.com/quic-go/quic-go/retransmission_queue.go
@@ -0,0 +1,129 @@
+package quic
+
+import (
+ "fmt"
+
+ "github.com/quic-go/quic-go/internal/protocol"
+ "github.com/quic-go/quic-go/internal/wire"
+)
+
+type retransmissionQueue struct {
+ initial []wire.Frame
+ initialCryptoData []*wire.CryptoFrame
+
+ handshake []wire.Frame
+ handshakeCryptoData []*wire.CryptoFrame
+
+ appData []wire.Frame
+}
+
+func newRetransmissionQueue() *retransmissionQueue {
+ return &retransmissionQueue{}
+}
+
+func (q *retransmissionQueue) AddInitial(f wire.Frame) {
+ if cf, ok := f.(*wire.CryptoFrame); ok {
+ q.initialCryptoData = append(q.initialCryptoData, cf)
+ return
+ }
+ q.initial = append(q.initial, f)
+}
+
+func (q *retransmissionQueue) AddHandshake(f wire.Frame) {
+ if cf, ok := f.(*wire.CryptoFrame); ok {
+ q.handshakeCryptoData = append(q.handshakeCryptoData, cf)
+ return
+ }
+ q.handshake = append(q.handshake, f)
+}
+
+func (q *retransmissionQueue) HasInitialData() bool {
+ return len(q.initialCryptoData) > 0 || len(q.initial) > 0
+}
+
+func (q *retransmissionQueue) HasHandshakeData() bool {
+ return len(q.handshakeCryptoData) > 0 || len(q.handshake) > 0
+}
+
+func (q *retransmissionQueue) HasAppData() bool {
+ return len(q.appData) > 0
+}
+
+func (q *retransmissionQueue) AddAppData(f wire.Frame) {
+ if _, ok := f.(*wire.StreamFrame); ok {
+ panic("STREAM frames are handled with their respective streams.")
+ }
+ q.appData = append(q.appData, f)
+}
+
+func (q *retransmissionQueue) GetInitialFrame(maxLen protocol.ByteCount, v protocol.VersionNumber) wire.Frame {
+ if len(q.initialCryptoData) > 0 {
+ f := q.initialCryptoData[0]
+ newFrame, needsSplit := f.MaybeSplitOffFrame(maxLen, v)
+ if newFrame == nil && !needsSplit { // the whole frame fits
+ q.initialCryptoData = q.initialCryptoData[1:]
+ return f
+ }
+ if newFrame != nil { // frame was split. Leave the original frame in the queue.
+ return newFrame
+ }
+ }
+ if len(q.initial) == 0 {
+ return nil
+ }
+ f := q.initial[0]
+ if f.Length(v) > maxLen {
+ return nil
+ }
+ q.initial = q.initial[1:]
+ return f
+}
+
+func (q *retransmissionQueue) GetHandshakeFrame(maxLen protocol.ByteCount, v protocol.VersionNumber) wire.Frame {
+ if len(q.handshakeCryptoData) > 0 {
+ f := q.handshakeCryptoData[0]
+ newFrame, needsSplit := f.MaybeSplitOffFrame(maxLen, v)
+ if newFrame == nil && !needsSplit { // the whole frame fits
+ q.handshakeCryptoData = q.handshakeCryptoData[1:]
+ return f
+ }
+ if newFrame != nil { // frame was split. Leave the original frame in the queue.
+ return newFrame
+ }
+ }
+ if len(q.handshake) == 0 {
+ return nil
+ }
+ f := q.handshake[0]
+ if f.Length(v) > maxLen {
+ return nil
+ }
+ q.handshake = q.handshake[1:]
+ return f
+}
+
+func (q *retransmissionQueue) GetAppDataFrame(maxLen protocol.ByteCount, v protocol.VersionNumber) wire.Frame {
+ if len(q.appData) == 0 {
+ return nil
+ }
+ f := q.appData[0]
+ if f.Length(v) > maxLen {
+ return nil
+ }
+ q.appData = q.appData[1:]
+ return f
+}
+
+func (q *retransmissionQueue) DropPackets(encLevel protocol.EncryptionLevel) {
+ //nolint:exhaustive // Can only drop Initial and Handshake packet number space.
+ switch encLevel {
+ case protocol.EncryptionInitial:
+ q.initial = nil
+ q.initialCryptoData = nil
+ case protocol.EncryptionHandshake:
+ q.handshake = nil
+ q.handshakeCryptoData = nil
+ default:
+ panic(fmt.Sprintf("unexpected encryption level: %s", encLevel))
+ }
+}
diff --git a/vendor/github.com/quic-go/quic-go/send_conn.go b/vendor/github.com/quic-go/quic-go/send_conn.go
new file mode 100644
index 0000000000..c53ebdfab1
--- /dev/null
+++ b/vendor/github.com/quic-go/quic-go/send_conn.go
@@ -0,0 +1,74 @@
+package quic
+
+import (
+ "net"
+)
+
+// A sendConn allows sending using a simple Write() on a non-connected packet conn.
+type sendConn interface {
+ Write([]byte) error
+ Close() error
+ LocalAddr() net.Addr
+ RemoteAddr() net.Addr
+}
+
+type sconn struct {
+ rawConn
+
+ remoteAddr net.Addr
+ info *packetInfo
+ oob []byte
+}
+
+var _ sendConn = &sconn{}
+
+func newSendConn(c rawConn, remote net.Addr, info *packetInfo) sendConn {
+ return &sconn{
+ rawConn: c,
+ remoteAddr: remote,
+ info: info,
+ oob: info.OOB(),
+ }
+}
+
+func (c *sconn) Write(p []byte) error {
+ _, err := c.WritePacket(p, c.remoteAddr, c.oob)
+ return err
+}
+
+func (c *sconn) RemoteAddr() net.Addr {
+ return c.remoteAddr
+}
+
+func (c *sconn) LocalAddr() net.Addr {
+ addr := c.rawConn.LocalAddr()
+ if c.info != nil {
+ if udpAddr, ok := addr.(*net.UDPAddr); ok {
+ addrCopy := *udpAddr
+ addrCopy.IP = c.info.addr
+ addr = &addrCopy
+ }
+ }
+ return addr
+}
+
+type spconn struct {
+ net.PacketConn
+
+ remoteAddr net.Addr
+}
+
+var _ sendConn = &spconn{}
+
+func newSendPconn(c net.PacketConn, remote net.Addr) sendConn {
+ return &spconn{PacketConn: c, remoteAddr: remote}
+}
+
+func (c *spconn) Write(p []byte) error {
+ _, err := c.WriteTo(p, c.remoteAddr)
+ return err
+}
+
+func (c *spconn) RemoteAddr() net.Addr {
+ return c.remoteAddr
+}
diff --git a/vendor/github.com/quic-go/quic-go/send_queue.go b/vendor/github.com/quic-go/quic-go/send_queue.go
new file mode 100644
index 0000000000..9eafcd374b
--- /dev/null
+++ b/vendor/github.com/quic-go/quic-go/send_queue.go
@@ -0,0 +1,95 @@
+package quic
+
+type sender interface {
+ Send(p *packetBuffer)
+ Run() error
+ WouldBlock() bool
+ Available() <-chan struct{}
+ Close()
+}
+
+type sendQueue struct {
+ queue chan *packetBuffer
+ closeCalled chan struct{} // runStopped when Close() is called
+ runStopped chan struct{} // runStopped when the run loop returns
+ available chan struct{}
+ conn sendConn
+}
+
+var _ sender = &sendQueue{}
+
+const sendQueueCapacity = 8
+
+func newSendQueue(conn sendConn) sender {
+ return &sendQueue{
+ conn: conn,
+ runStopped: make(chan struct{}),
+ closeCalled: make(chan struct{}),
+ available: make(chan struct{}, 1),
+ queue: make(chan *packetBuffer, sendQueueCapacity),
+ }
+}
+
+// Send sends out a packet. It's guaranteed to not block.
+// Callers need to make sure that there's actually space in the send queue by calling WouldBlock.
+// Otherwise Send will panic.
+func (h *sendQueue) Send(p *packetBuffer) {
+ select {
+ case h.queue <- p:
+ // clear available channel if we've reached capacity
+ if len(h.queue) == sendQueueCapacity {
+ select {
+ case <-h.available:
+ default:
+ }
+ }
+ case <-h.runStopped:
+ default:
+ panic("sendQueue.Send would have blocked")
+ }
+}
+
+func (h *sendQueue) WouldBlock() bool {
+ return len(h.queue) == sendQueueCapacity
+}
+
+func (h *sendQueue) Available() <-chan struct{} {
+ return h.available
+}
+
+func (h *sendQueue) Run() error {
+ defer close(h.runStopped)
+ var shouldClose bool
+ for {
+ if shouldClose && len(h.queue) == 0 {
+ return nil
+ }
+ select {
+ case <-h.closeCalled:
+ h.closeCalled = nil // prevent this case from being selected again
+ // make sure that all queued packets are actually sent out
+ shouldClose = true
+ case p := <-h.queue:
+ if err := h.conn.Write(p.Data); err != nil {
+ // This additional check enables:
+ // 1. Checking for "datagram too large" message from the kernel, as such,
+ // 2. Path MTU discovery,and
+ // 3. Eventual detection of loss PingFrame.
+ if !isMsgSizeErr(err) {
+ return err
+ }
+ }
+ p.Release()
+ select {
+ case h.available <- struct{}{}:
+ default:
+ }
+ }
+ }
+}
+
+func (h *sendQueue) Close() {
+ close(h.closeCalled)
+ // wait until the run loop returned
+ <-h.runStopped
+}
diff --git a/vendor/github.com/quic-go/quic-go/send_stream.go b/vendor/github.com/quic-go/quic-go/send_stream.go
new file mode 100644
index 0000000000..6b1d7b179a
--- /dev/null
+++ b/vendor/github.com/quic-go/quic-go/send_stream.go
@@ -0,0 +1,493 @@
+package quic
+
+import (
+ "context"
+ "fmt"
+ "sync"
+ "time"
+
+ "github.com/quic-go/quic-go/internal/ackhandler"
+ "github.com/quic-go/quic-go/internal/flowcontrol"
+ "github.com/quic-go/quic-go/internal/protocol"
+ "github.com/quic-go/quic-go/internal/qerr"
+ "github.com/quic-go/quic-go/internal/utils"
+ "github.com/quic-go/quic-go/internal/wire"
+)
+
+type sendStreamI interface {
+ SendStream
+ handleStopSendingFrame(*wire.StopSendingFrame)
+ hasData() bool
+ popStreamFrame(maxBytes protocol.ByteCount, v protocol.VersionNumber) (*ackhandler.Frame, bool)
+ closeForShutdown(error)
+ updateSendWindow(protocol.ByteCount)
+}
+
+type sendStream struct {
+ mutex sync.Mutex
+
+ numOutstandingFrames int64
+ retransmissionQueue []*wire.StreamFrame
+
+ ctx context.Context
+ ctxCancel context.CancelFunc
+
+ streamID protocol.StreamID
+ sender streamSender
+
+ writeOffset protocol.ByteCount
+
+ cancelWriteErr error
+ closeForShutdownErr error
+
+ closedForShutdown bool // set when CloseForShutdown() is called
+ finishedWriting bool // set once Close() is called
+ canceledWrite bool // set when CancelWrite() is called, or a STOP_SENDING frame is received
+ finSent bool // set when a STREAM_FRAME with FIN bit has been sent
+ completed bool // set when this stream has been reported to the streamSender as completed
+
+ dataForWriting []byte // during a Write() call, this slice is the part of p that still needs to be sent out
+ nextFrame *wire.StreamFrame
+
+ writeChan chan struct{}
+ writeOnce chan struct{}
+ deadline time.Time
+
+ flowController flowcontrol.StreamFlowController
+}
+
+var (
+ _ SendStream = &sendStream{}
+ _ sendStreamI = &sendStream{}
+)
+
+func newSendStream(
+ streamID protocol.StreamID,
+ sender streamSender,
+ flowController flowcontrol.StreamFlowController,
+) *sendStream {
+ s := &sendStream{
+ streamID: streamID,
+ sender: sender,
+ flowController: flowController,
+ writeChan: make(chan struct{}, 1),
+ writeOnce: make(chan struct{}, 1), // cap: 1, to protect against concurrent use of Write
+ }
+ s.ctx, s.ctxCancel = context.WithCancel(context.Background())
+ return s
+}
+
+func (s *sendStream) StreamID() protocol.StreamID {
+ return s.streamID // same for receiveStream and sendStream
+}
+
+func (s *sendStream) Write(p []byte) (int, error) {
+ // Concurrent use of Write is not permitted (and doesn't make any sense),
+ // but sometimes people do it anyway.
+ // Make sure that we only execute one call at any given time to avoid hard to debug failures.
+ s.writeOnce <- struct{}{}
+ defer func() { <-s.writeOnce }()
+
+ s.mutex.Lock()
+ defer s.mutex.Unlock()
+
+ if s.finishedWriting {
+ return 0, fmt.Errorf("write on closed stream %d", s.streamID)
+ }
+ if s.canceledWrite {
+ return 0, s.cancelWriteErr
+ }
+ if s.closeForShutdownErr != nil {
+ return 0, s.closeForShutdownErr
+ }
+ if !s.deadline.IsZero() && !time.Now().Before(s.deadline) {
+ return 0, errDeadline
+ }
+ if len(p) == 0 {
+ return 0, nil
+ }
+
+ s.dataForWriting = p
+
+ var (
+ deadlineTimer *utils.Timer
+ bytesWritten int
+ notifiedSender bool
+ )
+ for {
+ var copied bool
+ var deadline time.Time
+ // As soon as dataForWriting becomes smaller than a certain size x, we copy all the data to a STREAM frame (s.nextFrame),
+ // which can then be popped the next time we assemble a packet.
+ // This allows us to return Write() when all data but x bytes have been sent out.
+ // When the user now calls Close(), this is much more likely to happen before we popped that last STREAM frame,
+ // allowing us to set the FIN bit on that frame (instead of sending an empty STREAM frame with FIN).
+ if s.canBufferStreamFrame() && len(s.dataForWriting) > 0 {
+ if s.nextFrame == nil {
+ f := wire.GetStreamFrame()
+ f.Offset = s.writeOffset
+ f.StreamID = s.streamID
+ f.DataLenPresent = true
+ f.Data = f.Data[:len(s.dataForWriting)]
+ copy(f.Data, s.dataForWriting)
+ s.nextFrame = f
+ } else {
+ l := len(s.nextFrame.Data)
+ s.nextFrame.Data = s.nextFrame.Data[:l+len(s.dataForWriting)]
+ copy(s.nextFrame.Data[l:], s.dataForWriting)
+ }
+ s.dataForWriting = nil
+ bytesWritten = len(p)
+ copied = true
+ } else {
+ bytesWritten = len(p) - len(s.dataForWriting)
+ deadline = s.deadline
+ if !deadline.IsZero() {
+ if !time.Now().Before(deadline) {
+ s.dataForWriting = nil
+ return bytesWritten, errDeadline
+ }
+ if deadlineTimer == nil {
+ deadlineTimer = utils.NewTimer()
+ defer deadlineTimer.Stop()
+ }
+ deadlineTimer.Reset(deadline)
+ }
+ if s.dataForWriting == nil || s.canceledWrite || s.closedForShutdown {
+ break
+ }
+ }
+
+ s.mutex.Unlock()
+ if !notifiedSender {
+ s.sender.onHasStreamData(s.streamID) // must be called without holding the mutex
+ notifiedSender = true
+ }
+ if copied {
+ s.mutex.Lock()
+ break
+ }
+ if deadline.IsZero() {
+ <-s.writeChan
+ } else {
+ select {
+ case <-s.writeChan:
+ case <-deadlineTimer.Chan():
+ deadlineTimer.SetRead()
+ }
+ }
+ s.mutex.Lock()
+ }
+
+ if bytesWritten == len(p) {
+ return bytesWritten, nil
+ }
+ if s.closeForShutdownErr != nil {
+ return bytesWritten, s.closeForShutdownErr
+ } else if s.cancelWriteErr != nil {
+ return bytesWritten, s.cancelWriteErr
+ }
+ return bytesWritten, nil
+}
+
+func (s *sendStream) canBufferStreamFrame() bool {
+ var l protocol.ByteCount
+ if s.nextFrame != nil {
+ l = s.nextFrame.DataLen()
+ }
+ return l+protocol.ByteCount(len(s.dataForWriting)) <= protocol.MaxPacketBufferSize
+}
+
+// popStreamFrame returns the next STREAM frame that is supposed to be sent on this stream
+// maxBytes is the maximum length this frame (including frame header) will have.
+func (s *sendStream) popStreamFrame(maxBytes protocol.ByteCount, v protocol.VersionNumber) (*ackhandler.Frame, bool /* has more data to send */) {
+ s.mutex.Lock()
+ f, hasMoreData := s.popNewOrRetransmittedStreamFrame(maxBytes, v)
+ if f != nil {
+ s.numOutstandingFrames++
+ }
+ s.mutex.Unlock()
+
+ if f == nil {
+ return nil, hasMoreData
+ }
+ af := ackhandler.GetFrame()
+ af.Frame = f
+ af.OnLost = s.queueRetransmission
+ af.OnAcked = s.frameAcked
+ return af, hasMoreData
+}
+
+func (s *sendStream) popNewOrRetransmittedStreamFrame(maxBytes protocol.ByteCount, v protocol.VersionNumber) (*wire.StreamFrame, bool /* has more data to send */) {
+ if s.canceledWrite || s.closeForShutdownErr != nil {
+ return nil, false
+ }
+
+ if len(s.retransmissionQueue) > 0 {
+ f, hasMoreRetransmissions := s.maybeGetRetransmission(maxBytes, v)
+ if f != nil || hasMoreRetransmissions {
+ if f == nil {
+ return nil, true
+ }
+ // We always claim that we have more data to send.
+ // This might be incorrect, in which case there'll be a spurious call to popStreamFrame in the future.
+ return f, true
+ }
+ }
+
+ if len(s.dataForWriting) == 0 && s.nextFrame == nil {
+ if s.finishedWriting && !s.finSent {
+ s.finSent = true
+ return &wire.StreamFrame{
+ StreamID: s.streamID,
+ Offset: s.writeOffset,
+ DataLenPresent: true,
+ Fin: true,
+ }, false
+ }
+ return nil, false
+ }
+
+ sendWindow := s.flowController.SendWindowSize()
+ if sendWindow == 0 {
+ if isBlocked, offset := s.flowController.IsNewlyBlocked(); isBlocked {
+ s.sender.queueControlFrame(&wire.StreamDataBlockedFrame{
+ StreamID: s.streamID,
+ MaximumStreamData: offset,
+ })
+ return nil, false
+ }
+ return nil, true
+ }
+
+ f, hasMoreData := s.popNewStreamFrame(maxBytes, sendWindow, v)
+ if dataLen := f.DataLen(); dataLen > 0 {
+ s.writeOffset += f.DataLen()
+ s.flowController.AddBytesSent(f.DataLen())
+ }
+ f.Fin = s.finishedWriting && s.dataForWriting == nil && s.nextFrame == nil && !s.finSent
+ if f.Fin {
+ s.finSent = true
+ }
+ return f, hasMoreData
+}
+
+func (s *sendStream) popNewStreamFrame(maxBytes, sendWindow protocol.ByteCount, v protocol.VersionNumber) (*wire.StreamFrame, bool) {
+ if s.nextFrame != nil {
+ nextFrame := s.nextFrame
+ s.nextFrame = nil
+
+ maxDataLen := utils.Min(sendWindow, nextFrame.MaxDataLen(maxBytes, v))
+ if nextFrame.DataLen() > maxDataLen {
+ s.nextFrame = wire.GetStreamFrame()
+ s.nextFrame.StreamID = s.streamID
+ s.nextFrame.Offset = s.writeOffset + maxDataLen
+ s.nextFrame.Data = s.nextFrame.Data[:nextFrame.DataLen()-maxDataLen]
+ s.nextFrame.DataLenPresent = true
+ copy(s.nextFrame.Data, nextFrame.Data[maxDataLen:])
+ nextFrame.Data = nextFrame.Data[:maxDataLen]
+ } else {
+ s.signalWrite()
+ }
+ return nextFrame, s.nextFrame != nil || s.dataForWriting != nil
+ }
+
+ f := wire.GetStreamFrame()
+ f.Fin = false
+ f.StreamID = s.streamID
+ f.Offset = s.writeOffset
+ f.DataLenPresent = true
+ f.Data = f.Data[:0]
+
+ hasMoreData := s.popNewStreamFrameWithoutBuffer(f, maxBytes, sendWindow, v)
+ if len(f.Data) == 0 && !f.Fin {
+ f.PutBack()
+ return nil, hasMoreData
+ }
+ return f, hasMoreData
+}
+
+func (s *sendStream) popNewStreamFrameWithoutBuffer(f *wire.StreamFrame, maxBytes, sendWindow protocol.ByteCount, v protocol.VersionNumber) bool {
+ maxDataLen := f.MaxDataLen(maxBytes, v)
+ if maxDataLen == 0 { // a STREAM frame must have at least one byte of data
+ return s.dataForWriting != nil || s.nextFrame != nil || s.finishedWriting
+ }
+ s.getDataForWriting(f, utils.Min(maxDataLen, sendWindow))
+
+ return s.dataForWriting != nil || s.nextFrame != nil || s.finishedWriting
+}
+
+func (s *sendStream) maybeGetRetransmission(maxBytes protocol.ByteCount, v protocol.VersionNumber) (*wire.StreamFrame, bool /* has more retransmissions */) {
+ f := s.retransmissionQueue[0]
+ newFrame, needsSplit := f.MaybeSplitOffFrame(maxBytes, v)
+ if needsSplit {
+ return newFrame, true
+ }
+ s.retransmissionQueue = s.retransmissionQueue[1:]
+ return f, len(s.retransmissionQueue) > 0
+}
+
+func (s *sendStream) hasData() bool {
+ s.mutex.Lock()
+ hasData := len(s.dataForWriting) > 0
+ s.mutex.Unlock()
+ return hasData
+}
+
+func (s *sendStream) getDataForWriting(f *wire.StreamFrame, maxBytes protocol.ByteCount) {
+ if protocol.ByteCount(len(s.dataForWriting)) <= maxBytes {
+ f.Data = f.Data[:len(s.dataForWriting)]
+ copy(f.Data, s.dataForWriting)
+ s.dataForWriting = nil
+ s.signalWrite()
+ return
+ }
+ f.Data = f.Data[:maxBytes]
+ copy(f.Data, s.dataForWriting)
+ s.dataForWriting = s.dataForWriting[maxBytes:]
+ if s.canBufferStreamFrame() {
+ s.signalWrite()
+ }
+}
+
+func (s *sendStream) frameAcked(f wire.Frame) {
+ f.(*wire.StreamFrame).PutBack()
+
+ s.mutex.Lock()
+ if s.canceledWrite {
+ s.mutex.Unlock()
+ return
+ }
+ s.numOutstandingFrames--
+ if s.numOutstandingFrames < 0 {
+ panic("numOutStandingFrames negative")
+ }
+ newlyCompleted := s.isNewlyCompleted()
+ s.mutex.Unlock()
+
+ if newlyCompleted {
+ s.sender.onStreamCompleted(s.streamID)
+ }
+}
+
+func (s *sendStream) isNewlyCompleted() bool {
+ completed := (s.finSent || s.canceledWrite) && s.numOutstandingFrames == 0 && len(s.retransmissionQueue) == 0
+ if completed && !s.completed {
+ s.completed = true
+ return true
+ }
+ return false
+}
+
+func (s *sendStream) queueRetransmission(f wire.Frame) {
+ sf := f.(*wire.StreamFrame)
+ sf.DataLenPresent = true
+ s.mutex.Lock()
+ if s.canceledWrite {
+ s.mutex.Unlock()
+ return
+ }
+ s.retransmissionQueue = append(s.retransmissionQueue, sf)
+ s.numOutstandingFrames--
+ if s.numOutstandingFrames < 0 {
+ panic("numOutStandingFrames negative")
+ }
+ s.mutex.Unlock()
+
+ s.sender.onHasStreamData(s.streamID)
+}
+
+func (s *sendStream) Close() error {
+ s.mutex.Lock()
+ if s.closedForShutdown {
+ s.mutex.Unlock()
+ return nil
+ }
+ if s.canceledWrite {
+ s.mutex.Unlock()
+ return fmt.Errorf("close called for canceled stream %d", s.streamID)
+ }
+ s.ctxCancel()
+ s.finishedWriting = true
+ s.mutex.Unlock()
+
+ s.sender.onHasStreamData(s.streamID) // need to send the FIN, must be called without holding the mutex
+ return nil
+}
+
+func (s *sendStream) CancelWrite(errorCode StreamErrorCode) {
+ s.cancelWriteImpl(errorCode, false)
+}
+
+// must be called after locking the mutex
+func (s *sendStream) cancelWriteImpl(errorCode qerr.StreamErrorCode, remote bool) {
+ s.mutex.Lock()
+ if s.canceledWrite {
+ s.mutex.Unlock()
+ return
+ }
+ s.ctxCancel()
+ s.canceledWrite = true
+ s.cancelWriteErr = &StreamError{StreamID: s.streamID, ErrorCode: errorCode, Remote: remote}
+ s.numOutstandingFrames = 0
+ s.retransmissionQueue = nil
+ newlyCompleted := s.isNewlyCompleted()
+ s.mutex.Unlock()
+
+ s.signalWrite()
+ s.sender.queueControlFrame(&wire.ResetStreamFrame{
+ StreamID: s.streamID,
+ FinalSize: s.writeOffset,
+ ErrorCode: errorCode,
+ })
+ if newlyCompleted {
+ s.sender.onStreamCompleted(s.streamID)
+ }
+}
+
+func (s *sendStream) updateSendWindow(limit protocol.ByteCount) {
+ s.mutex.Lock()
+ hasStreamData := s.dataForWriting != nil || s.nextFrame != nil
+ s.mutex.Unlock()
+
+ s.flowController.UpdateSendWindow(limit)
+ if hasStreamData {
+ s.sender.onHasStreamData(s.streamID)
+ }
+}
+
+func (s *sendStream) handleStopSendingFrame(frame *wire.StopSendingFrame) {
+ s.cancelWriteImpl(frame.ErrorCode, true)
+}
+
+func (s *sendStream) Context() context.Context {
+ return s.ctx
+}
+
+func (s *sendStream) SetWriteDeadline(t time.Time) error {
+ s.mutex.Lock()
+ s.deadline = t
+ s.mutex.Unlock()
+ s.signalWrite()
+ return nil
+}
+
+// CloseForShutdown closes a stream abruptly.
+// It makes Write unblock (and return the error) immediately.
+// The peer will NOT be informed about this: the stream is closed without sending a FIN or RST.
+func (s *sendStream) closeForShutdown(err error) {
+ s.mutex.Lock()
+ s.ctxCancel()
+ s.closedForShutdown = true
+ s.closeForShutdownErr = err
+ s.mutex.Unlock()
+ s.signalWrite()
+}
+
+// signalWrite performs a non-blocking send on the writeChan
+func (s *sendStream) signalWrite() {
+ select {
+ case s.writeChan <- struct{}{}:
+ default:
+ }
+}
diff --git a/vendor/github.com/quic-go/quic-go/server.go b/vendor/github.com/quic-go/quic-go/server.go
new file mode 100644
index 0000000000..d934d8bd5a
--- /dev/null
+++ b/vendor/github.com/quic-go/quic-go/server.go
@@ -0,0 +1,682 @@
+package quic
+
+import (
+ "context"
+ "crypto/rand"
+ "crypto/tls"
+ "errors"
+ "fmt"
+ "net"
+ "sync"
+ "sync/atomic"
+ "time"
+
+ "github.com/quic-go/quic-go/internal/handshake"
+ "github.com/quic-go/quic-go/internal/protocol"
+ "github.com/quic-go/quic-go/internal/qerr"
+ "github.com/quic-go/quic-go/internal/utils"
+ "github.com/quic-go/quic-go/internal/wire"
+ "github.com/quic-go/quic-go/logging"
+)
+
+// ErrServerClosed is returned by the Listener or EarlyListener's Accept method after a call to Close.
+var ErrServerClosed = errors.New("quic: Server closed")
+
+// packetHandler handles packets
+type packetHandler interface {
+ handlePacket(*receivedPacket)
+ shutdown()
+ destroy(error)
+ getPerspective() protocol.Perspective
+}
+
+type unknownPacketHandler interface {
+ handlePacket(*receivedPacket)
+ setCloseError(error)
+}
+
+type packetHandlerManager interface {
+ AddWithConnID(protocol.ConnectionID, protocol.ConnectionID, func() packetHandler) bool
+ Destroy() error
+ connRunner
+ SetServer(unknownPacketHandler)
+ CloseServer()
+}
+
+type quicConn interface {
+ EarlyConnection
+ earlyConnReady() <-chan struct{}
+ handlePacket(*receivedPacket)
+ GetVersion() protocol.VersionNumber
+ getPerspective() protocol.Perspective
+ run() error
+ destroy(error)
+ shutdown()
+}
+
+// A Listener of QUIC
+type baseServer struct {
+ mutex sync.Mutex
+
+ acceptEarlyConns bool
+
+ tlsConf *tls.Config
+ config *Config
+
+ conn rawConn
+ // If the server is started with ListenAddr, we create a packet conn.
+ // If it is started with Listen, we take a packet conn as a parameter.
+ createdPacketConn bool
+
+ tokenGenerator *handshake.TokenGenerator
+
+ connHandler packetHandlerManager
+
+ receivedPackets chan *receivedPacket
+
+ // set as a member, so they can be set in the tests
+ newConn func(
+ sendConn,
+ connRunner,
+ protocol.ConnectionID, /* original dest connection ID */
+ *protocol.ConnectionID, /* retry src connection ID */
+ protocol.ConnectionID, /* client dest connection ID */
+ protocol.ConnectionID, /* destination connection ID */
+ protocol.ConnectionID, /* source connection ID */
+ protocol.StatelessResetToken,
+ *Config,
+ *tls.Config,
+ *handshake.TokenGenerator,
+ bool, /* client address validated by an address validation token */
+ logging.ConnectionTracer,
+ uint64,
+ utils.Logger,
+ protocol.VersionNumber,
+ ) quicConn
+
+ serverError error
+ errorChan chan struct{}
+ closed bool
+ running chan struct{} // closed as soon as run() returns
+
+ connQueue chan quicConn
+ connQueueLen int32 // to be used as an atomic
+
+ logger utils.Logger
+}
+
+var (
+ _ Listener = &baseServer{}
+ _ unknownPacketHandler = &baseServer{}
+)
+
+type earlyServer struct{ *baseServer }
+
+var _ EarlyListener = &earlyServer{}
+
+func (s *earlyServer) Accept(ctx context.Context) (EarlyConnection, error) {
+ return s.baseServer.accept(ctx)
+}
+
+// ListenAddr creates a QUIC server listening on a given address.
+// The tls.Config must not be nil and must contain a certificate configuration.
+// The quic.Config may be nil, in that case the default values will be used.
+func ListenAddr(addr string, tlsConf *tls.Config, config *Config) (Listener, error) {
+ return listenAddr(addr, tlsConf, config, false)
+}
+
+// ListenAddrEarly works like ListenAddr, but it returns connections before the handshake completes.
+func ListenAddrEarly(addr string, tlsConf *tls.Config, config *Config) (EarlyListener, error) {
+ s, err := listenAddr(addr, tlsConf, config, true)
+ if err != nil {
+ return nil, err
+ }
+ return &earlyServer{s}, nil
+}
+
+func listenAddr(addr string, tlsConf *tls.Config, config *Config, acceptEarly bool) (*baseServer, error) {
+ udpAddr, err := net.ResolveUDPAddr("udp", addr)
+ if err != nil {
+ return nil, err
+ }
+ conn, err := net.ListenUDP("udp", udpAddr)
+ if err != nil {
+ return nil, err
+ }
+ serv, err := listen(conn, tlsConf, config, acceptEarly)
+ if err != nil {
+ return nil, err
+ }
+ serv.createdPacketConn = true
+ return serv, nil
+}
+
+// Listen listens for QUIC connections on a given net.PacketConn. If the
+// PacketConn satisfies the OOBCapablePacketConn interface (as a net.UDPConn
+// does), ECN and packet info support will be enabled. In this case, ReadMsgUDP
+// and WriteMsgUDP will be used instead of ReadFrom and WriteTo to read/write
+// packets. A single net.PacketConn only be used for a single call to Listen.
+// The PacketConn can be used for simultaneous calls to Dial. QUIC connection
+// IDs are used for demultiplexing the different connections. The tls.Config
+// must not be nil and must contain a certificate configuration. The
+// tls.Config.CipherSuites allows setting of TLS 1.3 cipher suites. Furthermore,
+// it must define an application control (using NextProtos). The quic.Config may
+// be nil, in that case the default values will be used.
+func Listen(conn net.PacketConn, tlsConf *tls.Config, config *Config) (Listener, error) {
+ return listen(conn, tlsConf, config, false)
+}
+
+// ListenEarly works like Listen, but it returns connections before the handshake completes.
+func ListenEarly(conn net.PacketConn, tlsConf *tls.Config, config *Config) (EarlyListener, error) {
+ s, err := listen(conn, tlsConf, config, true)
+ if err != nil {
+ return nil, err
+ }
+ return &earlyServer{s}, nil
+}
+
+func listen(conn net.PacketConn, tlsConf *tls.Config, config *Config, acceptEarly bool) (*baseServer, error) {
+ if tlsConf == nil {
+ return nil, errors.New("quic: tls.Config not set")
+ }
+ if err := validateConfig(config); err != nil {
+ return nil, err
+ }
+ config = populateServerConfig(config)
+ for _, v := range config.Versions {
+ if !protocol.IsValidVersion(v) {
+ return nil, fmt.Errorf("%s is not a valid QUIC version", v)
+ }
+ }
+
+ connHandler, err := getMultiplexer().AddConn(conn, config.ConnectionIDGenerator.ConnectionIDLen(), config.StatelessResetKey, config.Tracer)
+ if err != nil {
+ return nil, err
+ }
+ tokenGenerator, err := handshake.NewTokenGenerator(rand.Reader)
+ if err != nil {
+ return nil, err
+ }
+ c, err := wrapConn(conn)
+ if err != nil {
+ return nil, err
+ }
+ s := &baseServer{
+ conn: c,
+ tlsConf: tlsConf,
+ config: config,
+ tokenGenerator: tokenGenerator,
+ connHandler: connHandler,
+ connQueue: make(chan quicConn),
+ errorChan: make(chan struct{}),
+ running: make(chan struct{}),
+ receivedPackets: make(chan *receivedPacket, protocol.MaxServerUnprocessedPackets),
+ newConn: newConnection,
+ logger: utils.DefaultLogger.WithPrefix("server"),
+ acceptEarlyConns: acceptEarly,
+ }
+ go s.run()
+ connHandler.SetServer(s)
+ s.logger.Debugf("Listening for %s connections on %s", conn.LocalAddr().Network(), conn.LocalAddr().String())
+ return s, nil
+}
+
+func (s *baseServer) run() {
+ defer close(s.running)
+ for {
+ select {
+ case <-s.errorChan:
+ return
+ default:
+ }
+ select {
+ case <-s.errorChan:
+ return
+ case p := <-s.receivedPackets:
+ if bufferStillInUse := s.handlePacketImpl(p); !bufferStillInUse {
+ p.buffer.Release()
+ }
+ }
+ }
+}
+
+// Accept returns connections that already completed the handshake.
+// It is only valid if acceptEarlyConns is false.
+func (s *baseServer) Accept(ctx context.Context) (Connection, error) {
+ return s.accept(ctx)
+}
+
+func (s *baseServer) accept(ctx context.Context) (quicConn, error) {
+ select {
+ case <-ctx.Done():
+ return nil, ctx.Err()
+ case conn := <-s.connQueue:
+ atomic.AddInt32(&s.connQueueLen, -1)
+ return conn, nil
+ case <-s.errorChan:
+ return nil, s.serverError
+ }
+}
+
+// Close the server
+func (s *baseServer) Close() error {
+ s.mutex.Lock()
+ if s.closed {
+ s.mutex.Unlock()
+ return nil
+ }
+ if s.serverError == nil {
+ s.serverError = ErrServerClosed
+ }
+ // If the server was started with ListenAddr, we created the packet conn.
+ // We need to close it in order to make the go routine reading from that conn return.
+ createdPacketConn := s.createdPacketConn
+ s.closed = true
+ close(s.errorChan)
+ s.mutex.Unlock()
+
+ <-s.running
+ s.connHandler.CloseServer()
+ if createdPacketConn {
+ return s.connHandler.Destroy()
+ }
+ return nil
+}
+
+func (s *baseServer) setCloseError(e error) {
+ s.mutex.Lock()
+ defer s.mutex.Unlock()
+ if s.closed {
+ return
+ }
+ s.closed = true
+ s.serverError = e
+ close(s.errorChan)
+}
+
+// Addr returns the server's network address
+func (s *baseServer) Addr() net.Addr {
+ return s.conn.LocalAddr()
+}
+
+func (s *baseServer) handlePacket(p *receivedPacket) {
+ select {
+ case s.receivedPackets <- p:
+ default:
+ s.logger.Debugf("Dropping packet from %s (%d bytes). Server receive queue full.", p.remoteAddr, p.Size())
+ if s.config.Tracer != nil {
+ s.config.Tracer.DroppedPacket(p.remoteAddr, logging.PacketTypeNotDetermined, p.Size(), logging.PacketDropDOSPrevention)
+ }
+ }
+}
+
+func (s *baseServer) handlePacketImpl(p *receivedPacket) bool /* is the buffer still in use? */ {
+ if wire.IsVersionNegotiationPacket(p.data) {
+ s.logger.Debugf("Dropping Version Negotiation packet.")
+ if s.config.Tracer != nil {
+ s.config.Tracer.DroppedPacket(p.remoteAddr, logging.PacketTypeVersionNegotiation, p.Size(), logging.PacketDropUnexpectedPacket)
+ }
+ return false
+ }
+ // Short header packets should never end up here in the first place
+ if !wire.IsLongHeaderPacket(p.data[0]) {
+ panic(fmt.Sprintf("misrouted packet: %#v", p.data))
+ }
+ v, err := wire.ParseVersion(p.data)
+ // send a Version Negotiation Packet if the client is speaking a different protocol version
+ if err != nil || !protocol.IsSupportedVersion(s.config.Versions, v) {
+ if err != nil || p.Size() < protocol.MinUnknownVersionPacketSize {
+ s.logger.Debugf("Dropping a packet with an unknown version that is too small (%d bytes)", p.Size())
+ if s.config.Tracer != nil {
+ s.config.Tracer.DroppedPacket(p.remoteAddr, logging.PacketTypeNotDetermined, p.Size(), logging.PacketDropUnexpectedPacket)
+ }
+ return false
+ }
+ _, src, dest, err := wire.ParseArbitraryLenConnectionIDs(p.data)
+ if err != nil { // should never happen
+ s.logger.Debugf("Dropping a packet with an unknown version for which we failed to parse connection IDs")
+ if s.config.Tracer != nil {
+ s.config.Tracer.DroppedPacket(p.remoteAddr, logging.PacketTypeNotDetermined, p.Size(), logging.PacketDropUnexpectedPacket)
+ }
+ return false
+ }
+ if !s.config.DisableVersionNegotiationPackets {
+ go s.sendVersionNegotiationPacket(p.remoteAddr, src, dest, p.info.OOB())
+ }
+ return false
+ }
+ // If we're creating a new connection, the packet will be passed to the connection.
+ // The header will then be parsed again.
+ hdr, _, _, err := wire.ParsePacket(p.data)
+ if err != nil {
+ if s.config.Tracer != nil {
+ s.config.Tracer.DroppedPacket(p.remoteAddr, logging.PacketTypeNotDetermined, p.Size(), logging.PacketDropHeaderParseError)
+ }
+ s.logger.Debugf("Error parsing packet: %s", err)
+ return false
+ }
+ if hdr.Type == protocol.PacketTypeInitial && p.Size() < protocol.MinInitialPacketSize {
+ s.logger.Debugf("Dropping a packet that is too small to be a valid Initial (%d bytes)", p.Size())
+ if s.config.Tracer != nil {
+ s.config.Tracer.DroppedPacket(p.remoteAddr, logging.PacketTypeInitial, p.Size(), logging.PacketDropUnexpectedPacket)
+ }
+ return false
+ }
+
+ if hdr.Type != protocol.PacketTypeInitial {
+ // Drop long header packets.
+ // There's little point in sending a Stateless Reset, since the client
+ // might not have received the token yet.
+ s.logger.Debugf("Dropping long header packet of type %s (%d bytes)", hdr.Type, len(p.data))
+ if s.config.Tracer != nil {
+ s.config.Tracer.DroppedPacket(p.remoteAddr, logging.PacketTypeFromHeader(hdr), p.Size(), logging.PacketDropUnexpectedPacket)
+ }
+ return false
+ }
+
+ s.logger.Debugf("<- Received Initial packet.")
+
+ if err := s.handleInitialImpl(p, hdr); err != nil {
+ s.logger.Errorf("Error occurred handling initial packet: %s", err)
+ }
+ // Don't put the packet buffer back.
+ // handleInitialImpl deals with the buffer.
+ return true
+}
+
+// validateToken returns false if:
+// - address is invalid
+// - token is expired
+// - token is null
+func (s *baseServer) validateToken(token *handshake.Token, addr net.Addr) bool {
+ if token == nil {
+ return false
+ }
+ if !token.ValidateRemoteAddr(addr) {
+ return false
+ }
+ if !token.IsRetryToken && time.Since(token.SentTime) > s.config.MaxTokenAge {
+ return false
+ }
+ if token.IsRetryToken && time.Since(token.SentTime) > s.config.MaxRetryTokenAge {
+ return false
+ }
+ return true
+}
+
+func (s *baseServer) handleInitialImpl(p *receivedPacket, hdr *wire.Header) error {
+ if len(hdr.Token) == 0 && hdr.DestConnectionID.Len() < protocol.MinConnectionIDLenInitial {
+ p.buffer.Release()
+ if s.config.Tracer != nil {
+ s.config.Tracer.DroppedPacket(p.remoteAddr, logging.PacketTypeInitial, p.Size(), logging.PacketDropUnexpectedPacket)
+ }
+ return errors.New("too short connection ID")
+ }
+
+ var (
+ token *handshake.Token
+ retrySrcConnID *protocol.ConnectionID
+ )
+ origDestConnID := hdr.DestConnectionID
+ if len(hdr.Token) > 0 {
+ tok, err := s.tokenGenerator.DecodeToken(hdr.Token)
+ if err == nil {
+ if tok.IsRetryToken {
+ origDestConnID = tok.OriginalDestConnectionID
+ retrySrcConnID = &tok.RetrySrcConnectionID
+ }
+ token = tok
+ }
+ }
+
+ clientAddrIsValid := s.validateToken(token, p.remoteAddr)
+
+ if token != nil && !clientAddrIsValid {
+ // For invalid and expired non-retry tokens, we don't send an INVALID_TOKEN error.
+ // We just ignore them, and act as if there was no token on this packet at all.
+ // This also means we might send a Retry later.
+ if !token.IsRetryToken {
+ token = nil
+ } else {
+ // For Retry tokens, we send an INVALID_ERROR if
+ // * the token is too old, or
+ // * the token is invalid, in case of a retry token.
+ go func() {
+ defer p.buffer.Release()
+ if err := s.maybeSendInvalidToken(p, hdr); err != nil {
+ s.logger.Debugf("Error sending INVALID_TOKEN error: %s", err)
+ }
+ }()
+ return nil
+ }
+ }
+ if token == nil && s.config.RequireAddressValidation(p.remoteAddr) {
+ go func() {
+ defer p.buffer.Release()
+ if err := s.sendRetry(p.remoteAddr, hdr, p.info); err != nil {
+ s.logger.Debugf("Error sending Retry: %s", err)
+ }
+ }()
+ return nil
+ }
+
+ if queueLen := atomic.LoadInt32(&s.connQueueLen); queueLen >= protocol.MaxAcceptQueueSize {
+ s.logger.Debugf("Rejecting new connection. Server currently busy. Accept queue length: %d (max %d)", queueLen, protocol.MaxAcceptQueueSize)
+ go func() {
+ defer p.buffer.Release()
+ if err := s.sendConnectionRefused(p.remoteAddr, hdr, p.info); err != nil {
+ s.logger.Debugf("Error rejecting connection: %s", err)
+ }
+ }()
+ return nil
+ }
+
+ connID, err := s.config.ConnectionIDGenerator.GenerateConnectionID()
+ if err != nil {
+ return err
+ }
+ s.logger.Debugf("Changing connection ID to %s.", connID)
+ var conn quicConn
+ tracingID := nextConnTracingID()
+ if added := s.connHandler.AddWithConnID(hdr.DestConnectionID, connID, func() packetHandler {
+ var tracer logging.ConnectionTracer
+ if s.config.Tracer != nil {
+ // Use the same connection ID that is passed to the client's GetLogWriter callback.
+ connID := hdr.DestConnectionID
+ if origDestConnID.Len() > 0 {
+ connID = origDestConnID
+ }
+ tracer = s.config.Tracer.TracerForConnection(
+ context.WithValue(context.Background(), ConnectionTracingKey, tracingID),
+ protocol.PerspectiveServer,
+ connID,
+ )
+ }
+ conn = s.newConn(
+ newSendConn(s.conn, p.remoteAddr, p.info),
+ s.connHandler,
+ origDestConnID,
+ retrySrcConnID,
+ hdr.DestConnectionID,
+ hdr.SrcConnectionID,
+ connID,
+ s.connHandler.GetStatelessResetToken(connID),
+ s.config,
+ s.tlsConf,
+ s.tokenGenerator,
+ clientAddrIsValid,
+ tracer,
+ tracingID,
+ s.logger,
+ hdr.Version,
+ )
+ conn.handlePacket(p)
+ return conn
+ }); !added {
+ return nil
+ }
+ go conn.run()
+ go s.handleNewConn(conn)
+ if conn == nil {
+ p.buffer.Release()
+ return nil
+ }
+ return nil
+}
+
+func (s *baseServer) handleNewConn(conn quicConn) {
+ connCtx := conn.Context()
+ if s.acceptEarlyConns {
+ // wait until the early connection is ready (or the handshake fails)
+ select {
+ case <-conn.earlyConnReady():
+ case <-connCtx.Done():
+ return
+ }
+ } else {
+ // wait until the handshake is complete (or fails)
+ select {
+ case <-conn.HandshakeComplete().Done():
+ case <-connCtx.Done():
+ return
+ }
+ }
+
+ atomic.AddInt32(&s.connQueueLen, 1)
+ select {
+ case s.connQueue <- conn:
+ // blocks until the connection is accepted
+ case <-connCtx.Done():
+ atomic.AddInt32(&s.connQueueLen, -1)
+ // don't pass connections that were already closed to Accept()
+ }
+}
+
+func (s *baseServer) sendRetry(remoteAddr net.Addr, hdr *wire.Header, info *packetInfo) error {
+ // Log the Initial packet now.
+ // If no Retry is sent, the packet will be logged by the connection.
+ (&wire.ExtendedHeader{Header: *hdr}).Log(s.logger)
+ srcConnID, err := s.config.ConnectionIDGenerator.GenerateConnectionID()
+ if err != nil {
+ return err
+ }
+ token, err := s.tokenGenerator.NewRetryToken(remoteAddr, hdr.DestConnectionID, srcConnID)
+ if err != nil {
+ return err
+ }
+ replyHdr := &wire.ExtendedHeader{}
+ replyHdr.Type = protocol.PacketTypeRetry
+ replyHdr.Version = hdr.Version
+ replyHdr.SrcConnectionID = srcConnID
+ replyHdr.DestConnectionID = hdr.SrcConnectionID
+ replyHdr.Token = token
+ if s.logger.Debug() {
+ s.logger.Debugf("Changing connection ID to %s.", srcConnID)
+ s.logger.Debugf("-> Sending Retry")
+ replyHdr.Log(s.logger)
+ }
+
+ buf := getPacketBuffer()
+ defer buf.Release()
+ buf.Data, err = replyHdr.Append(buf.Data, hdr.Version)
+ if err != nil {
+ return err
+ }
+ // append the Retry integrity tag
+ tag := handshake.GetRetryIntegrityTag(buf.Data, hdr.DestConnectionID, hdr.Version)
+ buf.Data = append(buf.Data, tag[:]...)
+ if s.config.Tracer != nil {
+ s.config.Tracer.SentPacket(remoteAddr, &replyHdr.Header, protocol.ByteCount(len(buf.Data)), nil)
+ }
+ _, err = s.conn.WritePacket(buf.Data, remoteAddr, info.OOB())
+ return err
+}
+
+func (s *baseServer) maybeSendInvalidToken(p *receivedPacket, hdr *wire.Header) error {
+ // Only send INVALID_TOKEN if we can unprotect the packet.
+ // This makes sure that we won't send it for packets that were corrupted.
+ sealer, opener := handshake.NewInitialAEAD(hdr.DestConnectionID, protocol.PerspectiveServer, hdr.Version)
+ data := p.data[:hdr.ParsedLen()+hdr.Length]
+ extHdr, err := unpackLongHeader(opener, hdr, data, hdr.Version)
+ if err != nil {
+ if s.config.Tracer != nil {
+ s.config.Tracer.DroppedPacket(p.remoteAddr, logging.PacketTypeInitial, p.Size(), logging.PacketDropHeaderParseError)
+ }
+ // don't return the error here. Just drop the packet.
+ return nil
+ }
+ hdrLen := extHdr.ParsedLen()
+ if _, err := opener.Open(data[hdrLen:hdrLen], data[hdrLen:], extHdr.PacketNumber, data[:hdrLen]); err != nil {
+ // don't return the error here. Just drop the packet.
+ if s.config.Tracer != nil {
+ s.config.Tracer.DroppedPacket(p.remoteAddr, logging.PacketTypeInitial, p.Size(), logging.PacketDropPayloadDecryptError)
+ }
+ return nil
+ }
+ if s.logger.Debug() {
+ s.logger.Debugf("Client sent an invalid retry token. Sending INVALID_TOKEN to %s.", p.remoteAddr)
+ }
+ return s.sendError(p.remoteAddr, hdr, sealer, qerr.InvalidToken, p.info)
+}
+
+func (s *baseServer) sendConnectionRefused(remoteAddr net.Addr, hdr *wire.Header, info *packetInfo) error {
+ sealer, _ := handshake.NewInitialAEAD(hdr.DestConnectionID, protocol.PerspectiveServer, hdr.Version)
+ return s.sendError(remoteAddr, hdr, sealer, qerr.ConnectionRefused, info)
+}
+
+// sendError sends the error as a response to the packet received with header hdr
+func (s *baseServer) sendError(remoteAddr net.Addr, hdr *wire.Header, sealer handshake.LongHeaderSealer, errorCode qerr.TransportErrorCode, info *packetInfo) error {
+ b := getPacketBuffer()
+ defer b.Release()
+
+ ccf := &wire.ConnectionCloseFrame{ErrorCode: uint64(errorCode)}
+
+ replyHdr := &wire.ExtendedHeader{}
+ replyHdr.Type = protocol.PacketTypeInitial
+ replyHdr.Version = hdr.Version
+ replyHdr.SrcConnectionID = hdr.DestConnectionID
+ replyHdr.DestConnectionID = hdr.SrcConnectionID
+ replyHdr.PacketNumberLen = protocol.PacketNumberLen4
+ replyHdr.Length = 4 /* packet number len */ + ccf.Length(hdr.Version) + protocol.ByteCount(sealer.Overhead())
+ var err error
+ b.Data, err = replyHdr.Append(b.Data, hdr.Version)
+ if err != nil {
+ return err
+ }
+ payloadOffset := len(b.Data)
+
+ b.Data, err = ccf.Append(b.Data, hdr.Version)
+ if err != nil {
+ return err
+ }
+
+ _ = sealer.Seal(b.Data[payloadOffset:payloadOffset], b.Data[payloadOffset:], replyHdr.PacketNumber, b.Data[:payloadOffset])
+ b.Data = b.Data[0 : len(b.Data)+sealer.Overhead()]
+
+ pnOffset := payloadOffset - int(replyHdr.PacketNumberLen)
+ sealer.EncryptHeader(
+ b.Data[pnOffset+4:pnOffset+4+16],
+ &b.Data[0],
+ b.Data[pnOffset:payloadOffset],
+ )
+
+ replyHdr.Log(s.logger)
+ wire.LogFrame(s.logger, ccf, true)
+ if s.config.Tracer != nil {
+ s.config.Tracer.SentPacket(remoteAddr, &replyHdr.Header, protocol.ByteCount(len(b.Data)), []logging.Frame{ccf})
+ }
+ _, err = s.conn.WritePacket(b.Data, remoteAddr, info.OOB())
+ return err
+}
+
+func (s *baseServer) sendVersionNegotiationPacket(remote net.Addr, src, dest protocol.ArbitraryLenConnectionID, oob []byte) {
+ s.logger.Debugf("Client offered version %s, sending Version Negotiation")
+
+ data := wire.ComposeVersionNegotiation(dest, src, s.config.Versions)
+ if s.config.Tracer != nil {
+ s.config.Tracer.SentVersionNegotiationPacket(remote, src, dest, s.config.Versions)
+ }
+ if _, err := s.conn.WritePacket(data, remote, oob); err != nil {
+ s.logger.Debugf("Error sending Version Negotiation: %s", err)
+ }
+}
diff --git a/vendor/github.com/quic-go/quic-go/stream.go b/vendor/github.com/quic-go/quic-go/stream.go
new file mode 100644
index 0000000000..98d2fc6e47
--- /dev/null
+++ b/vendor/github.com/quic-go/quic-go/stream.go
@@ -0,0 +1,146 @@
+package quic
+
+import (
+ "net"
+ "os"
+ "sync"
+ "time"
+
+ "github.com/quic-go/quic-go/internal/ackhandler"
+ "github.com/quic-go/quic-go/internal/flowcontrol"
+ "github.com/quic-go/quic-go/internal/protocol"
+ "github.com/quic-go/quic-go/internal/wire"
+)
+
+type deadlineError struct{}
+
+func (deadlineError) Error() string { return "deadline exceeded" }
+func (deadlineError) Temporary() bool { return true }
+func (deadlineError) Timeout() bool { return true }
+func (deadlineError) Unwrap() error { return os.ErrDeadlineExceeded }
+
+var errDeadline net.Error = &deadlineError{}
+
+// The streamSender is notified by the stream about various events.
+type streamSender interface {
+ queueControlFrame(wire.Frame)
+ onHasStreamData(protocol.StreamID)
+ // must be called without holding the mutex that is acquired by closeForShutdown
+ onStreamCompleted(protocol.StreamID)
+}
+
+// Each of the both stream halves gets its own uniStreamSender.
+// This is necessary in order to keep track when both halves have been completed.
+type uniStreamSender struct {
+ streamSender
+ onStreamCompletedImpl func()
+}
+
+func (s *uniStreamSender) queueControlFrame(f wire.Frame) {
+ s.streamSender.queueControlFrame(f)
+}
+
+func (s *uniStreamSender) onHasStreamData(id protocol.StreamID) {
+ s.streamSender.onHasStreamData(id)
+}
+
+func (s *uniStreamSender) onStreamCompleted(protocol.StreamID) {
+ s.onStreamCompletedImpl()
+}
+
+var _ streamSender = &uniStreamSender{}
+
+type streamI interface {
+ Stream
+ closeForShutdown(error)
+ // for receiving
+ handleStreamFrame(*wire.StreamFrame) error
+ handleResetStreamFrame(*wire.ResetStreamFrame) error
+ getWindowUpdate() protocol.ByteCount
+ // for sending
+ hasData() bool
+ handleStopSendingFrame(*wire.StopSendingFrame)
+ popStreamFrame(maxBytes protocol.ByteCount, v protocol.VersionNumber) (*ackhandler.Frame, bool)
+ updateSendWindow(protocol.ByteCount)
+}
+
+var (
+ _ receiveStreamI = (streamI)(nil)
+ _ sendStreamI = (streamI)(nil)
+)
+
+// A Stream assembles the data from StreamFrames and provides a super-convenient Read-Interface
+//
+// Read() and Write() may be called concurrently, but multiple calls to Read() or Write() individually must be synchronized manually.
+type stream struct {
+ receiveStream
+ sendStream
+
+ completedMutex sync.Mutex
+ sender streamSender
+ receiveStreamCompleted bool
+ sendStreamCompleted bool
+}
+
+var _ Stream = &stream{}
+
+// newStream creates a new Stream
+func newStream(streamID protocol.StreamID,
+ sender streamSender,
+ flowController flowcontrol.StreamFlowController,
+) *stream {
+ s := &stream{sender: sender}
+ senderForSendStream := &uniStreamSender{
+ streamSender: sender,
+ onStreamCompletedImpl: func() {
+ s.completedMutex.Lock()
+ s.sendStreamCompleted = true
+ s.checkIfCompleted()
+ s.completedMutex.Unlock()
+ },
+ }
+ s.sendStream = *newSendStream(streamID, senderForSendStream, flowController)
+ senderForReceiveStream := &uniStreamSender{
+ streamSender: sender,
+ onStreamCompletedImpl: func() {
+ s.completedMutex.Lock()
+ s.receiveStreamCompleted = true
+ s.checkIfCompleted()
+ s.completedMutex.Unlock()
+ },
+ }
+ s.receiveStream = *newReceiveStream(streamID, senderForReceiveStream, flowController)
+ return s
+}
+
+// need to define StreamID() here, since both receiveStream and readStream have a StreamID()
+func (s *stream) StreamID() protocol.StreamID {
+ // the result is same for receiveStream and sendStream
+ return s.sendStream.StreamID()
+}
+
+func (s *stream) Close() error {
+ return s.sendStream.Close()
+}
+
+func (s *stream) SetDeadline(t time.Time) error {
+ _ = s.SetReadDeadline(t) // SetReadDeadline never errors
+ _ = s.SetWriteDeadline(t) // SetWriteDeadline never errors
+ return nil
+}
+
+// CloseForShutdown closes a stream abruptly.
+// It makes Read and Write unblock (and return the error) immediately.
+// The peer will NOT be informed about this: the stream is closed without sending a FIN or RST.
+func (s *stream) closeForShutdown(err error) {
+ s.sendStream.closeForShutdown(err)
+ s.receiveStream.closeForShutdown(err)
+}
+
+// checkIfCompleted is called from the uniStreamSender, when one of the stream halves is completed.
+// It makes sure that the onStreamCompleted callback is only called if both receive and send side have completed.
+func (s *stream) checkIfCompleted() {
+ if s.sendStreamCompleted && s.receiveStreamCompleted {
+ s.sender.onStreamCompleted(s.StreamID())
+ }
+}
diff --git a/vendor/github.com/quic-go/quic-go/streams_map.go b/vendor/github.com/quic-go/quic-go/streams_map.go
new file mode 100644
index 0000000000..b1a80eb36f
--- /dev/null
+++ b/vendor/github.com/quic-go/quic-go/streams_map.go
@@ -0,0 +1,318 @@
+package quic
+
+import (
+ "context"
+ "errors"
+ "fmt"
+ "net"
+ "sync"
+
+ "github.com/quic-go/quic-go/internal/flowcontrol"
+ "github.com/quic-go/quic-go/internal/protocol"
+ "github.com/quic-go/quic-go/internal/qerr"
+ "github.com/quic-go/quic-go/internal/wire"
+)
+
+type streamError struct {
+ message string
+ nums []protocol.StreamNum
+}
+
+func (e streamError) Error() string {
+ return e.message
+}
+
+func convertStreamError(err error, stype protocol.StreamType, pers protocol.Perspective) error {
+ strError, ok := err.(streamError)
+ if !ok {
+ return err
+ }
+ ids := make([]interface{}, len(strError.nums))
+ for i, num := range strError.nums {
+ ids[i] = num.StreamID(stype, pers)
+ }
+ return fmt.Errorf(strError.Error(), ids...)
+}
+
+type streamOpenErr struct{ error }
+
+var _ net.Error = &streamOpenErr{}
+
+func (e streamOpenErr) Temporary() bool { return e.error == errTooManyOpenStreams }
+func (streamOpenErr) Timeout() bool { return false }
+
+// errTooManyOpenStreams is used internally by the outgoing streams maps.
+var errTooManyOpenStreams = errors.New("too many open streams")
+
+type streamsMap struct {
+ perspective protocol.Perspective
+
+ maxIncomingBidiStreams uint64
+ maxIncomingUniStreams uint64
+
+ sender streamSender
+ newFlowController func(protocol.StreamID) flowcontrol.StreamFlowController
+
+ mutex sync.Mutex
+ outgoingBidiStreams *outgoingStreamsMap[streamI]
+ outgoingUniStreams *outgoingStreamsMap[sendStreamI]
+ incomingBidiStreams *incomingStreamsMap[streamI]
+ incomingUniStreams *incomingStreamsMap[receiveStreamI]
+ reset bool
+}
+
+var _ streamManager = &streamsMap{}
+
+func newStreamsMap(
+ sender streamSender,
+ newFlowController func(protocol.StreamID) flowcontrol.StreamFlowController,
+ maxIncomingBidiStreams uint64,
+ maxIncomingUniStreams uint64,
+ perspective protocol.Perspective,
+) streamManager {
+ m := &streamsMap{
+ perspective: perspective,
+ newFlowController: newFlowController,
+ maxIncomingBidiStreams: maxIncomingBidiStreams,
+ maxIncomingUniStreams: maxIncomingUniStreams,
+ sender: sender,
+ }
+ m.initMaps()
+ return m
+}
+
+func (m *streamsMap) initMaps() {
+ m.outgoingBidiStreams = newOutgoingStreamsMap(
+ protocol.StreamTypeBidi,
+ func(num protocol.StreamNum) streamI {
+ id := num.StreamID(protocol.StreamTypeBidi, m.perspective)
+ return newStream(id, m.sender, m.newFlowController(id))
+ },
+ m.sender.queueControlFrame,
+ )
+ m.incomingBidiStreams = newIncomingStreamsMap(
+ protocol.StreamTypeBidi,
+ func(num protocol.StreamNum) streamI {
+ id := num.StreamID(protocol.StreamTypeBidi, m.perspective.Opposite())
+ return newStream(id, m.sender, m.newFlowController(id))
+ },
+ m.maxIncomingBidiStreams,
+ m.sender.queueControlFrame,
+ )
+ m.outgoingUniStreams = newOutgoingStreamsMap(
+ protocol.StreamTypeUni,
+ func(num protocol.StreamNum) sendStreamI {
+ id := num.StreamID(protocol.StreamTypeUni, m.perspective)
+ return newSendStream(id, m.sender, m.newFlowController(id))
+ },
+ m.sender.queueControlFrame,
+ )
+ m.incomingUniStreams = newIncomingStreamsMap(
+ protocol.StreamTypeUni,
+ func(num protocol.StreamNum) receiveStreamI {
+ id := num.StreamID(protocol.StreamTypeUni, m.perspective.Opposite())
+ return newReceiveStream(id, m.sender, m.newFlowController(id))
+ },
+ m.maxIncomingUniStreams,
+ m.sender.queueControlFrame,
+ )
+}
+
+func (m *streamsMap) OpenStream() (Stream, error) {
+ m.mutex.Lock()
+ reset := m.reset
+ mm := m.outgoingBidiStreams
+ m.mutex.Unlock()
+ if reset {
+ return nil, Err0RTTRejected
+ }
+ str, err := mm.OpenStream()
+ return str, convertStreamError(err, protocol.StreamTypeBidi, m.perspective)
+}
+
+func (m *streamsMap) OpenStreamSync(ctx context.Context) (Stream, error) {
+ m.mutex.Lock()
+ reset := m.reset
+ mm := m.outgoingBidiStreams
+ m.mutex.Unlock()
+ if reset {
+ return nil, Err0RTTRejected
+ }
+ str, err := mm.OpenStreamSync(ctx)
+ return str, convertStreamError(err, protocol.StreamTypeBidi, m.perspective)
+}
+
+func (m *streamsMap) OpenUniStream() (SendStream, error) {
+ m.mutex.Lock()
+ reset := m.reset
+ mm := m.outgoingUniStreams
+ m.mutex.Unlock()
+ if reset {
+ return nil, Err0RTTRejected
+ }
+ str, err := mm.OpenStream()
+ return str, convertStreamError(err, protocol.StreamTypeBidi, m.perspective)
+}
+
+func (m *streamsMap) OpenUniStreamSync(ctx context.Context) (SendStream, error) {
+ m.mutex.Lock()
+ reset := m.reset
+ mm := m.outgoingUniStreams
+ m.mutex.Unlock()
+ if reset {
+ return nil, Err0RTTRejected
+ }
+ str, err := mm.OpenStreamSync(ctx)
+ return str, convertStreamError(err, protocol.StreamTypeUni, m.perspective)
+}
+
+func (m *streamsMap) AcceptStream(ctx context.Context) (Stream, error) {
+ m.mutex.Lock()
+ reset := m.reset
+ mm := m.incomingBidiStreams
+ m.mutex.Unlock()
+ if reset {
+ return nil, Err0RTTRejected
+ }
+ str, err := mm.AcceptStream(ctx)
+ return str, convertStreamError(err, protocol.StreamTypeBidi, m.perspective.Opposite())
+}
+
+func (m *streamsMap) AcceptUniStream(ctx context.Context) (ReceiveStream, error) {
+ m.mutex.Lock()
+ reset := m.reset
+ mm := m.incomingUniStreams
+ m.mutex.Unlock()
+ if reset {
+ return nil, Err0RTTRejected
+ }
+ str, err := mm.AcceptStream(ctx)
+ return str, convertStreamError(err, protocol.StreamTypeUni, m.perspective.Opposite())
+}
+
+func (m *streamsMap) DeleteStream(id protocol.StreamID) error {
+ num := id.StreamNum()
+ switch id.Type() {
+ case protocol.StreamTypeUni:
+ if id.InitiatedBy() == m.perspective {
+ return convertStreamError(m.outgoingUniStreams.DeleteStream(num), protocol.StreamTypeUni, m.perspective)
+ }
+ return convertStreamError(m.incomingUniStreams.DeleteStream(num), protocol.StreamTypeUni, m.perspective.Opposite())
+ case protocol.StreamTypeBidi:
+ if id.InitiatedBy() == m.perspective {
+ return convertStreamError(m.outgoingBidiStreams.DeleteStream(num), protocol.StreamTypeBidi, m.perspective)
+ }
+ return convertStreamError(m.incomingBidiStreams.DeleteStream(num), protocol.StreamTypeBidi, m.perspective.Opposite())
+ }
+ panic("")
+}
+
+func (m *streamsMap) GetOrOpenReceiveStream(id protocol.StreamID) (receiveStreamI, error) {
+ str, err := m.getOrOpenReceiveStream(id)
+ if err != nil {
+ return nil, &qerr.TransportError{
+ ErrorCode: qerr.StreamStateError,
+ ErrorMessage: err.Error(),
+ }
+ }
+ return str, nil
+}
+
+func (m *streamsMap) getOrOpenReceiveStream(id protocol.StreamID) (receiveStreamI, error) {
+ num := id.StreamNum()
+ switch id.Type() {
+ case protocol.StreamTypeUni:
+ if id.InitiatedBy() == m.perspective {
+ // an outgoing unidirectional stream is a send stream, not a receive stream
+ return nil, fmt.Errorf("peer attempted to open receive stream %d", id)
+ }
+ str, err := m.incomingUniStreams.GetOrOpenStream(num)
+ return str, convertStreamError(err, protocol.StreamTypeUni, m.perspective)
+ case protocol.StreamTypeBidi:
+ var str receiveStreamI
+ var err error
+ if id.InitiatedBy() == m.perspective {
+ str, err = m.outgoingBidiStreams.GetStream(num)
+ } else {
+ str, err = m.incomingBidiStreams.GetOrOpenStream(num)
+ }
+ return str, convertStreamError(err, protocol.StreamTypeBidi, id.InitiatedBy())
+ }
+ panic("")
+}
+
+func (m *streamsMap) GetOrOpenSendStream(id protocol.StreamID) (sendStreamI, error) {
+ str, err := m.getOrOpenSendStream(id)
+ if err != nil {
+ return nil, &qerr.TransportError{
+ ErrorCode: qerr.StreamStateError,
+ ErrorMessage: err.Error(),
+ }
+ }
+ return str, nil
+}
+
+func (m *streamsMap) getOrOpenSendStream(id protocol.StreamID) (sendStreamI, error) {
+ num := id.StreamNum()
+ switch id.Type() {
+ case protocol.StreamTypeUni:
+ if id.InitiatedBy() == m.perspective {
+ str, err := m.outgoingUniStreams.GetStream(num)
+ return str, convertStreamError(err, protocol.StreamTypeUni, m.perspective)
+ }
+ // an incoming unidirectional stream is a receive stream, not a send stream
+ return nil, fmt.Errorf("peer attempted to open send stream %d", id)
+ case protocol.StreamTypeBidi:
+ var str sendStreamI
+ var err error
+ if id.InitiatedBy() == m.perspective {
+ str, err = m.outgoingBidiStreams.GetStream(num)
+ } else {
+ str, err = m.incomingBidiStreams.GetOrOpenStream(num)
+ }
+ return str, convertStreamError(err, protocol.StreamTypeBidi, id.InitiatedBy())
+ }
+ panic("")
+}
+
+func (m *streamsMap) HandleMaxStreamsFrame(f *wire.MaxStreamsFrame) {
+ switch f.Type {
+ case protocol.StreamTypeUni:
+ m.outgoingUniStreams.SetMaxStream(f.MaxStreamNum)
+ case protocol.StreamTypeBidi:
+ m.outgoingBidiStreams.SetMaxStream(f.MaxStreamNum)
+ }
+}
+
+func (m *streamsMap) UpdateLimits(p *wire.TransportParameters) {
+ m.outgoingBidiStreams.UpdateSendWindow(p.InitialMaxStreamDataBidiRemote)
+ m.outgoingBidiStreams.SetMaxStream(p.MaxBidiStreamNum)
+ m.outgoingUniStreams.UpdateSendWindow(p.InitialMaxStreamDataUni)
+ m.outgoingUniStreams.SetMaxStream(p.MaxUniStreamNum)
+}
+
+func (m *streamsMap) CloseWithError(err error) {
+ m.outgoingBidiStreams.CloseWithError(err)
+ m.outgoingUniStreams.CloseWithError(err)
+ m.incomingBidiStreams.CloseWithError(err)
+ m.incomingUniStreams.CloseWithError(err)
+}
+
+// ResetFor0RTT resets is used when 0-RTT is rejected. In that case, the streams maps are
+// 1. closed with an Err0RTTRejected, making calls to Open{Uni}Stream{Sync} / Accept{Uni}Stream return that error.
+// 2. reset to their initial state, such that we can immediately process new incoming stream data.
+// Afterwards, calls to Open{Uni}Stream{Sync} / Accept{Uni}Stream will continue to return the error,
+// until UseResetMaps() has been called.
+func (m *streamsMap) ResetFor0RTT() {
+ m.mutex.Lock()
+ defer m.mutex.Unlock()
+ m.reset = true
+ m.CloseWithError(Err0RTTRejected)
+ m.initMaps()
+}
+
+func (m *streamsMap) UseResetMaps() {
+ m.mutex.Lock()
+ m.reset = false
+ m.mutex.Unlock()
+}
diff --git a/vendor/github.com/quic-go/quic-go/streams_map_incoming.go b/vendor/github.com/quic-go/quic-go/streams_map_incoming.go
new file mode 100644
index 0000000000..18ec6f998b
--- /dev/null
+++ b/vendor/github.com/quic-go/quic-go/streams_map_incoming.go
@@ -0,0 +1,195 @@
+package quic
+
+import (
+ "context"
+ "sync"
+
+ "github.com/quic-go/quic-go/internal/protocol"
+ "github.com/quic-go/quic-go/internal/wire"
+)
+
+type incomingStream interface {
+ closeForShutdown(error)
+}
+
+// When a stream is deleted before it was accepted, we can't delete it from the map immediately.
+// We need to wait until the application accepts it, and delete it then.
+type incomingStreamEntry[T incomingStream] struct {
+ stream T
+ shouldDelete bool
+}
+
+type incomingStreamsMap[T incomingStream] struct {
+ mutex sync.RWMutex
+ newStreamChan chan struct{}
+
+ streamType protocol.StreamType
+ streams map[protocol.StreamNum]incomingStreamEntry[T]
+
+ nextStreamToAccept protocol.StreamNum // the next stream that will be returned by AcceptStream()
+ nextStreamToOpen protocol.StreamNum // the highest stream that the peer opened
+ maxStream protocol.StreamNum // the highest stream that the peer is allowed to open
+ maxNumStreams uint64 // maximum number of streams
+
+ newStream func(protocol.StreamNum) T
+ queueMaxStreamID func(*wire.MaxStreamsFrame)
+
+ closeErr error
+}
+
+func newIncomingStreamsMap[T incomingStream](
+ streamType protocol.StreamType,
+ newStream func(protocol.StreamNum) T,
+ maxStreams uint64,
+ queueControlFrame func(wire.Frame),
+) *incomingStreamsMap[T] {
+ return &incomingStreamsMap[T]{
+ newStreamChan: make(chan struct{}, 1),
+ streamType: streamType,
+ streams: make(map[protocol.StreamNum]incomingStreamEntry[T]),
+ maxStream: protocol.StreamNum(maxStreams),
+ maxNumStreams: maxStreams,
+ newStream: newStream,
+ nextStreamToOpen: 1,
+ nextStreamToAccept: 1,
+ queueMaxStreamID: func(f *wire.MaxStreamsFrame) { queueControlFrame(f) },
+ }
+}
+
+func (m *incomingStreamsMap[T]) AcceptStream(ctx context.Context) (T, error) {
+ // drain the newStreamChan, so we don't check the map twice if the stream doesn't exist
+ select {
+ case <-m.newStreamChan:
+ default:
+ }
+
+ m.mutex.Lock()
+
+ var num protocol.StreamNum
+ var entry incomingStreamEntry[T]
+ for {
+ num = m.nextStreamToAccept
+ if m.closeErr != nil {
+ m.mutex.Unlock()
+ return *new(T), m.closeErr
+ }
+ var ok bool
+ entry, ok = m.streams[num]
+ if ok {
+ break
+ }
+ m.mutex.Unlock()
+ select {
+ case <-ctx.Done():
+ return *new(T), ctx.Err()
+ case <-m.newStreamChan:
+ }
+ m.mutex.Lock()
+ }
+ m.nextStreamToAccept++
+ // If this stream was completed before being accepted, we can delete it now.
+ if entry.shouldDelete {
+ if err := m.deleteStream(num); err != nil {
+ m.mutex.Unlock()
+ return *new(T), err
+ }
+ }
+ m.mutex.Unlock()
+ return entry.stream, nil
+}
+
+func (m *incomingStreamsMap[T]) GetOrOpenStream(num protocol.StreamNum) (T, error) {
+ m.mutex.RLock()
+ if num > m.maxStream {
+ m.mutex.RUnlock()
+ return *new(T), streamError{
+ message: "peer tried to open stream %d (current limit: %d)",
+ nums: []protocol.StreamNum{num, m.maxStream},
+ }
+ }
+ // if the num is smaller than the highest we accepted
+ // * this stream exists in the map, and we can return it, or
+ // * this stream was already closed, then we can return the nil
+ if num < m.nextStreamToOpen {
+ var s T
+ // If the stream was already queued for deletion, and is just waiting to be accepted, don't return it.
+ if entry, ok := m.streams[num]; ok && !entry.shouldDelete {
+ s = entry.stream
+ }
+ m.mutex.RUnlock()
+ return s, nil
+ }
+ m.mutex.RUnlock()
+
+ m.mutex.Lock()
+ // no need to check the two error conditions from above again
+ // * maxStream can only increase, so if the id was valid before, it definitely is valid now
+ // * highestStream is only modified by this function
+ for newNum := m.nextStreamToOpen; newNum <= num; newNum++ {
+ m.streams[newNum] = incomingStreamEntry[T]{stream: m.newStream(newNum)}
+ select {
+ case m.newStreamChan <- struct{}{}:
+ default:
+ }
+ }
+ m.nextStreamToOpen = num + 1
+ entry := m.streams[num]
+ m.mutex.Unlock()
+ return entry.stream, nil
+}
+
+func (m *incomingStreamsMap[T]) DeleteStream(num protocol.StreamNum) error {
+ m.mutex.Lock()
+ defer m.mutex.Unlock()
+
+ return m.deleteStream(num)
+}
+
+func (m *incomingStreamsMap[T]) deleteStream(num protocol.StreamNum) error {
+ if _, ok := m.streams[num]; !ok {
+ return streamError{
+ message: "tried to delete unknown incoming stream %d",
+ nums: []protocol.StreamNum{num},
+ }
+ }
+
+ // Don't delete this stream yet, if it was not yet accepted.
+ // Just save it to streamsToDelete map, to make sure it is deleted as soon as it gets accepted.
+ if num >= m.nextStreamToAccept {
+ entry, ok := m.streams[num]
+ if ok && entry.shouldDelete {
+ return streamError{
+ message: "tried to delete incoming stream %d multiple times",
+ nums: []protocol.StreamNum{num},
+ }
+ }
+ entry.shouldDelete = true
+ m.streams[num] = entry // can't assign to struct in map, so we need to reassign
+ return nil
+ }
+
+ delete(m.streams, num)
+ // queue a MAX_STREAM_ID frame, giving the peer the option to open a new stream
+ if m.maxNumStreams > uint64(len(m.streams)) {
+ maxStream := m.nextStreamToOpen + protocol.StreamNum(m.maxNumStreams-uint64(len(m.streams))) - 1
+ // Never send a value larger than protocol.MaxStreamCount.
+ if maxStream <= protocol.MaxStreamCount {
+ m.maxStream = maxStream
+ m.queueMaxStreamID(&wire.MaxStreamsFrame{
+ Type: m.streamType,
+ MaxStreamNum: m.maxStream,
+ })
+ }
+ }
+ return nil
+}
+
+func (m *incomingStreamsMap[T]) CloseWithError(err error) {
+ m.mutex.Lock()
+ m.closeErr = err
+ for _, entry := range m.streams {
+ entry.stream.closeForShutdown(err)
+ }
+ m.mutex.Unlock()
+ close(m.newStreamChan)
+}
diff --git a/vendor/github.com/quic-go/quic-go/streams_map_outgoing.go b/vendor/github.com/quic-go/quic-go/streams_map_outgoing.go
new file mode 100644
index 0000000000..fd45f4e7cf
--- /dev/null
+++ b/vendor/github.com/quic-go/quic-go/streams_map_outgoing.go
@@ -0,0 +1,230 @@
+package quic
+
+import (
+ "context"
+ "sync"
+
+ "github.com/quic-go/quic-go/internal/protocol"
+ "github.com/quic-go/quic-go/internal/wire"
+)
+
+type outgoingStream interface {
+ updateSendWindow(protocol.ByteCount)
+ closeForShutdown(error)
+}
+
+type outgoingStreamsMap[T outgoingStream] struct {
+ mutex sync.RWMutex
+
+ streamType protocol.StreamType
+ streams map[protocol.StreamNum]T
+
+ openQueue map[uint64]chan struct{}
+ lowestInQueue uint64
+ highestInQueue uint64
+
+ nextStream protocol.StreamNum // stream ID of the stream returned by OpenStream(Sync)
+ maxStream protocol.StreamNum // the maximum stream ID we're allowed to open
+ blockedSent bool // was a STREAMS_BLOCKED sent for the current maxStream
+
+ newStream func(protocol.StreamNum) T
+ queueStreamIDBlocked func(*wire.StreamsBlockedFrame)
+
+ closeErr error
+}
+
+func newOutgoingStreamsMap[T outgoingStream](
+ streamType protocol.StreamType,
+ newStream func(protocol.StreamNum) T,
+ queueControlFrame func(wire.Frame),
+) *outgoingStreamsMap[T] {
+ return &outgoingStreamsMap[T]{
+ streamType: streamType,
+ streams: make(map[protocol.StreamNum]T),
+ openQueue: make(map[uint64]chan struct{}),
+ maxStream: protocol.InvalidStreamNum,
+ nextStream: 1,
+ newStream: newStream,
+ queueStreamIDBlocked: func(f *wire.StreamsBlockedFrame) { queueControlFrame(f) },
+ }
+}
+
+func (m *outgoingStreamsMap[T]) OpenStream() (T, error) {
+ m.mutex.Lock()
+ defer m.mutex.Unlock()
+
+ if m.closeErr != nil {
+ return *new(T), m.closeErr
+ }
+
+ // if there are OpenStreamSync calls waiting, return an error here
+ if len(m.openQueue) > 0 || m.nextStream > m.maxStream {
+ m.maybeSendBlockedFrame()
+ return *new(T), streamOpenErr{errTooManyOpenStreams}
+ }
+ return m.openStream(), nil
+}
+
+func (m *outgoingStreamsMap[T]) OpenStreamSync(ctx context.Context) (T, error) {
+ m.mutex.Lock()
+ defer m.mutex.Unlock()
+
+ if m.closeErr != nil {
+ return *new(T), m.closeErr
+ }
+
+ if err := ctx.Err(); err != nil {
+ return *new(T), err
+ }
+
+ if len(m.openQueue) == 0 && m.nextStream <= m.maxStream {
+ return m.openStream(), nil
+ }
+
+ waitChan := make(chan struct{}, 1)
+ queuePos := m.highestInQueue
+ m.highestInQueue++
+ if len(m.openQueue) == 0 {
+ m.lowestInQueue = queuePos
+ }
+ m.openQueue[queuePos] = waitChan
+ m.maybeSendBlockedFrame()
+
+ for {
+ m.mutex.Unlock()
+ select {
+ case <-ctx.Done():
+ m.mutex.Lock()
+ delete(m.openQueue, queuePos)
+ return *new(T), ctx.Err()
+ case <-waitChan:
+ }
+ m.mutex.Lock()
+
+ if m.closeErr != nil {
+ return *new(T), m.closeErr
+ }
+ if m.nextStream > m.maxStream {
+ // no stream available. Continue waiting
+ continue
+ }
+ str := m.openStream()
+ delete(m.openQueue, queuePos)
+ m.lowestInQueue = queuePos + 1
+ m.unblockOpenSync()
+ return str, nil
+ }
+}
+
+func (m *outgoingStreamsMap[T]) openStream() T {
+ s := m.newStream(m.nextStream)
+ m.streams[m.nextStream] = s
+ m.nextStream++
+ return s
+}
+
+// maybeSendBlockedFrame queues a STREAMS_BLOCKED frame for the current stream offset,
+// if we haven't sent one for this offset yet
+func (m *outgoingStreamsMap[T]) maybeSendBlockedFrame() {
+ if m.blockedSent {
+ return
+ }
+
+ var streamNum protocol.StreamNum
+ if m.maxStream != protocol.InvalidStreamNum {
+ streamNum = m.maxStream
+ }
+ m.queueStreamIDBlocked(&wire.StreamsBlockedFrame{
+ Type: m.streamType,
+ StreamLimit: streamNum,
+ })
+ m.blockedSent = true
+}
+
+func (m *outgoingStreamsMap[T]) GetStream(num protocol.StreamNum) (T, error) {
+ m.mutex.RLock()
+ if num >= m.nextStream {
+ m.mutex.RUnlock()
+ return *new(T), streamError{
+ message: "peer attempted to open stream %d",
+ nums: []protocol.StreamNum{num},
+ }
+ }
+ s := m.streams[num]
+ m.mutex.RUnlock()
+ return s, nil
+}
+
+func (m *outgoingStreamsMap[T]) DeleteStream(num protocol.StreamNum) error {
+ m.mutex.Lock()
+ defer m.mutex.Unlock()
+
+ if _, ok := m.streams[num]; !ok {
+ return streamError{
+ message: "tried to delete unknown outgoing stream %d",
+ nums: []protocol.StreamNum{num},
+ }
+ }
+ delete(m.streams, num)
+ return nil
+}
+
+func (m *outgoingStreamsMap[T]) SetMaxStream(num protocol.StreamNum) {
+ m.mutex.Lock()
+ defer m.mutex.Unlock()
+
+ if num <= m.maxStream {
+ return
+ }
+ m.maxStream = num
+ m.blockedSent = false
+ if m.maxStream < m.nextStream-1+protocol.StreamNum(len(m.openQueue)) {
+ m.maybeSendBlockedFrame()
+ }
+ m.unblockOpenSync()
+}
+
+// UpdateSendWindow is called when the peer's transport parameters are received.
+// Only in the case of a 0-RTT handshake will we have open streams at this point.
+// We might need to update the send window, in case the server increased it.
+func (m *outgoingStreamsMap[T]) UpdateSendWindow(limit protocol.ByteCount) {
+ m.mutex.Lock()
+ for _, str := range m.streams {
+ str.updateSendWindow(limit)
+ }
+ m.mutex.Unlock()
+}
+
+// unblockOpenSync unblocks the next OpenStreamSync go-routine to open a new stream
+func (m *outgoingStreamsMap[T]) unblockOpenSync() {
+ if len(m.openQueue) == 0 {
+ return
+ }
+ for qp := m.lowestInQueue; qp <= m.highestInQueue; qp++ {
+ c, ok := m.openQueue[qp]
+ if !ok { // entry was deleted because the context was canceled
+ continue
+ }
+ // unblockOpenSync is called both from OpenStreamSync and from SetMaxStream.
+ // It's sufficient to only unblock OpenStreamSync once.
+ select {
+ case c <- struct{}{}:
+ default:
+ }
+ return
+ }
+}
+
+func (m *outgoingStreamsMap[T]) CloseWithError(err error) {
+ m.mutex.Lock()
+ m.closeErr = err
+ for _, str := range m.streams {
+ str.closeForShutdown(err)
+ }
+ for _, c := range m.openQueue {
+ if c != nil {
+ close(c)
+ }
+ }
+ m.mutex.Unlock()
+}
diff --git a/vendor/github.com/quic-go/quic-go/sys_conn.go b/vendor/github.com/quic-go/quic-go/sys_conn.go
new file mode 100644
index 0000000000..d6c1d61645
--- /dev/null
+++ b/vendor/github.com/quic-go/quic-go/sys_conn.go
@@ -0,0 +1,80 @@
+package quic
+
+import (
+ "net"
+ "syscall"
+ "time"
+
+ "github.com/quic-go/quic-go/internal/protocol"
+ "github.com/quic-go/quic-go/internal/utils"
+)
+
+// OOBCapablePacketConn is a connection that allows the reading of ECN bits from the IP header.
+// If the PacketConn passed to Dial or Listen satisfies this interface, quic-go will use it.
+// In this case, ReadMsgUDP() will be used instead of ReadFrom() to read packets.
+type OOBCapablePacketConn interface {
+ net.PacketConn
+ SyscallConn() (syscall.RawConn, error)
+ ReadMsgUDP(b, oob []byte) (n, oobn, flags int, addr *net.UDPAddr, err error)
+ WriteMsgUDP(b, oob []byte, addr *net.UDPAddr) (n, oobn int, err error)
+}
+
+var _ OOBCapablePacketConn = &net.UDPConn{}
+
+func wrapConn(pc net.PacketConn) (rawConn, error) {
+ conn, ok := pc.(interface {
+ SyscallConn() (syscall.RawConn, error)
+ })
+ if ok {
+ rawConn, err := conn.SyscallConn()
+ if err != nil {
+ return nil, err
+ }
+
+ if _, ok := pc.LocalAddr().(*net.UDPAddr); ok {
+ // Only set DF on sockets that we expect to be able to handle that configuration.
+ err = setDF(rawConn)
+ if err != nil {
+ return nil, err
+ }
+ }
+ }
+ c, ok := pc.(OOBCapablePacketConn)
+ if !ok {
+ utils.DefaultLogger.Infof("PacketConn is not a net.UDPConn. Disabling optimizations possible on UDP connections.")
+ return &basicConn{PacketConn: pc}, nil
+ }
+ return newConn(c)
+}
+
+// The basicConn is the most trivial implementation of a connection.
+// It reads a single packet from the underlying net.PacketConn.
+// It is used when
+// * the net.PacketConn is not a OOBCapablePacketConn, and
+// * when the OS doesn't support OOB.
+type basicConn struct {
+ net.PacketConn
+}
+
+var _ rawConn = &basicConn{}
+
+func (c *basicConn) ReadPacket() (*receivedPacket, error) {
+ buffer := getPacketBuffer()
+ // The packet size should not exceed protocol.MaxPacketBufferSize bytes
+ // If it does, we only read a truncated packet, which will then end up undecryptable
+ buffer.Data = buffer.Data[:protocol.MaxPacketBufferSize]
+ n, addr, err := c.PacketConn.ReadFrom(buffer.Data)
+ if err != nil {
+ return nil, err
+ }
+ return &receivedPacket{
+ remoteAddr: addr,
+ rcvTime: time.Now(),
+ data: buffer.Data[:n],
+ buffer: buffer,
+ }, nil
+}
+
+func (c *basicConn) WritePacket(b []byte, addr net.Addr, _ []byte) (n int, err error) {
+ return c.PacketConn.WriteTo(b, addr)
+}
diff --git a/vendor/github.com/quic-go/quic-go/sys_conn_df.go b/vendor/github.com/quic-go/quic-go/sys_conn_df.go
new file mode 100644
index 0000000000..ef9f981ac6
--- /dev/null
+++ b/vendor/github.com/quic-go/quic-go/sys_conn_df.go
@@ -0,0 +1,15 @@
+//go:build !linux && !windows
+
+package quic
+
+import "syscall"
+
+func setDF(rawConn syscall.RawConn) error {
+ // no-op on unsupported platforms
+ return nil
+}
+
+func isMsgSizeErr(err error) bool {
+ // to be implemented for more specific platforms
+ return false
+}
diff --git a/vendor/github.com/quic-go/quic-go/sys_conn_df_linux.go b/vendor/github.com/quic-go/quic-go/sys_conn_df_linux.go
new file mode 100644
index 0000000000..98542b4102
--- /dev/null
+++ b/vendor/github.com/quic-go/quic-go/sys_conn_df_linux.go
@@ -0,0 +1,40 @@
+//go:build linux
+
+package quic
+
+import (
+ "errors"
+ "syscall"
+
+ "golang.org/x/sys/unix"
+
+ "github.com/quic-go/quic-go/internal/utils"
+)
+
+func setDF(rawConn syscall.RawConn) error {
+ // Enabling IP_MTU_DISCOVER will force the kernel to return "sendto: message too long"
+ // and the datagram will not be fragmented
+ var errDFIPv4, errDFIPv6 error
+ if err := rawConn.Control(func(fd uintptr) {
+ errDFIPv4 = unix.SetsockoptInt(int(fd), unix.IPPROTO_IP, unix.IP_MTU_DISCOVER, unix.IP_PMTUDISC_DO)
+ errDFIPv6 = unix.SetsockoptInt(int(fd), unix.IPPROTO_IPV6, unix.IPV6_MTU_DISCOVER, unix.IPV6_PMTUDISC_DO)
+ }); err != nil {
+ return err
+ }
+ switch {
+ case errDFIPv4 == nil && errDFIPv6 == nil:
+ utils.DefaultLogger.Debugf("Setting DF for IPv4 and IPv6.")
+ case errDFIPv4 == nil && errDFIPv6 != nil:
+ utils.DefaultLogger.Debugf("Setting DF for IPv4.")
+ case errDFIPv4 != nil && errDFIPv6 == nil:
+ utils.DefaultLogger.Debugf("Setting DF for IPv6.")
+ case errDFIPv4 != nil && errDFIPv6 != nil:
+ return errors.New("setting DF failed for both IPv4 and IPv6")
+ }
+ return nil
+}
+
+func isMsgSizeErr(err error) bool {
+ // https://man7.org/linux/man-pages/man7/udp.7.html
+ return errors.Is(err, unix.EMSGSIZE)
+}
diff --git a/vendor/github.com/quic-go/quic-go/sys_conn_df_windows.go b/vendor/github.com/quic-go/quic-go/sys_conn_df_windows.go
new file mode 100644
index 0000000000..9855e8de8d
--- /dev/null
+++ b/vendor/github.com/quic-go/quic-go/sys_conn_df_windows.go
@@ -0,0 +1,46 @@
+//go:build windows
+
+package quic
+
+import (
+ "errors"
+ "syscall"
+
+ "golang.org/x/sys/windows"
+
+ "github.com/quic-go/quic-go/internal/utils"
+)
+
+const (
+ // same for both IPv4 and IPv6 on Windows
+ // https://microsoft.github.io/windows-docs-rs/doc/windows/Win32/Networking/WinSock/constant.IP_DONTFRAG.html
+ // https://microsoft.github.io/windows-docs-rs/doc/windows/Win32/Networking/WinSock/constant.IPV6_DONTFRAG.html
+ IP_DONTFRAGMENT = 14
+ IPV6_DONTFRAG = 14
+)
+
+func setDF(rawConn syscall.RawConn) error {
+ var errDFIPv4, errDFIPv6 error
+ if err := rawConn.Control(func(fd uintptr) {
+ errDFIPv4 = windows.SetsockoptInt(windows.Handle(fd), windows.IPPROTO_IP, IP_DONTFRAGMENT, 1)
+ errDFIPv6 = windows.SetsockoptInt(windows.Handle(fd), windows.IPPROTO_IPV6, IPV6_DONTFRAG, 1)
+ }); err != nil {
+ return err
+ }
+ switch {
+ case errDFIPv4 == nil && errDFIPv6 == nil:
+ utils.DefaultLogger.Debugf("Setting DF for IPv4 and IPv6.")
+ case errDFIPv4 == nil && errDFIPv6 != nil:
+ utils.DefaultLogger.Debugf("Setting DF for IPv4.")
+ case errDFIPv4 != nil && errDFIPv6 == nil:
+ utils.DefaultLogger.Debugf("Setting DF for IPv6.")
+ case errDFIPv4 != nil && errDFIPv6 != nil:
+ return errors.New("setting DF failed for both IPv4 and IPv6")
+ }
+ return nil
+}
+
+func isMsgSizeErr(err error) bool {
+ // https://docs.microsoft.com/en-us/windows/win32/winsock/windows-sockets-error-codes-2
+ return errors.Is(err, windows.WSAEMSGSIZE)
+}
diff --git a/vendor/github.com/quic-go/quic-go/sys_conn_helper_darwin.go b/vendor/github.com/quic-go/quic-go/sys_conn_helper_darwin.go
new file mode 100644
index 0000000000..7ad5f3af16
--- /dev/null
+++ b/vendor/github.com/quic-go/quic-go/sys_conn_helper_darwin.go
@@ -0,0 +1,21 @@
+//go:build darwin
+
+package quic
+
+import "golang.org/x/sys/unix"
+
+const msgTypeIPTOS = unix.IP_RECVTOS
+
+const (
+ ipv4RECVPKTINFO = unix.IP_RECVPKTINFO
+ ipv6RECVPKTINFO = 0x3d
+)
+
+const (
+ msgTypeIPv4PKTINFO = unix.IP_PKTINFO
+ msgTypeIPv6PKTINFO = 0x2e
+)
+
+// ReadBatch only returns a single packet on OSX,
+// see https://godoc.org/golang.org/x/net/ipv4#PacketConn.ReadBatch.
+const batchSize = 1
diff --git a/vendor/github.com/quic-go/quic-go/sys_conn_helper_freebsd.go b/vendor/github.com/quic-go/quic-go/sys_conn_helper_freebsd.go
new file mode 100644
index 0000000000..8d16d0b910
--- /dev/null
+++ b/vendor/github.com/quic-go/quic-go/sys_conn_helper_freebsd.go
@@ -0,0 +1,21 @@
+//go:build freebsd
+
+package quic
+
+import "golang.org/x/sys/unix"
+
+const (
+ msgTypeIPTOS = unix.IP_RECVTOS
+)
+
+const (
+ ipv4RECVPKTINFO = 0x7
+ ipv6RECVPKTINFO = 0x24
+)
+
+const (
+ msgTypeIPv4PKTINFO = 0x7
+ msgTypeIPv6PKTINFO = 0x2e
+)
+
+const batchSize = 8
diff --git a/vendor/github.com/quic-go/quic-go/sys_conn_helper_linux.go b/vendor/github.com/quic-go/quic-go/sys_conn_helper_linux.go
new file mode 100644
index 0000000000..61c3f54ba0
--- /dev/null
+++ b/vendor/github.com/quic-go/quic-go/sys_conn_helper_linux.go
@@ -0,0 +1,19 @@
+//go:build linux
+
+package quic
+
+import "golang.org/x/sys/unix"
+
+const msgTypeIPTOS = unix.IP_TOS
+
+const (
+ ipv4RECVPKTINFO = unix.IP_PKTINFO
+ ipv6RECVPKTINFO = unix.IPV6_RECVPKTINFO
+)
+
+const (
+ msgTypeIPv4PKTINFO = unix.IP_PKTINFO
+ msgTypeIPv6PKTINFO = unix.IPV6_PKTINFO
+)
+
+const batchSize = 8 // needs to smaller than MaxUint8 (otherwise the type of oobConn.readPos has to be changed)
diff --git a/vendor/github.com/quic-go/quic-go/sys_conn_no_oob.go b/vendor/github.com/quic-go/quic-go/sys_conn_no_oob.go
new file mode 100644
index 0000000000..7ab5040aa1
--- /dev/null
+++ b/vendor/github.com/quic-go/quic-go/sys_conn_no_oob.go
@@ -0,0 +1,15 @@
+//go:build !darwin && !linux && !freebsd && !windows
+
+package quic
+
+import "net"
+
+func newConn(c net.PacketConn) (rawConn, error) {
+ return &basicConn{PacketConn: c}, nil
+}
+
+func inspectReadBuffer(interface{}) (int, error) {
+ return 0, nil
+}
+
+func (i *packetInfo) OOB() []byte { return nil }
diff --git a/vendor/github.com/quic-go/quic-go/sys_conn_oob.go b/vendor/github.com/quic-go/quic-go/sys_conn_oob.go
new file mode 100644
index 0000000000..806dfb81a3
--- /dev/null
+++ b/vendor/github.com/quic-go/quic-go/sys_conn_oob.go
@@ -0,0 +1,264 @@
+//go:build darwin || linux || freebsd
+
+package quic
+
+import (
+ "encoding/binary"
+ "errors"
+ "fmt"
+ "net"
+ "syscall"
+ "time"
+
+ "golang.org/x/net/ipv4"
+ "golang.org/x/net/ipv6"
+ "golang.org/x/sys/unix"
+
+ "github.com/quic-go/quic-go/internal/protocol"
+ "github.com/quic-go/quic-go/internal/utils"
+)
+
+const (
+ ecnMask = 0x3
+ oobBufferSize = 128
+)
+
+// Contrary to what the naming suggests, the ipv{4,6}.Message is not dependent on the IP version.
+// They're both just aliases for x/net/internal/socket.Message.
+// This means we can use this struct to read from a socket that receives both IPv4 and IPv6 messages.
+var _ ipv4.Message = ipv6.Message{}
+
+type batchConn interface {
+ ReadBatch(ms []ipv4.Message, flags int) (int, error)
+}
+
+func inspectReadBuffer(c interface{}) (int, error) {
+ conn, ok := c.(interface {
+ SyscallConn() (syscall.RawConn, error)
+ })
+ if !ok {
+ return 0, errors.New("doesn't have a SyscallConn")
+ }
+ rawConn, err := conn.SyscallConn()
+ if err != nil {
+ return 0, fmt.Errorf("couldn't get syscall.RawConn: %w", err)
+ }
+ var size int
+ var serr error
+ if err := rawConn.Control(func(fd uintptr) {
+ size, serr = unix.GetsockoptInt(int(fd), unix.SOL_SOCKET, unix.SO_RCVBUF)
+ }); err != nil {
+ return 0, err
+ }
+ return size, serr
+}
+
+type oobConn struct {
+ OOBCapablePacketConn
+ batchConn batchConn
+
+ readPos uint8
+ // Packets received from the kernel, but not yet returned by ReadPacket().
+ messages []ipv4.Message
+ buffers [batchSize]*packetBuffer
+}
+
+var _ rawConn = &oobConn{}
+
+func newConn(c OOBCapablePacketConn) (*oobConn, error) {
+ rawConn, err := c.SyscallConn()
+ if err != nil {
+ return nil, err
+ }
+ needsPacketInfo := false
+ if udpAddr, ok := c.LocalAddr().(*net.UDPAddr); ok && udpAddr.IP.IsUnspecified() {
+ needsPacketInfo = true
+ }
+ // We don't know if this a IPv4-only, IPv6-only or a IPv4-and-IPv6 connection.
+ // Try enabling receiving of ECN and packet info for both IP versions.
+ // We expect at least one of those syscalls to succeed.
+ var errECNIPv4, errECNIPv6, errPIIPv4, errPIIPv6 error
+ if err := rawConn.Control(func(fd uintptr) {
+ errECNIPv4 = unix.SetsockoptInt(int(fd), unix.IPPROTO_IP, unix.IP_RECVTOS, 1)
+ errECNIPv6 = unix.SetsockoptInt(int(fd), unix.IPPROTO_IPV6, unix.IPV6_RECVTCLASS, 1)
+
+ if needsPacketInfo {
+ errPIIPv4 = unix.SetsockoptInt(int(fd), unix.IPPROTO_IP, ipv4RECVPKTINFO, 1)
+ errPIIPv6 = unix.SetsockoptInt(int(fd), unix.IPPROTO_IPV6, ipv6RECVPKTINFO, 1)
+ }
+ }); err != nil {
+ return nil, err
+ }
+ switch {
+ case errECNIPv4 == nil && errECNIPv6 == nil:
+ utils.DefaultLogger.Debugf("Activating reading of ECN bits for IPv4 and IPv6.")
+ case errECNIPv4 == nil && errECNIPv6 != nil:
+ utils.DefaultLogger.Debugf("Activating reading of ECN bits for IPv4.")
+ case errECNIPv4 != nil && errECNIPv6 == nil:
+ utils.DefaultLogger.Debugf("Activating reading of ECN bits for IPv6.")
+ case errECNIPv4 != nil && errECNIPv6 != nil:
+ return nil, errors.New("activating ECN failed for both IPv4 and IPv6")
+ }
+ if needsPacketInfo {
+ switch {
+ case errPIIPv4 == nil && errPIIPv6 == nil:
+ utils.DefaultLogger.Debugf("Activating reading of packet info for IPv4 and IPv6.")
+ case errPIIPv4 == nil && errPIIPv6 != nil:
+ utils.DefaultLogger.Debugf("Activating reading of packet info bits for IPv4.")
+ case errPIIPv4 != nil && errPIIPv6 == nil:
+ utils.DefaultLogger.Debugf("Activating reading of packet info bits for IPv6.")
+ case errPIIPv4 != nil && errPIIPv6 != nil:
+ return nil, errors.New("activating packet info failed for both IPv4 and IPv6")
+ }
+ }
+
+ // Allows callers to pass in a connection that already satisfies batchConn interface
+ // to make use of the optimisation. Otherwise, ipv4.NewPacketConn would unwrap the file descriptor
+ // via SyscallConn(), and read it that way, which might not be what the caller wants.
+ var bc batchConn
+ if ibc, ok := c.(batchConn); ok {
+ bc = ibc
+ } else {
+ bc = ipv4.NewPacketConn(c)
+ }
+
+ msgs := make([]ipv4.Message, batchSize)
+ for i := range msgs {
+ // preallocate the [][]byte
+ msgs[i].Buffers = make([][]byte, 1)
+ }
+ oobConn := &oobConn{
+ OOBCapablePacketConn: c,
+ batchConn: bc,
+ messages: msgs,
+ readPos: batchSize,
+ }
+ for i := 0; i < batchSize; i++ {
+ oobConn.messages[i].OOB = make([]byte, oobBufferSize)
+ }
+ return oobConn, nil
+}
+
+func (c *oobConn) ReadPacket() (*receivedPacket, error) {
+ if len(c.messages) == int(c.readPos) { // all messages read. Read the next batch of messages.
+ c.messages = c.messages[:batchSize]
+ // replace buffers data buffers up to the packet that has been consumed during the last ReadBatch call
+ for i := uint8(0); i < c.readPos; i++ {
+ buffer := getPacketBuffer()
+ buffer.Data = buffer.Data[:protocol.MaxPacketBufferSize]
+ c.buffers[i] = buffer
+ c.messages[i].Buffers[0] = c.buffers[i].Data
+ }
+ c.readPos = 0
+
+ n, err := c.batchConn.ReadBatch(c.messages, 0)
+ if n == 0 || err != nil {
+ return nil, err
+ }
+ c.messages = c.messages[:n]
+ }
+
+ msg := c.messages[c.readPos]
+ buffer := c.buffers[c.readPos]
+ c.readPos++
+
+ data := msg.OOB[:msg.NN]
+ var ecn protocol.ECN
+ var destIP net.IP
+ var ifIndex uint32
+ for len(data) > 0 {
+ hdr, body, remainder, err := unix.ParseOneSocketControlMessage(data)
+ if err != nil {
+ return nil, err
+ }
+ if hdr.Level == unix.IPPROTO_IP {
+ switch hdr.Type {
+ case msgTypeIPTOS:
+ ecn = protocol.ECN(body[0] & ecnMask)
+ case msgTypeIPv4PKTINFO:
+ // struct in_pktinfo {
+ // unsigned int ipi_ifindex; /* Interface index */
+ // struct in_addr ipi_spec_dst; /* Local address */
+ // struct in_addr ipi_addr; /* Header Destination
+ // address */
+ // };
+ ip := make([]byte, 4)
+ if len(body) == 12 {
+ ifIndex = binary.LittleEndian.Uint32(body)
+ copy(ip, body[8:12])
+ } else if len(body) == 4 {
+ // FreeBSD
+ copy(ip, body)
+ }
+ destIP = net.IP(ip)
+ }
+ }
+ if hdr.Level == unix.IPPROTO_IPV6 {
+ switch hdr.Type {
+ case unix.IPV6_TCLASS:
+ ecn = protocol.ECN(body[0] & ecnMask)
+ case msgTypeIPv6PKTINFO:
+ // struct in6_pktinfo {
+ // struct in6_addr ipi6_addr; /* src/dst IPv6 address */
+ // unsigned int ipi6_ifindex; /* send/recv interface index */
+ // };
+ if len(body) == 20 {
+ ip := make([]byte, 16)
+ copy(ip, body[:16])
+ destIP = net.IP(ip)
+ ifIndex = binary.LittleEndian.Uint32(body[16:])
+ }
+ }
+ }
+ data = remainder
+ }
+ var info *packetInfo
+ if destIP != nil {
+ info = &packetInfo{
+ addr: destIP,
+ ifIndex: ifIndex,
+ }
+ }
+ return &receivedPacket{
+ remoteAddr: msg.Addr,
+ rcvTime: time.Now(),
+ data: msg.Buffers[0][:msg.N],
+ ecn: ecn,
+ info: info,
+ buffer: buffer,
+ }, nil
+}
+
+func (c *oobConn) WritePacket(b []byte, addr net.Addr, oob []byte) (n int, err error) {
+ n, _, err = c.OOBCapablePacketConn.WriteMsgUDP(b, oob, addr.(*net.UDPAddr))
+ return n, err
+}
+
+func (info *packetInfo) OOB() []byte {
+ if info == nil {
+ return nil
+ }
+ if ip4 := info.addr.To4(); ip4 != nil {
+ // struct in_pktinfo {
+ // unsigned int ipi_ifindex; /* Interface index */
+ // struct in_addr ipi_spec_dst; /* Local address */
+ // struct in_addr ipi_addr; /* Header Destination address */
+ // };
+ cm := ipv4.ControlMessage{
+ Src: ip4,
+ IfIndex: int(info.ifIndex),
+ }
+ return cm.Marshal()
+ } else if len(info.addr) == 16 {
+ // struct in6_pktinfo {
+ // struct in6_addr ipi6_addr; /* src/dst IPv6 address */
+ // unsigned int ipi6_ifindex; /* send/recv interface index */
+ // };
+ cm := ipv6.ControlMessage{
+ Src: info.addr,
+ IfIndex: int(info.ifIndex),
+ }
+ return cm.Marshal()
+ }
+ return nil
+}
diff --git a/vendor/github.com/quic-go/quic-go/sys_conn_windows.go b/vendor/github.com/quic-go/quic-go/sys_conn_windows.go
new file mode 100644
index 0000000000..b003fe94af
--- /dev/null
+++ b/vendor/github.com/quic-go/quic-go/sys_conn_windows.go
@@ -0,0 +1,39 @@
+//go:build windows
+
+package quic
+
+import (
+ "errors"
+ "fmt"
+ "net"
+ "syscall"
+
+ "golang.org/x/sys/windows"
+)
+
+func newConn(c OOBCapablePacketConn) (rawConn, error) {
+ return &basicConn{PacketConn: c}, nil
+}
+
+func inspectReadBuffer(c net.PacketConn) (int, error) {
+ conn, ok := c.(interface {
+ SyscallConn() (syscall.RawConn, error)
+ })
+ if !ok {
+ return 0, errors.New("doesn't have a SyscallConn")
+ }
+ rawConn, err := conn.SyscallConn()
+ if err != nil {
+ return 0, fmt.Errorf("couldn't get syscall.RawConn: %w", err)
+ }
+ var size int
+ var serr error
+ if err := rawConn.Control(func(fd uintptr) {
+ size, serr = windows.GetsockoptInt(windows.Handle(fd), windows.SOL_SOCKET, windows.SO_RCVBUF)
+ }); err != nil {
+ return 0, err
+ }
+ return size, serr
+}
+
+func (i *packetInfo) OOB() []byte { return nil }
diff --git a/vendor/github.com/quic-go/quic-go/token_store.go b/vendor/github.com/quic-go/quic-go/token_store.go
new file mode 100644
index 0000000000..00460e5028
--- /dev/null
+++ b/vendor/github.com/quic-go/quic-go/token_store.go
@@ -0,0 +1,117 @@
+package quic
+
+import (
+ "sync"
+
+ "github.com/quic-go/quic-go/internal/utils"
+ list "github.com/quic-go/quic-go/internal/utils/linkedlist"
+)
+
+type singleOriginTokenStore struct {
+ tokens []*ClientToken
+ len int
+ p int
+}
+
+func newSingleOriginTokenStore(size int) *singleOriginTokenStore {
+ return &singleOriginTokenStore{tokens: make([]*ClientToken, size)}
+}
+
+func (s *singleOriginTokenStore) Add(token *ClientToken) {
+ s.tokens[s.p] = token
+ s.p = s.index(s.p + 1)
+ s.len = utils.Min(s.len+1, len(s.tokens))
+}
+
+func (s *singleOriginTokenStore) Pop() *ClientToken {
+ s.p = s.index(s.p - 1)
+ token := s.tokens[s.p]
+ s.tokens[s.p] = nil
+ s.len = utils.Max(s.len-1, 0)
+ return token
+}
+
+func (s *singleOriginTokenStore) Len() int {
+ return s.len
+}
+
+func (s *singleOriginTokenStore) index(i int) int {
+ mod := len(s.tokens)
+ return (i + mod) % mod
+}
+
+type lruTokenStoreEntry struct {
+ key string
+ cache *singleOriginTokenStore
+}
+
+type lruTokenStore struct {
+ mutex sync.Mutex
+
+ m map[string]*list.Element[*lruTokenStoreEntry]
+ q *list.List[*lruTokenStoreEntry]
+ capacity int
+ singleOriginSize int
+}
+
+var _ TokenStore = &lruTokenStore{}
+
+// NewLRUTokenStore creates a new LRU cache for tokens received by the client.
+// maxOrigins specifies how many origins this cache is saving tokens for.
+// tokensPerOrigin specifies the maximum number of tokens per origin.
+func NewLRUTokenStore(maxOrigins, tokensPerOrigin int) TokenStore {
+ return &lruTokenStore{
+ m: make(map[string]*list.Element[*lruTokenStoreEntry]),
+ q: list.New[*lruTokenStoreEntry](),
+ capacity: maxOrigins,
+ singleOriginSize: tokensPerOrigin,
+ }
+}
+
+func (s *lruTokenStore) Put(key string, token *ClientToken) {
+ s.mutex.Lock()
+ defer s.mutex.Unlock()
+
+ if el, ok := s.m[key]; ok {
+ entry := el.Value
+ entry.cache.Add(token)
+ s.q.MoveToFront(el)
+ return
+ }
+
+ if s.q.Len() < s.capacity {
+ entry := &lruTokenStoreEntry{
+ key: key,
+ cache: newSingleOriginTokenStore(s.singleOriginSize),
+ }
+ entry.cache.Add(token)
+ s.m[key] = s.q.PushFront(entry)
+ return
+ }
+
+ elem := s.q.Back()
+ entry := elem.Value
+ delete(s.m, entry.key)
+ entry.key = key
+ entry.cache = newSingleOriginTokenStore(s.singleOriginSize)
+ entry.cache.Add(token)
+ s.q.MoveToFront(elem)
+ s.m[key] = elem
+}
+
+func (s *lruTokenStore) Pop(key string) *ClientToken {
+ s.mutex.Lock()
+ defer s.mutex.Unlock()
+
+ var token *ClientToken
+ if el, ok := s.m[key]; ok {
+ s.q.MoveToFront(el)
+ cache := el.Value.cache
+ token = cache.Pop()
+ if cache.Len() == 0 {
+ s.q.Remove(el)
+ delete(s.m, key)
+ }
+ }
+ return token
+}
diff --git a/vendor/github.com/quic-go/quic-go/tools.go b/vendor/github.com/quic-go/quic-go/tools.go
new file mode 100644
index 0000000000..e848317f15
--- /dev/null
+++ b/vendor/github.com/quic-go/quic-go/tools.go
@@ -0,0 +1,8 @@
+//go:build tools
+
+package quic
+
+import (
+ _ "github.com/golang/mock/mockgen"
+ _ "github.com/onsi/ginkgo/v2/ginkgo"
+)
diff --git a/vendor/github.com/quic-go/quic-go/window_update_queue.go b/vendor/github.com/quic-go/quic-go/window_update_queue.go
new file mode 100644
index 0000000000..9ed121430e
--- /dev/null
+++ b/vendor/github.com/quic-go/quic-go/window_update_queue.go
@@ -0,0 +1,71 @@
+package quic
+
+import (
+ "sync"
+
+ "github.com/quic-go/quic-go/internal/flowcontrol"
+ "github.com/quic-go/quic-go/internal/protocol"
+ "github.com/quic-go/quic-go/internal/wire"
+)
+
+type windowUpdateQueue struct {
+ mutex sync.Mutex
+
+ queue map[protocol.StreamID]struct{} // used as a set
+ queuedConn bool // connection-level window update
+
+ streamGetter streamGetter
+ connFlowController flowcontrol.ConnectionFlowController
+ callback func(wire.Frame)
+}
+
+func newWindowUpdateQueue(
+ streamGetter streamGetter,
+ connFC flowcontrol.ConnectionFlowController,
+ cb func(wire.Frame),
+) *windowUpdateQueue {
+ return &windowUpdateQueue{
+ queue: make(map[protocol.StreamID]struct{}),
+ streamGetter: streamGetter,
+ connFlowController: connFC,
+ callback: cb,
+ }
+}
+
+func (q *windowUpdateQueue) AddStream(id protocol.StreamID) {
+ q.mutex.Lock()
+ q.queue[id] = struct{}{}
+ q.mutex.Unlock()
+}
+
+func (q *windowUpdateQueue) AddConnection() {
+ q.mutex.Lock()
+ q.queuedConn = true
+ q.mutex.Unlock()
+}
+
+func (q *windowUpdateQueue) QueueAll() {
+ q.mutex.Lock()
+ // queue a connection-level window update
+ if q.queuedConn {
+ q.callback(&wire.MaxDataFrame{MaximumData: q.connFlowController.GetWindowUpdate()})
+ q.queuedConn = false
+ }
+ // queue all stream-level window updates
+ for id := range q.queue {
+ delete(q.queue, id)
+ str, err := q.streamGetter.GetOrOpenReceiveStream(id)
+ if err != nil || str == nil { // the stream can be nil if it was completed before dequeing the window update
+ continue
+ }
+ offset := str.getWindowUpdate()
+ if offset == 0 { // can happen if we received a final offset, right after queueing the window update
+ continue
+ }
+ q.callback(&wire.MaxStreamDataFrame{
+ StreamID: id,
+ MaximumStreamData: offset,
+ })
+ }
+ q.mutex.Unlock()
+}
diff --git a/vendor/github.com/quic-go/quic-go/zero_rtt_queue.go b/vendor/github.com/quic-go/quic-go/zero_rtt_queue.go
new file mode 100644
index 0000000000..b81a936e07
--- /dev/null
+++ b/vendor/github.com/quic-go/quic-go/zero_rtt_queue.go
@@ -0,0 +1,34 @@
+package quic
+
+import (
+ "time"
+
+ "github.com/quic-go/quic-go/internal/protocol"
+)
+
+type zeroRTTQueue struct {
+ queue []*receivedPacket
+ retireTimer *time.Timer
+}
+
+var _ packetHandler = &zeroRTTQueue{}
+
+func (h *zeroRTTQueue) handlePacket(p *receivedPacket) {
+ if len(h.queue) < protocol.Max0RTTQueueLen {
+ h.queue = append(h.queue, p)
+ }
+}
+func (h *zeroRTTQueue) shutdown() {}
+func (h *zeroRTTQueue) destroy(error) {}
+func (h *zeroRTTQueue) getPerspective() protocol.Perspective { return protocol.PerspectiveClient }
+func (h *zeroRTTQueue) EnqueueAll(sess packetHandler) {
+ for _, p := range h.queue {
+ sess.handlePacket(p)
+ }
+}
+
+func (h *zeroRTTQueue) Clear() {
+ for _, p := range h.queue {
+ p.buffer.Release()
+ }
+}
diff --git a/vendor/github.com/rivo/uniseg/README.md b/vendor/github.com/rivo/uniseg/README.md
index f8da293e15..25e9346874 100644
--- a/vendor/github.com/rivo/uniseg/README.md
+++ b/vendor/github.com/rivo/uniseg/README.md
@@ -1,15 +1,15 @@
# Unicode Text Segmentation for Go
-[![Godoc Reference](https://img.shields.io/badge/godoc-reference-blue.svg)](https://godoc.org/github.com/rivo/uniseg)
+[![Go Reference](https://pkg.go.dev/badge/github.com/rivo/uniseg.svg)](https://pkg.go.dev/github.com/rivo/uniseg)
[![Go Report](https://img.shields.io/badge/go%20report-A%2B-brightgreen.svg)](https://goreportcard.com/report/github.com/rivo/uniseg)
-This Go package implements Unicode Text Segmentation according to [Unicode Standard Annex #29](http://unicode.org/reports/tr29/) (Unicode version 12.0.0).
-
-At this point, only the determination of grapheme cluster boundaries is implemented.
+This Go package implements Unicode Text Segmentation according to [Unicode Standard Annex #29](https://unicode.org/reports/tr29/), Unicode Line Breaking according to [Unicode Standard Annex #14](https://unicode.org/reports/tr14/) (Unicode version 14.0.0), and monospace font string width calculation similar to [wcwidth](https://man7.org/linux/man-pages/man3/wcwidth.3.html).
## Background
-In Go, [strings are read-only slices of bytes](https://blog.golang.org/strings). They can be turned into Unicode code points using the `for` loop or by casting: `[]rune(str)`. However, multiple code points may be combined into one user-perceived character or what the Unicode specification calls "grapheme cluster". Here are some examples:
+### Grapheme Clusters
+
+In Go, [strings are read-only slices of bytes](https://go.dev/blog/strings). They can be turned into Unicode code points using the `for` loop or by casting: `[]rune(str)`. However, multiple code points may be combined into one user-perceived character or what the Unicode specification calls "grapheme cluster". Here are some examples:
|String|Bytes (UTF-8)|Code points (runes)|Grapheme clusters|
|-|-|-|-|
@@ -17,7 +17,23 @@ In Go, [strings are read-only slices of bytes](https://blog.golang.org/strings).
|🏳️🌈|14 bytes: `f0 9f 8f b3 ef b8 8f e2 80 8d f0 9f 8c 88`|4 code points: `1f3f3 fe0f 200d 1f308`|1 cluster: `[1f3f3 fe0f 200d 1f308]`|
|🇩🇪|8 bytes: `f0 9f 87 a9 f0 9f 87 aa`|2 code points: `1f1e9 1f1ea`|1 cluster: `[1f1e9 1f1ea]`|
-This package provides a tool to iterate over these grapheme clusters. This may be used to determine the number of user-perceived characters, to split strings in their intended places, or to extract individual characters which form a unit.
+This package provides tools to iterate over these grapheme clusters. This may be used to determine the number of user-perceived characters, to split strings in their intended places, or to extract individual characters which form a unit.
+
+### Word Boundaries
+
+Word boundaries are used in a number of different contexts. The most familiar ones are selection (double-click mouse selection), cursor movement ("move to next word" control-arrow keys), and the dialog option "Whole Word Search" for search and replace. They are also used in database queries, to determine whether elements are within a certain number of words of one another. Searching may also use word boundaries in determining matching items. This package provides tools to determine word boundaries within strings.
+
+### Sentence Boundaries
+
+Sentence boundaries are often used for triple-click or some other method of selecting or iterating through blocks of text that are larger than single words. They are also used to determine whether words occur within the same sentence in database queries. This package provides tools to determine sentence boundaries within strings.
+
+### Line Breaking
+
+Line breaking, also known as word wrapping, is the process of breaking a section of text into lines such that it will fit in the available width of a page, window or other display area. This package provides tools to determine where a string may or may not be broken and where it must be broken (for example after newline characters).
+
+### Monospace Width
+
+Most terminals or text displays / text editors using a monospace font (for example source code editors) use a fixed width for each character. Some characters such as emojis or characters found in Asian and other languages may take up more than one character cell. This package provides tools to determine the number of cells a string will take up when displayed in a monospace font. See [here](https://pkg.go.dev/github.com/rivo/uniseg#hdr-Monospace_Width) for more information.
## Installation
@@ -25,38 +41,117 @@ This package provides a tool to iterate over these grapheme clusters. This may b
go get github.com/rivo/uniseg
```
-## Basic Example
+## Examples
+
+### Counting Characters in a String
+
+```go
+n := uniseg.GraphemeClusterCount("🇩🇪🏳️🌈")
+fmt.Println(n)
+// 2
+```
+
+### Calculating the Monospace String Width
```go
-package uniseg
+width := uniseg.StringWidth("🇩🇪🏳️🌈!")
+fmt.Println(width)
+// 5
+```
-import (
- "fmt"
+### Using the [`Graphemes`](https://pkg.go.dev/github.com/rivo/uniseg#Graphemes) Class
- "github.com/rivo/uniseg"
-)
+This is the most convenient method of iterating over grapheme clusters:
+
+```go
+gr := uniseg.NewGraphemes("👍🏼!")
+for gr.Next() {
+ fmt.Printf("%x ", gr.Runes())
+}
+// [1f44d 1f3fc] [21]
+```
+
+### Using the [`Step`](https://pkg.go.dev/github.com/rivo/uniseg#Step) or [`StepString`](https://pkg.go.dev/github.com/rivo/uniseg#StepString) Function
-func main() {
- gr := uniseg.NewGraphemes("👍🏼!")
- for gr.Next() {
- fmt.Printf("%x ", gr.Runes())
+This is orders of magnitude faster than the `Graphemes` class, but it requires the handling of states and boundaries:
+
+```go
+str := "🇩🇪🏳️🌈"
+state := -1
+var c string
+for len(str) > 0 {
+ c, str, _, state = uniseg.StepString(str, state)
+ fmt.Printf("%x ", []rune(c))
+}
+// [1f1e9 1f1ea] [1f3f3 fe0f 200d 1f308]
+```
+
+### Advanced Examples
+
+Breaking into grapheme clusters and evaluating line breaks:
+
+```go
+str := "First line.\nSecond line."
+state := -1
+var (
+ c string
+ boundaries int
+)
+for len(str) > 0 {
+ c, str, boundaries, state = uniseg.StepString(str, state)
+ fmt.Print(c)
+ if boundaries&uniseg.MaskLine == uniseg.LineCanBreak {
+ fmt.Print("|")
+ } else if boundaries&uniseg.MaskLine == uniseg.LineMustBreak {
+ fmt.Print("‖")
}
- // Output: [1f44d 1f3fc] [21]
}
+// First |line.
+// ‖Second |line.‖
+```
+
+If you're only interested in word segmentation, use [`FirstWord`](https://pkg.go.dev/github.com/rivo/uniseg#FirstWord) or [`FirstWordInString`](https://pkg.go.dev/github.com/rivo/uniseg#FirstWordInString):
+
+```go
+str := "Hello, world!"
+state := -1
+var c string
+for len(str) > 0 {
+ c, str, state = uniseg.FirstWordInString(str, state)
+ fmt.Printf("(%s)\n", c)
+}
+// (Hello)
+// (,)
+// ( )
+// (world)
+// (!)
+```
+
+Similarly, use
+
+- [`FirstGraphemeCluster`](https://pkg.go.dev/github.com/rivo/uniseg#FirstGraphemeCluster) or [`FirstGraphemeClusterInString`](https://pkg.go.dev/github.com/rivo/uniseg#FirstGraphemeClusterInString) for grapheme cluster determination only,
+- [`FirstSentence`](https://pkg.go.dev/github.com/rivo/uniseg#FirstSentence) or [`FirstSentenceInString`](https://pkg.go.dev/github.com/rivo/uniseg#FirstSentenceInString) for sentence segmentation only, and
+- [`FirstLineSegment`](https://pkg.go.dev/github.com/rivo/uniseg#FirstLineSegment) or [`FirstLineSegmentInString`](https://pkg.go.dev/github.com/rivo/uniseg#FirstLineSegmentInString) for line breaking / word wrapping (although using [`Step`](https://pkg.go.dev/github.com/rivo/uniseg#Step) or [`StepString`](https://pkg.go.dev/github.com/rivo/uniseg#StepString) is preferred as it will observe grapheme cluster boundaries).
+
+Finally, if you need to reverse a string while preserving grapheme clusters, use [`ReverseString`](https://pkg.go.dev/github.com/rivo/uniseg#ReverseString):
+
+```go
+fmt.Println(uniseg.ReverseString("🇩🇪🏳️🌈"))
+// 🏳️🌈🇩🇪
```
## Documentation
-Refer to https://godoc.org/github.com/rivo/uniseg for the package's documentation.
+Refer to https://pkg.go.dev/github.com/rivo/uniseg for the package's documentation.
## Dependencies
This package does not depend on any packages outside the standard library.
-## Your Feedback
+## Sponsor this Project
-Add your issue here on GitHub. Feel free to get in touch if you have any questions.
+[Become a Sponsor on GitHub](https://github.com/sponsors/rivo?metadata_source=uniseg_readme) to support this project!
-## Version
+## Your Feedback
-Version tags will be introduced once Golang modules are official. Consider this version 0.1.
+Add your issue here on GitHub, preferably before submitting any PR's. Feel free to get in touch if you have any questions.
\ No newline at end of file
diff --git a/vendor/github.com/rivo/uniseg/doc.go b/vendor/github.com/rivo/uniseg/doc.go
index 60c737d7b3..11224ae22d 100644
--- a/vendor/github.com/rivo/uniseg/doc.go
+++ b/vendor/github.com/rivo/uniseg/doc.go
@@ -1,8 +1,108 @@
/*
-Package uniseg implements Unicode Text Segmentation according to Unicode
-Standard Annex #29 (http://unicode.org/reports/tr29/).
+Package uniseg implements Unicode Text Segmentation, Unicode Line Breaking, and
+string width calculation for monospace fonts. Unicode Text Segmentation conforms
+to Unicode Standard Annex #29 (https://unicode.org/reports/tr29/) and Unicode
+Line Breaking conforms to Unicode Standard Annex #14
+(https://unicode.org/reports/tr14/).
-At this point, only the determination of grapheme cluster boundaries is
-implemented.
+In short, using this package, you can split a string into grapheme clusters
+(what people would usually refer to as a "character"), into words, and into
+sentences. Or, in its simplest case, this package allows you to count the number
+of characters in a string, especially when it contains complex characters such
+as emojis, combining characters, or characters from Asian, Arabic, Hebrew, or
+other languages. Additionally, you can use it to implement line breaking (or
+"word wrapping"), that is, to determine where text can be broken over to the
+next line when the width of the line is not big enough to fit the entire text.
+Finally, you can use it to calculate the display width of a string for monospace
+fonts.
+
+# Getting Started
+
+If you just want to count the number of characters in a string, you can use
+[GraphemeClusterCount]. If you want to determine the display width of a string,
+you can use [StringWidth]. If you want to iterate over a string, you can use
+[Step], [StepString], or the [Graphemes] class (more convenient but less
+performant). This will provide you with all information: grapheme clusters,
+word boundaries, sentence boundaries, line breaks, and monospace character
+widths. The specialized functions [FirstGraphemeCluster],
+[FirstGraphemeClusterInString], [FirstWord], [FirstWordInString],
+[FirstSentence], and [FirstSentenceInString] can be used if only one type of
+information is needed.
+
+# Grapheme Clusters
+
+Consider the rainbow flag emoji: 🏳️🌈. On most modern systems, it appears as one
+character. But its string representation actually has 14 bytes, so counting
+bytes (or using len("🏳️🌈")) will not work as expected. Counting runes won't,
+either: The flag has 4 Unicode code points, thus 4 runes. The stdlib function
+utf8.RuneCountInString("🏳️🌈") and len([]rune("🏳️🌈")) will both return 4.
+
+The [GraphemeClusterCount] function will return 1 for the rainbow flag emoji.
+The Graphemes class and a variety of functions in this package will allow you to
+split strings into its grapheme clusters.
+
+# Word Boundaries
+
+Word boundaries are used in a number of different contexts. The most familiar
+ones are selection (double-click mouse selection), cursor movement ("move to
+next word" control-arrow keys), and the dialog option "Whole Word Search" for
+search and replace. This package provides methods for determining word
+boundaries.
+
+# Sentence Boundaries
+
+Sentence boundaries are often used for triple-click or some other method of
+selecting or iterating through blocks of text that are larger than single words.
+They are also used to determine whether words occur within the same sentence in
+database queries. This package provides methods for determining sentence
+boundaries.
+
+# Line Breaking
+
+Line breaking, also known as word wrapping, is the process of breaking a section
+of text into lines such that it will fit in the available width of a page,
+window or other display area. This package provides methods to determine the
+positions in a string where a line must be broken, may be broken, or must not be
+broken.
+
+# Monospace Width
+
+Monospace width, as referred to in this package, is the width of a string in a
+monospace font. This is commonly used in terminal user interfaces or text
+displays or editors that don't support proportional fonts. A width of 1
+corresponds to a single character cell. The C function [wcswidth()] and its
+implementation in other programming languages is in widespread use for the same
+purpose. However, there is no standard for the calculation of such widths, and
+this package differs from wcswidth() in a number of ways, presumably to generate
+more visually pleasing results.
+
+To start, we assume that every code point has a width of 1, with the following
+exceptions:
+
+ - Code points with grapheme cluster break properties Control, CR, LF, Extend,
+ and ZWJ have a width of 0.
+ - U+2E3A, Two-Em Dash, has a width of 3.
+ - U+2E3B, Three-Em Dash, has a width of 4.
+ - Characters with the East-Asian Width properties "Fullwidth" (F) and "Wide"
+ (W) have a width of 2. (Properties "Ambiguous" (A) and "Neutral" (N) both
+ have a width of 1.)
+ - Code points with grapheme cluster break property Regional Indicator have a
+ width of 2.
+ - Code points with grapheme cluster break property Extended Pictographic have
+ a width of 2, unless their Emoji Presentation flag is "No", in which case
+ the width is 1.
+
+For Hangul grapheme clusters composed of conjoining Jamo and for Regional
+Indicators (flags), all code points except the first one have a width of 0. For
+grapheme clusters starting with an Extended Pictographic, any additional code
+point will force a total width of 2, except if the Variation Selector-15
+(U+FE0E) is included, in which case the total width is always 1. Grapheme
+clusters ending with Variation Selector-16 (U+FE0F) have a width of 2.
+
+Note that whether these widths appear correct depends on your application's
+render engine, to which extent it conforms to the Unicode Standard, and its
+choice of font.
+
+[wcswidth()]: https://man7.org/linux/man-pages/man3/wcswidth.3.html
*/
package uniseg
diff --git a/vendor/github.com/rivo/uniseg/eastasianwidth.go b/vendor/github.com/rivo/uniseg/eastasianwidth.go
new file mode 100644
index 0000000000..661934ac2d
--- /dev/null
+++ b/vendor/github.com/rivo/uniseg/eastasianwidth.go
@@ -0,0 +1,2556 @@
+package uniseg
+
+// Code generated via go generate from gen_properties.go. DO NOT EDIT.
+
+// eastAsianWidth are taken from
+// https://www.unicode.org/Public/14.0.0/ucd/EastAsianWidth.txt
+// and
+// https://unicode.org/Public/14.0.0/ucd/emoji/emoji-data.txt
+// ("Extended_Pictographic" only)
+// on September 10, 2022. See https://www.unicode.org/license.html for the Unicode
+// license agreement.
+var eastAsianWidth = [][3]int{
+ {0x0000, 0x001F, prN}, // Cc [32] ..
+ {0x0020, 0x0020, prNa}, // Zs SPACE
+ {0x0021, 0x0023, prNa}, // Po [3] EXCLAMATION MARK..NUMBER SIGN
+ {0x0024, 0x0024, prNa}, // Sc DOLLAR SIGN
+ {0x0025, 0x0027, prNa}, // Po [3] PERCENT SIGN..APOSTROPHE
+ {0x0028, 0x0028, prNa}, // Ps LEFT PARENTHESIS
+ {0x0029, 0x0029, prNa}, // Pe RIGHT PARENTHESIS
+ {0x002A, 0x002A, prNa}, // Po ASTERISK
+ {0x002B, 0x002B, prNa}, // Sm PLUS SIGN
+ {0x002C, 0x002C, prNa}, // Po COMMA
+ {0x002D, 0x002D, prNa}, // Pd HYPHEN-MINUS
+ {0x002E, 0x002F, prNa}, // Po [2] FULL STOP..SOLIDUS
+ {0x0030, 0x0039, prNa}, // Nd [10] DIGIT ZERO..DIGIT NINE
+ {0x003A, 0x003B, prNa}, // Po [2] COLON..SEMICOLON
+ {0x003C, 0x003E, prNa}, // Sm [3] LESS-THAN SIGN..GREATER-THAN SIGN
+ {0x003F, 0x0040, prNa}, // Po [2] QUESTION MARK..COMMERCIAL AT
+ {0x0041, 0x005A, prNa}, // Lu [26] LATIN CAPITAL LETTER A..LATIN CAPITAL LETTER Z
+ {0x005B, 0x005B, prNa}, // Ps LEFT SQUARE BRACKET
+ {0x005C, 0x005C, prNa}, // Po REVERSE SOLIDUS
+ {0x005D, 0x005D, prNa}, // Pe RIGHT SQUARE BRACKET
+ {0x005E, 0x005E, prNa}, // Sk CIRCUMFLEX ACCENT
+ {0x005F, 0x005F, prNa}, // Pc LOW LINE
+ {0x0060, 0x0060, prNa}, // Sk GRAVE ACCENT
+ {0x0061, 0x007A, prNa}, // Ll [26] LATIN SMALL LETTER A..LATIN SMALL LETTER Z
+ {0x007B, 0x007B, prNa}, // Ps LEFT CURLY BRACKET
+ {0x007C, 0x007C, prNa}, // Sm VERTICAL LINE
+ {0x007D, 0x007D, prNa}, // Pe RIGHT CURLY BRACKET
+ {0x007E, 0x007E, prNa}, // Sm TILDE
+ {0x007F, 0x007F, prN}, // Cc
+ {0x0080, 0x009F, prN}, // Cc [32] ..
+ {0x00A0, 0x00A0, prN}, // Zs NO-BREAK SPACE
+ {0x00A1, 0x00A1, prA}, // Po INVERTED EXCLAMATION MARK
+ {0x00A2, 0x00A3, prNa}, // Sc [2] CENT SIGN..POUND SIGN
+ {0x00A4, 0x00A4, prA}, // Sc CURRENCY SIGN
+ {0x00A5, 0x00A5, prNa}, // Sc YEN SIGN
+ {0x00A6, 0x00A6, prNa}, // So BROKEN BAR
+ {0x00A7, 0x00A7, prA}, // Po SECTION SIGN
+ {0x00A8, 0x00A8, prA}, // Sk DIAERESIS
+ {0x00A9, 0x00A9, prN}, // So COPYRIGHT SIGN
+ {0x00AA, 0x00AA, prA}, // Lo FEMININE ORDINAL INDICATOR
+ {0x00AB, 0x00AB, prN}, // Pi LEFT-POINTING DOUBLE ANGLE QUOTATION MARK
+ {0x00AC, 0x00AC, prNa}, // Sm NOT SIGN
+ {0x00AD, 0x00AD, prA}, // Cf SOFT HYPHEN
+ {0x00AE, 0x00AE, prA}, // So REGISTERED SIGN
+ {0x00AF, 0x00AF, prNa}, // Sk MACRON
+ {0x00B0, 0x00B0, prA}, // So DEGREE SIGN
+ {0x00B1, 0x00B1, prA}, // Sm PLUS-MINUS SIGN
+ {0x00B2, 0x00B3, prA}, // No [2] SUPERSCRIPT TWO..SUPERSCRIPT THREE
+ {0x00B4, 0x00B4, prA}, // Sk ACUTE ACCENT
+ {0x00B5, 0x00B5, prN}, // Ll MICRO SIGN
+ {0x00B6, 0x00B7, prA}, // Po [2] PILCROW SIGN..MIDDLE DOT
+ {0x00B8, 0x00B8, prA}, // Sk CEDILLA
+ {0x00B9, 0x00B9, prA}, // No SUPERSCRIPT ONE
+ {0x00BA, 0x00BA, prA}, // Lo MASCULINE ORDINAL INDICATOR
+ {0x00BB, 0x00BB, prN}, // Pf RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK
+ {0x00BC, 0x00BE, prA}, // No [3] VULGAR FRACTION ONE QUARTER..VULGAR FRACTION THREE QUARTERS
+ {0x00BF, 0x00BF, prA}, // Po INVERTED QUESTION MARK
+ {0x00C0, 0x00C5, prN}, // Lu [6] LATIN CAPITAL LETTER A WITH GRAVE..LATIN CAPITAL LETTER A WITH RING ABOVE
+ {0x00C6, 0x00C6, prA}, // Lu LATIN CAPITAL LETTER AE
+ {0x00C7, 0x00CF, prN}, // Lu [9] LATIN CAPITAL LETTER C WITH CEDILLA..LATIN CAPITAL LETTER I WITH DIAERESIS
+ {0x00D0, 0x00D0, prA}, // Lu LATIN CAPITAL LETTER ETH
+ {0x00D1, 0x00D6, prN}, // Lu [6] LATIN CAPITAL LETTER N WITH TILDE..LATIN CAPITAL LETTER O WITH DIAERESIS
+ {0x00D7, 0x00D7, prA}, // Sm MULTIPLICATION SIGN
+ {0x00D8, 0x00D8, prA}, // Lu LATIN CAPITAL LETTER O WITH STROKE
+ {0x00D9, 0x00DD, prN}, // Lu [5] LATIN CAPITAL LETTER U WITH GRAVE..LATIN CAPITAL LETTER Y WITH ACUTE
+ {0x00DE, 0x00E1, prA}, // L& [4] LATIN CAPITAL LETTER THORN..LATIN SMALL LETTER A WITH ACUTE
+ {0x00E2, 0x00E5, prN}, // Ll [4] LATIN SMALL LETTER A WITH CIRCUMFLEX..LATIN SMALL LETTER A WITH RING ABOVE
+ {0x00E6, 0x00E6, prA}, // Ll LATIN SMALL LETTER AE
+ {0x00E7, 0x00E7, prN}, // Ll LATIN SMALL LETTER C WITH CEDILLA
+ {0x00E8, 0x00EA, prA}, // Ll [3] LATIN SMALL LETTER E WITH GRAVE..LATIN SMALL LETTER E WITH CIRCUMFLEX
+ {0x00EB, 0x00EB, prN}, // Ll LATIN SMALL LETTER E WITH DIAERESIS
+ {0x00EC, 0x00ED, prA}, // Ll [2] LATIN SMALL LETTER I WITH GRAVE..LATIN SMALL LETTER I WITH ACUTE
+ {0x00EE, 0x00EF, prN}, // Ll [2] LATIN SMALL LETTER I WITH CIRCUMFLEX..LATIN SMALL LETTER I WITH DIAERESIS
+ {0x00F0, 0x00F0, prA}, // Ll LATIN SMALL LETTER ETH
+ {0x00F1, 0x00F1, prN}, // Ll LATIN SMALL LETTER N WITH TILDE
+ {0x00F2, 0x00F3, prA}, // Ll [2] LATIN SMALL LETTER O WITH GRAVE..LATIN SMALL LETTER O WITH ACUTE
+ {0x00F4, 0x00F6, prN}, // Ll [3] LATIN SMALL LETTER O WITH CIRCUMFLEX..LATIN SMALL LETTER O WITH DIAERESIS
+ {0x00F7, 0x00F7, prA}, // Sm DIVISION SIGN
+ {0x00F8, 0x00FA, prA}, // Ll [3] LATIN SMALL LETTER O WITH STROKE..LATIN SMALL LETTER U WITH ACUTE
+ {0x00FB, 0x00FB, prN}, // Ll LATIN SMALL LETTER U WITH CIRCUMFLEX
+ {0x00FC, 0x00FC, prA}, // Ll LATIN SMALL LETTER U WITH DIAERESIS
+ {0x00FD, 0x00FD, prN}, // Ll LATIN SMALL LETTER Y WITH ACUTE
+ {0x00FE, 0x00FE, prA}, // Ll LATIN SMALL LETTER THORN
+ {0x00FF, 0x00FF, prN}, // Ll LATIN SMALL LETTER Y WITH DIAERESIS
+ {0x0100, 0x0100, prN}, // Lu LATIN CAPITAL LETTER A WITH MACRON
+ {0x0101, 0x0101, prA}, // Ll LATIN SMALL LETTER A WITH MACRON
+ {0x0102, 0x0110, prN}, // L& [15] LATIN CAPITAL LETTER A WITH BREVE..LATIN CAPITAL LETTER D WITH STROKE
+ {0x0111, 0x0111, prA}, // Ll LATIN SMALL LETTER D WITH STROKE
+ {0x0112, 0x0112, prN}, // Lu LATIN CAPITAL LETTER E WITH MACRON
+ {0x0113, 0x0113, prA}, // Ll LATIN SMALL LETTER E WITH MACRON
+ {0x0114, 0x011A, prN}, // L& [7] LATIN CAPITAL LETTER E WITH BREVE..LATIN CAPITAL LETTER E WITH CARON
+ {0x011B, 0x011B, prA}, // Ll LATIN SMALL LETTER E WITH CARON
+ {0x011C, 0x0125, prN}, // L& [10] LATIN CAPITAL LETTER G WITH CIRCUMFLEX..LATIN SMALL LETTER H WITH CIRCUMFLEX
+ {0x0126, 0x0127, prA}, // L& [2] LATIN CAPITAL LETTER H WITH STROKE..LATIN SMALL LETTER H WITH STROKE
+ {0x0128, 0x012A, prN}, // L& [3] LATIN CAPITAL LETTER I WITH TILDE..LATIN CAPITAL LETTER I WITH MACRON
+ {0x012B, 0x012B, prA}, // Ll LATIN SMALL LETTER I WITH MACRON
+ {0x012C, 0x0130, prN}, // L& [5] LATIN CAPITAL LETTER I WITH BREVE..LATIN CAPITAL LETTER I WITH DOT ABOVE
+ {0x0131, 0x0133, prA}, // L& [3] LATIN SMALL LETTER DOTLESS I..LATIN SMALL LIGATURE IJ
+ {0x0134, 0x0137, prN}, // L& [4] LATIN CAPITAL LETTER J WITH CIRCUMFLEX..LATIN SMALL LETTER K WITH CEDILLA
+ {0x0138, 0x0138, prA}, // Ll LATIN SMALL LETTER KRA
+ {0x0139, 0x013E, prN}, // L& [6] LATIN CAPITAL LETTER L WITH ACUTE..LATIN SMALL LETTER L WITH CARON
+ {0x013F, 0x0142, prA}, // L& [4] LATIN CAPITAL LETTER L WITH MIDDLE DOT..LATIN SMALL LETTER L WITH STROKE
+ {0x0143, 0x0143, prN}, // Lu LATIN CAPITAL LETTER N WITH ACUTE
+ {0x0144, 0x0144, prA}, // Ll LATIN SMALL LETTER N WITH ACUTE
+ {0x0145, 0x0147, prN}, // L& [3] LATIN CAPITAL LETTER N WITH CEDILLA..LATIN CAPITAL LETTER N WITH CARON
+ {0x0148, 0x014B, prA}, // L& [4] LATIN SMALL LETTER N WITH CARON..LATIN SMALL LETTER ENG
+ {0x014C, 0x014C, prN}, // Lu LATIN CAPITAL LETTER O WITH MACRON
+ {0x014D, 0x014D, prA}, // Ll LATIN SMALL LETTER O WITH MACRON
+ {0x014E, 0x0151, prN}, // L& [4] LATIN CAPITAL LETTER O WITH BREVE..LATIN SMALL LETTER O WITH DOUBLE ACUTE
+ {0x0152, 0x0153, prA}, // L& [2] LATIN CAPITAL LIGATURE OE..LATIN SMALL LIGATURE OE
+ {0x0154, 0x0165, prN}, // L& [18] LATIN CAPITAL LETTER R WITH ACUTE..LATIN SMALL LETTER T WITH CARON
+ {0x0166, 0x0167, prA}, // L& [2] LATIN CAPITAL LETTER T WITH STROKE..LATIN SMALL LETTER T WITH STROKE
+ {0x0168, 0x016A, prN}, // L& [3] LATIN CAPITAL LETTER U WITH TILDE..LATIN CAPITAL LETTER U WITH MACRON
+ {0x016B, 0x016B, prA}, // Ll LATIN SMALL LETTER U WITH MACRON
+ {0x016C, 0x017F, prN}, // L& [20] LATIN CAPITAL LETTER U WITH BREVE..LATIN SMALL LETTER LONG S
+ {0x0180, 0x01BA, prN}, // L& [59] LATIN SMALL LETTER B WITH STROKE..LATIN SMALL LETTER EZH WITH TAIL
+ {0x01BB, 0x01BB, prN}, // Lo LATIN LETTER TWO WITH STROKE
+ {0x01BC, 0x01BF, prN}, // L& [4] LATIN CAPITAL LETTER TONE FIVE..LATIN LETTER WYNN
+ {0x01C0, 0x01C3, prN}, // Lo [4] LATIN LETTER DENTAL CLICK..LATIN LETTER RETROFLEX CLICK
+ {0x01C4, 0x01CD, prN}, // L& [10] LATIN CAPITAL LETTER DZ WITH CARON..LATIN CAPITAL LETTER A WITH CARON
+ {0x01CE, 0x01CE, prA}, // Ll LATIN SMALL LETTER A WITH CARON
+ {0x01CF, 0x01CF, prN}, // Lu LATIN CAPITAL LETTER I WITH CARON
+ {0x01D0, 0x01D0, prA}, // Ll LATIN SMALL LETTER I WITH CARON
+ {0x01D1, 0x01D1, prN}, // Lu LATIN CAPITAL LETTER O WITH CARON
+ {0x01D2, 0x01D2, prA}, // Ll LATIN SMALL LETTER O WITH CARON
+ {0x01D3, 0x01D3, prN}, // Lu LATIN CAPITAL LETTER U WITH CARON
+ {0x01D4, 0x01D4, prA}, // Ll LATIN SMALL LETTER U WITH CARON
+ {0x01D5, 0x01D5, prN}, // Lu LATIN CAPITAL LETTER U WITH DIAERESIS AND MACRON
+ {0x01D6, 0x01D6, prA}, // Ll LATIN SMALL LETTER U WITH DIAERESIS AND MACRON
+ {0x01D7, 0x01D7, prN}, // Lu LATIN CAPITAL LETTER U WITH DIAERESIS AND ACUTE
+ {0x01D8, 0x01D8, prA}, // Ll LATIN SMALL LETTER U WITH DIAERESIS AND ACUTE
+ {0x01D9, 0x01D9, prN}, // Lu LATIN CAPITAL LETTER U WITH DIAERESIS AND CARON
+ {0x01DA, 0x01DA, prA}, // Ll LATIN SMALL LETTER U WITH DIAERESIS AND CARON
+ {0x01DB, 0x01DB, prN}, // Lu LATIN CAPITAL LETTER U WITH DIAERESIS AND GRAVE
+ {0x01DC, 0x01DC, prA}, // Ll LATIN SMALL LETTER U WITH DIAERESIS AND GRAVE
+ {0x01DD, 0x024F, prN}, // L& [115] LATIN SMALL LETTER TURNED E..LATIN SMALL LETTER Y WITH STROKE
+ {0x0250, 0x0250, prN}, // Ll LATIN SMALL LETTER TURNED A
+ {0x0251, 0x0251, prA}, // Ll LATIN SMALL LETTER ALPHA
+ {0x0252, 0x0260, prN}, // Ll [15] LATIN SMALL LETTER TURNED ALPHA..LATIN SMALL LETTER G WITH HOOK
+ {0x0261, 0x0261, prA}, // Ll LATIN SMALL LETTER SCRIPT G
+ {0x0262, 0x0293, prN}, // Ll [50] LATIN LETTER SMALL CAPITAL G..LATIN SMALL LETTER EZH WITH CURL
+ {0x0294, 0x0294, prN}, // Lo LATIN LETTER GLOTTAL STOP
+ {0x0295, 0x02AF, prN}, // Ll [27] LATIN LETTER PHARYNGEAL VOICED FRICATIVE..LATIN SMALL LETTER TURNED H WITH FISHHOOK AND TAIL
+ {0x02B0, 0x02C1, prN}, // Lm [18] MODIFIER LETTER SMALL H..MODIFIER LETTER REVERSED GLOTTAL STOP
+ {0x02C2, 0x02C3, prN}, // Sk [2] MODIFIER LETTER LEFT ARROWHEAD..MODIFIER LETTER RIGHT ARROWHEAD
+ {0x02C4, 0x02C4, prA}, // Sk MODIFIER LETTER UP ARROWHEAD
+ {0x02C5, 0x02C5, prN}, // Sk MODIFIER LETTER DOWN ARROWHEAD
+ {0x02C6, 0x02C6, prN}, // Lm MODIFIER LETTER CIRCUMFLEX ACCENT
+ {0x02C7, 0x02C7, prA}, // Lm CARON
+ {0x02C8, 0x02C8, prN}, // Lm MODIFIER LETTER VERTICAL LINE
+ {0x02C9, 0x02CB, prA}, // Lm [3] MODIFIER LETTER MACRON..MODIFIER LETTER GRAVE ACCENT
+ {0x02CC, 0x02CC, prN}, // Lm MODIFIER LETTER LOW VERTICAL LINE
+ {0x02CD, 0x02CD, prA}, // Lm MODIFIER LETTER LOW MACRON
+ {0x02CE, 0x02CF, prN}, // Lm [2] MODIFIER LETTER LOW GRAVE ACCENT..MODIFIER LETTER LOW ACUTE ACCENT
+ {0x02D0, 0x02D0, prA}, // Lm MODIFIER LETTER TRIANGULAR COLON
+ {0x02D1, 0x02D1, prN}, // Lm MODIFIER LETTER HALF TRIANGULAR COLON
+ {0x02D2, 0x02D7, prN}, // Sk [6] MODIFIER LETTER CENTRED RIGHT HALF RING..MODIFIER LETTER MINUS SIGN
+ {0x02D8, 0x02DB, prA}, // Sk [4] BREVE..OGONEK
+ {0x02DC, 0x02DC, prN}, // Sk SMALL TILDE
+ {0x02DD, 0x02DD, prA}, // Sk DOUBLE ACUTE ACCENT
+ {0x02DE, 0x02DE, prN}, // Sk MODIFIER LETTER RHOTIC HOOK
+ {0x02DF, 0x02DF, prA}, // Sk MODIFIER LETTER CROSS ACCENT
+ {0x02E0, 0x02E4, prN}, // Lm [5] MODIFIER LETTER SMALL GAMMA..MODIFIER LETTER SMALL REVERSED GLOTTAL STOP
+ {0x02E5, 0x02EB, prN}, // Sk [7] MODIFIER LETTER EXTRA-HIGH TONE BAR..MODIFIER LETTER YANG DEPARTING TONE MARK
+ {0x02EC, 0x02EC, prN}, // Lm MODIFIER LETTER VOICING
+ {0x02ED, 0x02ED, prN}, // Sk MODIFIER LETTER UNASPIRATED
+ {0x02EE, 0x02EE, prN}, // Lm MODIFIER LETTER DOUBLE APOSTROPHE
+ {0x02EF, 0x02FF, prN}, // Sk [17] MODIFIER LETTER LOW DOWN ARROWHEAD..MODIFIER LETTER LOW LEFT ARROW
+ {0x0300, 0x036F, prA}, // Mn [112] COMBINING GRAVE ACCENT..COMBINING LATIN SMALL LETTER X
+ {0x0370, 0x0373, prN}, // L& [4] GREEK CAPITAL LETTER HETA..GREEK SMALL LETTER ARCHAIC SAMPI
+ {0x0374, 0x0374, prN}, // Lm GREEK NUMERAL SIGN
+ {0x0375, 0x0375, prN}, // Sk GREEK LOWER NUMERAL SIGN
+ {0x0376, 0x0377, prN}, // L& [2] GREEK CAPITAL LETTER PAMPHYLIAN DIGAMMA..GREEK SMALL LETTER PAMPHYLIAN DIGAMMA
+ {0x037A, 0x037A, prN}, // Lm GREEK YPOGEGRAMMENI
+ {0x037B, 0x037D, prN}, // Ll [3] GREEK SMALL REVERSED LUNATE SIGMA SYMBOL..GREEK SMALL REVERSED DOTTED LUNATE SIGMA SYMBOL
+ {0x037E, 0x037E, prN}, // Po GREEK QUESTION MARK
+ {0x037F, 0x037F, prN}, // Lu GREEK CAPITAL LETTER YOT
+ {0x0384, 0x0385, prN}, // Sk [2] GREEK TONOS..GREEK DIALYTIKA TONOS
+ {0x0386, 0x0386, prN}, // Lu GREEK CAPITAL LETTER ALPHA WITH TONOS
+ {0x0387, 0x0387, prN}, // Po GREEK ANO TELEIA
+ {0x0388, 0x038A, prN}, // Lu [3] GREEK CAPITAL LETTER EPSILON WITH TONOS..GREEK CAPITAL LETTER IOTA WITH TONOS
+ {0x038C, 0x038C, prN}, // Lu GREEK CAPITAL LETTER OMICRON WITH TONOS
+ {0x038E, 0x0390, prN}, // L& [3] GREEK CAPITAL LETTER UPSILON WITH TONOS..GREEK SMALL LETTER IOTA WITH DIALYTIKA AND TONOS
+ {0x0391, 0x03A1, prA}, // Lu [17] GREEK CAPITAL LETTER ALPHA..GREEK CAPITAL LETTER RHO
+ {0x03A3, 0x03A9, prA}, // Lu [7] GREEK CAPITAL LETTER SIGMA..GREEK CAPITAL LETTER OMEGA
+ {0x03AA, 0x03B0, prN}, // L& [7] GREEK CAPITAL LETTER IOTA WITH DIALYTIKA..GREEK SMALL LETTER UPSILON WITH DIALYTIKA AND TONOS
+ {0x03B1, 0x03C1, prA}, // Ll [17] GREEK SMALL LETTER ALPHA..GREEK SMALL LETTER RHO
+ {0x03C2, 0x03C2, prN}, // Ll GREEK SMALL LETTER FINAL SIGMA
+ {0x03C3, 0x03C9, prA}, // Ll [7] GREEK SMALL LETTER SIGMA..GREEK SMALL LETTER OMEGA
+ {0x03CA, 0x03F5, prN}, // L& [44] GREEK SMALL LETTER IOTA WITH DIALYTIKA..GREEK LUNATE EPSILON SYMBOL
+ {0x03F6, 0x03F6, prN}, // Sm GREEK REVERSED LUNATE EPSILON SYMBOL
+ {0x03F7, 0x03FF, prN}, // L& [9] GREEK CAPITAL LETTER SHO..GREEK CAPITAL REVERSED DOTTED LUNATE SIGMA SYMBOL
+ {0x0400, 0x0400, prN}, // Lu CYRILLIC CAPITAL LETTER IE WITH GRAVE
+ {0x0401, 0x0401, prA}, // Lu CYRILLIC CAPITAL LETTER IO
+ {0x0402, 0x040F, prN}, // Lu [14] CYRILLIC CAPITAL LETTER DJE..CYRILLIC CAPITAL LETTER DZHE
+ {0x0410, 0x044F, prA}, // L& [64] CYRILLIC CAPITAL LETTER A..CYRILLIC SMALL LETTER YA
+ {0x0450, 0x0450, prN}, // Ll CYRILLIC SMALL LETTER IE WITH GRAVE
+ {0x0451, 0x0451, prA}, // Ll CYRILLIC SMALL LETTER IO
+ {0x0452, 0x0481, prN}, // L& [48] CYRILLIC SMALL LETTER DJE..CYRILLIC SMALL LETTER KOPPA
+ {0x0482, 0x0482, prN}, // So CYRILLIC THOUSANDS SIGN
+ {0x0483, 0x0487, prN}, // Mn [5] COMBINING CYRILLIC TITLO..COMBINING CYRILLIC POKRYTIE
+ {0x0488, 0x0489, prN}, // Me [2] COMBINING CYRILLIC HUNDRED THOUSANDS SIGN..COMBINING CYRILLIC MILLIONS SIGN
+ {0x048A, 0x04FF, prN}, // L& [118] CYRILLIC CAPITAL LETTER SHORT I WITH TAIL..CYRILLIC SMALL LETTER HA WITH STROKE
+ {0x0500, 0x052F, prN}, // L& [48] CYRILLIC CAPITAL LETTER KOMI DE..CYRILLIC SMALL LETTER EL WITH DESCENDER
+ {0x0531, 0x0556, prN}, // Lu [38] ARMENIAN CAPITAL LETTER AYB..ARMENIAN CAPITAL LETTER FEH
+ {0x0559, 0x0559, prN}, // Lm ARMENIAN MODIFIER LETTER LEFT HALF RING
+ {0x055A, 0x055F, prN}, // Po [6] ARMENIAN APOSTROPHE..ARMENIAN ABBREVIATION MARK
+ {0x0560, 0x0588, prN}, // Ll [41] ARMENIAN SMALL LETTER TURNED AYB..ARMENIAN SMALL LETTER YI WITH STROKE
+ {0x0589, 0x0589, prN}, // Po ARMENIAN FULL STOP
+ {0x058A, 0x058A, prN}, // Pd ARMENIAN HYPHEN
+ {0x058D, 0x058E, prN}, // So [2] RIGHT-FACING ARMENIAN ETERNITY SIGN..LEFT-FACING ARMENIAN ETERNITY SIGN
+ {0x058F, 0x058F, prN}, // Sc ARMENIAN DRAM SIGN
+ {0x0591, 0x05BD, prN}, // Mn [45] HEBREW ACCENT ETNAHTA..HEBREW POINT METEG
+ {0x05BE, 0x05BE, prN}, // Pd HEBREW PUNCTUATION MAQAF
+ {0x05BF, 0x05BF, prN}, // Mn HEBREW POINT RAFE
+ {0x05C0, 0x05C0, prN}, // Po HEBREW PUNCTUATION PASEQ
+ {0x05C1, 0x05C2, prN}, // Mn [2] HEBREW POINT SHIN DOT..HEBREW POINT SIN DOT
+ {0x05C3, 0x05C3, prN}, // Po HEBREW PUNCTUATION SOF PASUQ
+ {0x05C4, 0x05C5, prN}, // Mn [2] HEBREW MARK UPPER DOT..HEBREW MARK LOWER DOT
+ {0x05C6, 0x05C6, prN}, // Po HEBREW PUNCTUATION NUN HAFUKHA
+ {0x05C7, 0x05C7, prN}, // Mn HEBREW POINT QAMATS QATAN
+ {0x05D0, 0x05EA, prN}, // Lo [27] HEBREW LETTER ALEF..HEBREW LETTER TAV
+ {0x05EF, 0x05F2, prN}, // Lo [4] HEBREW YOD TRIANGLE..HEBREW LIGATURE YIDDISH DOUBLE YOD
+ {0x05F3, 0x05F4, prN}, // Po [2] HEBREW PUNCTUATION GERESH..HEBREW PUNCTUATION GERSHAYIM
+ {0x0600, 0x0605, prN}, // Cf [6] ARABIC NUMBER SIGN..ARABIC NUMBER MARK ABOVE
+ {0x0606, 0x0608, prN}, // Sm [3] ARABIC-INDIC CUBE ROOT..ARABIC RAY
+ {0x0609, 0x060A, prN}, // Po [2] ARABIC-INDIC PER MILLE SIGN..ARABIC-INDIC PER TEN THOUSAND SIGN
+ {0x060B, 0x060B, prN}, // Sc AFGHANI SIGN
+ {0x060C, 0x060D, prN}, // Po [2] ARABIC COMMA..ARABIC DATE SEPARATOR
+ {0x060E, 0x060F, prN}, // So [2] ARABIC POETIC VERSE SIGN..ARABIC SIGN MISRA
+ {0x0610, 0x061A, prN}, // Mn [11] ARABIC SIGN SALLALLAHOU ALAYHE WASSALLAM..ARABIC SMALL KASRA
+ {0x061B, 0x061B, prN}, // Po ARABIC SEMICOLON
+ {0x061C, 0x061C, prN}, // Cf ARABIC LETTER MARK
+ {0x061D, 0x061F, prN}, // Po [3] ARABIC END OF TEXT MARK..ARABIC QUESTION MARK
+ {0x0620, 0x063F, prN}, // Lo [32] ARABIC LETTER KASHMIRI YEH..ARABIC LETTER FARSI YEH WITH THREE DOTS ABOVE
+ {0x0640, 0x0640, prN}, // Lm ARABIC TATWEEL
+ {0x0641, 0x064A, prN}, // Lo [10] ARABIC LETTER FEH..ARABIC LETTER YEH
+ {0x064B, 0x065F, prN}, // Mn [21] ARABIC FATHATAN..ARABIC WAVY HAMZA BELOW
+ {0x0660, 0x0669, prN}, // Nd [10] ARABIC-INDIC DIGIT ZERO..ARABIC-INDIC DIGIT NINE
+ {0x066A, 0x066D, prN}, // Po [4] ARABIC PERCENT SIGN..ARABIC FIVE POINTED STAR
+ {0x066E, 0x066F, prN}, // Lo [2] ARABIC LETTER DOTLESS BEH..ARABIC LETTER DOTLESS QAF
+ {0x0670, 0x0670, prN}, // Mn ARABIC LETTER SUPERSCRIPT ALEF
+ {0x0671, 0x06D3, prN}, // Lo [99] ARABIC LETTER ALEF WASLA..ARABIC LETTER YEH BARREE WITH HAMZA ABOVE
+ {0x06D4, 0x06D4, prN}, // Po ARABIC FULL STOP
+ {0x06D5, 0x06D5, prN}, // Lo ARABIC LETTER AE
+ {0x06D6, 0x06DC, prN}, // Mn [7] ARABIC SMALL HIGH LIGATURE SAD WITH LAM WITH ALEF MAKSURA..ARABIC SMALL HIGH SEEN
+ {0x06DD, 0x06DD, prN}, // Cf ARABIC END OF AYAH
+ {0x06DE, 0x06DE, prN}, // So ARABIC START OF RUB EL HIZB
+ {0x06DF, 0x06E4, prN}, // Mn [6] ARABIC SMALL HIGH ROUNDED ZERO..ARABIC SMALL HIGH MADDA
+ {0x06E5, 0x06E6, prN}, // Lm [2] ARABIC SMALL WAW..ARABIC SMALL YEH
+ {0x06E7, 0x06E8, prN}, // Mn [2] ARABIC SMALL HIGH YEH..ARABIC SMALL HIGH NOON
+ {0x06E9, 0x06E9, prN}, // So ARABIC PLACE OF SAJDAH
+ {0x06EA, 0x06ED, prN}, // Mn [4] ARABIC EMPTY CENTRE LOW STOP..ARABIC SMALL LOW MEEM
+ {0x06EE, 0x06EF, prN}, // Lo [2] ARABIC LETTER DAL WITH INVERTED V..ARABIC LETTER REH WITH INVERTED V
+ {0x06F0, 0x06F9, prN}, // Nd [10] EXTENDED ARABIC-INDIC DIGIT ZERO..EXTENDED ARABIC-INDIC DIGIT NINE
+ {0x06FA, 0x06FC, prN}, // Lo [3] ARABIC LETTER SHEEN WITH DOT BELOW..ARABIC LETTER GHAIN WITH DOT BELOW
+ {0x06FD, 0x06FE, prN}, // So [2] ARABIC SIGN SINDHI AMPERSAND..ARABIC SIGN SINDHI POSTPOSITION MEN
+ {0x06FF, 0x06FF, prN}, // Lo ARABIC LETTER HEH WITH INVERTED V
+ {0x0700, 0x070D, prN}, // Po [14] SYRIAC END OF PARAGRAPH..SYRIAC HARKLEAN ASTERISCUS
+ {0x070F, 0x070F, prN}, // Cf SYRIAC ABBREVIATION MARK
+ {0x0710, 0x0710, prN}, // Lo SYRIAC LETTER ALAPH
+ {0x0711, 0x0711, prN}, // Mn SYRIAC LETTER SUPERSCRIPT ALAPH
+ {0x0712, 0x072F, prN}, // Lo [30] SYRIAC LETTER BETH..SYRIAC LETTER PERSIAN DHALATH
+ {0x0730, 0x074A, prN}, // Mn [27] SYRIAC PTHAHA ABOVE..SYRIAC BARREKH
+ {0x074D, 0x074F, prN}, // Lo [3] SYRIAC LETTER SOGDIAN ZHAIN..SYRIAC LETTER SOGDIAN FE
+ {0x0750, 0x077F, prN}, // Lo [48] ARABIC LETTER BEH WITH THREE DOTS HORIZONTALLY BELOW..ARABIC LETTER KAF WITH TWO DOTS ABOVE
+ {0x0780, 0x07A5, prN}, // Lo [38] THAANA LETTER HAA..THAANA LETTER WAAVU
+ {0x07A6, 0x07B0, prN}, // Mn [11] THAANA ABAFILI..THAANA SUKUN
+ {0x07B1, 0x07B1, prN}, // Lo THAANA LETTER NAA
+ {0x07C0, 0x07C9, prN}, // Nd [10] NKO DIGIT ZERO..NKO DIGIT NINE
+ {0x07CA, 0x07EA, prN}, // Lo [33] NKO LETTER A..NKO LETTER JONA RA
+ {0x07EB, 0x07F3, prN}, // Mn [9] NKO COMBINING SHORT HIGH TONE..NKO COMBINING DOUBLE DOT ABOVE
+ {0x07F4, 0x07F5, prN}, // Lm [2] NKO HIGH TONE APOSTROPHE..NKO LOW TONE APOSTROPHE
+ {0x07F6, 0x07F6, prN}, // So NKO SYMBOL OO DENNEN
+ {0x07F7, 0x07F9, prN}, // Po [3] NKO SYMBOL GBAKURUNEN..NKO EXCLAMATION MARK
+ {0x07FA, 0x07FA, prN}, // Lm NKO LAJANYALAN
+ {0x07FD, 0x07FD, prN}, // Mn NKO DANTAYALAN
+ {0x07FE, 0x07FF, prN}, // Sc [2] NKO DOROME SIGN..NKO TAMAN SIGN
+ {0x0800, 0x0815, prN}, // Lo [22] SAMARITAN LETTER ALAF..SAMARITAN LETTER TAAF
+ {0x0816, 0x0819, prN}, // Mn [4] SAMARITAN MARK IN..SAMARITAN MARK DAGESH
+ {0x081A, 0x081A, prN}, // Lm SAMARITAN MODIFIER LETTER EPENTHETIC YUT
+ {0x081B, 0x0823, prN}, // Mn [9] SAMARITAN MARK EPENTHETIC YUT..SAMARITAN VOWEL SIGN A
+ {0x0824, 0x0824, prN}, // Lm SAMARITAN MODIFIER LETTER SHORT A
+ {0x0825, 0x0827, prN}, // Mn [3] SAMARITAN VOWEL SIGN SHORT A..SAMARITAN VOWEL SIGN U
+ {0x0828, 0x0828, prN}, // Lm SAMARITAN MODIFIER LETTER I
+ {0x0829, 0x082D, prN}, // Mn [5] SAMARITAN VOWEL SIGN LONG I..SAMARITAN MARK NEQUDAA
+ {0x0830, 0x083E, prN}, // Po [15] SAMARITAN PUNCTUATION NEQUDAA..SAMARITAN PUNCTUATION ANNAAU
+ {0x0840, 0x0858, prN}, // Lo [25] MANDAIC LETTER HALQA..MANDAIC LETTER AIN
+ {0x0859, 0x085B, prN}, // Mn [3] MANDAIC AFFRICATION MARK..MANDAIC GEMINATION MARK
+ {0x085E, 0x085E, prN}, // Po MANDAIC PUNCTUATION
+ {0x0860, 0x086A, prN}, // Lo [11] SYRIAC LETTER MALAYALAM NGA..SYRIAC LETTER MALAYALAM SSA
+ {0x0870, 0x0887, prN}, // Lo [24] ARABIC LETTER ALEF WITH ATTACHED FATHA..ARABIC BASELINE ROUND DOT
+ {0x0888, 0x0888, prN}, // Sk ARABIC RAISED ROUND DOT
+ {0x0889, 0x088E, prN}, // Lo [6] ARABIC LETTER NOON WITH INVERTED SMALL V..ARABIC VERTICAL TAIL
+ {0x0890, 0x0891, prN}, // Cf [2] ARABIC POUND MARK ABOVE..ARABIC PIASTRE MARK ABOVE
+ {0x0898, 0x089F, prN}, // Mn [8] ARABIC SMALL HIGH WORD AL-JUZ..ARABIC HALF MADDA OVER MADDA
+ {0x08A0, 0x08C8, prN}, // Lo [41] ARABIC LETTER BEH WITH SMALL V BELOW..ARABIC LETTER GRAF
+ {0x08C9, 0x08C9, prN}, // Lm ARABIC SMALL FARSI YEH
+ {0x08CA, 0x08E1, prN}, // Mn [24] ARABIC SMALL HIGH FARSI YEH..ARABIC SMALL HIGH SIGN SAFHA
+ {0x08E2, 0x08E2, prN}, // Cf ARABIC DISPUTED END OF AYAH
+ {0x08E3, 0x08FF, prN}, // Mn [29] ARABIC TURNED DAMMA BELOW..ARABIC MARK SIDEWAYS NOON GHUNNA
+ {0x0900, 0x0902, prN}, // Mn [3] DEVANAGARI SIGN INVERTED CANDRABINDU..DEVANAGARI SIGN ANUSVARA
+ {0x0903, 0x0903, prN}, // Mc DEVANAGARI SIGN VISARGA
+ {0x0904, 0x0939, prN}, // Lo [54] DEVANAGARI LETTER SHORT A..DEVANAGARI LETTER HA
+ {0x093A, 0x093A, prN}, // Mn DEVANAGARI VOWEL SIGN OE
+ {0x093B, 0x093B, prN}, // Mc DEVANAGARI VOWEL SIGN OOE
+ {0x093C, 0x093C, prN}, // Mn DEVANAGARI SIGN NUKTA
+ {0x093D, 0x093D, prN}, // Lo DEVANAGARI SIGN AVAGRAHA
+ {0x093E, 0x0940, prN}, // Mc [3] DEVANAGARI VOWEL SIGN AA..DEVANAGARI VOWEL SIGN II
+ {0x0941, 0x0948, prN}, // Mn [8] DEVANAGARI VOWEL SIGN U..DEVANAGARI VOWEL SIGN AI
+ {0x0949, 0x094C, prN}, // Mc [4] DEVANAGARI VOWEL SIGN CANDRA O..DEVANAGARI VOWEL SIGN AU
+ {0x094D, 0x094D, prN}, // Mn DEVANAGARI SIGN VIRAMA
+ {0x094E, 0x094F, prN}, // Mc [2] DEVANAGARI VOWEL SIGN PRISHTHAMATRA E..DEVANAGARI VOWEL SIGN AW
+ {0x0950, 0x0950, prN}, // Lo DEVANAGARI OM
+ {0x0951, 0x0957, prN}, // Mn [7] DEVANAGARI STRESS SIGN UDATTA..DEVANAGARI VOWEL SIGN UUE
+ {0x0958, 0x0961, prN}, // Lo [10] DEVANAGARI LETTER QA..DEVANAGARI LETTER VOCALIC LL
+ {0x0962, 0x0963, prN}, // Mn [2] DEVANAGARI VOWEL SIGN VOCALIC L..DEVANAGARI VOWEL SIGN VOCALIC LL
+ {0x0964, 0x0965, prN}, // Po [2] DEVANAGARI DANDA..DEVANAGARI DOUBLE DANDA
+ {0x0966, 0x096F, prN}, // Nd [10] DEVANAGARI DIGIT ZERO..DEVANAGARI DIGIT NINE
+ {0x0970, 0x0970, prN}, // Po DEVANAGARI ABBREVIATION SIGN
+ {0x0971, 0x0971, prN}, // Lm DEVANAGARI SIGN HIGH SPACING DOT
+ {0x0972, 0x097F, prN}, // Lo [14] DEVANAGARI LETTER CANDRA A..DEVANAGARI LETTER BBA
+ {0x0980, 0x0980, prN}, // Lo BENGALI ANJI
+ {0x0981, 0x0981, prN}, // Mn BENGALI SIGN CANDRABINDU
+ {0x0982, 0x0983, prN}, // Mc [2] BENGALI SIGN ANUSVARA..BENGALI SIGN VISARGA
+ {0x0985, 0x098C, prN}, // Lo [8] BENGALI LETTER A..BENGALI LETTER VOCALIC L
+ {0x098F, 0x0990, prN}, // Lo [2] BENGALI LETTER E..BENGALI LETTER AI
+ {0x0993, 0x09A8, prN}, // Lo [22] BENGALI LETTER O..BENGALI LETTER NA
+ {0x09AA, 0x09B0, prN}, // Lo [7] BENGALI LETTER PA..BENGALI LETTER RA
+ {0x09B2, 0x09B2, prN}, // Lo BENGALI LETTER LA
+ {0x09B6, 0x09B9, prN}, // Lo [4] BENGALI LETTER SHA..BENGALI LETTER HA
+ {0x09BC, 0x09BC, prN}, // Mn BENGALI SIGN NUKTA
+ {0x09BD, 0x09BD, prN}, // Lo BENGALI SIGN AVAGRAHA
+ {0x09BE, 0x09C0, prN}, // Mc [3] BENGALI VOWEL SIGN AA..BENGALI VOWEL SIGN II
+ {0x09C1, 0x09C4, prN}, // Mn [4] BENGALI VOWEL SIGN U..BENGALI VOWEL SIGN VOCALIC RR
+ {0x09C7, 0x09C8, prN}, // Mc [2] BENGALI VOWEL SIGN E..BENGALI VOWEL SIGN AI
+ {0x09CB, 0x09CC, prN}, // Mc [2] BENGALI VOWEL SIGN O..BENGALI VOWEL SIGN AU
+ {0x09CD, 0x09CD, prN}, // Mn BENGALI SIGN VIRAMA
+ {0x09CE, 0x09CE, prN}, // Lo BENGALI LETTER KHANDA TA
+ {0x09D7, 0x09D7, prN}, // Mc BENGALI AU LENGTH MARK
+ {0x09DC, 0x09DD, prN}, // Lo [2] BENGALI LETTER RRA..BENGALI LETTER RHA
+ {0x09DF, 0x09E1, prN}, // Lo [3] BENGALI LETTER YYA..BENGALI LETTER VOCALIC LL
+ {0x09E2, 0x09E3, prN}, // Mn [2] BENGALI VOWEL SIGN VOCALIC L..BENGALI VOWEL SIGN VOCALIC LL
+ {0x09E6, 0x09EF, prN}, // Nd [10] BENGALI DIGIT ZERO..BENGALI DIGIT NINE
+ {0x09F0, 0x09F1, prN}, // Lo [2] BENGALI LETTER RA WITH MIDDLE DIAGONAL..BENGALI LETTER RA WITH LOWER DIAGONAL
+ {0x09F2, 0x09F3, prN}, // Sc [2] BENGALI RUPEE MARK..BENGALI RUPEE SIGN
+ {0x09F4, 0x09F9, prN}, // No [6] BENGALI CURRENCY NUMERATOR ONE..BENGALI CURRENCY DENOMINATOR SIXTEEN
+ {0x09FA, 0x09FA, prN}, // So BENGALI ISSHAR
+ {0x09FB, 0x09FB, prN}, // Sc BENGALI GANDA MARK
+ {0x09FC, 0x09FC, prN}, // Lo BENGALI LETTER VEDIC ANUSVARA
+ {0x09FD, 0x09FD, prN}, // Po BENGALI ABBREVIATION SIGN
+ {0x09FE, 0x09FE, prN}, // Mn BENGALI SANDHI MARK
+ {0x0A01, 0x0A02, prN}, // Mn [2] GURMUKHI SIGN ADAK BINDI..GURMUKHI SIGN BINDI
+ {0x0A03, 0x0A03, prN}, // Mc GURMUKHI SIGN VISARGA
+ {0x0A05, 0x0A0A, prN}, // Lo [6] GURMUKHI LETTER A..GURMUKHI LETTER UU
+ {0x0A0F, 0x0A10, prN}, // Lo [2] GURMUKHI LETTER EE..GURMUKHI LETTER AI
+ {0x0A13, 0x0A28, prN}, // Lo [22] GURMUKHI LETTER OO..GURMUKHI LETTER NA
+ {0x0A2A, 0x0A30, prN}, // Lo [7] GURMUKHI LETTER PA..GURMUKHI LETTER RA
+ {0x0A32, 0x0A33, prN}, // Lo [2] GURMUKHI LETTER LA..GURMUKHI LETTER LLA
+ {0x0A35, 0x0A36, prN}, // Lo [2] GURMUKHI LETTER VA..GURMUKHI LETTER SHA
+ {0x0A38, 0x0A39, prN}, // Lo [2] GURMUKHI LETTER SA..GURMUKHI LETTER HA
+ {0x0A3C, 0x0A3C, prN}, // Mn GURMUKHI SIGN NUKTA
+ {0x0A3E, 0x0A40, prN}, // Mc [3] GURMUKHI VOWEL SIGN AA..GURMUKHI VOWEL SIGN II
+ {0x0A41, 0x0A42, prN}, // Mn [2] GURMUKHI VOWEL SIGN U..GURMUKHI VOWEL SIGN UU
+ {0x0A47, 0x0A48, prN}, // Mn [2] GURMUKHI VOWEL SIGN EE..GURMUKHI VOWEL SIGN AI
+ {0x0A4B, 0x0A4D, prN}, // Mn [3] GURMUKHI VOWEL SIGN OO..GURMUKHI SIGN VIRAMA
+ {0x0A51, 0x0A51, prN}, // Mn GURMUKHI SIGN UDAAT
+ {0x0A59, 0x0A5C, prN}, // Lo [4] GURMUKHI LETTER KHHA..GURMUKHI LETTER RRA
+ {0x0A5E, 0x0A5E, prN}, // Lo GURMUKHI LETTER FA
+ {0x0A66, 0x0A6F, prN}, // Nd [10] GURMUKHI DIGIT ZERO..GURMUKHI DIGIT NINE
+ {0x0A70, 0x0A71, prN}, // Mn [2] GURMUKHI TIPPI..GURMUKHI ADDAK
+ {0x0A72, 0x0A74, prN}, // Lo [3] GURMUKHI IRI..GURMUKHI EK ONKAR
+ {0x0A75, 0x0A75, prN}, // Mn GURMUKHI SIGN YAKASH
+ {0x0A76, 0x0A76, prN}, // Po GURMUKHI ABBREVIATION SIGN
+ {0x0A81, 0x0A82, prN}, // Mn [2] GUJARATI SIGN CANDRABINDU..GUJARATI SIGN ANUSVARA
+ {0x0A83, 0x0A83, prN}, // Mc GUJARATI SIGN VISARGA
+ {0x0A85, 0x0A8D, prN}, // Lo [9] GUJARATI LETTER A..GUJARATI VOWEL CANDRA E
+ {0x0A8F, 0x0A91, prN}, // Lo [3] GUJARATI LETTER E..GUJARATI VOWEL CANDRA O
+ {0x0A93, 0x0AA8, prN}, // Lo [22] GUJARATI LETTER O..GUJARATI LETTER NA
+ {0x0AAA, 0x0AB0, prN}, // Lo [7] GUJARATI LETTER PA..GUJARATI LETTER RA
+ {0x0AB2, 0x0AB3, prN}, // Lo [2] GUJARATI LETTER LA..GUJARATI LETTER LLA
+ {0x0AB5, 0x0AB9, prN}, // Lo [5] GUJARATI LETTER VA..GUJARATI LETTER HA
+ {0x0ABC, 0x0ABC, prN}, // Mn GUJARATI SIGN NUKTA
+ {0x0ABD, 0x0ABD, prN}, // Lo GUJARATI SIGN AVAGRAHA
+ {0x0ABE, 0x0AC0, prN}, // Mc [3] GUJARATI VOWEL SIGN AA..GUJARATI VOWEL SIGN II
+ {0x0AC1, 0x0AC5, prN}, // Mn [5] GUJARATI VOWEL SIGN U..GUJARATI VOWEL SIGN CANDRA E
+ {0x0AC7, 0x0AC8, prN}, // Mn [2] GUJARATI VOWEL SIGN E..GUJARATI VOWEL SIGN AI
+ {0x0AC9, 0x0AC9, prN}, // Mc GUJARATI VOWEL SIGN CANDRA O
+ {0x0ACB, 0x0ACC, prN}, // Mc [2] GUJARATI VOWEL SIGN O..GUJARATI VOWEL SIGN AU
+ {0x0ACD, 0x0ACD, prN}, // Mn GUJARATI SIGN VIRAMA
+ {0x0AD0, 0x0AD0, prN}, // Lo GUJARATI OM
+ {0x0AE0, 0x0AE1, prN}, // Lo [2] GUJARATI LETTER VOCALIC RR..GUJARATI LETTER VOCALIC LL
+ {0x0AE2, 0x0AE3, prN}, // Mn [2] GUJARATI VOWEL SIGN VOCALIC L..GUJARATI VOWEL SIGN VOCALIC LL
+ {0x0AE6, 0x0AEF, prN}, // Nd [10] GUJARATI DIGIT ZERO..GUJARATI DIGIT NINE
+ {0x0AF0, 0x0AF0, prN}, // Po GUJARATI ABBREVIATION SIGN
+ {0x0AF1, 0x0AF1, prN}, // Sc GUJARATI RUPEE SIGN
+ {0x0AF9, 0x0AF9, prN}, // Lo GUJARATI LETTER ZHA
+ {0x0AFA, 0x0AFF, prN}, // Mn [6] GUJARATI SIGN SUKUN..GUJARATI SIGN TWO-CIRCLE NUKTA ABOVE
+ {0x0B01, 0x0B01, prN}, // Mn ORIYA SIGN CANDRABINDU
+ {0x0B02, 0x0B03, prN}, // Mc [2] ORIYA SIGN ANUSVARA..ORIYA SIGN VISARGA
+ {0x0B05, 0x0B0C, prN}, // Lo [8] ORIYA LETTER A..ORIYA LETTER VOCALIC L
+ {0x0B0F, 0x0B10, prN}, // Lo [2] ORIYA LETTER E..ORIYA LETTER AI
+ {0x0B13, 0x0B28, prN}, // Lo [22] ORIYA LETTER O..ORIYA LETTER NA
+ {0x0B2A, 0x0B30, prN}, // Lo [7] ORIYA LETTER PA..ORIYA LETTER RA
+ {0x0B32, 0x0B33, prN}, // Lo [2] ORIYA LETTER LA..ORIYA LETTER LLA
+ {0x0B35, 0x0B39, prN}, // Lo [5] ORIYA LETTER VA..ORIYA LETTER HA
+ {0x0B3C, 0x0B3C, prN}, // Mn ORIYA SIGN NUKTA
+ {0x0B3D, 0x0B3D, prN}, // Lo ORIYA SIGN AVAGRAHA
+ {0x0B3E, 0x0B3E, prN}, // Mc ORIYA VOWEL SIGN AA
+ {0x0B3F, 0x0B3F, prN}, // Mn ORIYA VOWEL SIGN I
+ {0x0B40, 0x0B40, prN}, // Mc ORIYA VOWEL SIGN II
+ {0x0B41, 0x0B44, prN}, // Mn [4] ORIYA VOWEL SIGN U..ORIYA VOWEL SIGN VOCALIC RR
+ {0x0B47, 0x0B48, prN}, // Mc [2] ORIYA VOWEL SIGN E..ORIYA VOWEL SIGN AI
+ {0x0B4B, 0x0B4C, prN}, // Mc [2] ORIYA VOWEL SIGN O..ORIYA VOWEL SIGN AU
+ {0x0B4D, 0x0B4D, prN}, // Mn ORIYA SIGN VIRAMA
+ {0x0B55, 0x0B56, prN}, // Mn [2] ORIYA SIGN OVERLINE..ORIYA AI LENGTH MARK
+ {0x0B57, 0x0B57, prN}, // Mc ORIYA AU LENGTH MARK
+ {0x0B5C, 0x0B5D, prN}, // Lo [2] ORIYA LETTER RRA..ORIYA LETTER RHA
+ {0x0B5F, 0x0B61, prN}, // Lo [3] ORIYA LETTER YYA..ORIYA LETTER VOCALIC LL
+ {0x0B62, 0x0B63, prN}, // Mn [2] ORIYA VOWEL SIGN VOCALIC L..ORIYA VOWEL SIGN VOCALIC LL
+ {0x0B66, 0x0B6F, prN}, // Nd [10] ORIYA DIGIT ZERO..ORIYA DIGIT NINE
+ {0x0B70, 0x0B70, prN}, // So ORIYA ISSHAR
+ {0x0B71, 0x0B71, prN}, // Lo ORIYA LETTER WA
+ {0x0B72, 0x0B77, prN}, // No [6] ORIYA FRACTION ONE QUARTER..ORIYA FRACTION THREE SIXTEENTHS
+ {0x0B82, 0x0B82, prN}, // Mn TAMIL SIGN ANUSVARA
+ {0x0B83, 0x0B83, prN}, // Lo TAMIL SIGN VISARGA
+ {0x0B85, 0x0B8A, prN}, // Lo [6] TAMIL LETTER A..TAMIL LETTER UU
+ {0x0B8E, 0x0B90, prN}, // Lo [3] TAMIL LETTER E..TAMIL LETTER AI
+ {0x0B92, 0x0B95, prN}, // Lo [4] TAMIL LETTER O..TAMIL LETTER KA
+ {0x0B99, 0x0B9A, prN}, // Lo [2] TAMIL LETTER NGA..TAMIL LETTER CA
+ {0x0B9C, 0x0B9C, prN}, // Lo TAMIL LETTER JA
+ {0x0B9E, 0x0B9F, prN}, // Lo [2] TAMIL LETTER NYA..TAMIL LETTER TTA
+ {0x0BA3, 0x0BA4, prN}, // Lo [2] TAMIL LETTER NNA..TAMIL LETTER TA
+ {0x0BA8, 0x0BAA, prN}, // Lo [3] TAMIL LETTER NA..TAMIL LETTER PA
+ {0x0BAE, 0x0BB9, prN}, // Lo [12] TAMIL LETTER MA..TAMIL LETTER HA
+ {0x0BBE, 0x0BBF, prN}, // Mc [2] TAMIL VOWEL SIGN AA..TAMIL VOWEL SIGN I
+ {0x0BC0, 0x0BC0, prN}, // Mn TAMIL VOWEL SIGN II
+ {0x0BC1, 0x0BC2, prN}, // Mc [2] TAMIL VOWEL SIGN U..TAMIL VOWEL SIGN UU
+ {0x0BC6, 0x0BC8, prN}, // Mc [3] TAMIL VOWEL SIGN E..TAMIL VOWEL SIGN AI
+ {0x0BCA, 0x0BCC, prN}, // Mc [3] TAMIL VOWEL SIGN O..TAMIL VOWEL SIGN AU
+ {0x0BCD, 0x0BCD, prN}, // Mn TAMIL SIGN VIRAMA
+ {0x0BD0, 0x0BD0, prN}, // Lo TAMIL OM
+ {0x0BD7, 0x0BD7, prN}, // Mc TAMIL AU LENGTH MARK
+ {0x0BE6, 0x0BEF, prN}, // Nd [10] TAMIL DIGIT ZERO..TAMIL DIGIT NINE
+ {0x0BF0, 0x0BF2, prN}, // No [3] TAMIL NUMBER TEN..TAMIL NUMBER ONE THOUSAND
+ {0x0BF3, 0x0BF8, prN}, // So [6] TAMIL DAY SIGN..TAMIL AS ABOVE SIGN
+ {0x0BF9, 0x0BF9, prN}, // Sc TAMIL RUPEE SIGN
+ {0x0BFA, 0x0BFA, prN}, // So TAMIL NUMBER SIGN
+ {0x0C00, 0x0C00, prN}, // Mn TELUGU SIGN COMBINING CANDRABINDU ABOVE
+ {0x0C01, 0x0C03, prN}, // Mc [3] TELUGU SIGN CANDRABINDU..TELUGU SIGN VISARGA
+ {0x0C04, 0x0C04, prN}, // Mn TELUGU SIGN COMBINING ANUSVARA ABOVE
+ {0x0C05, 0x0C0C, prN}, // Lo [8] TELUGU LETTER A..TELUGU LETTER VOCALIC L
+ {0x0C0E, 0x0C10, prN}, // Lo [3] TELUGU LETTER E..TELUGU LETTER AI
+ {0x0C12, 0x0C28, prN}, // Lo [23] TELUGU LETTER O..TELUGU LETTER NA
+ {0x0C2A, 0x0C39, prN}, // Lo [16] TELUGU LETTER PA..TELUGU LETTER HA
+ {0x0C3C, 0x0C3C, prN}, // Mn TELUGU SIGN NUKTA
+ {0x0C3D, 0x0C3D, prN}, // Lo TELUGU SIGN AVAGRAHA
+ {0x0C3E, 0x0C40, prN}, // Mn [3] TELUGU VOWEL SIGN AA..TELUGU VOWEL SIGN II
+ {0x0C41, 0x0C44, prN}, // Mc [4] TELUGU VOWEL SIGN U..TELUGU VOWEL SIGN VOCALIC RR
+ {0x0C46, 0x0C48, prN}, // Mn [3] TELUGU VOWEL SIGN E..TELUGU VOWEL SIGN AI
+ {0x0C4A, 0x0C4D, prN}, // Mn [4] TELUGU VOWEL SIGN O..TELUGU SIGN VIRAMA
+ {0x0C55, 0x0C56, prN}, // Mn [2] TELUGU LENGTH MARK..TELUGU AI LENGTH MARK
+ {0x0C58, 0x0C5A, prN}, // Lo [3] TELUGU LETTER TSA..TELUGU LETTER RRRA
+ {0x0C5D, 0x0C5D, prN}, // Lo TELUGU LETTER NAKAARA POLLU
+ {0x0C60, 0x0C61, prN}, // Lo [2] TELUGU LETTER VOCALIC RR..TELUGU LETTER VOCALIC LL
+ {0x0C62, 0x0C63, prN}, // Mn [2] TELUGU VOWEL SIGN VOCALIC L..TELUGU VOWEL SIGN VOCALIC LL
+ {0x0C66, 0x0C6F, prN}, // Nd [10] TELUGU DIGIT ZERO..TELUGU DIGIT NINE
+ {0x0C77, 0x0C77, prN}, // Po TELUGU SIGN SIDDHAM
+ {0x0C78, 0x0C7E, prN}, // No [7] TELUGU FRACTION DIGIT ZERO FOR ODD POWERS OF FOUR..TELUGU FRACTION DIGIT THREE FOR EVEN POWERS OF FOUR
+ {0x0C7F, 0x0C7F, prN}, // So TELUGU SIGN TUUMU
+ {0x0C80, 0x0C80, prN}, // Lo KANNADA SIGN SPACING CANDRABINDU
+ {0x0C81, 0x0C81, prN}, // Mn KANNADA SIGN CANDRABINDU
+ {0x0C82, 0x0C83, prN}, // Mc [2] KANNADA SIGN ANUSVARA..KANNADA SIGN VISARGA
+ {0x0C84, 0x0C84, prN}, // Po KANNADA SIGN SIDDHAM
+ {0x0C85, 0x0C8C, prN}, // Lo [8] KANNADA LETTER A..KANNADA LETTER VOCALIC L
+ {0x0C8E, 0x0C90, prN}, // Lo [3] KANNADA LETTER E..KANNADA LETTER AI
+ {0x0C92, 0x0CA8, prN}, // Lo [23] KANNADA LETTER O..KANNADA LETTER NA
+ {0x0CAA, 0x0CB3, prN}, // Lo [10] KANNADA LETTER PA..KANNADA LETTER LLA
+ {0x0CB5, 0x0CB9, prN}, // Lo [5] KANNADA LETTER VA..KANNADA LETTER HA
+ {0x0CBC, 0x0CBC, prN}, // Mn KANNADA SIGN NUKTA
+ {0x0CBD, 0x0CBD, prN}, // Lo KANNADA SIGN AVAGRAHA
+ {0x0CBE, 0x0CBE, prN}, // Mc KANNADA VOWEL SIGN AA
+ {0x0CBF, 0x0CBF, prN}, // Mn KANNADA VOWEL SIGN I
+ {0x0CC0, 0x0CC4, prN}, // Mc [5] KANNADA VOWEL SIGN II..KANNADA VOWEL SIGN VOCALIC RR
+ {0x0CC6, 0x0CC6, prN}, // Mn KANNADA VOWEL SIGN E
+ {0x0CC7, 0x0CC8, prN}, // Mc [2] KANNADA VOWEL SIGN EE..KANNADA VOWEL SIGN AI
+ {0x0CCA, 0x0CCB, prN}, // Mc [2] KANNADA VOWEL SIGN O..KANNADA VOWEL SIGN OO
+ {0x0CCC, 0x0CCD, prN}, // Mn [2] KANNADA VOWEL SIGN AU..KANNADA SIGN VIRAMA
+ {0x0CD5, 0x0CD6, prN}, // Mc [2] KANNADA LENGTH MARK..KANNADA AI LENGTH MARK
+ {0x0CDD, 0x0CDE, prN}, // Lo [2] KANNADA LETTER NAKAARA POLLU..KANNADA LETTER FA
+ {0x0CE0, 0x0CE1, prN}, // Lo [2] KANNADA LETTER VOCALIC RR..KANNADA LETTER VOCALIC LL
+ {0x0CE2, 0x0CE3, prN}, // Mn [2] KANNADA VOWEL SIGN VOCALIC L..KANNADA VOWEL SIGN VOCALIC LL
+ {0x0CE6, 0x0CEF, prN}, // Nd [10] KANNADA DIGIT ZERO..KANNADA DIGIT NINE
+ {0x0CF1, 0x0CF2, prN}, // Lo [2] KANNADA SIGN JIHVAMULIYA..KANNADA SIGN UPADHMANIYA
+ {0x0D00, 0x0D01, prN}, // Mn [2] MALAYALAM SIGN COMBINING ANUSVARA ABOVE..MALAYALAM SIGN CANDRABINDU
+ {0x0D02, 0x0D03, prN}, // Mc [2] MALAYALAM SIGN ANUSVARA..MALAYALAM SIGN VISARGA
+ {0x0D04, 0x0D0C, prN}, // Lo [9] MALAYALAM LETTER VEDIC ANUSVARA..MALAYALAM LETTER VOCALIC L
+ {0x0D0E, 0x0D10, prN}, // Lo [3] MALAYALAM LETTER E..MALAYALAM LETTER AI
+ {0x0D12, 0x0D3A, prN}, // Lo [41] MALAYALAM LETTER O..MALAYALAM LETTER TTTA
+ {0x0D3B, 0x0D3C, prN}, // Mn [2] MALAYALAM SIGN VERTICAL BAR VIRAMA..MALAYALAM SIGN CIRCULAR VIRAMA
+ {0x0D3D, 0x0D3D, prN}, // Lo MALAYALAM SIGN AVAGRAHA
+ {0x0D3E, 0x0D40, prN}, // Mc [3] MALAYALAM VOWEL SIGN AA..MALAYALAM VOWEL SIGN II
+ {0x0D41, 0x0D44, prN}, // Mn [4] MALAYALAM VOWEL SIGN U..MALAYALAM VOWEL SIGN VOCALIC RR
+ {0x0D46, 0x0D48, prN}, // Mc [3] MALAYALAM VOWEL SIGN E..MALAYALAM VOWEL SIGN AI
+ {0x0D4A, 0x0D4C, prN}, // Mc [3] MALAYALAM VOWEL SIGN O..MALAYALAM VOWEL SIGN AU
+ {0x0D4D, 0x0D4D, prN}, // Mn MALAYALAM SIGN VIRAMA
+ {0x0D4E, 0x0D4E, prN}, // Lo MALAYALAM LETTER DOT REPH
+ {0x0D4F, 0x0D4F, prN}, // So MALAYALAM SIGN PARA
+ {0x0D54, 0x0D56, prN}, // Lo [3] MALAYALAM LETTER CHILLU M..MALAYALAM LETTER CHILLU LLL
+ {0x0D57, 0x0D57, prN}, // Mc MALAYALAM AU LENGTH MARK
+ {0x0D58, 0x0D5E, prN}, // No [7] MALAYALAM FRACTION ONE ONE-HUNDRED-AND-SIXTIETH..MALAYALAM FRACTION ONE FIFTH
+ {0x0D5F, 0x0D61, prN}, // Lo [3] MALAYALAM LETTER ARCHAIC II..MALAYALAM LETTER VOCALIC LL
+ {0x0D62, 0x0D63, prN}, // Mn [2] MALAYALAM VOWEL SIGN VOCALIC L..MALAYALAM VOWEL SIGN VOCALIC LL
+ {0x0D66, 0x0D6F, prN}, // Nd [10] MALAYALAM DIGIT ZERO..MALAYALAM DIGIT NINE
+ {0x0D70, 0x0D78, prN}, // No [9] MALAYALAM NUMBER TEN..MALAYALAM FRACTION THREE SIXTEENTHS
+ {0x0D79, 0x0D79, prN}, // So MALAYALAM DATE MARK
+ {0x0D7A, 0x0D7F, prN}, // Lo [6] MALAYALAM LETTER CHILLU NN..MALAYALAM LETTER CHILLU K
+ {0x0D81, 0x0D81, prN}, // Mn SINHALA SIGN CANDRABINDU
+ {0x0D82, 0x0D83, prN}, // Mc [2] SINHALA SIGN ANUSVARAYA..SINHALA SIGN VISARGAYA
+ {0x0D85, 0x0D96, prN}, // Lo [18] SINHALA LETTER AYANNA..SINHALA LETTER AUYANNA
+ {0x0D9A, 0x0DB1, prN}, // Lo [24] SINHALA LETTER ALPAPRAANA KAYANNA..SINHALA LETTER DANTAJA NAYANNA
+ {0x0DB3, 0x0DBB, prN}, // Lo [9] SINHALA LETTER SANYAKA DAYANNA..SINHALA LETTER RAYANNA
+ {0x0DBD, 0x0DBD, prN}, // Lo SINHALA LETTER DANTAJA LAYANNA
+ {0x0DC0, 0x0DC6, prN}, // Lo [7] SINHALA LETTER VAYANNA..SINHALA LETTER FAYANNA
+ {0x0DCA, 0x0DCA, prN}, // Mn SINHALA SIGN AL-LAKUNA
+ {0x0DCF, 0x0DD1, prN}, // Mc [3] SINHALA VOWEL SIGN AELA-PILLA..SINHALA VOWEL SIGN DIGA AEDA-PILLA
+ {0x0DD2, 0x0DD4, prN}, // Mn [3] SINHALA VOWEL SIGN KETTI IS-PILLA..SINHALA VOWEL SIGN KETTI PAA-PILLA
+ {0x0DD6, 0x0DD6, prN}, // Mn SINHALA VOWEL SIGN DIGA PAA-PILLA
+ {0x0DD8, 0x0DDF, prN}, // Mc [8] SINHALA VOWEL SIGN GAETTA-PILLA..SINHALA VOWEL SIGN GAYANUKITTA
+ {0x0DE6, 0x0DEF, prN}, // Nd [10] SINHALA LITH DIGIT ZERO..SINHALA LITH DIGIT NINE
+ {0x0DF2, 0x0DF3, prN}, // Mc [2] SINHALA VOWEL SIGN DIGA GAETTA-PILLA..SINHALA VOWEL SIGN DIGA GAYANUKITTA
+ {0x0DF4, 0x0DF4, prN}, // Po SINHALA PUNCTUATION KUNDDALIYA
+ {0x0E01, 0x0E30, prN}, // Lo [48] THAI CHARACTER KO KAI..THAI CHARACTER SARA A
+ {0x0E31, 0x0E31, prN}, // Mn THAI CHARACTER MAI HAN-AKAT
+ {0x0E32, 0x0E33, prN}, // Lo [2] THAI CHARACTER SARA AA..THAI CHARACTER SARA AM
+ {0x0E34, 0x0E3A, prN}, // Mn [7] THAI CHARACTER SARA I..THAI CHARACTER PHINTHU
+ {0x0E3F, 0x0E3F, prN}, // Sc THAI CURRENCY SYMBOL BAHT
+ {0x0E40, 0x0E45, prN}, // Lo [6] THAI CHARACTER SARA E..THAI CHARACTER LAKKHANGYAO
+ {0x0E46, 0x0E46, prN}, // Lm THAI CHARACTER MAIYAMOK
+ {0x0E47, 0x0E4E, prN}, // Mn [8] THAI CHARACTER MAITAIKHU..THAI CHARACTER YAMAKKAN
+ {0x0E4F, 0x0E4F, prN}, // Po THAI CHARACTER FONGMAN
+ {0x0E50, 0x0E59, prN}, // Nd [10] THAI DIGIT ZERO..THAI DIGIT NINE
+ {0x0E5A, 0x0E5B, prN}, // Po [2] THAI CHARACTER ANGKHANKHU..THAI CHARACTER KHOMUT
+ {0x0E81, 0x0E82, prN}, // Lo [2] LAO LETTER KO..LAO LETTER KHO SUNG
+ {0x0E84, 0x0E84, prN}, // Lo LAO LETTER KHO TAM
+ {0x0E86, 0x0E8A, prN}, // Lo [5] LAO LETTER PALI GHA..LAO LETTER SO TAM
+ {0x0E8C, 0x0EA3, prN}, // Lo [24] LAO LETTER PALI JHA..LAO LETTER LO LING
+ {0x0EA5, 0x0EA5, prN}, // Lo LAO LETTER LO LOOT
+ {0x0EA7, 0x0EB0, prN}, // Lo [10] LAO LETTER WO..LAO VOWEL SIGN A
+ {0x0EB1, 0x0EB1, prN}, // Mn LAO VOWEL SIGN MAI KAN
+ {0x0EB2, 0x0EB3, prN}, // Lo [2] LAO VOWEL SIGN AA..LAO VOWEL SIGN AM
+ {0x0EB4, 0x0EBC, prN}, // Mn [9] LAO VOWEL SIGN I..LAO SEMIVOWEL SIGN LO
+ {0x0EBD, 0x0EBD, prN}, // Lo LAO SEMIVOWEL SIGN NYO
+ {0x0EC0, 0x0EC4, prN}, // Lo [5] LAO VOWEL SIGN E..LAO VOWEL SIGN AI
+ {0x0EC6, 0x0EC6, prN}, // Lm LAO KO LA
+ {0x0EC8, 0x0ECD, prN}, // Mn [6] LAO TONE MAI EK..LAO NIGGAHITA
+ {0x0ED0, 0x0ED9, prN}, // Nd [10] LAO DIGIT ZERO..LAO DIGIT NINE
+ {0x0EDC, 0x0EDF, prN}, // Lo [4] LAO HO NO..LAO LETTER KHMU NYO
+ {0x0F00, 0x0F00, prN}, // Lo TIBETAN SYLLABLE OM
+ {0x0F01, 0x0F03, prN}, // So [3] TIBETAN MARK GTER YIG MGO TRUNCATED A..TIBETAN MARK GTER YIG MGO -UM GTER TSHEG MA
+ {0x0F04, 0x0F12, prN}, // Po [15] TIBETAN MARK INITIAL YIG MGO MDUN MA..TIBETAN MARK RGYA GRAM SHAD
+ {0x0F13, 0x0F13, prN}, // So TIBETAN MARK CARET -DZUD RTAGS ME LONG CAN
+ {0x0F14, 0x0F14, prN}, // Po TIBETAN MARK GTER TSHEG
+ {0x0F15, 0x0F17, prN}, // So [3] TIBETAN LOGOTYPE SIGN CHAD RTAGS..TIBETAN ASTROLOGICAL SIGN SGRA GCAN -CHAR RTAGS
+ {0x0F18, 0x0F19, prN}, // Mn [2] TIBETAN ASTROLOGICAL SIGN -KHYUD PA..TIBETAN ASTROLOGICAL SIGN SDONG TSHUGS
+ {0x0F1A, 0x0F1F, prN}, // So [6] TIBETAN SIGN RDEL DKAR GCIG..TIBETAN SIGN RDEL DKAR RDEL NAG
+ {0x0F20, 0x0F29, prN}, // Nd [10] TIBETAN DIGIT ZERO..TIBETAN DIGIT NINE
+ {0x0F2A, 0x0F33, prN}, // No [10] TIBETAN DIGIT HALF ONE..TIBETAN DIGIT HALF ZERO
+ {0x0F34, 0x0F34, prN}, // So TIBETAN MARK BSDUS RTAGS
+ {0x0F35, 0x0F35, prN}, // Mn TIBETAN MARK NGAS BZUNG NYI ZLA
+ {0x0F36, 0x0F36, prN}, // So TIBETAN MARK CARET -DZUD RTAGS BZHI MIG CAN
+ {0x0F37, 0x0F37, prN}, // Mn TIBETAN MARK NGAS BZUNG SGOR RTAGS
+ {0x0F38, 0x0F38, prN}, // So TIBETAN MARK CHE MGO
+ {0x0F39, 0x0F39, prN}, // Mn TIBETAN MARK TSA -PHRU
+ {0x0F3A, 0x0F3A, prN}, // Ps TIBETAN MARK GUG RTAGS GYON
+ {0x0F3B, 0x0F3B, prN}, // Pe TIBETAN MARK GUG RTAGS GYAS
+ {0x0F3C, 0x0F3C, prN}, // Ps TIBETAN MARK ANG KHANG GYON
+ {0x0F3D, 0x0F3D, prN}, // Pe TIBETAN MARK ANG KHANG GYAS
+ {0x0F3E, 0x0F3F, prN}, // Mc [2] TIBETAN SIGN YAR TSHES..TIBETAN SIGN MAR TSHES
+ {0x0F40, 0x0F47, prN}, // Lo [8] TIBETAN LETTER KA..TIBETAN LETTER JA
+ {0x0F49, 0x0F6C, prN}, // Lo [36] TIBETAN LETTER NYA..TIBETAN LETTER RRA
+ {0x0F71, 0x0F7E, prN}, // Mn [14] TIBETAN VOWEL SIGN AA..TIBETAN SIGN RJES SU NGA RO
+ {0x0F7F, 0x0F7F, prN}, // Mc TIBETAN SIGN RNAM BCAD
+ {0x0F80, 0x0F84, prN}, // Mn [5] TIBETAN VOWEL SIGN REVERSED I..TIBETAN MARK HALANTA
+ {0x0F85, 0x0F85, prN}, // Po TIBETAN MARK PALUTA
+ {0x0F86, 0x0F87, prN}, // Mn [2] TIBETAN SIGN LCI RTAGS..TIBETAN SIGN YANG RTAGS
+ {0x0F88, 0x0F8C, prN}, // Lo [5] TIBETAN SIGN LCE TSA CAN..TIBETAN SIGN INVERTED MCHU CAN
+ {0x0F8D, 0x0F97, prN}, // Mn [11] TIBETAN SUBJOINED SIGN LCE TSA CAN..TIBETAN SUBJOINED LETTER JA
+ {0x0F99, 0x0FBC, prN}, // Mn [36] TIBETAN SUBJOINED LETTER NYA..TIBETAN SUBJOINED LETTER FIXED-FORM RA
+ {0x0FBE, 0x0FC5, prN}, // So [8] TIBETAN KU RU KHA..TIBETAN SYMBOL RDO RJE
+ {0x0FC6, 0x0FC6, prN}, // Mn TIBETAN SYMBOL PADMA GDAN
+ {0x0FC7, 0x0FCC, prN}, // So [6] TIBETAN SYMBOL RDO RJE RGYA GRAM..TIBETAN SYMBOL NOR BU BZHI -KHYIL
+ {0x0FCE, 0x0FCF, prN}, // So [2] TIBETAN SIGN RDEL NAG RDEL DKAR..TIBETAN SIGN RDEL NAG GSUM
+ {0x0FD0, 0x0FD4, prN}, // Po [5] TIBETAN MARK BSKA- SHOG GI MGO RGYAN..TIBETAN MARK CLOSING BRDA RNYING YIG MGO SGAB MA
+ {0x0FD5, 0x0FD8, prN}, // So [4] RIGHT-FACING SVASTI SIGN..LEFT-FACING SVASTI SIGN WITH DOTS
+ {0x0FD9, 0x0FDA, prN}, // Po [2] TIBETAN MARK LEADING MCHAN RTAGS..TIBETAN MARK TRAILING MCHAN RTAGS
+ {0x1000, 0x102A, prN}, // Lo [43] MYANMAR LETTER KA..MYANMAR LETTER AU
+ {0x102B, 0x102C, prN}, // Mc [2] MYANMAR VOWEL SIGN TALL AA..MYANMAR VOWEL SIGN AA
+ {0x102D, 0x1030, prN}, // Mn [4] MYANMAR VOWEL SIGN I..MYANMAR VOWEL SIGN UU
+ {0x1031, 0x1031, prN}, // Mc MYANMAR VOWEL SIGN E
+ {0x1032, 0x1037, prN}, // Mn [6] MYANMAR VOWEL SIGN AI..MYANMAR SIGN DOT BELOW
+ {0x1038, 0x1038, prN}, // Mc MYANMAR SIGN VISARGA
+ {0x1039, 0x103A, prN}, // Mn [2] MYANMAR SIGN VIRAMA..MYANMAR SIGN ASAT
+ {0x103B, 0x103C, prN}, // Mc [2] MYANMAR CONSONANT SIGN MEDIAL YA..MYANMAR CONSONANT SIGN MEDIAL RA
+ {0x103D, 0x103E, prN}, // Mn [2] MYANMAR CONSONANT SIGN MEDIAL WA..MYANMAR CONSONANT SIGN MEDIAL HA
+ {0x103F, 0x103F, prN}, // Lo MYANMAR LETTER GREAT SA
+ {0x1040, 0x1049, prN}, // Nd [10] MYANMAR DIGIT ZERO..MYANMAR DIGIT NINE
+ {0x104A, 0x104F, prN}, // Po [6] MYANMAR SIGN LITTLE SECTION..MYANMAR SYMBOL GENITIVE
+ {0x1050, 0x1055, prN}, // Lo [6] MYANMAR LETTER SHA..MYANMAR LETTER VOCALIC LL
+ {0x1056, 0x1057, prN}, // Mc [2] MYANMAR VOWEL SIGN VOCALIC R..MYANMAR VOWEL SIGN VOCALIC RR
+ {0x1058, 0x1059, prN}, // Mn [2] MYANMAR VOWEL SIGN VOCALIC L..MYANMAR VOWEL SIGN VOCALIC LL
+ {0x105A, 0x105D, prN}, // Lo [4] MYANMAR LETTER MON NGA..MYANMAR LETTER MON BBE
+ {0x105E, 0x1060, prN}, // Mn [3] MYANMAR CONSONANT SIGN MON MEDIAL NA..MYANMAR CONSONANT SIGN MON MEDIAL LA
+ {0x1061, 0x1061, prN}, // Lo MYANMAR LETTER SGAW KAREN SHA
+ {0x1062, 0x1064, prN}, // Mc [3] MYANMAR VOWEL SIGN SGAW KAREN EU..MYANMAR TONE MARK SGAW KAREN KE PHO
+ {0x1065, 0x1066, prN}, // Lo [2] MYANMAR LETTER WESTERN PWO KAREN THA..MYANMAR LETTER WESTERN PWO KAREN PWA
+ {0x1067, 0x106D, prN}, // Mc [7] MYANMAR VOWEL SIGN WESTERN PWO KAREN EU..MYANMAR SIGN WESTERN PWO KAREN TONE-5
+ {0x106E, 0x1070, prN}, // Lo [3] MYANMAR LETTER EASTERN PWO KAREN NNA..MYANMAR LETTER EASTERN PWO KAREN GHWA
+ {0x1071, 0x1074, prN}, // Mn [4] MYANMAR VOWEL SIGN GEBA KAREN I..MYANMAR VOWEL SIGN KAYAH EE
+ {0x1075, 0x1081, prN}, // Lo [13] MYANMAR LETTER SHAN KA..MYANMAR LETTER SHAN HA
+ {0x1082, 0x1082, prN}, // Mn MYANMAR CONSONANT SIGN SHAN MEDIAL WA
+ {0x1083, 0x1084, prN}, // Mc [2] MYANMAR VOWEL SIGN SHAN AA..MYANMAR VOWEL SIGN SHAN E
+ {0x1085, 0x1086, prN}, // Mn [2] MYANMAR VOWEL SIGN SHAN E ABOVE..MYANMAR VOWEL SIGN SHAN FINAL Y
+ {0x1087, 0x108C, prN}, // Mc [6] MYANMAR SIGN SHAN TONE-2..MYANMAR SIGN SHAN COUNCIL TONE-3
+ {0x108D, 0x108D, prN}, // Mn MYANMAR SIGN SHAN COUNCIL EMPHATIC TONE
+ {0x108E, 0x108E, prN}, // Lo MYANMAR LETTER RUMAI PALAUNG FA
+ {0x108F, 0x108F, prN}, // Mc MYANMAR SIGN RUMAI PALAUNG TONE-5
+ {0x1090, 0x1099, prN}, // Nd [10] MYANMAR SHAN DIGIT ZERO..MYANMAR SHAN DIGIT NINE
+ {0x109A, 0x109C, prN}, // Mc [3] MYANMAR SIGN KHAMTI TONE-1..MYANMAR VOWEL SIGN AITON A
+ {0x109D, 0x109D, prN}, // Mn MYANMAR VOWEL SIGN AITON AI
+ {0x109E, 0x109F, prN}, // So [2] MYANMAR SYMBOL SHAN ONE..MYANMAR SYMBOL SHAN EXCLAMATION
+ {0x10A0, 0x10C5, prN}, // Lu [38] GEORGIAN CAPITAL LETTER AN..GEORGIAN CAPITAL LETTER HOE
+ {0x10C7, 0x10C7, prN}, // Lu GEORGIAN CAPITAL LETTER YN
+ {0x10CD, 0x10CD, prN}, // Lu GEORGIAN CAPITAL LETTER AEN
+ {0x10D0, 0x10FA, prN}, // Ll [43] GEORGIAN LETTER AN..GEORGIAN LETTER AIN
+ {0x10FB, 0x10FB, prN}, // Po GEORGIAN PARAGRAPH SEPARATOR
+ {0x10FC, 0x10FC, prN}, // Lm MODIFIER LETTER GEORGIAN NAR
+ {0x10FD, 0x10FF, prN}, // Ll [3] GEORGIAN LETTER AEN..GEORGIAN LETTER LABIAL SIGN
+ {0x1100, 0x115F, prW}, // Lo [96] HANGUL CHOSEONG KIYEOK..HANGUL CHOSEONG FILLER
+ {0x1160, 0x11FF, prN}, // Lo [160] HANGUL JUNGSEONG FILLER..HANGUL JONGSEONG SSANGNIEUN
+ {0x1200, 0x1248, prN}, // Lo [73] ETHIOPIC SYLLABLE HA..ETHIOPIC SYLLABLE QWA
+ {0x124A, 0x124D, prN}, // Lo [4] ETHIOPIC SYLLABLE QWI..ETHIOPIC SYLLABLE QWE
+ {0x1250, 0x1256, prN}, // Lo [7] ETHIOPIC SYLLABLE QHA..ETHIOPIC SYLLABLE QHO
+ {0x1258, 0x1258, prN}, // Lo ETHIOPIC SYLLABLE QHWA
+ {0x125A, 0x125D, prN}, // Lo [4] ETHIOPIC SYLLABLE QHWI..ETHIOPIC SYLLABLE QHWE
+ {0x1260, 0x1288, prN}, // Lo [41] ETHIOPIC SYLLABLE BA..ETHIOPIC SYLLABLE XWA
+ {0x128A, 0x128D, prN}, // Lo [4] ETHIOPIC SYLLABLE XWI..ETHIOPIC SYLLABLE XWE
+ {0x1290, 0x12B0, prN}, // Lo [33] ETHIOPIC SYLLABLE NA..ETHIOPIC SYLLABLE KWA
+ {0x12B2, 0x12B5, prN}, // Lo [4] ETHIOPIC SYLLABLE KWI..ETHIOPIC SYLLABLE KWE
+ {0x12B8, 0x12BE, prN}, // Lo [7] ETHIOPIC SYLLABLE KXA..ETHIOPIC SYLLABLE KXO
+ {0x12C0, 0x12C0, prN}, // Lo ETHIOPIC SYLLABLE KXWA
+ {0x12C2, 0x12C5, prN}, // Lo [4] ETHIOPIC SYLLABLE KXWI..ETHIOPIC SYLLABLE KXWE
+ {0x12C8, 0x12D6, prN}, // Lo [15] ETHIOPIC SYLLABLE WA..ETHIOPIC SYLLABLE PHARYNGEAL O
+ {0x12D8, 0x1310, prN}, // Lo [57] ETHIOPIC SYLLABLE ZA..ETHIOPIC SYLLABLE GWA
+ {0x1312, 0x1315, prN}, // Lo [4] ETHIOPIC SYLLABLE GWI..ETHIOPIC SYLLABLE GWE
+ {0x1318, 0x135A, prN}, // Lo [67] ETHIOPIC SYLLABLE GGA..ETHIOPIC SYLLABLE FYA
+ {0x135D, 0x135F, prN}, // Mn [3] ETHIOPIC COMBINING GEMINATION AND VOWEL LENGTH MARK..ETHIOPIC COMBINING GEMINATION MARK
+ {0x1360, 0x1368, prN}, // Po [9] ETHIOPIC SECTION MARK..ETHIOPIC PARAGRAPH SEPARATOR
+ {0x1369, 0x137C, prN}, // No [20] ETHIOPIC DIGIT ONE..ETHIOPIC NUMBER TEN THOUSAND
+ {0x1380, 0x138F, prN}, // Lo [16] ETHIOPIC SYLLABLE SEBATBEIT MWA..ETHIOPIC SYLLABLE PWE
+ {0x1390, 0x1399, prN}, // So [10] ETHIOPIC TONAL MARK YIZET..ETHIOPIC TONAL MARK KURT
+ {0x13A0, 0x13F5, prN}, // Lu [86] CHEROKEE LETTER A..CHEROKEE LETTER MV
+ {0x13F8, 0x13FD, prN}, // Ll [6] CHEROKEE SMALL LETTER YE..CHEROKEE SMALL LETTER MV
+ {0x1400, 0x1400, prN}, // Pd CANADIAN SYLLABICS HYPHEN
+ {0x1401, 0x166C, prN}, // Lo [620] CANADIAN SYLLABICS E..CANADIAN SYLLABICS CARRIER TTSA
+ {0x166D, 0x166D, prN}, // So CANADIAN SYLLABICS CHI SIGN
+ {0x166E, 0x166E, prN}, // Po CANADIAN SYLLABICS FULL STOP
+ {0x166F, 0x167F, prN}, // Lo [17] CANADIAN SYLLABICS QAI..CANADIAN SYLLABICS BLACKFOOT W
+ {0x1680, 0x1680, prN}, // Zs OGHAM SPACE MARK
+ {0x1681, 0x169A, prN}, // Lo [26] OGHAM LETTER BEITH..OGHAM LETTER PEITH
+ {0x169B, 0x169B, prN}, // Ps OGHAM FEATHER MARK
+ {0x169C, 0x169C, prN}, // Pe OGHAM REVERSED FEATHER MARK
+ {0x16A0, 0x16EA, prN}, // Lo [75] RUNIC LETTER FEHU FEOH FE F..RUNIC LETTER X
+ {0x16EB, 0x16ED, prN}, // Po [3] RUNIC SINGLE PUNCTUATION..RUNIC CROSS PUNCTUATION
+ {0x16EE, 0x16F0, prN}, // Nl [3] RUNIC ARLAUG SYMBOL..RUNIC BELGTHOR SYMBOL
+ {0x16F1, 0x16F8, prN}, // Lo [8] RUNIC LETTER K..RUNIC LETTER FRANKS CASKET AESC
+ {0x1700, 0x1711, prN}, // Lo [18] TAGALOG LETTER A..TAGALOG LETTER HA
+ {0x1712, 0x1714, prN}, // Mn [3] TAGALOG VOWEL SIGN I..TAGALOG SIGN VIRAMA
+ {0x1715, 0x1715, prN}, // Mc TAGALOG SIGN PAMUDPOD
+ {0x171F, 0x171F, prN}, // Lo TAGALOG LETTER ARCHAIC RA
+ {0x1720, 0x1731, prN}, // Lo [18] HANUNOO LETTER A..HANUNOO LETTER HA
+ {0x1732, 0x1733, prN}, // Mn [2] HANUNOO VOWEL SIGN I..HANUNOO VOWEL SIGN U
+ {0x1734, 0x1734, prN}, // Mc HANUNOO SIGN PAMUDPOD
+ {0x1735, 0x1736, prN}, // Po [2] PHILIPPINE SINGLE PUNCTUATION..PHILIPPINE DOUBLE PUNCTUATION
+ {0x1740, 0x1751, prN}, // Lo [18] BUHID LETTER A..BUHID LETTER HA
+ {0x1752, 0x1753, prN}, // Mn [2] BUHID VOWEL SIGN I..BUHID VOWEL SIGN U
+ {0x1760, 0x176C, prN}, // Lo [13] TAGBANWA LETTER A..TAGBANWA LETTER YA
+ {0x176E, 0x1770, prN}, // Lo [3] TAGBANWA LETTER LA..TAGBANWA LETTER SA
+ {0x1772, 0x1773, prN}, // Mn [2] TAGBANWA VOWEL SIGN I..TAGBANWA VOWEL SIGN U
+ {0x1780, 0x17B3, prN}, // Lo [52] KHMER LETTER KA..KHMER INDEPENDENT VOWEL QAU
+ {0x17B4, 0x17B5, prN}, // Mn [2] KHMER VOWEL INHERENT AQ..KHMER VOWEL INHERENT AA
+ {0x17B6, 0x17B6, prN}, // Mc KHMER VOWEL SIGN AA
+ {0x17B7, 0x17BD, prN}, // Mn [7] KHMER VOWEL SIGN I..KHMER VOWEL SIGN UA
+ {0x17BE, 0x17C5, prN}, // Mc [8] KHMER VOWEL SIGN OE..KHMER VOWEL SIGN AU
+ {0x17C6, 0x17C6, prN}, // Mn KHMER SIGN NIKAHIT
+ {0x17C7, 0x17C8, prN}, // Mc [2] KHMER SIGN REAHMUK..KHMER SIGN YUUKALEAPINTU
+ {0x17C9, 0x17D3, prN}, // Mn [11] KHMER SIGN MUUSIKATOAN..KHMER SIGN BATHAMASAT
+ {0x17D4, 0x17D6, prN}, // Po [3] KHMER SIGN KHAN..KHMER SIGN CAMNUC PII KUUH
+ {0x17D7, 0x17D7, prN}, // Lm KHMER SIGN LEK TOO
+ {0x17D8, 0x17DA, prN}, // Po [3] KHMER SIGN BEYYAL..KHMER SIGN KOOMUUT
+ {0x17DB, 0x17DB, prN}, // Sc KHMER CURRENCY SYMBOL RIEL
+ {0x17DC, 0x17DC, prN}, // Lo KHMER SIGN AVAKRAHASANYA
+ {0x17DD, 0x17DD, prN}, // Mn KHMER SIGN ATTHACAN
+ {0x17E0, 0x17E9, prN}, // Nd [10] KHMER DIGIT ZERO..KHMER DIGIT NINE
+ {0x17F0, 0x17F9, prN}, // No [10] KHMER SYMBOL LEK ATTAK SON..KHMER SYMBOL LEK ATTAK PRAM-BUON
+ {0x1800, 0x1805, prN}, // Po [6] MONGOLIAN BIRGA..MONGOLIAN FOUR DOTS
+ {0x1806, 0x1806, prN}, // Pd MONGOLIAN TODO SOFT HYPHEN
+ {0x1807, 0x180A, prN}, // Po [4] MONGOLIAN SIBE SYLLABLE BOUNDARY MARKER..MONGOLIAN NIRUGU
+ {0x180B, 0x180D, prN}, // Mn [3] MONGOLIAN FREE VARIATION SELECTOR ONE..MONGOLIAN FREE VARIATION SELECTOR THREE
+ {0x180E, 0x180E, prN}, // Cf MONGOLIAN VOWEL SEPARATOR
+ {0x180F, 0x180F, prN}, // Mn MONGOLIAN FREE VARIATION SELECTOR FOUR
+ {0x1810, 0x1819, prN}, // Nd [10] MONGOLIAN DIGIT ZERO..MONGOLIAN DIGIT NINE
+ {0x1820, 0x1842, prN}, // Lo [35] MONGOLIAN LETTER A..MONGOLIAN LETTER CHI
+ {0x1843, 0x1843, prN}, // Lm MONGOLIAN LETTER TODO LONG VOWEL SIGN
+ {0x1844, 0x1878, prN}, // Lo [53] MONGOLIAN LETTER TODO E..MONGOLIAN LETTER CHA WITH TWO DOTS
+ {0x1880, 0x1884, prN}, // Lo [5] MONGOLIAN LETTER ALI GALI ANUSVARA ONE..MONGOLIAN LETTER ALI GALI INVERTED UBADAMA
+ {0x1885, 0x1886, prN}, // Mn [2] MONGOLIAN LETTER ALI GALI BALUDA..MONGOLIAN LETTER ALI GALI THREE BALUDA
+ {0x1887, 0x18A8, prN}, // Lo [34] MONGOLIAN LETTER ALI GALI A..MONGOLIAN LETTER MANCHU ALI GALI BHA
+ {0x18A9, 0x18A9, prN}, // Mn MONGOLIAN LETTER ALI GALI DAGALGA
+ {0x18AA, 0x18AA, prN}, // Lo MONGOLIAN LETTER MANCHU ALI GALI LHA
+ {0x18B0, 0x18F5, prN}, // Lo [70] CANADIAN SYLLABICS OY..CANADIAN SYLLABICS CARRIER DENTAL S
+ {0x1900, 0x191E, prN}, // Lo [31] LIMBU VOWEL-CARRIER LETTER..LIMBU LETTER TRA
+ {0x1920, 0x1922, prN}, // Mn [3] LIMBU VOWEL SIGN A..LIMBU VOWEL SIGN U
+ {0x1923, 0x1926, prN}, // Mc [4] LIMBU VOWEL SIGN EE..LIMBU VOWEL SIGN AU
+ {0x1927, 0x1928, prN}, // Mn [2] LIMBU VOWEL SIGN E..LIMBU VOWEL SIGN O
+ {0x1929, 0x192B, prN}, // Mc [3] LIMBU SUBJOINED LETTER YA..LIMBU SUBJOINED LETTER WA
+ {0x1930, 0x1931, prN}, // Mc [2] LIMBU SMALL LETTER KA..LIMBU SMALL LETTER NGA
+ {0x1932, 0x1932, prN}, // Mn LIMBU SMALL LETTER ANUSVARA
+ {0x1933, 0x1938, prN}, // Mc [6] LIMBU SMALL LETTER TA..LIMBU SMALL LETTER LA
+ {0x1939, 0x193B, prN}, // Mn [3] LIMBU SIGN MUKPHRENG..LIMBU SIGN SA-I
+ {0x1940, 0x1940, prN}, // So LIMBU SIGN LOO
+ {0x1944, 0x1945, prN}, // Po [2] LIMBU EXCLAMATION MARK..LIMBU QUESTION MARK
+ {0x1946, 0x194F, prN}, // Nd [10] LIMBU DIGIT ZERO..LIMBU DIGIT NINE
+ {0x1950, 0x196D, prN}, // Lo [30] TAI LE LETTER KA..TAI LE LETTER AI
+ {0x1970, 0x1974, prN}, // Lo [5] TAI LE LETTER TONE-2..TAI LE LETTER TONE-6
+ {0x1980, 0x19AB, prN}, // Lo [44] NEW TAI LUE LETTER HIGH QA..NEW TAI LUE LETTER LOW SUA
+ {0x19B0, 0x19C9, prN}, // Lo [26] NEW TAI LUE VOWEL SIGN VOWEL SHORTENER..NEW TAI LUE TONE MARK-2
+ {0x19D0, 0x19D9, prN}, // Nd [10] NEW TAI LUE DIGIT ZERO..NEW TAI LUE DIGIT NINE
+ {0x19DA, 0x19DA, prN}, // No NEW TAI LUE THAM DIGIT ONE
+ {0x19DE, 0x19DF, prN}, // So [2] NEW TAI LUE SIGN LAE..NEW TAI LUE SIGN LAEV
+ {0x19E0, 0x19FF, prN}, // So [32] KHMER SYMBOL PATHAMASAT..KHMER SYMBOL DAP-PRAM ROC
+ {0x1A00, 0x1A16, prN}, // Lo [23] BUGINESE LETTER KA..BUGINESE LETTER HA
+ {0x1A17, 0x1A18, prN}, // Mn [2] BUGINESE VOWEL SIGN I..BUGINESE VOWEL SIGN U
+ {0x1A19, 0x1A1A, prN}, // Mc [2] BUGINESE VOWEL SIGN E..BUGINESE VOWEL SIGN O
+ {0x1A1B, 0x1A1B, prN}, // Mn BUGINESE VOWEL SIGN AE
+ {0x1A1E, 0x1A1F, prN}, // Po [2] BUGINESE PALLAWA..BUGINESE END OF SECTION
+ {0x1A20, 0x1A54, prN}, // Lo [53] TAI THAM LETTER HIGH KA..TAI THAM LETTER GREAT SA
+ {0x1A55, 0x1A55, prN}, // Mc TAI THAM CONSONANT SIGN MEDIAL RA
+ {0x1A56, 0x1A56, prN}, // Mn TAI THAM CONSONANT SIGN MEDIAL LA
+ {0x1A57, 0x1A57, prN}, // Mc TAI THAM CONSONANT SIGN LA TANG LAI
+ {0x1A58, 0x1A5E, prN}, // Mn [7] TAI THAM SIGN MAI KANG LAI..TAI THAM CONSONANT SIGN SA
+ {0x1A60, 0x1A60, prN}, // Mn TAI THAM SIGN SAKOT
+ {0x1A61, 0x1A61, prN}, // Mc TAI THAM VOWEL SIGN A
+ {0x1A62, 0x1A62, prN}, // Mn TAI THAM VOWEL SIGN MAI SAT
+ {0x1A63, 0x1A64, prN}, // Mc [2] TAI THAM VOWEL SIGN AA..TAI THAM VOWEL SIGN TALL AA
+ {0x1A65, 0x1A6C, prN}, // Mn [8] TAI THAM VOWEL SIGN I..TAI THAM VOWEL SIGN OA BELOW
+ {0x1A6D, 0x1A72, prN}, // Mc [6] TAI THAM VOWEL SIGN OY..TAI THAM VOWEL SIGN THAM AI
+ {0x1A73, 0x1A7C, prN}, // Mn [10] TAI THAM VOWEL SIGN OA ABOVE..TAI THAM SIGN KHUEN-LUE KARAN
+ {0x1A7F, 0x1A7F, prN}, // Mn TAI THAM COMBINING CRYPTOGRAMMIC DOT
+ {0x1A80, 0x1A89, prN}, // Nd [10] TAI THAM HORA DIGIT ZERO..TAI THAM HORA DIGIT NINE
+ {0x1A90, 0x1A99, prN}, // Nd [10] TAI THAM THAM DIGIT ZERO..TAI THAM THAM DIGIT NINE
+ {0x1AA0, 0x1AA6, prN}, // Po [7] TAI THAM SIGN WIANG..TAI THAM SIGN REVERSED ROTATED RANA
+ {0x1AA7, 0x1AA7, prN}, // Lm TAI THAM SIGN MAI YAMOK
+ {0x1AA8, 0x1AAD, prN}, // Po [6] TAI THAM SIGN KAAN..TAI THAM SIGN CAANG
+ {0x1AB0, 0x1ABD, prN}, // Mn [14] COMBINING DOUBLED CIRCUMFLEX ACCENT..COMBINING PARENTHESES BELOW
+ {0x1ABE, 0x1ABE, prN}, // Me COMBINING PARENTHESES OVERLAY
+ {0x1ABF, 0x1ACE, prN}, // Mn [16] COMBINING LATIN SMALL LETTER W BELOW..COMBINING LATIN SMALL LETTER INSULAR T
+ {0x1B00, 0x1B03, prN}, // Mn [4] BALINESE SIGN ULU RICEM..BALINESE SIGN SURANG
+ {0x1B04, 0x1B04, prN}, // Mc BALINESE SIGN BISAH
+ {0x1B05, 0x1B33, prN}, // Lo [47] BALINESE LETTER AKARA..BALINESE LETTER HA
+ {0x1B34, 0x1B34, prN}, // Mn BALINESE SIGN REREKAN
+ {0x1B35, 0x1B35, prN}, // Mc BALINESE VOWEL SIGN TEDUNG
+ {0x1B36, 0x1B3A, prN}, // Mn [5] BALINESE VOWEL SIGN ULU..BALINESE VOWEL SIGN RA REPA
+ {0x1B3B, 0x1B3B, prN}, // Mc BALINESE VOWEL SIGN RA REPA TEDUNG
+ {0x1B3C, 0x1B3C, prN}, // Mn BALINESE VOWEL SIGN LA LENGA
+ {0x1B3D, 0x1B41, prN}, // Mc [5] BALINESE VOWEL SIGN LA LENGA TEDUNG..BALINESE VOWEL SIGN TALING REPA TEDUNG
+ {0x1B42, 0x1B42, prN}, // Mn BALINESE VOWEL SIGN PEPET
+ {0x1B43, 0x1B44, prN}, // Mc [2] BALINESE VOWEL SIGN PEPET TEDUNG..BALINESE ADEG ADEG
+ {0x1B45, 0x1B4C, prN}, // Lo [8] BALINESE LETTER KAF SASAK..BALINESE LETTER ARCHAIC JNYA
+ {0x1B50, 0x1B59, prN}, // Nd [10] BALINESE DIGIT ZERO..BALINESE DIGIT NINE
+ {0x1B5A, 0x1B60, prN}, // Po [7] BALINESE PANTI..BALINESE PAMENENG
+ {0x1B61, 0x1B6A, prN}, // So [10] BALINESE MUSICAL SYMBOL DONG..BALINESE MUSICAL SYMBOL DANG GEDE
+ {0x1B6B, 0x1B73, prN}, // Mn [9] BALINESE MUSICAL SYMBOL COMBINING TEGEH..BALINESE MUSICAL SYMBOL COMBINING GONG
+ {0x1B74, 0x1B7C, prN}, // So [9] BALINESE MUSICAL SYMBOL RIGHT-HAND OPEN DUG..BALINESE MUSICAL SYMBOL LEFT-HAND OPEN PING
+ {0x1B7D, 0x1B7E, prN}, // Po [2] BALINESE PANTI LANTANG..BALINESE PAMADA LANTANG
+ {0x1B80, 0x1B81, prN}, // Mn [2] SUNDANESE SIGN PANYECEK..SUNDANESE SIGN PANGLAYAR
+ {0x1B82, 0x1B82, prN}, // Mc SUNDANESE SIGN PANGWISAD
+ {0x1B83, 0x1BA0, prN}, // Lo [30] SUNDANESE LETTER A..SUNDANESE LETTER HA
+ {0x1BA1, 0x1BA1, prN}, // Mc SUNDANESE CONSONANT SIGN PAMINGKAL
+ {0x1BA2, 0x1BA5, prN}, // Mn [4] SUNDANESE CONSONANT SIGN PANYAKRA..SUNDANESE VOWEL SIGN PANYUKU
+ {0x1BA6, 0x1BA7, prN}, // Mc [2] SUNDANESE VOWEL SIGN PANAELAENG..SUNDANESE VOWEL SIGN PANOLONG
+ {0x1BA8, 0x1BA9, prN}, // Mn [2] SUNDANESE VOWEL SIGN PAMEPET..SUNDANESE VOWEL SIGN PANEULEUNG
+ {0x1BAA, 0x1BAA, prN}, // Mc SUNDANESE SIGN PAMAAEH
+ {0x1BAB, 0x1BAD, prN}, // Mn [3] SUNDANESE SIGN VIRAMA..SUNDANESE CONSONANT SIGN PASANGAN WA
+ {0x1BAE, 0x1BAF, prN}, // Lo [2] SUNDANESE LETTER KHA..SUNDANESE LETTER SYA
+ {0x1BB0, 0x1BB9, prN}, // Nd [10] SUNDANESE DIGIT ZERO..SUNDANESE DIGIT NINE
+ {0x1BBA, 0x1BBF, prN}, // Lo [6] SUNDANESE AVAGRAHA..SUNDANESE LETTER FINAL M
+ {0x1BC0, 0x1BE5, prN}, // Lo [38] BATAK LETTER A..BATAK LETTER U
+ {0x1BE6, 0x1BE6, prN}, // Mn BATAK SIGN TOMPI
+ {0x1BE7, 0x1BE7, prN}, // Mc BATAK VOWEL SIGN E
+ {0x1BE8, 0x1BE9, prN}, // Mn [2] BATAK VOWEL SIGN PAKPAK E..BATAK VOWEL SIGN EE
+ {0x1BEA, 0x1BEC, prN}, // Mc [3] BATAK VOWEL SIGN I..BATAK VOWEL SIGN O
+ {0x1BED, 0x1BED, prN}, // Mn BATAK VOWEL SIGN KARO O
+ {0x1BEE, 0x1BEE, prN}, // Mc BATAK VOWEL SIGN U
+ {0x1BEF, 0x1BF1, prN}, // Mn [3] BATAK VOWEL SIGN U FOR SIMALUNGUN SA..BATAK CONSONANT SIGN H
+ {0x1BF2, 0x1BF3, prN}, // Mc [2] BATAK PANGOLAT..BATAK PANONGONAN
+ {0x1BFC, 0x1BFF, prN}, // Po [4] BATAK SYMBOL BINDU NA METEK..BATAK SYMBOL BINDU PANGOLAT
+ {0x1C00, 0x1C23, prN}, // Lo [36] LEPCHA LETTER KA..LEPCHA LETTER A
+ {0x1C24, 0x1C2B, prN}, // Mc [8] LEPCHA SUBJOINED LETTER YA..LEPCHA VOWEL SIGN UU
+ {0x1C2C, 0x1C33, prN}, // Mn [8] LEPCHA VOWEL SIGN E..LEPCHA CONSONANT SIGN T
+ {0x1C34, 0x1C35, prN}, // Mc [2] LEPCHA CONSONANT SIGN NYIN-DO..LEPCHA CONSONANT SIGN KANG
+ {0x1C36, 0x1C37, prN}, // Mn [2] LEPCHA SIGN RAN..LEPCHA SIGN NUKTA
+ {0x1C3B, 0x1C3F, prN}, // Po [5] LEPCHA PUNCTUATION TA-ROL..LEPCHA PUNCTUATION TSHOOK
+ {0x1C40, 0x1C49, prN}, // Nd [10] LEPCHA DIGIT ZERO..LEPCHA DIGIT NINE
+ {0x1C4D, 0x1C4F, prN}, // Lo [3] LEPCHA LETTER TTA..LEPCHA LETTER DDA
+ {0x1C50, 0x1C59, prN}, // Nd [10] OL CHIKI DIGIT ZERO..OL CHIKI DIGIT NINE
+ {0x1C5A, 0x1C77, prN}, // Lo [30] OL CHIKI LETTER LA..OL CHIKI LETTER OH
+ {0x1C78, 0x1C7D, prN}, // Lm [6] OL CHIKI MU TTUDDAG..OL CHIKI AHAD
+ {0x1C7E, 0x1C7F, prN}, // Po [2] OL CHIKI PUNCTUATION MUCAAD..OL CHIKI PUNCTUATION DOUBLE MUCAAD
+ {0x1C80, 0x1C88, prN}, // Ll [9] CYRILLIC SMALL LETTER ROUNDED VE..CYRILLIC SMALL LETTER UNBLENDED UK
+ {0x1C90, 0x1CBA, prN}, // Lu [43] GEORGIAN MTAVRULI CAPITAL LETTER AN..GEORGIAN MTAVRULI CAPITAL LETTER AIN
+ {0x1CBD, 0x1CBF, prN}, // Lu [3] GEORGIAN MTAVRULI CAPITAL LETTER AEN..GEORGIAN MTAVRULI CAPITAL LETTER LABIAL SIGN
+ {0x1CC0, 0x1CC7, prN}, // Po [8] SUNDANESE PUNCTUATION BINDU SURYA..SUNDANESE PUNCTUATION BINDU BA SATANGA
+ {0x1CD0, 0x1CD2, prN}, // Mn [3] VEDIC TONE KARSHANA..VEDIC TONE PRENKHA
+ {0x1CD3, 0x1CD3, prN}, // Po VEDIC SIGN NIHSHVASA
+ {0x1CD4, 0x1CE0, prN}, // Mn [13] VEDIC SIGN YAJURVEDIC MIDLINE SVARITA..VEDIC TONE RIGVEDIC KASHMIRI INDEPENDENT SVARITA
+ {0x1CE1, 0x1CE1, prN}, // Mc VEDIC TONE ATHARVAVEDIC INDEPENDENT SVARITA
+ {0x1CE2, 0x1CE8, prN}, // Mn [7] VEDIC SIGN VISARGA SVARITA..VEDIC SIGN VISARGA ANUDATTA WITH TAIL
+ {0x1CE9, 0x1CEC, prN}, // Lo [4] VEDIC SIGN ANUSVARA ANTARGOMUKHA..VEDIC SIGN ANUSVARA VAMAGOMUKHA WITH TAIL
+ {0x1CED, 0x1CED, prN}, // Mn VEDIC SIGN TIRYAK
+ {0x1CEE, 0x1CF3, prN}, // Lo [6] VEDIC SIGN HEXIFORM LONG ANUSVARA..VEDIC SIGN ROTATED ARDHAVISARGA
+ {0x1CF4, 0x1CF4, prN}, // Mn VEDIC TONE CANDRA ABOVE
+ {0x1CF5, 0x1CF6, prN}, // Lo [2] VEDIC SIGN JIHVAMULIYA..VEDIC SIGN UPADHMANIYA
+ {0x1CF7, 0x1CF7, prN}, // Mc VEDIC SIGN ATIKRAMA
+ {0x1CF8, 0x1CF9, prN}, // Mn [2] VEDIC TONE RING ABOVE..VEDIC TONE DOUBLE RING ABOVE
+ {0x1CFA, 0x1CFA, prN}, // Lo VEDIC SIGN DOUBLE ANUSVARA ANTARGOMUKHA
+ {0x1D00, 0x1D2B, prN}, // Ll [44] LATIN LETTER SMALL CAPITAL A..CYRILLIC LETTER SMALL CAPITAL EL
+ {0x1D2C, 0x1D6A, prN}, // Lm [63] MODIFIER LETTER CAPITAL A..GREEK SUBSCRIPT SMALL LETTER CHI
+ {0x1D6B, 0x1D77, prN}, // Ll [13] LATIN SMALL LETTER UE..LATIN SMALL LETTER TURNED G
+ {0x1D78, 0x1D78, prN}, // Lm MODIFIER LETTER CYRILLIC EN
+ {0x1D79, 0x1D7F, prN}, // Ll [7] LATIN SMALL LETTER INSULAR G..LATIN SMALL LETTER UPSILON WITH STROKE
+ {0x1D80, 0x1D9A, prN}, // Ll [27] LATIN SMALL LETTER B WITH PALATAL HOOK..LATIN SMALL LETTER EZH WITH RETROFLEX HOOK
+ {0x1D9B, 0x1DBF, prN}, // Lm [37] MODIFIER LETTER SMALL TURNED ALPHA..MODIFIER LETTER SMALL THETA
+ {0x1DC0, 0x1DFF, prN}, // Mn [64] COMBINING DOTTED GRAVE ACCENT..COMBINING RIGHT ARROWHEAD AND DOWN ARROWHEAD BELOW
+ {0x1E00, 0x1EFF, prN}, // L& [256] LATIN CAPITAL LETTER A WITH RING BELOW..LATIN SMALL LETTER Y WITH LOOP
+ {0x1F00, 0x1F15, prN}, // L& [22] GREEK SMALL LETTER ALPHA WITH PSILI..GREEK SMALL LETTER EPSILON WITH DASIA AND OXIA
+ {0x1F18, 0x1F1D, prN}, // Lu [6] GREEK CAPITAL LETTER EPSILON WITH PSILI..GREEK CAPITAL LETTER EPSILON WITH DASIA AND OXIA
+ {0x1F20, 0x1F45, prN}, // L& [38] GREEK SMALL LETTER ETA WITH PSILI..GREEK SMALL LETTER OMICRON WITH DASIA AND OXIA
+ {0x1F48, 0x1F4D, prN}, // Lu [6] GREEK CAPITAL LETTER OMICRON WITH PSILI..GREEK CAPITAL LETTER OMICRON WITH DASIA AND OXIA
+ {0x1F50, 0x1F57, prN}, // Ll [8] GREEK SMALL LETTER UPSILON WITH PSILI..GREEK SMALL LETTER UPSILON WITH DASIA AND PERISPOMENI
+ {0x1F59, 0x1F59, prN}, // Lu GREEK CAPITAL LETTER UPSILON WITH DASIA
+ {0x1F5B, 0x1F5B, prN}, // Lu GREEK CAPITAL LETTER UPSILON WITH DASIA AND VARIA
+ {0x1F5D, 0x1F5D, prN}, // Lu GREEK CAPITAL LETTER UPSILON WITH DASIA AND OXIA
+ {0x1F5F, 0x1F7D, prN}, // L& [31] GREEK CAPITAL LETTER UPSILON WITH DASIA AND PERISPOMENI..GREEK SMALL LETTER OMEGA WITH OXIA
+ {0x1F80, 0x1FB4, prN}, // L& [53] GREEK SMALL LETTER ALPHA WITH PSILI AND YPOGEGRAMMENI..GREEK SMALL LETTER ALPHA WITH OXIA AND YPOGEGRAMMENI
+ {0x1FB6, 0x1FBC, prN}, // L& [7] GREEK SMALL LETTER ALPHA WITH PERISPOMENI..GREEK CAPITAL LETTER ALPHA WITH PROSGEGRAMMENI
+ {0x1FBD, 0x1FBD, prN}, // Sk GREEK KORONIS
+ {0x1FBE, 0x1FBE, prN}, // Ll GREEK PROSGEGRAMMENI
+ {0x1FBF, 0x1FC1, prN}, // Sk [3] GREEK PSILI..GREEK DIALYTIKA AND PERISPOMENI
+ {0x1FC2, 0x1FC4, prN}, // Ll [3] GREEK SMALL LETTER ETA WITH VARIA AND YPOGEGRAMMENI..GREEK SMALL LETTER ETA WITH OXIA AND YPOGEGRAMMENI
+ {0x1FC6, 0x1FCC, prN}, // L& [7] GREEK SMALL LETTER ETA WITH PERISPOMENI..GREEK CAPITAL LETTER ETA WITH PROSGEGRAMMENI
+ {0x1FCD, 0x1FCF, prN}, // Sk [3] GREEK PSILI AND VARIA..GREEK PSILI AND PERISPOMENI
+ {0x1FD0, 0x1FD3, prN}, // Ll [4] GREEK SMALL LETTER IOTA WITH VRACHY..GREEK SMALL LETTER IOTA WITH DIALYTIKA AND OXIA
+ {0x1FD6, 0x1FDB, prN}, // L& [6] GREEK SMALL LETTER IOTA WITH PERISPOMENI..GREEK CAPITAL LETTER IOTA WITH OXIA
+ {0x1FDD, 0x1FDF, prN}, // Sk [3] GREEK DASIA AND VARIA..GREEK DASIA AND PERISPOMENI
+ {0x1FE0, 0x1FEC, prN}, // L& [13] GREEK SMALL LETTER UPSILON WITH VRACHY..GREEK CAPITAL LETTER RHO WITH DASIA
+ {0x1FED, 0x1FEF, prN}, // Sk [3] GREEK DIALYTIKA AND VARIA..GREEK VARIA
+ {0x1FF2, 0x1FF4, prN}, // Ll [3] GREEK SMALL LETTER OMEGA WITH VARIA AND YPOGEGRAMMENI..GREEK SMALL LETTER OMEGA WITH OXIA AND YPOGEGRAMMENI
+ {0x1FF6, 0x1FFC, prN}, // L& [7] GREEK SMALL LETTER OMEGA WITH PERISPOMENI..GREEK CAPITAL LETTER OMEGA WITH PROSGEGRAMMENI
+ {0x1FFD, 0x1FFE, prN}, // Sk [2] GREEK OXIA..GREEK DASIA
+ {0x2000, 0x200A, prN}, // Zs [11] EN QUAD..HAIR SPACE
+ {0x200B, 0x200F, prN}, // Cf [5] ZERO WIDTH SPACE..RIGHT-TO-LEFT MARK
+ {0x2010, 0x2010, prA}, // Pd HYPHEN
+ {0x2011, 0x2012, prN}, // Pd [2] NON-BREAKING HYPHEN..FIGURE DASH
+ {0x2013, 0x2015, prA}, // Pd [3] EN DASH..HORIZONTAL BAR
+ {0x2016, 0x2016, prA}, // Po DOUBLE VERTICAL LINE
+ {0x2017, 0x2017, prN}, // Po DOUBLE LOW LINE
+ {0x2018, 0x2018, prA}, // Pi LEFT SINGLE QUOTATION MARK
+ {0x2019, 0x2019, prA}, // Pf RIGHT SINGLE QUOTATION MARK
+ {0x201A, 0x201A, prN}, // Ps SINGLE LOW-9 QUOTATION MARK
+ {0x201B, 0x201B, prN}, // Pi SINGLE HIGH-REVERSED-9 QUOTATION MARK
+ {0x201C, 0x201C, prA}, // Pi LEFT DOUBLE QUOTATION MARK
+ {0x201D, 0x201D, prA}, // Pf RIGHT DOUBLE QUOTATION MARK
+ {0x201E, 0x201E, prN}, // Ps DOUBLE LOW-9 QUOTATION MARK
+ {0x201F, 0x201F, prN}, // Pi DOUBLE HIGH-REVERSED-9 QUOTATION MARK
+ {0x2020, 0x2022, prA}, // Po [3] DAGGER..BULLET
+ {0x2023, 0x2023, prN}, // Po TRIANGULAR BULLET
+ {0x2024, 0x2027, prA}, // Po [4] ONE DOT LEADER..HYPHENATION POINT
+ {0x2028, 0x2028, prN}, // Zl LINE SEPARATOR
+ {0x2029, 0x2029, prN}, // Zp PARAGRAPH SEPARATOR
+ {0x202A, 0x202E, prN}, // Cf [5] LEFT-TO-RIGHT EMBEDDING..RIGHT-TO-LEFT OVERRIDE
+ {0x202F, 0x202F, prN}, // Zs NARROW NO-BREAK SPACE
+ {0x2030, 0x2030, prA}, // Po PER MILLE SIGN
+ {0x2031, 0x2031, prN}, // Po PER TEN THOUSAND SIGN
+ {0x2032, 0x2033, prA}, // Po [2] PRIME..DOUBLE PRIME
+ {0x2034, 0x2034, prN}, // Po TRIPLE PRIME
+ {0x2035, 0x2035, prA}, // Po REVERSED PRIME
+ {0x2036, 0x2038, prN}, // Po [3] REVERSED DOUBLE PRIME..CARET
+ {0x2039, 0x2039, prN}, // Pi SINGLE LEFT-POINTING ANGLE QUOTATION MARK
+ {0x203A, 0x203A, prN}, // Pf SINGLE RIGHT-POINTING ANGLE QUOTATION MARK
+ {0x203B, 0x203B, prA}, // Po REFERENCE MARK
+ {0x203C, 0x203D, prN}, // Po [2] DOUBLE EXCLAMATION MARK..INTERROBANG
+ {0x203E, 0x203E, prA}, // Po OVERLINE
+ {0x203F, 0x2040, prN}, // Pc [2] UNDERTIE..CHARACTER TIE
+ {0x2041, 0x2043, prN}, // Po [3] CARET INSERTION POINT..HYPHEN BULLET
+ {0x2044, 0x2044, prN}, // Sm FRACTION SLASH
+ {0x2045, 0x2045, prN}, // Ps LEFT SQUARE BRACKET WITH QUILL
+ {0x2046, 0x2046, prN}, // Pe RIGHT SQUARE BRACKET WITH QUILL
+ {0x2047, 0x2051, prN}, // Po [11] DOUBLE QUESTION MARK..TWO ASTERISKS ALIGNED VERTICALLY
+ {0x2052, 0x2052, prN}, // Sm COMMERCIAL MINUS SIGN
+ {0x2053, 0x2053, prN}, // Po SWUNG DASH
+ {0x2054, 0x2054, prN}, // Pc INVERTED UNDERTIE
+ {0x2055, 0x205E, prN}, // Po [10] FLOWER PUNCTUATION MARK..VERTICAL FOUR DOTS
+ {0x205F, 0x205F, prN}, // Zs MEDIUM MATHEMATICAL SPACE
+ {0x2060, 0x2064, prN}, // Cf [5] WORD JOINER..INVISIBLE PLUS
+ {0x2066, 0x206F, prN}, // Cf [10] LEFT-TO-RIGHT ISOLATE..NOMINAL DIGIT SHAPES
+ {0x2070, 0x2070, prN}, // No SUPERSCRIPT ZERO
+ {0x2071, 0x2071, prN}, // Lm SUPERSCRIPT LATIN SMALL LETTER I
+ {0x2074, 0x2074, prA}, // No SUPERSCRIPT FOUR
+ {0x2075, 0x2079, prN}, // No [5] SUPERSCRIPT FIVE..SUPERSCRIPT NINE
+ {0x207A, 0x207C, prN}, // Sm [3] SUPERSCRIPT PLUS SIGN..SUPERSCRIPT EQUALS SIGN
+ {0x207D, 0x207D, prN}, // Ps SUPERSCRIPT LEFT PARENTHESIS
+ {0x207E, 0x207E, prN}, // Pe SUPERSCRIPT RIGHT PARENTHESIS
+ {0x207F, 0x207F, prA}, // Lm SUPERSCRIPT LATIN SMALL LETTER N
+ {0x2080, 0x2080, prN}, // No SUBSCRIPT ZERO
+ {0x2081, 0x2084, prA}, // No [4] SUBSCRIPT ONE..SUBSCRIPT FOUR
+ {0x2085, 0x2089, prN}, // No [5] SUBSCRIPT FIVE..SUBSCRIPT NINE
+ {0x208A, 0x208C, prN}, // Sm [3] SUBSCRIPT PLUS SIGN..SUBSCRIPT EQUALS SIGN
+ {0x208D, 0x208D, prN}, // Ps SUBSCRIPT LEFT PARENTHESIS
+ {0x208E, 0x208E, prN}, // Pe SUBSCRIPT RIGHT PARENTHESIS
+ {0x2090, 0x209C, prN}, // Lm [13] LATIN SUBSCRIPT SMALL LETTER A..LATIN SUBSCRIPT SMALL LETTER T
+ {0x20A0, 0x20A8, prN}, // Sc [9] EURO-CURRENCY SIGN..RUPEE SIGN
+ {0x20A9, 0x20A9, prH}, // Sc WON SIGN
+ {0x20AA, 0x20AB, prN}, // Sc [2] NEW SHEQEL SIGN..DONG SIGN
+ {0x20AC, 0x20AC, prA}, // Sc EURO SIGN
+ {0x20AD, 0x20C0, prN}, // Sc [20] KIP SIGN..SOM SIGN
+ {0x20D0, 0x20DC, prN}, // Mn [13] COMBINING LEFT HARPOON ABOVE..COMBINING FOUR DOTS ABOVE
+ {0x20DD, 0x20E0, prN}, // Me [4] COMBINING ENCLOSING CIRCLE..COMBINING ENCLOSING CIRCLE BACKSLASH
+ {0x20E1, 0x20E1, prN}, // Mn COMBINING LEFT RIGHT ARROW ABOVE
+ {0x20E2, 0x20E4, prN}, // Me [3] COMBINING ENCLOSING SCREEN..COMBINING ENCLOSING UPWARD POINTING TRIANGLE
+ {0x20E5, 0x20F0, prN}, // Mn [12] COMBINING REVERSE SOLIDUS OVERLAY..COMBINING ASTERISK ABOVE
+ {0x2100, 0x2101, prN}, // So [2] ACCOUNT OF..ADDRESSED TO THE SUBJECT
+ {0x2102, 0x2102, prN}, // Lu DOUBLE-STRUCK CAPITAL C
+ {0x2103, 0x2103, prA}, // So DEGREE CELSIUS
+ {0x2104, 0x2104, prN}, // So CENTRE LINE SYMBOL
+ {0x2105, 0x2105, prA}, // So CARE OF
+ {0x2106, 0x2106, prN}, // So CADA UNA
+ {0x2107, 0x2107, prN}, // Lu EULER CONSTANT
+ {0x2108, 0x2108, prN}, // So SCRUPLE
+ {0x2109, 0x2109, prA}, // So DEGREE FAHRENHEIT
+ {0x210A, 0x2112, prN}, // L& [9] SCRIPT SMALL G..SCRIPT CAPITAL L
+ {0x2113, 0x2113, prA}, // Ll SCRIPT SMALL L
+ {0x2114, 0x2114, prN}, // So L B BAR SYMBOL
+ {0x2115, 0x2115, prN}, // Lu DOUBLE-STRUCK CAPITAL N
+ {0x2116, 0x2116, prA}, // So NUMERO SIGN
+ {0x2117, 0x2117, prN}, // So SOUND RECORDING COPYRIGHT
+ {0x2118, 0x2118, prN}, // Sm SCRIPT CAPITAL P
+ {0x2119, 0x211D, prN}, // Lu [5] DOUBLE-STRUCK CAPITAL P..DOUBLE-STRUCK CAPITAL R
+ {0x211E, 0x2120, prN}, // So [3] PRESCRIPTION TAKE..SERVICE MARK
+ {0x2121, 0x2122, prA}, // So [2] TELEPHONE SIGN..TRADE MARK SIGN
+ {0x2123, 0x2123, prN}, // So VERSICLE
+ {0x2124, 0x2124, prN}, // Lu DOUBLE-STRUCK CAPITAL Z
+ {0x2125, 0x2125, prN}, // So OUNCE SIGN
+ {0x2126, 0x2126, prA}, // Lu OHM SIGN
+ {0x2127, 0x2127, prN}, // So INVERTED OHM SIGN
+ {0x2128, 0x2128, prN}, // Lu BLACK-LETTER CAPITAL Z
+ {0x2129, 0x2129, prN}, // So TURNED GREEK SMALL LETTER IOTA
+ {0x212A, 0x212A, prN}, // Lu KELVIN SIGN
+ {0x212B, 0x212B, prA}, // Lu ANGSTROM SIGN
+ {0x212C, 0x212D, prN}, // Lu [2] SCRIPT CAPITAL B..BLACK-LETTER CAPITAL C
+ {0x212E, 0x212E, prN}, // So ESTIMATED SYMBOL
+ {0x212F, 0x2134, prN}, // L& [6] SCRIPT SMALL E..SCRIPT SMALL O
+ {0x2135, 0x2138, prN}, // Lo [4] ALEF SYMBOL..DALET SYMBOL
+ {0x2139, 0x2139, prN}, // Ll INFORMATION SOURCE
+ {0x213A, 0x213B, prN}, // So [2] ROTATED CAPITAL Q..FACSIMILE SIGN
+ {0x213C, 0x213F, prN}, // L& [4] DOUBLE-STRUCK SMALL PI..DOUBLE-STRUCK CAPITAL PI
+ {0x2140, 0x2144, prN}, // Sm [5] DOUBLE-STRUCK N-ARY SUMMATION..TURNED SANS-SERIF CAPITAL Y
+ {0x2145, 0x2149, prN}, // L& [5] DOUBLE-STRUCK ITALIC CAPITAL D..DOUBLE-STRUCK ITALIC SMALL J
+ {0x214A, 0x214A, prN}, // So PROPERTY LINE
+ {0x214B, 0x214B, prN}, // Sm TURNED AMPERSAND
+ {0x214C, 0x214D, prN}, // So [2] PER SIGN..AKTIESELSKAB
+ {0x214E, 0x214E, prN}, // Ll TURNED SMALL F
+ {0x214F, 0x214F, prN}, // So SYMBOL FOR SAMARITAN SOURCE
+ {0x2150, 0x2152, prN}, // No [3] VULGAR FRACTION ONE SEVENTH..VULGAR FRACTION ONE TENTH
+ {0x2153, 0x2154, prA}, // No [2] VULGAR FRACTION ONE THIRD..VULGAR FRACTION TWO THIRDS
+ {0x2155, 0x215A, prN}, // No [6] VULGAR FRACTION ONE FIFTH..VULGAR FRACTION FIVE SIXTHS
+ {0x215B, 0x215E, prA}, // No [4] VULGAR FRACTION ONE EIGHTH..VULGAR FRACTION SEVEN EIGHTHS
+ {0x215F, 0x215F, prN}, // No FRACTION NUMERATOR ONE
+ {0x2160, 0x216B, prA}, // Nl [12] ROMAN NUMERAL ONE..ROMAN NUMERAL TWELVE
+ {0x216C, 0x216F, prN}, // Nl [4] ROMAN NUMERAL FIFTY..ROMAN NUMERAL ONE THOUSAND
+ {0x2170, 0x2179, prA}, // Nl [10] SMALL ROMAN NUMERAL ONE..SMALL ROMAN NUMERAL TEN
+ {0x217A, 0x2182, prN}, // Nl [9] SMALL ROMAN NUMERAL ELEVEN..ROMAN NUMERAL TEN THOUSAND
+ {0x2183, 0x2184, prN}, // L& [2] ROMAN NUMERAL REVERSED ONE HUNDRED..LATIN SMALL LETTER REVERSED C
+ {0x2185, 0x2188, prN}, // Nl [4] ROMAN NUMERAL SIX LATE FORM..ROMAN NUMERAL ONE HUNDRED THOUSAND
+ {0x2189, 0x2189, prA}, // No VULGAR FRACTION ZERO THIRDS
+ {0x218A, 0x218B, prN}, // So [2] TURNED DIGIT TWO..TURNED DIGIT THREE
+ {0x2190, 0x2194, prA}, // Sm [5] LEFTWARDS ARROW..LEFT RIGHT ARROW
+ {0x2195, 0x2199, prA}, // So [5] UP DOWN ARROW..SOUTH WEST ARROW
+ {0x219A, 0x219B, prN}, // Sm [2] LEFTWARDS ARROW WITH STROKE..RIGHTWARDS ARROW WITH STROKE
+ {0x219C, 0x219F, prN}, // So [4] LEFTWARDS WAVE ARROW..UPWARDS TWO HEADED ARROW
+ {0x21A0, 0x21A0, prN}, // Sm RIGHTWARDS TWO HEADED ARROW
+ {0x21A1, 0x21A2, prN}, // So [2] DOWNWARDS TWO HEADED ARROW..LEFTWARDS ARROW WITH TAIL
+ {0x21A3, 0x21A3, prN}, // Sm RIGHTWARDS ARROW WITH TAIL
+ {0x21A4, 0x21A5, prN}, // So [2] LEFTWARDS ARROW FROM BAR..UPWARDS ARROW FROM BAR
+ {0x21A6, 0x21A6, prN}, // Sm RIGHTWARDS ARROW FROM BAR
+ {0x21A7, 0x21AD, prN}, // So [7] DOWNWARDS ARROW FROM BAR..LEFT RIGHT WAVE ARROW
+ {0x21AE, 0x21AE, prN}, // Sm LEFT RIGHT ARROW WITH STROKE
+ {0x21AF, 0x21B7, prN}, // So [9] DOWNWARDS ZIGZAG ARROW..CLOCKWISE TOP SEMICIRCLE ARROW
+ {0x21B8, 0x21B9, prA}, // So [2] NORTH WEST ARROW TO LONG BAR..LEFTWARDS ARROW TO BAR OVER RIGHTWARDS ARROW TO BAR
+ {0x21BA, 0x21CD, prN}, // So [20] ANTICLOCKWISE OPEN CIRCLE ARROW..LEFTWARDS DOUBLE ARROW WITH STROKE
+ {0x21CE, 0x21CF, prN}, // Sm [2] LEFT RIGHT DOUBLE ARROW WITH STROKE..RIGHTWARDS DOUBLE ARROW WITH STROKE
+ {0x21D0, 0x21D1, prN}, // So [2] LEFTWARDS DOUBLE ARROW..UPWARDS DOUBLE ARROW
+ {0x21D2, 0x21D2, prA}, // Sm RIGHTWARDS DOUBLE ARROW
+ {0x21D3, 0x21D3, prN}, // So DOWNWARDS DOUBLE ARROW
+ {0x21D4, 0x21D4, prA}, // Sm LEFT RIGHT DOUBLE ARROW
+ {0x21D5, 0x21E6, prN}, // So [18] UP DOWN DOUBLE ARROW..LEFTWARDS WHITE ARROW
+ {0x21E7, 0x21E7, prA}, // So UPWARDS WHITE ARROW
+ {0x21E8, 0x21F3, prN}, // So [12] RIGHTWARDS WHITE ARROW..UP DOWN WHITE ARROW
+ {0x21F4, 0x21FF, prN}, // Sm [12] RIGHT ARROW WITH SMALL CIRCLE..LEFT RIGHT OPEN-HEADED ARROW
+ {0x2200, 0x2200, prA}, // Sm FOR ALL
+ {0x2201, 0x2201, prN}, // Sm COMPLEMENT
+ {0x2202, 0x2203, prA}, // Sm [2] PARTIAL DIFFERENTIAL..THERE EXISTS
+ {0x2204, 0x2206, prN}, // Sm [3] THERE DOES NOT EXIST..INCREMENT
+ {0x2207, 0x2208, prA}, // Sm [2] NABLA..ELEMENT OF
+ {0x2209, 0x220A, prN}, // Sm [2] NOT AN ELEMENT OF..SMALL ELEMENT OF
+ {0x220B, 0x220B, prA}, // Sm CONTAINS AS MEMBER
+ {0x220C, 0x220E, prN}, // Sm [3] DOES NOT CONTAIN AS MEMBER..END OF PROOF
+ {0x220F, 0x220F, prA}, // Sm N-ARY PRODUCT
+ {0x2210, 0x2210, prN}, // Sm N-ARY COPRODUCT
+ {0x2211, 0x2211, prA}, // Sm N-ARY SUMMATION
+ {0x2212, 0x2214, prN}, // Sm [3] MINUS SIGN..DOT PLUS
+ {0x2215, 0x2215, prA}, // Sm DIVISION SLASH
+ {0x2216, 0x2219, prN}, // Sm [4] SET MINUS..BULLET OPERATOR
+ {0x221A, 0x221A, prA}, // Sm SQUARE ROOT
+ {0x221B, 0x221C, prN}, // Sm [2] CUBE ROOT..FOURTH ROOT
+ {0x221D, 0x2220, prA}, // Sm [4] PROPORTIONAL TO..ANGLE
+ {0x2221, 0x2222, prN}, // Sm [2] MEASURED ANGLE..SPHERICAL ANGLE
+ {0x2223, 0x2223, prA}, // Sm DIVIDES
+ {0x2224, 0x2224, prN}, // Sm DOES NOT DIVIDE
+ {0x2225, 0x2225, prA}, // Sm PARALLEL TO
+ {0x2226, 0x2226, prN}, // Sm NOT PARALLEL TO
+ {0x2227, 0x222C, prA}, // Sm [6] LOGICAL AND..DOUBLE INTEGRAL
+ {0x222D, 0x222D, prN}, // Sm TRIPLE INTEGRAL
+ {0x222E, 0x222E, prA}, // Sm CONTOUR INTEGRAL
+ {0x222F, 0x2233, prN}, // Sm [5] SURFACE INTEGRAL..ANTICLOCKWISE CONTOUR INTEGRAL
+ {0x2234, 0x2237, prA}, // Sm [4] THEREFORE..PROPORTION
+ {0x2238, 0x223B, prN}, // Sm [4] DOT MINUS..HOMOTHETIC
+ {0x223C, 0x223D, prA}, // Sm [2] TILDE OPERATOR..REVERSED TILDE
+ {0x223E, 0x2247, prN}, // Sm [10] INVERTED LAZY S..NEITHER APPROXIMATELY NOR ACTUALLY EQUAL TO
+ {0x2248, 0x2248, prA}, // Sm ALMOST EQUAL TO
+ {0x2249, 0x224B, prN}, // Sm [3] NOT ALMOST EQUAL TO..TRIPLE TILDE
+ {0x224C, 0x224C, prA}, // Sm ALL EQUAL TO
+ {0x224D, 0x2251, prN}, // Sm [5] EQUIVALENT TO..GEOMETRICALLY EQUAL TO
+ {0x2252, 0x2252, prA}, // Sm APPROXIMATELY EQUAL TO OR THE IMAGE OF
+ {0x2253, 0x225F, prN}, // Sm [13] IMAGE OF OR APPROXIMATELY EQUAL TO..QUESTIONED EQUAL TO
+ {0x2260, 0x2261, prA}, // Sm [2] NOT EQUAL TO..IDENTICAL TO
+ {0x2262, 0x2263, prN}, // Sm [2] NOT IDENTICAL TO..STRICTLY EQUIVALENT TO
+ {0x2264, 0x2267, prA}, // Sm [4] LESS-THAN OR EQUAL TO..GREATER-THAN OVER EQUAL TO
+ {0x2268, 0x2269, prN}, // Sm [2] LESS-THAN BUT NOT EQUAL TO..GREATER-THAN BUT NOT EQUAL TO
+ {0x226A, 0x226B, prA}, // Sm [2] MUCH LESS-THAN..MUCH GREATER-THAN
+ {0x226C, 0x226D, prN}, // Sm [2] BETWEEN..NOT EQUIVALENT TO
+ {0x226E, 0x226F, prA}, // Sm [2] NOT LESS-THAN..NOT GREATER-THAN
+ {0x2270, 0x2281, prN}, // Sm [18] NEITHER LESS-THAN NOR EQUAL TO..DOES NOT SUCCEED
+ {0x2282, 0x2283, prA}, // Sm [2] SUBSET OF..SUPERSET OF
+ {0x2284, 0x2285, prN}, // Sm [2] NOT A SUBSET OF..NOT A SUPERSET OF
+ {0x2286, 0x2287, prA}, // Sm [2] SUBSET OF OR EQUAL TO..SUPERSET OF OR EQUAL TO
+ {0x2288, 0x2294, prN}, // Sm [13] NEITHER A SUBSET OF NOR EQUAL TO..SQUARE CUP
+ {0x2295, 0x2295, prA}, // Sm CIRCLED PLUS
+ {0x2296, 0x2298, prN}, // Sm [3] CIRCLED MINUS..CIRCLED DIVISION SLASH
+ {0x2299, 0x2299, prA}, // Sm CIRCLED DOT OPERATOR
+ {0x229A, 0x22A4, prN}, // Sm [11] CIRCLED RING OPERATOR..DOWN TACK
+ {0x22A5, 0x22A5, prA}, // Sm UP TACK
+ {0x22A6, 0x22BE, prN}, // Sm [25] ASSERTION..RIGHT ANGLE WITH ARC
+ {0x22BF, 0x22BF, prA}, // Sm RIGHT TRIANGLE
+ {0x22C0, 0x22FF, prN}, // Sm [64] N-ARY LOGICAL AND..Z NOTATION BAG MEMBERSHIP
+ {0x2300, 0x2307, prN}, // So [8] DIAMETER SIGN..WAVY LINE
+ {0x2308, 0x2308, prN}, // Ps LEFT CEILING
+ {0x2309, 0x2309, prN}, // Pe RIGHT CEILING
+ {0x230A, 0x230A, prN}, // Ps LEFT FLOOR
+ {0x230B, 0x230B, prN}, // Pe RIGHT FLOOR
+ {0x230C, 0x2311, prN}, // So [6] BOTTOM RIGHT CROP..SQUARE LOZENGE
+ {0x2312, 0x2312, prA}, // So ARC
+ {0x2313, 0x2319, prN}, // So [7] SEGMENT..TURNED NOT SIGN
+ {0x231A, 0x231B, prW}, // So [2] WATCH..HOURGLASS
+ {0x231C, 0x231F, prN}, // So [4] TOP LEFT CORNER..BOTTOM RIGHT CORNER
+ {0x2320, 0x2321, prN}, // Sm [2] TOP HALF INTEGRAL..BOTTOM HALF INTEGRAL
+ {0x2322, 0x2328, prN}, // So [7] FROWN..KEYBOARD
+ {0x2329, 0x2329, prW}, // Ps LEFT-POINTING ANGLE BRACKET
+ {0x232A, 0x232A, prW}, // Pe RIGHT-POINTING ANGLE BRACKET
+ {0x232B, 0x237B, prN}, // So [81] ERASE TO THE LEFT..NOT CHECK MARK
+ {0x237C, 0x237C, prN}, // Sm RIGHT ANGLE WITH DOWNWARDS ZIGZAG ARROW
+ {0x237D, 0x239A, prN}, // So [30] SHOULDERED OPEN BOX..CLEAR SCREEN SYMBOL
+ {0x239B, 0x23B3, prN}, // Sm [25] LEFT PARENTHESIS UPPER HOOK..SUMMATION BOTTOM
+ {0x23B4, 0x23DB, prN}, // So [40] TOP SQUARE BRACKET..FUSE
+ {0x23DC, 0x23E1, prN}, // Sm [6] TOP PARENTHESIS..BOTTOM TORTOISE SHELL BRACKET
+ {0x23E2, 0x23E8, prN}, // So [7] WHITE TRAPEZIUM..DECIMAL EXPONENT SYMBOL
+ {0x23E9, 0x23EC, prW}, // So [4] BLACK RIGHT-POINTING DOUBLE TRIANGLE..BLACK DOWN-POINTING DOUBLE TRIANGLE
+ {0x23ED, 0x23EF, prN}, // So [3] BLACK RIGHT-POINTING DOUBLE TRIANGLE WITH VERTICAL BAR..BLACK RIGHT-POINTING TRIANGLE WITH DOUBLE VERTICAL BAR
+ {0x23F0, 0x23F0, prW}, // So ALARM CLOCK
+ {0x23F1, 0x23F2, prN}, // So [2] STOPWATCH..TIMER CLOCK
+ {0x23F3, 0x23F3, prW}, // So HOURGLASS WITH FLOWING SAND
+ {0x23F4, 0x23FF, prN}, // So [12] BLACK MEDIUM LEFT-POINTING TRIANGLE..OBSERVER EYE SYMBOL
+ {0x2400, 0x2426, prN}, // So [39] SYMBOL FOR NULL..SYMBOL FOR SUBSTITUTE FORM TWO
+ {0x2440, 0x244A, prN}, // So [11] OCR HOOK..OCR DOUBLE BACKSLASH
+ {0x2460, 0x249B, prA}, // No [60] CIRCLED DIGIT ONE..NUMBER TWENTY FULL STOP
+ {0x249C, 0x24E9, prA}, // So [78] PARENTHESIZED LATIN SMALL LETTER A..CIRCLED LATIN SMALL LETTER Z
+ {0x24EA, 0x24EA, prN}, // No CIRCLED DIGIT ZERO
+ {0x24EB, 0x24FF, prA}, // No [21] NEGATIVE CIRCLED NUMBER ELEVEN..NEGATIVE CIRCLED DIGIT ZERO
+ {0x2500, 0x254B, prA}, // So [76] BOX DRAWINGS LIGHT HORIZONTAL..BOX DRAWINGS HEAVY VERTICAL AND HORIZONTAL
+ {0x254C, 0x254F, prN}, // So [4] BOX DRAWINGS LIGHT DOUBLE DASH HORIZONTAL..BOX DRAWINGS HEAVY DOUBLE DASH VERTICAL
+ {0x2550, 0x2573, prA}, // So [36] BOX DRAWINGS DOUBLE HORIZONTAL..BOX DRAWINGS LIGHT DIAGONAL CROSS
+ {0x2574, 0x257F, prN}, // So [12] BOX DRAWINGS LIGHT LEFT..BOX DRAWINGS HEAVY UP AND LIGHT DOWN
+ {0x2580, 0x258F, prA}, // So [16] UPPER HALF BLOCK..LEFT ONE EIGHTH BLOCK
+ {0x2590, 0x2591, prN}, // So [2] RIGHT HALF BLOCK..LIGHT SHADE
+ {0x2592, 0x2595, prA}, // So [4] MEDIUM SHADE..RIGHT ONE EIGHTH BLOCK
+ {0x2596, 0x259F, prN}, // So [10] QUADRANT LOWER LEFT..QUADRANT UPPER RIGHT AND LOWER LEFT AND LOWER RIGHT
+ {0x25A0, 0x25A1, prA}, // So [2] BLACK SQUARE..WHITE SQUARE
+ {0x25A2, 0x25A2, prN}, // So WHITE SQUARE WITH ROUNDED CORNERS
+ {0x25A3, 0x25A9, prA}, // So [7] WHITE SQUARE CONTAINING BLACK SMALL SQUARE..SQUARE WITH DIAGONAL CROSSHATCH FILL
+ {0x25AA, 0x25B1, prN}, // So [8] BLACK SMALL SQUARE..WHITE PARALLELOGRAM
+ {0x25B2, 0x25B3, prA}, // So [2] BLACK UP-POINTING TRIANGLE..WHITE UP-POINTING TRIANGLE
+ {0x25B4, 0x25B5, prN}, // So [2] BLACK UP-POINTING SMALL TRIANGLE..WHITE UP-POINTING SMALL TRIANGLE
+ {0x25B6, 0x25B6, prA}, // So BLACK RIGHT-POINTING TRIANGLE
+ {0x25B7, 0x25B7, prA}, // Sm WHITE RIGHT-POINTING TRIANGLE
+ {0x25B8, 0x25BB, prN}, // So [4] BLACK RIGHT-POINTING SMALL TRIANGLE..WHITE RIGHT-POINTING POINTER
+ {0x25BC, 0x25BD, prA}, // So [2] BLACK DOWN-POINTING TRIANGLE..WHITE DOWN-POINTING TRIANGLE
+ {0x25BE, 0x25BF, prN}, // So [2] BLACK DOWN-POINTING SMALL TRIANGLE..WHITE DOWN-POINTING SMALL TRIANGLE
+ {0x25C0, 0x25C0, prA}, // So BLACK LEFT-POINTING TRIANGLE
+ {0x25C1, 0x25C1, prA}, // Sm WHITE LEFT-POINTING TRIANGLE
+ {0x25C2, 0x25C5, prN}, // So [4] BLACK LEFT-POINTING SMALL TRIANGLE..WHITE LEFT-POINTING POINTER
+ {0x25C6, 0x25C8, prA}, // So [3] BLACK DIAMOND..WHITE DIAMOND CONTAINING BLACK SMALL DIAMOND
+ {0x25C9, 0x25CA, prN}, // So [2] FISHEYE..LOZENGE
+ {0x25CB, 0x25CB, prA}, // So WHITE CIRCLE
+ {0x25CC, 0x25CD, prN}, // So [2] DOTTED CIRCLE..CIRCLE WITH VERTICAL FILL
+ {0x25CE, 0x25D1, prA}, // So [4] BULLSEYE..CIRCLE WITH RIGHT HALF BLACK
+ {0x25D2, 0x25E1, prN}, // So [16] CIRCLE WITH LOWER HALF BLACK..LOWER HALF CIRCLE
+ {0x25E2, 0x25E5, prA}, // So [4] BLACK LOWER RIGHT TRIANGLE..BLACK UPPER RIGHT TRIANGLE
+ {0x25E6, 0x25EE, prN}, // So [9] WHITE BULLET..UP-POINTING TRIANGLE WITH RIGHT HALF BLACK
+ {0x25EF, 0x25EF, prA}, // So LARGE CIRCLE
+ {0x25F0, 0x25F7, prN}, // So [8] WHITE SQUARE WITH UPPER LEFT QUADRANT..WHITE CIRCLE WITH UPPER RIGHT QUADRANT
+ {0x25F8, 0x25FC, prN}, // Sm [5] UPPER LEFT TRIANGLE..BLACK MEDIUM SQUARE
+ {0x25FD, 0x25FE, prW}, // Sm [2] WHITE MEDIUM SMALL SQUARE..BLACK MEDIUM SMALL SQUARE
+ {0x25FF, 0x25FF, prN}, // Sm LOWER RIGHT TRIANGLE
+ {0x2600, 0x2604, prN}, // So [5] BLACK SUN WITH RAYS..COMET
+ {0x2605, 0x2606, prA}, // So [2] BLACK STAR..WHITE STAR
+ {0x2607, 0x2608, prN}, // So [2] LIGHTNING..THUNDERSTORM
+ {0x2609, 0x2609, prA}, // So SUN
+ {0x260A, 0x260D, prN}, // So [4] ASCENDING NODE..OPPOSITION
+ {0x260E, 0x260F, prA}, // So [2] BLACK TELEPHONE..WHITE TELEPHONE
+ {0x2610, 0x2613, prN}, // So [4] BALLOT BOX..SALTIRE
+ {0x2614, 0x2615, prW}, // So [2] UMBRELLA WITH RAIN DROPS..HOT BEVERAGE
+ {0x2616, 0x261B, prN}, // So [6] WHITE SHOGI PIECE..BLACK RIGHT POINTING INDEX
+ {0x261C, 0x261C, prA}, // So WHITE LEFT POINTING INDEX
+ {0x261D, 0x261D, prN}, // So WHITE UP POINTING INDEX
+ {0x261E, 0x261E, prA}, // So WHITE RIGHT POINTING INDEX
+ {0x261F, 0x263F, prN}, // So [33] WHITE DOWN POINTING INDEX..MERCURY
+ {0x2640, 0x2640, prA}, // So FEMALE SIGN
+ {0x2641, 0x2641, prN}, // So EARTH
+ {0x2642, 0x2642, prA}, // So MALE SIGN
+ {0x2643, 0x2647, prN}, // So [5] JUPITER..PLUTO
+ {0x2648, 0x2653, prW}, // So [12] ARIES..PISCES
+ {0x2654, 0x265F, prN}, // So [12] WHITE CHESS KING..BLACK CHESS PAWN
+ {0x2660, 0x2661, prA}, // So [2] BLACK SPADE SUIT..WHITE HEART SUIT
+ {0x2662, 0x2662, prN}, // So WHITE DIAMOND SUIT
+ {0x2663, 0x2665, prA}, // So [3] BLACK CLUB SUIT..BLACK HEART SUIT
+ {0x2666, 0x2666, prN}, // So BLACK DIAMOND SUIT
+ {0x2667, 0x266A, prA}, // So [4] WHITE CLUB SUIT..EIGHTH NOTE
+ {0x266B, 0x266B, prN}, // So BEAMED EIGHTH NOTES
+ {0x266C, 0x266D, prA}, // So [2] BEAMED SIXTEENTH NOTES..MUSIC FLAT SIGN
+ {0x266E, 0x266E, prN}, // So MUSIC NATURAL SIGN
+ {0x266F, 0x266F, prA}, // Sm MUSIC SHARP SIGN
+ {0x2670, 0x267E, prN}, // So [15] WEST SYRIAC CROSS..PERMANENT PAPER SIGN
+ {0x267F, 0x267F, prW}, // So WHEELCHAIR SYMBOL
+ {0x2680, 0x2692, prN}, // So [19] DIE FACE-1..HAMMER AND PICK
+ {0x2693, 0x2693, prW}, // So ANCHOR
+ {0x2694, 0x269D, prN}, // So [10] CROSSED SWORDS..OUTLINED WHITE STAR
+ {0x269E, 0x269F, prA}, // So [2] THREE LINES CONVERGING RIGHT..THREE LINES CONVERGING LEFT
+ {0x26A0, 0x26A0, prN}, // So WARNING SIGN
+ {0x26A1, 0x26A1, prW}, // So HIGH VOLTAGE SIGN
+ {0x26A2, 0x26A9, prN}, // So [8] DOUBLED FEMALE SIGN..HORIZONTAL MALE WITH STROKE SIGN
+ {0x26AA, 0x26AB, prW}, // So [2] MEDIUM WHITE CIRCLE..MEDIUM BLACK CIRCLE
+ {0x26AC, 0x26BC, prN}, // So [17] MEDIUM SMALL WHITE CIRCLE..SESQUIQUADRATE
+ {0x26BD, 0x26BE, prW}, // So [2] SOCCER BALL..BASEBALL
+ {0x26BF, 0x26BF, prA}, // So SQUARED KEY
+ {0x26C0, 0x26C3, prN}, // So [4] WHITE DRAUGHTS MAN..BLACK DRAUGHTS KING
+ {0x26C4, 0x26C5, prW}, // So [2] SNOWMAN WITHOUT SNOW..SUN BEHIND CLOUD
+ {0x26C6, 0x26CD, prA}, // So [8] RAIN..DISABLED CAR
+ {0x26CE, 0x26CE, prW}, // So OPHIUCHUS
+ {0x26CF, 0x26D3, prA}, // So [5] PICK..CHAINS
+ {0x26D4, 0x26D4, prW}, // So NO ENTRY
+ {0x26D5, 0x26E1, prA}, // So [13] ALTERNATE ONE-WAY LEFT WAY TRAFFIC..RESTRICTED LEFT ENTRY-2
+ {0x26E2, 0x26E2, prN}, // So ASTRONOMICAL SYMBOL FOR URANUS
+ {0x26E3, 0x26E3, prA}, // So HEAVY CIRCLE WITH STROKE AND TWO DOTS ABOVE
+ {0x26E4, 0x26E7, prN}, // So [4] PENTAGRAM..INVERTED PENTAGRAM
+ {0x26E8, 0x26E9, prA}, // So [2] BLACK CROSS ON SHIELD..SHINTO SHRINE
+ {0x26EA, 0x26EA, prW}, // So CHURCH
+ {0x26EB, 0x26F1, prA}, // So [7] CASTLE..UMBRELLA ON GROUND
+ {0x26F2, 0x26F3, prW}, // So [2] FOUNTAIN..FLAG IN HOLE
+ {0x26F4, 0x26F4, prA}, // So FERRY
+ {0x26F5, 0x26F5, prW}, // So SAILBOAT
+ {0x26F6, 0x26F9, prA}, // So [4] SQUARE FOUR CORNERS..PERSON WITH BALL
+ {0x26FA, 0x26FA, prW}, // So TENT
+ {0x26FB, 0x26FC, prA}, // So [2] JAPANESE BANK SYMBOL..HEADSTONE GRAVEYARD SYMBOL
+ {0x26FD, 0x26FD, prW}, // So FUEL PUMP
+ {0x26FE, 0x26FF, prA}, // So [2] CUP ON BLACK SQUARE..WHITE FLAG WITH HORIZONTAL MIDDLE BLACK STRIPE
+ {0x2700, 0x2704, prN}, // So [5] BLACK SAFETY SCISSORS..WHITE SCISSORS
+ {0x2705, 0x2705, prW}, // So WHITE HEAVY CHECK MARK
+ {0x2706, 0x2709, prN}, // So [4] TELEPHONE LOCATION SIGN..ENVELOPE
+ {0x270A, 0x270B, prW}, // So [2] RAISED FIST..RAISED HAND
+ {0x270C, 0x2727, prN}, // So [28] VICTORY HAND..WHITE FOUR POINTED STAR
+ {0x2728, 0x2728, prW}, // So SPARKLES
+ {0x2729, 0x273C, prN}, // So [20] STRESS OUTLINED WHITE STAR..OPEN CENTRE TEARDROP-SPOKED ASTERISK
+ {0x273D, 0x273D, prA}, // So HEAVY TEARDROP-SPOKED ASTERISK
+ {0x273E, 0x274B, prN}, // So [14] SIX PETALLED BLACK AND WHITE FLORETTE..HEAVY EIGHT TEARDROP-SPOKED PROPELLER ASTERISK
+ {0x274C, 0x274C, prW}, // So CROSS MARK
+ {0x274D, 0x274D, prN}, // So SHADOWED WHITE CIRCLE
+ {0x274E, 0x274E, prW}, // So NEGATIVE SQUARED CROSS MARK
+ {0x274F, 0x2752, prN}, // So [4] LOWER RIGHT DROP-SHADOWED WHITE SQUARE..UPPER RIGHT SHADOWED WHITE SQUARE
+ {0x2753, 0x2755, prW}, // So [3] BLACK QUESTION MARK ORNAMENT..WHITE EXCLAMATION MARK ORNAMENT
+ {0x2756, 0x2756, prN}, // So BLACK DIAMOND MINUS WHITE X
+ {0x2757, 0x2757, prW}, // So HEAVY EXCLAMATION MARK SYMBOL
+ {0x2758, 0x2767, prN}, // So [16] LIGHT VERTICAL BAR..ROTATED FLORAL HEART BULLET
+ {0x2768, 0x2768, prN}, // Ps MEDIUM LEFT PARENTHESIS ORNAMENT
+ {0x2769, 0x2769, prN}, // Pe MEDIUM RIGHT PARENTHESIS ORNAMENT
+ {0x276A, 0x276A, prN}, // Ps MEDIUM FLATTENED LEFT PARENTHESIS ORNAMENT
+ {0x276B, 0x276B, prN}, // Pe MEDIUM FLATTENED RIGHT PARENTHESIS ORNAMENT
+ {0x276C, 0x276C, prN}, // Ps MEDIUM LEFT-POINTING ANGLE BRACKET ORNAMENT
+ {0x276D, 0x276D, prN}, // Pe MEDIUM RIGHT-POINTING ANGLE BRACKET ORNAMENT
+ {0x276E, 0x276E, prN}, // Ps HEAVY LEFT-POINTING ANGLE QUOTATION MARK ORNAMENT
+ {0x276F, 0x276F, prN}, // Pe HEAVY RIGHT-POINTING ANGLE QUOTATION MARK ORNAMENT
+ {0x2770, 0x2770, prN}, // Ps HEAVY LEFT-POINTING ANGLE BRACKET ORNAMENT
+ {0x2771, 0x2771, prN}, // Pe HEAVY RIGHT-POINTING ANGLE BRACKET ORNAMENT
+ {0x2772, 0x2772, prN}, // Ps LIGHT LEFT TORTOISE SHELL BRACKET ORNAMENT
+ {0x2773, 0x2773, prN}, // Pe LIGHT RIGHT TORTOISE SHELL BRACKET ORNAMENT
+ {0x2774, 0x2774, prN}, // Ps MEDIUM LEFT CURLY BRACKET ORNAMENT
+ {0x2775, 0x2775, prN}, // Pe MEDIUM RIGHT CURLY BRACKET ORNAMENT
+ {0x2776, 0x277F, prA}, // No [10] DINGBAT NEGATIVE CIRCLED DIGIT ONE..DINGBAT NEGATIVE CIRCLED NUMBER TEN
+ {0x2780, 0x2793, prN}, // No [20] DINGBAT CIRCLED SANS-SERIF DIGIT ONE..DINGBAT NEGATIVE CIRCLED SANS-SERIF NUMBER TEN
+ {0x2794, 0x2794, prN}, // So HEAVY WIDE-HEADED RIGHTWARDS ARROW
+ {0x2795, 0x2797, prW}, // So [3] HEAVY PLUS SIGN..HEAVY DIVISION SIGN
+ {0x2798, 0x27AF, prN}, // So [24] HEAVY SOUTH EAST ARROW..NOTCHED LOWER RIGHT-SHADOWED WHITE RIGHTWARDS ARROW
+ {0x27B0, 0x27B0, prW}, // So CURLY LOOP
+ {0x27B1, 0x27BE, prN}, // So [14] NOTCHED UPPER RIGHT-SHADOWED WHITE RIGHTWARDS ARROW..OPEN-OUTLINED RIGHTWARDS ARROW
+ {0x27BF, 0x27BF, prW}, // So DOUBLE CURLY LOOP
+ {0x27C0, 0x27C4, prN}, // Sm [5] THREE DIMENSIONAL ANGLE..OPEN SUPERSET
+ {0x27C5, 0x27C5, prN}, // Ps LEFT S-SHAPED BAG DELIMITER
+ {0x27C6, 0x27C6, prN}, // Pe RIGHT S-SHAPED BAG DELIMITER
+ {0x27C7, 0x27E5, prN}, // Sm [31] OR WITH DOT INSIDE..WHITE SQUARE WITH RIGHTWARDS TICK
+ {0x27E6, 0x27E6, prNa}, // Ps MATHEMATICAL LEFT WHITE SQUARE BRACKET
+ {0x27E7, 0x27E7, prNa}, // Pe MATHEMATICAL RIGHT WHITE SQUARE BRACKET
+ {0x27E8, 0x27E8, prNa}, // Ps MATHEMATICAL LEFT ANGLE BRACKET
+ {0x27E9, 0x27E9, prNa}, // Pe MATHEMATICAL RIGHT ANGLE BRACKET
+ {0x27EA, 0x27EA, prNa}, // Ps MATHEMATICAL LEFT DOUBLE ANGLE BRACKET
+ {0x27EB, 0x27EB, prNa}, // Pe MATHEMATICAL RIGHT DOUBLE ANGLE BRACKET
+ {0x27EC, 0x27EC, prNa}, // Ps MATHEMATICAL LEFT WHITE TORTOISE SHELL BRACKET
+ {0x27ED, 0x27ED, prNa}, // Pe MATHEMATICAL RIGHT WHITE TORTOISE SHELL BRACKET
+ {0x27EE, 0x27EE, prN}, // Ps MATHEMATICAL LEFT FLATTENED PARENTHESIS
+ {0x27EF, 0x27EF, prN}, // Pe MATHEMATICAL RIGHT FLATTENED PARENTHESIS
+ {0x27F0, 0x27FF, prN}, // Sm [16] UPWARDS QUADRUPLE ARROW..LONG RIGHTWARDS SQUIGGLE ARROW
+ {0x2800, 0x28FF, prN}, // So [256] BRAILLE PATTERN BLANK..BRAILLE PATTERN DOTS-12345678
+ {0x2900, 0x297F, prN}, // Sm [128] RIGHTWARDS TWO-HEADED ARROW WITH VERTICAL STROKE..DOWN FISH TAIL
+ {0x2980, 0x2982, prN}, // Sm [3] TRIPLE VERTICAL BAR DELIMITER..Z NOTATION TYPE COLON
+ {0x2983, 0x2983, prN}, // Ps LEFT WHITE CURLY BRACKET
+ {0x2984, 0x2984, prN}, // Pe RIGHT WHITE CURLY BRACKET
+ {0x2985, 0x2985, prNa}, // Ps LEFT WHITE PARENTHESIS
+ {0x2986, 0x2986, prNa}, // Pe RIGHT WHITE PARENTHESIS
+ {0x2987, 0x2987, prN}, // Ps Z NOTATION LEFT IMAGE BRACKET
+ {0x2988, 0x2988, prN}, // Pe Z NOTATION RIGHT IMAGE BRACKET
+ {0x2989, 0x2989, prN}, // Ps Z NOTATION LEFT BINDING BRACKET
+ {0x298A, 0x298A, prN}, // Pe Z NOTATION RIGHT BINDING BRACKET
+ {0x298B, 0x298B, prN}, // Ps LEFT SQUARE BRACKET WITH UNDERBAR
+ {0x298C, 0x298C, prN}, // Pe RIGHT SQUARE BRACKET WITH UNDERBAR
+ {0x298D, 0x298D, prN}, // Ps LEFT SQUARE BRACKET WITH TICK IN TOP CORNER
+ {0x298E, 0x298E, prN}, // Pe RIGHT SQUARE BRACKET WITH TICK IN BOTTOM CORNER
+ {0x298F, 0x298F, prN}, // Ps LEFT SQUARE BRACKET WITH TICK IN BOTTOM CORNER
+ {0x2990, 0x2990, prN}, // Pe RIGHT SQUARE BRACKET WITH TICK IN TOP CORNER
+ {0x2991, 0x2991, prN}, // Ps LEFT ANGLE BRACKET WITH DOT
+ {0x2992, 0x2992, prN}, // Pe RIGHT ANGLE BRACKET WITH DOT
+ {0x2993, 0x2993, prN}, // Ps LEFT ARC LESS-THAN BRACKET
+ {0x2994, 0x2994, prN}, // Pe RIGHT ARC GREATER-THAN BRACKET
+ {0x2995, 0x2995, prN}, // Ps DOUBLE LEFT ARC GREATER-THAN BRACKET
+ {0x2996, 0x2996, prN}, // Pe DOUBLE RIGHT ARC LESS-THAN BRACKET
+ {0x2997, 0x2997, prN}, // Ps LEFT BLACK TORTOISE SHELL BRACKET
+ {0x2998, 0x2998, prN}, // Pe RIGHT BLACK TORTOISE SHELL BRACKET
+ {0x2999, 0x29D7, prN}, // Sm [63] DOTTED FENCE..BLACK HOURGLASS
+ {0x29D8, 0x29D8, prN}, // Ps LEFT WIGGLY FENCE
+ {0x29D9, 0x29D9, prN}, // Pe RIGHT WIGGLY FENCE
+ {0x29DA, 0x29DA, prN}, // Ps LEFT DOUBLE WIGGLY FENCE
+ {0x29DB, 0x29DB, prN}, // Pe RIGHT DOUBLE WIGGLY FENCE
+ {0x29DC, 0x29FB, prN}, // Sm [32] INCOMPLETE INFINITY..TRIPLE PLUS
+ {0x29FC, 0x29FC, prN}, // Ps LEFT-POINTING CURVED ANGLE BRACKET
+ {0x29FD, 0x29FD, prN}, // Pe RIGHT-POINTING CURVED ANGLE BRACKET
+ {0x29FE, 0x29FF, prN}, // Sm [2] TINY..MINY
+ {0x2A00, 0x2AFF, prN}, // Sm [256] N-ARY CIRCLED DOT OPERATOR..N-ARY WHITE VERTICAL BAR
+ {0x2B00, 0x2B1A, prN}, // So [27] NORTH EAST WHITE ARROW..DOTTED SQUARE
+ {0x2B1B, 0x2B1C, prW}, // So [2] BLACK LARGE SQUARE..WHITE LARGE SQUARE
+ {0x2B1D, 0x2B2F, prN}, // So [19] BLACK VERY SMALL SQUARE..WHITE VERTICAL ELLIPSE
+ {0x2B30, 0x2B44, prN}, // Sm [21] LEFT ARROW WITH SMALL CIRCLE..RIGHTWARDS ARROW THROUGH SUPERSET
+ {0x2B45, 0x2B46, prN}, // So [2] LEFTWARDS QUADRUPLE ARROW..RIGHTWARDS QUADRUPLE ARROW
+ {0x2B47, 0x2B4C, prN}, // Sm [6] REVERSE TILDE OPERATOR ABOVE RIGHTWARDS ARROW..RIGHTWARDS ARROW ABOVE REVERSE TILDE OPERATOR
+ {0x2B4D, 0x2B4F, prN}, // So [3] DOWNWARDS TRIANGLE-HEADED ZIGZAG ARROW..SHORT BACKSLANTED SOUTH ARROW
+ {0x2B50, 0x2B50, prW}, // So WHITE MEDIUM STAR
+ {0x2B51, 0x2B54, prN}, // So [4] BLACK SMALL STAR..WHITE RIGHT-POINTING PENTAGON
+ {0x2B55, 0x2B55, prW}, // So HEAVY LARGE CIRCLE
+ {0x2B56, 0x2B59, prA}, // So [4] HEAVY OVAL WITH OVAL INSIDE..HEAVY CIRCLED SALTIRE
+ {0x2B5A, 0x2B73, prN}, // So [26] SLANTED NORTH ARROW WITH HOOKED HEAD..DOWNWARDS TRIANGLE-HEADED ARROW TO BAR
+ {0x2B76, 0x2B95, prN}, // So [32] NORTH WEST TRIANGLE-HEADED ARROW TO BAR..RIGHTWARDS BLACK ARROW
+ {0x2B97, 0x2BFF, prN}, // So [105] SYMBOL FOR TYPE A ELECTRONICS..HELLSCHREIBER PAUSE SYMBOL
+ {0x2C00, 0x2C5F, prN}, // L& [96] GLAGOLITIC CAPITAL LETTER AZU..GLAGOLITIC SMALL LETTER CAUDATE CHRIVI
+ {0x2C60, 0x2C7B, prN}, // L& [28] LATIN CAPITAL LETTER L WITH DOUBLE BAR..LATIN LETTER SMALL CAPITAL TURNED E
+ {0x2C7C, 0x2C7D, prN}, // Lm [2] LATIN SUBSCRIPT SMALL LETTER J..MODIFIER LETTER CAPITAL V
+ {0x2C7E, 0x2C7F, prN}, // Lu [2] LATIN CAPITAL LETTER S WITH SWASH TAIL..LATIN CAPITAL LETTER Z WITH SWASH TAIL
+ {0x2C80, 0x2CE4, prN}, // L& [101] COPTIC CAPITAL LETTER ALFA..COPTIC SYMBOL KAI
+ {0x2CE5, 0x2CEA, prN}, // So [6] COPTIC SYMBOL MI RO..COPTIC SYMBOL SHIMA SIMA
+ {0x2CEB, 0x2CEE, prN}, // L& [4] COPTIC CAPITAL LETTER CRYPTOGRAMMIC SHEI..COPTIC SMALL LETTER CRYPTOGRAMMIC GANGIA
+ {0x2CEF, 0x2CF1, prN}, // Mn [3] COPTIC COMBINING NI ABOVE..COPTIC COMBINING SPIRITUS LENIS
+ {0x2CF2, 0x2CF3, prN}, // L& [2] COPTIC CAPITAL LETTER BOHAIRIC KHEI..COPTIC SMALL LETTER BOHAIRIC KHEI
+ {0x2CF9, 0x2CFC, prN}, // Po [4] COPTIC OLD NUBIAN FULL STOP..COPTIC OLD NUBIAN VERSE DIVIDER
+ {0x2CFD, 0x2CFD, prN}, // No COPTIC FRACTION ONE HALF
+ {0x2CFE, 0x2CFF, prN}, // Po [2] COPTIC FULL STOP..COPTIC MORPHOLOGICAL DIVIDER
+ {0x2D00, 0x2D25, prN}, // Ll [38] GEORGIAN SMALL LETTER AN..GEORGIAN SMALL LETTER HOE
+ {0x2D27, 0x2D27, prN}, // Ll GEORGIAN SMALL LETTER YN
+ {0x2D2D, 0x2D2D, prN}, // Ll GEORGIAN SMALL LETTER AEN
+ {0x2D30, 0x2D67, prN}, // Lo [56] TIFINAGH LETTER YA..TIFINAGH LETTER YO
+ {0x2D6F, 0x2D6F, prN}, // Lm TIFINAGH MODIFIER LETTER LABIALIZATION MARK
+ {0x2D70, 0x2D70, prN}, // Po TIFINAGH SEPARATOR MARK
+ {0x2D7F, 0x2D7F, prN}, // Mn TIFINAGH CONSONANT JOINER
+ {0x2D80, 0x2D96, prN}, // Lo [23] ETHIOPIC SYLLABLE LOA..ETHIOPIC SYLLABLE GGWE
+ {0x2DA0, 0x2DA6, prN}, // Lo [7] ETHIOPIC SYLLABLE SSA..ETHIOPIC SYLLABLE SSO
+ {0x2DA8, 0x2DAE, prN}, // Lo [7] ETHIOPIC SYLLABLE CCA..ETHIOPIC SYLLABLE CCO
+ {0x2DB0, 0x2DB6, prN}, // Lo [7] ETHIOPIC SYLLABLE ZZA..ETHIOPIC SYLLABLE ZZO
+ {0x2DB8, 0x2DBE, prN}, // Lo [7] ETHIOPIC SYLLABLE CCHA..ETHIOPIC SYLLABLE CCHO
+ {0x2DC0, 0x2DC6, prN}, // Lo [7] ETHIOPIC SYLLABLE QYA..ETHIOPIC SYLLABLE QYO
+ {0x2DC8, 0x2DCE, prN}, // Lo [7] ETHIOPIC SYLLABLE KYA..ETHIOPIC SYLLABLE KYO
+ {0x2DD0, 0x2DD6, prN}, // Lo [7] ETHIOPIC SYLLABLE XYA..ETHIOPIC SYLLABLE XYO
+ {0x2DD8, 0x2DDE, prN}, // Lo [7] ETHIOPIC SYLLABLE GYA..ETHIOPIC SYLLABLE GYO
+ {0x2DE0, 0x2DFF, prN}, // Mn [32] COMBINING CYRILLIC LETTER BE..COMBINING CYRILLIC LETTER IOTIFIED BIG YUS
+ {0x2E00, 0x2E01, prN}, // Po [2] RIGHT ANGLE SUBSTITUTION MARKER..RIGHT ANGLE DOTTED SUBSTITUTION MARKER
+ {0x2E02, 0x2E02, prN}, // Pi LEFT SUBSTITUTION BRACKET
+ {0x2E03, 0x2E03, prN}, // Pf RIGHT SUBSTITUTION BRACKET
+ {0x2E04, 0x2E04, prN}, // Pi LEFT DOTTED SUBSTITUTION BRACKET
+ {0x2E05, 0x2E05, prN}, // Pf RIGHT DOTTED SUBSTITUTION BRACKET
+ {0x2E06, 0x2E08, prN}, // Po [3] RAISED INTERPOLATION MARKER..DOTTED TRANSPOSITION MARKER
+ {0x2E09, 0x2E09, prN}, // Pi LEFT TRANSPOSITION BRACKET
+ {0x2E0A, 0x2E0A, prN}, // Pf RIGHT TRANSPOSITION BRACKET
+ {0x2E0B, 0x2E0B, prN}, // Po RAISED SQUARE
+ {0x2E0C, 0x2E0C, prN}, // Pi LEFT RAISED OMISSION BRACKET
+ {0x2E0D, 0x2E0D, prN}, // Pf RIGHT RAISED OMISSION BRACKET
+ {0x2E0E, 0x2E16, prN}, // Po [9] EDITORIAL CORONIS..DOTTED RIGHT-POINTING ANGLE
+ {0x2E17, 0x2E17, prN}, // Pd DOUBLE OBLIQUE HYPHEN
+ {0x2E18, 0x2E19, prN}, // Po [2] INVERTED INTERROBANG..PALM BRANCH
+ {0x2E1A, 0x2E1A, prN}, // Pd HYPHEN WITH DIAERESIS
+ {0x2E1B, 0x2E1B, prN}, // Po TILDE WITH RING ABOVE
+ {0x2E1C, 0x2E1C, prN}, // Pi LEFT LOW PARAPHRASE BRACKET
+ {0x2E1D, 0x2E1D, prN}, // Pf RIGHT LOW PARAPHRASE BRACKET
+ {0x2E1E, 0x2E1F, prN}, // Po [2] TILDE WITH DOT ABOVE..TILDE WITH DOT BELOW
+ {0x2E20, 0x2E20, prN}, // Pi LEFT VERTICAL BAR WITH QUILL
+ {0x2E21, 0x2E21, prN}, // Pf RIGHT VERTICAL BAR WITH QUILL
+ {0x2E22, 0x2E22, prN}, // Ps TOP LEFT HALF BRACKET
+ {0x2E23, 0x2E23, prN}, // Pe TOP RIGHT HALF BRACKET
+ {0x2E24, 0x2E24, prN}, // Ps BOTTOM LEFT HALF BRACKET
+ {0x2E25, 0x2E25, prN}, // Pe BOTTOM RIGHT HALF BRACKET
+ {0x2E26, 0x2E26, prN}, // Ps LEFT SIDEWAYS U BRACKET
+ {0x2E27, 0x2E27, prN}, // Pe RIGHT SIDEWAYS U BRACKET
+ {0x2E28, 0x2E28, prN}, // Ps LEFT DOUBLE PARENTHESIS
+ {0x2E29, 0x2E29, prN}, // Pe RIGHT DOUBLE PARENTHESIS
+ {0x2E2A, 0x2E2E, prN}, // Po [5] TWO DOTS OVER ONE DOT PUNCTUATION..REVERSED QUESTION MARK
+ {0x2E2F, 0x2E2F, prN}, // Lm VERTICAL TILDE
+ {0x2E30, 0x2E39, prN}, // Po [10] RING POINT..TOP HALF SECTION SIGN
+ {0x2E3A, 0x2E3B, prN}, // Pd [2] TWO-EM DASH..THREE-EM DASH
+ {0x2E3C, 0x2E3F, prN}, // Po [4] STENOGRAPHIC FULL STOP..CAPITULUM
+ {0x2E40, 0x2E40, prN}, // Pd DOUBLE HYPHEN
+ {0x2E41, 0x2E41, prN}, // Po REVERSED COMMA
+ {0x2E42, 0x2E42, prN}, // Ps DOUBLE LOW-REVERSED-9 QUOTATION MARK
+ {0x2E43, 0x2E4F, prN}, // Po [13] DASH WITH LEFT UPTURN..CORNISH VERSE DIVIDER
+ {0x2E50, 0x2E51, prN}, // So [2] CROSS PATTY WITH RIGHT CROSSBAR..CROSS PATTY WITH LEFT CROSSBAR
+ {0x2E52, 0x2E54, prN}, // Po [3] TIRONIAN SIGN CAPITAL ET..MEDIEVAL QUESTION MARK
+ {0x2E55, 0x2E55, prN}, // Ps LEFT SQUARE BRACKET WITH STROKE
+ {0x2E56, 0x2E56, prN}, // Pe RIGHT SQUARE BRACKET WITH STROKE
+ {0x2E57, 0x2E57, prN}, // Ps LEFT SQUARE BRACKET WITH DOUBLE STROKE
+ {0x2E58, 0x2E58, prN}, // Pe RIGHT SQUARE BRACKET WITH DOUBLE STROKE
+ {0x2E59, 0x2E59, prN}, // Ps TOP HALF LEFT PARENTHESIS
+ {0x2E5A, 0x2E5A, prN}, // Pe TOP HALF RIGHT PARENTHESIS
+ {0x2E5B, 0x2E5B, prN}, // Ps BOTTOM HALF LEFT PARENTHESIS
+ {0x2E5C, 0x2E5C, prN}, // Pe BOTTOM HALF RIGHT PARENTHESIS
+ {0x2E5D, 0x2E5D, prN}, // Pd OBLIQUE HYPHEN
+ {0x2E80, 0x2E99, prW}, // So [26] CJK RADICAL REPEAT..CJK RADICAL RAP
+ {0x2E9B, 0x2EF3, prW}, // So [89] CJK RADICAL CHOKE..CJK RADICAL C-SIMPLIFIED TURTLE
+ {0x2F00, 0x2FD5, prW}, // So [214] KANGXI RADICAL ONE..KANGXI RADICAL FLUTE
+ {0x2FF0, 0x2FFB, prW}, // So [12] IDEOGRAPHIC DESCRIPTION CHARACTER LEFT TO RIGHT..IDEOGRAPHIC DESCRIPTION CHARACTER OVERLAID
+ {0x3000, 0x3000, prF}, // Zs IDEOGRAPHIC SPACE
+ {0x3001, 0x3003, prW}, // Po [3] IDEOGRAPHIC COMMA..DITTO MARK
+ {0x3004, 0x3004, prW}, // So JAPANESE INDUSTRIAL STANDARD SYMBOL
+ {0x3005, 0x3005, prW}, // Lm IDEOGRAPHIC ITERATION MARK
+ {0x3006, 0x3006, prW}, // Lo IDEOGRAPHIC CLOSING MARK
+ {0x3007, 0x3007, prW}, // Nl IDEOGRAPHIC NUMBER ZERO
+ {0x3008, 0x3008, prW}, // Ps LEFT ANGLE BRACKET
+ {0x3009, 0x3009, prW}, // Pe RIGHT ANGLE BRACKET
+ {0x300A, 0x300A, prW}, // Ps LEFT DOUBLE ANGLE BRACKET
+ {0x300B, 0x300B, prW}, // Pe RIGHT DOUBLE ANGLE BRACKET
+ {0x300C, 0x300C, prW}, // Ps LEFT CORNER BRACKET
+ {0x300D, 0x300D, prW}, // Pe RIGHT CORNER BRACKET
+ {0x300E, 0x300E, prW}, // Ps LEFT WHITE CORNER BRACKET
+ {0x300F, 0x300F, prW}, // Pe RIGHT WHITE CORNER BRACKET
+ {0x3010, 0x3010, prW}, // Ps LEFT BLACK LENTICULAR BRACKET
+ {0x3011, 0x3011, prW}, // Pe RIGHT BLACK LENTICULAR BRACKET
+ {0x3012, 0x3013, prW}, // So [2] POSTAL MARK..GETA MARK
+ {0x3014, 0x3014, prW}, // Ps LEFT TORTOISE SHELL BRACKET
+ {0x3015, 0x3015, prW}, // Pe RIGHT TORTOISE SHELL BRACKET
+ {0x3016, 0x3016, prW}, // Ps LEFT WHITE LENTICULAR BRACKET
+ {0x3017, 0x3017, prW}, // Pe RIGHT WHITE LENTICULAR BRACKET
+ {0x3018, 0x3018, prW}, // Ps LEFT WHITE TORTOISE SHELL BRACKET
+ {0x3019, 0x3019, prW}, // Pe RIGHT WHITE TORTOISE SHELL BRACKET
+ {0x301A, 0x301A, prW}, // Ps LEFT WHITE SQUARE BRACKET
+ {0x301B, 0x301B, prW}, // Pe RIGHT WHITE SQUARE BRACKET
+ {0x301C, 0x301C, prW}, // Pd WAVE DASH
+ {0x301D, 0x301D, prW}, // Ps REVERSED DOUBLE PRIME QUOTATION MARK
+ {0x301E, 0x301F, prW}, // Pe [2] DOUBLE PRIME QUOTATION MARK..LOW DOUBLE PRIME QUOTATION MARK
+ {0x3020, 0x3020, prW}, // So POSTAL MARK FACE
+ {0x3021, 0x3029, prW}, // Nl [9] HANGZHOU NUMERAL ONE..HANGZHOU NUMERAL NINE
+ {0x302A, 0x302D, prW}, // Mn [4] IDEOGRAPHIC LEVEL TONE MARK..IDEOGRAPHIC ENTERING TONE MARK
+ {0x302E, 0x302F, prW}, // Mc [2] HANGUL SINGLE DOT TONE MARK..HANGUL DOUBLE DOT TONE MARK
+ {0x3030, 0x3030, prW}, // Pd WAVY DASH
+ {0x3031, 0x3035, prW}, // Lm [5] VERTICAL KANA REPEAT MARK..VERTICAL KANA REPEAT MARK LOWER HALF
+ {0x3036, 0x3037, prW}, // So [2] CIRCLED POSTAL MARK..IDEOGRAPHIC TELEGRAPH LINE FEED SEPARATOR SYMBOL
+ {0x3038, 0x303A, prW}, // Nl [3] HANGZHOU NUMERAL TEN..HANGZHOU NUMERAL THIRTY
+ {0x303B, 0x303B, prW}, // Lm VERTICAL IDEOGRAPHIC ITERATION MARK
+ {0x303C, 0x303C, prW}, // Lo MASU MARK
+ {0x303D, 0x303D, prW}, // Po PART ALTERNATION MARK
+ {0x303E, 0x303E, prW}, // So IDEOGRAPHIC VARIATION INDICATOR
+ {0x303F, 0x303F, prN}, // So IDEOGRAPHIC HALF FILL SPACE
+ {0x3041, 0x3096, prW}, // Lo [86] HIRAGANA LETTER SMALL A..HIRAGANA LETTER SMALL KE
+ {0x3099, 0x309A, prW}, // Mn [2] COMBINING KATAKANA-HIRAGANA VOICED SOUND MARK..COMBINING KATAKANA-HIRAGANA SEMI-VOICED SOUND MARK
+ {0x309B, 0x309C, prW}, // Sk [2] KATAKANA-HIRAGANA VOICED SOUND MARK..KATAKANA-HIRAGANA SEMI-VOICED SOUND MARK
+ {0x309D, 0x309E, prW}, // Lm [2] HIRAGANA ITERATION MARK..HIRAGANA VOICED ITERATION MARK
+ {0x309F, 0x309F, prW}, // Lo HIRAGANA DIGRAPH YORI
+ {0x30A0, 0x30A0, prW}, // Pd KATAKANA-HIRAGANA DOUBLE HYPHEN
+ {0x30A1, 0x30FA, prW}, // Lo [90] KATAKANA LETTER SMALL A..KATAKANA LETTER VO
+ {0x30FB, 0x30FB, prW}, // Po KATAKANA MIDDLE DOT
+ {0x30FC, 0x30FE, prW}, // Lm [3] KATAKANA-HIRAGANA PROLONGED SOUND MARK..KATAKANA VOICED ITERATION MARK
+ {0x30FF, 0x30FF, prW}, // Lo KATAKANA DIGRAPH KOTO
+ {0x3105, 0x312F, prW}, // Lo [43] BOPOMOFO LETTER B..BOPOMOFO LETTER NN
+ {0x3131, 0x318E, prW}, // Lo [94] HANGUL LETTER KIYEOK..HANGUL LETTER ARAEAE
+ {0x3190, 0x3191, prW}, // So [2] IDEOGRAPHIC ANNOTATION LINKING MARK..IDEOGRAPHIC ANNOTATION REVERSE MARK
+ {0x3192, 0x3195, prW}, // No [4] IDEOGRAPHIC ANNOTATION ONE MARK..IDEOGRAPHIC ANNOTATION FOUR MARK
+ {0x3196, 0x319F, prW}, // So [10] IDEOGRAPHIC ANNOTATION TOP MARK..IDEOGRAPHIC ANNOTATION MAN MARK
+ {0x31A0, 0x31BF, prW}, // Lo [32] BOPOMOFO LETTER BU..BOPOMOFO LETTER AH
+ {0x31C0, 0x31E3, prW}, // So [36] CJK STROKE T..CJK STROKE Q
+ {0x31F0, 0x31FF, prW}, // Lo [16] KATAKANA LETTER SMALL KU..KATAKANA LETTER SMALL RO
+ {0x3200, 0x321E, prW}, // So [31] PARENTHESIZED HANGUL KIYEOK..PARENTHESIZED KOREAN CHARACTER O HU
+ {0x3220, 0x3229, prW}, // No [10] PARENTHESIZED IDEOGRAPH ONE..PARENTHESIZED IDEOGRAPH TEN
+ {0x322A, 0x3247, prW}, // So [30] PARENTHESIZED IDEOGRAPH MOON..CIRCLED IDEOGRAPH KOTO
+ {0x3248, 0x324F, prA}, // No [8] CIRCLED NUMBER TEN ON BLACK SQUARE..CIRCLED NUMBER EIGHTY ON BLACK SQUARE
+ {0x3250, 0x3250, prW}, // So PARTNERSHIP SIGN
+ {0x3251, 0x325F, prW}, // No [15] CIRCLED NUMBER TWENTY ONE..CIRCLED NUMBER THIRTY FIVE
+ {0x3260, 0x327F, prW}, // So [32] CIRCLED HANGUL KIYEOK..KOREAN STANDARD SYMBOL
+ {0x3280, 0x3289, prW}, // No [10] CIRCLED IDEOGRAPH ONE..CIRCLED IDEOGRAPH TEN
+ {0x328A, 0x32B0, prW}, // So [39] CIRCLED IDEOGRAPH MOON..CIRCLED IDEOGRAPH NIGHT
+ {0x32B1, 0x32BF, prW}, // No [15] CIRCLED NUMBER THIRTY SIX..CIRCLED NUMBER FIFTY
+ {0x32C0, 0x32FF, prW}, // So [64] IDEOGRAPHIC TELEGRAPH SYMBOL FOR JANUARY..SQUARE ERA NAME REIWA
+ {0x3300, 0x33FF, prW}, // So [256] SQUARE APAATO..SQUARE GAL
+ {0x3400, 0x4DBF, prW}, // Lo [6592] CJK UNIFIED IDEOGRAPH-3400..CJK UNIFIED IDEOGRAPH-4DBF
+ {0x4DC0, 0x4DFF, prN}, // So [64] HEXAGRAM FOR THE CREATIVE HEAVEN..HEXAGRAM FOR BEFORE COMPLETION
+ {0x4E00, 0x9FFF, prW}, // Lo [20992] CJK UNIFIED IDEOGRAPH-4E00..CJK UNIFIED IDEOGRAPH-9FFF
+ {0xA000, 0xA014, prW}, // Lo [21] YI SYLLABLE IT..YI SYLLABLE E
+ {0xA015, 0xA015, prW}, // Lm YI SYLLABLE WU
+ {0xA016, 0xA48C, prW}, // Lo [1143] YI SYLLABLE BIT..YI SYLLABLE YYR
+ {0xA490, 0xA4C6, prW}, // So [55] YI RADICAL QOT..YI RADICAL KE
+ {0xA4D0, 0xA4F7, prN}, // Lo [40] LISU LETTER BA..LISU LETTER OE
+ {0xA4F8, 0xA4FD, prN}, // Lm [6] LISU LETTER TONE MYA TI..LISU LETTER TONE MYA JEU
+ {0xA4FE, 0xA4FF, prN}, // Po [2] LISU PUNCTUATION COMMA..LISU PUNCTUATION FULL STOP
+ {0xA500, 0xA60B, prN}, // Lo [268] VAI SYLLABLE EE..VAI SYLLABLE NG
+ {0xA60C, 0xA60C, prN}, // Lm VAI SYLLABLE LENGTHENER
+ {0xA60D, 0xA60F, prN}, // Po [3] VAI COMMA..VAI QUESTION MARK
+ {0xA610, 0xA61F, prN}, // Lo [16] VAI SYLLABLE NDOLE FA..VAI SYMBOL JONG
+ {0xA620, 0xA629, prN}, // Nd [10] VAI DIGIT ZERO..VAI DIGIT NINE
+ {0xA62A, 0xA62B, prN}, // Lo [2] VAI SYLLABLE NDOLE MA..VAI SYLLABLE NDOLE DO
+ {0xA640, 0xA66D, prN}, // L& [46] CYRILLIC CAPITAL LETTER ZEMLYA..CYRILLIC SMALL LETTER DOUBLE MONOCULAR O
+ {0xA66E, 0xA66E, prN}, // Lo CYRILLIC LETTER MULTIOCULAR O
+ {0xA66F, 0xA66F, prN}, // Mn COMBINING CYRILLIC VZMET
+ {0xA670, 0xA672, prN}, // Me [3] COMBINING CYRILLIC TEN MILLIONS SIGN..COMBINING CYRILLIC THOUSAND MILLIONS SIGN
+ {0xA673, 0xA673, prN}, // Po SLAVONIC ASTERISK
+ {0xA674, 0xA67D, prN}, // Mn [10] COMBINING CYRILLIC LETTER UKRAINIAN IE..COMBINING CYRILLIC PAYEROK
+ {0xA67E, 0xA67E, prN}, // Po CYRILLIC KAVYKA
+ {0xA67F, 0xA67F, prN}, // Lm CYRILLIC PAYEROK
+ {0xA680, 0xA69B, prN}, // L& [28] CYRILLIC CAPITAL LETTER DWE..CYRILLIC SMALL LETTER CROSSED O
+ {0xA69C, 0xA69D, prN}, // Lm [2] MODIFIER LETTER CYRILLIC HARD SIGN..MODIFIER LETTER CYRILLIC SOFT SIGN
+ {0xA69E, 0xA69F, prN}, // Mn [2] COMBINING CYRILLIC LETTER EF..COMBINING CYRILLIC LETTER IOTIFIED E
+ {0xA6A0, 0xA6E5, prN}, // Lo [70] BAMUM LETTER A..BAMUM LETTER KI
+ {0xA6E6, 0xA6EF, prN}, // Nl [10] BAMUM LETTER MO..BAMUM LETTER KOGHOM
+ {0xA6F0, 0xA6F1, prN}, // Mn [2] BAMUM COMBINING MARK KOQNDON..BAMUM COMBINING MARK TUKWENTIS
+ {0xA6F2, 0xA6F7, prN}, // Po [6] BAMUM NJAEMLI..BAMUM QUESTION MARK
+ {0xA700, 0xA716, prN}, // Sk [23] MODIFIER LETTER CHINESE TONE YIN PING..MODIFIER LETTER EXTRA-LOW LEFT-STEM TONE BAR
+ {0xA717, 0xA71F, prN}, // Lm [9] MODIFIER LETTER DOT VERTICAL BAR..MODIFIER LETTER LOW INVERTED EXCLAMATION MARK
+ {0xA720, 0xA721, prN}, // Sk [2] MODIFIER LETTER STRESS AND HIGH TONE..MODIFIER LETTER STRESS AND LOW TONE
+ {0xA722, 0xA76F, prN}, // L& [78] LATIN CAPITAL LETTER EGYPTOLOGICAL ALEF..LATIN SMALL LETTER CON
+ {0xA770, 0xA770, prN}, // Lm MODIFIER LETTER US
+ {0xA771, 0xA787, prN}, // L& [23] LATIN SMALL LETTER DUM..LATIN SMALL LETTER INSULAR T
+ {0xA788, 0xA788, prN}, // Lm MODIFIER LETTER LOW CIRCUMFLEX ACCENT
+ {0xA789, 0xA78A, prN}, // Sk [2] MODIFIER LETTER COLON..MODIFIER LETTER SHORT EQUALS SIGN
+ {0xA78B, 0xA78E, prN}, // L& [4] LATIN CAPITAL LETTER SALTILLO..LATIN SMALL LETTER L WITH RETROFLEX HOOK AND BELT
+ {0xA78F, 0xA78F, prN}, // Lo LATIN LETTER SINOLOGICAL DOT
+ {0xA790, 0xA7CA, prN}, // L& [59] LATIN CAPITAL LETTER N WITH DESCENDER..LATIN SMALL LETTER S WITH SHORT STROKE OVERLAY
+ {0xA7D0, 0xA7D1, prN}, // L& [2] LATIN CAPITAL LETTER CLOSED INSULAR G..LATIN SMALL LETTER CLOSED INSULAR G
+ {0xA7D3, 0xA7D3, prN}, // Ll LATIN SMALL LETTER DOUBLE THORN
+ {0xA7D5, 0xA7D9, prN}, // L& [5] LATIN SMALL LETTER DOUBLE WYNN..LATIN SMALL LETTER SIGMOID S
+ {0xA7F2, 0xA7F4, prN}, // Lm [3] MODIFIER LETTER CAPITAL C..MODIFIER LETTER CAPITAL Q
+ {0xA7F5, 0xA7F6, prN}, // L& [2] LATIN CAPITAL LETTER REVERSED HALF H..LATIN SMALL LETTER REVERSED HALF H
+ {0xA7F7, 0xA7F7, prN}, // Lo LATIN EPIGRAPHIC LETTER SIDEWAYS I
+ {0xA7F8, 0xA7F9, prN}, // Lm [2] MODIFIER LETTER CAPITAL H WITH STROKE..MODIFIER LETTER SMALL LIGATURE OE
+ {0xA7FA, 0xA7FA, prN}, // Ll LATIN LETTER SMALL CAPITAL TURNED M
+ {0xA7FB, 0xA7FF, prN}, // Lo [5] LATIN EPIGRAPHIC LETTER REVERSED F..LATIN EPIGRAPHIC LETTER ARCHAIC M
+ {0xA800, 0xA801, prN}, // Lo [2] SYLOTI NAGRI LETTER A..SYLOTI NAGRI LETTER I
+ {0xA802, 0xA802, prN}, // Mn SYLOTI NAGRI SIGN DVISVARA
+ {0xA803, 0xA805, prN}, // Lo [3] SYLOTI NAGRI LETTER U..SYLOTI NAGRI LETTER O
+ {0xA806, 0xA806, prN}, // Mn SYLOTI NAGRI SIGN HASANTA
+ {0xA807, 0xA80A, prN}, // Lo [4] SYLOTI NAGRI LETTER KO..SYLOTI NAGRI LETTER GHO
+ {0xA80B, 0xA80B, prN}, // Mn SYLOTI NAGRI SIGN ANUSVARA
+ {0xA80C, 0xA822, prN}, // Lo [23] SYLOTI NAGRI LETTER CO..SYLOTI NAGRI LETTER HO
+ {0xA823, 0xA824, prN}, // Mc [2] SYLOTI NAGRI VOWEL SIGN A..SYLOTI NAGRI VOWEL SIGN I
+ {0xA825, 0xA826, prN}, // Mn [2] SYLOTI NAGRI VOWEL SIGN U..SYLOTI NAGRI VOWEL SIGN E
+ {0xA827, 0xA827, prN}, // Mc SYLOTI NAGRI VOWEL SIGN OO
+ {0xA828, 0xA82B, prN}, // So [4] SYLOTI NAGRI POETRY MARK-1..SYLOTI NAGRI POETRY MARK-4
+ {0xA82C, 0xA82C, prN}, // Mn SYLOTI NAGRI SIGN ALTERNATE HASANTA
+ {0xA830, 0xA835, prN}, // No [6] NORTH INDIC FRACTION ONE QUARTER..NORTH INDIC FRACTION THREE SIXTEENTHS
+ {0xA836, 0xA837, prN}, // So [2] NORTH INDIC QUARTER MARK..NORTH INDIC PLACEHOLDER MARK
+ {0xA838, 0xA838, prN}, // Sc NORTH INDIC RUPEE MARK
+ {0xA839, 0xA839, prN}, // So NORTH INDIC QUANTITY MARK
+ {0xA840, 0xA873, prN}, // Lo [52] PHAGS-PA LETTER KA..PHAGS-PA LETTER CANDRABINDU
+ {0xA874, 0xA877, prN}, // Po [4] PHAGS-PA SINGLE HEAD MARK..PHAGS-PA MARK DOUBLE SHAD
+ {0xA880, 0xA881, prN}, // Mc [2] SAURASHTRA SIGN ANUSVARA..SAURASHTRA SIGN VISARGA
+ {0xA882, 0xA8B3, prN}, // Lo [50] SAURASHTRA LETTER A..SAURASHTRA LETTER LLA
+ {0xA8B4, 0xA8C3, prN}, // Mc [16] SAURASHTRA CONSONANT SIGN HAARU..SAURASHTRA VOWEL SIGN AU
+ {0xA8C4, 0xA8C5, prN}, // Mn [2] SAURASHTRA SIGN VIRAMA..SAURASHTRA SIGN CANDRABINDU
+ {0xA8CE, 0xA8CF, prN}, // Po [2] SAURASHTRA DANDA..SAURASHTRA DOUBLE DANDA
+ {0xA8D0, 0xA8D9, prN}, // Nd [10] SAURASHTRA DIGIT ZERO..SAURASHTRA DIGIT NINE
+ {0xA8E0, 0xA8F1, prN}, // Mn [18] COMBINING DEVANAGARI DIGIT ZERO..COMBINING DEVANAGARI SIGN AVAGRAHA
+ {0xA8F2, 0xA8F7, prN}, // Lo [6] DEVANAGARI SIGN SPACING CANDRABINDU..DEVANAGARI SIGN CANDRABINDU AVAGRAHA
+ {0xA8F8, 0xA8FA, prN}, // Po [3] DEVANAGARI SIGN PUSHPIKA..DEVANAGARI CARET
+ {0xA8FB, 0xA8FB, prN}, // Lo DEVANAGARI HEADSTROKE
+ {0xA8FC, 0xA8FC, prN}, // Po DEVANAGARI SIGN SIDDHAM
+ {0xA8FD, 0xA8FE, prN}, // Lo [2] DEVANAGARI JAIN OM..DEVANAGARI LETTER AY
+ {0xA8FF, 0xA8FF, prN}, // Mn DEVANAGARI VOWEL SIGN AY
+ {0xA900, 0xA909, prN}, // Nd [10] KAYAH LI DIGIT ZERO..KAYAH LI DIGIT NINE
+ {0xA90A, 0xA925, prN}, // Lo [28] KAYAH LI LETTER KA..KAYAH LI LETTER OO
+ {0xA926, 0xA92D, prN}, // Mn [8] KAYAH LI VOWEL UE..KAYAH LI TONE CALYA PLOPHU
+ {0xA92E, 0xA92F, prN}, // Po [2] KAYAH LI SIGN CWI..KAYAH LI SIGN SHYA
+ {0xA930, 0xA946, prN}, // Lo [23] REJANG LETTER KA..REJANG LETTER A
+ {0xA947, 0xA951, prN}, // Mn [11] REJANG VOWEL SIGN I..REJANG CONSONANT SIGN R
+ {0xA952, 0xA953, prN}, // Mc [2] REJANG CONSONANT SIGN H..REJANG VIRAMA
+ {0xA95F, 0xA95F, prN}, // Po REJANG SECTION MARK
+ {0xA960, 0xA97C, prW}, // Lo [29] HANGUL CHOSEONG TIKEUT-MIEUM..HANGUL CHOSEONG SSANGYEORINHIEUH
+ {0xA980, 0xA982, prN}, // Mn [3] JAVANESE SIGN PANYANGGA..JAVANESE SIGN LAYAR
+ {0xA983, 0xA983, prN}, // Mc JAVANESE SIGN WIGNYAN
+ {0xA984, 0xA9B2, prN}, // Lo [47] JAVANESE LETTER A..JAVANESE LETTER HA
+ {0xA9B3, 0xA9B3, prN}, // Mn JAVANESE SIGN CECAK TELU
+ {0xA9B4, 0xA9B5, prN}, // Mc [2] JAVANESE VOWEL SIGN TARUNG..JAVANESE VOWEL SIGN TOLONG
+ {0xA9B6, 0xA9B9, prN}, // Mn [4] JAVANESE VOWEL SIGN WULU..JAVANESE VOWEL SIGN SUKU MENDUT
+ {0xA9BA, 0xA9BB, prN}, // Mc [2] JAVANESE VOWEL SIGN TALING..JAVANESE VOWEL SIGN DIRGA MURE
+ {0xA9BC, 0xA9BD, prN}, // Mn [2] JAVANESE VOWEL SIGN PEPET..JAVANESE CONSONANT SIGN KERET
+ {0xA9BE, 0xA9C0, prN}, // Mc [3] JAVANESE CONSONANT SIGN PENGKAL..JAVANESE PANGKON
+ {0xA9C1, 0xA9CD, prN}, // Po [13] JAVANESE LEFT RERENGGAN..JAVANESE TURNED PADA PISELEH
+ {0xA9CF, 0xA9CF, prN}, // Lm JAVANESE PANGRANGKEP
+ {0xA9D0, 0xA9D9, prN}, // Nd [10] JAVANESE DIGIT ZERO..JAVANESE DIGIT NINE
+ {0xA9DE, 0xA9DF, prN}, // Po [2] JAVANESE PADA TIRTA TUMETES..JAVANESE PADA ISEN-ISEN
+ {0xA9E0, 0xA9E4, prN}, // Lo [5] MYANMAR LETTER SHAN GHA..MYANMAR LETTER SHAN BHA
+ {0xA9E5, 0xA9E5, prN}, // Mn MYANMAR SIGN SHAN SAW
+ {0xA9E6, 0xA9E6, prN}, // Lm MYANMAR MODIFIER LETTER SHAN REDUPLICATION
+ {0xA9E7, 0xA9EF, prN}, // Lo [9] MYANMAR LETTER TAI LAING NYA..MYANMAR LETTER TAI LAING NNA
+ {0xA9F0, 0xA9F9, prN}, // Nd [10] MYANMAR TAI LAING DIGIT ZERO..MYANMAR TAI LAING DIGIT NINE
+ {0xA9FA, 0xA9FE, prN}, // Lo [5] MYANMAR LETTER TAI LAING LLA..MYANMAR LETTER TAI LAING BHA
+ {0xAA00, 0xAA28, prN}, // Lo [41] CHAM LETTER A..CHAM LETTER HA
+ {0xAA29, 0xAA2E, prN}, // Mn [6] CHAM VOWEL SIGN AA..CHAM VOWEL SIGN OE
+ {0xAA2F, 0xAA30, prN}, // Mc [2] CHAM VOWEL SIGN O..CHAM VOWEL SIGN AI
+ {0xAA31, 0xAA32, prN}, // Mn [2] CHAM VOWEL SIGN AU..CHAM VOWEL SIGN UE
+ {0xAA33, 0xAA34, prN}, // Mc [2] CHAM CONSONANT SIGN YA..CHAM CONSONANT SIGN RA
+ {0xAA35, 0xAA36, prN}, // Mn [2] CHAM CONSONANT SIGN LA..CHAM CONSONANT SIGN WA
+ {0xAA40, 0xAA42, prN}, // Lo [3] CHAM LETTER FINAL K..CHAM LETTER FINAL NG
+ {0xAA43, 0xAA43, prN}, // Mn CHAM CONSONANT SIGN FINAL NG
+ {0xAA44, 0xAA4B, prN}, // Lo [8] CHAM LETTER FINAL CH..CHAM LETTER FINAL SS
+ {0xAA4C, 0xAA4C, prN}, // Mn CHAM CONSONANT SIGN FINAL M
+ {0xAA4D, 0xAA4D, prN}, // Mc CHAM CONSONANT SIGN FINAL H
+ {0xAA50, 0xAA59, prN}, // Nd [10] CHAM DIGIT ZERO..CHAM DIGIT NINE
+ {0xAA5C, 0xAA5F, prN}, // Po [4] CHAM PUNCTUATION SPIRAL..CHAM PUNCTUATION TRIPLE DANDA
+ {0xAA60, 0xAA6F, prN}, // Lo [16] MYANMAR LETTER KHAMTI GA..MYANMAR LETTER KHAMTI FA
+ {0xAA70, 0xAA70, prN}, // Lm MYANMAR MODIFIER LETTER KHAMTI REDUPLICATION
+ {0xAA71, 0xAA76, prN}, // Lo [6] MYANMAR LETTER KHAMTI XA..MYANMAR LOGOGRAM KHAMTI HM
+ {0xAA77, 0xAA79, prN}, // So [3] MYANMAR SYMBOL AITON EXCLAMATION..MYANMAR SYMBOL AITON TWO
+ {0xAA7A, 0xAA7A, prN}, // Lo MYANMAR LETTER AITON RA
+ {0xAA7B, 0xAA7B, prN}, // Mc MYANMAR SIGN PAO KAREN TONE
+ {0xAA7C, 0xAA7C, prN}, // Mn MYANMAR SIGN TAI LAING TONE-2
+ {0xAA7D, 0xAA7D, prN}, // Mc MYANMAR SIGN TAI LAING TONE-5
+ {0xAA7E, 0xAA7F, prN}, // Lo [2] MYANMAR LETTER SHWE PALAUNG CHA..MYANMAR LETTER SHWE PALAUNG SHA
+ {0xAA80, 0xAAAF, prN}, // Lo [48] TAI VIET LETTER LOW KO..TAI VIET LETTER HIGH O
+ {0xAAB0, 0xAAB0, prN}, // Mn TAI VIET MAI KANG
+ {0xAAB1, 0xAAB1, prN}, // Lo TAI VIET VOWEL AA
+ {0xAAB2, 0xAAB4, prN}, // Mn [3] TAI VIET VOWEL I..TAI VIET VOWEL U
+ {0xAAB5, 0xAAB6, prN}, // Lo [2] TAI VIET VOWEL E..TAI VIET VOWEL O
+ {0xAAB7, 0xAAB8, prN}, // Mn [2] TAI VIET MAI KHIT..TAI VIET VOWEL IA
+ {0xAAB9, 0xAABD, prN}, // Lo [5] TAI VIET VOWEL UEA..TAI VIET VOWEL AN
+ {0xAABE, 0xAABF, prN}, // Mn [2] TAI VIET VOWEL AM..TAI VIET TONE MAI EK
+ {0xAAC0, 0xAAC0, prN}, // Lo TAI VIET TONE MAI NUENG
+ {0xAAC1, 0xAAC1, prN}, // Mn TAI VIET TONE MAI THO
+ {0xAAC2, 0xAAC2, prN}, // Lo TAI VIET TONE MAI SONG
+ {0xAADB, 0xAADC, prN}, // Lo [2] TAI VIET SYMBOL KON..TAI VIET SYMBOL NUENG
+ {0xAADD, 0xAADD, prN}, // Lm TAI VIET SYMBOL SAM
+ {0xAADE, 0xAADF, prN}, // Po [2] TAI VIET SYMBOL HO HOI..TAI VIET SYMBOL KOI KOI
+ {0xAAE0, 0xAAEA, prN}, // Lo [11] MEETEI MAYEK LETTER E..MEETEI MAYEK LETTER SSA
+ {0xAAEB, 0xAAEB, prN}, // Mc MEETEI MAYEK VOWEL SIGN II
+ {0xAAEC, 0xAAED, prN}, // Mn [2] MEETEI MAYEK VOWEL SIGN UU..MEETEI MAYEK VOWEL SIGN AAI
+ {0xAAEE, 0xAAEF, prN}, // Mc [2] MEETEI MAYEK VOWEL SIGN AU..MEETEI MAYEK VOWEL SIGN AAU
+ {0xAAF0, 0xAAF1, prN}, // Po [2] MEETEI MAYEK CHEIKHAN..MEETEI MAYEK AHANG KHUDAM
+ {0xAAF2, 0xAAF2, prN}, // Lo MEETEI MAYEK ANJI
+ {0xAAF3, 0xAAF4, prN}, // Lm [2] MEETEI MAYEK SYLLABLE REPETITION MARK..MEETEI MAYEK WORD REPETITION MARK
+ {0xAAF5, 0xAAF5, prN}, // Mc MEETEI MAYEK VOWEL SIGN VISARGA
+ {0xAAF6, 0xAAF6, prN}, // Mn MEETEI MAYEK VIRAMA
+ {0xAB01, 0xAB06, prN}, // Lo [6] ETHIOPIC SYLLABLE TTHU..ETHIOPIC SYLLABLE TTHO
+ {0xAB09, 0xAB0E, prN}, // Lo [6] ETHIOPIC SYLLABLE DDHU..ETHIOPIC SYLLABLE DDHO
+ {0xAB11, 0xAB16, prN}, // Lo [6] ETHIOPIC SYLLABLE DZU..ETHIOPIC SYLLABLE DZO
+ {0xAB20, 0xAB26, prN}, // Lo [7] ETHIOPIC SYLLABLE CCHHA..ETHIOPIC SYLLABLE CCHHO
+ {0xAB28, 0xAB2E, prN}, // Lo [7] ETHIOPIC SYLLABLE BBA..ETHIOPIC SYLLABLE BBO
+ {0xAB30, 0xAB5A, prN}, // Ll [43] LATIN SMALL LETTER BARRED ALPHA..LATIN SMALL LETTER Y WITH SHORT RIGHT LEG
+ {0xAB5B, 0xAB5B, prN}, // Sk MODIFIER BREVE WITH INVERTED BREVE
+ {0xAB5C, 0xAB5F, prN}, // Lm [4] MODIFIER LETTER SMALL HENG..MODIFIER LETTER SMALL U WITH LEFT HOOK
+ {0xAB60, 0xAB68, prN}, // Ll [9] LATIN SMALL LETTER SAKHA YAT..LATIN SMALL LETTER TURNED R WITH MIDDLE TILDE
+ {0xAB69, 0xAB69, prN}, // Lm MODIFIER LETTER SMALL TURNED W
+ {0xAB6A, 0xAB6B, prN}, // Sk [2] MODIFIER LETTER LEFT TACK..MODIFIER LETTER RIGHT TACK
+ {0xAB70, 0xABBF, prN}, // Ll [80] CHEROKEE SMALL LETTER A..CHEROKEE SMALL LETTER YA
+ {0xABC0, 0xABE2, prN}, // Lo [35] MEETEI MAYEK LETTER KOK..MEETEI MAYEK LETTER I LONSUM
+ {0xABE3, 0xABE4, prN}, // Mc [2] MEETEI MAYEK VOWEL SIGN ONAP..MEETEI MAYEK VOWEL SIGN INAP
+ {0xABE5, 0xABE5, prN}, // Mn MEETEI MAYEK VOWEL SIGN ANAP
+ {0xABE6, 0xABE7, prN}, // Mc [2] MEETEI MAYEK VOWEL SIGN YENAP..MEETEI MAYEK VOWEL SIGN SOUNAP
+ {0xABE8, 0xABE8, prN}, // Mn MEETEI MAYEK VOWEL SIGN UNAP
+ {0xABE9, 0xABEA, prN}, // Mc [2] MEETEI MAYEK VOWEL SIGN CHEINAP..MEETEI MAYEK VOWEL SIGN NUNG
+ {0xABEB, 0xABEB, prN}, // Po MEETEI MAYEK CHEIKHEI
+ {0xABEC, 0xABEC, prN}, // Mc MEETEI MAYEK LUM IYEK
+ {0xABED, 0xABED, prN}, // Mn MEETEI MAYEK APUN IYEK
+ {0xABF0, 0xABF9, prN}, // Nd [10] MEETEI MAYEK DIGIT ZERO..MEETEI MAYEK DIGIT NINE
+ {0xAC00, 0xD7A3, prW}, // Lo [11172] HANGUL SYLLABLE GA..HANGUL SYLLABLE HIH
+ {0xD7B0, 0xD7C6, prN}, // Lo [23] HANGUL JUNGSEONG O-YEO..HANGUL JUNGSEONG ARAEA-E
+ {0xD7CB, 0xD7FB, prN}, // Lo [49] HANGUL JONGSEONG NIEUN-RIEUL..HANGUL JONGSEONG PHIEUPH-THIEUTH
+ {0xD800, 0xDB7F, prN}, // Cs [896] ..
+ {0xDB80, 0xDBFF, prN}, // Cs [128] ..
+ {0xDC00, 0xDFFF, prN}, // Cs [1024] ..
+ {0xE000, 0xF8FF, prA}, // Co [6400] ..
+ {0xF900, 0xFA6D, prW}, // Lo [366] CJK COMPATIBILITY IDEOGRAPH-F900..CJK COMPATIBILITY IDEOGRAPH-FA6D
+ {0xFA6E, 0xFA6F, prW}, // Cn [2] ..
+ {0xFA70, 0xFAD9, prW}, // Lo [106] CJK COMPATIBILITY IDEOGRAPH-FA70..CJK COMPATIBILITY IDEOGRAPH-FAD9
+ {0xFADA, 0xFAFF, prW}, // Cn [38] ..
+ {0xFB00, 0xFB06, prN}, // Ll [7] LATIN SMALL LIGATURE FF..LATIN SMALL LIGATURE ST
+ {0xFB13, 0xFB17, prN}, // Ll [5] ARMENIAN SMALL LIGATURE MEN NOW..ARMENIAN SMALL LIGATURE MEN XEH
+ {0xFB1D, 0xFB1D, prN}, // Lo HEBREW LETTER YOD WITH HIRIQ
+ {0xFB1E, 0xFB1E, prN}, // Mn HEBREW POINT JUDEO-SPANISH VARIKA
+ {0xFB1F, 0xFB28, prN}, // Lo [10] HEBREW LIGATURE YIDDISH YOD YOD PATAH..HEBREW LETTER WIDE TAV
+ {0xFB29, 0xFB29, prN}, // Sm HEBREW LETTER ALTERNATIVE PLUS SIGN
+ {0xFB2A, 0xFB36, prN}, // Lo [13] HEBREW LETTER SHIN WITH SHIN DOT..HEBREW LETTER ZAYIN WITH DAGESH
+ {0xFB38, 0xFB3C, prN}, // Lo [5] HEBREW LETTER TET WITH DAGESH..HEBREW LETTER LAMED WITH DAGESH
+ {0xFB3E, 0xFB3E, prN}, // Lo HEBREW LETTER MEM WITH DAGESH
+ {0xFB40, 0xFB41, prN}, // Lo [2] HEBREW LETTER NUN WITH DAGESH..HEBREW LETTER SAMEKH WITH DAGESH
+ {0xFB43, 0xFB44, prN}, // Lo [2] HEBREW LETTER FINAL PE WITH DAGESH..HEBREW LETTER PE WITH DAGESH
+ {0xFB46, 0xFB4F, prN}, // Lo [10] HEBREW LETTER TSADI WITH DAGESH..HEBREW LIGATURE ALEF LAMED
+ {0xFB50, 0xFBB1, prN}, // Lo [98] ARABIC LETTER ALEF WASLA ISOLATED FORM..ARABIC LETTER YEH BARREE WITH HAMZA ABOVE FINAL FORM
+ {0xFBB2, 0xFBC2, prN}, // Sk [17] ARABIC SYMBOL DOT ABOVE..ARABIC SYMBOL WASLA ABOVE
+ {0xFBD3, 0xFD3D, prN}, // Lo [363] ARABIC LETTER NG ISOLATED FORM..ARABIC LIGATURE ALEF WITH FATHATAN ISOLATED FORM
+ {0xFD3E, 0xFD3E, prN}, // Pe ORNATE LEFT PARENTHESIS
+ {0xFD3F, 0xFD3F, prN}, // Ps ORNATE RIGHT PARENTHESIS
+ {0xFD40, 0xFD4F, prN}, // So [16] ARABIC LIGATURE RAHIMAHU ALLAAH..ARABIC LIGATURE RAHIMAHUM ALLAAH
+ {0xFD50, 0xFD8F, prN}, // Lo [64] ARABIC LIGATURE TEH WITH JEEM WITH MEEM INITIAL FORM..ARABIC LIGATURE MEEM WITH KHAH WITH MEEM INITIAL FORM
+ {0xFD92, 0xFDC7, prN}, // Lo [54] ARABIC LIGATURE MEEM WITH JEEM WITH KHAH INITIAL FORM..ARABIC LIGATURE NOON WITH JEEM WITH YEH FINAL FORM
+ {0xFDCF, 0xFDCF, prN}, // So ARABIC LIGATURE SALAAMUHU ALAYNAA
+ {0xFDF0, 0xFDFB, prN}, // Lo [12] ARABIC LIGATURE SALLA USED AS KORANIC STOP SIGN ISOLATED FORM..ARABIC LIGATURE JALLAJALALOUHOU
+ {0xFDFC, 0xFDFC, prN}, // Sc RIAL SIGN
+ {0xFDFD, 0xFDFF, prN}, // So [3] ARABIC LIGATURE BISMILLAH AR-RAHMAN AR-RAHEEM..ARABIC LIGATURE AZZA WA JALL
+ {0xFE00, 0xFE0F, prA}, // Mn [16] VARIATION SELECTOR-1..VARIATION SELECTOR-16
+ {0xFE10, 0xFE16, prW}, // Po [7] PRESENTATION FORM FOR VERTICAL COMMA..PRESENTATION FORM FOR VERTICAL QUESTION MARK
+ {0xFE17, 0xFE17, prW}, // Ps PRESENTATION FORM FOR VERTICAL LEFT WHITE LENTICULAR BRACKET
+ {0xFE18, 0xFE18, prW}, // Pe PRESENTATION FORM FOR VERTICAL RIGHT WHITE LENTICULAR BRAKCET
+ {0xFE19, 0xFE19, prW}, // Po PRESENTATION FORM FOR VERTICAL HORIZONTAL ELLIPSIS
+ {0xFE20, 0xFE2F, prN}, // Mn [16] COMBINING LIGATURE LEFT HALF..COMBINING CYRILLIC TITLO RIGHT HALF
+ {0xFE30, 0xFE30, prW}, // Po PRESENTATION FORM FOR VERTICAL TWO DOT LEADER
+ {0xFE31, 0xFE32, prW}, // Pd [2] PRESENTATION FORM FOR VERTICAL EM DASH..PRESENTATION FORM FOR VERTICAL EN DASH
+ {0xFE33, 0xFE34, prW}, // Pc [2] PRESENTATION FORM FOR VERTICAL LOW LINE..PRESENTATION FORM FOR VERTICAL WAVY LOW LINE
+ {0xFE35, 0xFE35, prW}, // Ps PRESENTATION FORM FOR VERTICAL LEFT PARENTHESIS
+ {0xFE36, 0xFE36, prW}, // Pe PRESENTATION FORM FOR VERTICAL RIGHT PARENTHESIS
+ {0xFE37, 0xFE37, prW}, // Ps PRESENTATION FORM FOR VERTICAL LEFT CURLY BRACKET
+ {0xFE38, 0xFE38, prW}, // Pe PRESENTATION FORM FOR VERTICAL RIGHT CURLY BRACKET
+ {0xFE39, 0xFE39, prW}, // Ps PRESENTATION FORM FOR VERTICAL LEFT TORTOISE SHELL BRACKET
+ {0xFE3A, 0xFE3A, prW}, // Pe PRESENTATION FORM FOR VERTICAL RIGHT TORTOISE SHELL BRACKET
+ {0xFE3B, 0xFE3B, prW}, // Ps PRESENTATION FORM FOR VERTICAL LEFT BLACK LENTICULAR BRACKET
+ {0xFE3C, 0xFE3C, prW}, // Pe PRESENTATION FORM FOR VERTICAL RIGHT BLACK LENTICULAR BRACKET
+ {0xFE3D, 0xFE3D, prW}, // Ps PRESENTATION FORM FOR VERTICAL LEFT DOUBLE ANGLE BRACKET
+ {0xFE3E, 0xFE3E, prW}, // Pe PRESENTATION FORM FOR VERTICAL RIGHT DOUBLE ANGLE BRACKET
+ {0xFE3F, 0xFE3F, prW}, // Ps PRESENTATION FORM FOR VERTICAL LEFT ANGLE BRACKET
+ {0xFE40, 0xFE40, prW}, // Pe PRESENTATION FORM FOR VERTICAL RIGHT ANGLE BRACKET
+ {0xFE41, 0xFE41, prW}, // Ps PRESENTATION FORM FOR VERTICAL LEFT CORNER BRACKET
+ {0xFE42, 0xFE42, prW}, // Pe PRESENTATION FORM FOR VERTICAL RIGHT CORNER BRACKET
+ {0xFE43, 0xFE43, prW}, // Ps PRESENTATION FORM FOR VERTICAL LEFT WHITE CORNER BRACKET
+ {0xFE44, 0xFE44, prW}, // Pe PRESENTATION FORM FOR VERTICAL RIGHT WHITE CORNER BRACKET
+ {0xFE45, 0xFE46, prW}, // Po [2] SESAME DOT..WHITE SESAME DOT
+ {0xFE47, 0xFE47, prW}, // Ps PRESENTATION FORM FOR VERTICAL LEFT SQUARE BRACKET
+ {0xFE48, 0xFE48, prW}, // Pe PRESENTATION FORM FOR VERTICAL RIGHT SQUARE BRACKET
+ {0xFE49, 0xFE4C, prW}, // Po [4] DASHED OVERLINE..DOUBLE WAVY OVERLINE
+ {0xFE4D, 0xFE4F, prW}, // Pc [3] DASHED LOW LINE..WAVY LOW LINE
+ {0xFE50, 0xFE52, prW}, // Po [3] SMALL COMMA..SMALL FULL STOP
+ {0xFE54, 0xFE57, prW}, // Po [4] SMALL SEMICOLON..SMALL EXCLAMATION MARK
+ {0xFE58, 0xFE58, prW}, // Pd SMALL EM DASH
+ {0xFE59, 0xFE59, prW}, // Ps SMALL LEFT PARENTHESIS
+ {0xFE5A, 0xFE5A, prW}, // Pe SMALL RIGHT PARENTHESIS
+ {0xFE5B, 0xFE5B, prW}, // Ps SMALL LEFT CURLY BRACKET
+ {0xFE5C, 0xFE5C, prW}, // Pe SMALL RIGHT CURLY BRACKET
+ {0xFE5D, 0xFE5D, prW}, // Ps SMALL LEFT TORTOISE SHELL BRACKET
+ {0xFE5E, 0xFE5E, prW}, // Pe SMALL RIGHT TORTOISE SHELL BRACKET
+ {0xFE5F, 0xFE61, prW}, // Po [3] SMALL NUMBER SIGN..SMALL ASTERISK
+ {0xFE62, 0xFE62, prW}, // Sm SMALL PLUS SIGN
+ {0xFE63, 0xFE63, prW}, // Pd SMALL HYPHEN-MINUS
+ {0xFE64, 0xFE66, prW}, // Sm [3] SMALL LESS-THAN SIGN..SMALL EQUALS SIGN
+ {0xFE68, 0xFE68, prW}, // Po SMALL REVERSE SOLIDUS
+ {0xFE69, 0xFE69, prW}, // Sc SMALL DOLLAR SIGN
+ {0xFE6A, 0xFE6B, prW}, // Po [2] SMALL PERCENT SIGN..SMALL COMMERCIAL AT
+ {0xFE70, 0xFE74, prN}, // Lo [5] ARABIC FATHATAN ISOLATED FORM..ARABIC KASRATAN ISOLATED FORM
+ {0xFE76, 0xFEFC, prN}, // Lo [135] ARABIC FATHA ISOLATED FORM..ARABIC LIGATURE LAM WITH ALEF FINAL FORM
+ {0xFEFF, 0xFEFF, prN}, // Cf ZERO WIDTH NO-BREAK SPACE
+ {0xFF01, 0xFF03, prF}, // Po [3] FULLWIDTH EXCLAMATION MARK..FULLWIDTH NUMBER SIGN
+ {0xFF04, 0xFF04, prF}, // Sc FULLWIDTH DOLLAR SIGN
+ {0xFF05, 0xFF07, prF}, // Po [3] FULLWIDTH PERCENT SIGN..FULLWIDTH APOSTROPHE
+ {0xFF08, 0xFF08, prF}, // Ps FULLWIDTH LEFT PARENTHESIS
+ {0xFF09, 0xFF09, prF}, // Pe FULLWIDTH RIGHT PARENTHESIS
+ {0xFF0A, 0xFF0A, prF}, // Po FULLWIDTH ASTERISK
+ {0xFF0B, 0xFF0B, prF}, // Sm FULLWIDTH PLUS SIGN
+ {0xFF0C, 0xFF0C, prF}, // Po FULLWIDTH COMMA
+ {0xFF0D, 0xFF0D, prF}, // Pd FULLWIDTH HYPHEN-MINUS
+ {0xFF0E, 0xFF0F, prF}, // Po [2] FULLWIDTH FULL STOP..FULLWIDTH SOLIDUS
+ {0xFF10, 0xFF19, prF}, // Nd [10] FULLWIDTH DIGIT ZERO..FULLWIDTH DIGIT NINE
+ {0xFF1A, 0xFF1B, prF}, // Po [2] FULLWIDTH COLON..FULLWIDTH SEMICOLON
+ {0xFF1C, 0xFF1E, prF}, // Sm [3] FULLWIDTH LESS-THAN SIGN..FULLWIDTH GREATER-THAN SIGN
+ {0xFF1F, 0xFF20, prF}, // Po [2] FULLWIDTH QUESTION MARK..FULLWIDTH COMMERCIAL AT
+ {0xFF21, 0xFF3A, prF}, // Lu [26] FULLWIDTH LATIN CAPITAL LETTER A..FULLWIDTH LATIN CAPITAL LETTER Z
+ {0xFF3B, 0xFF3B, prF}, // Ps FULLWIDTH LEFT SQUARE BRACKET
+ {0xFF3C, 0xFF3C, prF}, // Po FULLWIDTH REVERSE SOLIDUS
+ {0xFF3D, 0xFF3D, prF}, // Pe FULLWIDTH RIGHT SQUARE BRACKET
+ {0xFF3E, 0xFF3E, prF}, // Sk FULLWIDTH CIRCUMFLEX ACCENT
+ {0xFF3F, 0xFF3F, prF}, // Pc FULLWIDTH LOW LINE
+ {0xFF40, 0xFF40, prF}, // Sk FULLWIDTH GRAVE ACCENT
+ {0xFF41, 0xFF5A, prF}, // Ll [26] FULLWIDTH LATIN SMALL LETTER A..FULLWIDTH LATIN SMALL LETTER Z
+ {0xFF5B, 0xFF5B, prF}, // Ps FULLWIDTH LEFT CURLY BRACKET
+ {0xFF5C, 0xFF5C, prF}, // Sm FULLWIDTH VERTICAL LINE
+ {0xFF5D, 0xFF5D, prF}, // Pe FULLWIDTH RIGHT CURLY BRACKET
+ {0xFF5E, 0xFF5E, prF}, // Sm FULLWIDTH TILDE
+ {0xFF5F, 0xFF5F, prF}, // Ps FULLWIDTH LEFT WHITE PARENTHESIS
+ {0xFF60, 0xFF60, prF}, // Pe FULLWIDTH RIGHT WHITE PARENTHESIS
+ {0xFF61, 0xFF61, prH}, // Po HALFWIDTH IDEOGRAPHIC FULL STOP
+ {0xFF62, 0xFF62, prH}, // Ps HALFWIDTH LEFT CORNER BRACKET
+ {0xFF63, 0xFF63, prH}, // Pe HALFWIDTH RIGHT CORNER BRACKET
+ {0xFF64, 0xFF65, prH}, // Po [2] HALFWIDTH IDEOGRAPHIC COMMA..HALFWIDTH KATAKANA MIDDLE DOT
+ {0xFF66, 0xFF6F, prH}, // Lo [10] HALFWIDTH KATAKANA LETTER WO..HALFWIDTH KATAKANA LETTER SMALL TU
+ {0xFF70, 0xFF70, prH}, // Lm HALFWIDTH KATAKANA-HIRAGANA PROLONGED SOUND MARK
+ {0xFF71, 0xFF9D, prH}, // Lo [45] HALFWIDTH KATAKANA LETTER A..HALFWIDTH KATAKANA LETTER N
+ {0xFF9E, 0xFF9F, prH}, // Lm [2] HALFWIDTH KATAKANA VOICED SOUND MARK..HALFWIDTH KATAKANA SEMI-VOICED SOUND MARK
+ {0xFFA0, 0xFFBE, prH}, // Lo [31] HALFWIDTH HANGUL FILLER..HALFWIDTH HANGUL LETTER HIEUH
+ {0xFFC2, 0xFFC7, prH}, // Lo [6] HALFWIDTH HANGUL LETTER A..HALFWIDTH HANGUL LETTER E
+ {0xFFCA, 0xFFCF, prH}, // Lo [6] HALFWIDTH HANGUL LETTER YEO..HALFWIDTH HANGUL LETTER OE
+ {0xFFD2, 0xFFD7, prH}, // Lo [6] HALFWIDTH HANGUL LETTER YO..HALFWIDTH HANGUL LETTER YU
+ {0xFFDA, 0xFFDC, prH}, // Lo [3] HALFWIDTH HANGUL LETTER EU..HALFWIDTH HANGUL LETTER I
+ {0xFFE0, 0xFFE1, prF}, // Sc [2] FULLWIDTH CENT SIGN..FULLWIDTH POUND SIGN
+ {0xFFE2, 0xFFE2, prF}, // Sm FULLWIDTH NOT SIGN
+ {0xFFE3, 0xFFE3, prF}, // Sk FULLWIDTH MACRON
+ {0xFFE4, 0xFFE4, prF}, // So FULLWIDTH BROKEN BAR
+ {0xFFE5, 0xFFE6, prF}, // Sc [2] FULLWIDTH YEN SIGN..FULLWIDTH WON SIGN
+ {0xFFE8, 0xFFE8, prH}, // So HALFWIDTH FORMS LIGHT VERTICAL
+ {0xFFE9, 0xFFEC, prH}, // Sm [4] HALFWIDTH LEFTWARDS ARROW..HALFWIDTH DOWNWARDS ARROW
+ {0xFFED, 0xFFEE, prH}, // So [2] HALFWIDTH BLACK SQUARE..HALFWIDTH WHITE CIRCLE
+ {0xFFF9, 0xFFFB, prN}, // Cf [3] INTERLINEAR ANNOTATION ANCHOR..INTERLINEAR ANNOTATION TERMINATOR
+ {0xFFFC, 0xFFFC, prN}, // So OBJECT REPLACEMENT CHARACTER
+ {0xFFFD, 0xFFFD, prA}, // So REPLACEMENT CHARACTER
+ {0x10000, 0x1000B, prN}, // Lo [12] LINEAR B SYLLABLE B008 A..LINEAR B SYLLABLE B046 JE
+ {0x1000D, 0x10026, prN}, // Lo [26] LINEAR B SYLLABLE B036 JO..LINEAR B SYLLABLE B032 QO
+ {0x10028, 0x1003A, prN}, // Lo [19] LINEAR B SYLLABLE B060 RA..LINEAR B SYLLABLE B042 WO
+ {0x1003C, 0x1003D, prN}, // Lo [2] LINEAR B SYLLABLE B017 ZA..LINEAR B SYLLABLE B074 ZE
+ {0x1003F, 0x1004D, prN}, // Lo [15] LINEAR B SYLLABLE B020 ZO..LINEAR B SYLLABLE B091 TWO
+ {0x10050, 0x1005D, prN}, // Lo [14] LINEAR B SYMBOL B018..LINEAR B SYMBOL B089
+ {0x10080, 0x100FA, prN}, // Lo [123] LINEAR B IDEOGRAM B100 MAN..LINEAR B IDEOGRAM VESSEL B305
+ {0x10100, 0x10102, prN}, // Po [3] AEGEAN WORD SEPARATOR LINE..AEGEAN CHECK MARK
+ {0x10107, 0x10133, prN}, // No [45] AEGEAN NUMBER ONE..AEGEAN NUMBER NINETY THOUSAND
+ {0x10137, 0x1013F, prN}, // So [9] AEGEAN WEIGHT BASE UNIT..AEGEAN MEASURE THIRD SUBUNIT
+ {0x10140, 0x10174, prN}, // Nl [53] GREEK ACROPHONIC ATTIC ONE QUARTER..GREEK ACROPHONIC STRATIAN FIFTY MNAS
+ {0x10175, 0x10178, prN}, // No [4] GREEK ONE HALF SIGN..GREEK THREE QUARTERS SIGN
+ {0x10179, 0x10189, prN}, // So [17] GREEK YEAR SIGN..GREEK TRYBLION BASE SIGN
+ {0x1018A, 0x1018B, prN}, // No [2] GREEK ZERO SIGN..GREEK ONE QUARTER SIGN
+ {0x1018C, 0x1018E, prN}, // So [3] GREEK SINUSOID SIGN..NOMISMA SIGN
+ {0x10190, 0x1019C, prN}, // So [13] ROMAN SEXTANS SIGN..ASCIA SYMBOL
+ {0x101A0, 0x101A0, prN}, // So GREEK SYMBOL TAU RHO
+ {0x101D0, 0x101FC, prN}, // So [45] PHAISTOS DISC SIGN PEDESTRIAN..PHAISTOS DISC SIGN WAVY BAND
+ {0x101FD, 0x101FD, prN}, // Mn PHAISTOS DISC SIGN COMBINING OBLIQUE STROKE
+ {0x10280, 0x1029C, prN}, // Lo [29] LYCIAN LETTER A..LYCIAN LETTER X
+ {0x102A0, 0x102D0, prN}, // Lo [49] CARIAN LETTER A..CARIAN LETTER UUU3
+ {0x102E0, 0x102E0, prN}, // Mn COPTIC EPACT THOUSANDS MARK
+ {0x102E1, 0x102FB, prN}, // No [27] COPTIC EPACT DIGIT ONE..COPTIC EPACT NUMBER NINE HUNDRED
+ {0x10300, 0x1031F, prN}, // Lo [32] OLD ITALIC LETTER A..OLD ITALIC LETTER ESS
+ {0x10320, 0x10323, prN}, // No [4] OLD ITALIC NUMERAL ONE..OLD ITALIC NUMERAL FIFTY
+ {0x1032D, 0x1032F, prN}, // Lo [3] OLD ITALIC LETTER YE..OLD ITALIC LETTER SOUTHERN TSE
+ {0x10330, 0x10340, prN}, // Lo [17] GOTHIC LETTER AHSA..GOTHIC LETTER PAIRTHRA
+ {0x10341, 0x10341, prN}, // Nl GOTHIC LETTER NINETY
+ {0x10342, 0x10349, prN}, // Lo [8] GOTHIC LETTER RAIDA..GOTHIC LETTER OTHAL
+ {0x1034A, 0x1034A, prN}, // Nl GOTHIC LETTER NINE HUNDRED
+ {0x10350, 0x10375, prN}, // Lo [38] OLD PERMIC LETTER AN..OLD PERMIC LETTER IA
+ {0x10376, 0x1037A, prN}, // Mn [5] COMBINING OLD PERMIC LETTER AN..COMBINING OLD PERMIC LETTER SII
+ {0x10380, 0x1039D, prN}, // Lo [30] UGARITIC LETTER ALPA..UGARITIC LETTER SSU
+ {0x1039F, 0x1039F, prN}, // Po UGARITIC WORD DIVIDER
+ {0x103A0, 0x103C3, prN}, // Lo [36] OLD PERSIAN SIGN A..OLD PERSIAN SIGN HA
+ {0x103C8, 0x103CF, prN}, // Lo [8] OLD PERSIAN SIGN AURAMAZDAA..OLD PERSIAN SIGN BUUMISH
+ {0x103D0, 0x103D0, prN}, // Po OLD PERSIAN WORD DIVIDER
+ {0x103D1, 0x103D5, prN}, // Nl [5] OLD PERSIAN NUMBER ONE..OLD PERSIAN NUMBER HUNDRED
+ {0x10400, 0x1044F, prN}, // L& [80] DESERET CAPITAL LETTER LONG I..DESERET SMALL LETTER EW
+ {0x10450, 0x1047F, prN}, // Lo [48] SHAVIAN LETTER PEEP..SHAVIAN LETTER YEW
+ {0x10480, 0x1049D, prN}, // Lo [30] OSMANYA LETTER ALEF..OSMANYA LETTER OO
+ {0x104A0, 0x104A9, prN}, // Nd [10] OSMANYA DIGIT ZERO..OSMANYA DIGIT NINE
+ {0x104B0, 0x104D3, prN}, // Lu [36] OSAGE CAPITAL LETTER A..OSAGE CAPITAL LETTER ZHA
+ {0x104D8, 0x104FB, prN}, // Ll [36] OSAGE SMALL LETTER A..OSAGE SMALL LETTER ZHA
+ {0x10500, 0x10527, prN}, // Lo [40] ELBASAN LETTER A..ELBASAN LETTER KHE
+ {0x10530, 0x10563, prN}, // Lo [52] CAUCASIAN ALBANIAN LETTER ALT..CAUCASIAN ALBANIAN LETTER KIW
+ {0x1056F, 0x1056F, prN}, // Po CAUCASIAN ALBANIAN CITATION MARK
+ {0x10570, 0x1057A, prN}, // Lu [11] VITHKUQI CAPITAL LETTER A..VITHKUQI CAPITAL LETTER GA
+ {0x1057C, 0x1058A, prN}, // Lu [15] VITHKUQI CAPITAL LETTER HA..VITHKUQI CAPITAL LETTER RE
+ {0x1058C, 0x10592, prN}, // Lu [7] VITHKUQI CAPITAL LETTER SE..VITHKUQI CAPITAL LETTER XE
+ {0x10594, 0x10595, prN}, // Lu [2] VITHKUQI CAPITAL LETTER Y..VITHKUQI CAPITAL LETTER ZE
+ {0x10597, 0x105A1, prN}, // Ll [11] VITHKUQI SMALL LETTER A..VITHKUQI SMALL LETTER GA
+ {0x105A3, 0x105B1, prN}, // Ll [15] VITHKUQI SMALL LETTER HA..VITHKUQI SMALL LETTER RE
+ {0x105B3, 0x105B9, prN}, // Ll [7] VITHKUQI SMALL LETTER SE..VITHKUQI SMALL LETTER XE
+ {0x105BB, 0x105BC, prN}, // Ll [2] VITHKUQI SMALL LETTER Y..VITHKUQI SMALL LETTER ZE
+ {0x10600, 0x10736, prN}, // Lo [311] LINEAR A SIGN AB001..LINEAR A SIGN A664
+ {0x10740, 0x10755, prN}, // Lo [22] LINEAR A SIGN A701 A..LINEAR A SIGN A732 JE
+ {0x10760, 0x10767, prN}, // Lo [8] LINEAR A SIGN A800..LINEAR A SIGN A807
+ {0x10780, 0x10785, prN}, // Lm [6] MODIFIER LETTER SMALL CAPITAL AA..MODIFIER LETTER SMALL B WITH HOOK
+ {0x10787, 0x107B0, prN}, // Lm [42] MODIFIER LETTER SMALL DZ DIGRAPH..MODIFIER LETTER SMALL V WITH RIGHT HOOK
+ {0x107B2, 0x107BA, prN}, // Lm [9] MODIFIER LETTER SMALL CAPITAL Y..MODIFIER LETTER SMALL S WITH CURL
+ {0x10800, 0x10805, prN}, // Lo [6] CYPRIOT SYLLABLE A..CYPRIOT SYLLABLE JA
+ {0x10808, 0x10808, prN}, // Lo CYPRIOT SYLLABLE JO
+ {0x1080A, 0x10835, prN}, // Lo [44] CYPRIOT SYLLABLE KA..CYPRIOT SYLLABLE WO
+ {0x10837, 0x10838, prN}, // Lo [2] CYPRIOT SYLLABLE XA..CYPRIOT SYLLABLE XE
+ {0x1083C, 0x1083C, prN}, // Lo CYPRIOT SYLLABLE ZA
+ {0x1083F, 0x1083F, prN}, // Lo CYPRIOT SYLLABLE ZO
+ {0x10840, 0x10855, prN}, // Lo [22] IMPERIAL ARAMAIC LETTER ALEPH..IMPERIAL ARAMAIC LETTER TAW
+ {0x10857, 0x10857, prN}, // Po IMPERIAL ARAMAIC SECTION SIGN
+ {0x10858, 0x1085F, prN}, // No [8] IMPERIAL ARAMAIC NUMBER ONE..IMPERIAL ARAMAIC NUMBER TEN THOUSAND
+ {0x10860, 0x10876, prN}, // Lo [23] PALMYRENE LETTER ALEPH..PALMYRENE LETTER TAW
+ {0x10877, 0x10878, prN}, // So [2] PALMYRENE LEFT-POINTING FLEURON..PALMYRENE RIGHT-POINTING FLEURON
+ {0x10879, 0x1087F, prN}, // No [7] PALMYRENE NUMBER ONE..PALMYRENE NUMBER TWENTY
+ {0x10880, 0x1089E, prN}, // Lo [31] NABATAEAN LETTER FINAL ALEPH..NABATAEAN LETTER TAW
+ {0x108A7, 0x108AF, prN}, // No [9] NABATAEAN NUMBER ONE..NABATAEAN NUMBER ONE HUNDRED
+ {0x108E0, 0x108F2, prN}, // Lo [19] HATRAN LETTER ALEPH..HATRAN LETTER QOPH
+ {0x108F4, 0x108F5, prN}, // Lo [2] HATRAN LETTER SHIN..HATRAN LETTER TAW
+ {0x108FB, 0x108FF, prN}, // No [5] HATRAN NUMBER ONE..HATRAN NUMBER ONE HUNDRED
+ {0x10900, 0x10915, prN}, // Lo [22] PHOENICIAN LETTER ALF..PHOENICIAN LETTER TAU
+ {0x10916, 0x1091B, prN}, // No [6] PHOENICIAN NUMBER ONE..PHOENICIAN NUMBER THREE
+ {0x1091F, 0x1091F, prN}, // Po PHOENICIAN WORD SEPARATOR
+ {0x10920, 0x10939, prN}, // Lo [26] LYDIAN LETTER A..LYDIAN LETTER C
+ {0x1093F, 0x1093F, prN}, // Po LYDIAN TRIANGULAR MARK
+ {0x10980, 0x1099F, prN}, // Lo [32] MEROITIC HIEROGLYPHIC LETTER A..MEROITIC HIEROGLYPHIC SYMBOL VIDJ-2
+ {0x109A0, 0x109B7, prN}, // Lo [24] MEROITIC CURSIVE LETTER A..MEROITIC CURSIVE LETTER DA
+ {0x109BC, 0x109BD, prN}, // No [2] MEROITIC CURSIVE FRACTION ELEVEN TWELFTHS..MEROITIC CURSIVE FRACTION ONE HALF
+ {0x109BE, 0x109BF, prN}, // Lo [2] MEROITIC CURSIVE LOGOGRAM RMT..MEROITIC CURSIVE LOGOGRAM IMN
+ {0x109C0, 0x109CF, prN}, // No [16] MEROITIC CURSIVE NUMBER ONE..MEROITIC CURSIVE NUMBER SEVENTY
+ {0x109D2, 0x109FF, prN}, // No [46] MEROITIC CURSIVE NUMBER ONE HUNDRED..MEROITIC CURSIVE FRACTION TEN TWELFTHS
+ {0x10A00, 0x10A00, prN}, // Lo KHAROSHTHI LETTER A
+ {0x10A01, 0x10A03, prN}, // Mn [3] KHAROSHTHI VOWEL SIGN I..KHAROSHTHI VOWEL SIGN VOCALIC R
+ {0x10A05, 0x10A06, prN}, // Mn [2] KHAROSHTHI VOWEL SIGN E..KHAROSHTHI VOWEL SIGN O
+ {0x10A0C, 0x10A0F, prN}, // Mn [4] KHAROSHTHI VOWEL LENGTH MARK..KHAROSHTHI SIGN VISARGA
+ {0x10A10, 0x10A13, prN}, // Lo [4] KHAROSHTHI LETTER KA..KHAROSHTHI LETTER GHA
+ {0x10A15, 0x10A17, prN}, // Lo [3] KHAROSHTHI LETTER CA..KHAROSHTHI LETTER JA
+ {0x10A19, 0x10A35, prN}, // Lo [29] KHAROSHTHI LETTER NYA..KHAROSHTHI LETTER VHA
+ {0x10A38, 0x10A3A, prN}, // Mn [3] KHAROSHTHI SIGN BAR ABOVE..KHAROSHTHI SIGN DOT BELOW
+ {0x10A3F, 0x10A3F, prN}, // Mn KHAROSHTHI VIRAMA
+ {0x10A40, 0x10A48, prN}, // No [9] KHAROSHTHI DIGIT ONE..KHAROSHTHI FRACTION ONE HALF
+ {0x10A50, 0x10A58, prN}, // Po [9] KHAROSHTHI PUNCTUATION DOT..KHAROSHTHI PUNCTUATION LINES
+ {0x10A60, 0x10A7C, prN}, // Lo [29] OLD SOUTH ARABIAN LETTER HE..OLD SOUTH ARABIAN LETTER THETH
+ {0x10A7D, 0x10A7E, prN}, // No [2] OLD SOUTH ARABIAN NUMBER ONE..OLD SOUTH ARABIAN NUMBER FIFTY
+ {0x10A7F, 0x10A7F, prN}, // Po OLD SOUTH ARABIAN NUMERIC INDICATOR
+ {0x10A80, 0x10A9C, prN}, // Lo [29] OLD NORTH ARABIAN LETTER HEH..OLD NORTH ARABIAN LETTER ZAH
+ {0x10A9D, 0x10A9F, prN}, // No [3] OLD NORTH ARABIAN NUMBER ONE..OLD NORTH ARABIAN NUMBER TWENTY
+ {0x10AC0, 0x10AC7, prN}, // Lo [8] MANICHAEAN LETTER ALEPH..MANICHAEAN LETTER WAW
+ {0x10AC8, 0x10AC8, prN}, // So MANICHAEAN SIGN UD
+ {0x10AC9, 0x10AE4, prN}, // Lo [28] MANICHAEAN LETTER ZAYIN..MANICHAEAN LETTER TAW
+ {0x10AE5, 0x10AE6, prN}, // Mn [2] MANICHAEAN ABBREVIATION MARK ABOVE..MANICHAEAN ABBREVIATION MARK BELOW
+ {0x10AEB, 0x10AEF, prN}, // No [5] MANICHAEAN NUMBER ONE..MANICHAEAN NUMBER ONE HUNDRED
+ {0x10AF0, 0x10AF6, prN}, // Po [7] MANICHAEAN PUNCTUATION STAR..MANICHAEAN PUNCTUATION LINE FILLER
+ {0x10B00, 0x10B35, prN}, // Lo [54] AVESTAN LETTER A..AVESTAN LETTER HE
+ {0x10B39, 0x10B3F, prN}, // Po [7] AVESTAN ABBREVIATION MARK..LARGE ONE RING OVER TWO RINGS PUNCTUATION
+ {0x10B40, 0x10B55, prN}, // Lo [22] INSCRIPTIONAL PARTHIAN LETTER ALEPH..INSCRIPTIONAL PARTHIAN LETTER TAW
+ {0x10B58, 0x10B5F, prN}, // No [8] INSCRIPTIONAL PARTHIAN NUMBER ONE..INSCRIPTIONAL PARTHIAN NUMBER ONE THOUSAND
+ {0x10B60, 0x10B72, prN}, // Lo [19] INSCRIPTIONAL PAHLAVI LETTER ALEPH..INSCRIPTIONAL PAHLAVI LETTER TAW
+ {0x10B78, 0x10B7F, prN}, // No [8] INSCRIPTIONAL PAHLAVI NUMBER ONE..INSCRIPTIONAL PAHLAVI NUMBER ONE THOUSAND
+ {0x10B80, 0x10B91, prN}, // Lo [18] PSALTER PAHLAVI LETTER ALEPH..PSALTER PAHLAVI LETTER TAW
+ {0x10B99, 0x10B9C, prN}, // Po [4] PSALTER PAHLAVI SECTION MARK..PSALTER PAHLAVI FOUR DOTS WITH DOT
+ {0x10BA9, 0x10BAF, prN}, // No [7] PSALTER PAHLAVI NUMBER ONE..PSALTER PAHLAVI NUMBER ONE HUNDRED
+ {0x10C00, 0x10C48, prN}, // Lo [73] OLD TURKIC LETTER ORKHON A..OLD TURKIC LETTER ORKHON BASH
+ {0x10C80, 0x10CB2, prN}, // Lu [51] OLD HUNGARIAN CAPITAL LETTER A..OLD HUNGARIAN CAPITAL LETTER US
+ {0x10CC0, 0x10CF2, prN}, // Ll [51] OLD HUNGARIAN SMALL LETTER A..OLD HUNGARIAN SMALL LETTER US
+ {0x10CFA, 0x10CFF, prN}, // No [6] OLD HUNGARIAN NUMBER ONE..OLD HUNGARIAN NUMBER ONE THOUSAND
+ {0x10D00, 0x10D23, prN}, // Lo [36] HANIFI ROHINGYA LETTER A..HANIFI ROHINGYA MARK NA KHONNA
+ {0x10D24, 0x10D27, prN}, // Mn [4] HANIFI ROHINGYA SIGN HARBAHAY..HANIFI ROHINGYA SIGN TASSI
+ {0x10D30, 0x10D39, prN}, // Nd [10] HANIFI ROHINGYA DIGIT ZERO..HANIFI ROHINGYA DIGIT NINE
+ {0x10E60, 0x10E7E, prN}, // No [31] RUMI DIGIT ONE..RUMI FRACTION TWO THIRDS
+ {0x10E80, 0x10EA9, prN}, // Lo [42] YEZIDI LETTER ELIF..YEZIDI LETTER ET
+ {0x10EAB, 0x10EAC, prN}, // Mn [2] YEZIDI COMBINING HAMZA MARK..YEZIDI COMBINING MADDA MARK
+ {0x10EAD, 0x10EAD, prN}, // Pd YEZIDI HYPHENATION MARK
+ {0x10EB0, 0x10EB1, prN}, // Lo [2] YEZIDI LETTER LAM WITH DOT ABOVE..YEZIDI LETTER YOT WITH CIRCUMFLEX ABOVE
+ {0x10F00, 0x10F1C, prN}, // Lo [29] OLD SOGDIAN LETTER ALEPH..OLD SOGDIAN LETTER FINAL TAW WITH VERTICAL TAIL
+ {0x10F1D, 0x10F26, prN}, // No [10] OLD SOGDIAN NUMBER ONE..OLD SOGDIAN FRACTION ONE HALF
+ {0x10F27, 0x10F27, prN}, // Lo OLD SOGDIAN LIGATURE AYIN-DALETH
+ {0x10F30, 0x10F45, prN}, // Lo [22] SOGDIAN LETTER ALEPH..SOGDIAN INDEPENDENT SHIN
+ {0x10F46, 0x10F50, prN}, // Mn [11] SOGDIAN COMBINING DOT BELOW..SOGDIAN COMBINING STROKE BELOW
+ {0x10F51, 0x10F54, prN}, // No [4] SOGDIAN NUMBER ONE..SOGDIAN NUMBER ONE HUNDRED
+ {0x10F55, 0x10F59, prN}, // Po [5] SOGDIAN PUNCTUATION TWO VERTICAL BARS..SOGDIAN PUNCTUATION HALF CIRCLE WITH DOT
+ {0x10F70, 0x10F81, prN}, // Lo [18] OLD UYGHUR LETTER ALEPH..OLD UYGHUR LETTER LESH
+ {0x10F82, 0x10F85, prN}, // Mn [4] OLD UYGHUR COMBINING DOT ABOVE..OLD UYGHUR COMBINING TWO DOTS BELOW
+ {0x10F86, 0x10F89, prN}, // Po [4] OLD UYGHUR PUNCTUATION BAR..OLD UYGHUR PUNCTUATION FOUR DOTS
+ {0x10FB0, 0x10FC4, prN}, // Lo [21] CHORASMIAN LETTER ALEPH..CHORASMIAN LETTER TAW
+ {0x10FC5, 0x10FCB, prN}, // No [7] CHORASMIAN NUMBER ONE..CHORASMIAN NUMBER ONE HUNDRED
+ {0x10FE0, 0x10FF6, prN}, // Lo [23] ELYMAIC LETTER ALEPH..ELYMAIC LIGATURE ZAYIN-YODH
+ {0x11000, 0x11000, prN}, // Mc BRAHMI SIGN CANDRABINDU
+ {0x11001, 0x11001, prN}, // Mn BRAHMI SIGN ANUSVARA
+ {0x11002, 0x11002, prN}, // Mc BRAHMI SIGN VISARGA
+ {0x11003, 0x11037, prN}, // Lo [53] BRAHMI SIGN JIHVAMULIYA..BRAHMI LETTER OLD TAMIL NNNA
+ {0x11038, 0x11046, prN}, // Mn [15] BRAHMI VOWEL SIGN AA..BRAHMI VIRAMA
+ {0x11047, 0x1104D, prN}, // Po [7] BRAHMI DANDA..BRAHMI PUNCTUATION LOTUS
+ {0x11052, 0x11065, prN}, // No [20] BRAHMI NUMBER ONE..BRAHMI NUMBER ONE THOUSAND
+ {0x11066, 0x1106F, prN}, // Nd [10] BRAHMI DIGIT ZERO..BRAHMI DIGIT NINE
+ {0x11070, 0x11070, prN}, // Mn BRAHMI SIGN OLD TAMIL VIRAMA
+ {0x11071, 0x11072, prN}, // Lo [2] BRAHMI LETTER OLD TAMIL SHORT E..BRAHMI LETTER OLD TAMIL SHORT O
+ {0x11073, 0x11074, prN}, // Mn [2] BRAHMI VOWEL SIGN OLD TAMIL SHORT E..BRAHMI VOWEL SIGN OLD TAMIL SHORT O
+ {0x11075, 0x11075, prN}, // Lo BRAHMI LETTER OLD TAMIL LLA
+ {0x1107F, 0x1107F, prN}, // Mn BRAHMI NUMBER JOINER
+ {0x11080, 0x11081, prN}, // Mn [2] KAITHI SIGN CANDRABINDU..KAITHI SIGN ANUSVARA
+ {0x11082, 0x11082, prN}, // Mc KAITHI SIGN VISARGA
+ {0x11083, 0x110AF, prN}, // Lo [45] KAITHI LETTER A..KAITHI LETTER HA
+ {0x110B0, 0x110B2, prN}, // Mc [3] KAITHI VOWEL SIGN AA..KAITHI VOWEL SIGN II
+ {0x110B3, 0x110B6, prN}, // Mn [4] KAITHI VOWEL SIGN U..KAITHI VOWEL SIGN AI
+ {0x110B7, 0x110B8, prN}, // Mc [2] KAITHI VOWEL SIGN O..KAITHI VOWEL SIGN AU
+ {0x110B9, 0x110BA, prN}, // Mn [2] KAITHI SIGN VIRAMA..KAITHI SIGN NUKTA
+ {0x110BB, 0x110BC, prN}, // Po [2] KAITHI ABBREVIATION SIGN..KAITHI ENUMERATION SIGN
+ {0x110BD, 0x110BD, prN}, // Cf KAITHI NUMBER SIGN
+ {0x110BE, 0x110C1, prN}, // Po [4] KAITHI SECTION MARK..KAITHI DOUBLE DANDA
+ {0x110C2, 0x110C2, prN}, // Mn KAITHI VOWEL SIGN VOCALIC R
+ {0x110CD, 0x110CD, prN}, // Cf KAITHI NUMBER SIGN ABOVE
+ {0x110D0, 0x110E8, prN}, // Lo [25] SORA SOMPENG LETTER SAH..SORA SOMPENG LETTER MAE
+ {0x110F0, 0x110F9, prN}, // Nd [10] SORA SOMPENG DIGIT ZERO..SORA SOMPENG DIGIT NINE
+ {0x11100, 0x11102, prN}, // Mn [3] CHAKMA SIGN CANDRABINDU..CHAKMA SIGN VISARGA
+ {0x11103, 0x11126, prN}, // Lo [36] CHAKMA LETTER AA..CHAKMA LETTER HAA
+ {0x11127, 0x1112B, prN}, // Mn [5] CHAKMA VOWEL SIGN A..CHAKMA VOWEL SIGN UU
+ {0x1112C, 0x1112C, prN}, // Mc CHAKMA VOWEL SIGN E
+ {0x1112D, 0x11134, prN}, // Mn [8] CHAKMA VOWEL SIGN AI..CHAKMA MAAYYAA
+ {0x11136, 0x1113F, prN}, // Nd [10] CHAKMA DIGIT ZERO..CHAKMA DIGIT NINE
+ {0x11140, 0x11143, prN}, // Po [4] CHAKMA SECTION MARK..CHAKMA QUESTION MARK
+ {0x11144, 0x11144, prN}, // Lo CHAKMA LETTER LHAA
+ {0x11145, 0x11146, prN}, // Mc [2] CHAKMA VOWEL SIGN AA..CHAKMA VOWEL SIGN EI
+ {0x11147, 0x11147, prN}, // Lo CHAKMA LETTER VAA
+ {0x11150, 0x11172, prN}, // Lo [35] MAHAJANI LETTER A..MAHAJANI LETTER RRA
+ {0x11173, 0x11173, prN}, // Mn MAHAJANI SIGN NUKTA
+ {0x11174, 0x11175, prN}, // Po [2] MAHAJANI ABBREVIATION SIGN..MAHAJANI SECTION MARK
+ {0x11176, 0x11176, prN}, // Lo MAHAJANI LIGATURE SHRI
+ {0x11180, 0x11181, prN}, // Mn [2] SHARADA SIGN CANDRABINDU..SHARADA SIGN ANUSVARA
+ {0x11182, 0x11182, prN}, // Mc SHARADA SIGN VISARGA
+ {0x11183, 0x111B2, prN}, // Lo [48] SHARADA LETTER A..SHARADA LETTER HA
+ {0x111B3, 0x111B5, prN}, // Mc [3] SHARADA VOWEL SIGN AA..SHARADA VOWEL SIGN II
+ {0x111B6, 0x111BE, prN}, // Mn [9] SHARADA VOWEL SIGN U..SHARADA VOWEL SIGN O
+ {0x111BF, 0x111C0, prN}, // Mc [2] SHARADA VOWEL SIGN AU..SHARADA SIGN VIRAMA
+ {0x111C1, 0x111C4, prN}, // Lo [4] SHARADA SIGN AVAGRAHA..SHARADA OM
+ {0x111C5, 0x111C8, prN}, // Po [4] SHARADA DANDA..SHARADA SEPARATOR
+ {0x111C9, 0x111CC, prN}, // Mn [4] SHARADA SANDHI MARK..SHARADA EXTRA SHORT VOWEL MARK
+ {0x111CD, 0x111CD, prN}, // Po SHARADA SUTRA MARK
+ {0x111CE, 0x111CE, prN}, // Mc SHARADA VOWEL SIGN PRISHTHAMATRA E
+ {0x111CF, 0x111CF, prN}, // Mn SHARADA SIGN INVERTED CANDRABINDU
+ {0x111D0, 0x111D9, prN}, // Nd [10] SHARADA DIGIT ZERO..SHARADA DIGIT NINE
+ {0x111DA, 0x111DA, prN}, // Lo SHARADA EKAM
+ {0x111DB, 0x111DB, prN}, // Po SHARADA SIGN SIDDHAM
+ {0x111DC, 0x111DC, prN}, // Lo SHARADA HEADSTROKE
+ {0x111DD, 0x111DF, prN}, // Po [3] SHARADA CONTINUATION SIGN..SHARADA SECTION MARK-2
+ {0x111E1, 0x111F4, prN}, // No [20] SINHALA ARCHAIC DIGIT ONE..SINHALA ARCHAIC NUMBER ONE THOUSAND
+ {0x11200, 0x11211, prN}, // Lo [18] KHOJKI LETTER A..KHOJKI LETTER JJA
+ {0x11213, 0x1122B, prN}, // Lo [25] KHOJKI LETTER NYA..KHOJKI LETTER LLA
+ {0x1122C, 0x1122E, prN}, // Mc [3] KHOJKI VOWEL SIGN AA..KHOJKI VOWEL SIGN II
+ {0x1122F, 0x11231, prN}, // Mn [3] KHOJKI VOWEL SIGN U..KHOJKI VOWEL SIGN AI
+ {0x11232, 0x11233, prN}, // Mc [2] KHOJKI VOWEL SIGN O..KHOJKI VOWEL SIGN AU
+ {0x11234, 0x11234, prN}, // Mn KHOJKI SIGN ANUSVARA
+ {0x11235, 0x11235, prN}, // Mc KHOJKI SIGN VIRAMA
+ {0x11236, 0x11237, prN}, // Mn [2] KHOJKI SIGN NUKTA..KHOJKI SIGN SHADDA
+ {0x11238, 0x1123D, prN}, // Po [6] KHOJKI DANDA..KHOJKI ABBREVIATION SIGN
+ {0x1123E, 0x1123E, prN}, // Mn KHOJKI SIGN SUKUN
+ {0x11280, 0x11286, prN}, // Lo [7] MULTANI LETTER A..MULTANI LETTER GA
+ {0x11288, 0x11288, prN}, // Lo MULTANI LETTER GHA
+ {0x1128A, 0x1128D, prN}, // Lo [4] MULTANI LETTER CA..MULTANI LETTER JJA
+ {0x1128F, 0x1129D, prN}, // Lo [15] MULTANI LETTER NYA..MULTANI LETTER BA
+ {0x1129F, 0x112A8, prN}, // Lo [10] MULTANI LETTER BHA..MULTANI LETTER RHA
+ {0x112A9, 0x112A9, prN}, // Po MULTANI SECTION MARK
+ {0x112B0, 0x112DE, prN}, // Lo [47] KHUDAWADI LETTER A..KHUDAWADI LETTER HA
+ {0x112DF, 0x112DF, prN}, // Mn KHUDAWADI SIGN ANUSVARA
+ {0x112E0, 0x112E2, prN}, // Mc [3] KHUDAWADI VOWEL SIGN AA..KHUDAWADI VOWEL SIGN II
+ {0x112E3, 0x112EA, prN}, // Mn [8] KHUDAWADI VOWEL SIGN U..KHUDAWADI SIGN VIRAMA
+ {0x112F0, 0x112F9, prN}, // Nd [10] KHUDAWADI DIGIT ZERO..KHUDAWADI DIGIT NINE
+ {0x11300, 0x11301, prN}, // Mn [2] GRANTHA SIGN COMBINING ANUSVARA ABOVE..GRANTHA SIGN CANDRABINDU
+ {0x11302, 0x11303, prN}, // Mc [2] GRANTHA SIGN ANUSVARA..GRANTHA SIGN VISARGA
+ {0x11305, 0x1130C, prN}, // Lo [8] GRANTHA LETTER A..GRANTHA LETTER VOCALIC L
+ {0x1130F, 0x11310, prN}, // Lo [2] GRANTHA LETTER EE..GRANTHA LETTER AI
+ {0x11313, 0x11328, prN}, // Lo [22] GRANTHA LETTER OO..GRANTHA LETTER NA
+ {0x1132A, 0x11330, prN}, // Lo [7] GRANTHA LETTER PA..GRANTHA LETTER RA
+ {0x11332, 0x11333, prN}, // Lo [2] GRANTHA LETTER LA..GRANTHA LETTER LLA
+ {0x11335, 0x11339, prN}, // Lo [5] GRANTHA LETTER VA..GRANTHA LETTER HA
+ {0x1133B, 0x1133C, prN}, // Mn [2] COMBINING BINDU BELOW..GRANTHA SIGN NUKTA
+ {0x1133D, 0x1133D, prN}, // Lo GRANTHA SIGN AVAGRAHA
+ {0x1133E, 0x1133F, prN}, // Mc [2] GRANTHA VOWEL SIGN AA..GRANTHA VOWEL SIGN I
+ {0x11340, 0x11340, prN}, // Mn GRANTHA VOWEL SIGN II
+ {0x11341, 0x11344, prN}, // Mc [4] GRANTHA VOWEL SIGN U..GRANTHA VOWEL SIGN VOCALIC RR
+ {0x11347, 0x11348, prN}, // Mc [2] GRANTHA VOWEL SIGN EE..GRANTHA VOWEL SIGN AI
+ {0x1134B, 0x1134D, prN}, // Mc [3] GRANTHA VOWEL SIGN OO..GRANTHA SIGN VIRAMA
+ {0x11350, 0x11350, prN}, // Lo GRANTHA OM
+ {0x11357, 0x11357, prN}, // Mc GRANTHA AU LENGTH MARK
+ {0x1135D, 0x11361, prN}, // Lo [5] GRANTHA SIGN PLUTA..GRANTHA LETTER VOCALIC LL
+ {0x11362, 0x11363, prN}, // Mc [2] GRANTHA VOWEL SIGN VOCALIC L..GRANTHA VOWEL SIGN VOCALIC LL
+ {0x11366, 0x1136C, prN}, // Mn [7] COMBINING GRANTHA DIGIT ZERO..COMBINING GRANTHA DIGIT SIX
+ {0x11370, 0x11374, prN}, // Mn [5] COMBINING GRANTHA LETTER A..COMBINING GRANTHA LETTER PA
+ {0x11400, 0x11434, prN}, // Lo [53] NEWA LETTER A..NEWA LETTER HA
+ {0x11435, 0x11437, prN}, // Mc [3] NEWA VOWEL SIGN AA..NEWA VOWEL SIGN II
+ {0x11438, 0x1143F, prN}, // Mn [8] NEWA VOWEL SIGN U..NEWA VOWEL SIGN AI
+ {0x11440, 0x11441, prN}, // Mc [2] NEWA VOWEL SIGN O..NEWA VOWEL SIGN AU
+ {0x11442, 0x11444, prN}, // Mn [3] NEWA SIGN VIRAMA..NEWA SIGN ANUSVARA
+ {0x11445, 0x11445, prN}, // Mc NEWA SIGN VISARGA
+ {0x11446, 0x11446, prN}, // Mn NEWA SIGN NUKTA
+ {0x11447, 0x1144A, prN}, // Lo [4] NEWA SIGN AVAGRAHA..NEWA SIDDHI
+ {0x1144B, 0x1144F, prN}, // Po [5] NEWA DANDA..NEWA ABBREVIATION SIGN
+ {0x11450, 0x11459, prN}, // Nd [10] NEWA DIGIT ZERO..NEWA DIGIT NINE
+ {0x1145A, 0x1145B, prN}, // Po [2] NEWA DOUBLE COMMA..NEWA PLACEHOLDER MARK
+ {0x1145D, 0x1145D, prN}, // Po NEWA INSERTION SIGN
+ {0x1145E, 0x1145E, prN}, // Mn NEWA SANDHI MARK
+ {0x1145F, 0x11461, prN}, // Lo [3] NEWA LETTER VEDIC ANUSVARA..NEWA SIGN UPADHMANIYA
+ {0x11480, 0x114AF, prN}, // Lo [48] TIRHUTA ANJI..TIRHUTA LETTER HA
+ {0x114B0, 0x114B2, prN}, // Mc [3] TIRHUTA VOWEL SIGN AA..TIRHUTA VOWEL SIGN II
+ {0x114B3, 0x114B8, prN}, // Mn [6] TIRHUTA VOWEL SIGN U..TIRHUTA VOWEL SIGN VOCALIC LL
+ {0x114B9, 0x114B9, prN}, // Mc TIRHUTA VOWEL SIGN E
+ {0x114BA, 0x114BA, prN}, // Mn TIRHUTA VOWEL SIGN SHORT E
+ {0x114BB, 0x114BE, prN}, // Mc [4] TIRHUTA VOWEL SIGN AI..TIRHUTA VOWEL SIGN AU
+ {0x114BF, 0x114C0, prN}, // Mn [2] TIRHUTA SIGN CANDRABINDU..TIRHUTA SIGN ANUSVARA
+ {0x114C1, 0x114C1, prN}, // Mc TIRHUTA SIGN VISARGA
+ {0x114C2, 0x114C3, prN}, // Mn [2] TIRHUTA SIGN VIRAMA..TIRHUTA SIGN NUKTA
+ {0x114C4, 0x114C5, prN}, // Lo [2] TIRHUTA SIGN AVAGRAHA..TIRHUTA GVANG
+ {0x114C6, 0x114C6, prN}, // Po TIRHUTA ABBREVIATION SIGN
+ {0x114C7, 0x114C7, prN}, // Lo TIRHUTA OM
+ {0x114D0, 0x114D9, prN}, // Nd [10] TIRHUTA DIGIT ZERO..TIRHUTA DIGIT NINE
+ {0x11580, 0x115AE, prN}, // Lo [47] SIDDHAM LETTER A..SIDDHAM LETTER HA
+ {0x115AF, 0x115B1, prN}, // Mc [3] SIDDHAM VOWEL SIGN AA..SIDDHAM VOWEL SIGN II
+ {0x115B2, 0x115B5, prN}, // Mn [4] SIDDHAM VOWEL SIGN U..SIDDHAM VOWEL SIGN VOCALIC RR
+ {0x115B8, 0x115BB, prN}, // Mc [4] SIDDHAM VOWEL SIGN E..SIDDHAM VOWEL SIGN AU
+ {0x115BC, 0x115BD, prN}, // Mn [2] SIDDHAM SIGN CANDRABINDU..SIDDHAM SIGN ANUSVARA
+ {0x115BE, 0x115BE, prN}, // Mc SIDDHAM SIGN VISARGA
+ {0x115BF, 0x115C0, prN}, // Mn [2] SIDDHAM SIGN VIRAMA..SIDDHAM SIGN NUKTA
+ {0x115C1, 0x115D7, prN}, // Po [23] SIDDHAM SIGN SIDDHAM..SIDDHAM SECTION MARK WITH CIRCLES AND FOUR ENCLOSURES
+ {0x115D8, 0x115DB, prN}, // Lo [4] SIDDHAM LETTER THREE-CIRCLE ALTERNATE I..SIDDHAM LETTER ALTERNATE U
+ {0x115DC, 0x115DD, prN}, // Mn [2] SIDDHAM VOWEL SIGN ALTERNATE U..SIDDHAM VOWEL SIGN ALTERNATE UU
+ {0x11600, 0x1162F, prN}, // Lo [48] MODI LETTER A..MODI LETTER LLA
+ {0x11630, 0x11632, prN}, // Mc [3] MODI VOWEL SIGN AA..MODI VOWEL SIGN II
+ {0x11633, 0x1163A, prN}, // Mn [8] MODI VOWEL SIGN U..MODI VOWEL SIGN AI
+ {0x1163B, 0x1163C, prN}, // Mc [2] MODI VOWEL SIGN O..MODI VOWEL SIGN AU
+ {0x1163D, 0x1163D, prN}, // Mn MODI SIGN ANUSVARA
+ {0x1163E, 0x1163E, prN}, // Mc MODI SIGN VISARGA
+ {0x1163F, 0x11640, prN}, // Mn [2] MODI SIGN VIRAMA..MODI SIGN ARDHACANDRA
+ {0x11641, 0x11643, prN}, // Po [3] MODI DANDA..MODI ABBREVIATION SIGN
+ {0x11644, 0x11644, prN}, // Lo MODI SIGN HUVA
+ {0x11650, 0x11659, prN}, // Nd [10] MODI DIGIT ZERO..MODI DIGIT NINE
+ {0x11660, 0x1166C, prN}, // Po [13] MONGOLIAN BIRGA WITH ORNAMENT..MONGOLIAN TURNED SWIRL BIRGA WITH DOUBLE ORNAMENT
+ {0x11680, 0x116AA, prN}, // Lo [43] TAKRI LETTER A..TAKRI LETTER RRA
+ {0x116AB, 0x116AB, prN}, // Mn TAKRI SIGN ANUSVARA
+ {0x116AC, 0x116AC, prN}, // Mc TAKRI SIGN VISARGA
+ {0x116AD, 0x116AD, prN}, // Mn TAKRI VOWEL SIGN AA
+ {0x116AE, 0x116AF, prN}, // Mc [2] TAKRI VOWEL SIGN I..TAKRI VOWEL SIGN II
+ {0x116B0, 0x116B5, prN}, // Mn [6] TAKRI VOWEL SIGN U..TAKRI VOWEL SIGN AU
+ {0x116B6, 0x116B6, prN}, // Mc TAKRI SIGN VIRAMA
+ {0x116B7, 0x116B7, prN}, // Mn TAKRI SIGN NUKTA
+ {0x116B8, 0x116B8, prN}, // Lo TAKRI LETTER ARCHAIC KHA
+ {0x116B9, 0x116B9, prN}, // Po TAKRI ABBREVIATION SIGN
+ {0x116C0, 0x116C9, prN}, // Nd [10] TAKRI DIGIT ZERO..TAKRI DIGIT NINE
+ {0x11700, 0x1171A, prN}, // Lo [27] AHOM LETTER KA..AHOM LETTER ALTERNATE BA
+ {0x1171D, 0x1171F, prN}, // Mn [3] AHOM CONSONANT SIGN MEDIAL LA..AHOM CONSONANT SIGN MEDIAL LIGATING RA
+ {0x11720, 0x11721, prN}, // Mc [2] AHOM VOWEL SIGN A..AHOM VOWEL SIGN AA
+ {0x11722, 0x11725, prN}, // Mn [4] AHOM VOWEL SIGN I..AHOM VOWEL SIGN UU
+ {0x11726, 0x11726, prN}, // Mc AHOM VOWEL SIGN E
+ {0x11727, 0x1172B, prN}, // Mn [5] AHOM VOWEL SIGN AW..AHOM SIGN KILLER
+ {0x11730, 0x11739, prN}, // Nd [10] AHOM DIGIT ZERO..AHOM DIGIT NINE
+ {0x1173A, 0x1173B, prN}, // No [2] AHOM NUMBER TEN..AHOM NUMBER TWENTY
+ {0x1173C, 0x1173E, prN}, // Po [3] AHOM SIGN SMALL SECTION..AHOM SIGN RULAI
+ {0x1173F, 0x1173F, prN}, // So AHOM SYMBOL VI
+ {0x11740, 0x11746, prN}, // Lo [7] AHOM LETTER CA..AHOM LETTER LLA
+ {0x11800, 0x1182B, prN}, // Lo [44] DOGRA LETTER A..DOGRA LETTER RRA
+ {0x1182C, 0x1182E, prN}, // Mc [3] DOGRA VOWEL SIGN AA..DOGRA VOWEL SIGN II
+ {0x1182F, 0x11837, prN}, // Mn [9] DOGRA VOWEL SIGN U..DOGRA SIGN ANUSVARA
+ {0x11838, 0x11838, prN}, // Mc DOGRA SIGN VISARGA
+ {0x11839, 0x1183A, prN}, // Mn [2] DOGRA SIGN VIRAMA..DOGRA SIGN NUKTA
+ {0x1183B, 0x1183B, prN}, // Po DOGRA ABBREVIATION SIGN
+ {0x118A0, 0x118DF, prN}, // L& [64] WARANG CITI CAPITAL LETTER NGAA..WARANG CITI SMALL LETTER VIYO
+ {0x118E0, 0x118E9, prN}, // Nd [10] WARANG CITI DIGIT ZERO..WARANG CITI DIGIT NINE
+ {0x118EA, 0x118F2, prN}, // No [9] WARANG CITI NUMBER TEN..WARANG CITI NUMBER NINETY
+ {0x118FF, 0x118FF, prN}, // Lo WARANG CITI OM
+ {0x11900, 0x11906, prN}, // Lo [7] DIVES AKURU LETTER A..DIVES AKURU LETTER E
+ {0x11909, 0x11909, prN}, // Lo DIVES AKURU LETTER O
+ {0x1190C, 0x11913, prN}, // Lo [8] DIVES AKURU LETTER KA..DIVES AKURU LETTER JA
+ {0x11915, 0x11916, prN}, // Lo [2] DIVES AKURU LETTER NYA..DIVES AKURU LETTER TTA
+ {0x11918, 0x1192F, prN}, // Lo [24] DIVES AKURU LETTER DDA..DIVES AKURU LETTER ZA
+ {0x11930, 0x11935, prN}, // Mc [6] DIVES AKURU VOWEL SIGN AA..DIVES AKURU VOWEL SIGN E
+ {0x11937, 0x11938, prN}, // Mc [2] DIVES AKURU VOWEL SIGN AI..DIVES AKURU VOWEL SIGN O
+ {0x1193B, 0x1193C, prN}, // Mn [2] DIVES AKURU SIGN ANUSVARA..DIVES AKURU SIGN CANDRABINDU
+ {0x1193D, 0x1193D, prN}, // Mc DIVES AKURU SIGN HALANTA
+ {0x1193E, 0x1193E, prN}, // Mn DIVES AKURU VIRAMA
+ {0x1193F, 0x1193F, prN}, // Lo DIVES AKURU PREFIXED NASAL SIGN
+ {0x11940, 0x11940, prN}, // Mc DIVES AKURU MEDIAL YA
+ {0x11941, 0x11941, prN}, // Lo DIVES AKURU INITIAL RA
+ {0x11942, 0x11942, prN}, // Mc DIVES AKURU MEDIAL RA
+ {0x11943, 0x11943, prN}, // Mn DIVES AKURU SIGN NUKTA
+ {0x11944, 0x11946, prN}, // Po [3] DIVES AKURU DOUBLE DANDA..DIVES AKURU END OF TEXT MARK
+ {0x11950, 0x11959, prN}, // Nd [10] DIVES AKURU DIGIT ZERO..DIVES AKURU DIGIT NINE
+ {0x119A0, 0x119A7, prN}, // Lo [8] NANDINAGARI LETTER A..NANDINAGARI LETTER VOCALIC RR
+ {0x119AA, 0x119D0, prN}, // Lo [39] NANDINAGARI LETTER E..NANDINAGARI LETTER RRA
+ {0x119D1, 0x119D3, prN}, // Mc [3] NANDINAGARI VOWEL SIGN AA..NANDINAGARI VOWEL SIGN II
+ {0x119D4, 0x119D7, prN}, // Mn [4] NANDINAGARI VOWEL SIGN U..NANDINAGARI VOWEL SIGN VOCALIC RR
+ {0x119DA, 0x119DB, prN}, // Mn [2] NANDINAGARI VOWEL SIGN E..NANDINAGARI VOWEL SIGN AI
+ {0x119DC, 0x119DF, prN}, // Mc [4] NANDINAGARI VOWEL SIGN O..NANDINAGARI SIGN VISARGA
+ {0x119E0, 0x119E0, prN}, // Mn NANDINAGARI SIGN VIRAMA
+ {0x119E1, 0x119E1, prN}, // Lo NANDINAGARI SIGN AVAGRAHA
+ {0x119E2, 0x119E2, prN}, // Po NANDINAGARI SIGN SIDDHAM
+ {0x119E3, 0x119E3, prN}, // Lo NANDINAGARI HEADSTROKE
+ {0x119E4, 0x119E4, prN}, // Mc NANDINAGARI VOWEL SIGN PRISHTHAMATRA E
+ {0x11A00, 0x11A00, prN}, // Lo ZANABAZAR SQUARE LETTER A
+ {0x11A01, 0x11A0A, prN}, // Mn [10] ZANABAZAR SQUARE VOWEL SIGN I..ZANABAZAR SQUARE VOWEL LENGTH MARK
+ {0x11A0B, 0x11A32, prN}, // Lo [40] ZANABAZAR SQUARE LETTER KA..ZANABAZAR SQUARE LETTER KSSA
+ {0x11A33, 0x11A38, prN}, // Mn [6] ZANABAZAR SQUARE FINAL CONSONANT MARK..ZANABAZAR SQUARE SIGN ANUSVARA
+ {0x11A39, 0x11A39, prN}, // Mc ZANABAZAR SQUARE SIGN VISARGA
+ {0x11A3A, 0x11A3A, prN}, // Lo ZANABAZAR SQUARE CLUSTER-INITIAL LETTER RA
+ {0x11A3B, 0x11A3E, prN}, // Mn [4] ZANABAZAR SQUARE CLUSTER-FINAL LETTER YA..ZANABAZAR SQUARE CLUSTER-FINAL LETTER VA
+ {0x11A3F, 0x11A46, prN}, // Po [8] ZANABAZAR SQUARE INITIAL HEAD MARK..ZANABAZAR SQUARE CLOSING DOUBLE-LINED HEAD MARK
+ {0x11A47, 0x11A47, prN}, // Mn ZANABAZAR SQUARE SUBJOINER
+ {0x11A50, 0x11A50, prN}, // Lo SOYOMBO LETTER A
+ {0x11A51, 0x11A56, prN}, // Mn [6] SOYOMBO VOWEL SIGN I..SOYOMBO VOWEL SIGN OE
+ {0x11A57, 0x11A58, prN}, // Mc [2] SOYOMBO VOWEL SIGN AI..SOYOMBO VOWEL SIGN AU
+ {0x11A59, 0x11A5B, prN}, // Mn [3] SOYOMBO VOWEL SIGN VOCALIC R..SOYOMBO VOWEL LENGTH MARK
+ {0x11A5C, 0x11A89, prN}, // Lo [46] SOYOMBO LETTER KA..SOYOMBO CLUSTER-INITIAL LETTER SA
+ {0x11A8A, 0x11A96, prN}, // Mn [13] SOYOMBO FINAL CONSONANT SIGN G..SOYOMBO SIGN ANUSVARA
+ {0x11A97, 0x11A97, prN}, // Mc SOYOMBO SIGN VISARGA
+ {0x11A98, 0x11A99, prN}, // Mn [2] SOYOMBO GEMINATION MARK..SOYOMBO SUBJOINER
+ {0x11A9A, 0x11A9C, prN}, // Po [3] SOYOMBO MARK TSHEG..SOYOMBO MARK DOUBLE SHAD
+ {0x11A9D, 0x11A9D, prN}, // Lo SOYOMBO MARK PLUTA
+ {0x11A9E, 0x11AA2, prN}, // Po [5] SOYOMBO HEAD MARK WITH MOON AND SUN AND TRIPLE FLAME..SOYOMBO TERMINAL MARK-2
+ {0x11AB0, 0x11ABF, prN}, // Lo [16] CANADIAN SYLLABICS NATTILIK HI..CANADIAN SYLLABICS SPA
+ {0x11AC0, 0x11AF8, prN}, // Lo [57] PAU CIN HAU LETTER PA..PAU CIN HAU GLOTTAL STOP FINAL
+ {0x11C00, 0x11C08, prN}, // Lo [9] BHAIKSUKI LETTER A..BHAIKSUKI LETTER VOCALIC L
+ {0x11C0A, 0x11C2E, prN}, // Lo [37] BHAIKSUKI LETTER E..BHAIKSUKI LETTER HA
+ {0x11C2F, 0x11C2F, prN}, // Mc BHAIKSUKI VOWEL SIGN AA
+ {0x11C30, 0x11C36, prN}, // Mn [7] BHAIKSUKI VOWEL SIGN I..BHAIKSUKI VOWEL SIGN VOCALIC L
+ {0x11C38, 0x11C3D, prN}, // Mn [6] BHAIKSUKI VOWEL SIGN E..BHAIKSUKI SIGN ANUSVARA
+ {0x11C3E, 0x11C3E, prN}, // Mc BHAIKSUKI SIGN VISARGA
+ {0x11C3F, 0x11C3F, prN}, // Mn BHAIKSUKI SIGN VIRAMA
+ {0x11C40, 0x11C40, prN}, // Lo BHAIKSUKI SIGN AVAGRAHA
+ {0x11C41, 0x11C45, prN}, // Po [5] BHAIKSUKI DANDA..BHAIKSUKI GAP FILLER-2
+ {0x11C50, 0x11C59, prN}, // Nd [10] BHAIKSUKI DIGIT ZERO..BHAIKSUKI DIGIT NINE
+ {0x11C5A, 0x11C6C, prN}, // No [19] BHAIKSUKI NUMBER ONE..BHAIKSUKI HUNDREDS UNIT MARK
+ {0x11C70, 0x11C71, prN}, // Po [2] MARCHEN HEAD MARK..MARCHEN MARK SHAD
+ {0x11C72, 0x11C8F, prN}, // Lo [30] MARCHEN LETTER KA..MARCHEN LETTER A
+ {0x11C92, 0x11CA7, prN}, // Mn [22] MARCHEN SUBJOINED LETTER KA..MARCHEN SUBJOINED LETTER ZA
+ {0x11CA9, 0x11CA9, prN}, // Mc MARCHEN SUBJOINED LETTER YA
+ {0x11CAA, 0x11CB0, prN}, // Mn [7] MARCHEN SUBJOINED LETTER RA..MARCHEN VOWEL SIGN AA
+ {0x11CB1, 0x11CB1, prN}, // Mc MARCHEN VOWEL SIGN I
+ {0x11CB2, 0x11CB3, prN}, // Mn [2] MARCHEN VOWEL SIGN U..MARCHEN VOWEL SIGN E
+ {0x11CB4, 0x11CB4, prN}, // Mc MARCHEN VOWEL SIGN O
+ {0x11CB5, 0x11CB6, prN}, // Mn [2] MARCHEN SIGN ANUSVARA..MARCHEN SIGN CANDRABINDU
+ {0x11D00, 0x11D06, prN}, // Lo [7] MASARAM GONDI LETTER A..MASARAM GONDI LETTER E
+ {0x11D08, 0x11D09, prN}, // Lo [2] MASARAM GONDI LETTER AI..MASARAM GONDI LETTER O
+ {0x11D0B, 0x11D30, prN}, // Lo [38] MASARAM GONDI LETTER AU..MASARAM GONDI LETTER TRA
+ {0x11D31, 0x11D36, prN}, // Mn [6] MASARAM GONDI VOWEL SIGN AA..MASARAM GONDI VOWEL SIGN VOCALIC R
+ {0x11D3A, 0x11D3A, prN}, // Mn MASARAM GONDI VOWEL SIGN E
+ {0x11D3C, 0x11D3D, prN}, // Mn [2] MASARAM GONDI VOWEL SIGN AI..MASARAM GONDI VOWEL SIGN O
+ {0x11D3F, 0x11D45, prN}, // Mn [7] MASARAM GONDI VOWEL SIGN AU..MASARAM GONDI VIRAMA
+ {0x11D46, 0x11D46, prN}, // Lo MASARAM GONDI REPHA
+ {0x11D47, 0x11D47, prN}, // Mn MASARAM GONDI RA-KARA
+ {0x11D50, 0x11D59, prN}, // Nd [10] MASARAM GONDI DIGIT ZERO..MASARAM GONDI DIGIT NINE
+ {0x11D60, 0x11D65, prN}, // Lo [6] GUNJALA GONDI LETTER A..GUNJALA GONDI LETTER UU
+ {0x11D67, 0x11D68, prN}, // Lo [2] GUNJALA GONDI LETTER EE..GUNJALA GONDI LETTER AI
+ {0x11D6A, 0x11D89, prN}, // Lo [32] GUNJALA GONDI LETTER OO..GUNJALA GONDI LETTER SA
+ {0x11D8A, 0x11D8E, prN}, // Mc [5] GUNJALA GONDI VOWEL SIGN AA..GUNJALA GONDI VOWEL SIGN UU
+ {0x11D90, 0x11D91, prN}, // Mn [2] GUNJALA GONDI VOWEL SIGN EE..GUNJALA GONDI VOWEL SIGN AI
+ {0x11D93, 0x11D94, prN}, // Mc [2] GUNJALA GONDI VOWEL SIGN OO..GUNJALA GONDI VOWEL SIGN AU
+ {0x11D95, 0x11D95, prN}, // Mn GUNJALA GONDI SIGN ANUSVARA
+ {0x11D96, 0x11D96, prN}, // Mc GUNJALA GONDI SIGN VISARGA
+ {0x11D97, 0x11D97, prN}, // Mn GUNJALA GONDI VIRAMA
+ {0x11D98, 0x11D98, prN}, // Lo GUNJALA GONDI OM
+ {0x11DA0, 0x11DA9, prN}, // Nd [10] GUNJALA GONDI DIGIT ZERO..GUNJALA GONDI DIGIT NINE
+ {0x11EE0, 0x11EF2, prN}, // Lo [19] MAKASAR LETTER KA..MAKASAR ANGKA
+ {0x11EF3, 0x11EF4, prN}, // Mn [2] MAKASAR VOWEL SIGN I..MAKASAR VOWEL SIGN U
+ {0x11EF5, 0x11EF6, prN}, // Mc [2] MAKASAR VOWEL SIGN E..MAKASAR VOWEL SIGN O
+ {0x11EF7, 0x11EF8, prN}, // Po [2] MAKASAR PASSIMBANG..MAKASAR END OF SECTION
+ {0x11FB0, 0x11FB0, prN}, // Lo LISU LETTER YHA
+ {0x11FC0, 0x11FD4, prN}, // No [21] TAMIL FRACTION ONE THREE-HUNDRED-AND-TWENTIETH..TAMIL FRACTION DOWNSCALING FACTOR KIIZH
+ {0x11FD5, 0x11FDC, prN}, // So [8] TAMIL SIGN NEL..TAMIL SIGN MUKKURUNI
+ {0x11FDD, 0x11FE0, prN}, // Sc [4] TAMIL SIGN KAACU..TAMIL SIGN VARAAKAN
+ {0x11FE1, 0x11FF1, prN}, // So [17] TAMIL SIGN PAARAM..TAMIL SIGN VAKAIYARAA
+ {0x11FFF, 0x11FFF, prN}, // Po TAMIL PUNCTUATION END OF TEXT
+ {0x12000, 0x12399, prN}, // Lo [922] CUNEIFORM SIGN A..CUNEIFORM SIGN U U
+ {0x12400, 0x1246E, prN}, // Nl [111] CUNEIFORM NUMERIC SIGN TWO ASH..CUNEIFORM NUMERIC SIGN NINE U VARIANT FORM
+ {0x12470, 0x12474, prN}, // Po [5] CUNEIFORM PUNCTUATION SIGN OLD ASSYRIAN WORD DIVIDER..CUNEIFORM PUNCTUATION SIGN DIAGONAL QUADCOLON
+ {0x12480, 0x12543, prN}, // Lo [196] CUNEIFORM SIGN AB TIMES NUN TENU..CUNEIFORM SIGN ZU5 TIMES THREE DISH TENU
+ {0x12F90, 0x12FF0, prN}, // Lo [97] CYPRO-MINOAN SIGN CM001..CYPRO-MINOAN SIGN CM114
+ {0x12FF1, 0x12FF2, prN}, // Po [2] CYPRO-MINOAN SIGN CM301..CYPRO-MINOAN SIGN CM302
+ {0x13000, 0x1342E, prN}, // Lo [1071] EGYPTIAN HIEROGLYPH A001..EGYPTIAN HIEROGLYPH AA032
+ {0x13430, 0x13438, prN}, // Cf [9] EGYPTIAN HIEROGLYPH VERTICAL JOINER..EGYPTIAN HIEROGLYPH END SEGMENT
+ {0x14400, 0x14646, prN}, // Lo [583] ANATOLIAN HIEROGLYPH A001..ANATOLIAN HIEROGLYPH A530
+ {0x16800, 0x16A38, prN}, // Lo [569] BAMUM LETTER PHASE-A NGKUE MFON..BAMUM LETTER PHASE-F VUEQ
+ {0x16A40, 0x16A5E, prN}, // Lo [31] MRO LETTER TA..MRO LETTER TEK
+ {0x16A60, 0x16A69, prN}, // Nd [10] MRO DIGIT ZERO..MRO DIGIT NINE
+ {0x16A6E, 0x16A6F, prN}, // Po [2] MRO DANDA..MRO DOUBLE DANDA
+ {0x16A70, 0x16ABE, prN}, // Lo [79] TANGSA LETTER OZ..TANGSA LETTER ZA
+ {0x16AC0, 0x16AC9, prN}, // Nd [10] TANGSA DIGIT ZERO..TANGSA DIGIT NINE
+ {0x16AD0, 0x16AED, prN}, // Lo [30] BASSA VAH LETTER ENNI..BASSA VAH LETTER I
+ {0x16AF0, 0x16AF4, prN}, // Mn [5] BASSA VAH COMBINING HIGH TONE..BASSA VAH COMBINING HIGH-LOW TONE
+ {0x16AF5, 0x16AF5, prN}, // Po BASSA VAH FULL STOP
+ {0x16B00, 0x16B2F, prN}, // Lo [48] PAHAWH HMONG VOWEL KEEB..PAHAWH HMONG CONSONANT CAU
+ {0x16B30, 0x16B36, prN}, // Mn [7] PAHAWH HMONG MARK CIM TUB..PAHAWH HMONG MARK CIM TAUM
+ {0x16B37, 0x16B3B, prN}, // Po [5] PAHAWH HMONG SIGN VOS THOM..PAHAWH HMONG SIGN VOS FEEM
+ {0x16B3C, 0x16B3F, prN}, // So [4] PAHAWH HMONG SIGN XYEEM NTXIV..PAHAWH HMONG SIGN XYEEM FAIB
+ {0x16B40, 0x16B43, prN}, // Lm [4] PAHAWH HMONG SIGN VOS SEEV..PAHAWH HMONG SIGN IB YAM
+ {0x16B44, 0x16B44, prN}, // Po PAHAWH HMONG SIGN XAUS
+ {0x16B45, 0x16B45, prN}, // So PAHAWH HMONG SIGN CIM TSOV ROG
+ {0x16B50, 0x16B59, prN}, // Nd [10] PAHAWH HMONG DIGIT ZERO..PAHAWH HMONG DIGIT NINE
+ {0x16B5B, 0x16B61, prN}, // No [7] PAHAWH HMONG NUMBER TENS..PAHAWH HMONG NUMBER TRILLIONS
+ {0x16B63, 0x16B77, prN}, // Lo [21] PAHAWH HMONG SIGN VOS LUB..PAHAWH HMONG SIGN CIM NRES TOS
+ {0x16B7D, 0x16B8F, prN}, // Lo [19] PAHAWH HMONG CLAN SIGN TSHEEJ..PAHAWH HMONG CLAN SIGN VWJ
+ {0x16E40, 0x16E7F, prN}, // L& [64] MEDEFAIDRIN CAPITAL LETTER M..MEDEFAIDRIN SMALL LETTER Y
+ {0x16E80, 0x16E96, prN}, // No [23] MEDEFAIDRIN DIGIT ZERO..MEDEFAIDRIN DIGIT THREE ALTERNATE FORM
+ {0x16E97, 0x16E9A, prN}, // Po [4] MEDEFAIDRIN COMMA..MEDEFAIDRIN EXCLAMATION OH
+ {0x16F00, 0x16F4A, prN}, // Lo [75] MIAO LETTER PA..MIAO LETTER RTE
+ {0x16F4F, 0x16F4F, prN}, // Mn MIAO SIGN CONSONANT MODIFIER BAR
+ {0x16F50, 0x16F50, prN}, // Lo MIAO LETTER NASALIZATION
+ {0x16F51, 0x16F87, prN}, // Mc [55] MIAO SIGN ASPIRATION..MIAO VOWEL SIGN UI
+ {0x16F8F, 0x16F92, prN}, // Mn [4] MIAO TONE RIGHT..MIAO TONE BELOW
+ {0x16F93, 0x16F9F, prN}, // Lm [13] MIAO LETTER TONE-2..MIAO LETTER REFORMED TONE-8
+ {0x16FE0, 0x16FE1, prW}, // Lm [2] TANGUT ITERATION MARK..NUSHU ITERATION MARK
+ {0x16FE2, 0x16FE2, prW}, // Po OLD CHINESE HOOK MARK
+ {0x16FE3, 0x16FE3, prW}, // Lm OLD CHINESE ITERATION MARK
+ {0x16FE4, 0x16FE4, prW}, // Mn KHITAN SMALL SCRIPT FILLER
+ {0x16FF0, 0x16FF1, prW}, // Mc [2] VIETNAMESE ALTERNATE READING MARK CA..VIETNAMESE ALTERNATE READING MARK NHAY
+ {0x17000, 0x187F7, prW}, // Lo [6136] TANGUT IDEOGRAPH-17000..TANGUT IDEOGRAPH-187F7
+ {0x18800, 0x18AFF, prW}, // Lo [768] TANGUT COMPONENT-001..TANGUT COMPONENT-768
+ {0x18B00, 0x18CD5, prW}, // Lo [470] KHITAN SMALL SCRIPT CHARACTER-18B00..KHITAN SMALL SCRIPT CHARACTER-18CD5
+ {0x18D00, 0x18D08, prW}, // Lo [9] TANGUT IDEOGRAPH-18D00..TANGUT IDEOGRAPH-18D08
+ {0x1AFF0, 0x1AFF3, prW}, // Lm [4] KATAKANA LETTER MINNAN TONE-2..KATAKANA LETTER MINNAN TONE-5
+ {0x1AFF5, 0x1AFFB, prW}, // Lm [7] KATAKANA LETTER MINNAN TONE-7..KATAKANA LETTER MINNAN NASALIZED TONE-5
+ {0x1AFFD, 0x1AFFE, prW}, // Lm [2] KATAKANA LETTER MINNAN NASALIZED TONE-7..KATAKANA LETTER MINNAN NASALIZED TONE-8
+ {0x1B000, 0x1B0FF, prW}, // Lo [256] KATAKANA LETTER ARCHAIC E..HENTAIGANA LETTER RE-2
+ {0x1B100, 0x1B122, prW}, // Lo [35] HENTAIGANA LETTER RE-3..KATAKANA LETTER ARCHAIC WU
+ {0x1B150, 0x1B152, prW}, // Lo [3] HIRAGANA LETTER SMALL WI..HIRAGANA LETTER SMALL WO
+ {0x1B164, 0x1B167, prW}, // Lo [4] KATAKANA LETTER SMALL WI..KATAKANA LETTER SMALL N
+ {0x1B170, 0x1B2FB, prW}, // Lo [396] NUSHU CHARACTER-1B170..NUSHU CHARACTER-1B2FB
+ {0x1BC00, 0x1BC6A, prN}, // Lo [107] DUPLOYAN LETTER H..DUPLOYAN LETTER VOCALIC M
+ {0x1BC70, 0x1BC7C, prN}, // Lo [13] DUPLOYAN AFFIX LEFT HORIZONTAL SECANT..DUPLOYAN AFFIX ATTACHED TANGENT HOOK
+ {0x1BC80, 0x1BC88, prN}, // Lo [9] DUPLOYAN AFFIX HIGH ACUTE..DUPLOYAN AFFIX HIGH VERTICAL
+ {0x1BC90, 0x1BC99, prN}, // Lo [10] DUPLOYAN AFFIX LOW ACUTE..DUPLOYAN AFFIX LOW ARROW
+ {0x1BC9C, 0x1BC9C, prN}, // So DUPLOYAN SIGN O WITH CROSS
+ {0x1BC9D, 0x1BC9E, prN}, // Mn [2] DUPLOYAN THICK LETTER SELECTOR..DUPLOYAN DOUBLE MARK
+ {0x1BC9F, 0x1BC9F, prN}, // Po DUPLOYAN PUNCTUATION CHINOOK FULL STOP
+ {0x1BCA0, 0x1BCA3, prN}, // Cf [4] SHORTHAND FORMAT LETTER OVERLAP..SHORTHAND FORMAT UP STEP
+ {0x1CF00, 0x1CF2D, prN}, // Mn [46] ZNAMENNY COMBINING MARK GORAZDO NIZKO S KRYZHEM ON LEFT..ZNAMENNY COMBINING MARK KRYZH ON LEFT
+ {0x1CF30, 0x1CF46, prN}, // Mn [23] ZNAMENNY COMBINING TONAL RANGE MARK MRACHNO..ZNAMENNY PRIZNAK MODIFIER ROG
+ {0x1CF50, 0x1CFC3, prN}, // So [116] ZNAMENNY NEUME KRYUK..ZNAMENNY NEUME PAUK
+ {0x1D000, 0x1D0F5, prN}, // So [246] BYZANTINE MUSICAL SYMBOL PSILI..BYZANTINE MUSICAL SYMBOL GORGON NEO KATO
+ {0x1D100, 0x1D126, prN}, // So [39] MUSICAL SYMBOL SINGLE BARLINE..MUSICAL SYMBOL DRUM CLEF-2
+ {0x1D129, 0x1D164, prN}, // So [60] MUSICAL SYMBOL MULTIPLE MEASURE REST..MUSICAL SYMBOL ONE HUNDRED TWENTY-EIGHTH NOTE
+ {0x1D165, 0x1D166, prN}, // Mc [2] MUSICAL SYMBOL COMBINING STEM..MUSICAL SYMBOL COMBINING SPRECHGESANG STEM
+ {0x1D167, 0x1D169, prN}, // Mn [3] MUSICAL SYMBOL COMBINING TREMOLO-1..MUSICAL SYMBOL COMBINING TREMOLO-3
+ {0x1D16A, 0x1D16C, prN}, // So [3] MUSICAL SYMBOL FINGERED TREMOLO-1..MUSICAL SYMBOL FINGERED TREMOLO-3
+ {0x1D16D, 0x1D172, prN}, // Mc [6] MUSICAL SYMBOL COMBINING AUGMENTATION DOT..MUSICAL SYMBOL COMBINING FLAG-5
+ {0x1D173, 0x1D17A, prN}, // Cf [8] MUSICAL SYMBOL BEGIN BEAM..MUSICAL SYMBOL END PHRASE
+ {0x1D17B, 0x1D182, prN}, // Mn [8] MUSICAL SYMBOL COMBINING ACCENT..MUSICAL SYMBOL COMBINING LOURE
+ {0x1D183, 0x1D184, prN}, // So [2] MUSICAL SYMBOL ARPEGGIATO UP..MUSICAL SYMBOL ARPEGGIATO DOWN
+ {0x1D185, 0x1D18B, prN}, // Mn [7] MUSICAL SYMBOL COMBINING DOIT..MUSICAL SYMBOL COMBINING TRIPLE TONGUE
+ {0x1D18C, 0x1D1A9, prN}, // So [30] MUSICAL SYMBOL RINFORZANDO..MUSICAL SYMBOL DEGREE SLASH
+ {0x1D1AA, 0x1D1AD, prN}, // Mn [4] MUSICAL SYMBOL COMBINING DOWN BOW..MUSICAL SYMBOL COMBINING SNAP PIZZICATO
+ {0x1D1AE, 0x1D1EA, prN}, // So [61] MUSICAL SYMBOL PEDAL MARK..MUSICAL SYMBOL KORON
+ {0x1D200, 0x1D241, prN}, // So [66] GREEK VOCAL NOTATION SYMBOL-1..GREEK INSTRUMENTAL NOTATION SYMBOL-54
+ {0x1D242, 0x1D244, prN}, // Mn [3] COMBINING GREEK MUSICAL TRISEME..COMBINING GREEK MUSICAL PENTASEME
+ {0x1D245, 0x1D245, prN}, // So GREEK MUSICAL LEIMMA
+ {0x1D2E0, 0x1D2F3, prN}, // No [20] MAYAN NUMERAL ZERO..MAYAN NUMERAL NINETEEN
+ {0x1D300, 0x1D356, prN}, // So [87] MONOGRAM FOR EARTH..TETRAGRAM FOR FOSTERING
+ {0x1D360, 0x1D378, prN}, // No [25] COUNTING ROD UNIT DIGIT ONE..TALLY MARK FIVE
+ {0x1D400, 0x1D454, prN}, // L& [85] MATHEMATICAL BOLD CAPITAL A..MATHEMATICAL ITALIC SMALL G
+ {0x1D456, 0x1D49C, prN}, // L& [71] MATHEMATICAL ITALIC SMALL I..MATHEMATICAL SCRIPT CAPITAL A
+ {0x1D49E, 0x1D49F, prN}, // Lu [2] MATHEMATICAL SCRIPT CAPITAL C..MATHEMATICAL SCRIPT CAPITAL D
+ {0x1D4A2, 0x1D4A2, prN}, // Lu MATHEMATICAL SCRIPT CAPITAL G
+ {0x1D4A5, 0x1D4A6, prN}, // Lu [2] MATHEMATICAL SCRIPT CAPITAL J..MATHEMATICAL SCRIPT CAPITAL K
+ {0x1D4A9, 0x1D4AC, prN}, // Lu [4] MATHEMATICAL SCRIPT CAPITAL N..MATHEMATICAL SCRIPT CAPITAL Q
+ {0x1D4AE, 0x1D4B9, prN}, // L& [12] MATHEMATICAL SCRIPT CAPITAL S..MATHEMATICAL SCRIPT SMALL D
+ {0x1D4BB, 0x1D4BB, prN}, // Ll MATHEMATICAL SCRIPT SMALL F
+ {0x1D4BD, 0x1D4C3, prN}, // Ll [7] MATHEMATICAL SCRIPT SMALL H..MATHEMATICAL SCRIPT SMALL N
+ {0x1D4C5, 0x1D505, prN}, // L& [65] MATHEMATICAL SCRIPT SMALL P..MATHEMATICAL FRAKTUR CAPITAL B
+ {0x1D507, 0x1D50A, prN}, // Lu [4] MATHEMATICAL FRAKTUR CAPITAL D..MATHEMATICAL FRAKTUR CAPITAL G
+ {0x1D50D, 0x1D514, prN}, // Lu [8] MATHEMATICAL FRAKTUR CAPITAL J..MATHEMATICAL FRAKTUR CAPITAL Q
+ {0x1D516, 0x1D51C, prN}, // Lu [7] MATHEMATICAL FRAKTUR CAPITAL S..MATHEMATICAL FRAKTUR CAPITAL Y
+ {0x1D51E, 0x1D539, prN}, // L& [28] MATHEMATICAL FRAKTUR SMALL A..MATHEMATICAL DOUBLE-STRUCK CAPITAL B
+ {0x1D53B, 0x1D53E, prN}, // Lu [4] MATHEMATICAL DOUBLE-STRUCK CAPITAL D..MATHEMATICAL DOUBLE-STRUCK CAPITAL G
+ {0x1D540, 0x1D544, prN}, // Lu [5] MATHEMATICAL DOUBLE-STRUCK CAPITAL I..MATHEMATICAL DOUBLE-STRUCK CAPITAL M
+ {0x1D546, 0x1D546, prN}, // Lu MATHEMATICAL DOUBLE-STRUCK CAPITAL O
+ {0x1D54A, 0x1D550, prN}, // Lu [7] MATHEMATICAL DOUBLE-STRUCK CAPITAL S..MATHEMATICAL DOUBLE-STRUCK CAPITAL Y
+ {0x1D552, 0x1D6A5, prN}, // L& [340] MATHEMATICAL DOUBLE-STRUCK SMALL A..MATHEMATICAL ITALIC SMALL DOTLESS J
+ {0x1D6A8, 0x1D6C0, prN}, // Lu [25] MATHEMATICAL BOLD CAPITAL ALPHA..MATHEMATICAL BOLD CAPITAL OMEGA
+ {0x1D6C1, 0x1D6C1, prN}, // Sm MATHEMATICAL BOLD NABLA
+ {0x1D6C2, 0x1D6DA, prN}, // Ll [25] MATHEMATICAL BOLD SMALL ALPHA..MATHEMATICAL BOLD SMALL OMEGA
+ {0x1D6DB, 0x1D6DB, prN}, // Sm MATHEMATICAL BOLD PARTIAL DIFFERENTIAL
+ {0x1D6DC, 0x1D6FA, prN}, // L& [31] MATHEMATICAL BOLD EPSILON SYMBOL..MATHEMATICAL ITALIC CAPITAL OMEGA
+ {0x1D6FB, 0x1D6FB, prN}, // Sm MATHEMATICAL ITALIC NABLA
+ {0x1D6FC, 0x1D714, prN}, // Ll [25] MATHEMATICAL ITALIC SMALL ALPHA..MATHEMATICAL ITALIC SMALL OMEGA
+ {0x1D715, 0x1D715, prN}, // Sm MATHEMATICAL ITALIC PARTIAL DIFFERENTIAL
+ {0x1D716, 0x1D734, prN}, // L& [31] MATHEMATICAL ITALIC EPSILON SYMBOL..MATHEMATICAL BOLD ITALIC CAPITAL OMEGA
+ {0x1D735, 0x1D735, prN}, // Sm MATHEMATICAL BOLD ITALIC NABLA
+ {0x1D736, 0x1D74E, prN}, // Ll [25] MATHEMATICAL BOLD ITALIC SMALL ALPHA..MATHEMATICAL BOLD ITALIC SMALL OMEGA
+ {0x1D74F, 0x1D74F, prN}, // Sm MATHEMATICAL BOLD ITALIC PARTIAL DIFFERENTIAL
+ {0x1D750, 0x1D76E, prN}, // L& [31] MATHEMATICAL BOLD ITALIC EPSILON SYMBOL..MATHEMATICAL SANS-SERIF BOLD CAPITAL OMEGA
+ {0x1D76F, 0x1D76F, prN}, // Sm MATHEMATICAL SANS-SERIF BOLD NABLA
+ {0x1D770, 0x1D788, prN}, // Ll [25] MATHEMATICAL SANS-SERIF BOLD SMALL ALPHA..MATHEMATICAL SANS-SERIF BOLD SMALL OMEGA
+ {0x1D789, 0x1D789, prN}, // Sm MATHEMATICAL SANS-SERIF BOLD PARTIAL DIFFERENTIAL
+ {0x1D78A, 0x1D7A8, prN}, // L& [31] MATHEMATICAL SANS-SERIF BOLD EPSILON SYMBOL..MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL OMEGA
+ {0x1D7A9, 0x1D7A9, prN}, // Sm MATHEMATICAL SANS-SERIF BOLD ITALIC NABLA
+ {0x1D7AA, 0x1D7C2, prN}, // Ll [25] MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL ALPHA..MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL OMEGA
+ {0x1D7C3, 0x1D7C3, prN}, // Sm MATHEMATICAL SANS-SERIF BOLD ITALIC PARTIAL DIFFERENTIAL
+ {0x1D7C4, 0x1D7CB, prN}, // L& [8] MATHEMATICAL SANS-SERIF BOLD ITALIC EPSILON SYMBOL..MATHEMATICAL BOLD SMALL DIGAMMA
+ {0x1D7CE, 0x1D7FF, prN}, // Nd [50] MATHEMATICAL BOLD DIGIT ZERO..MATHEMATICAL MONOSPACE DIGIT NINE
+ {0x1D800, 0x1D9FF, prN}, // So [512] SIGNWRITING HAND-FIST INDEX..SIGNWRITING HEAD
+ {0x1DA00, 0x1DA36, prN}, // Mn [55] SIGNWRITING HEAD RIM..SIGNWRITING AIR SUCKING IN
+ {0x1DA37, 0x1DA3A, prN}, // So [4] SIGNWRITING AIR BLOW SMALL ROTATIONS..SIGNWRITING BREATH EXHALE
+ {0x1DA3B, 0x1DA6C, prN}, // Mn [50] SIGNWRITING MOUTH CLOSED NEUTRAL..SIGNWRITING EXCITEMENT
+ {0x1DA6D, 0x1DA74, prN}, // So [8] SIGNWRITING SHOULDER HIP SPINE..SIGNWRITING TORSO-FLOORPLANE TWISTING
+ {0x1DA75, 0x1DA75, prN}, // Mn SIGNWRITING UPPER BODY TILTING FROM HIP JOINTS
+ {0x1DA76, 0x1DA83, prN}, // So [14] SIGNWRITING LIMB COMBINATION..SIGNWRITING LOCATION DEPTH
+ {0x1DA84, 0x1DA84, prN}, // Mn SIGNWRITING LOCATION HEAD NECK
+ {0x1DA85, 0x1DA86, prN}, // So [2] SIGNWRITING LOCATION TORSO..SIGNWRITING LOCATION LIMBS DIGITS
+ {0x1DA87, 0x1DA8B, prN}, // Po [5] SIGNWRITING COMMA..SIGNWRITING PARENTHESIS
+ {0x1DA9B, 0x1DA9F, prN}, // Mn [5] SIGNWRITING FILL MODIFIER-2..SIGNWRITING FILL MODIFIER-6
+ {0x1DAA1, 0x1DAAF, prN}, // Mn [15] SIGNWRITING ROTATION MODIFIER-2..SIGNWRITING ROTATION MODIFIER-16
+ {0x1DF00, 0x1DF09, prN}, // Ll [10] LATIN SMALL LETTER FENG DIGRAPH WITH TRILL..LATIN SMALL LETTER T WITH HOOK AND RETROFLEX HOOK
+ {0x1DF0A, 0x1DF0A, prN}, // Lo LATIN LETTER RETROFLEX CLICK WITH RETROFLEX HOOK
+ {0x1DF0B, 0x1DF1E, prN}, // Ll [20] LATIN SMALL LETTER ESH WITH DOUBLE BAR..LATIN SMALL LETTER S WITH CURL
+ {0x1E000, 0x1E006, prN}, // Mn [7] COMBINING GLAGOLITIC LETTER AZU..COMBINING GLAGOLITIC LETTER ZHIVETE
+ {0x1E008, 0x1E018, prN}, // Mn [17] COMBINING GLAGOLITIC LETTER ZEMLJA..COMBINING GLAGOLITIC LETTER HERU
+ {0x1E01B, 0x1E021, prN}, // Mn [7] COMBINING GLAGOLITIC LETTER SHTA..COMBINING GLAGOLITIC LETTER YATI
+ {0x1E023, 0x1E024, prN}, // Mn [2] COMBINING GLAGOLITIC LETTER YU..COMBINING GLAGOLITIC LETTER SMALL YUS
+ {0x1E026, 0x1E02A, prN}, // Mn [5] COMBINING GLAGOLITIC LETTER YO..COMBINING GLAGOLITIC LETTER FITA
+ {0x1E100, 0x1E12C, prN}, // Lo [45] NYIAKENG PUACHUE HMONG LETTER MA..NYIAKENG PUACHUE HMONG LETTER W
+ {0x1E130, 0x1E136, prN}, // Mn [7] NYIAKENG PUACHUE HMONG TONE-B..NYIAKENG PUACHUE HMONG TONE-D
+ {0x1E137, 0x1E13D, prN}, // Lm [7] NYIAKENG PUACHUE HMONG SIGN FOR PERSON..NYIAKENG PUACHUE HMONG SYLLABLE LENGTHENER
+ {0x1E140, 0x1E149, prN}, // Nd [10] NYIAKENG PUACHUE HMONG DIGIT ZERO..NYIAKENG PUACHUE HMONG DIGIT NINE
+ {0x1E14E, 0x1E14E, prN}, // Lo NYIAKENG PUACHUE HMONG LOGOGRAM NYAJ
+ {0x1E14F, 0x1E14F, prN}, // So NYIAKENG PUACHUE HMONG CIRCLED CA
+ {0x1E290, 0x1E2AD, prN}, // Lo [30] TOTO LETTER PA..TOTO LETTER A
+ {0x1E2AE, 0x1E2AE, prN}, // Mn TOTO SIGN RISING TONE
+ {0x1E2C0, 0x1E2EB, prN}, // Lo [44] WANCHO LETTER AA..WANCHO LETTER YIH
+ {0x1E2EC, 0x1E2EF, prN}, // Mn [4] WANCHO TONE TUP..WANCHO TONE KOINI
+ {0x1E2F0, 0x1E2F9, prN}, // Nd [10] WANCHO DIGIT ZERO..WANCHO DIGIT NINE
+ {0x1E2FF, 0x1E2FF, prN}, // Sc WANCHO NGUN SIGN
+ {0x1E7E0, 0x1E7E6, prN}, // Lo [7] ETHIOPIC SYLLABLE HHYA..ETHIOPIC SYLLABLE HHYO
+ {0x1E7E8, 0x1E7EB, prN}, // Lo [4] ETHIOPIC SYLLABLE GURAGE HHWA..ETHIOPIC SYLLABLE HHWE
+ {0x1E7ED, 0x1E7EE, prN}, // Lo [2] ETHIOPIC SYLLABLE GURAGE MWI..ETHIOPIC SYLLABLE GURAGE MWEE
+ {0x1E7F0, 0x1E7FE, prN}, // Lo [15] ETHIOPIC SYLLABLE GURAGE QWI..ETHIOPIC SYLLABLE GURAGE PWEE
+ {0x1E800, 0x1E8C4, prN}, // Lo [197] MENDE KIKAKUI SYLLABLE M001 KI..MENDE KIKAKUI SYLLABLE M060 NYON
+ {0x1E8C7, 0x1E8CF, prN}, // No [9] MENDE KIKAKUI DIGIT ONE..MENDE KIKAKUI DIGIT NINE
+ {0x1E8D0, 0x1E8D6, prN}, // Mn [7] MENDE KIKAKUI COMBINING NUMBER TEENS..MENDE KIKAKUI COMBINING NUMBER MILLIONS
+ {0x1E900, 0x1E943, prN}, // L& [68] ADLAM CAPITAL LETTER ALIF..ADLAM SMALL LETTER SHA
+ {0x1E944, 0x1E94A, prN}, // Mn [7] ADLAM ALIF LENGTHENER..ADLAM NUKTA
+ {0x1E94B, 0x1E94B, prN}, // Lm ADLAM NASALIZATION MARK
+ {0x1E950, 0x1E959, prN}, // Nd [10] ADLAM DIGIT ZERO..ADLAM DIGIT NINE
+ {0x1E95E, 0x1E95F, prN}, // Po [2] ADLAM INITIAL EXCLAMATION MARK..ADLAM INITIAL QUESTION MARK
+ {0x1EC71, 0x1ECAB, prN}, // No [59] INDIC SIYAQ NUMBER ONE..INDIC SIYAQ NUMBER PREFIXED NINE
+ {0x1ECAC, 0x1ECAC, prN}, // So INDIC SIYAQ PLACEHOLDER
+ {0x1ECAD, 0x1ECAF, prN}, // No [3] INDIC SIYAQ FRACTION ONE QUARTER..INDIC SIYAQ FRACTION THREE QUARTERS
+ {0x1ECB0, 0x1ECB0, prN}, // Sc INDIC SIYAQ RUPEE MARK
+ {0x1ECB1, 0x1ECB4, prN}, // No [4] INDIC SIYAQ NUMBER ALTERNATE ONE..INDIC SIYAQ ALTERNATE LAKH MARK
+ {0x1ED01, 0x1ED2D, prN}, // No [45] OTTOMAN SIYAQ NUMBER ONE..OTTOMAN SIYAQ NUMBER NINETY THOUSAND
+ {0x1ED2E, 0x1ED2E, prN}, // So OTTOMAN SIYAQ MARRATAN
+ {0x1ED2F, 0x1ED3D, prN}, // No [15] OTTOMAN SIYAQ ALTERNATE NUMBER TWO..OTTOMAN SIYAQ FRACTION ONE SIXTH
+ {0x1EE00, 0x1EE03, prN}, // Lo [4] ARABIC MATHEMATICAL ALEF..ARABIC MATHEMATICAL DAL
+ {0x1EE05, 0x1EE1F, prN}, // Lo [27] ARABIC MATHEMATICAL WAW..ARABIC MATHEMATICAL DOTLESS QAF
+ {0x1EE21, 0x1EE22, prN}, // Lo [2] ARABIC MATHEMATICAL INITIAL BEH..ARABIC MATHEMATICAL INITIAL JEEM
+ {0x1EE24, 0x1EE24, prN}, // Lo ARABIC MATHEMATICAL INITIAL HEH
+ {0x1EE27, 0x1EE27, prN}, // Lo ARABIC MATHEMATICAL INITIAL HAH
+ {0x1EE29, 0x1EE32, prN}, // Lo [10] ARABIC MATHEMATICAL INITIAL YEH..ARABIC MATHEMATICAL INITIAL QAF
+ {0x1EE34, 0x1EE37, prN}, // Lo [4] ARABIC MATHEMATICAL INITIAL SHEEN..ARABIC MATHEMATICAL INITIAL KHAH
+ {0x1EE39, 0x1EE39, prN}, // Lo ARABIC MATHEMATICAL INITIAL DAD
+ {0x1EE3B, 0x1EE3B, prN}, // Lo ARABIC MATHEMATICAL INITIAL GHAIN
+ {0x1EE42, 0x1EE42, prN}, // Lo ARABIC MATHEMATICAL TAILED JEEM
+ {0x1EE47, 0x1EE47, prN}, // Lo ARABIC MATHEMATICAL TAILED HAH
+ {0x1EE49, 0x1EE49, prN}, // Lo ARABIC MATHEMATICAL TAILED YEH
+ {0x1EE4B, 0x1EE4B, prN}, // Lo ARABIC MATHEMATICAL TAILED LAM
+ {0x1EE4D, 0x1EE4F, prN}, // Lo [3] ARABIC MATHEMATICAL TAILED NOON..ARABIC MATHEMATICAL TAILED AIN
+ {0x1EE51, 0x1EE52, prN}, // Lo [2] ARABIC MATHEMATICAL TAILED SAD..ARABIC MATHEMATICAL TAILED QAF
+ {0x1EE54, 0x1EE54, prN}, // Lo ARABIC MATHEMATICAL TAILED SHEEN
+ {0x1EE57, 0x1EE57, prN}, // Lo ARABIC MATHEMATICAL TAILED KHAH
+ {0x1EE59, 0x1EE59, prN}, // Lo ARABIC MATHEMATICAL TAILED DAD
+ {0x1EE5B, 0x1EE5B, prN}, // Lo ARABIC MATHEMATICAL TAILED GHAIN
+ {0x1EE5D, 0x1EE5D, prN}, // Lo ARABIC MATHEMATICAL TAILED DOTLESS NOON
+ {0x1EE5F, 0x1EE5F, prN}, // Lo ARABIC MATHEMATICAL TAILED DOTLESS QAF
+ {0x1EE61, 0x1EE62, prN}, // Lo [2] ARABIC MATHEMATICAL STRETCHED BEH..ARABIC MATHEMATICAL STRETCHED JEEM
+ {0x1EE64, 0x1EE64, prN}, // Lo ARABIC MATHEMATICAL STRETCHED HEH
+ {0x1EE67, 0x1EE6A, prN}, // Lo [4] ARABIC MATHEMATICAL STRETCHED HAH..ARABIC MATHEMATICAL STRETCHED KAF
+ {0x1EE6C, 0x1EE72, prN}, // Lo [7] ARABIC MATHEMATICAL STRETCHED MEEM..ARABIC MATHEMATICAL STRETCHED QAF
+ {0x1EE74, 0x1EE77, prN}, // Lo [4] ARABIC MATHEMATICAL STRETCHED SHEEN..ARABIC MATHEMATICAL STRETCHED KHAH
+ {0x1EE79, 0x1EE7C, prN}, // Lo [4] ARABIC MATHEMATICAL STRETCHED DAD..ARABIC MATHEMATICAL STRETCHED DOTLESS BEH
+ {0x1EE7E, 0x1EE7E, prN}, // Lo ARABIC MATHEMATICAL STRETCHED DOTLESS FEH
+ {0x1EE80, 0x1EE89, prN}, // Lo [10] ARABIC MATHEMATICAL LOOPED ALEF..ARABIC MATHEMATICAL LOOPED YEH
+ {0x1EE8B, 0x1EE9B, prN}, // Lo [17] ARABIC MATHEMATICAL LOOPED LAM..ARABIC MATHEMATICAL LOOPED GHAIN
+ {0x1EEA1, 0x1EEA3, prN}, // Lo [3] ARABIC MATHEMATICAL DOUBLE-STRUCK BEH..ARABIC MATHEMATICAL DOUBLE-STRUCK DAL
+ {0x1EEA5, 0x1EEA9, prN}, // Lo [5] ARABIC MATHEMATICAL DOUBLE-STRUCK WAW..ARABIC MATHEMATICAL DOUBLE-STRUCK YEH
+ {0x1EEAB, 0x1EEBB, prN}, // Lo [17] ARABIC MATHEMATICAL DOUBLE-STRUCK LAM..ARABIC MATHEMATICAL DOUBLE-STRUCK GHAIN
+ {0x1EEF0, 0x1EEF1, prN}, // Sm [2] ARABIC MATHEMATICAL OPERATOR MEEM WITH HAH WITH TATWEEL..ARABIC MATHEMATICAL OPERATOR HAH WITH DAL
+ {0x1F000, 0x1F003, prN}, // So [4] MAHJONG TILE EAST WIND..MAHJONG TILE NORTH WIND
+ {0x1F004, 0x1F004, prW}, // So MAHJONG TILE RED DRAGON
+ {0x1F005, 0x1F02B, prN}, // So [39] MAHJONG TILE GREEN DRAGON..MAHJONG TILE BACK
+ {0x1F030, 0x1F093, prN}, // So [100] DOMINO TILE HORIZONTAL BACK..DOMINO TILE VERTICAL-06-06
+ {0x1F0A0, 0x1F0AE, prN}, // So [15] PLAYING CARD BACK..PLAYING CARD KING OF SPADES
+ {0x1F0B1, 0x1F0BF, prN}, // So [15] PLAYING CARD ACE OF HEARTS..PLAYING CARD RED JOKER
+ {0x1F0C1, 0x1F0CE, prN}, // So [14] PLAYING CARD ACE OF DIAMONDS..PLAYING CARD KING OF DIAMONDS
+ {0x1F0CF, 0x1F0CF, prW}, // So PLAYING CARD BLACK JOKER
+ {0x1F0D1, 0x1F0F5, prN}, // So [37] PLAYING CARD ACE OF CLUBS..PLAYING CARD TRUMP-21
+ {0x1F100, 0x1F10A, prA}, // No [11] DIGIT ZERO FULL STOP..DIGIT NINE COMMA
+ {0x1F10B, 0x1F10C, prN}, // No [2] DINGBAT CIRCLED SANS-SERIF DIGIT ZERO..DINGBAT NEGATIVE CIRCLED SANS-SERIF DIGIT ZERO
+ {0x1F10D, 0x1F10F, prN}, // So [3] CIRCLED ZERO WITH SLASH..CIRCLED DOLLAR SIGN WITH OVERLAID BACKSLASH
+ {0x1F110, 0x1F12D, prA}, // So [30] PARENTHESIZED LATIN CAPITAL LETTER A..CIRCLED CD
+ {0x1F12E, 0x1F12F, prN}, // So [2] CIRCLED WZ..COPYLEFT SYMBOL
+ {0x1F130, 0x1F169, prA}, // So [58] SQUARED LATIN CAPITAL LETTER A..NEGATIVE CIRCLED LATIN CAPITAL LETTER Z
+ {0x1F16A, 0x1F16F, prN}, // So [6] RAISED MC SIGN..CIRCLED HUMAN FIGURE
+ {0x1F170, 0x1F18D, prA}, // So [30] NEGATIVE SQUARED LATIN CAPITAL LETTER A..NEGATIVE SQUARED SA
+ {0x1F18E, 0x1F18E, prW}, // So NEGATIVE SQUARED AB
+ {0x1F18F, 0x1F190, prA}, // So [2] NEGATIVE SQUARED WC..SQUARE DJ
+ {0x1F191, 0x1F19A, prW}, // So [10] SQUARED CL..SQUARED VS
+ {0x1F19B, 0x1F1AC, prA}, // So [18] SQUARED THREE D..SQUARED VOD
+ {0x1F1AD, 0x1F1AD, prN}, // So MASK WORK SYMBOL
+ {0x1F1E6, 0x1F1FF, prN}, // So [26] REGIONAL INDICATOR SYMBOL LETTER A..REGIONAL INDICATOR SYMBOL LETTER Z
+ {0x1F200, 0x1F202, prW}, // So [3] SQUARE HIRAGANA HOKA..SQUARED KATAKANA SA
+ {0x1F210, 0x1F23B, prW}, // So [44] SQUARED CJK UNIFIED IDEOGRAPH-624B..SQUARED CJK UNIFIED IDEOGRAPH-914D
+ {0x1F240, 0x1F248, prW}, // So [9] TORTOISE SHELL BRACKETED CJK UNIFIED IDEOGRAPH-672C..TORTOISE SHELL BRACKETED CJK UNIFIED IDEOGRAPH-6557
+ {0x1F250, 0x1F251, prW}, // So [2] CIRCLED IDEOGRAPH ADVANTAGE..CIRCLED IDEOGRAPH ACCEPT
+ {0x1F260, 0x1F265, prW}, // So [6] ROUNDED SYMBOL FOR FU..ROUNDED SYMBOL FOR CAI
+ {0x1F300, 0x1F320, prW}, // So [33] CYCLONE..SHOOTING STAR
+ {0x1F321, 0x1F32C, prN}, // So [12] THERMOMETER..WIND BLOWING FACE
+ {0x1F32D, 0x1F335, prW}, // So [9] HOT DOG..CACTUS
+ {0x1F336, 0x1F336, prN}, // So HOT PEPPER
+ {0x1F337, 0x1F37C, prW}, // So [70] TULIP..BABY BOTTLE
+ {0x1F37D, 0x1F37D, prN}, // So FORK AND KNIFE WITH PLATE
+ {0x1F37E, 0x1F393, prW}, // So [22] BOTTLE WITH POPPING CORK..GRADUATION CAP
+ {0x1F394, 0x1F39F, prN}, // So [12] HEART WITH TIP ON THE LEFT..ADMISSION TICKETS
+ {0x1F3A0, 0x1F3CA, prW}, // So [43] CAROUSEL HORSE..SWIMMER
+ {0x1F3CB, 0x1F3CE, prN}, // So [4] WEIGHT LIFTER..RACING CAR
+ {0x1F3CF, 0x1F3D3, prW}, // So [5] CRICKET BAT AND BALL..TABLE TENNIS PADDLE AND BALL
+ {0x1F3D4, 0x1F3DF, prN}, // So [12] SNOW CAPPED MOUNTAIN..STADIUM
+ {0x1F3E0, 0x1F3F0, prW}, // So [17] HOUSE BUILDING..EUROPEAN CASTLE
+ {0x1F3F1, 0x1F3F3, prN}, // So [3] WHITE PENNANT..WAVING WHITE FLAG
+ {0x1F3F4, 0x1F3F4, prW}, // So WAVING BLACK FLAG
+ {0x1F3F5, 0x1F3F7, prN}, // So [3] ROSETTE..LABEL
+ {0x1F3F8, 0x1F3FA, prW}, // So [3] BADMINTON RACQUET AND SHUTTLECOCK..AMPHORA
+ {0x1F3FB, 0x1F3FF, prW}, // Sk [5] EMOJI MODIFIER FITZPATRICK TYPE-1-2..EMOJI MODIFIER FITZPATRICK TYPE-6
+ {0x1F400, 0x1F43E, prW}, // So [63] RAT..PAW PRINTS
+ {0x1F43F, 0x1F43F, prN}, // So CHIPMUNK
+ {0x1F440, 0x1F440, prW}, // So EYES
+ {0x1F441, 0x1F441, prN}, // So EYE
+ {0x1F442, 0x1F4FC, prW}, // So [187] EAR..VIDEOCASSETTE
+ {0x1F4FD, 0x1F4FE, prN}, // So [2] FILM PROJECTOR..PORTABLE STEREO
+ {0x1F4FF, 0x1F53D, prW}, // So [63] PRAYER BEADS..DOWN-POINTING SMALL RED TRIANGLE
+ {0x1F53E, 0x1F54A, prN}, // So [13] LOWER RIGHT SHADOWED WHITE CIRCLE..DOVE OF PEACE
+ {0x1F54B, 0x1F54E, prW}, // So [4] KAABA..MENORAH WITH NINE BRANCHES
+ {0x1F54F, 0x1F54F, prN}, // So BOWL OF HYGIEIA
+ {0x1F550, 0x1F567, prW}, // So [24] CLOCK FACE ONE OCLOCK..CLOCK FACE TWELVE-THIRTY
+ {0x1F568, 0x1F579, prN}, // So [18] RIGHT SPEAKER..JOYSTICK
+ {0x1F57A, 0x1F57A, prW}, // So MAN DANCING
+ {0x1F57B, 0x1F594, prN}, // So [26] LEFT HAND TELEPHONE RECEIVER..REVERSED VICTORY HAND
+ {0x1F595, 0x1F596, prW}, // So [2] REVERSED HAND WITH MIDDLE FINGER EXTENDED..RAISED HAND WITH PART BETWEEN MIDDLE AND RING FINGERS
+ {0x1F597, 0x1F5A3, prN}, // So [13] WHITE DOWN POINTING LEFT HAND INDEX..BLACK DOWN POINTING BACKHAND INDEX
+ {0x1F5A4, 0x1F5A4, prW}, // So BLACK HEART
+ {0x1F5A5, 0x1F5FA, prN}, // So [86] DESKTOP COMPUTER..WORLD MAP
+ {0x1F5FB, 0x1F5FF, prW}, // So [5] MOUNT FUJI..MOYAI
+ {0x1F600, 0x1F64F, prW}, // So [80] GRINNING FACE..PERSON WITH FOLDED HANDS
+ {0x1F650, 0x1F67F, prN}, // So [48] NORTH WEST POINTING LEAF..REVERSE CHECKER BOARD
+ {0x1F680, 0x1F6C5, prW}, // So [70] ROCKET..LEFT LUGGAGE
+ {0x1F6C6, 0x1F6CB, prN}, // So [6] TRIANGLE WITH ROUNDED CORNERS..COUCH AND LAMP
+ {0x1F6CC, 0x1F6CC, prW}, // So SLEEPING ACCOMMODATION
+ {0x1F6CD, 0x1F6CF, prN}, // So [3] SHOPPING BAGS..BED
+ {0x1F6D0, 0x1F6D2, prW}, // So [3] PLACE OF WORSHIP..SHOPPING TROLLEY
+ {0x1F6D3, 0x1F6D4, prN}, // So [2] STUPA..PAGODA
+ {0x1F6D5, 0x1F6D7, prW}, // So [3] HINDU TEMPLE..ELEVATOR
+ {0x1F6DD, 0x1F6DF, prW}, // So [3] PLAYGROUND SLIDE..RING BUOY
+ {0x1F6E0, 0x1F6EA, prN}, // So [11] HAMMER AND WRENCH..NORTHEAST-POINTING AIRPLANE
+ {0x1F6EB, 0x1F6EC, prW}, // So [2] AIRPLANE DEPARTURE..AIRPLANE ARRIVING
+ {0x1F6F0, 0x1F6F3, prN}, // So [4] SATELLITE..PASSENGER SHIP
+ {0x1F6F4, 0x1F6FC, prW}, // So [9] SCOOTER..ROLLER SKATE
+ {0x1F700, 0x1F773, prN}, // So [116] ALCHEMICAL SYMBOL FOR QUINTESSENCE..ALCHEMICAL SYMBOL FOR HALF OUNCE
+ {0x1F780, 0x1F7D8, prN}, // So [89] BLACK LEFT-POINTING ISOSCELES RIGHT TRIANGLE..NEGATIVE CIRCLED SQUARE
+ {0x1F7E0, 0x1F7EB, prW}, // So [12] LARGE ORANGE CIRCLE..LARGE BROWN SQUARE
+ {0x1F7F0, 0x1F7F0, prW}, // So HEAVY EQUALS SIGN
+ {0x1F800, 0x1F80B, prN}, // So [12] LEFTWARDS ARROW WITH SMALL TRIANGLE ARROWHEAD..DOWNWARDS ARROW WITH LARGE TRIANGLE ARROWHEAD
+ {0x1F810, 0x1F847, prN}, // So [56] LEFTWARDS ARROW WITH SMALL EQUILATERAL ARROWHEAD..DOWNWARDS HEAVY ARROW
+ {0x1F850, 0x1F859, prN}, // So [10] LEFTWARDS SANS-SERIF ARROW..UP DOWN SANS-SERIF ARROW
+ {0x1F860, 0x1F887, prN}, // So [40] WIDE-HEADED LEFTWARDS LIGHT BARB ARROW..WIDE-HEADED SOUTH WEST VERY HEAVY BARB ARROW
+ {0x1F890, 0x1F8AD, prN}, // So [30] LEFTWARDS TRIANGLE ARROWHEAD..WHITE ARROW SHAFT WIDTH TWO THIRDS
+ {0x1F8B0, 0x1F8B1, prN}, // So [2] ARROW POINTING UPWARDS THEN NORTH WEST..ARROW POINTING RIGHTWARDS THEN CURVING SOUTH WEST
+ {0x1F900, 0x1F90B, prN}, // So [12] CIRCLED CROSS FORMEE WITH FOUR DOTS..DOWNWARD FACING NOTCHED HOOK WITH DOT
+ {0x1F90C, 0x1F93A, prW}, // So [47] PINCHED FINGERS..FENCER
+ {0x1F93B, 0x1F93B, prN}, // So MODERN PENTATHLON
+ {0x1F93C, 0x1F945, prW}, // So [10] WRESTLERS..GOAL NET
+ {0x1F946, 0x1F946, prN}, // So RIFLE
+ {0x1F947, 0x1F9FF, prW}, // So [185] FIRST PLACE MEDAL..NAZAR AMULET
+ {0x1FA00, 0x1FA53, prN}, // So [84] NEUTRAL CHESS KING..BLACK CHESS KNIGHT-BISHOP
+ {0x1FA60, 0x1FA6D, prN}, // So [14] XIANGQI RED GENERAL..XIANGQI BLACK SOLDIER
+ {0x1FA70, 0x1FA74, prW}, // So [5] BALLET SHOES..THONG SANDAL
+ {0x1FA78, 0x1FA7C, prW}, // So [5] DROP OF BLOOD..CRUTCH
+ {0x1FA80, 0x1FA86, prW}, // So [7] YO-YO..NESTING DOLLS
+ {0x1FA90, 0x1FAAC, prW}, // So [29] RINGED PLANET..HAMSA
+ {0x1FAB0, 0x1FABA, prW}, // So [11] FLY..NEST WITH EGGS
+ {0x1FAC0, 0x1FAC5, prW}, // So [6] ANATOMICAL HEART..PERSON WITH CROWN
+ {0x1FAD0, 0x1FAD9, prW}, // So [10] BLUEBERRIES..JAR
+ {0x1FAE0, 0x1FAE7, prW}, // So [8] MELTING FACE..BUBBLES
+ {0x1FAF0, 0x1FAF6, prW}, // So [7] HAND WITH INDEX FINGER AND THUMB CROSSED..HEART HANDS
+ {0x1FB00, 0x1FB92, prN}, // So [147] BLOCK SEXTANT-1..UPPER HALF INVERSE MEDIUM SHADE AND LOWER HALF BLOCK
+ {0x1FB94, 0x1FBCA, prN}, // So [55] LEFT HALF INVERSE MEDIUM SHADE AND RIGHT HALF BLOCK..WHITE UP-POINTING CHEVRON
+ {0x1FBF0, 0x1FBF9, prN}, // Nd [10] SEGMENTED DIGIT ZERO..SEGMENTED DIGIT NINE
+ {0x20000, 0x2A6DF, prW}, // Lo [42720] CJK UNIFIED IDEOGRAPH-20000..CJK UNIFIED IDEOGRAPH-2A6DF
+ {0x2A6E0, 0x2A6FF, prW}, // Cn [32] ..
+ {0x2A700, 0x2B738, prW}, // Lo [4153] CJK UNIFIED IDEOGRAPH-2A700..CJK UNIFIED IDEOGRAPH-2B738
+ {0x2B739, 0x2B73F, prW}, // Cn [7] ..
+ {0x2B740, 0x2B81D, prW}, // Lo [222] CJK UNIFIED IDEOGRAPH-2B740..CJK UNIFIED IDEOGRAPH-2B81D
+ {0x2B81E, 0x2B81F, prW}, // Cn [2] ..
+ {0x2B820, 0x2CEA1, prW}, // Lo [5762] CJK UNIFIED IDEOGRAPH-2B820..CJK UNIFIED IDEOGRAPH-2CEA1
+ {0x2CEA2, 0x2CEAF, prW}, // Cn [14] ..
+ {0x2CEB0, 0x2EBE0, prW}, // Lo [7473] CJK UNIFIED IDEOGRAPH-2CEB0..CJK UNIFIED IDEOGRAPH-2EBE0
+ {0x2EBE1, 0x2F7FF, prW}, // Cn [3103] ..
+ {0x2F800, 0x2FA1D, prW}, // Lo [542] CJK COMPATIBILITY IDEOGRAPH-2F800..CJK COMPATIBILITY IDEOGRAPH-2FA1D
+ {0x2FA1E, 0x2FA1F, prW}, // Cn [2] ..
+ {0x2FA20, 0x2FFFD, prW}, // Cn [1502] ..
+ {0x30000, 0x3134A, prW}, // Lo [4939] CJK UNIFIED IDEOGRAPH-30000..CJK UNIFIED IDEOGRAPH-3134A
+ {0x3134B, 0x3FFFD, prW}, // Cn [60595] ..
+ {0xE0001, 0xE0001, prN}, // Cf LANGUAGE TAG
+ {0xE0020, 0xE007F, prN}, // Cf [96] TAG SPACE..CANCEL TAG
+ {0xE0100, 0xE01EF, prA}, // Mn [240] VARIATION SELECTOR-17..VARIATION SELECTOR-256
+ {0xF0000, 0xFFFFD, prA}, // Co [65534] ..
+ {0x100000, 0x10FFFD, prA}, // Co [65534] ..
+}
diff --git a/vendor/github.com/rivo/uniseg/emojipresentation.go b/vendor/github.com/rivo/uniseg/emojipresentation.go
new file mode 100644
index 0000000000..fd0f7451af
--- /dev/null
+++ b/vendor/github.com/rivo/uniseg/emojipresentation.go
@@ -0,0 +1,285 @@
+package uniseg
+
+// Code generated via go generate from gen_properties.go. DO NOT EDIT.
+
+// emojiPresentation are taken from
+//
+// and
+// https://unicode.org/Public/14.0.0/ucd/emoji/emoji-data.txt
+// ("Extended_Pictographic" only)
+// on September 10, 2022. See https://www.unicode.org/license.html for the Unicode
+// license agreement.
+var emojiPresentation = [][3]int{
+ {0x231A, 0x231B, prEmojiPresentation}, // E0.6 [2] (⌚..⌛) watch..hourglass done
+ {0x23E9, 0x23EC, prEmojiPresentation}, // E0.6 [4] (⏩..⏬) fast-forward button..fast down button
+ {0x23F0, 0x23F0, prEmojiPresentation}, // E0.6 [1] (⏰) alarm clock
+ {0x23F3, 0x23F3, prEmojiPresentation}, // E0.6 [1] (⏳) hourglass not done
+ {0x25FD, 0x25FE, prEmojiPresentation}, // E0.6 [2] (◽..◾) white medium-small square..black medium-small square
+ {0x2614, 0x2615, prEmojiPresentation}, // E0.6 [2] (☔..☕) umbrella with rain drops..hot beverage
+ {0x2648, 0x2653, prEmojiPresentation}, // E0.6 [12] (♈..♓) Aries..Pisces
+ {0x267F, 0x267F, prEmojiPresentation}, // E0.6 [1] (♿) wheelchair symbol
+ {0x2693, 0x2693, prEmojiPresentation}, // E0.6 [1] (⚓) anchor
+ {0x26A1, 0x26A1, prEmojiPresentation}, // E0.6 [1] (⚡) high voltage
+ {0x26AA, 0x26AB, prEmojiPresentation}, // E0.6 [2] (⚪..⚫) white circle..black circle
+ {0x26BD, 0x26BE, prEmojiPresentation}, // E0.6 [2] (⚽..⚾) soccer ball..baseball
+ {0x26C4, 0x26C5, prEmojiPresentation}, // E0.6 [2] (⛄..⛅) snowman without snow..sun behind cloud
+ {0x26CE, 0x26CE, prEmojiPresentation}, // E0.6 [1] (⛎) Ophiuchus
+ {0x26D4, 0x26D4, prEmojiPresentation}, // E0.6 [1] (⛔) no entry
+ {0x26EA, 0x26EA, prEmojiPresentation}, // E0.6 [1] (⛪) church
+ {0x26F2, 0x26F3, prEmojiPresentation}, // E0.6 [2] (⛲..⛳) fountain..flag in hole
+ {0x26F5, 0x26F5, prEmojiPresentation}, // E0.6 [1] (⛵) sailboat
+ {0x26FA, 0x26FA, prEmojiPresentation}, // E0.6 [1] (⛺) tent
+ {0x26FD, 0x26FD, prEmojiPresentation}, // E0.6 [1] (⛽) fuel pump
+ {0x2705, 0x2705, prEmojiPresentation}, // E0.6 [1] (✅) check mark button
+ {0x270A, 0x270B, prEmojiPresentation}, // E0.6 [2] (✊..✋) raised fist..raised hand
+ {0x2728, 0x2728, prEmojiPresentation}, // E0.6 [1] (✨) sparkles
+ {0x274C, 0x274C, prEmojiPresentation}, // E0.6 [1] (❌) cross mark
+ {0x274E, 0x274E, prEmojiPresentation}, // E0.6 [1] (❎) cross mark button
+ {0x2753, 0x2755, prEmojiPresentation}, // E0.6 [3] (❓..❕) red question mark..white exclamation mark
+ {0x2757, 0x2757, prEmojiPresentation}, // E0.6 [1] (❗) red exclamation mark
+ {0x2795, 0x2797, prEmojiPresentation}, // E0.6 [3] (➕..➗) plus..divide
+ {0x27B0, 0x27B0, prEmojiPresentation}, // E0.6 [1] (➰) curly loop
+ {0x27BF, 0x27BF, prEmojiPresentation}, // E1.0 [1] (➿) double curly loop
+ {0x2B1B, 0x2B1C, prEmojiPresentation}, // E0.6 [2] (⬛..⬜) black large square..white large square
+ {0x2B50, 0x2B50, prEmojiPresentation}, // E0.6 [1] (⭐) star
+ {0x2B55, 0x2B55, prEmojiPresentation}, // E0.6 [1] (⭕) hollow red circle
+ {0x1F004, 0x1F004, prEmojiPresentation}, // E0.6 [1] (🀄) mahjong red dragon
+ {0x1F0CF, 0x1F0CF, prEmojiPresentation}, // E0.6 [1] (🃏) joker
+ {0x1F18E, 0x1F18E, prEmojiPresentation}, // E0.6 [1] (🆎) AB button (blood type)
+ {0x1F191, 0x1F19A, prEmojiPresentation}, // E0.6 [10] (🆑..🆚) CL button..VS button
+ {0x1F1E6, 0x1F1FF, prEmojiPresentation}, // E0.0 [26] (🇦..🇿) regional indicator symbol letter a..regional indicator symbol letter z
+ {0x1F201, 0x1F201, prEmojiPresentation}, // E0.6 [1] (🈁) Japanese “here” button
+ {0x1F21A, 0x1F21A, prEmojiPresentation}, // E0.6 [1] (🈚) Japanese “free of charge” button
+ {0x1F22F, 0x1F22F, prEmojiPresentation}, // E0.6 [1] (🈯) Japanese “reserved” button
+ {0x1F232, 0x1F236, prEmojiPresentation}, // E0.6 [5] (🈲..🈶) Japanese “prohibited” button..Japanese “not free of charge” button
+ {0x1F238, 0x1F23A, prEmojiPresentation}, // E0.6 [3] (🈸..🈺) Japanese “application” button..Japanese “open for business” button
+ {0x1F250, 0x1F251, prEmojiPresentation}, // E0.6 [2] (🉐..🉑) Japanese “bargain” button..Japanese “acceptable” button
+ {0x1F300, 0x1F30C, prEmojiPresentation}, // E0.6 [13] (🌀..🌌) cyclone..milky way
+ {0x1F30D, 0x1F30E, prEmojiPresentation}, // E0.7 [2] (🌍..🌎) globe showing Europe-Africa..globe showing Americas
+ {0x1F30F, 0x1F30F, prEmojiPresentation}, // E0.6 [1] (🌏) globe showing Asia-Australia
+ {0x1F310, 0x1F310, prEmojiPresentation}, // E1.0 [1] (🌐) globe with meridians
+ {0x1F311, 0x1F311, prEmojiPresentation}, // E0.6 [1] (🌑) new moon
+ {0x1F312, 0x1F312, prEmojiPresentation}, // E1.0 [1] (🌒) waxing crescent moon
+ {0x1F313, 0x1F315, prEmojiPresentation}, // E0.6 [3] (🌓..🌕) first quarter moon..full moon
+ {0x1F316, 0x1F318, prEmojiPresentation}, // E1.0 [3] (🌖..🌘) waning gibbous moon..waning crescent moon
+ {0x1F319, 0x1F319, prEmojiPresentation}, // E0.6 [1] (🌙) crescent moon
+ {0x1F31A, 0x1F31A, prEmojiPresentation}, // E1.0 [1] (🌚) new moon face
+ {0x1F31B, 0x1F31B, prEmojiPresentation}, // E0.6 [1] (🌛) first quarter moon face
+ {0x1F31C, 0x1F31C, prEmojiPresentation}, // E0.7 [1] (🌜) last quarter moon face
+ {0x1F31D, 0x1F31E, prEmojiPresentation}, // E1.0 [2] (🌝..🌞) full moon face..sun with face
+ {0x1F31F, 0x1F320, prEmojiPresentation}, // E0.6 [2] (🌟..🌠) glowing star..shooting star
+ {0x1F32D, 0x1F32F, prEmojiPresentation}, // E1.0 [3] (🌭..🌯) hot dog..burrito
+ {0x1F330, 0x1F331, prEmojiPresentation}, // E0.6 [2] (🌰..🌱) chestnut..seedling
+ {0x1F332, 0x1F333, prEmojiPresentation}, // E1.0 [2] (🌲..🌳) evergreen tree..deciduous tree
+ {0x1F334, 0x1F335, prEmojiPresentation}, // E0.6 [2] (🌴..🌵) palm tree..cactus
+ {0x1F337, 0x1F34A, prEmojiPresentation}, // E0.6 [20] (🌷..🍊) tulip..tangerine
+ {0x1F34B, 0x1F34B, prEmojiPresentation}, // E1.0 [1] (🍋) lemon
+ {0x1F34C, 0x1F34F, prEmojiPresentation}, // E0.6 [4] (🍌..🍏) banana..green apple
+ {0x1F350, 0x1F350, prEmojiPresentation}, // E1.0 [1] (🍐) pear
+ {0x1F351, 0x1F37B, prEmojiPresentation}, // E0.6 [43] (🍑..🍻) peach..clinking beer mugs
+ {0x1F37C, 0x1F37C, prEmojiPresentation}, // E1.0 [1] (🍼) baby bottle
+ {0x1F37E, 0x1F37F, prEmojiPresentation}, // E1.0 [2] (🍾..🍿) bottle with popping cork..popcorn
+ {0x1F380, 0x1F393, prEmojiPresentation}, // E0.6 [20] (🎀..🎓) ribbon..graduation cap
+ {0x1F3A0, 0x1F3C4, prEmojiPresentation}, // E0.6 [37] (🎠..🏄) carousel horse..person surfing
+ {0x1F3C5, 0x1F3C5, prEmojiPresentation}, // E1.0 [1] (🏅) sports medal
+ {0x1F3C6, 0x1F3C6, prEmojiPresentation}, // E0.6 [1] (🏆) trophy
+ {0x1F3C7, 0x1F3C7, prEmojiPresentation}, // E1.0 [1] (🏇) horse racing
+ {0x1F3C8, 0x1F3C8, prEmojiPresentation}, // E0.6 [1] (🏈) american football
+ {0x1F3C9, 0x1F3C9, prEmojiPresentation}, // E1.0 [1] (🏉) rugby football
+ {0x1F3CA, 0x1F3CA, prEmojiPresentation}, // E0.6 [1] (🏊) person swimming
+ {0x1F3CF, 0x1F3D3, prEmojiPresentation}, // E1.0 [5] (🏏..🏓) cricket game..ping pong
+ {0x1F3E0, 0x1F3E3, prEmojiPresentation}, // E0.6 [4] (🏠..🏣) house..Japanese post office
+ {0x1F3E4, 0x1F3E4, prEmojiPresentation}, // E1.0 [1] (🏤) post office
+ {0x1F3E5, 0x1F3F0, prEmojiPresentation}, // E0.6 [12] (🏥..🏰) hospital..castle
+ {0x1F3F4, 0x1F3F4, prEmojiPresentation}, // E1.0 [1] (🏴) black flag
+ {0x1F3F8, 0x1F407, prEmojiPresentation}, // E1.0 [16] (🏸..🐇) badminton..rabbit
+ {0x1F408, 0x1F408, prEmojiPresentation}, // E0.7 [1] (🐈) cat
+ {0x1F409, 0x1F40B, prEmojiPresentation}, // E1.0 [3] (🐉..🐋) dragon..whale
+ {0x1F40C, 0x1F40E, prEmojiPresentation}, // E0.6 [3] (🐌..🐎) snail..horse
+ {0x1F40F, 0x1F410, prEmojiPresentation}, // E1.0 [2] (🐏..🐐) ram..goat
+ {0x1F411, 0x1F412, prEmojiPresentation}, // E0.6 [2] (🐑..🐒) ewe..monkey
+ {0x1F413, 0x1F413, prEmojiPresentation}, // E1.0 [1] (🐓) rooster
+ {0x1F414, 0x1F414, prEmojiPresentation}, // E0.6 [1] (🐔) chicken
+ {0x1F415, 0x1F415, prEmojiPresentation}, // E0.7 [1] (🐕) dog
+ {0x1F416, 0x1F416, prEmojiPresentation}, // E1.0 [1] (🐖) pig
+ {0x1F417, 0x1F429, prEmojiPresentation}, // E0.6 [19] (🐗..🐩) boar..poodle
+ {0x1F42A, 0x1F42A, prEmojiPresentation}, // E1.0 [1] (🐪) camel
+ {0x1F42B, 0x1F43E, prEmojiPresentation}, // E0.6 [20] (🐫..🐾) two-hump camel..paw prints
+ {0x1F440, 0x1F440, prEmojiPresentation}, // E0.6 [1] (👀) eyes
+ {0x1F442, 0x1F464, prEmojiPresentation}, // E0.6 [35] (👂..👤) ear..bust in silhouette
+ {0x1F465, 0x1F465, prEmojiPresentation}, // E1.0 [1] (👥) busts in silhouette
+ {0x1F466, 0x1F46B, prEmojiPresentation}, // E0.6 [6] (👦..👫) boy..woman and man holding hands
+ {0x1F46C, 0x1F46D, prEmojiPresentation}, // E1.0 [2] (👬..👭) men holding hands..women holding hands
+ {0x1F46E, 0x1F4AC, prEmojiPresentation}, // E0.6 [63] (👮..💬) police officer..speech balloon
+ {0x1F4AD, 0x1F4AD, prEmojiPresentation}, // E1.0 [1] (💭) thought balloon
+ {0x1F4AE, 0x1F4B5, prEmojiPresentation}, // E0.6 [8] (💮..💵) white flower..dollar banknote
+ {0x1F4B6, 0x1F4B7, prEmojiPresentation}, // E1.0 [2] (💶..💷) euro banknote..pound banknote
+ {0x1F4B8, 0x1F4EB, prEmojiPresentation}, // E0.6 [52] (💸..📫) money with wings..closed mailbox with raised flag
+ {0x1F4EC, 0x1F4ED, prEmojiPresentation}, // E0.7 [2] (📬..📭) open mailbox with raised flag..open mailbox with lowered flag
+ {0x1F4EE, 0x1F4EE, prEmojiPresentation}, // E0.6 [1] (📮) postbox
+ {0x1F4EF, 0x1F4EF, prEmojiPresentation}, // E1.0 [1] (📯) postal horn
+ {0x1F4F0, 0x1F4F4, prEmojiPresentation}, // E0.6 [5] (📰..📴) newspaper..mobile phone off
+ {0x1F4F5, 0x1F4F5, prEmojiPresentation}, // E1.0 [1] (📵) no mobile phones
+ {0x1F4F6, 0x1F4F7, prEmojiPresentation}, // E0.6 [2] (📶..📷) antenna bars..camera
+ {0x1F4F8, 0x1F4F8, prEmojiPresentation}, // E1.0 [1] (📸) camera with flash
+ {0x1F4F9, 0x1F4FC, prEmojiPresentation}, // E0.6 [4] (📹..📼) video camera..videocassette
+ {0x1F4FF, 0x1F502, prEmojiPresentation}, // E1.0 [4] (📿..🔂) prayer beads..repeat single button
+ {0x1F503, 0x1F503, prEmojiPresentation}, // E0.6 [1] (🔃) clockwise vertical arrows
+ {0x1F504, 0x1F507, prEmojiPresentation}, // E1.0 [4] (🔄..🔇) counterclockwise arrows button..muted speaker
+ {0x1F508, 0x1F508, prEmojiPresentation}, // E0.7 [1] (🔈) speaker low volume
+ {0x1F509, 0x1F509, prEmojiPresentation}, // E1.0 [1] (🔉) speaker medium volume
+ {0x1F50A, 0x1F514, prEmojiPresentation}, // E0.6 [11] (🔊..🔔) speaker high volume..bell
+ {0x1F515, 0x1F515, prEmojiPresentation}, // E1.0 [1] (🔕) bell with slash
+ {0x1F516, 0x1F52B, prEmojiPresentation}, // E0.6 [22] (🔖..🔫) bookmark..water pistol
+ {0x1F52C, 0x1F52D, prEmojiPresentation}, // E1.0 [2] (🔬..🔭) microscope..telescope
+ {0x1F52E, 0x1F53D, prEmojiPresentation}, // E0.6 [16] (🔮..🔽) crystal ball..downwards button
+ {0x1F54B, 0x1F54E, prEmojiPresentation}, // E1.0 [4] (🕋..🕎) kaaba..menorah
+ {0x1F550, 0x1F55B, prEmojiPresentation}, // E0.6 [12] (🕐..🕛) one o’clock..twelve o’clock
+ {0x1F55C, 0x1F567, prEmojiPresentation}, // E0.7 [12] (🕜..🕧) one-thirty..twelve-thirty
+ {0x1F57A, 0x1F57A, prEmojiPresentation}, // E3.0 [1] (🕺) man dancing
+ {0x1F595, 0x1F596, prEmojiPresentation}, // E1.0 [2] (🖕..🖖) middle finger..vulcan salute
+ {0x1F5A4, 0x1F5A4, prEmojiPresentation}, // E3.0 [1] (🖤) black heart
+ {0x1F5FB, 0x1F5FF, prEmojiPresentation}, // E0.6 [5] (🗻..🗿) mount fuji..moai
+ {0x1F600, 0x1F600, prEmojiPresentation}, // E1.0 [1] (😀) grinning face
+ {0x1F601, 0x1F606, prEmojiPresentation}, // E0.6 [6] (😁..😆) beaming face with smiling eyes..grinning squinting face
+ {0x1F607, 0x1F608, prEmojiPresentation}, // E1.0 [2] (😇..😈) smiling face with halo..smiling face with horns
+ {0x1F609, 0x1F60D, prEmojiPresentation}, // E0.6 [5] (😉..😍) winking face..smiling face with heart-eyes
+ {0x1F60E, 0x1F60E, prEmojiPresentation}, // E1.0 [1] (😎) smiling face with sunglasses
+ {0x1F60F, 0x1F60F, prEmojiPresentation}, // E0.6 [1] (😏) smirking face
+ {0x1F610, 0x1F610, prEmojiPresentation}, // E0.7 [1] (😐) neutral face
+ {0x1F611, 0x1F611, prEmojiPresentation}, // E1.0 [1] (😑) expressionless face
+ {0x1F612, 0x1F614, prEmojiPresentation}, // E0.6 [3] (😒..😔) unamused face..pensive face
+ {0x1F615, 0x1F615, prEmojiPresentation}, // E1.0 [1] (😕) confused face
+ {0x1F616, 0x1F616, prEmojiPresentation}, // E0.6 [1] (😖) confounded face
+ {0x1F617, 0x1F617, prEmojiPresentation}, // E1.0 [1] (😗) kissing face
+ {0x1F618, 0x1F618, prEmojiPresentation}, // E0.6 [1] (😘) face blowing a kiss
+ {0x1F619, 0x1F619, prEmojiPresentation}, // E1.0 [1] (😙) kissing face with smiling eyes
+ {0x1F61A, 0x1F61A, prEmojiPresentation}, // E0.6 [1] (😚) kissing face with closed eyes
+ {0x1F61B, 0x1F61B, prEmojiPresentation}, // E1.0 [1] (😛) face with tongue
+ {0x1F61C, 0x1F61E, prEmojiPresentation}, // E0.6 [3] (😜..😞) winking face with tongue..disappointed face
+ {0x1F61F, 0x1F61F, prEmojiPresentation}, // E1.0 [1] (😟) worried face
+ {0x1F620, 0x1F625, prEmojiPresentation}, // E0.6 [6] (😠..😥) angry face..sad but relieved face
+ {0x1F626, 0x1F627, prEmojiPresentation}, // E1.0 [2] (😦..😧) frowning face with open mouth..anguished face
+ {0x1F628, 0x1F62B, prEmojiPresentation}, // E0.6 [4] (😨..😫) fearful face..tired face
+ {0x1F62C, 0x1F62C, prEmojiPresentation}, // E1.0 [1] (😬) grimacing face
+ {0x1F62D, 0x1F62D, prEmojiPresentation}, // E0.6 [1] (😭) loudly crying face
+ {0x1F62E, 0x1F62F, prEmojiPresentation}, // E1.0 [2] (😮..😯) face with open mouth..hushed face
+ {0x1F630, 0x1F633, prEmojiPresentation}, // E0.6 [4] (😰..😳) anxious face with sweat..flushed face
+ {0x1F634, 0x1F634, prEmojiPresentation}, // E1.0 [1] (😴) sleeping face
+ {0x1F635, 0x1F635, prEmojiPresentation}, // E0.6 [1] (😵) face with crossed-out eyes
+ {0x1F636, 0x1F636, prEmojiPresentation}, // E1.0 [1] (😶) face without mouth
+ {0x1F637, 0x1F640, prEmojiPresentation}, // E0.6 [10] (😷..🙀) face with medical mask..weary cat
+ {0x1F641, 0x1F644, prEmojiPresentation}, // E1.0 [4] (🙁..🙄) slightly frowning face..face with rolling eyes
+ {0x1F645, 0x1F64F, prEmojiPresentation}, // E0.6 [11] (🙅..🙏) person gesturing NO..folded hands
+ {0x1F680, 0x1F680, prEmojiPresentation}, // E0.6 [1] (🚀) rocket
+ {0x1F681, 0x1F682, prEmojiPresentation}, // E1.0 [2] (🚁..🚂) helicopter..locomotive
+ {0x1F683, 0x1F685, prEmojiPresentation}, // E0.6 [3] (🚃..🚅) railway car..bullet train
+ {0x1F686, 0x1F686, prEmojiPresentation}, // E1.0 [1] (🚆) train
+ {0x1F687, 0x1F687, prEmojiPresentation}, // E0.6 [1] (🚇) metro
+ {0x1F688, 0x1F688, prEmojiPresentation}, // E1.0 [1] (🚈) light rail
+ {0x1F689, 0x1F689, prEmojiPresentation}, // E0.6 [1] (🚉) station
+ {0x1F68A, 0x1F68B, prEmojiPresentation}, // E1.0 [2] (🚊..🚋) tram..tram car
+ {0x1F68C, 0x1F68C, prEmojiPresentation}, // E0.6 [1] (🚌) bus
+ {0x1F68D, 0x1F68D, prEmojiPresentation}, // E0.7 [1] (🚍) oncoming bus
+ {0x1F68E, 0x1F68E, prEmojiPresentation}, // E1.0 [1] (🚎) trolleybus
+ {0x1F68F, 0x1F68F, prEmojiPresentation}, // E0.6 [1] (🚏) bus stop
+ {0x1F690, 0x1F690, prEmojiPresentation}, // E1.0 [1] (🚐) minibus
+ {0x1F691, 0x1F693, prEmojiPresentation}, // E0.6 [3] (🚑..🚓) ambulance..police car
+ {0x1F694, 0x1F694, prEmojiPresentation}, // E0.7 [1] (🚔) oncoming police car
+ {0x1F695, 0x1F695, prEmojiPresentation}, // E0.6 [1] (🚕) taxi
+ {0x1F696, 0x1F696, prEmojiPresentation}, // E1.0 [1] (🚖) oncoming taxi
+ {0x1F697, 0x1F697, prEmojiPresentation}, // E0.6 [1] (🚗) automobile
+ {0x1F698, 0x1F698, prEmojiPresentation}, // E0.7 [1] (🚘) oncoming automobile
+ {0x1F699, 0x1F69A, prEmojiPresentation}, // E0.6 [2] (🚙..🚚) sport utility vehicle..delivery truck
+ {0x1F69B, 0x1F6A1, prEmojiPresentation}, // E1.0 [7] (🚛..🚡) articulated lorry..aerial tramway
+ {0x1F6A2, 0x1F6A2, prEmojiPresentation}, // E0.6 [1] (🚢) ship
+ {0x1F6A3, 0x1F6A3, prEmojiPresentation}, // E1.0 [1] (🚣) person rowing boat
+ {0x1F6A4, 0x1F6A5, prEmojiPresentation}, // E0.6 [2] (🚤..🚥) speedboat..horizontal traffic light
+ {0x1F6A6, 0x1F6A6, prEmojiPresentation}, // E1.0 [1] (🚦) vertical traffic light
+ {0x1F6A7, 0x1F6AD, prEmojiPresentation}, // E0.6 [7] (🚧..🚭) construction..no smoking
+ {0x1F6AE, 0x1F6B1, prEmojiPresentation}, // E1.0 [4] (🚮..🚱) litter in bin sign..non-potable water
+ {0x1F6B2, 0x1F6B2, prEmojiPresentation}, // E0.6 [1] (🚲) bicycle
+ {0x1F6B3, 0x1F6B5, prEmojiPresentation}, // E1.0 [3] (🚳..🚵) no bicycles..person mountain biking
+ {0x1F6B6, 0x1F6B6, prEmojiPresentation}, // E0.6 [1] (🚶) person walking
+ {0x1F6B7, 0x1F6B8, prEmojiPresentation}, // E1.0 [2] (🚷..🚸) no pedestrians..children crossing
+ {0x1F6B9, 0x1F6BE, prEmojiPresentation}, // E0.6 [6] (🚹..🚾) men’s room..water closet
+ {0x1F6BF, 0x1F6BF, prEmojiPresentation}, // E1.0 [1] (🚿) shower
+ {0x1F6C0, 0x1F6C0, prEmojiPresentation}, // E0.6 [1] (🛀) person taking bath
+ {0x1F6C1, 0x1F6C5, prEmojiPresentation}, // E1.0 [5] (🛁..🛅) bathtub..left luggage
+ {0x1F6CC, 0x1F6CC, prEmojiPresentation}, // E1.0 [1] (🛌) person in bed
+ {0x1F6D0, 0x1F6D0, prEmojiPresentation}, // E1.0 [1] (🛐) place of worship
+ {0x1F6D1, 0x1F6D2, prEmojiPresentation}, // E3.0 [2] (🛑..🛒) stop sign..shopping cart
+ {0x1F6D5, 0x1F6D5, prEmojiPresentation}, // E12.0 [1] (🛕) hindu temple
+ {0x1F6D6, 0x1F6D7, prEmojiPresentation}, // E13.0 [2] (🛖..🛗) hut..elevator
+ {0x1F6DD, 0x1F6DF, prEmojiPresentation}, // E14.0 [3] (🛝..🛟) playground slide..ring buoy
+ {0x1F6EB, 0x1F6EC, prEmojiPresentation}, // E1.0 [2] (🛫..🛬) airplane departure..airplane arrival
+ {0x1F6F4, 0x1F6F6, prEmojiPresentation}, // E3.0 [3] (🛴..🛶) kick scooter..canoe
+ {0x1F6F7, 0x1F6F8, prEmojiPresentation}, // E5.0 [2] (🛷..🛸) sled..flying saucer
+ {0x1F6F9, 0x1F6F9, prEmojiPresentation}, // E11.0 [1] (🛹) skateboard
+ {0x1F6FA, 0x1F6FA, prEmojiPresentation}, // E12.0 [1] (🛺) auto rickshaw
+ {0x1F6FB, 0x1F6FC, prEmojiPresentation}, // E13.0 [2] (🛻..🛼) pickup truck..roller skate
+ {0x1F7E0, 0x1F7EB, prEmojiPresentation}, // E12.0 [12] (🟠..🟫) orange circle..brown square
+ {0x1F7F0, 0x1F7F0, prEmojiPresentation}, // E14.0 [1] (🟰) heavy equals sign
+ {0x1F90C, 0x1F90C, prEmojiPresentation}, // E13.0 [1] (🤌) pinched fingers
+ {0x1F90D, 0x1F90F, prEmojiPresentation}, // E12.0 [3] (🤍..🤏) white heart..pinching hand
+ {0x1F910, 0x1F918, prEmojiPresentation}, // E1.0 [9] (🤐..🤘) zipper-mouth face..sign of the horns
+ {0x1F919, 0x1F91E, prEmojiPresentation}, // E3.0 [6] (🤙..🤞) call me hand..crossed fingers
+ {0x1F91F, 0x1F91F, prEmojiPresentation}, // E5.0 [1] (🤟) love-you gesture
+ {0x1F920, 0x1F927, prEmojiPresentation}, // E3.0 [8] (🤠..🤧) cowboy hat face..sneezing face
+ {0x1F928, 0x1F92F, prEmojiPresentation}, // E5.0 [8] (🤨..🤯) face with raised eyebrow..exploding head
+ {0x1F930, 0x1F930, prEmojiPresentation}, // E3.0 [1] (🤰) pregnant woman
+ {0x1F931, 0x1F932, prEmojiPresentation}, // E5.0 [2] (🤱..🤲) breast-feeding..palms up together
+ {0x1F933, 0x1F93A, prEmojiPresentation}, // E3.0 [8] (🤳..🤺) selfie..person fencing
+ {0x1F93C, 0x1F93E, prEmojiPresentation}, // E3.0 [3] (🤼..🤾) people wrestling..person playing handball
+ {0x1F93F, 0x1F93F, prEmojiPresentation}, // E12.0 [1] (🤿) diving mask
+ {0x1F940, 0x1F945, prEmojiPresentation}, // E3.0 [6] (🥀..🥅) wilted flower..goal net
+ {0x1F947, 0x1F94B, prEmojiPresentation}, // E3.0 [5] (🥇..🥋) 1st place medal..martial arts uniform
+ {0x1F94C, 0x1F94C, prEmojiPresentation}, // E5.0 [1] (🥌) curling stone
+ {0x1F94D, 0x1F94F, prEmojiPresentation}, // E11.0 [3] (🥍..🥏) lacrosse..flying disc
+ {0x1F950, 0x1F95E, prEmojiPresentation}, // E3.0 [15] (🥐..🥞) croissant..pancakes
+ {0x1F95F, 0x1F96B, prEmojiPresentation}, // E5.0 [13] (🥟..🥫) dumpling..canned food
+ {0x1F96C, 0x1F970, prEmojiPresentation}, // E11.0 [5] (🥬..🥰) leafy green..smiling face with hearts
+ {0x1F971, 0x1F971, prEmojiPresentation}, // E12.0 [1] (🥱) yawning face
+ {0x1F972, 0x1F972, prEmojiPresentation}, // E13.0 [1] (🥲) smiling face with tear
+ {0x1F973, 0x1F976, prEmojiPresentation}, // E11.0 [4] (🥳..🥶) partying face..cold face
+ {0x1F977, 0x1F978, prEmojiPresentation}, // E13.0 [2] (🥷..🥸) ninja..disguised face
+ {0x1F979, 0x1F979, prEmojiPresentation}, // E14.0 [1] (🥹) face holding back tears
+ {0x1F97A, 0x1F97A, prEmojiPresentation}, // E11.0 [1] (🥺) pleading face
+ {0x1F97B, 0x1F97B, prEmojiPresentation}, // E12.0 [1] (🥻) sari
+ {0x1F97C, 0x1F97F, prEmojiPresentation}, // E11.0 [4] (🥼..🥿) lab coat..flat shoe
+ {0x1F980, 0x1F984, prEmojiPresentation}, // E1.0 [5] (🦀..🦄) crab..unicorn
+ {0x1F985, 0x1F991, prEmojiPresentation}, // E3.0 [13] (🦅..🦑) eagle..squid
+ {0x1F992, 0x1F997, prEmojiPresentation}, // E5.0 [6] (🦒..🦗) giraffe..cricket
+ {0x1F998, 0x1F9A2, prEmojiPresentation}, // E11.0 [11] (🦘..🦢) kangaroo..swan
+ {0x1F9A3, 0x1F9A4, prEmojiPresentation}, // E13.0 [2] (🦣..🦤) mammoth..dodo
+ {0x1F9A5, 0x1F9AA, prEmojiPresentation}, // E12.0 [6] (🦥..🦪) sloth..oyster
+ {0x1F9AB, 0x1F9AD, prEmojiPresentation}, // E13.0 [3] (🦫..🦭) beaver..seal
+ {0x1F9AE, 0x1F9AF, prEmojiPresentation}, // E12.0 [2] (🦮..🦯) guide dog..white cane
+ {0x1F9B0, 0x1F9B9, prEmojiPresentation}, // E11.0 [10] (🦰..🦹) red hair..supervillain
+ {0x1F9BA, 0x1F9BF, prEmojiPresentation}, // E12.0 [6] (🦺..🦿) safety vest..mechanical leg
+ {0x1F9C0, 0x1F9C0, prEmojiPresentation}, // E1.0 [1] (🧀) cheese wedge
+ {0x1F9C1, 0x1F9C2, prEmojiPresentation}, // E11.0 [2] (🧁..🧂) cupcake..salt
+ {0x1F9C3, 0x1F9CA, prEmojiPresentation}, // E12.0 [8] (🧃..🧊) beverage box..ice
+ {0x1F9CB, 0x1F9CB, prEmojiPresentation}, // E13.0 [1] (🧋) bubble tea
+ {0x1F9CC, 0x1F9CC, prEmojiPresentation}, // E14.0 [1] (🧌) troll
+ {0x1F9CD, 0x1F9CF, prEmojiPresentation}, // E12.0 [3] (🧍..🧏) person standing..deaf person
+ {0x1F9D0, 0x1F9E6, prEmojiPresentation}, // E5.0 [23] (🧐..🧦) face with monocle..socks
+ {0x1F9E7, 0x1F9FF, prEmojiPresentation}, // E11.0 [25] (🧧..🧿) red envelope..nazar amulet
+ {0x1FA70, 0x1FA73, prEmojiPresentation}, // E12.0 [4] (🩰..🩳) ballet shoes..shorts
+ {0x1FA74, 0x1FA74, prEmojiPresentation}, // E13.0 [1] (🩴) thong sandal
+ {0x1FA78, 0x1FA7A, prEmojiPresentation}, // E12.0 [3] (🩸..🩺) drop of blood..stethoscope
+ {0x1FA7B, 0x1FA7C, prEmojiPresentation}, // E14.0 [2] (🩻..🩼) x-ray..crutch
+ {0x1FA80, 0x1FA82, prEmojiPresentation}, // E12.0 [3] (🪀..🪂) yo-yo..parachute
+ {0x1FA83, 0x1FA86, prEmojiPresentation}, // E13.0 [4] (🪃..🪆) boomerang..nesting dolls
+ {0x1FA90, 0x1FA95, prEmojiPresentation}, // E12.0 [6] (🪐..🪕) ringed planet..banjo
+ {0x1FA96, 0x1FAA8, prEmojiPresentation}, // E13.0 [19] (🪖..🪨) military helmet..rock
+ {0x1FAA9, 0x1FAAC, prEmojiPresentation}, // E14.0 [4] (🪩..🪬) mirror ball..hamsa
+ {0x1FAB0, 0x1FAB6, prEmojiPresentation}, // E13.0 [7] (🪰..🪶) fly..feather
+ {0x1FAB7, 0x1FABA, prEmojiPresentation}, // E14.0 [4] (🪷..🪺) lotus..nest with eggs
+ {0x1FAC0, 0x1FAC2, prEmojiPresentation}, // E13.0 [3] (🫀..🫂) anatomical heart..people hugging
+ {0x1FAC3, 0x1FAC5, prEmojiPresentation}, // E14.0 [3] (🫃..🫅) pregnant man..person with crown
+ {0x1FAD0, 0x1FAD6, prEmojiPresentation}, // E13.0 [7] (🫐..🫖) blueberries..teapot
+ {0x1FAD7, 0x1FAD9, prEmojiPresentation}, // E14.0 [3] (🫗..🫙) pouring liquid..jar
+ {0x1FAE0, 0x1FAE7, prEmojiPresentation}, // E14.0 [8] (🫠..🫧) melting face..bubbles
+ {0x1FAF0, 0x1FAF6, prEmojiPresentation}, // E14.0 [7] (🫰..🫶) hand with index finger and thumb crossed..heart hands
+}
diff --git a/vendor/github.com/rivo/uniseg/gen_breaktest.go b/vendor/github.com/rivo/uniseg/gen_breaktest.go
new file mode 100644
index 0000000000..e613c4cd00
--- /dev/null
+++ b/vendor/github.com/rivo/uniseg/gen_breaktest.go
@@ -0,0 +1,213 @@
+//go:build generate
+
+// This program generates a Go containing a slice of test cases based on the
+// Unicode Character Database auxiliary data files. The command line arguments
+// are as follows:
+//
+// 1. The name of the Unicode data file (just the filename, without extension).
+// 2. The name of the locally generated Go file.
+// 3. The name of the slice containing the test cases.
+// 4. The name of the generator, for logging purposes.
+//
+//go:generate go run gen_breaktest.go GraphemeBreakTest graphemebreak_test.go graphemeBreakTestCases graphemes
+//go:generate go run gen_breaktest.go WordBreakTest wordbreak_test.go wordBreakTestCases words
+//go:generate go run gen_breaktest.go SentenceBreakTest sentencebreak_test.go sentenceBreakTestCases sentences
+//go:generate go run gen_breaktest.go LineBreakTest linebreak_test.go lineBreakTestCases lines
+
+package main
+
+import (
+ "bufio"
+ "bytes"
+ "errors"
+ "fmt"
+ "go/format"
+ "io/ioutil"
+ "log"
+ "net/http"
+ "os"
+ "time"
+)
+
+// We want to test against a specific version rather than the latest. When the
+// package is upgraded to a new version, change these to generate new tests.
+const (
+ testCaseURL = `https://www.unicode.org/Public/14.0.0/ucd/auxiliary/%s.txt`
+)
+
+func main() {
+ if len(os.Args) < 5 {
+ fmt.Println("Not enough arguments, see code for details")
+ os.Exit(1)
+ }
+
+ log.SetPrefix("gen_breaktest (" + os.Args[4] + "): ")
+ log.SetFlags(0)
+
+ // Read text of testcases and parse into Go source code.
+ src, err := parse(fmt.Sprintf(testCaseURL, os.Args[1]))
+ if err != nil {
+ log.Fatal(err)
+ }
+
+ // Format the Go code.
+ formatted, err := format.Source(src)
+ if err != nil {
+ log.Fatalln("gofmt:", err)
+ }
+
+ // Write it out.
+ log.Print("Writing to ", os.Args[2])
+ if err := ioutil.WriteFile(os.Args[2], formatted, 0644); err != nil {
+ log.Fatal(err)
+ }
+}
+
+// parse reads a break text file, either from a local file or from a URL. It
+// parses the file data into Go source code representing the test cases.
+func parse(url string) ([]byte, error) {
+ log.Printf("Parsing %s", url)
+ res, err := http.Get(url)
+ if err != nil {
+ return nil, err
+ }
+ body := res.Body
+ defer body.Close()
+
+ buf := new(bytes.Buffer)
+ buf.Grow(120 << 10)
+ buf.WriteString(`package uniseg
+
+// Code generated via go generate from gen_breaktest.go. DO NOT EDIT.
+
+// ` + os.Args[3] + ` are Grapheme testcases taken from
+// ` + url + `
+// on ` + time.Now().Format("January 2, 2006") + `. See
+// https://www.unicode.org/license.html for the Unicode license agreement.
+var ` + os.Args[3] + ` = []testCase {
+`)
+
+ sc := bufio.NewScanner(body)
+ num := 1
+ var line []byte
+ original := make([]byte, 0, 64)
+ expected := make([]byte, 0, 64)
+ for sc.Scan() {
+ num++
+ line = sc.Bytes()
+ if len(line) == 0 || line[0] == '#' {
+ continue
+ }
+ var comment []byte
+ if i := bytes.IndexByte(line, '#'); i >= 0 {
+ comment = bytes.TrimSpace(line[i+1:])
+ line = bytes.TrimSpace(line[:i])
+ }
+ original, expected, err := parseRuneSequence(line, original[:0], expected[:0])
+ if err != nil {
+ return nil, fmt.Errorf(`line %d: %v: %q`, num, err, line)
+ }
+ fmt.Fprintf(buf, "\t{original: \"%s\", expected: %s}, // %s\n", original, expected, comment)
+ }
+ if err := sc.Err(); err != nil {
+ return nil, err
+ }
+
+ // Check for final "# EOF", useful check if we're streaming via HTTP
+ if !bytes.Equal(line, []byte("# EOF")) {
+ return nil, fmt.Errorf(`line %d: exected "# EOF" as final line, got %q`, num, line)
+ }
+ buf.WriteString("}\n")
+ return buf.Bytes(), nil
+}
+
+// Used by parseRuneSequence to match input via bytes.HasPrefix.
+var (
+ prefixBreak = []byte("÷ ")
+ prefixDontBreak = []byte("× ")
+ breakOk = []byte("÷")
+ breakNo = []byte("×")
+)
+
+// parseRuneSequence parses a rune + breaking opportunity sequence from b
+// and appends the Go code for testcase.original to orig
+// and appends the Go code for testcase.expected to exp.
+// It retuns the new orig and exp slices.
+//
+// E.g. for the input b="÷ 0020 × 0308 ÷ 1F1E6 ÷"
+// it will append
+// "\u0020\u0308\U0001F1E6"
+// and "[][]rune{{0x0020,0x0308},{0x1F1E6},}"
+// to orig and exp respectively.
+//
+// The formatting of exp is expected to be cleaned up by gofmt or format.Source.
+// Note we explicitly require the sequence to start with ÷ and we implicitly
+// require it to end with ÷.
+func parseRuneSequence(b, orig, exp []byte) ([]byte, []byte, error) {
+ // Check for and remove first ÷ or ×.
+ if !bytes.HasPrefix(b, prefixBreak) && !bytes.HasPrefix(b, prefixDontBreak) {
+ return nil, nil, errors.New("expected ÷ or × as first character")
+ }
+ if bytes.HasPrefix(b, prefixBreak) {
+ b = b[len(prefixBreak):]
+ } else {
+ b = b[len(prefixDontBreak):]
+ }
+
+ boundary := true
+ exp = append(exp, "[][]rune{"...)
+ for len(b) > 0 {
+ if boundary {
+ exp = append(exp, '{')
+ }
+ exp = append(exp, "0x"...)
+ // Find end of hex digits.
+ var i int
+ for i = 0; i < len(b) && b[i] != ' '; i++ {
+ if d := b[i]; ('0' <= d || d <= '9') ||
+ ('A' <= d || d <= 'F') ||
+ ('a' <= d || d <= 'f') {
+ continue
+ }
+ return nil, nil, errors.New("bad hex digit")
+ }
+ switch i {
+ case 4:
+ orig = append(orig, "\\u"...)
+ case 5:
+ orig = append(orig, "\\U000"...)
+ default:
+ return nil, nil, errors.New("unsupport code point hex length")
+ }
+ orig = append(orig, b[:i]...)
+ exp = append(exp, b[:i]...)
+ b = b[i:]
+
+ // Check for space between hex and ÷ or ×.
+ if len(b) < 1 || b[0] != ' ' {
+ return nil, nil, errors.New("bad input")
+ }
+ b = b[1:]
+
+ // Check for next boundary.
+ switch {
+ case bytes.HasPrefix(b, breakOk):
+ boundary = true
+ b = b[len(breakOk):]
+ case bytes.HasPrefix(b, breakNo):
+ boundary = false
+ b = b[len(breakNo):]
+ default:
+ return nil, nil, errors.New("missing ÷ or ×")
+ }
+ if boundary {
+ exp = append(exp, '}')
+ }
+ exp = append(exp, ',')
+ if len(b) > 0 && b[0] == ' ' {
+ b = b[1:]
+ }
+ }
+ exp = append(exp, '}')
+ return orig, exp, nil
+}
diff --git a/vendor/github.com/rivo/uniseg/gen_properties.go b/vendor/github.com/rivo/uniseg/gen_properties.go
new file mode 100644
index 0000000000..999d5efddf
--- /dev/null
+++ b/vendor/github.com/rivo/uniseg/gen_properties.go
@@ -0,0 +1,256 @@
+//go:build generate
+
+// This program generates a property file in Go file from Unicode Character
+// Database auxiliary data files. The command line arguments are as follows:
+//
+// 1. The name of the Unicode data file (just the filename, without extension).
+// Can be "-" (to skip) if the emoji flag is included.
+// 2. The name of the locally generated Go file.
+// 3. The name of the slice mapping code points to properties.
+// 4. The name of the generator, for logging purposes.
+// 5. (Optional) Flags, comma-separated. The following flags are available:
+// - "emojis=": include the specified emoji properties (e.g.
+// "Extended_Pictographic").
+// - "gencat": include general category properties.
+//
+//go:generate go run gen_properties.go auxiliary/GraphemeBreakProperty graphemeproperties.go graphemeCodePoints graphemes emojis=Extended_Pictographic
+//go:generate go run gen_properties.go auxiliary/WordBreakProperty wordproperties.go workBreakCodePoints words emojis=Extended_Pictographic
+//go:generate go run gen_properties.go auxiliary/SentenceBreakProperty sentenceproperties.go sentenceBreakCodePoints sentences
+//go:generate go run gen_properties.go LineBreak lineproperties.go lineBreakCodePoints lines gencat
+//go:generate go run gen_properties.go EastAsianWidth eastasianwidth.go eastAsianWidth eastasianwidth
+//go:generate go run gen_properties.go - emojipresentation.go emojiPresentation emojipresentation emojis=Emoji_Presentation
+package main
+
+import (
+ "bufio"
+ "bytes"
+ "errors"
+ "fmt"
+ "go/format"
+ "io/ioutil"
+ "log"
+ "net/http"
+ "os"
+ "regexp"
+ "sort"
+ "strconv"
+ "strings"
+ "time"
+)
+
+// We want to test against a specific version rather than the latest. When the
+// package is upgraded to a new version, change these to generate new tests.
+const (
+ propertyURL = `https://www.unicode.org/Public/14.0.0/ucd/%s.txt`
+ emojiURL = `https://unicode.org/Public/14.0.0/ucd/emoji/emoji-data.txt`
+)
+
+// The regular expression for a line containing a code point range property.
+var propertyPattern = regexp.MustCompile(`^([0-9A-F]{4,6})(\.\.([0-9A-F]{4,6}))?\s*;\s*([A-Za-z0-9_]+)\s*#\s(.+)$`)
+
+func main() {
+ if len(os.Args) < 5 {
+ fmt.Println("Not enough arguments, see code for details")
+ os.Exit(1)
+ }
+
+ log.SetPrefix("gen_properties (" + os.Args[4] + "): ")
+ log.SetFlags(0)
+
+ // Parse flags.
+ flags := make(map[string]string)
+ if len(os.Args) >= 6 {
+ for _, flag := range strings.Split(os.Args[5], ",") {
+ flagFields := strings.Split(flag, "=")
+ if len(flagFields) == 1 {
+ flags[flagFields[0]] = "yes"
+ } else {
+ flags[flagFields[0]] = flagFields[1]
+ }
+ }
+ }
+
+ // Parse the text file and generate Go source code from it.
+ _, includeGeneralCategory := flags["gencat"]
+ var mainURL string
+ if os.Args[1] != "-" {
+ mainURL = fmt.Sprintf(propertyURL, os.Args[1])
+ }
+ src, err := parse(mainURL, flags["emojis"], includeGeneralCategory)
+ if err != nil {
+ log.Fatal(err)
+ }
+
+ // Format the Go code.
+ formatted, err := format.Source([]byte(src))
+ if err != nil {
+ log.Fatal("gofmt:", err)
+ }
+
+ // Save it to the (local) target file.
+ log.Print("Writing to ", os.Args[2])
+ if err := ioutil.WriteFile(os.Args[2], formatted, 0644); err != nil {
+ log.Fatal(err)
+ }
+}
+
+// parse parses the Unicode Properties text files located at the given URLs and
+// returns their equivalent Go source code to be used in the uniseg package. If
+// "emojiProperty" is not an empty string, emoji code points for that emoji
+// property (e.g. "Extended_Pictographic") will be included. In those cases, you
+// may pass an empty "propertyURL" to skip parsing the main properties file. If
+// "includeGeneralCategory" is true, the Unicode General Category property will
+// be extracted from the comments and included in the output.
+func parse(propertyURL, emojiProperty string, includeGeneralCategory bool) (string, error) {
+ if propertyURL == "" && emojiProperty == "" {
+ return "", errors.New("no properties to parse")
+ }
+
+ // Temporary buffer to hold properties.
+ var properties [][4]string
+
+ // Open the first URL.
+ if propertyURL != "" {
+ log.Printf("Parsing %s", propertyURL)
+ res, err := http.Get(propertyURL)
+ if err != nil {
+ return "", err
+ }
+ in1 := res.Body
+ defer in1.Close()
+
+ // Parse it.
+ scanner := bufio.NewScanner(in1)
+ num := 0
+ for scanner.Scan() {
+ num++
+ line := strings.TrimSpace(scanner.Text())
+
+ // Skip comments and empty lines.
+ if strings.HasPrefix(line, "#") || line == "" {
+ continue
+ }
+
+ // Everything else must be a code point range, a property and a comment.
+ from, to, property, comment, err := parseProperty(line)
+ if err != nil {
+ return "", fmt.Errorf("%s line %d: %v", os.Args[4], num, err)
+ }
+ properties = append(properties, [4]string{from, to, property, comment})
+ }
+ if err := scanner.Err(); err != nil {
+ return "", err
+ }
+ }
+
+ // Open the second URL.
+ if emojiProperty != "" {
+ log.Printf("Parsing %s", emojiURL)
+ res, err := http.Get(emojiURL)
+ if err != nil {
+ return "", err
+ }
+ in2 := res.Body
+ defer in2.Close()
+
+ // Parse it.
+ scanner := bufio.NewScanner(in2)
+ num := 0
+ for scanner.Scan() {
+ num++
+ line := scanner.Text()
+
+ // Skip comments, empty lines, and everything not containing
+ // "Extended_Pictographic".
+ if strings.HasPrefix(line, "#") || line == "" || !strings.Contains(line, emojiProperty) {
+ continue
+ }
+
+ // Everything else must be a code point range, a property and a comment.
+ from, to, property, comment, err := parseProperty(line)
+ if err != nil {
+ return "", fmt.Errorf("emojis line %d: %v", num, err)
+ }
+ properties = append(properties, [4]string{from, to, property, comment})
+ }
+ if err := scanner.Err(); err != nil {
+ return "", err
+ }
+ }
+
+ // Sort properties.
+ sort.Slice(properties, func(i, j int) bool {
+ left, _ := strconv.ParseUint(properties[i][0], 16, 64)
+ right, _ := strconv.ParseUint(properties[j][0], 16, 64)
+ return left < right
+ })
+
+ // Header.
+ var (
+ buf bytes.Buffer
+ emojiComment string
+ )
+ columns := 3
+ if includeGeneralCategory {
+ columns = 4
+ }
+ if emojiURL != "" {
+ emojiComment = `
+// and
+// ` + emojiURL + `
+// ("Extended_Pictographic" only)`
+ }
+ buf.WriteString(`package uniseg
+
+// Code generated via go generate from gen_properties.go. DO NOT EDIT.
+
+// ` + os.Args[3] + ` are taken from
+// ` + propertyURL + emojiComment + `
+// on ` + time.Now().Format("January 2, 2006") + `. See https://www.unicode.org/license.html for the Unicode
+// license agreement.
+var ` + os.Args[3] + ` = [][` + strconv.Itoa(columns) + `]int{
+ `)
+
+ // Properties.
+ for _, prop := range properties {
+ if includeGeneralCategory {
+ generalCategory := "gc" + prop[3][:2]
+ if generalCategory == "gcL&" {
+ generalCategory = "gcLC"
+ }
+ prop[3] = prop[3][3:]
+ fmt.Fprintf(&buf, "{0x%s,0x%s,%s,%s}, // %s\n", prop[0], prop[1], translateProperty("pr", prop[2]), generalCategory, prop[3])
+ } else {
+ fmt.Fprintf(&buf, "{0x%s,0x%s,%s}, // %s\n", prop[0], prop[1], translateProperty("pr", prop[2]), prop[3])
+ }
+ }
+
+ // Tail.
+ buf.WriteString("}")
+
+ return buf.String(), nil
+}
+
+// parseProperty parses a line of the Unicode properties text file containing a
+// property for a code point range and returns it along with its comment.
+func parseProperty(line string) (from, to, property, comment string, err error) {
+ fields := propertyPattern.FindStringSubmatch(line)
+ if fields == nil {
+ err = errors.New("no property found")
+ return
+ }
+ from = fields[1]
+ to = fields[3]
+ if to == "" {
+ to = from
+ }
+ property = fields[4]
+ comment = fields[5]
+ return
+}
+
+// translateProperty translates a property name as used in the Unicode data file
+// to a variable used in the Go code.
+func translateProperty(prefix, property string) string {
+ return prefix + strings.ReplaceAll(property, "_", "")
+}
diff --git a/vendor/github.com/rivo/uniseg/grapheme.go b/vendor/github.com/rivo/uniseg/grapheme.go
index 207157f5e4..0086fc1b20 100644
--- a/vendor/github.com/rivo/uniseg/grapheme.go
+++ b/vendor/github.com/rivo/uniseg/grapheme.go
@@ -2,267 +2,333 @@ package uniseg
import "unicode/utf8"
-// The states of the grapheme cluster parser.
-const (
- grAny = iota
- grCR
- grControlLF
- grL
- grLVV
- grLVTT
- grPrepend
- grExtendedPictographic
- grExtendedPictographicZWJ
- grRIOdd
- grRIEven
-)
-
-// The grapheme cluster parser's breaking instructions.
-const (
- grNoBoundary = iota
- grBoundary
-)
-
-// The grapheme cluster parser's state transitions. Maps (state, property) to
-// (new state, breaking instruction, rule number). The breaking instruction
-// always refers to the boundary between the last and next code point.
+// Graphemes implements an iterator over Unicode grapheme clusters, or
+// user-perceived characters. While iterating, it also provides information
+// about word boundaries, sentence boundaries, line breaks, and monospace
+// character widths.
//
-// This map is queried as follows:
+// After constructing the class via [NewGraphemes] for a given string "str",
+// [Graphemes.Next] is called for every grapheme cluster in a loop until it
+// returns false. Inside the loop, information about the grapheme cluster as
+// well as boundary information and character width is available via the various
+// methods (see examples below).
//
-// 1. Find specific state + specific property. Stop if found.
-// 2. Find specific state + any property.
-// 3. Find any state + specific property.
-// 4. If only (2) or (3) (but not both) was found, stop.
-// 5. If both (2) and (3) were found, use state and breaking instruction from
-// the transition with the lower rule number, prefer (3) if rule numbers
-// are equal. Stop.
-// 6. Assume grAny and grBoundary.
-var grTransitions = map[[2]int][3]int{
- // GB5
- {grAny, prCR}: {grCR, grBoundary, 50},
- {grAny, prLF}: {grControlLF, grBoundary, 50},
- {grAny, prControl}: {grControlLF, grBoundary, 50},
-
- // GB4
- {grCR, prAny}: {grAny, grBoundary, 40},
- {grControlLF, prAny}: {grAny, grBoundary, 40},
-
- // GB3.
- {grCR, prLF}: {grAny, grNoBoundary, 30},
-
- // GB6.
- {grAny, prL}: {grL, grBoundary, 9990},
- {grL, prL}: {grL, grNoBoundary, 60},
- {grL, prV}: {grLVV, grNoBoundary, 60},
- {grL, prLV}: {grLVV, grNoBoundary, 60},
- {grL, prLVT}: {grLVTT, grNoBoundary, 60},
-
- // GB7.
- {grAny, prLV}: {grLVV, grBoundary, 9990},
- {grAny, prV}: {grLVV, grBoundary, 9990},
- {grLVV, prV}: {grLVV, grNoBoundary, 70},
- {grLVV, prT}: {grLVTT, grNoBoundary, 70},
-
- // GB8.
- {grAny, prLVT}: {grLVTT, grBoundary, 9990},
- {grAny, prT}: {grLVTT, grBoundary, 9990},
- {grLVTT, prT}: {grLVTT, grNoBoundary, 80},
-
- // GB9.
- {grAny, prExtend}: {grAny, grNoBoundary, 90},
- {grAny, prZWJ}: {grAny, grNoBoundary, 90},
-
- // GB9a.
- {grAny, prSpacingMark}: {grAny, grNoBoundary, 91},
-
- // GB9b.
- {grAny, prPreprend}: {grPrepend, grBoundary, 9990},
- {grPrepend, prAny}: {grAny, grNoBoundary, 92},
-
- // GB11.
- {grAny, prExtendedPictographic}: {grExtendedPictographic, grBoundary, 9990},
- {grExtendedPictographic, prExtend}: {grExtendedPictographic, grNoBoundary, 110},
- {grExtendedPictographic, prZWJ}: {grExtendedPictographicZWJ, grNoBoundary, 110},
- {grExtendedPictographicZWJ, prExtendedPictographic}: {grExtendedPictographic, grNoBoundary, 110},
-
- // GB12 / GB13.
- {grAny, prRegionalIndicator}: {grRIOdd, grBoundary, 9990},
- {grRIOdd, prRegionalIndicator}: {grRIEven, grNoBoundary, 120},
- {grRIEven, prRegionalIndicator}: {grRIOdd, grBoundary, 120},
-}
-
-// Graphemes implements an iterator over Unicode extended grapheme clusters,
-// specified in the Unicode Standard Annex #29. Grapheme clusters correspond to
-// "user-perceived characters". These characters often consist of multiple
-// code points (e.g. the "woman kissing woman" emoji consists of 8 code points:
-// woman + ZWJ + heavy black heart (2 code points) + ZWJ + kiss mark + ZWJ +
-// woman) and the rules described in Annex #29 must be applied to group those
-// code points into clusters perceived by the user as one character.
+// Using this class to iterate over a string is convenient but it is much slower
+// than using this package's [Step] or [StepString] functions or any of the
+// other specialized functions starting with "First".
type Graphemes struct {
- // The code points over which this class iterates.
- codePoints []rune
+ // The original string.
+ original string
+
+ // The remaining string to be parsed.
+ remaining string
- // The (byte-based) indices of the code points into the original string plus
- // len(original string). Thus, len(indices) = len(codePoints) + 1.
- indices []int
+ // The current grapheme cluster.
+ cluster string
- // The current grapheme cluster to be returned. These are indices into
- // codePoints/indices. If start == end, we either haven't started iterating
- // yet (0) or the iteration has already completed (1).
- start, end int
+ // The byte offset of the current grapheme cluster relative to the original
+ // string.
+ offset int
- // The index of the next code point to be parsed.
- pos int
+ // The current boundary information of the [Step] parser.
+ boundaries int
- // The current state of the code point parser.
+ // The current state of the [Step] parser.
state int
}
// NewGraphemes returns a new grapheme cluster iterator.
-func NewGraphemes(s string) *Graphemes {
- l := utf8.RuneCountInString(s)
- codePoints := make([]rune, l)
- indices := make([]int, l+1)
- i := 0
- for pos, r := range s {
- codePoints[i] = r
- indices[i] = pos
- i++
+func NewGraphemes(str string) *Graphemes {
+ return &Graphemes{
+ original: str,
+ remaining: str,
+ state: -1,
}
- indices[l] = len(s)
- g := &Graphemes{
- codePoints: codePoints,
- indices: indices,
- }
- g.Next() // Parse ahead.
- return g
}
// Next advances the iterator by one grapheme cluster and returns false if no
// clusters are left. This function must be called before the first cluster is
// accessed.
func (g *Graphemes) Next() bool {
- g.start = g.end
-
- // The state transition gives us a boundary instruction BEFORE the next code
- // point so we always need to stay ahead by one code point.
-
- // Parse the next code point.
- for g.pos <= len(g.codePoints) {
- // GB2.
- if g.pos == len(g.codePoints) {
- g.end = g.pos
- g.pos++
- break
- }
-
- // Determine the property of the next character.
- nextProperty := property(g.codePoints[g.pos])
- g.pos++
-
- // Find the applicable transition.
- var boundary bool
- transition, ok := grTransitions[[2]int{g.state, nextProperty}]
- if ok {
- // We have a specific transition. We'll use it.
- g.state = transition[0]
- boundary = transition[1] == grBoundary
- } else {
- // No specific transition found. Try the less specific ones.
- transAnyProp, okAnyProp := grTransitions[[2]int{g.state, prAny}]
- transAnyState, okAnyState := grTransitions[[2]int{grAny, nextProperty}]
- if okAnyProp && okAnyState {
- // Both apply. We'll use a mix (see comments for grTransitions).
- g.state = transAnyState[0]
- boundary = transAnyState[1] == grBoundary
- if transAnyProp[2] < transAnyState[2] {
- g.state = transAnyProp[0]
- boundary = transAnyProp[1] == grBoundary
- }
- } else if okAnyProp {
- // We only have a specific state.
- g.state = transAnyProp[0]
- boundary = transAnyProp[1] == grBoundary
- // This branch will probably never be reached because okAnyState will
- // always be true given the current transition map. But we keep it here
- // for future modifications to the transition map where this may not be
- // true anymore.
- } else if okAnyState {
- // We only have a specific property.
- g.state = transAnyState[0]
- boundary = transAnyState[1] == grBoundary
- } else {
- // No known transition. GB999: Any x Any.
- g.state = grAny
- boundary = true
- }
- }
-
- // If we found a cluster boundary, let's stop here. The current cluster will
- // be the one that just ended.
- if g.pos-1 == 0 /* GB1 */ || boundary {
- g.end = g.pos - 1
- break
- }
+ if len(g.remaining) == 0 {
+ // We're already past the end.
+ g.state = -2
+ g.cluster = ""
+ return false
}
-
- return g.start != g.end
+ g.offset += len(g.cluster)
+ g.cluster, g.remaining, g.boundaries, g.state = StepString(g.remaining, g.state)
+ return true
}
// Runes returns a slice of runes (code points) which corresponds to the current
-// grapheme cluster. If the iterator is already past the end or Next() has not
-// yet been called, nil is returned.
+// grapheme cluster. If the iterator is already past the end or [Graphemes.Next]
+// has not yet been called, nil is returned.
func (g *Graphemes) Runes() []rune {
- if g.start == g.end {
+ if g.state < 0 {
return nil
}
- return g.codePoints[g.start:g.end]
+ return []rune(g.cluster)
}
// Str returns a substring of the original string which corresponds to the
-// current grapheme cluster. If the iterator is already past the end or Next()
-// has not yet been called, an empty string is returned.
+// current grapheme cluster. If the iterator is already past the end or
+// [Graphemes.Next] has not yet been called, an empty string is returned.
func (g *Graphemes) Str() string {
- if g.start == g.end {
- return ""
- }
- return string(g.codePoints[g.start:g.end])
+ return g.cluster
}
// Bytes returns a byte slice which corresponds to the current grapheme cluster.
-// If the iterator is already past the end or Next() has not yet been called,
-// nil is returned.
+// If the iterator is already past the end or [Graphemes.Next] has not yet been
+// called, nil is returned.
func (g *Graphemes) Bytes() []byte {
- if g.start == g.end {
+ if g.state < 0 {
return nil
}
- return []byte(string(g.codePoints[g.start:g.end]))
+ return []byte(g.cluster)
}
// Positions returns the interval of the current grapheme cluster as byte
// positions into the original string. The first returned value "from" indexes
// the first byte and the second returned value "to" indexes the first byte that
// is not included anymore, i.e. str[from:to] is the current grapheme cluster of
-// the original string "str". If Next() has not yet been called, both values are
-// 0. If the iterator is already past the end, both values are 1.
+// the original string "str". If [Graphemes.Next] has not yet been called, both
+// values are 0. If the iterator is already past the end, both values are 1.
func (g *Graphemes) Positions() (int, int) {
- return g.indices[g.start], g.indices[g.end]
+ if g.state == -1 {
+ return 0, 0
+ } else if g.state == -2 {
+ return 1, 1
+ }
+ return g.offset, g.offset + len(g.cluster)
+}
+
+// IsWordBoundary returns true if a word ends after the current grapheme
+// cluster.
+func (g *Graphemes) IsWordBoundary() bool {
+ if g.state < 0 {
+ return true
+ }
+ return g.boundaries&MaskWord != 0
+}
+
+// IsSentenceBoundary returns true if a sentence ends after the current
+// grapheme cluster.
+func (g *Graphemes) IsSentenceBoundary() bool {
+ if g.state < 0 {
+ return true
+ }
+ return g.boundaries&MaskSentence != 0
+}
+
+// LineBreak returns whether the line can be broken after the current grapheme
+// cluster. A value of [LineDontBreak] means the line may not be broken, a value
+// of [LineMustBreak] means the line must be broken, and a value of
+// [LineCanBreak] means the line may or may not be broken.
+func (g *Graphemes) LineBreak() int {
+ if g.state == -1 {
+ return LineDontBreak
+ }
+ if g.state == -2 {
+ return LineMustBreak
+ }
+ return g.boundaries & MaskLine
+}
+
+// Width returns the monospace width of the current grapheme cluster.
+func (g *Graphemes) Width() int {
+ if g.state < 0 {
+ return 0
+ }
+ return g.boundaries >> ShiftWidth
}
// Reset puts the iterator into its initial state such that the next call to
-// Next() sets it to the first grapheme cluster again.
+// [Graphemes.Next] sets it to the first grapheme cluster again.
func (g *Graphemes) Reset() {
- g.start, g.end, g.pos, g.state = 0, 0, 0, grAny
- g.Next() // Parse ahead again.
+ g.state = -1
+ g.offset = 0
+ g.cluster = ""
+ g.remaining = g.original
}
// GraphemeClusterCount returns the number of user-perceived characters
-// (grapheme clusters) for the given string. To calculate this number, it
-// iterates through the string using the Graphemes iterator.
+// (grapheme clusters) for the given string.
func GraphemeClusterCount(s string) (n int) {
- g := NewGraphemes(s)
- for g.Next() {
+ state := -1
+ for len(s) > 0 {
+ _, s, _, state = FirstGraphemeClusterInString(s, state)
n++
}
return
}
+
+// ReverseString reverses the given string while observing grapheme cluster
+// boundaries.
+func ReverseString(s string) string {
+ str := []byte(s)
+ reversed := make([]byte, len(str))
+ state := -1
+ index := len(str)
+ for len(str) > 0 {
+ var cluster []byte
+ cluster, str, _, state = FirstGraphemeCluster(str, state)
+ index -= len(cluster)
+ copy(reversed[index:], cluster)
+ if index <= len(str)/2 {
+ break
+ }
+ }
+ return string(reversed)
+}
+
+// The number of bits the grapheme property must be shifted to make place for
+// grapheme states.
+const shiftGraphemePropState = 4
+
+// FirstGraphemeCluster returns the first grapheme cluster found in the given
+// byte slice according to the rules of [Unicode Standard Annex #29, Grapheme
+// Cluster Boundaries]. This function can be called continuously to extract all
+// grapheme clusters from a byte slice, as illustrated in the example below.
+//
+// If you don't know the current state, for example when calling the function
+// for the first time, you must pass -1. For consecutive calls, pass the state
+// and rest slice returned by the previous call.
+//
+// The "rest" slice is the sub-slice of the original byte slice "b" starting
+// after the last byte of the identified grapheme cluster. If the length of the
+// "rest" slice is 0, the entire byte slice "b" has been processed. The
+// "cluster" byte slice is the sub-slice of the input slice containing the
+// identified grapheme cluster.
+//
+// The returned width is the width of the grapheme cluster for most monospace
+// fonts where a value of 1 represents one character cell.
+//
+// Given an empty byte slice "b", the function returns nil values.
+//
+// While slightly less convenient than using the Graphemes class, this function
+// has much better performance and makes no allocations. It lends itself well to
+// large byte slices.
+//
+// [Unicode Standard Annex #29, Grapheme Cluster Boundaries]: http://unicode.org/reports/tr29/#Grapheme_Cluster_Boundaries
+func FirstGraphemeCluster(b []byte, state int) (cluster, rest []byte, width, newState int) {
+ // An empty byte slice returns nothing.
+ if len(b) == 0 {
+ return
+ }
+
+ // Extract the first rune.
+ r, length := utf8.DecodeRune(b)
+ if len(b) <= length { // If we're already past the end, there is nothing else to parse.
+ var prop int
+ if state < 0 {
+ prop = property(graphemeCodePoints, r)
+ } else {
+ prop = state >> shiftGraphemePropState
+ }
+ return b, nil, runeWidth(r, prop), grAny | (prop << shiftGraphemePropState)
+ }
+
+ // If we don't know the state, determine it now.
+ var firstProp int
+ if state < 0 {
+ state, firstProp, _ = transitionGraphemeState(state, r)
+ } else {
+ firstProp = state >> shiftGraphemePropState
+ }
+ width += runeWidth(r, firstProp)
+
+ // Transition until we find a boundary.
+ for {
+ var (
+ prop int
+ boundary bool
+ )
+
+ r, l := utf8.DecodeRune(b[length:])
+ state, prop, boundary = transitionGraphemeState(state&maskGraphemeState, r)
+
+ if boundary {
+ return b[:length], b[length:], width, state | (prop << shiftGraphemePropState)
+ }
+
+ if r == vs16 {
+ width = 2
+ } else if firstProp != prExtendedPictographic && firstProp != prRegionalIndicator && firstProp != prL {
+ width += runeWidth(r, prop)
+ } else if firstProp == prExtendedPictographic {
+ if r == vs15 {
+ width = 1
+ } else {
+ width = 2
+ }
+ }
+
+ length += l
+ if len(b) <= length {
+ return b, nil, width, grAny | (prop << shiftGraphemePropState)
+ }
+ }
+}
+
+// FirstGraphemeClusterInString is like [FirstGraphemeCluster] but its input and
+// outputs are strings.
+func FirstGraphemeClusterInString(str string, state int) (cluster, rest string, width, newState int) {
+ // An empty string returns nothing.
+ if len(str) == 0 {
+ return
+ }
+
+ // Extract the first rune.
+ r, length := utf8.DecodeRuneInString(str)
+ if len(str) <= length { // If we're already past the end, there is nothing else to parse.
+ var prop int
+ if state < 0 {
+ prop = property(graphemeCodePoints, r)
+ } else {
+ prop = state >> shiftGraphemePropState
+ }
+ return str, "", runeWidth(r, prop), grAny | (prop << shiftGraphemePropState)
+ }
+
+ // If we don't know the state, determine it now.
+ var firstProp int
+ if state < 0 {
+ state, firstProp, _ = transitionGraphemeState(state, r)
+ } else {
+ firstProp = state >> shiftGraphemePropState
+ }
+ width += runeWidth(r, firstProp)
+
+ // Transition until we find a boundary.
+ for {
+ var (
+ prop int
+ boundary bool
+ )
+
+ r, l := utf8.DecodeRuneInString(str[length:])
+ state, prop, boundary = transitionGraphemeState(state&maskGraphemeState, r)
+
+ if boundary {
+ return str[:length], str[length:], width, state | (prop << shiftGraphemePropState)
+ }
+
+ if r == vs16 {
+ width = 2
+ } else if firstProp != prExtendedPictographic && firstProp != prRegionalIndicator && firstProp != prL {
+ width += runeWidth(r, prop)
+ } else if firstProp == prExtendedPictographic {
+ if r == vs15 {
+ width = 1
+ } else {
+ width = 2
+ }
+ }
+
+ length += l
+ if len(str) <= length {
+ return str, "", width, grAny | (prop << shiftGraphemePropState)
+ }
+ }
+}
diff --git a/vendor/github.com/rivo/uniseg/graphemeproperties.go b/vendor/github.com/rivo/uniseg/graphemeproperties.go
new file mode 100644
index 0000000000..a87d140bf2
--- /dev/null
+++ b/vendor/github.com/rivo/uniseg/graphemeproperties.go
@@ -0,0 +1,1891 @@
+package uniseg
+
+// Code generated via go generate from gen_properties.go. DO NOT EDIT.
+
+// graphemeCodePoints are taken from
+// https://www.unicode.org/Public/14.0.0/ucd/auxiliary/GraphemeBreakProperty.txt
+// and
+// https://unicode.org/Public/14.0.0/ucd/emoji/emoji-data.txt
+// ("Extended_Pictographic" only)
+// on September 10, 2022. See https://www.unicode.org/license.html for the Unicode
+// license agreement.
+var graphemeCodePoints = [][3]int{
+ {0x0000, 0x0009, prControl}, // Cc [10] ..
+ {0x000A, 0x000A, prLF}, // Cc
+ {0x000B, 0x000C, prControl}, // Cc [2] ..
+ {0x000D, 0x000D, prCR}, // Cc
+ {0x000E, 0x001F, prControl}, // Cc [18] ..
+ {0x007F, 0x009F, prControl}, // Cc [33] ..
+ {0x00A9, 0x00A9, prExtendedPictographic}, // E0.6 [1] (©️) copyright
+ {0x00AD, 0x00AD, prControl}, // Cf SOFT HYPHEN
+ {0x00AE, 0x00AE, prExtendedPictographic}, // E0.6 [1] (®️) registered
+ {0x0300, 0x036F, prExtend}, // Mn [112] COMBINING GRAVE ACCENT..COMBINING LATIN SMALL LETTER X
+ {0x0483, 0x0487, prExtend}, // Mn [5] COMBINING CYRILLIC TITLO..COMBINING CYRILLIC POKRYTIE
+ {0x0488, 0x0489, prExtend}, // Me [2] COMBINING CYRILLIC HUNDRED THOUSANDS SIGN..COMBINING CYRILLIC MILLIONS SIGN
+ {0x0591, 0x05BD, prExtend}, // Mn [45] HEBREW ACCENT ETNAHTA..HEBREW POINT METEG
+ {0x05BF, 0x05BF, prExtend}, // Mn HEBREW POINT RAFE
+ {0x05C1, 0x05C2, prExtend}, // Mn [2] HEBREW POINT SHIN DOT..HEBREW POINT SIN DOT
+ {0x05C4, 0x05C5, prExtend}, // Mn [2] HEBREW MARK UPPER DOT..HEBREW MARK LOWER DOT
+ {0x05C7, 0x05C7, prExtend}, // Mn HEBREW POINT QAMATS QATAN
+ {0x0600, 0x0605, prPrepend}, // Cf [6] ARABIC NUMBER SIGN..ARABIC NUMBER MARK ABOVE
+ {0x0610, 0x061A, prExtend}, // Mn [11] ARABIC SIGN SALLALLAHOU ALAYHE WASSALLAM..ARABIC SMALL KASRA
+ {0x061C, 0x061C, prControl}, // Cf ARABIC LETTER MARK
+ {0x064B, 0x065F, prExtend}, // Mn [21] ARABIC FATHATAN..ARABIC WAVY HAMZA BELOW
+ {0x0670, 0x0670, prExtend}, // Mn ARABIC LETTER SUPERSCRIPT ALEF
+ {0x06D6, 0x06DC, prExtend}, // Mn [7] ARABIC SMALL HIGH LIGATURE SAD WITH LAM WITH ALEF MAKSURA..ARABIC SMALL HIGH SEEN
+ {0x06DD, 0x06DD, prPrepend}, // Cf ARABIC END OF AYAH
+ {0x06DF, 0x06E4, prExtend}, // Mn [6] ARABIC SMALL HIGH ROUNDED ZERO..ARABIC SMALL HIGH MADDA
+ {0x06E7, 0x06E8, prExtend}, // Mn [2] ARABIC SMALL HIGH YEH..ARABIC SMALL HIGH NOON
+ {0x06EA, 0x06ED, prExtend}, // Mn [4] ARABIC EMPTY CENTRE LOW STOP..ARABIC SMALL LOW MEEM
+ {0x070F, 0x070F, prPrepend}, // Cf SYRIAC ABBREVIATION MARK
+ {0x0711, 0x0711, prExtend}, // Mn SYRIAC LETTER SUPERSCRIPT ALAPH
+ {0x0730, 0x074A, prExtend}, // Mn [27] SYRIAC PTHAHA ABOVE..SYRIAC BARREKH
+ {0x07A6, 0x07B0, prExtend}, // Mn [11] THAANA ABAFILI..THAANA SUKUN
+ {0x07EB, 0x07F3, prExtend}, // Mn [9] NKO COMBINING SHORT HIGH TONE..NKO COMBINING DOUBLE DOT ABOVE
+ {0x07FD, 0x07FD, prExtend}, // Mn NKO DANTAYALAN
+ {0x0816, 0x0819, prExtend}, // Mn [4] SAMARITAN MARK IN..SAMARITAN MARK DAGESH
+ {0x081B, 0x0823, prExtend}, // Mn [9] SAMARITAN MARK EPENTHETIC YUT..SAMARITAN VOWEL SIGN A
+ {0x0825, 0x0827, prExtend}, // Mn [3] SAMARITAN VOWEL SIGN SHORT A..SAMARITAN VOWEL SIGN U
+ {0x0829, 0x082D, prExtend}, // Mn [5] SAMARITAN VOWEL SIGN LONG I..SAMARITAN MARK NEQUDAA
+ {0x0859, 0x085B, prExtend}, // Mn [3] MANDAIC AFFRICATION MARK..MANDAIC GEMINATION MARK
+ {0x0890, 0x0891, prPrepend}, // Cf [2] ARABIC POUND MARK ABOVE..ARABIC PIASTRE MARK ABOVE
+ {0x0898, 0x089F, prExtend}, // Mn [8] ARABIC SMALL HIGH WORD AL-JUZ..ARABIC HALF MADDA OVER MADDA
+ {0x08CA, 0x08E1, prExtend}, // Mn [24] ARABIC SMALL HIGH FARSI YEH..ARABIC SMALL HIGH SIGN SAFHA
+ {0x08E2, 0x08E2, prPrepend}, // Cf ARABIC DISPUTED END OF AYAH
+ {0x08E3, 0x0902, prExtend}, // Mn [32] ARABIC TURNED DAMMA BELOW..DEVANAGARI SIGN ANUSVARA
+ {0x0903, 0x0903, prSpacingMark}, // Mc DEVANAGARI SIGN VISARGA
+ {0x093A, 0x093A, prExtend}, // Mn DEVANAGARI VOWEL SIGN OE
+ {0x093B, 0x093B, prSpacingMark}, // Mc DEVANAGARI VOWEL SIGN OOE
+ {0x093C, 0x093C, prExtend}, // Mn DEVANAGARI SIGN NUKTA
+ {0x093E, 0x0940, prSpacingMark}, // Mc [3] DEVANAGARI VOWEL SIGN AA..DEVANAGARI VOWEL SIGN II
+ {0x0941, 0x0948, prExtend}, // Mn [8] DEVANAGARI VOWEL SIGN U..DEVANAGARI VOWEL SIGN AI
+ {0x0949, 0x094C, prSpacingMark}, // Mc [4] DEVANAGARI VOWEL SIGN CANDRA O..DEVANAGARI VOWEL SIGN AU
+ {0x094D, 0x094D, prExtend}, // Mn DEVANAGARI SIGN VIRAMA
+ {0x094E, 0x094F, prSpacingMark}, // Mc [2] DEVANAGARI VOWEL SIGN PRISHTHAMATRA E..DEVANAGARI VOWEL SIGN AW
+ {0x0951, 0x0957, prExtend}, // Mn [7] DEVANAGARI STRESS SIGN UDATTA..DEVANAGARI VOWEL SIGN UUE
+ {0x0962, 0x0963, prExtend}, // Mn [2] DEVANAGARI VOWEL SIGN VOCALIC L..DEVANAGARI VOWEL SIGN VOCALIC LL
+ {0x0981, 0x0981, prExtend}, // Mn BENGALI SIGN CANDRABINDU
+ {0x0982, 0x0983, prSpacingMark}, // Mc [2] BENGALI SIGN ANUSVARA..BENGALI SIGN VISARGA
+ {0x09BC, 0x09BC, prExtend}, // Mn BENGALI SIGN NUKTA
+ {0x09BE, 0x09BE, prExtend}, // Mc BENGALI VOWEL SIGN AA
+ {0x09BF, 0x09C0, prSpacingMark}, // Mc [2] BENGALI VOWEL SIGN I..BENGALI VOWEL SIGN II
+ {0x09C1, 0x09C4, prExtend}, // Mn [4] BENGALI VOWEL SIGN U..BENGALI VOWEL SIGN VOCALIC RR
+ {0x09C7, 0x09C8, prSpacingMark}, // Mc [2] BENGALI VOWEL SIGN E..BENGALI VOWEL SIGN AI
+ {0x09CB, 0x09CC, prSpacingMark}, // Mc [2] BENGALI VOWEL SIGN O..BENGALI VOWEL SIGN AU
+ {0x09CD, 0x09CD, prExtend}, // Mn BENGALI SIGN VIRAMA
+ {0x09D7, 0x09D7, prExtend}, // Mc BENGALI AU LENGTH MARK
+ {0x09E2, 0x09E3, prExtend}, // Mn [2] BENGALI VOWEL SIGN VOCALIC L..BENGALI VOWEL SIGN VOCALIC LL
+ {0x09FE, 0x09FE, prExtend}, // Mn BENGALI SANDHI MARK
+ {0x0A01, 0x0A02, prExtend}, // Mn [2] GURMUKHI SIGN ADAK BINDI..GURMUKHI SIGN BINDI
+ {0x0A03, 0x0A03, prSpacingMark}, // Mc GURMUKHI SIGN VISARGA
+ {0x0A3C, 0x0A3C, prExtend}, // Mn GURMUKHI SIGN NUKTA
+ {0x0A3E, 0x0A40, prSpacingMark}, // Mc [3] GURMUKHI VOWEL SIGN AA..GURMUKHI VOWEL SIGN II
+ {0x0A41, 0x0A42, prExtend}, // Mn [2] GURMUKHI VOWEL SIGN U..GURMUKHI VOWEL SIGN UU
+ {0x0A47, 0x0A48, prExtend}, // Mn [2] GURMUKHI VOWEL SIGN EE..GURMUKHI VOWEL SIGN AI
+ {0x0A4B, 0x0A4D, prExtend}, // Mn [3] GURMUKHI VOWEL SIGN OO..GURMUKHI SIGN VIRAMA
+ {0x0A51, 0x0A51, prExtend}, // Mn GURMUKHI SIGN UDAAT
+ {0x0A70, 0x0A71, prExtend}, // Mn [2] GURMUKHI TIPPI..GURMUKHI ADDAK
+ {0x0A75, 0x0A75, prExtend}, // Mn GURMUKHI SIGN YAKASH
+ {0x0A81, 0x0A82, prExtend}, // Mn [2] GUJARATI SIGN CANDRABINDU..GUJARATI SIGN ANUSVARA
+ {0x0A83, 0x0A83, prSpacingMark}, // Mc GUJARATI SIGN VISARGA
+ {0x0ABC, 0x0ABC, prExtend}, // Mn GUJARATI SIGN NUKTA
+ {0x0ABE, 0x0AC0, prSpacingMark}, // Mc [3] GUJARATI VOWEL SIGN AA..GUJARATI VOWEL SIGN II
+ {0x0AC1, 0x0AC5, prExtend}, // Mn [5] GUJARATI VOWEL SIGN U..GUJARATI VOWEL SIGN CANDRA E
+ {0x0AC7, 0x0AC8, prExtend}, // Mn [2] GUJARATI VOWEL SIGN E..GUJARATI VOWEL SIGN AI
+ {0x0AC9, 0x0AC9, prSpacingMark}, // Mc GUJARATI VOWEL SIGN CANDRA O
+ {0x0ACB, 0x0ACC, prSpacingMark}, // Mc [2] GUJARATI VOWEL SIGN O..GUJARATI VOWEL SIGN AU
+ {0x0ACD, 0x0ACD, prExtend}, // Mn GUJARATI SIGN VIRAMA
+ {0x0AE2, 0x0AE3, prExtend}, // Mn [2] GUJARATI VOWEL SIGN VOCALIC L..GUJARATI VOWEL SIGN VOCALIC LL
+ {0x0AFA, 0x0AFF, prExtend}, // Mn [6] GUJARATI SIGN SUKUN..GUJARATI SIGN TWO-CIRCLE NUKTA ABOVE
+ {0x0B01, 0x0B01, prExtend}, // Mn ORIYA SIGN CANDRABINDU
+ {0x0B02, 0x0B03, prSpacingMark}, // Mc [2] ORIYA SIGN ANUSVARA..ORIYA SIGN VISARGA
+ {0x0B3C, 0x0B3C, prExtend}, // Mn ORIYA SIGN NUKTA
+ {0x0B3E, 0x0B3E, prExtend}, // Mc ORIYA VOWEL SIGN AA
+ {0x0B3F, 0x0B3F, prExtend}, // Mn ORIYA VOWEL SIGN I
+ {0x0B40, 0x0B40, prSpacingMark}, // Mc ORIYA VOWEL SIGN II
+ {0x0B41, 0x0B44, prExtend}, // Mn [4] ORIYA VOWEL SIGN U..ORIYA VOWEL SIGN VOCALIC RR
+ {0x0B47, 0x0B48, prSpacingMark}, // Mc [2] ORIYA VOWEL SIGN E..ORIYA VOWEL SIGN AI
+ {0x0B4B, 0x0B4C, prSpacingMark}, // Mc [2] ORIYA VOWEL SIGN O..ORIYA VOWEL SIGN AU
+ {0x0B4D, 0x0B4D, prExtend}, // Mn ORIYA SIGN VIRAMA
+ {0x0B55, 0x0B56, prExtend}, // Mn [2] ORIYA SIGN OVERLINE..ORIYA AI LENGTH MARK
+ {0x0B57, 0x0B57, prExtend}, // Mc ORIYA AU LENGTH MARK
+ {0x0B62, 0x0B63, prExtend}, // Mn [2] ORIYA VOWEL SIGN VOCALIC L..ORIYA VOWEL SIGN VOCALIC LL
+ {0x0B82, 0x0B82, prExtend}, // Mn TAMIL SIGN ANUSVARA
+ {0x0BBE, 0x0BBE, prExtend}, // Mc TAMIL VOWEL SIGN AA
+ {0x0BBF, 0x0BBF, prSpacingMark}, // Mc TAMIL VOWEL SIGN I
+ {0x0BC0, 0x0BC0, prExtend}, // Mn TAMIL VOWEL SIGN II
+ {0x0BC1, 0x0BC2, prSpacingMark}, // Mc [2] TAMIL VOWEL SIGN U..TAMIL VOWEL SIGN UU
+ {0x0BC6, 0x0BC8, prSpacingMark}, // Mc [3] TAMIL VOWEL SIGN E..TAMIL VOWEL SIGN AI
+ {0x0BCA, 0x0BCC, prSpacingMark}, // Mc [3] TAMIL VOWEL SIGN O..TAMIL VOWEL SIGN AU
+ {0x0BCD, 0x0BCD, prExtend}, // Mn TAMIL SIGN VIRAMA
+ {0x0BD7, 0x0BD7, prExtend}, // Mc TAMIL AU LENGTH MARK
+ {0x0C00, 0x0C00, prExtend}, // Mn TELUGU SIGN COMBINING CANDRABINDU ABOVE
+ {0x0C01, 0x0C03, prSpacingMark}, // Mc [3] TELUGU SIGN CANDRABINDU..TELUGU SIGN VISARGA
+ {0x0C04, 0x0C04, prExtend}, // Mn TELUGU SIGN COMBINING ANUSVARA ABOVE
+ {0x0C3C, 0x0C3C, prExtend}, // Mn TELUGU SIGN NUKTA
+ {0x0C3E, 0x0C40, prExtend}, // Mn [3] TELUGU VOWEL SIGN AA..TELUGU VOWEL SIGN II
+ {0x0C41, 0x0C44, prSpacingMark}, // Mc [4] TELUGU VOWEL SIGN U..TELUGU VOWEL SIGN VOCALIC RR
+ {0x0C46, 0x0C48, prExtend}, // Mn [3] TELUGU VOWEL SIGN E..TELUGU VOWEL SIGN AI
+ {0x0C4A, 0x0C4D, prExtend}, // Mn [4] TELUGU VOWEL SIGN O..TELUGU SIGN VIRAMA
+ {0x0C55, 0x0C56, prExtend}, // Mn [2] TELUGU LENGTH MARK..TELUGU AI LENGTH MARK
+ {0x0C62, 0x0C63, prExtend}, // Mn [2] TELUGU VOWEL SIGN VOCALIC L..TELUGU VOWEL SIGN VOCALIC LL
+ {0x0C81, 0x0C81, prExtend}, // Mn KANNADA SIGN CANDRABINDU
+ {0x0C82, 0x0C83, prSpacingMark}, // Mc [2] KANNADA SIGN ANUSVARA..KANNADA SIGN VISARGA
+ {0x0CBC, 0x0CBC, prExtend}, // Mn KANNADA SIGN NUKTA
+ {0x0CBE, 0x0CBE, prSpacingMark}, // Mc KANNADA VOWEL SIGN AA
+ {0x0CBF, 0x0CBF, prExtend}, // Mn KANNADA VOWEL SIGN I
+ {0x0CC0, 0x0CC1, prSpacingMark}, // Mc [2] KANNADA VOWEL SIGN II..KANNADA VOWEL SIGN U
+ {0x0CC2, 0x0CC2, prExtend}, // Mc KANNADA VOWEL SIGN UU
+ {0x0CC3, 0x0CC4, prSpacingMark}, // Mc [2] KANNADA VOWEL SIGN VOCALIC R..KANNADA VOWEL SIGN VOCALIC RR
+ {0x0CC6, 0x0CC6, prExtend}, // Mn KANNADA VOWEL SIGN E
+ {0x0CC7, 0x0CC8, prSpacingMark}, // Mc [2] KANNADA VOWEL SIGN EE..KANNADA VOWEL SIGN AI
+ {0x0CCA, 0x0CCB, prSpacingMark}, // Mc [2] KANNADA VOWEL SIGN O..KANNADA VOWEL SIGN OO
+ {0x0CCC, 0x0CCD, prExtend}, // Mn [2] KANNADA VOWEL SIGN AU..KANNADA SIGN VIRAMA
+ {0x0CD5, 0x0CD6, prExtend}, // Mc [2] KANNADA LENGTH MARK..KANNADA AI LENGTH MARK
+ {0x0CE2, 0x0CE3, prExtend}, // Mn [2] KANNADA VOWEL SIGN VOCALIC L..KANNADA VOWEL SIGN VOCALIC LL
+ {0x0D00, 0x0D01, prExtend}, // Mn [2] MALAYALAM SIGN COMBINING ANUSVARA ABOVE..MALAYALAM SIGN CANDRABINDU
+ {0x0D02, 0x0D03, prSpacingMark}, // Mc [2] MALAYALAM SIGN ANUSVARA..MALAYALAM SIGN VISARGA
+ {0x0D3B, 0x0D3C, prExtend}, // Mn [2] MALAYALAM SIGN VERTICAL BAR VIRAMA..MALAYALAM SIGN CIRCULAR VIRAMA
+ {0x0D3E, 0x0D3E, prExtend}, // Mc MALAYALAM VOWEL SIGN AA
+ {0x0D3F, 0x0D40, prSpacingMark}, // Mc [2] MALAYALAM VOWEL SIGN I..MALAYALAM VOWEL SIGN II
+ {0x0D41, 0x0D44, prExtend}, // Mn [4] MALAYALAM VOWEL SIGN U..MALAYALAM VOWEL SIGN VOCALIC RR
+ {0x0D46, 0x0D48, prSpacingMark}, // Mc [3] MALAYALAM VOWEL SIGN E..MALAYALAM VOWEL SIGN AI
+ {0x0D4A, 0x0D4C, prSpacingMark}, // Mc [3] MALAYALAM VOWEL SIGN O..MALAYALAM VOWEL SIGN AU
+ {0x0D4D, 0x0D4D, prExtend}, // Mn MALAYALAM SIGN VIRAMA
+ {0x0D4E, 0x0D4E, prPrepend}, // Lo MALAYALAM LETTER DOT REPH
+ {0x0D57, 0x0D57, prExtend}, // Mc MALAYALAM AU LENGTH MARK
+ {0x0D62, 0x0D63, prExtend}, // Mn [2] MALAYALAM VOWEL SIGN VOCALIC L..MALAYALAM VOWEL SIGN VOCALIC LL
+ {0x0D81, 0x0D81, prExtend}, // Mn SINHALA SIGN CANDRABINDU
+ {0x0D82, 0x0D83, prSpacingMark}, // Mc [2] SINHALA SIGN ANUSVARAYA..SINHALA SIGN VISARGAYA
+ {0x0DCA, 0x0DCA, prExtend}, // Mn SINHALA SIGN AL-LAKUNA
+ {0x0DCF, 0x0DCF, prExtend}, // Mc SINHALA VOWEL SIGN AELA-PILLA
+ {0x0DD0, 0x0DD1, prSpacingMark}, // Mc [2] SINHALA VOWEL SIGN KETTI AEDA-PILLA..SINHALA VOWEL SIGN DIGA AEDA-PILLA
+ {0x0DD2, 0x0DD4, prExtend}, // Mn [3] SINHALA VOWEL SIGN KETTI IS-PILLA..SINHALA VOWEL SIGN KETTI PAA-PILLA
+ {0x0DD6, 0x0DD6, prExtend}, // Mn SINHALA VOWEL SIGN DIGA PAA-PILLA
+ {0x0DD8, 0x0DDE, prSpacingMark}, // Mc [7] SINHALA VOWEL SIGN GAETTA-PILLA..SINHALA VOWEL SIGN KOMBUVA HAA GAYANUKITTA
+ {0x0DDF, 0x0DDF, prExtend}, // Mc SINHALA VOWEL SIGN GAYANUKITTA
+ {0x0DF2, 0x0DF3, prSpacingMark}, // Mc [2] SINHALA VOWEL SIGN DIGA GAETTA-PILLA..SINHALA VOWEL SIGN DIGA GAYANUKITTA
+ {0x0E31, 0x0E31, prExtend}, // Mn THAI CHARACTER MAI HAN-AKAT
+ {0x0E33, 0x0E33, prSpacingMark}, // Lo THAI CHARACTER SARA AM
+ {0x0E34, 0x0E3A, prExtend}, // Mn [7] THAI CHARACTER SARA I..THAI CHARACTER PHINTHU
+ {0x0E47, 0x0E4E, prExtend}, // Mn [8] THAI CHARACTER MAITAIKHU..THAI CHARACTER YAMAKKAN
+ {0x0EB1, 0x0EB1, prExtend}, // Mn LAO VOWEL SIGN MAI KAN
+ {0x0EB3, 0x0EB3, prSpacingMark}, // Lo LAO VOWEL SIGN AM
+ {0x0EB4, 0x0EBC, prExtend}, // Mn [9] LAO VOWEL SIGN I..LAO SEMIVOWEL SIGN LO
+ {0x0EC8, 0x0ECD, prExtend}, // Mn [6] LAO TONE MAI EK..LAO NIGGAHITA
+ {0x0F18, 0x0F19, prExtend}, // Mn [2] TIBETAN ASTROLOGICAL SIGN -KHYUD PA..TIBETAN ASTROLOGICAL SIGN SDONG TSHUGS
+ {0x0F35, 0x0F35, prExtend}, // Mn TIBETAN MARK NGAS BZUNG NYI ZLA
+ {0x0F37, 0x0F37, prExtend}, // Mn TIBETAN MARK NGAS BZUNG SGOR RTAGS
+ {0x0F39, 0x0F39, prExtend}, // Mn TIBETAN MARK TSA -PHRU
+ {0x0F3E, 0x0F3F, prSpacingMark}, // Mc [2] TIBETAN SIGN YAR TSHES..TIBETAN SIGN MAR TSHES
+ {0x0F71, 0x0F7E, prExtend}, // Mn [14] TIBETAN VOWEL SIGN AA..TIBETAN SIGN RJES SU NGA RO
+ {0x0F7F, 0x0F7F, prSpacingMark}, // Mc TIBETAN SIGN RNAM BCAD
+ {0x0F80, 0x0F84, prExtend}, // Mn [5] TIBETAN VOWEL SIGN REVERSED I..TIBETAN MARK HALANTA
+ {0x0F86, 0x0F87, prExtend}, // Mn [2] TIBETAN SIGN LCI RTAGS..TIBETAN SIGN YANG RTAGS
+ {0x0F8D, 0x0F97, prExtend}, // Mn [11] TIBETAN SUBJOINED SIGN LCE TSA CAN..TIBETAN SUBJOINED LETTER JA
+ {0x0F99, 0x0FBC, prExtend}, // Mn [36] TIBETAN SUBJOINED LETTER NYA..TIBETAN SUBJOINED LETTER FIXED-FORM RA
+ {0x0FC6, 0x0FC6, prExtend}, // Mn TIBETAN SYMBOL PADMA GDAN
+ {0x102D, 0x1030, prExtend}, // Mn [4] MYANMAR VOWEL SIGN I..MYANMAR VOWEL SIGN UU
+ {0x1031, 0x1031, prSpacingMark}, // Mc MYANMAR VOWEL SIGN E
+ {0x1032, 0x1037, prExtend}, // Mn [6] MYANMAR VOWEL SIGN AI..MYANMAR SIGN DOT BELOW
+ {0x1039, 0x103A, prExtend}, // Mn [2] MYANMAR SIGN VIRAMA..MYANMAR SIGN ASAT
+ {0x103B, 0x103C, prSpacingMark}, // Mc [2] MYANMAR CONSONANT SIGN MEDIAL YA..MYANMAR CONSONANT SIGN MEDIAL RA
+ {0x103D, 0x103E, prExtend}, // Mn [2] MYANMAR CONSONANT SIGN MEDIAL WA..MYANMAR CONSONANT SIGN MEDIAL HA
+ {0x1056, 0x1057, prSpacingMark}, // Mc [2] MYANMAR VOWEL SIGN VOCALIC R..MYANMAR VOWEL SIGN VOCALIC RR
+ {0x1058, 0x1059, prExtend}, // Mn [2] MYANMAR VOWEL SIGN VOCALIC L..MYANMAR VOWEL SIGN VOCALIC LL
+ {0x105E, 0x1060, prExtend}, // Mn [3] MYANMAR CONSONANT SIGN MON MEDIAL NA..MYANMAR CONSONANT SIGN MON MEDIAL LA
+ {0x1071, 0x1074, prExtend}, // Mn [4] MYANMAR VOWEL SIGN GEBA KAREN I..MYANMAR VOWEL SIGN KAYAH EE
+ {0x1082, 0x1082, prExtend}, // Mn MYANMAR CONSONANT SIGN SHAN MEDIAL WA
+ {0x1084, 0x1084, prSpacingMark}, // Mc MYANMAR VOWEL SIGN SHAN E
+ {0x1085, 0x1086, prExtend}, // Mn [2] MYANMAR VOWEL SIGN SHAN E ABOVE..MYANMAR VOWEL SIGN SHAN FINAL Y
+ {0x108D, 0x108D, prExtend}, // Mn MYANMAR SIGN SHAN COUNCIL EMPHATIC TONE
+ {0x109D, 0x109D, prExtend}, // Mn MYANMAR VOWEL SIGN AITON AI
+ {0x1100, 0x115F, prL}, // Lo [96] HANGUL CHOSEONG KIYEOK..HANGUL CHOSEONG FILLER
+ {0x1160, 0x11A7, prV}, // Lo [72] HANGUL JUNGSEONG FILLER..HANGUL JUNGSEONG O-YAE
+ {0x11A8, 0x11FF, prT}, // Lo [88] HANGUL JONGSEONG KIYEOK..HANGUL JONGSEONG SSANGNIEUN
+ {0x135D, 0x135F, prExtend}, // Mn [3] ETHIOPIC COMBINING GEMINATION AND VOWEL LENGTH MARK..ETHIOPIC COMBINING GEMINATION MARK
+ {0x1712, 0x1714, prExtend}, // Mn [3] TAGALOG VOWEL SIGN I..TAGALOG SIGN VIRAMA
+ {0x1715, 0x1715, prSpacingMark}, // Mc TAGALOG SIGN PAMUDPOD
+ {0x1732, 0x1733, prExtend}, // Mn [2] HANUNOO VOWEL SIGN I..HANUNOO VOWEL SIGN U
+ {0x1734, 0x1734, prSpacingMark}, // Mc HANUNOO SIGN PAMUDPOD
+ {0x1752, 0x1753, prExtend}, // Mn [2] BUHID VOWEL SIGN I..BUHID VOWEL SIGN U
+ {0x1772, 0x1773, prExtend}, // Mn [2] TAGBANWA VOWEL SIGN I..TAGBANWA VOWEL SIGN U
+ {0x17B4, 0x17B5, prExtend}, // Mn [2] KHMER VOWEL INHERENT AQ..KHMER VOWEL INHERENT AA
+ {0x17B6, 0x17B6, prSpacingMark}, // Mc KHMER VOWEL SIGN AA
+ {0x17B7, 0x17BD, prExtend}, // Mn [7] KHMER VOWEL SIGN I..KHMER VOWEL SIGN UA
+ {0x17BE, 0x17C5, prSpacingMark}, // Mc [8] KHMER VOWEL SIGN OE..KHMER VOWEL SIGN AU
+ {0x17C6, 0x17C6, prExtend}, // Mn KHMER SIGN NIKAHIT
+ {0x17C7, 0x17C8, prSpacingMark}, // Mc [2] KHMER SIGN REAHMUK..KHMER SIGN YUUKALEAPINTU
+ {0x17C9, 0x17D3, prExtend}, // Mn [11] KHMER SIGN MUUSIKATOAN..KHMER SIGN BATHAMASAT
+ {0x17DD, 0x17DD, prExtend}, // Mn KHMER SIGN ATTHACAN
+ {0x180B, 0x180D, prExtend}, // Mn [3] MONGOLIAN FREE VARIATION SELECTOR ONE..MONGOLIAN FREE VARIATION SELECTOR THREE
+ {0x180E, 0x180E, prControl}, // Cf MONGOLIAN VOWEL SEPARATOR
+ {0x180F, 0x180F, prExtend}, // Mn MONGOLIAN FREE VARIATION SELECTOR FOUR
+ {0x1885, 0x1886, prExtend}, // Mn [2] MONGOLIAN LETTER ALI GALI BALUDA..MONGOLIAN LETTER ALI GALI THREE BALUDA
+ {0x18A9, 0x18A9, prExtend}, // Mn MONGOLIAN LETTER ALI GALI DAGALGA
+ {0x1920, 0x1922, prExtend}, // Mn [3] LIMBU VOWEL SIGN A..LIMBU VOWEL SIGN U
+ {0x1923, 0x1926, prSpacingMark}, // Mc [4] LIMBU VOWEL SIGN EE..LIMBU VOWEL SIGN AU
+ {0x1927, 0x1928, prExtend}, // Mn [2] LIMBU VOWEL SIGN E..LIMBU VOWEL SIGN O
+ {0x1929, 0x192B, prSpacingMark}, // Mc [3] LIMBU SUBJOINED LETTER YA..LIMBU SUBJOINED LETTER WA
+ {0x1930, 0x1931, prSpacingMark}, // Mc [2] LIMBU SMALL LETTER KA..LIMBU SMALL LETTER NGA
+ {0x1932, 0x1932, prExtend}, // Mn LIMBU SMALL LETTER ANUSVARA
+ {0x1933, 0x1938, prSpacingMark}, // Mc [6] LIMBU SMALL LETTER TA..LIMBU SMALL LETTER LA
+ {0x1939, 0x193B, prExtend}, // Mn [3] LIMBU SIGN MUKPHRENG..LIMBU SIGN SA-I
+ {0x1A17, 0x1A18, prExtend}, // Mn [2] BUGINESE VOWEL SIGN I..BUGINESE VOWEL SIGN U
+ {0x1A19, 0x1A1A, prSpacingMark}, // Mc [2] BUGINESE VOWEL SIGN E..BUGINESE VOWEL SIGN O
+ {0x1A1B, 0x1A1B, prExtend}, // Mn BUGINESE VOWEL SIGN AE
+ {0x1A55, 0x1A55, prSpacingMark}, // Mc TAI THAM CONSONANT SIGN MEDIAL RA
+ {0x1A56, 0x1A56, prExtend}, // Mn TAI THAM CONSONANT SIGN MEDIAL LA
+ {0x1A57, 0x1A57, prSpacingMark}, // Mc TAI THAM CONSONANT SIGN LA TANG LAI
+ {0x1A58, 0x1A5E, prExtend}, // Mn [7] TAI THAM SIGN MAI KANG LAI..TAI THAM CONSONANT SIGN SA
+ {0x1A60, 0x1A60, prExtend}, // Mn TAI THAM SIGN SAKOT
+ {0x1A62, 0x1A62, prExtend}, // Mn TAI THAM VOWEL SIGN MAI SAT
+ {0x1A65, 0x1A6C, prExtend}, // Mn [8] TAI THAM VOWEL SIGN I..TAI THAM VOWEL SIGN OA BELOW
+ {0x1A6D, 0x1A72, prSpacingMark}, // Mc [6] TAI THAM VOWEL SIGN OY..TAI THAM VOWEL SIGN THAM AI
+ {0x1A73, 0x1A7C, prExtend}, // Mn [10] TAI THAM VOWEL SIGN OA ABOVE..TAI THAM SIGN KHUEN-LUE KARAN
+ {0x1A7F, 0x1A7F, prExtend}, // Mn TAI THAM COMBINING CRYPTOGRAMMIC DOT
+ {0x1AB0, 0x1ABD, prExtend}, // Mn [14] COMBINING DOUBLED CIRCUMFLEX ACCENT..COMBINING PARENTHESES BELOW
+ {0x1ABE, 0x1ABE, prExtend}, // Me COMBINING PARENTHESES OVERLAY
+ {0x1ABF, 0x1ACE, prExtend}, // Mn [16] COMBINING LATIN SMALL LETTER W BELOW..COMBINING LATIN SMALL LETTER INSULAR T
+ {0x1B00, 0x1B03, prExtend}, // Mn [4] BALINESE SIGN ULU RICEM..BALINESE SIGN SURANG
+ {0x1B04, 0x1B04, prSpacingMark}, // Mc BALINESE SIGN BISAH
+ {0x1B34, 0x1B34, prExtend}, // Mn BALINESE SIGN REREKAN
+ {0x1B35, 0x1B35, prExtend}, // Mc BALINESE VOWEL SIGN TEDUNG
+ {0x1B36, 0x1B3A, prExtend}, // Mn [5] BALINESE VOWEL SIGN ULU..BALINESE VOWEL SIGN RA REPA
+ {0x1B3B, 0x1B3B, prSpacingMark}, // Mc BALINESE VOWEL SIGN RA REPA TEDUNG
+ {0x1B3C, 0x1B3C, prExtend}, // Mn BALINESE VOWEL SIGN LA LENGA
+ {0x1B3D, 0x1B41, prSpacingMark}, // Mc [5] BALINESE VOWEL SIGN LA LENGA TEDUNG..BALINESE VOWEL SIGN TALING REPA TEDUNG
+ {0x1B42, 0x1B42, prExtend}, // Mn BALINESE VOWEL SIGN PEPET
+ {0x1B43, 0x1B44, prSpacingMark}, // Mc [2] BALINESE VOWEL SIGN PEPET TEDUNG..BALINESE ADEG ADEG
+ {0x1B6B, 0x1B73, prExtend}, // Mn [9] BALINESE MUSICAL SYMBOL COMBINING TEGEH..BALINESE MUSICAL SYMBOL COMBINING GONG
+ {0x1B80, 0x1B81, prExtend}, // Mn [2] SUNDANESE SIGN PANYECEK..SUNDANESE SIGN PANGLAYAR
+ {0x1B82, 0x1B82, prSpacingMark}, // Mc SUNDANESE SIGN PANGWISAD
+ {0x1BA1, 0x1BA1, prSpacingMark}, // Mc SUNDANESE CONSONANT SIGN PAMINGKAL
+ {0x1BA2, 0x1BA5, prExtend}, // Mn [4] SUNDANESE CONSONANT SIGN PANYAKRA..SUNDANESE VOWEL SIGN PANYUKU
+ {0x1BA6, 0x1BA7, prSpacingMark}, // Mc [2] SUNDANESE VOWEL SIGN PANAELAENG..SUNDANESE VOWEL SIGN PANOLONG
+ {0x1BA8, 0x1BA9, prExtend}, // Mn [2] SUNDANESE VOWEL SIGN PAMEPET..SUNDANESE VOWEL SIGN PANEULEUNG
+ {0x1BAA, 0x1BAA, prSpacingMark}, // Mc SUNDANESE SIGN PAMAAEH
+ {0x1BAB, 0x1BAD, prExtend}, // Mn [3] SUNDANESE SIGN VIRAMA..SUNDANESE CONSONANT SIGN PASANGAN WA
+ {0x1BE6, 0x1BE6, prExtend}, // Mn BATAK SIGN TOMPI
+ {0x1BE7, 0x1BE7, prSpacingMark}, // Mc BATAK VOWEL SIGN E
+ {0x1BE8, 0x1BE9, prExtend}, // Mn [2] BATAK VOWEL SIGN PAKPAK E..BATAK VOWEL SIGN EE
+ {0x1BEA, 0x1BEC, prSpacingMark}, // Mc [3] BATAK VOWEL SIGN I..BATAK VOWEL SIGN O
+ {0x1BED, 0x1BED, prExtend}, // Mn BATAK VOWEL SIGN KARO O
+ {0x1BEE, 0x1BEE, prSpacingMark}, // Mc BATAK VOWEL SIGN U
+ {0x1BEF, 0x1BF1, prExtend}, // Mn [3] BATAK VOWEL SIGN U FOR SIMALUNGUN SA..BATAK CONSONANT SIGN H
+ {0x1BF2, 0x1BF3, prSpacingMark}, // Mc [2] BATAK PANGOLAT..BATAK PANONGONAN
+ {0x1C24, 0x1C2B, prSpacingMark}, // Mc [8] LEPCHA SUBJOINED LETTER YA..LEPCHA VOWEL SIGN UU
+ {0x1C2C, 0x1C33, prExtend}, // Mn [8] LEPCHA VOWEL SIGN E..LEPCHA CONSONANT SIGN T
+ {0x1C34, 0x1C35, prSpacingMark}, // Mc [2] LEPCHA CONSONANT SIGN NYIN-DO..LEPCHA CONSONANT SIGN KANG
+ {0x1C36, 0x1C37, prExtend}, // Mn [2] LEPCHA SIGN RAN..LEPCHA SIGN NUKTA
+ {0x1CD0, 0x1CD2, prExtend}, // Mn [3] VEDIC TONE KARSHANA..VEDIC TONE PRENKHA
+ {0x1CD4, 0x1CE0, prExtend}, // Mn [13] VEDIC SIGN YAJURVEDIC MIDLINE SVARITA..VEDIC TONE RIGVEDIC KASHMIRI INDEPENDENT SVARITA
+ {0x1CE1, 0x1CE1, prSpacingMark}, // Mc VEDIC TONE ATHARVAVEDIC INDEPENDENT SVARITA
+ {0x1CE2, 0x1CE8, prExtend}, // Mn [7] VEDIC SIGN VISARGA SVARITA..VEDIC SIGN VISARGA ANUDATTA WITH TAIL
+ {0x1CED, 0x1CED, prExtend}, // Mn VEDIC SIGN TIRYAK
+ {0x1CF4, 0x1CF4, prExtend}, // Mn VEDIC TONE CANDRA ABOVE
+ {0x1CF7, 0x1CF7, prSpacingMark}, // Mc VEDIC SIGN ATIKRAMA
+ {0x1CF8, 0x1CF9, prExtend}, // Mn [2] VEDIC TONE RING ABOVE..VEDIC TONE DOUBLE RING ABOVE
+ {0x1DC0, 0x1DFF, prExtend}, // Mn [64] COMBINING DOTTED GRAVE ACCENT..COMBINING RIGHT ARROWHEAD AND DOWN ARROWHEAD BELOW
+ {0x200B, 0x200B, prControl}, // Cf ZERO WIDTH SPACE
+ {0x200C, 0x200C, prExtend}, // Cf ZERO WIDTH NON-JOINER
+ {0x200D, 0x200D, prZWJ}, // Cf ZERO WIDTH JOINER
+ {0x200E, 0x200F, prControl}, // Cf [2] LEFT-TO-RIGHT MARK..RIGHT-TO-LEFT MARK
+ {0x2028, 0x2028, prControl}, // Zl LINE SEPARATOR
+ {0x2029, 0x2029, prControl}, // Zp PARAGRAPH SEPARATOR
+ {0x202A, 0x202E, prControl}, // Cf [5] LEFT-TO-RIGHT EMBEDDING..RIGHT-TO-LEFT OVERRIDE
+ {0x203C, 0x203C, prExtendedPictographic}, // E0.6 [1] (‼️) double exclamation mark
+ {0x2049, 0x2049, prExtendedPictographic}, // E0.6 [1] (⁉️) exclamation question mark
+ {0x2060, 0x2064, prControl}, // Cf [5] WORD JOINER..INVISIBLE PLUS
+ {0x2065, 0x2065, prControl}, // Cn
+ {0x2066, 0x206F, prControl}, // Cf [10] LEFT-TO-RIGHT ISOLATE..NOMINAL DIGIT SHAPES
+ {0x20D0, 0x20DC, prExtend}, // Mn [13] COMBINING LEFT HARPOON ABOVE..COMBINING FOUR DOTS ABOVE
+ {0x20DD, 0x20E0, prExtend}, // Me [4] COMBINING ENCLOSING CIRCLE..COMBINING ENCLOSING CIRCLE BACKSLASH
+ {0x20E1, 0x20E1, prExtend}, // Mn COMBINING LEFT RIGHT ARROW ABOVE
+ {0x20E2, 0x20E4, prExtend}, // Me [3] COMBINING ENCLOSING SCREEN..COMBINING ENCLOSING UPWARD POINTING TRIANGLE
+ {0x20E5, 0x20F0, prExtend}, // Mn [12] COMBINING REVERSE SOLIDUS OVERLAY..COMBINING ASTERISK ABOVE
+ {0x2122, 0x2122, prExtendedPictographic}, // E0.6 [1] (™️) trade mark
+ {0x2139, 0x2139, prExtendedPictographic}, // E0.6 [1] (ℹ️) information
+ {0x2194, 0x2199, prExtendedPictographic}, // E0.6 [6] (↔️..↙️) left-right arrow..down-left arrow
+ {0x21A9, 0x21AA, prExtendedPictographic}, // E0.6 [2] (↩️..↪️) right arrow curving left..left arrow curving right
+ {0x231A, 0x231B, prExtendedPictographic}, // E0.6 [2] (⌚..⌛) watch..hourglass done
+ {0x2328, 0x2328, prExtendedPictographic}, // E1.0 [1] (⌨️) keyboard
+ {0x2388, 0x2388, prExtendedPictographic}, // E0.0 [1] (⎈) HELM SYMBOL
+ {0x23CF, 0x23CF, prExtendedPictographic}, // E1.0 [1] (⏏️) eject button
+ {0x23E9, 0x23EC, prExtendedPictographic}, // E0.6 [4] (⏩..⏬) fast-forward button..fast down button
+ {0x23ED, 0x23EE, prExtendedPictographic}, // E0.7 [2] (⏭️..⏮️) next track button..last track button
+ {0x23EF, 0x23EF, prExtendedPictographic}, // E1.0 [1] (⏯️) play or pause button
+ {0x23F0, 0x23F0, prExtendedPictographic}, // E0.6 [1] (⏰) alarm clock
+ {0x23F1, 0x23F2, prExtendedPictographic}, // E1.0 [2] (⏱️..⏲️) stopwatch..timer clock
+ {0x23F3, 0x23F3, prExtendedPictographic}, // E0.6 [1] (⏳) hourglass not done
+ {0x23F8, 0x23FA, prExtendedPictographic}, // E0.7 [3] (⏸️..⏺️) pause button..record button
+ {0x24C2, 0x24C2, prExtendedPictographic}, // E0.6 [1] (Ⓜ️) circled M
+ {0x25AA, 0x25AB, prExtendedPictographic}, // E0.6 [2] (▪️..▫️) black small square..white small square
+ {0x25B6, 0x25B6, prExtendedPictographic}, // E0.6 [1] (▶️) play button
+ {0x25C0, 0x25C0, prExtendedPictographic}, // E0.6 [1] (◀️) reverse button
+ {0x25FB, 0x25FE, prExtendedPictographic}, // E0.6 [4] (◻️..◾) white medium square..black medium-small square
+ {0x2600, 0x2601, prExtendedPictographic}, // E0.6 [2] (☀️..☁️) sun..cloud
+ {0x2602, 0x2603, prExtendedPictographic}, // E0.7 [2] (☂️..☃️) umbrella..snowman
+ {0x2604, 0x2604, prExtendedPictographic}, // E1.0 [1] (☄️) comet
+ {0x2605, 0x2605, prExtendedPictographic}, // E0.0 [1] (★) BLACK STAR
+ {0x2607, 0x260D, prExtendedPictographic}, // E0.0 [7] (☇..☍) LIGHTNING..OPPOSITION
+ {0x260E, 0x260E, prExtendedPictographic}, // E0.6 [1] (☎️) telephone
+ {0x260F, 0x2610, prExtendedPictographic}, // E0.0 [2] (☏..☐) WHITE TELEPHONE..BALLOT BOX
+ {0x2611, 0x2611, prExtendedPictographic}, // E0.6 [1] (☑️) check box with check
+ {0x2612, 0x2612, prExtendedPictographic}, // E0.0 [1] (☒) BALLOT BOX WITH X
+ {0x2614, 0x2615, prExtendedPictographic}, // E0.6 [2] (☔..☕) umbrella with rain drops..hot beverage
+ {0x2616, 0x2617, prExtendedPictographic}, // E0.0 [2] (☖..☗) WHITE SHOGI PIECE..BLACK SHOGI PIECE
+ {0x2618, 0x2618, prExtendedPictographic}, // E1.0 [1] (☘️) shamrock
+ {0x2619, 0x261C, prExtendedPictographic}, // E0.0 [4] (☙..☜) REVERSED ROTATED FLORAL HEART BULLET..WHITE LEFT POINTING INDEX
+ {0x261D, 0x261D, prExtendedPictographic}, // E0.6 [1] (☝️) index pointing up
+ {0x261E, 0x261F, prExtendedPictographic}, // E0.0 [2] (☞..☟) WHITE RIGHT POINTING INDEX..WHITE DOWN POINTING INDEX
+ {0x2620, 0x2620, prExtendedPictographic}, // E1.0 [1] (☠️) skull and crossbones
+ {0x2621, 0x2621, prExtendedPictographic}, // E0.0 [1] (☡) CAUTION SIGN
+ {0x2622, 0x2623, prExtendedPictographic}, // E1.0 [2] (☢️..☣️) radioactive..biohazard
+ {0x2624, 0x2625, prExtendedPictographic}, // E0.0 [2] (☤..☥) CADUCEUS..ANKH
+ {0x2626, 0x2626, prExtendedPictographic}, // E1.0 [1] (☦️) orthodox cross
+ {0x2627, 0x2629, prExtendedPictographic}, // E0.0 [3] (☧..☩) CHI RHO..CROSS OF JERUSALEM
+ {0x262A, 0x262A, prExtendedPictographic}, // E0.7 [1] (☪️) star and crescent
+ {0x262B, 0x262D, prExtendedPictographic}, // E0.0 [3] (☫..☭) FARSI SYMBOL..HAMMER AND SICKLE
+ {0x262E, 0x262E, prExtendedPictographic}, // E1.0 [1] (☮️) peace symbol
+ {0x262F, 0x262F, prExtendedPictographic}, // E0.7 [1] (☯️) yin yang
+ {0x2630, 0x2637, prExtendedPictographic}, // E0.0 [8] (☰..☷) TRIGRAM FOR HEAVEN..TRIGRAM FOR EARTH
+ {0x2638, 0x2639, prExtendedPictographic}, // E0.7 [2] (☸️..☹️) wheel of dharma..frowning face
+ {0x263A, 0x263A, prExtendedPictographic}, // E0.6 [1] (☺️) smiling face
+ {0x263B, 0x263F, prExtendedPictographic}, // E0.0 [5] (☻..☿) BLACK SMILING FACE..MERCURY
+ {0x2640, 0x2640, prExtendedPictographic}, // E4.0 [1] (♀️) female sign
+ {0x2641, 0x2641, prExtendedPictographic}, // E0.0 [1] (♁) EARTH
+ {0x2642, 0x2642, prExtendedPictographic}, // E4.0 [1] (♂️) male sign
+ {0x2643, 0x2647, prExtendedPictographic}, // E0.0 [5] (♃..♇) JUPITER..PLUTO
+ {0x2648, 0x2653, prExtendedPictographic}, // E0.6 [12] (♈..♓) Aries..Pisces
+ {0x2654, 0x265E, prExtendedPictographic}, // E0.0 [11] (♔..♞) WHITE CHESS KING..BLACK CHESS KNIGHT
+ {0x265F, 0x265F, prExtendedPictographic}, // E11.0 [1] (♟️) chess pawn
+ {0x2660, 0x2660, prExtendedPictographic}, // E0.6 [1] (♠️) spade suit
+ {0x2661, 0x2662, prExtendedPictographic}, // E0.0 [2] (♡..♢) WHITE HEART SUIT..WHITE DIAMOND SUIT
+ {0x2663, 0x2663, prExtendedPictographic}, // E0.6 [1] (♣️) club suit
+ {0x2664, 0x2664, prExtendedPictographic}, // E0.0 [1] (♤) WHITE SPADE SUIT
+ {0x2665, 0x2666, prExtendedPictographic}, // E0.6 [2] (♥️..♦️) heart suit..diamond suit
+ {0x2667, 0x2667, prExtendedPictographic}, // E0.0 [1] (♧) WHITE CLUB SUIT
+ {0x2668, 0x2668, prExtendedPictographic}, // E0.6 [1] (♨️) hot springs
+ {0x2669, 0x267A, prExtendedPictographic}, // E0.0 [18] (♩..♺) QUARTER NOTE..RECYCLING SYMBOL FOR GENERIC MATERIALS
+ {0x267B, 0x267B, prExtendedPictographic}, // E0.6 [1] (♻️) recycling symbol
+ {0x267C, 0x267D, prExtendedPictographic}, // E0.0 [2] (♼..♽) RECYCLED PAPER SYMBOL..PARTIALLY-RECYCLED PAPER SYMBOL
+ {0x267E, 0x267E, prExtendedPictographic}, // E11.0 [1] (♾️) infinity
+ {0x267F, 0x267F, prExtendedPictographic}, // E0.6 [1] (♿) wheelchair symbol
+ {0x2680, 0x2685, prExtendedPictographic}, // E0.0 [6] (⚀..⚅) DIE FACE-1..DIE FACE-6
+ {0x2690, 0x2691, prExtendedPictographic}, // E0.0 [2] (⚐..⚑) WHITE FLAG..BLACK FLAG
+ {0x2692, 0x2692, prExtendedPictographic}, // E1.0 [1] (⚒️) hammer and pick
+ {0x2693, 0x2693, prExtendedPictographic}, // E0.6 [1] (⚓) anchor
+ {0x2694, 0x2694, prExtendedPictographic}, // E1.0 [1] (⚔️) crossed swords
+ {0x2695, 0x2695, prExtendedPictographic}, // E4.0 [1] (⚕️) medical symbol
+ {0x2696, 0x2697, prExtendedPictographic}, // E1.0 [2] (⚖️..⚗️) balance scale..alembic
+ {0x2698, 0x2698, prExtendedPictographic}, // E0.0 [1] (⚘) FLOWER
+ {0x2699, 0x2699, prExtendedPictographic}, // E1.0 [1] (⚙️) gear
+ {0x269A, 0x269A, prExtendedPictographic}, // E0.0 [1] (⚚) STAFF OF HERMES
+ {0x269B, 0x269C, prExtendedPictographic}, // E1.0 [2] (⚛️..⚜️) atom symbol..fleur-de-lis
+ {0x269D, 0x269F, prExtendedPictographic}, // E0.0 [3] (⚝..⚟) OUTLINED WHITE STAR..THREE LINES CONVERGING LEFT
+ {0x26A0, 0x26A1, prExtendedPictographic}, // E0.6 [2] (⚠️..⚡) warning..high voltage
+ {0x26A2, 0x26A6, prExtendedPictographic}, // E0.0 [5] (⚢..⚦) DOUBLED FEMALE SIGN..MALE WITH STROKE SIGN
+ {0x26A7, 0x26A7, prExtendedPictographic}, // E13.0 [1] (⚧️) transgender symbol
+ {0x26A8, 0x26A9, prExtendedPictographic}, // E0.0 [2] (⚨..⚩) VERTICAL MALE WITH STROKE SIGN..HORIZONTAL MALE WITH STROKE SIGN
+ {0x26AA, 0x26AB, prExtendedPictographic}, // E0.6 [2] (⚪..⚫) white circle..black circle
+ {0x26AC, 0x26AF, prExtendedPictographic}, // E0.0 [4] (⚬..⚯) MEDIUM SMALL WHITE CIRCLE..UNMARRIED PARTNERSHIP SYMBOL
+ {0x26B0, 0x26B1, prExtendedPictographic}, // E1.0 [2] (⚰️..⚱️) coffin..funeral urn
+ {0x26B2, 0x26BC, prExtendedPictographic}, // E0.0 [11] (⚲..⚼) NEUTER..SESQUIQUADRATE
+ {0x26BD, 0x26BE, prExtendedPictographic}, // E0.6 [2] (⚽..⚾) soccer ball..baseball
+ {0x26BF, 0x26C3, prExtendedPictographic}, // E0.0 [5] (⚿..⛃) SQUARED KEY..BLACK DRAUGHTS KING
+ {0x26C4, 0x26C5, prExtendedPictographic}, // E0.6 [2] (⛄..⛅) snowman without snow..sun behind cloud
+ {0x26C6, 0x26C7, prExtendedPictographic}, // E0.0 [2] (⛆..⛇) RAIN..BLACK SNOWMAN
+ {0x26C8, 0x26C8, prExtendedPictographic}, // E0.7 [1] (⛈️) cloud with lightning and rain
+ {0x26C9, 0x26CD, prExtendedPictographic}, // E0.0 [5] (⛉..⛍) TURNED WHITE SHOGI PIECE..DISABLED CAR
+ {0x26CE, 0x26CE, prExtendedPictographic}, // E0.6 [1] (⛎) Ophiuchus
+ {0x26CF, 0x26CF, prExtendedPictographic}, // E0.7 [1] (⛏️) pick
+ {0x26D0, 0x26D0, prExtendedPictographic}, // E0.0 [1] (⛐) CAR SLIDING
+ {0x26D1, 0x26D1, prExtendedPictographic}, // E0.7 [1] (⛑️) rescue worker’s helmet
+ {0x26D2, 0x26D2, prExtendedPictographic}, // E0.0 [1] (⛒) CIRCLED CROSSING LANES
+ {0x26D3, 0x26D3, prExtendedPictographic}, // E0.7 [1] (⛓️) chains
+ {0x26D4, 0x26D4, prExtendedPictographic}, // E0.6 [1] (⛔) no entry
+ {0x26D5, 0x26E8, prExtendedPictographic}, // E0.0 [20] (⛕..⛨) ALTERNATE ONE-WAY LEFT WAY TRAFFIC..BLACK CROSS ON SHIELD
+ {0x26E9, 0x26E9, prExtendedPictographic}, // E0.7 [1] (⛩️) shinto shrine
+ {0x26EA, 0x26EA, prExtendedPictographic}, // E0.6 [1] (⛪) church
+ {0x26EB, 0x26EF, prExtendedPictographic}, // E0.0 [5] (⛫..⛯) CASTLE..MAP SYMBOL FOR LIGHTHOUSE
+ {0x26F0, 0x26F1, prExtendedPictographic}, // E0.7 [2] (⛰️..⛱️) mountain..umbrella on ground
+ {0x26F2, 0x26F3, prExtendedPictographic}, // E0.6 [2] (⛲..⛳) fountain..flag in hole
+ {0x26F4, 0x26F4, prExtendedPictographic}, // E0.7 [1] (⛴️) ferry
+ {0x26F5, 0x26F5, prExtendedPictographic}, // E0.6 [1] (⛵) sailboat
+ {0x26F6, 0x26F6, prExtendedPictographic}, // E0.0 [1] (⛶) SQUARE FOUR CORNERS
+ {0x26F7, 0x26F9, prExtendedPictographic}, // E0.7 [3] (⛷️..⛹️) skier..person bouncing ball
+ {0x26FA, 0x26FA, prExtendedPictographic}, // E0.6 [1] (⛺) tent
+ {0x26FB, 0x26FC, prExtendedPictographic}, // E0.0 [2] (⛻..⛼) JAPANESE BANK SYMBOL..HEADSTONE GRAVEYARD SYMBOL
+ {0x26FD, 0x26FD, prExtendedPictographic}, // E0.6 [1] (⛽) fuel pump
+ {0x26FE, 0x2701, prExtendedPictographic}, // E0.0 [4] (⛾..✁) CUP ON BLACK SQUARE..UPPER BLADE SCISSORS
+ {0x2702, 0x2702, prExtendedPictographic}, // E0.6 [1] (✂️) scissors
+ {0x2703, 0x2704, prExtendedPictographic}, // E0.0 [2] (✃..✄) LOWER BLADE SCISSORS..WHITE SCISSORS
+ {0x2705, 0x2705, prExtendedPictographic}, // E0.6 [1] (✅) check mark button
+ {0x2708, 0x270C, prExtendedPictographic}, // E0.6 [5] (✈️..✌️) airplane..victory hand
+ {0x270D, 0x270D, prExtendedPictographic}, // E0.7 [1] (✍️) writing hand
+ {0x270E, 0x270E, prExtendedPictographic}, // E0.0 [1] (✎) LOWER RIGHT PENCIL
+ {0x270F, 0x270F, prExtendedPictographic}, // E0.6 [1] (✏️) pencil
+ {0x2710, 0x2711, prExtendedPictographic}, // E0.0 [2] (✐..✑) UPPER RIGHT PENCIL..WHITE NIB
+ {0x2712, 0x2712, prExtendedPictographic}, // E0.6 [1] (✒️) black nib
+ {0x2714, 0x2714, prExtendedPictographic}, // E0.6 [1] (✔️) check mark
+ {0x2716, 0x2716, prExtendedPictographic}, // E0.6 [1] (✖️) multiply
+ {0x271D, 0x271D, prExtendedPictographic}, // E0.7 [1] (✝️) latin cross
+ {0x2721, 0x2721, prExtendedPictographic}, // E0.7 [1] (✡️) star of David
+ {0x2728, 0x2728, prExtendedPictographic}, // E0.6 [1] (✨) sparkles
+ {0x2733, 0x2734, prExtendedPictographic}, // E0.6 [2] (✳️..✴️) eight-spoked asterisk..eight-pointed star
+ {0x2744, 0x2744, prExtendedPictographic}, // E0.6 [1] (❄️) snowflake
+ {0x2747, 0x2747, prExtendedPictographic}, // E0.6 [1] (❇️) sparkle
+ {0x274C, 0x274C, prExtendedPictographic}, // E0.6 [1] (❌) cross mark
+ {0x274E, 0x274E, prExtendedPictographic}, // E0.6 [1] (❎) cross mark button
+ {0x2753, 0x2755, prExtendedPictographic}, // E0.6 [3] (❓..❕) red question mark..white exclamation mark
+ {0x2757, 0x2757, prExtendedPictographic}, // E0.6 [1] (❗) red exclamation mark
+ {0x2763, 0x2763, prExtendedPictographic}, // E1.0 [1] (❣️) heart exclamation
+ {0x2764, 0x2764, prExtendedPictographic}, // E0.6 [1] (❤️) red heart
+ {0x2765, 0x2767, prExtendedPictographic}, // E0.0 [3] (❥..❧) ROTATED HEAVY BLACK HEART BULLET..ROTATED FLORAL HEART BULLET
+ {0x2795, 0x2797, prExtendedPictographic}, // E0.6 [3] (➕..➗) plus..divide
+ {0x27A1, 0x27A1, prExtendedPictographic}, // E0.6 [1] (➡️) right arrow
+ {0x27B0, 0x27B0, prExtendedPictographic}, // E0.6 [1] (➰) curly loop
+ {0x27BF, 0x27BF, prExtendedPictographic}, // E1.0 [1] (➿) double curly loop
+ {0x2934, 0x2935, prExtendedPictographic}, // E0.6 [2] (⤴️..⤵️) right arrow curving up..right arrow curving down
+ {0x2B05, 0x2B07, prExtendedPictographic}, // E0.6 [3] (⬅️..⬇️) left arrow..down arrow
+ {0x2B1B, 0x2B1C, prExtendedPictographic}, // E0.6 [2] (⬛..⬜) black large square..white large square
+ {0x2B50, 0x2B50, prExtendedPictographic}, // E0.6 [1] (⭐) star
+ {0x2B55, 0x2B55, prExtendedPictographic}, // E0.6 [1] (⭕) hollow red circle
+ {0x2CEF, 0x2CF1, prExtend}, // Mn [3] COPTIC COMBINING NI ABOVE..COPTIC COMBINING SPIRITUS LENIS
+ {0x2D7F, 0x2D7F, prExtend}, // Mn TIFINAGH CONSONANT JOINER
+ {0x2DE0, 0x2DFF, prExtend}, // Mn [32] COMBINING CYRILLIC LETTER BE..COMBINING CYRILLIC LETTER IOTIFIED BIG YUS
+ {0x302A, 0x302D, prExtend}, // Mn [4] IDEOGRAPHIC LEVEL TONE MARK..IDEOGRAPHIC ENTERING TONE MARK
+ {0x302E, 0x302F, prExtend}, // Mc [2] HANGUL SINGLE DOT TONE MARK..HANGUL DOUBLE DOT TONE MARK
+ {0x3030, 0x3030, prExtendedPictographic}, // E0.6 [1] (〰️) wavy dash
+ {0x303D, 0x303D, prExtendedPictographic}, // E0.6 [1] (〽️) part alternation mark
+ {0x3099, 0x309A, prExtend}, // Mn [2] COMBINING KATAKANA-HIRAGANA VOICED SOUND MARK..COMBINING KATAKANA-HIRAGANA SEMI-VOICED SOUND MARK
+ {0x3297, 0x3297, prExtendedPictographic}, // E0.6 [1] (㊗️) Japanese “congratulations” button
+ {0x3299, 0x3299, prExtendedPictographic}, // E0.6 [1] (㊙️) Japanese “secret” button
+ {0xA66F, 0xA66F, prExtend}, // Mn COMBINING CYRILLIC VZMET
+ {0xA670, 0xA672, prExtend}, // Me [3] COMBINING CYRILLIC TEN MILLIONS SIGN..COMBINING CYRILLIC THOUSAND MILLIONS SIGN
+ {0xA674, 0xA67D, prExtend}, // Mn [10] COMBINING CYRILLIC LETTER UKRAINIAN IE..COMBINING CYRILLIC PAYEROK
+ {0xA69E, 0xA69F, prExtend}, // Mn [2] COMBINING CYRILLIC LETTER EF..COMBINING CYRILLIC LETTER IOTIFIED E
+ {0xA6F0, 0xA6F1, prExtend}, // Mn [2] BAMUM COMBINING MARK KOQNDON..BAMUM COMBINING MARK TUKWENTIS
+ {0xA802, 0xA802, prExtend}, // Mn SYLOTI NAGRI SIGN DVISVARA
+ {0xA806, 0xA806, prExtend}, // Mn SYLOTI NAGRI SIGN HASANTA
+ {0xA80B, 0xA80B, prExtend}, // Mn SYLOTI NAGRI SIGN ANUSVARA
+ {0xA823, 0xA824, prSpacingMark}, // Mc [2] SYLOTI NAGRI VOWEL SIGN A..SYLOTI NAGRI VOWEL SIGN I
+ {0xA825, 0xA826, prExtend}, // Mn [2] SYLOTI NAGRI VOWEL SIGN U..SYLOTI NAGRI VOWEL SIGN E
+ {0xA827, 0xA827, prSpacingMark}, // Mc SYLOTI NAGRI VOWEL SIGN OO
+ {0xA82C, 0xA82C, prExtend}, // Mn SYLOTI NAGRI SIGN ALTERNATE HASANTA
+ {0xA880, 0xA881, prSpacingMark}, // Mc [2] SAURASHTRA SIGN ANUSVARA..SAURASHTRA SIGN VISARGA
+ {0xA8B4, 0xA8C3, prSpacingMark}, // Mc [16] SAURASHTRA CONSONANT SIGN HAARU..SAURASHTRA VOWEL SIGN AU
+ {0xA8C4, 0xA8C5, prExtend}, // Mn [2] SAURASHTRA SIGN VIRAMA..SAURASHTRA SIGN CANDRABINDU
+ {0xA8E0, 0xA8F1, prExtend}, // Mn [18] COMBINING DEVANAGARI DIGIT ZERO..COMBINING DEVANAGARI SIGN AVAGRAHA
+ {0xA8FF, 0xA8FF, prExtend}, // Mn DEVANAGARI VOWEL SIGN AY
+ {0xA926, 0xA92D, prExtend}, // Mn [8] KAYAH LI VOWEL UE..KAYAH LI TONE CALYA PLOPHU
+ {0xA947, 0xA951, prExtend}, // Mn [11] REJANG VOWEL SIGN I..REJANG CONSONANT SIGN R
+ {0xA952, 0xA953, prSpacingMark}, // Mc [2] REJANG CONSONANT SIGN H..REJANG VIRAMA
+ {0xA960, 0xA97C, prL}, // Lo [29] HANGUL CHOSEONG TIKEUT-MIEUM..HANGUL CHOSEONG SSANGYEORINHIEUH
+ {0xA980, 0xA982, prExtend}, // Mn [3] JAVANESE SIGN PANYANGGA..JAVANESE SIGN LAYAR
+ {0xA983, 0xA983, prSpacingMark}, // Mc JAVANESE SIGN WIGNYAN
+ {0xA9B3, 0xA9B3, prExtend}, // Mn JAVANESE SIGN CECAK TELU
+ {0xA9B4, 0xA9B5, prSpacingMark}, // Mc [2] JAVANESE VOWEL SIGN TARUNG..JAVANESE VOWEL SIGN TOLONG
+ {0xA9B6, 0xA9B9, prExtend}, // Mn [4] JAVANESE VOWEL SIGN WULU..JAVANESE VOWEL SIGN SUKU MENDUT
+ {0xA9BA, 0xA9BB, prSpacingMark}, // Mc [2] JAVANESE VOWEL SIGN TALING..JAVANESE VOWEL SIGN DIRGA MURE
+ {0xA9BC, 0xA9BD, prExtend}, // Mn [2] JAVANESE VOWEL SIGN PEPET..JAVANESE CONSONANT SIGN KERET
+ {0xA9BE, 0xA9C0, prSpacingMark}, // Mc [3] JAVANESE CONSONANT SIGN PENGKAL..JAVANESE PANGKON
+ {0xA9E5, 0xA9E5, prExtend}, // Mn MYANMAR SIGN SHAN SAW
+ {0xAA29, 0xAA2E, prExtend}, // Mn [6] CHAM VOWEL SIGN AA..CHAM VOWEL SIGN OE
+ {0xAA2F, 0xAA30, prSpacingMark}, // Mc [2] CHAM VOWEL SIGN O..CHAM VOWEL SIGN AI
+ {0xAA31, 0xAA32, prExtend}, // Mn [2] CHAM VOWEL SIGN AU..CHAM VOWEL SIGN UE
+ {0xAA33, 0xAA34, prSpacingMark}, // Mc [2] CHAM CONSONANT SIGN YA..CHAM CONSONANT SIGN RA
+ {0xAA35, 0xAA36, prExtend}, // Mn [2] CHAM CONSONANT SIGN LA..CHAM CONSONANT SIGN WA
+ {0xAA43, 0xAA43, prExtend}, // Mn CHAM CONSONANT SIGN FINAL NG
+ {0xAA4C, 0xAA4C, prExtend}, // Mn CHAM CONSONANT SIGN FINAL M
+ {0xAA4D, 0xAA4D, prSpacingMark}, // Mc CHAM CONSONANT SIGN FINAL H
+ {0xAA7C, 0xAA7C, prExtend}, // Mn MYANMAR SIGN TAI LAING TONE-2
+ {0xAAB0, 0xAAB0, prExtend}, // Mn TAI VIET MAI KANG
+ {0xAAB2, 0xAAB4, prExtend}, // Mn [3] TAI VIET VOWEL I..TAI VIET VOWEL U
+ {0xAAB7, 0xAAB8, prExtend}, // Mn [2] TAI VIET MAI KHIT..TAI VIET VOWEL IA
+ {0xAABE, 0xAABF, prExtend}, // Mn [2] TAI VIET VOWEL AM..TAI VIET TONE MAI EK
+ {0xAAC1, 0xAAC1, prExtend}, // Mn TAI VIET TONE MAI THO
+ {0xAAEB, 0xAAEB, prSpacingMark}, // Mc MEETEI MAYEK VOWEL SIGN II
+ {0xAAEC, 0xAAED, prExtend}, // Mn [2] MEETEI MAYEK VOWEL SIGN UU..MEETEI MAYEK VOWEL SIGN AAI
+ {0xAAEE, 0xAAEF, prSpacingMark}, // Mc [2] MEETEI MAYEK VOWEL SIGN AU..MEETEI MAYEK VOWEL SIGN AAU
+ {0xAAF5, 0xAAF5, prSpacingMark}, // Mc MEETEI MAYEK VOWEL SIGN VISARGA
+ {0xAAF6, 0xAAF6, prExtend}, // Mn MEETEI MAYEK VIRAMA
+ {0xABE3, 0xABE4, prSpacingMark}, // Mc [2] MEETEI MAYEK VOWEL SIGN ONAP..MEETEI MAYEK VOWEL SIGN INAP
+ {0xABE5, 0xABE5, prExtend}, // Mn MEETEI MAYEK VOWEL SIGN ANAP
+ {0xABE6, 0xABE7, prSpacingMark}, // Mc [2] MEETEI MAYEK VOWEL SIGN YENAP..MEETEI MAYEK VOWEL SIGN SOUNAP
+ {0xABE8, 0xABE8, prExtend}, // Mn MEETEI MAYEK VOWEL SIGN UNAP
+ {0xABE9, 0xABEA, prSpacingMark}, // Mc [2] MEETEI MAYEK VOWEL SIGN CHEINAP..MEETEI MAYEK VOWEL SIGN NUNG
+ {0xABEC, 0xABEC, prSpacingMark}, // Mc MEETEI MAYEK LUM IYEK
+ {0xABED, 0xABED, prExtend}, // Mn MEETEI MAYEK APUN IYEK
+ {0xAC00, 0xAC00, prLV}, // Lo HANGUL SYLLABLE GA
+ {0xAC01, 0xAC1B, prLVT}, // Lo [27] HANGUL SYLLABLE GAG..HANGUL SYLLABLE GAH
+ {0xAC1C, 0xAC1C, prLV}, // Lo HANGUL SYLLABLE GAE
+ {0xAC1D, 0xAC37, prLVT}, // Lo [27] HANGUL SYLLABLE GAEG..HANGUL SYLLABLE GAEH
+ {0xAC38, 0xAC38, prLV}, // Lo HANGUL SYLLABLE GYA
+ {0xAC39, 0xAC53, prLVT}, // Lo [27] HANGUL SYLLABLE GYAG..HANGUL SYLLABLE GYAH
+ {0xAC54, 0xAC54, prLV}, // Lo HANGUL SYLLABLE GYAE
+ {0xAC55, 0xAC6F, prLVT}, // Lo [27] HANGUL SYLLABLE GYAEG..HANGUL SYLLABLE GYAEH
+ {0xAC70, 0xAC70, prLV}, // Lo HANGUL SYLLABLE GEO
+ {0xAC71, 0xAC8B, prLVT}, // Lo [27] HANGUL SYLLABLE GEOG..HANGUL SYLLABLE GEOH
+ {0xAC8C, 0xAC8C, prLV}, // Lo HANGUL SYLLABLE GE
+ {0xAC8D, 0xACA7, prLVT}, // Lo [27] HANGUL SYLLABLE GEG..HANGUL SYLLABLE GEH
+ {0xACA8, 0xACA8, prLV}, // Lo HANGUL SYLLABLE GYEO
+ {0xACA9, 0xACC3, prLVT}, // Lo [27] HANGUL SYLLABLE GYEOG..HANGUL SYLLABLE GYEOH
+ {0xACC4, 0xACC4, prLV}, // Lo HANGUL SYLLABLE GYE
+ {0xACC5, 0xACDF, prLVT}, // Lo [27] HANGUL SYLLABLE GYEG..HANGUL SYLLABLE GYEH
+ {0xACE0, 0xACE0, prLV}, // Lo HANGUL SYLLABLE GO
+ {0xACE1, 0xACFB, prLVT}, // Lo [27] HANGUL SYLLABLE GOG..HANGUL SYLLABLE GOH
+ {0xACFC, 0xACFC, prLV}, // Lo HANGUL SYLLABLE GWA
+ {0xACFD, 0xAD17, prLVT}, // Lo [27] HANGUL SYLLABLE GWAG..HANGUL SYLLABLE GWAH
+ {0xAD18, 0xAD18, prLV}, // Lo HANGUL SYLLABLE GWAE
+ {0xAD19, 0xAD33, prLVT}, // Lo [27] HANGUL SYLLABLE GWAEG..HANGUL SYLLABLE GWAEH
+ {0xAD34, 0xAD34, prLV}, // Lo HANGUL SYLLABLE GOE
+ {0xAD35, 0xAD4F, prLVT}, // Lo [27] HANGUL SYLLABLE GOEG..HANGUL SYLLABLE GOEH
+ {0xAD50, 0xAD50, prLV}, // Lo HANGUL SYLLABLE GYO
+ {0xAD51, 0xAD6B, prLVT}, // Lo [27] HANGUL SYLLABLE GYOG..HANGUL SYLLABLE GYOH
+ {0xAD6C, 0xAD6C, prLV}, // Lo HANGUL SYLLABLE GU
+ {0xAD6D, 0xAD87, prLVT}, // Lo [27] HANGUL SYLLABLE GUG..HANGUL SYLLABLE GUH
+ {0xAD88, 0xAD88, prLV}, // Lo HANGUL SYLLABLE GWEO
+ {0xAD89, 0xADA3, prLVT}, // Lo [27] HANGUL SYLLABLE GWEOG..HANGUL SYLLABLE GWEOH
+ {0xADA4, 0xADA4, prLV}, // Lo HANGUL SYLLABLE GWE
+ {0xADA5, 0xADBF, prLVT}, // Lo [27] HANGUL SYLLABLE GWEG..HANGUL SYLLABLE GWEH
+ {0xADC0, 0xADC0, prLV}, // Lo HANGUL SYLLABLE GWI
+ {0xADC1, 0xADDB, prLVT}, // Lo [27] HANGUL SYLLABLE GWIG..HANGUL SYLLABLE GWIH
+ {0xADDC, 0xADDC, prLV}, // Lo HANGUL SYLLABLE GYU
+ {0xADDD, 0xADF7, prLVT}, // Lo [27] HANGUL SYLLABLE GYUG..HANGUL SYLLABLE GYUH
+ {0xADF8, 0xADF8, prLV}, // Lo HANGUL SYLLABLE GEU
+ {0xADF9, 0xAE13, prLVT}, // Lo [27] HANGUL SYLLABLE GEUG..HANGUL SYLLABLE GEUH
+ {0xAE14, 0xAE14, prLV}, // Lo HANGUL SYLLABLE GYI
+ {0xAE15, 0xAE2F, prLVT}, // Lo [27] HANGUL SYLLABLE GYIG..HANGUL SYLLABLE GYIH
+ {0xAE30, 0xAE30, prLV}, // Lo HANGUL SYLLABLE GI
+ {0xAE31, 0xAE4B, prLVT}, // Lo [27] HANGUL SYLLABLE GIG..HANGUL SYLLABLE GIH
+ {0xAE4C, 0xAE4C, prLV}, // Lo HANGUL SYLLABLE GGA
+ {0xAE4D, 0xAE67, prLVT}, // Lo [27] HANGUL SYLLABLE GGAG..HANGUL SYLLABLE GGAH
+ {0xAE68, 0xAE68, prLV}, // Lo HANGUL SYLLABLE GGAE
+ {0xAE69, 0xAE83, prLVT}, // Lo [27] HANGUL SYLLABLE GGAEG..HANGUL SYLLABLE GGAEH
+ {0xAE84, 0xAE84, prLV}, // Lo HANGUL SYLLABLE GGYA
+ {0xAE85, 0xAE9F, prLVT}, // Lo [27] HANGUL SYLLABLE GGYAG..HANGUL SYLLABLE GGYAH
+ {0xAEA0, 0xAEA0, prLV}, // Lo HANGUL SYLLABLE GGYAE
+ {0xAEA1, 0xAEBB, prLVT}, // Lo [27] HANGUL SYLLABLE GGYAEG..HANGUL SYLLABLE GGYAEH
+ {0xAEBC, 0xAEBC, prLV}, // Lo HANGUL SYLLABLE GGEO
+ {0xAEBD, 0xAED7, prLVT}, // Lo [27] HANGUL SYLLABLE GGEOG..HANGUL SYLLABLE GGEOH
+ {0xAED8, 0xAED8, prLV}, // Lo HANGUL SYLLABLE GGE
+ {0xAED9, 0xAEF3, prLVT}, // Lo [27] HANGUL SYLLABLE GGEG..HANGUL SYLLABLE GGEH
+ {0xAEF4, 0xAEF4, prLV}, // Lo HANGUL SYLLABLE GGYEO
+ {0xAEF5, 0xAF0F, prLVT}, // Lo [27] HANGUL SYLLABLE GGYEOG..HANGUL SYLLABLE GGYEOH
+ {0xAF10, 0xAF10, prLV}, // Lo HANGUL SYLLABLE GGYE
+ {0xAF11, 0xAF2B, prLVT}, // Lo [27] HANGUL SYLLABLE GGYEG..HANGUL SYLLABLE GGYEH
+ {0xAF2C, 0xAF2C, prLV}, // Lo HANGUL SYLLABLE GGO
+ {0xAF2D, 0xAF47, prLVT}, // Lo [27] HANGUL SYLLABLE GGOG..HANGUL SYLLABLE GGOH
+ {0xAF48, 0xAF48, prLV}, // Lo HANGUL SYLLABLE GGWA
+ {0xAF49, 0xAF63, prLVT}, // Lo [27] HANGUL SYLLABLE GGWAG..HANGUL SYLLABLE GGWAH
+ {0xAF64, 0xAF64, prLV}, // Lo HANGUL SYLLABLE GGWAE
+ {0xAF65, 0xAF7F, prLVT}, // Lo [27] HANGUL SYLLABLE GGWAEG..HANGUL SYLLABLE GGWAEH
+ {0xAF80, 0xAF80, prLV}, // Lo HANGUL SYLLABLE GGOE
+ {0xAF81, 0xAF9B, prLVT}, // Lo [27] HANGUL SYLLABLE GGOEG..HANGUL SYLLABLE GGOEH
+ {0xAF9C, 0xAF9C, prLV}, // Lo HANGUL SYLLABLE GGYO
+ {0xAF9D, 0xAFB7, prLVT}, // Lo [27] HANGUL SYLLABLE GGYOG..HANGUL SYLLABLE GGYOH
+ {0xAFB8, 0xAFB8, prLV}, // Lo HANGUL SYLLABLE GGU
+ {0xAFB9, 0xAFD3, prLVT}, // Lo [27] HANGUL SYLLABLE GGUG..HANGUL SYLLABLE GGUH
+ {0xAFD4, 0xAFD4, prLV}, // Lo HANGUL SYLLABLE GGWEO
+ {0xAFD5, 0xAFEF, prLVT}, // Lo [27] HANGUL SYLLABLE GGWEOG..HANGUL SYLLABLE GGWEOH
+ {0xAFF0, 0xAFF0, prLV}, // Lo HANGUL SYLLABLE GGWE
+ {0xAFF1, 0xB00B, prLVT}, // Lo [27] HANGUL SYLLABLE GGWEG..HANGUL SYLLABLE GGWEH
+ {0xB00C, 0xB00C, prLV}, // Lo HANGUL SYLLABLE GGWI
+ {0xB00D, 0xB027, prLVT}, // Lo [27] HANGUL SYLLABLE GGWIG..HANGUL SYLLABLE GGWIH
+ {0xB028, 0xB028, prLV}, // Lo HANGUL SYLLABLE GGYU
+ {0xB029, 0xB043, prLVT}, // Lo [27] HANGUL SYLLABLE GGYUG..HANGUL SYLLABLE GGYUH
+ {0xB044, 0xB044, prLV}, // Lo HANGUL SYLLABLE GGEU
+ {0xB045, 0xB05F, prLVT}, // Lo [27] HANGUL SYLLABLE GGEUG..HANGUL SYLLABLE GGEUH
+ {0xB060, 0xB060, prLV}, // Lo HANGUL SYLLABLE GGYI
+ {0xB061, 0xB07B, prLVT}, // Lo [27] HANGUL SYLLABLE GGYIG..HANGUL SYLLABLE GGYIH
+ {0xB07C, 0xB07C, prLV}, // Lo HANGUL SYLLABLE GGI
+ {0xB07D, 0xB097, prLVT}, // Lo [27] HANGUL SYLLABLE GGIG..HANGUL SYLLABLE GGIH
+ {0xB098, 0xB098, prLV}, // Lo HANGUL SYLLABLE NA
+ {0xB099, 0xB0B3, prLVT}, // Lo [27] HANGUL SYLLABLE NAG..HANGUL SYLLABLE NAH
+ {0xB0B4, 0xB0B4, prLV}, // Lo HANGUL SYLLABLE NAE
+ {0xB0B5, 0xB0CF, prLVT}, // Lo [27] HANGUL SYLLABLE NAEG..HANGUL SYLLABLE NAEH
+ {0xB0D0, 0xB0D0, prLV}, // Lo HANGUL SYLLABLE NYA
+ {0xB0D1, 0xB0EB, prLVT}, // Lo [27] HANGUL SYLLABLE NYAG..HANGUL SYLLABLE NYAH
+ {0xB0EC, 0xB0EC, prLV}, // Lo HANGUL SYLLABLE NYAE
+ {0xB0ED, 0xB107, prLVT}, // Lo [27] HANGUL SYLLABLE NYAEG..HANGUL SYLLABLE NYAEH
+ {0xB108, 0xB108, prLV}, // Lo HANGUL SYLLABLE NEO
+ {0xB109, 0xB123, prLVT}, // Lo [27] HANGUL SYLLABLE NEOG..HANGUL SYLLABLE NEOH
+ {0xB124, 0xB124, prLV}, // Lo HANGUL SYLLABLE NE
+ {0xB125, 0xB13F, prLVT}, // Lo [27] HANGUL SYLLABLE NEG..HANGUL SYLLABLE NEH
+ {0xB140, 0xB140, prLV}, // Lo HANGUL SYLLABLE NYEO
+ {0xB141, 0xB15B, prLVT}, // Lo [27] HANGUL SYLLABLE NYEOG..HANGUL SYLLABLE NYEOH
+ {0xB15C, 0xB15C, prLV}, // Lo HANGUL SYLLABLE NYE
+ {0xB15D, 0xB177, prLVT}, // Lo [27] HANGUL SYLLABLE NYEG..HANGUL SYLLABLE NYEH
+ {0xB178, 0xB178, prLV}, // Lo HANGUL SYLLABLE NO
+ {0xB179, 0xB193, prLVT}, // Lo [27] HANGUL SYLLABLE NOG..HANGUL SYLLABLE NOH
+ {0xB194, 0xB194, prLV}, // Lo HANGUL SYLLABLE NWA
+ {0xB195, 0xB1AF, prLVT}, // Lo [27] HANGUL SYLLABLE NWAG..HANGUL SYLLABLE NWAH
+ {0xB1B0, 0xB1B0, prLV}, // Lo HANGUL SYLLABLE NWAE
+ {0xB1B1, 0xB1CB, prLVT}, // Lo [27] HANGUL SYLLABLE NWAEG..HANGUL SYLLABLE NWAEH
+ {0xB1CC, 0xB1CC, prLV}, // Lo HANGUL SYLLABLE NOE
+ {0xB1CD, 0xB1E7, prLVT}, // Lo [27] HANGUL SYLLABLE NOEG..HANGUL SYLLABLE NOEH
+ {0xB1E8, 0xB1E8, prLV}, // Lo HANGUL SYLLABLE NYO
+ {0xB1E9, 0xB203, prLVT}, // Lo [27] HANGUL SYLLABLE NYOG..HANGUL SYLLABLE NYOH
+ {0xB204, 0xB204, prLV}, // Lo HANGUL SYLLABLE NU
+ {0xB205, 0xB21F, prLVT}, // Lo [27] HANGUL SYLLABLE NUG..HANGUL SYLLABLE NUH
+ {0xB220, 0xB220, prLV}, // Lo HANGUL SYLLABLE NWEO
+ {0xB221, 0xB23B, prLVT}, // Lo [27] HANGUL SYLLABLE NWEOG..HANGUL SYLLABLE NWEOH
+ {0xB23C, 0xB23C, prLV}, // Lo HANGUL SYLLABLE NWE
+ {0xB23D, 0xB257, prLVT}, // Lo [27] HANGUL SYLLABLE NWEG..HANGUL SYLLABLE NWEH
+ {0xB258, 0xB258, prLV}, // Lo HANGUL SYLLABLE NWI
+ {0xB259, 0xB273, prLVT}, // Lo [27] HANGUL SYLLABLE NWIG..HANGUL SYLLABLE NWIH
+ {0xB274, 0xB274, prLV}, // Lo HANGUL SYLLABLE NYU
+ {0xB275, 0xB28F, prLVT}, // Lo [27] HANGUL SYLLABLE NYUG..HANGUL SYLLABLE NYUH
+ {0xB290, 0xB290, prLV}, // Lo HANGUL SYLLABLE NEU
+ {0xB291, 0xB2AB, prLVT}, // Lo [27] HANGUL SYLLABLE NEUG..HANGUL SYLLABLE NEUH
+ {0xB2AC, 0xB2AC, prLV}, // Lo HANGUL SYLLABLE NYI
+ {0xB2AD, 0xB2C7, prLVT}, // Lo [27] HANGUL SYLLABLE NYIG..HANGUL SYLLABLE NYIH
+ {0xB2C8, 0xB2C8, prLV}, // Lo HANGUL SYLLABLE NI
+ {0xB2C9, 0xB2E3, prLVT}, // Lo [27] HANGUL SYLLABLE NIG..HANGUL SYLLABLE NIH
+ {0xB2E4, 0xB2E4, prLV}, // Lo HANGUL SYLLABLE DA
+ {0xB2E5, 0xB2FF, prLVT}, // Lo [27] HANGUL SYLLABLE DAG..HANGUL SYLLABLE DAH
+ {0xB300, 0xB300, prLV}, // Lo HANGUL SYLLABLE DAE
+ {0xB301, 0xB31B, prLVT}, // Lo [27] HANGUL SYLLABLE DAEG..HANGUL SYLLABLE DAEH
+ {0xB31C, 0xB31C, prLV}, // Lo HANGUL SYLLABLE DYA
+ {0xB31D, 0xB337, prLVT}, // Lo [27] HANGUL SYLLABLE DYAG..HANGUL SYLLABLE DYAH
+ {0xB338, 0xB338, prLV}, // Lo HANGUL SYLLABLE DYAE
+ {0xB339, 0xB353, prLVT}, // Lo [27] HANGUL SYLLABLE DYAEG..HANGUL SYLLABLE DYAEH
+ {0xB354, 0xB354, prLV}, // Lo HANGUL SYLLABLE DEO
+ {0xB355, 0xB36F, prLVT}, // Lo [27] HANGUL SYLLABLE DEOG..HANGUL SYLLABLE DEOH
+ {0xB370, 0xB370, prLV}, // Lo HANGUL SYLLABLE DE
+ {0xB371, 0xB38B, prLVT}, // Lo [27] HANGUL SYLLABLE DEG..HANGUL SYLLABLE DEH
+ {0xB38C, 0xB38C, prLV}, // Lo HANGUL SYLLABLE DYEO
+ {0xB38D, 0xB3A7, prLVT}, // Lo [27] HANGUL SYLLABLE DYEOG..HANGUL SYLLABLE DYEOH
+ {0xB3A8, 0xB3A8, prLV}, // Lo HANGUL SYLLABLE DYE
+ {0xB3A9, 0xB3C3, prLVT}, // Lo [27] HANGUL SYLLABLE DYEG..HANGUL SYLLABLE DYEH
+ {0xB3C4, 0xB3C4, prLV}, // Lo HANGUL SYLLABLE DO
+ {0xB3C5, 0xB3DF, prLVT}, // Lo [27] HANGUL SYLLABLE DOG..HANGUL SYLLABLE DOH
+ {0xB3E0, 0xB3E0, prLV}, // Lo HANGUL SYLLABLE DWA
+ {0xB3E1, 0xB3FB, prLVT}, // Lo [27] HANGUL SYLLABLE DWAG..HANGUL SYLLABLE DWAH
+ {0xB3FC, 0xB3FC, prLV}, // Lo HANGUL SYLLABLE DWAE
+ {0xB3FD, 0xB417, prLVT}, // Lo [27] HANGUL SYLLABLE DWAEG..HANGUL SYLLABLE DWAEH
+ {0xB418, 0xB418, prLV}, // Lo HANGUL SYLLABLE DOE
+ {0xB419, 0xB433, prLVT}, // Lo [27] HANGUL SYLLABLE DOEG..HANGUL SYLLABLE DOEH
+ {0xB434, 0xB434, prLV}, // Lo HANGUL SYLLABLE DYO
+ {0xB435, 0xB44F, prLVT}, // Lo [27] HANGUL SYLLABLE DYOG..HANGUL SYLLABLE DYOH
+ {0xB450, 0xB450, prLV}, // Lo HANGUL SYLLABLE DU
+ {0xB451, 0xB46B, prLVT}, // Lo [27] HANGUL SYLLABLE DUG..HANGUL SYLLABLE DUH
+ {0xB46C, 0xB46C, prLV}, // Lo HANGUL SYLLABLE DWEO
+ {0xB46D, 0xB487, prLVT}, // Lo [27] HANGUL SYLLABLE DWEOG..HANGUL SYLLABLE DWEOH
+ {0xB488, 0xB488, prLV}, // Lo HANGUL SYLLABLE DWE
+ {0xB489, 0xB4A3, prLVT}, // Lo [27] HANGUL SYLLABLE DWEG..HANGUL SYLLABLE DWEH
+ {0xB4A4, 0xB4A4, prLV}, // Lo HANGUL SYLLABLE DWI
+ {0xB4A5, 0xB4BF, prLVT}, // Lo [27] HANGUL SYLLABLE DWIG..HANGUL SYLLABLE DWIH
+ {0xB4C0, 0xB4C0, prLV}, // Lo HANGUL SYLLABLE DYU
+ {0xB4C1, 0xB4DB, prLVT}, // Lo [27] HANGUL SYLLABLE DYUG..HANGUL SYLLABLE DYUH
+ {0xB4DC, 0xB4DC, prLV}, // Lo HANGUL SYLLABLE DEU
+ {0xB4DD, 0xB4F7, prLVT}, // Lo [27] HANGUL SYLLABLE DEUG..HANGUL SYLLABLE DEUH
+ {0xB4F8, 0xB4F8, prLV}, // Lo HANGUL SYLLABLE DYI
+ {0xB4F9, 0xB513, prLVT}, // Lo [27] HANGUL SYLLABLE DYIG..HANGUL SYLLABLE DYIH
+ {0xB514, 0xB514, prLV}, // Lo HANGUL SYLLABLE DI
+ {0xB515, 0xB52F, prLVT}, // Lo [27] HANGUL SYLLABLE DIG..HANGUL SYLLABLE DIH
+ {0xB530, 0xB530, prLV}, // Lo HANGUL SYLLABLE DDA
+ {0xB531, 0xB54B, prLVT}, // Lo [27] HANGUL SYLLABLE DDAG..HANGUL SYLLABLE DDAH
+ {0xB54C, 0xB54C, prLV}, // Lo HANGUL SYLLABLE DDAE
+ {0xB54D, 0xB567, prLVT}, // Lo [27] HANGUL SYLLABLE DDAEG..HANGUL SYLLABLE DDAEH
+ {0xB568, 0xB568, prLV}, // Lo HANGUL SYLLABLE DDYA
+ {0xB569, 0xB583, prLVT}, // Lo [27] HANGUL SYLLABLE DDYAG..HANGUL SYLLABLE DDYAH
+ {0xB584, 0xB584, prLV}, // Lo HANGUL SYLLABLE DDYAE
+ {0xB585, 0xB59F, prLVT}, // Lo [27] HANGUL SYLLABLE DDYAEG..HANGUL SYLLABLE DDYAEH
+ {0xB5A0, 0xB5A0, prLV}, // Lo HANGUL SYLLABLE DDEO
+ {0xB5A1, 0xB5BB, prLVT}, // Lo [27] HANGUL SYLLABLE DDEOG..HANGUL SYLLABLE DDEOH
+ {0xB5BC, 0xB5BC, prLV}, // Lo HANGUL SYLLABLE DDE
+ {0xB5BD, 0xB5D7, prLVT}, // Lo [27] HANGUL SYLLABLE DDEG..HANGUL SYLLABLE DDEH
+ {0xB5D8, 0xB5D8, prLV}, // Lo HANGUL SYLLABLE DDYEO
+ {0xB5D9, 0xB5F3, prLVT}, // Lo [27] HANGUL SYLLABLE DDYEOG..HANGUL SYLLABLE DDYEOH
+ {0xB5F4, 0xB5F4, prLV}, // Lo HANGUL SYLLABLE DDYE
+ {0xB5F5, 0xB60F, prLVT}, // Lo [27] HANGUL SYLLABLE DDYEG..HANGUL SYLLABLE DDYEH
+ {0xB610, 0xB610, prLV}, // Lo HANGUL SYLLABLE DDO
+ {0xB611, 0xB62B, prLVT}, // Lo [27] HANGUL SYLLABLE DDOG..HANGUL SYLLABLE DDOH
+ {0xB62C, 0xB62C, prLV}, // Lo HANGUL SYLLABLE DDWA
+ {0xB62D, 0xB647, prLVT}, // Lo [27] HANGUL SYLLABLE DDWAG..HANGUL SYLLABLE DDWAH
+ {0xB648, 0xB648, prLV}, // Lo HANGUL SYLLABLE DDWAE
+ {0xB649, 0xB663, prLVT}, // Lo [27] HANGUL SYLLABLE DDWAEG..HANGUL SYLLABLE DDWAEH
+ {0xB664, 0xB664, prLV}, // Lo HANGUL SYLLABLE DDOE
+ {0xB665, 0xB67F, prLVT}, // Lo [27] HANGUL SYLLABLE DDOEG..HANGUL SYLLABLE DDOEH
+ {0xB680, 0xB680, prLV}, // Lo HANGUL SYLLABLE DDYO
+ {0xB681, 0xB69B, prLVT}, // Lo [27] HANGUL SYLLABLE DDYOG..HANGUL SYLLABLE DDYOH
+ {0xB69C, 0xB69C, prLV}, // Lo HANGUL SYLLABLE DDU
+ {0xB69D, 0xB6B7, prLVT}, // Lo [27] HANGUL SYLLABLE DDUG..HANGUL SYLLABLE DDUH
+ {0xB6B8, 0xB6B8, prLV}, // Lo HANGUL SYLLABLE DDWEO
+ {0xB6B9, 0xB6D3, prLVT}, // Lo [27] HANGUL SYLLABLE DDWEOG..HANGUL SYLLABLE DDWEOH
+ {0xB6D4, 0xB6D4, prLV}, // Lo HANGUL SYLLABLE DDWE
+ {0xB6D5, 0xB6EF, prLVT}, // Lo [27] HANGUL SYLLABLE DDWEG..HANGUL SYLLABLE DDWEH
+ {0xB6F0, 0xB6F0, prLV}, // Lo HANGUL SYLLABLE DDWI
+ {0xB6F1, 0xB70B, prLVT}, // Lo [27] HANGUL SYLLABLE DDWIG..HANGUL SYLLABLE DDWIH
+ {0xB70C, 0xB70C, prLV}, // Lo HANGUL SYLLABLE DDYU
+ {0xB70D, 0xB727, prLVT}, // Lo [27] HANGUL SYLLABLE DDYUG..HANGUL SYLLABLE DDYUH
+ {0xB728, 0xB728, prLV}, // Lo HANGUL SYLLABLE DDEU
+ {0xB729, 0xB743, prLVT}, // Lo [27] HANGUL SYLLABLE DDEUG..HANGUL SYLLABLE DDEUH
+ {0xB744, 0xB744, prLV}, // Lo HANGUL SYLLABLE DDYI
+ {0xB745, 0xB75F, prLVT}, // Lo [27] HANGUL SYLLABLE DDYIG..HANGUL SYLLABLE DDYIH
+ {0xB760, 0xB760, prLV}, // Lo HANGUL SYLLABLE DDI
+ {0xB761, 0xB77B, prLVT}, // Lo [27] HANGUL SYLLABLE DDIG..HANGUL SYLLABLE DDIH
+ {0xB77C, 0xB77C, prLV}, // Lo HANGUL SYLLABLE RA
+ {0xB77D, 0xB797, prLVT}, // Lo [27] HANGUL SYLLABLE RAG..HANGUL SYLLABLE RAH
+ {0xB798, 0xB798, prLV}, // Lo HANGUL SYLLABLE RAE
+ {0xB799, 0xB7B3, prLVT}, // Lo [27] HANGUL SYLLABLE RAEG..HANGUL SYLLABLE RAEH
+ {0xB7B4, 0xB7B4, prLV}, // Lo HANGUL SYLLABLE RYA
+ {0xB7B5, 0xB7CF, prLVT}, // Lo [27] HANGUL SYLLABLE RYAG..HANGUL SYLLABLE RYAH
+ {0xB7D0, 0xB7D0, prLV}, // Lo HANGUL SYLLABLE RYAE
+ {0xB7D1, 0xB7EB, prLVT}, // Lo [27] HANGUL SYLLABLE RYAEG..HANGUL SYLLABLE RYAEH
+ {0xB7EC, 0xB7EC, prLV}, // Lo HANGUL SYLLABLE REO
+ {0xB7ED, 0xB807, prLVT}, // Lo [27] HANGUL SYLLABLE REOG..HANGUL SYLLABLE REOH
+ {0xB808, 0xB808, prLV}, // Lo HANGUL SYLLABLE RE
+ {0xB809, 0xB823, prLVT}, // Lo [27] HANGUL SYLLABLE REG..HANGUL SYLLABLE REH
+ {0xB824, 0xB824, prLV}, // Lo HANGUL SYLLABLE RYEO
+ {0xB825, 0xB83F, prLVT}, // Lo [27] HANGUL SYLLABLE RYEOG..HANGUL SYLLABLE RYEOH
+ {0xB840, 0xB840, prLV}, // Lo HANGUL SYLLABLE RYE
+ {0xB841, 0xB85B, prLVT}, // Lo [27] HANGUL SYLLABLE RYEG..HANGUL SYLLABLE RYEH
+ {0xB85C, 0xB85C, prLV}, // Lo HANGUL SYLLABLE RO
+ {0xB85D, 0xB877, prLVT}, // Lo [27] HANGUL SYLLABLE ROG..HANGUL SYLLABLE ROH
+ {0xB878, 0xB878, prLV}, // Lo HANGUL SYLLABLE RWA
+ {0xB879, 0xB893, prLVT}, // Lo [27] HANGUL SYLLABLE RWAG..HANGUL SYLLABLE RWAH
+ {0xB894, 0xB894, prLV}, // Lo HANGUL SYLLABLE RWAE
+ {0xB895, 0xB8AF, prLVT}, // Lo [27] HANGUL SYLLABLE RWAEG..HANGUL SYLLABLE RWAEH
+ {0xB8B0, 0xB8B0, prLV}, // Lo HANGUL SYLLABLE ROE
+ {0xB8B1, 0xB8CB, prLVT}, // Lo [27] HANGUL SYLLABLE ROEG..HANGUL SYLLABLE ROEH
+ {0xB8CC, 0xB8CC, prLV}, // Lo HANGUL SYLLABLE RYO
+ {0xB8CD, 0xB8E7, prLVT}, // Lo [27] HANGUL SYLLABLE RYOG..HANGUL SYLLABLE RYOH
+ {0xB8E8, 0xB8E8, prLV}, // Lo HANGUL SYLLABLE RU
+ {0xB8E9, 0xB903, prLVT}, // Lo [27] HANGUL SYLLABLE RUG..HANGUL SYLLABLE RUH
+ {0xB904, 0xB904, prLV}, // Lo HANGUL SYLLABLE RWEO
+ {0xB905, 0xB91F, prLVT}, // Lo [27] HANGUL SYLLABLE RWEOG..HANGUL SYLLABLE RWEOH
+ {0xB920, 0xB920, prLV}, // Lo HANGUL SYLLABLE RWE
+ {0xB921, 0xB93B, prLVT}, // Lo [27] HANGUL SYLLABLE RWEG..HANGUL SYLLABLE RWEH
+ {0xB93C, 0xB93C, prLV}, // Lo HANGUL SYLLABLE RWI
+ {0xB93D, 0xB957, prLVT}, // Lo [27] HANGUL SYLLABLE RWIG..HANGUL SYLLABLE RWIH
+ {0xB958, 0xB958, prLV}, // Lo HANGUL SYLLABLE RYU
+ {0xB959, 0xB973, prLVT}, // Lo [27] HANGUL SYLLABLE RYUG..HANGUL SYLLABLE RYUH
+ {0xB974, 0xB974, prLV}, // Lo HANGUL SYLLABLE REU
+ {0xB975, 0xB98F, prLVT}, // Lo [27] HANGUL SYLLABLE REUG..HANGUL SYLLABLE REUH
+ {0xB990, 0xB990, prLV}, // Lo HANGUL SYLLABLE RYI
+ {0xB991, 0xB9AB, prLVT}, // Lo [27] HANGUL SYLLABLE RYIG..HANGUL SYLLABLE RYIH
+ {0xB9AC, 0xB9AC, prLV}, // Lo HANGUL SYLLABLE RI
+ {0xB9AD, 0xB9C7, prLVT}, // Lo [27] HANGUL SYLLABLE RIG..HANGUL SYLLABLE RIH
+ {0xB9C8, 0xB9C8, prLV}, // Lo HANGUL SYLLABLE MA
+ {0xB9C9, 0xB9E3, prLVT}, // Lo [27] HANGUL SYLLABLE MAG..HANGUL SYLLABLE MAH
+ {0xB9E4, 0xB9E4, prLV}, // Lo HANGUL SYLLABLE MAE
+ {0xB9E5, 0xB9FF, prLVT}, // Lo [27] HANGUL SYLLABLE MAEG..HANGUL SYLLABLE MAEH
+ {0xBA00, 0xBA00, prLV}, // Lo HANGUL SYLLABLE MYA
+ {0xBA01, 0xBA1B, prLVT}, // Lo [27] HANGUL SYLLABLE MYAG..HANGUL SYLLABLE MYAH
+ {0xBA1C, 0xBA1C, prLV}, // Lo HANGUL SYLLABLE MYAE
+ {0xBA1D, 0xBA37, prLVT}, // Lo [27] HANGUL SYLLABLE MYAEG..HANGUL SYLLABLE MYAEH
+ {0xBA38, 0xBA38, prLV}, // Lo HANGUL SYLLABLE MEO
+ {0xBA39, 0xBA53, prLVT}, // Lo [27] HANGUL SYLLABLE MEOG..HANGUL SYLLABLE MEOH
+ {0xBA54, 0xBA54, prLV}, // Lo HANGUL SYLLABLE ME
+ {0xBA55, 0xBA6F, prLVT}, // Lo [27] HANGUL SYLLABLE MEG..HANGUL SYLLABLE MEH
+ {0xBA70, 0xBA70, prLV}, // Lo HANGUL SYLLABLE MYEO
+ {0xBA71, 0xBA8B, prLVT}, // Lo [27] HANGUL SYLLABLE MYEOG..HANGUL SYLLABLE MYEOH
+ {0xBA8C, 0xBA8C, prLV}, // Lo HANGUL SYLLABLE MYE
+ {0xBA8D, 0xBAA7, prLVT}, // Lo [27] HANGUL SYLLABLE MYEG..HANGUL SYLLABLE MYEH
+ {0xBAA8, 0xBAA8, prLV}, // Lo HANGUL SYLLABLE MO
+ {0xBAA9, 0xBAC3, prLVT}, // Lo [27] HANGUL SYLLABLE MOG..HANGUL SYLLABLE MOH
+ {0xBAC4, 0xBAC4, prLV}, // Lo HANGUL SYLLABLE MWA
+ {0xBAC5, 0xBADF, prLVT}, // Lo [27] HANGUL SYLLABLE MWAG..HANGUL SYLLABLE MWAH
+ {0xBAE0, 0xBAE0, prLV}, // Lo HANGUL SYLLABLE MWAE
+ {0xBAE1, 0xBAFB, prLVT}, // Lo [27] HANGUL SYLLABLE MWAEG..HANGUL SYLLABLE MWAEH
+ {0xBAFC, 0xBAFC, prLV}, // Lo HANGUL SYLLABLE MOE
+ {0xBAFD, 0xBB17, prLVT}, // Lo [27] HANGUL SYLLABLE MOEG..HANGUL SYLLABLE MOEH
+ {0xBB18, 0xBB18, prLV}, // Lo HANGUL SYLLABLE MYO
+ {0xBB19, 0xBB33, prLVT}, // Lo [27] HANGUL SYLLABLE MYOG..HANGUL SYLLABLE MYOH
+ {0xBB34, 0xBB34, prLV}, // Lo HANGUL SYLLABLE MU
+ {0xBB35, 0xBB4F, prLVT}, // Lo [27] HANGUL SYLLABLE MUG..HANGUL SYLLABLE MUH
+ {0xBB50, 0xBB50, prLV}, // Lo HANGUL SYLLABLE MWEO
+ {0xBB51, 0xBB6B, prLVT}, // Lo [27] HANGUL SYLLABLE MWEOG..HANGUL SYLLABLE MWEOH
+ {0xBB6C, 0xBB6C, prLV}, // Lo HANGUL SYLLABLE MWE
+ {0xBB6D, 0xBB87, prLVT}, // Lo [27] HANGUL SYLLABLE MWEG..HANGUL SYLLABLE MWEH
+ {0xBB88, 0xBB88, prLV}, // Lo HANGUL SYLLABLE MWI
+ {0xBB89, 0xBBA3, prLVT}, // Lo [27] HANGUL SYLLABLE MWIG..HANGUL SYLLABLE MWIH
+ {0xBBA4, 0xBBA4, prLV}, // Lo HANGUL SYLLABLE MYU
+ {0xBBA5, 0xBBBF, prLVT}, // Lo [27] HANGUL SYLLABLE MYUG..HANGUL SYLLABLE MYUH
+ {0xBBC0, 0xBBC0, prLV}, // Lo HANGUL SYLLABLE MEU
+ {0xBBC1, 0xBBDB, prLVT}, // Lo [27] HANGUL SYLLABLE MEUG..HANGUL SYLLABLE MEUH
+ {0xBBDC, 0xBBDC, prLV}, // Lo HANGUL SYLLABLE MYI
+ {0xBBDD, 0xBBF7, prLVT}, // Lo [27] HANGUL SYLLABLE MYIG..HANGUL SYLLABLE MYIH
+ {0xBBF8, 0xBBF8, prLV}, // Lo HANGUL SYLLABLE MI
+ {0xBBF9, 0xBC13, prLVT}, // Lo [27] HANGUL SYLLABLE MIG..HANGUL SYLLABLE MIH
+ {0xBC14, 0xBC14, prLV}, // Lo HANGUL SYLLABLE BA
+ {0xBC15, 0xBC2F, prLVT}, // Lo [27] HANGUL SYLLABLE BAG..HANGUL SYLLABLE BAH
+ {0xBC30, 0xBC30, prLV}, // Lo HANGUL SYLLABLE BAE
+ {0xBC31, 0xBC4B, prLVT}, // Lo [27] HANGUL SYLLABLE BAEG..HANGUL SYLLABLE BAEH
+ {0xBC4C, 0xBC4C, prLV}, // Lo HANGUL SYLLABLE BYA
+ {0xBC4D, 0xBC67, prLVT}, // Lo [27] HANGUL SYLLABLE BYAG..HANGUL SYLLABLE BYAH
+ {0xBC68, 0xBC68, prLV}, // Lo HANGUL SYLLABLE BYAE
+ {0xBC69, 0xBC83, prLVT}, // Lo [27] HANGUL SYLLABLE BYAEG..HANGUL SYLLABLE BYAEH
+ {0xBC84, 0xBC84, prLV}, // Lo HANGUL SYLLABLE BEO
+ {0xBC85, 0xBC9F, prLVT}, // Lo [27] HANGUL SYLLABLE BEOG..HANGUL SYLLABLE BEOH
+ {0xBCA0, 0xBCA0, prLV}, // Lo HANGUL SYLLABLE BE
+ {0xBCA1, 0xBCBB, prLVT}, // Lo [27] HANGUL SYLLABLE BEG..HANGUL SYLLABLE BEH
+ {0xBCBC, 0xBCBC, prLV}, // Lo HANGUL SYLLABLE BYEO
+ {0xBCBD, 0xBCD7, prLVT}, // Lo [27] HANGUL SYLLABLE BYEOG..HANGUL SYLLABLE BYEOH
+ {0xBCD8, 0xBCD8, prLV}, // Lo HANGUL SYLLABLE BYE
+ {0xBCD9, 0xBCF3, prLVT}, // Lo [27] HANGUL SYLLABLE BYEG..HANGUL SYLLABLE BYEH
+ {0xBCF4, 0xBCF4, prLV}, // Lo HANGUL SYLLABLE BO
+ {0xBCF5, 0xBD0F, prLVT}, // Lo [27] HANGUL SYLLABLE BOG..HANGUL SYLLABLE BOH
+ {0xBD10, 0xBD10, prLV}, // Lo HANGUL SYLLABLE BWA
+ {0xBD11, 0xBD2B, prLVT}, // Lo [27] HANGUL SYLLABLE BWAG..HANGUL SYLLABLE BWAH
+ {0xBD2C, 0xBD2C, prLV}, // Lo HANGUL SYLLABLE BWAE
+ {0xBD2D, 0xBD47, prLVT}, // Lo [27] HANGUL SYLLABLE BWAEG..HANGUL SYLLABLE BWAEH
+ {0xBD48, 0xBD48, prLV}, // Lo HANGUL SYLLABLE BOE
+ {0xBD49, 0xBD63, prLVT}, // Lo [27] HANGUL SYLLABLE BOEG..HANGUL SYLLABLE BOEH
+ {0xBD64, 0xBD64, prLV}, // Lo HANGUL SYLLABLE BYO
+ {0xBD65, 0xBD7F, prLVT}, // Lo [27] HANGUL SYLLABLE BYOG..HANGUL SYLLABLE BYOH
+ {0xBD80, 0xBD80, prLV}, // Lo HANGUL SYLLABLE BU
+ {0xBD81, 0xBD9B, prLVT}, // Lo [27] HANGUL SYLLABLE BUG..HANGUL SYLLABLE BUH
+ {0xBD9C, 0xBD9C, prLV}, // Lo HANGUL SYLLABLE BWEO
+ {0xBD9D, 0xBDB7, prLVT}, // Lo [27] HANGUL SYLLABLE BWEOG..HANGUL SYLLABLE BWEOH
+ {0xBDB8, 0xBDB8, prLV}, // Lo HANGUL SYLLABLE BWE
+ {0xBDB9, 0xBDD3, prLVT}, // Lo [27] HANGUL SYLLABLE BWEG..HANGUL SYLLABLE BWEH
+ {0xBDD4, 0xBDD4, prLV}, // Lo HANGUL SYLLABLE BWI
+ {0xBDD5, 0xBDEF, prLVT}, // Lo [27] HANGUL SYLLABLE BWIG..HANGUL SYLLABLE BWIH
+ {0xBDF0, 0xBDF0, prLV}, // Lo HANGUL SYLLABLE BYU
+ {0xBDF1, 0xBE0B, prLVT}, // Lo [27] HANGUL SYLLABLE BYUG..HANGUL SYLLABLE BYUH
+ {0xBE0C, 0xBE0C, prLV}, // Lo HANGUL SYLLABLE BEU
+ {0xBE0D, 0xBE27, prLVT}, // Lo [27] HANGUL SYLLABLE BEUG..HANGUL SYLLABLE BEUH
+ {0xBE28, 0xBE28, prLV}, // Lo HANGUL SYLLABLE BYI
+ {0xBE29, 0xBE43, prLVT}, // Lo [27] HANGUL SYLLABLE BYIG..HANGUL SYLLABLE BYIH
+ {0xBE44, 0xBE44, prLV}, // Lo HANGUL SYLLABLE BI
+ {0xBE45, 0xBE5F, prLVT}, // Lo [27] HANGUL SYLLABLE BIG..HANGUL SYLLABLE BIH
+ {0xBE60, 0xBE60, prLV}, // Lo HANGUL SYLLABLE BBA
+ {0xBE61, 0xBE7B, prLVT}, // Lo [27] HANGUL SYLLABLE BBAG..HANGUL SYLLABLE BBAH
+ {0xBE7C, 0xBE7C, prLV}, // Lo HANGUL SYLLABLE BBAE
+ {0xBE7D, 0xBE97, prLVT}, // Lo [27] HANGUL SYLLABLE BBAEG..HANGUL SYLLABLE BBAEH
+ {0xBE98, 0xBE98, prLV}, // Lo HANGUL SYLLABLE BBYA
+ {0xBE99, 0xBEB3, prLVT}, // Lo [27] HANGUL SYLLABLE BBYAG..HANGUL SYLLABLE BBYAH
+ {0xBEB4, 0xBEB4, prLV}, // Lo HANGUL SYLLABLE BBYAE
+ {0xBEB5, 0xBECF, prLVT}, // Lo [27] HANGUL SYLLABLE BBYAEG..HANGUL SYLLABLE BBYAEH
+ {0xBED0, 0xBED0, prLV}, // Lo HANGUL SYLLABLE BBEO
+ {0xBED1, 0xBEEB, prLVT}, // Lo [27] HANGUL SYLLABLE BBEOG..HANGUL SYLLABLE BBEOH
+ {0xBEEC, 0xBEEC, prLV}, // Lo HANGUL SYLLABLE BBE
+ {0xBEED, 0xBF07, prLVT}, // Lo [27] HANGUL SYLLABLE BBEG..HANGUL SYLLABLE BBEH
+ {0xBF08, 0xBF08, prLV}, // Lo HANGUL SYLLABLE BBYEO
+ {0xBF09, 0xBF23, prLVT}, // Lo [27] HANGUL SYLLABLE BBYEOG..HANGUL SYLLABLE BBYEOH
+ {0xBF24, 0xBF24, prLV}, // Lo HANGUL SYLLABLE BBYE
+ {0xBF25, 0xBF3F, prLVT}, // Lo [27] HANGUL SYLLABLE BBYEG..HANGUL SYLLABLE BBYEH
+ {0xBF40, 0xBF40, prLV}, // Lo HANGUL SYLLABLE BBO
+ {0xBF41, 0xBF5B, prLVT}, // Lo [27] HANGUL SYLLABLE BBOG..HANGUL SYLLABLE BBOH
+ {0xBF5C, 0xBF5C, prLV}, // Lo HANGUL SYLLABLE BBWA
+ {0xBF5D, 0xBF77, prLVT}, // Lo [27] HANGUL SYLLABLE BBWAG..HANGUL SYLLABLE BBWAH
+ {0xBF78, 0xBF78, prLV}, // Lo HANGUL SYLLABLE BBWAE
+ {0xBF79, 0xBF93, prLVT}, // Lo [27] HANGUL SYLLABLE BBWAEG..HANGUL SYLLABLE BBWAEH
+ {0xBF94, 0xBF94, prLV}, // Lo HANGUL SYLLABLE BBOE
+ {0xBF95, 0xBFAF, prLVT}, // Lo [27] HANGUL SYLLABLE BBOEG..HANGUL SYLLABLE BBOEH
+ {0xBFB0, 0xBFB0, prLV}, // Lo HANGUL SYLLABLE BBYO
+ {0xBFB1, 0xBFCB, prLVT}, // Lo [27] HANGUL SYLLABLE BBYOG..HANGUL SYLLABLE BBYOH
+ {0xBFCC, 0xBFCC, prLV}, // Lo HANGUL SYLLABLE BBU
+ {0xBFCD, 0xBFE7, prLVT}, // Lo [27] HANGUL SYLLABLE BBUG..HANGUL SYLLABLE BBUH
+ {0xBFE8, 0xBFE8, prLV}, // Lo HANGUL SYLLABLE BBWEO
+ {0xBFE9, 0xC003, prLVT}, // Lo [27] HANGUL SYLLABLE BBWEOG..HANGUL SYLLABLE BBWEOH
+ {0xC004, 0xC004, prLV}, // Lo HANGUL SYLLABLE BBWE
+ {0xC005, 0xC01F, prLVT}, // Lo [27] HANGUL SYLLABLE BBWEG..HANGUL SYLLABLE BBWEH
+ {0xC020, 0xC020, prLV}, // Lo HANGUL SYLLABLE BBWI
+ {0xC021, 0xC03B, prLVT}, // Lo [27] HANGUL SYLLABLE BBWIG..HANGUL SYLLABLE BBWIH
+ {0xC03C, 0xC03C, prLV}, // Lo HANGUL SYLLABLE BBYU
+ {0xC03D, 0xC057, prLVT}, // Lo [27] HANGUL SYLLABLE BBYUG..HANGUL SYLLABLE BBYUH
+ {0xC058, 0xC058, prLV}, // Lo HANGUL SYLLABLE BBEU
+ {0xC059, 0xC073, prLVT}, // Lo [27] HANGUL SYLLABLE BBEUG..HANGUL SYLLABLE BBEUH
+ {0xC074, 0xC074, prLV}, // Lo HANGUL SYLLABLE BBYI
+ {0xC075, 0xC08F, prLVT}, // Lo [27] HANGUL SYLLABLE BBYIG..HANGUL SYLLABLE BBYIH
+ {0xC090, 0xC090, prLV}, // Lo HANGUL SYLLABLE BBI
+ {0xC091, 0xC0AB, prLVT}, // Lo [27] HANGUL SYLLABLE BBIG..HANGUL SYLLABLE BBIH
+ {0xC0AC, 0xC0AC, prLV}, // Lo HANGUL SYLLABLE SA
+ {0xC0AD, 0xC0C7, prLVT}, // Lo [27] HANGUL SYLLABLE SAG..HANGUL SYLLABLE SAH
+ {0xC0C8, 0xC0C8, prLV}, // Lo HANGUL SYLLABLE SAE
+ {0xC0C9, 0xC0E3, prLVT}, // Lo [27] HANGUL SYLLABLE SAEG..HANGUL SYLLABLE SAEH
+ {0xC0E4, 0xC0E4, prLV}, // Lo HANGUL SYLLABLE SYA
+ {0xC0E5, 0xC0FF, prLVT}, // Lo [27] HANGUL SYLLABLE SYAG..HANGUL SYLLABLE SYAH
+ {0xC100, 0xC100, prLV}, // Lo HANGUL SYLLABLE SYAE
+ {0xC101, 0xC11B, prLVT}, // Lo [27] HANGUL SYLLABLE SYAEG..HANGUL SYLLABLE SYAEH
+ {0xC11C, 0xC11C, prLV}, // Lo HANGUL SYLLABLE SEO
+ {0xC11D, 0xC137, prLVT}, // Lo [27] HANGUL SYLLABLE SEOG..HANGUL SYLLABLE SEOH
+ {0xC138, 0xC138, prLV}, // Lo HANGUL SYLLABLE SE
+ {0xC139, 0xC153, prLVT}, // Lo [27] HANGUL SYLLABLE SEG..HANGUL SYLLABLE SEH
+ {0xC154, 0xC154, prLV}, // Lo HANGUL SYLLABLE SYEO
+ {0xC155, 0xC16F, prLVT}, // Lo [27] HANGUL SYLLABLE SYEOG..HANGUL SYLLABLE SYEOH
+ {0xC170, 0xC170, prLV}, // Lo HANGUL SYLLABLE SYE
+ {0xC171, 0xC18B, prLVT}, // Lo [27] HANGUL SYLLABLE SYEG..HANGUL SYLLABLE SYEH
+ {0xC18C, 0xC18C, prLV}, // Lo HANGUL SYLLABLE SO
+ {0xC18D, 0xC1A7, prLVT}, // Lo [27] HANGUL SYLLABLE SOG..HANGUL SYLLABLE SOH
+ {0xC1A8, 0xC1A8, prLV}, // Lo HANGUL SYLLABLE SWA
+ {0xC1A9, 0xC1C3, prLVT}, // Lo [27] HANGUL SYLLABLE SWAG..HANGUL SYLLABLE SWAH
+ {0xC1C4, 0xC1C4, prLV}, // Lo HANGUL SYLLABLE SWAE
+ {0xC1C5, 0xC1DF, prLVT}, // Lo [27] HANGUL SYLLABLE SWAEG..HANGUL SYLLABLE SWAEH
+ {0xC1E0, 0xC1E0, prLV}, // Lo HANGUL SYLLABLE SOE
+ {0xC1E1, 0xC1FB, prLVT}, // Lo [27] HANGUL SYLLABLE SOEG..HANGUL SYLLABLE SOEH
+ {0xC1FC, 0xC1FC, prLV}, // Lo HANGUL SYLLABLE SYO
+ {0xC1FD, 0xC217, prLVT}, // Lo [27] HANGUL SYLLABLE SYOG..HANGUL SYLLABLE SYOH
+ {0xC218, 0xC218, prLV}, // Lo HANGUL SYLLABLE SU
+ {0xC219, 0xC233, prLVT}, // Lo [27] HANGUL SYLLABLE SUG..HANGUL SYLLABLE SUH
+ {0xC234, 0xC234, prLV}, // Lo HANGUL SYLLABLE SWEO
+ {0xC235, 0xC24F, prLVT}, // Lo [27] HANGUL SYLLABLE SWEOG..HANGUL SYLLABLE SWEOH
+ {0xC250, 0xC250, prLV}, // Lo HANGUL SYLLABLE SWE
+ {0xC251, 0xC26B, prLVT}, // Lo [27] HANGUL SYLLABLE SWEG..HANGUL SYLLABLE SWEH
+ {0xC26C, 0xC26C, prLV}, // Lo HANGUL SYLLABLE SWI
+ {0xC26D, 0xC287, prLVT}, // Lo [27] HANGUL SYLLABLE SWIG..HANGUL SYLLABLE SWIH
+ {0xC288, 0xC288, prLV}, // Lo HANGUL SYLLABLE SYU
+ {0xC289, 0xC2A3, prLVT}, // Lo [27] HANGUL SYLLABLE SYUG..HANGUL SYLLABLE SYUH
+ {0xC2A4, 0xC2A4, prLV}, // Lo HANGUL SYLLABLE SEU
+ {0xC2A5, 0xC2BF, prLVT}, // Lo [27] HANGUL SYLLABLE SEUG..HANGUL SYLLABLE SEUH
+ {0xC2C0, 0xC2C0, prLV}, // Lo HANGUL SYLLABLE SYI
+ {0xC2C1, 0xC2DB, prLVT}, // Lo [27] HANGUL SYLLABLE SYIG..HANGUL SYLLABLE SYIH
+ {0xC2DC, 0xC2DC, prLV}, // Lo HANGUL SYLLABLE SI
+ {0xC2DD, 0xC2F7, prLVT}, // Lo [27] HANGUL SYLLABLE SIG..HANGUL SYLLABLE SIH
+ {0xC2F8, 0xC2F8, prLV}, // Lo HANGUL SYLLABLE SSA
+ {0xC2F9, 0xC313, prLVT}, // Lo [27] HANGUL SYLLABLE SSAG..HANGUL SYLLABLE SSAH
+ {0xC314, 0xC314, prLV}, // Lo HANGUL SYLLABLE SSAE
+ {0xC315, 0xC32F, prLVT}, // Lo [27] HANGUL SYLLABLE SSAEG..HANGUL SYLLABLE SSAEH
+ {0xC330, 0xC330, prLV}, // Lo HANGUL SYLLABLE SSYA
+ {0xC331, 0xC34B, prLVT}, // Lo [27] HANGUL SYLLABLE SSYAG..HANGUL SYLLABLE SSYAH
+ {0xC34C, 0xC34C, prLV}, // Lo HANGUL SYLLABLE SSYAE
+ {0xC34D, 0xC367, prLVT}, // Lo [27] HANGUL SYLLABLE SSYAEG..HANGUL SYLLABLE SSYAEH
+ {0xC368, 0xC368, prLV}, // Lo HANGUL SYLLABLE SSEO
+ {0xC369, 0xC383, prLVT}, // Lo [27] HANGUL SYLLABLE SSEOG..HANGUL SYLLABLE SSEOH
+ {0xC384, 0xC384, prLV}, // Lo HANGUL SYLLABLE SSE
+ {0xC385, 0xC39F, prLVT}, // Lo [27] HANGUL SYLLABLE SSEG..HANGUL SYLLABLE SSEH
+ {0xC3A0, 0xC3A0, prLV}, // Lo HANGUL SYLLABLE SSYEO
+ {0xC3A1, 0xC3BB, prLVT}, // Lo [27] HANGUL SYLLABLE SSYEOG..HANGUL SYLLABLE SSYEOH
+ {0xC3BC, 0xC3BC, prLV}, // Lo HANGUL SYLLABLE SSYE
+ {0xC3BD, 0xC3D7, prLVT}, // Lo [27] HANGUL SYLLABLE SSYEG..HANGUL SYLLABLE SSYEH
+ {0xC3D8, 0xC3D8, prLV}, // Lo HANGUL SYLLABLE SSO
+ {0xC3D9, 0xC3F3, prLVT}, // Lo [27] HANGUL SYLLABLE SSOG..HANGUL SYLLABLE SSOH
+ {0xC3F4, 0xC3F4, prLV}, // Lo HANGUL SYLLABLE SSWA
+ {0xC3F5, 0xC40F, prLVT}, // Lo [27] HANGUL SYLLABLE SSWAG..HANGUL SYLLABLE SSWAH
+ {0xC410, 0xC410, prLV}, // Lo HANGUL SYLLABLE SSWAE
+ {0xC411, 0xC42B, prLVT}, // Lo [27] HANGUL SYLLABLE SSWAEG..HANGUL SYLLABLE SSWAEH
+ {0xC42C, 0xC42C, prLV}, // Lo HANGUL SYLLABLE SSOE
+ {0xC42D, 0xC447, prLVT}, // Lo [27] HANGUL SYLLABLE SSOEG..HANGUL SYLLABLE SSOEH
+ {0xC448, 0xC448, prLV}, // Lo HANGUL SYLLABLE SSYO
+ {0xC449, 0xC463, prLVT}, // Lo [27] HANGUL SYLLABLE SSYOG..HANGUL SYLLABLE SSYOH
+ {0xC464, 0xC464, prLV}, // Lo HANGUL SYLLABLE SSU
+ {0xC465, 0xC47F, prLVT}, // Lo [27] HANGUL SYLLABLE SSUG..HANGUL SYLLABLE SSUH
+ {0xC480, 0xC480, prLV}, // Lo HANGUL SYLLABLE SSWEO
+ {0xC481, 0xC49B, prLVT}, // Lo [27] HANGUL SYLLABLE SSWEOG..HANGUL SYLLABLE SSWEOH
+ {0xC49C, 0xC49C, prLV}, // Lo HANGUL SYLLABLE SSWE
+ {0xC49D, 0xC4B7, prLVT}, // Lo [27] HANGUL SYLLABLE SSWEG..HANGUL SYLLABLE SSWEH
+ {0xC4B8, 0xC4B8, prLV}, // Lo HANGUL SYLLABLE SSWI
+ {0xC4B9, 0xC4D3, prLVT}, // Lo [27] HANGUL SYLLABLE SSWIG..HANGUL SYLLABLE SSWIH
+ {0xC4D4, 0xC4D4, prLV}, // Lo HANGUL SYLLABLE SSYU
+ {0xC4D5, 0xC4EF, prLVT}, // Lo [27] HANGUL SYLLABLE SSYUG..HANGUL SYLLABLE SSYUH
+ {0xC4F0, 0xC4F0, prLV}, // Lo HANGUL SYLLABLE SSEU
+ {0xC4F1, 0xC50B, prLVT}, // Lo [27] HANGUL SYLLABLE SSEUG..HANGUL SYLLABLE SSEUH
+ {0xC50C, 0xC50C, prLV}, // Lo HANGUL SYLLABLE SSYI
+ {0xC50D, 0xC527, prLVT}, // Lo [27] HANGUL SYLLABLE SSYIG..HANGUL SYLLABLE SSYIH
+ {0xC528, 0xC528, prLV}, // Lo HANGUL SYLLABLE SSI
+ {0xC529, 0xC543, prLVT}, // Lo [27] HANGUL SYLLABLE SSIG..HANGUL SYLLABLE SSIH
+ {0xC544, 0xC544, prLV}, // Lo HANGUL SYLLABLE A
+ {0xC545, 0xC55F, prLVT}, // Lo [27] HANGUL SYLLABLE AG..HANGUL SYLLABLE AH
+ {0xC560, 0xC560, prLV}, // Lo HANGUL SYLLABLE AE
+ {0xC561, 0xC57B, prLVT}, // Lo [27] HANGUL SYLLABLE AEG..HANGUL SYLLABLE AEH
+ {0xC57C, 0xC57C, prLV}, // Lo HANGUL SYLLABLE YA
+ {0xC57D, 0xC597, prLVT}, // Lo [27] HANGUL SYLLABLE YAG..HANGUL SYLLABLE YAH
+ {0xC598, 0xC598, prLV}, // Lo HANGUL SYLLABLE YAE
+ {0xC599, 0xC5B3, prLVT}, // Lo [27] HANGUL SYLLABLE YAEG..HANGUL SYLLABLE YAEH
+ {0xC5B4, 0xC5B4, prLV}, // Lo HANGUL SYLLABLE EO
+ {0xC5B5, 0xC5CF, prLVT}, // Lo [27] HANGUL SYLLABLE EOG..HANGUL SYLLABLE EOH
+ {0xC5D0, 0xC5D0, prLV}, // Lo HANGUL SYLLABLE E
+ {0xC5D1, 0xC5EB, prLVT}, // Lo [27] HANGUL SYLLABLE EG..HANGUL SYLLABLE EH
+ {0xC5EC, 0xC5EC, prLV}, // Lo HANGUL SYLLABLE YEO
+ {0xC5ED, 0xC607, prLVT}, // Lo [27] HANGUL SYLLABLE YEOG..HANGUL SYLLABLE YEOH
+ {0xC608, 0xC608, prLV}, // Lo HANGUL SYLLABLE YE
+ {0xC609, 0xC623, prLVT}, // Lo [27] HANGUL SYLLABLE YEG..HANGUL SYLLABLE YEH
+ {0xC624, 0xC624, prLV}, // Lo HANGUL SYLLABLE O
+ {0xC625, 0xC63F, prLVT}, // Lo [27] HANGUL SYLLABLE OG..HANGUL SYLLABLE OH
+ {0xC640, 0xC640, prLV}, // Lo HANGUL SYLLABLE WA
+ {0xC641, 0xC65B, prLVT}, // Lo [27] HANGUL SYLLABLE WAG..HANGUL SYLLABLE WAH
+ {0xC65C, 0xC65C, prLV}, // Lo HANGUL SYLLABLE WAE
+ {0xC65D, 0xC677, prLVT}, // Lo [27] HANGUL SYLLABLE WAEG..HANGUL SYLLABLE WAEH
+ {0xC678, 0xC678, prLV}, // Lo HANGUL SYLLABLE OE
+ {0xC679, 0xC693, prLVT}, // Lo [27] HANGUL SYLLABLE OEG..HANGUL SYLLABLE OEH
+ {0xC694, 0xC694, prLV}, // Lo HANGUL SYLLABLE YO
+ {0xC695, 0xC6AF, prLVT}, // Lo [27] HANGUL SYLLABLE YOG..HANGUL SYLLABLE YOH
+ {0xC6B0, 0xC6B0, prLV}, // Lo HANGUL SYLLABLE U
+ {0xC6B1, 0xC6CB, prLVT}, // Lo [27] HANGUL SYLLABLE UG..HANGUL SYLLABLE UH
+ {0xC6CC, 0xC6CC, prLV}, // Lo HANGUL SYLLABLE WEO
+ {0xC6CD, 0xC6E7, prLVT}, // Lo [27] HANGUL SYLLABLE WEOG..HANGUL SYLLABLE WEOH
+ {0xC6E8, 0xC6E8, prLV}, // Lo HANGUL SYLLABLE WE
+ {0xC6E9, 0xC703, prLVT}, // Lo [27] HANGUL SYLLABLE WEG..HANGUL SYLLABLE WEH
+ {0xC704, 0xC704, prLV}, // Lo HANGUL SYLLABLE WI
+ {0xC705, 0xC71F, prLVT}, // Lo [27] HANGUL SYLLABLE WIG..HANGUL SYLLABLE WIH
+ {0xC720, 0xC720, prLV}, // Lo HANGUL SYLLABLE YU
+ {0xC721, 0xC73B, prLVT}, // Lo [27] HANGUL SYLLABLE YUG..HANGUL SYLLABLE YUH
+ {0xC73C, 0xC73C, prLV}, // Lo HANGUL SYLLABLE EU
+ {0xC73D, 0xC757, prLVT}, // Lo [27] HANGUL SYLLABLE EUG..HANGUL SYLLABLE EUH
+ {0xC758, 0xC758, prLV}, // Lo HANGUL SYLLABLE YI
+ {0xC759, 0xC773, prLVT}, // Lo [27] HANGUL SYLLABLE YIG..HANGUL SYLLABLE YIH
+ {0xC774, 0xC774, prLV}, // Lo HANGUL SYLLABLE I
+ {0xC775, 0xC78F, prLVT}, // Lo [27] HANGUL SYLLABLE IG..HANGUL SYLLABLE IH
+ {0xC790, 0xC790, prLV}, // Lo HANGUL SYLLABLE JA
+ {0xC791, 0xC7AB, prLVT}, // Lo [27] HANGUL SYLLABLE JAG..HANGUL SYLLABLE JAH
+ {0xC7AC, 0xC7AC, prLV}, // Lo HANGUL SYLLABLE JAE
+ {0xC7AD, 0xC7C7, prLVT}, // Lo [27] HANGUL SYLLABLE JAEG..HANGUL SYLLABLE JAEH
+ {0xC7C8, 0xC7C8, prLV}, // Lo HANGUL SYLLABLE JYA
+ {0xC7C9, 0xC7E3, prLVT}, // Lo [27] HANGUL SYLLABLE JYAG..HANGUL SYLLABLE JYAH
+ {0xC7E4, 0xC7E4, prLV}, // Lo HANGUL SYLLABLE JYAE
+ {0xC7E5, 0xC7FF, prLVT}, // Lo [27] HANGUL SYLLABLE JYAEG..HANGUL SYLLABLE JYAEH
+ {0xC800, 0xC800, prLV}, // Lo HANGUL SYLLABLE JEO
+ {0xC801, 0xC81B, prLVT}, // Lo [27] HANGUL SYLLABLE JEOG..HANGUL SYLLABLE JEOH
+ {0xC81C, 0xC81C, prLV}, // Lo HANGUL SYLLABLE JE
+ {0xC81D, 0xC837, prLVT}, // Lo [27] HANGUL SYLLABLE JEG..HANGUL SYLLABLE JEH
+ {0xC838, 0xC838, prLV}, // Lo HANGUL SYLLABLE JYEO
+ {0xC839, 0xC853, prLVT}, // Lo [27] HANGUL SYLLABLE JYEOG..HANGUL SYLLABLE JYEOH
+ {0xC854, 0xC854, prLV}, // Lo HANGUL SYLLABLE JYE
+ {0xC855, 0xC86F, prLVT}, // Lo [27] HANGUL SYLLABLE JYEG..HANGUL SYLLABLE JYEH
+ {0xC870, 0xC870, prLV}, // Lo HANGUL SYLLABLE JO
+ {0xC871, 0xC88B, prLVT}, // Lo [27] HANGUL SYLLABLE JOG..HANGUL SYLLABLE JOH
+ {0xC88C, 0xC88C, prLV}, // Lo HANGUL SYLLABLE JWA
+ {0xC88D, 0xC8A7, prLVT}, // Lo [27] HANGUL SYLLABLE JWAG..HANGUL SYLLABLE JWAH
+ {0xC8A8, 0xC8A8, prLV}, // Lo HANGUL SYLLABLE JWAE
+ {0xC8A9, 0xC8C3, prLVT}, // Lo [27] HANGUL SYLLABLE JWAEG..HANGUL SYLLABLE JWAEH
+ {0xC8C4, 0xC8C4, prLV}, // Lo HANGUL SYLLABLE JOE
+ {0xC8C5, 0xC8DF, prLVT}, // Lo [27] HANGUL SYLLABLE JOEG..HANGUL SYLLABLE JOEH
+ {0xC8E0, 0xC8E0, prLV}, // Lo HANGUL SYLLABLE JYO
+ {0xC8E1, 0xC8FB, prLVT}, // Lo [27] HANGUL SYLLABLE JYOG..HANGUL SYLLABLE JYOH
+ {0xC8FC, 0xC8FC, prLV}, // Lo HANGUL SYLLABLE JU
+ {0xC8FD, 0xC917, prLVT}, // Lo [27] HANGUL SYLLABLE JUG..HANGUL SYLLABLE JUH
+ {0xC918, 0xC918, prLV}, // Lo HANGUL SYLLABLE JWEO
+ {0xC919, 0xC933, prLVT}, // Lo [27] HANGUL SYLLABLE JWEOG..HANGUL SYLLABLE JWEOH
+ {0xC934, 0xC934, prLV}, // Lo HANGUL SYLLABLE JWE
+ {0xC935, 0xC94F, prLVT}, // Lo [27] HANGUL SYLLABLE JWEG..HANGUL SYLLABLE JWEH
+ {0xC950, 0xC950, prLV}, // Lo HANGUL SYLLABLE JWI
+ {0xC951, 0xC96B, prLVT}, // Lo [27] HANGUL SYLLABLE JWIG..HANGUL SYLLABLE JWIH
+ {0xC96C, 0xC96C, prLV}, // Lo HANGUL SYLLABLE JYU
+ {0xC96D, 0xC987, prLVT}, // Lo [27] HANGUL SYLLABLE JYUG..HANGUL SYLLABLE JYUH
+ {0xC988, 0xC988, prLV}, // Lo HANGUL SYLLABLE JEU
+ {0xC989, 0xC9A3, prLVT}, // Lo [27] HANGUL SYLLABLE JEUG..HANGUL SYLLABLE JEUH
+ {0xC9A4, 0xC9A4, prLV}, // Lo HANGUL SYLLABLE JYI
+ {0xC9A5, 0xC9BF, prLVT}, // Lo [27] HANGUL SYLLABLE JYIG..HANGUL SYLLABLE JYIH
+ {0xC9C0, 0xC9C0, prLV}, // Lo HANGUL SYLLABLE JI
+ {0xC9C1, 0xC9DB, prLVT}, // Lo [27] HANGUL SYLLABLE JIG..HANGUL SYLLABLE JIH
+ {0xC9DC, 0xC9DC, prLV}, // Lo HANGUL SYLLABLE JJA
+ {0xC9DD, 0xC9F7, prLVT}, // Lo [27] HANGUL SYLLABLE JJAG..HANGUL SYLLABLE JJAH
+ {0xC9F8, 0xC9F8, prLV}, // Lo HANGUL SYLLABLE JJAE
+ {0xC9F9, 0xCA13, prLVT}, // Lo [27] HANGUL SYLLABLE JJAEG..HANGUL SYLLABLE JJAEH
+ {0xCA14, 0xCA14, prLV}, // Lo HANGUL SYLLABLE JJYA
+ {0xCA15, 0xCA2F, prLVT}, // Lo [27] HANGUL SYLLABLE JJYAG..HANGUL SYLLABLE JJYAH
+ {0xCA30, 0xCA30, prLV}, // Lo HANGUL SYLLABLE JJYAE
+ {0xCA31, 0xCA4B, prLVT}, // Lo [27] HANGUL SYLLABLE JJYAEG..HANGUL SYLLABLE JJYAEH
+ {0xCA4C, 0xCA4C, prLV}, // Lo HANGUL SYLLABLE JJEO
+ {0xCA4D, 0xCA67, prLVT}, // Lo [27] HANGUL SYLLABLE JJEOG..HANGUL SYLLABLE JJEOH
+ {0xCA68, 0xCA68, prLV}, // Lo HANGUL SYLLABLE JJE
+ {0xCA69, 0xCA83, prLVT}, // Lo [27] HANGUL SYLLABLE JJEG..HANGUL SYLLABLE JJEH
+ {0xCA84, 0xCA84, prLV}, // Lo HANGUL SYLLABLE JJYEO
+ {0xCA85, 0xCA9F, prLVT}, // Lo [27] HANGUL SYLLABLE JJYEOG..HANGUL SYLLABLE JJYEOH
+ {0xCAA0, 0xCAA0, prLV}, // Lo HANGUL SYLLABLE JJYE
+ {0xCAA1, 0xCABB, prLVT}, // Lo [27] HANGUL SYLLABLE JJYEG..HANGUL SYLLABLE JJYEH
+ {0xCABC, 0xCABC, prLV}, // Lo HANGUL SYLLABLE JJO
+ {0xCABD, 0xCAD7, prLVT}, // Lo [27] HANGUL SYLLABLE JJOG..HANGUL SYLLABLE JJOH
+ {0xCAD8, 0xCAD8, prLV}, // Lo HANGUL SYLLABLE JJWA
+ {0xCAD9, 0xCAF3, prLVT}, // Lo [27] HANGUL SYLLABLE JJWAG..HANGUL SYLLABLE JJWAH
+ {0xCAF4, 0xCAF4, prLV}, // Lo HANGUL SYLLABLE JJWAE
+ {0xCAF5, 0xCB0F, prLVT}, // Lo [27] HANGUL SYLLABLE JJWAEG..HANGUL SYLLABLE JJWAEH
+ {0xCB10, 0xCB10, prLV}, // Lo HANGUL SYLLABLE JJOE
+ {0xCB11, 0xCB2B, prLVT}, // Lo [27] HANGUL SYLLABLE JJOEG..HANGUL SYLLABLE JJOEH
+ {0xCB2C, 0xCB2C, prLV}, // Lo HANGUL SYLLABLE JJYO
+ {0xCB2D, 0xCB47, prLVT}, // Lo [27] HANGUL SYLLABLE JJYOG..HANGUL SYLLABLE JJYOH
+ {0xCB48, 0xCB48, prLV}, // Lo HANGUL SYLLABLE JJU
+ {0xCB49, 0xCB63, prLVT}, // Lo [27] HANGUL SYLLABLE JJUG..HANGUL SYLLABLE JJUH
+ {0xCB64, 0xCB64, prLV}, // Lo HANGUL SYLLABLE JJWEO
+ {0xCB65, 0xCB7F, prLVT}, // Lo [27] HANGUL SYLLABLE JJWEOG..HANGUL SYLLABLE JJWEOH
+ {0xCB80, 0xCB80, prLV}, // Lo HANGUL SYLLABLE JJWE
+ {0xCB81, 0xCB9B, prLVT}, // Lo [27] HANGUL SYLLABLE JJWEG..HANGUL SYLLABLE JJWEH
+ {0xCB9C, 0xCB9C, prLV}, // Lo HANGUL SYLLABLE JJWI
+ {0xCB9D, 0xCBB7, prLVT}, // Lo [27] HANGUL SYLLABLE JJWIG..HANGUL SYLLABLE JJWIH
+ {0xCBB8, 0xCBB8, prLV}, // Lo HANGUL SYLLABLE JJYU
+ {0xCBB9, 0xCBD3, prLVT}, // Lo [27] HANGUL SYLLABLE JJYUG..HANGUL SYLLABLE JJYUH
+ {0xCBD4, 0xCBD4, prLV}, // Lo HANGUL SYLLABLE JJEU
+ {0xCBD5, 0xCBEF, prLVT}, // Lo [27] HANGUL SYLLABLE JJEUG..HANGUL SYLLABLE JJEUH
+ {0xCBF0, 0xCBF0, prLV}, // Lo HANGUL SYLLABLE JJYI
+ {0xCBF1, 0xCC0B, prLVT}, // Lo [27] HANGUL SYLLABLE JJYIG..HANGUL SYLLABLE JJYIH
+ {0xCC0C, 0xCC0C, prLV}, // Lo HANGUL SYLLABLE JJI
+ {0xCC0D, 0xCC27, prLVT}, // Lo [27] HANGUL SYLLABLE JJIG..HANGUL SYLLABLE JJIH
+ {0xCC28, 0xCC28, prLV}, // Lo HANGUL SYLLABLE CA
+ {0xCC29, 0xCC43, prLVT}, // Lo [27] HANGUL SYLLABLE CAG..HANGUL SYLLABLE CAH
+ {0xCC44, 0xCC44, prLV}, // Lo HANGUL SYLLABLE CAE
+ {0xCC45, 0xCC5F, prLVT}, // Lo [27] HANGUL SYLLABLE CAEG..HANGUL SYLLABLE CAEH
+ {0xCC60, 0xCC60, prLV}, // Lo HANGUL SYLLABLE CYA
+ {0xCC61, 0xCC7B, prLVT}, // Lo [27] HANGUL SYLLABLE CYAG..HANGUL SYLLABLE CYAH
+ {0xCC7C, 0xCC7C, prLV}, // Lo HANGUL SYLLABLE CYAE
+ {0xCC7D, 0xCC97, prLVT}, // Lo [27] HANGUL SYLLABLE CYAEG..HANGUL SYLLABLE CYAEH
+ {0xCC98, 0xCC98, prLV}, // Lo HANGUL SYLLABLE CEO
+ {0xCC99, 0xCCB3, prLVT}, // Lo [27] HANGUL SYLLABLE CEOG..HANGUL SYLLABLE CEOH
+ {0xCCB4, 0xCCB4, prLV}, // Lo HANGUL SYLLABLE CE
+ {0xCCB5, 0xCCCF, prLVT}, // Lo [27] HANGUL SYLLABLE CEG..HANGUL SYLLABLE CEH
+ {0xCCD0, 0xCCD0, prLV}, // Lo HANGUL SYLLABLE CYEO
+ {0xCCD1, 0xCCEB, prLVT}, // Lo [27] HANGUL SYLLABLE CYEOG..HANGUL SYLLABLE CYEOH
+ {0xCCEC, 0xCCEC, prLV}, // Lo HANGUL SYLLABLE CYE
+ {0xCCED, 0xCD07, prLVT}, // Lo [27] HANGUL SYLLABLE CYEG..HANGUL SYLLABLE CYEH
+ {0xCD08, 0xCD08, prLV}, // Lo HANGUL SYLLABLE CO
+ {0xCD09, 0xCD23, prLVT}, // Lo [27] HANGUL SYLLABLE COG..HANGUL SYLLABLE COH
+ {0xCD24, 0xCD24, prLV}, // Lo HANGUL SYLLABLE CWA
+ {0xCD25, 0xCD3F, prLVT}, // Lo [27] HANGUL SYLLABLE CWAG..HANGUL SYLLABLE CWAH
+ {0xCD40, 0xCD40, prLV}, // Lo HANGUL SYLLABLE CWAE
+ {0xCD41, 0xCD5B, prLVT}, // Lo [27] HANGUL SYLLABLE CWAEG..HANGUL SYLLABLE CWAEH
+ {0xCD5C, 0xCD5C, prLV}, // Lo HANGUL SYLLABLE COE
+ {0xCD5D, 0xCD77, prLVT}, // Lo [27] HANGUL SYLLABLE COEG..HANGUL SYLLABLE COEH
+ {0xCD78, 0xCD78, prLV}, // Lo HANGUL SYLLABLE CYO
+ {0xCD79, 0xCD93, prLVT}, // Lo [27] HANGUL SYLLABLE CYOG..HANGUL SYLLABLE CYOH
+ {0xCD94, 0xCD94, prLV}, // Lo HANGUL SYLLABLE CU
+ {0xCD95, 0xCDAF, prLVT}, // Lo [27] HANGUL SYLLABLE CUG..HANGUL SYLLABLE CUH
+ {0xCDB0, 0xCDB0, prLV}, // Lo HANGUL SYLLABLE CWEO
+ {0xCDB1, 0xCDCB, prLVT}, // Lo [27] HANGUL SYLLABLE CWEOG..HANGUL SYLLABLE CWEOH
+ {0xCDCC, 0xCDCC, prLV}, // Lo HANGUL SYLLABLE CWE
+ {0xCDCD, 0xCDE7, prLVT}, // Lo [27] HANGUL SYLLABLE CWEG..HANGUL SYLLABLE CWEH
+ {0xCDE8, 0xCDE8, prLV}, // Lo HANGUL SYLLABLE CWI
+ {0xCDE9, 0xCE03, prLVT}, // Lo [27] HANGUL SYLLABLE CWIG..HANGUL SYLLABLE CWIH
+ {0xCE04, 0xCE04, prLV}, // Lo HANGUL SYLLABLE CYU
+ {0xCE05, 0xCE1F, prLVT}, // Lo [27] HANGUL SYLLABLE CYUG..HANGUL SYLLABLE CYUH
+ {0xCE20, 0xCE20, prLV}, // Lo HANGUL SYLLABLE CEU
+ {0xCE21, 0xCE3B, prLVT}, // Lo [27] HANGUL SYLLABLE CEUG..HANGUL SYLLABLE CEUH
+ {0xCE3C, 0xCE3C, prLV}, // Lo HANGUL SYLLABLE CYI
+ {0xCE3D, 0xCE57, prLVT}, // Lo [27] HANGUL SYLLABLE CYIG..HANGUL SYLLABLE CYIH
+ {0xCE58, 0xCE58, prLV}, // Lo HANGUL SYLLABLE CI
+ {0xCE59, 0xCE73, prLVT}, // Lo [27] HANGUL SYLLABLE CIG..HANGUL SYLLABLE CIH
+ {0xCE74, 0xCE74, prLV}, // Lo HANGUL SYLLABLE KA
+ {0xCE75, 0xCE8F, prLVT}, // Lo [27] HANGUL SYLLABLE KAG..HANGUL SYLLABLE KAH
+ {0xCE90, 0xCE90, prLV}, // Lo HANGUL SYLLABLE KAE
+ {0xCE91, 0xCEAB, prLVT}, // Lo [27] HANGUL SYLLABLE KAEG..HANGUL SYLLABLE KAEH
+ {0xCEAC, 0xCEAC, prLV}, // Lo HANGUL SYLLABLE KYA
+ {0xCEAD, 0xCEC7, prLVT}, // Lo [27] HANGUL SYLLABLE KYAG..HANGUL SYLLABLE KYAH
+ {0xCEC8, 0xCEC8, prLV}, // Lo HANGUL SYLLABLE KYAE
+ {0xCEC9, 0xCEE3, prLVT}, // Lo [27] HANGUL SYLLABLE KYAEG..HANGUL SYLLABLE KYAEH
+ {0xCEE4, 0xCEE4, prLV}, // Lo HANGUL SYLLABLE KEO
+ {0xCEE5, 0xCEFF, prLVT}, // Lo [27] HANGUL SYLLABLE KEOG..HANGUL SYLLABLE KEOH
+ {0xCF00, 0xCF00, prLV}, // Lo HANGUL SYLLABLE KE
+ {0xCF01, 0xCF1B, prLVT}, // Lo [27] HANGUL SYLLABLE KEG..HANGUL SYLLABLE KEH
+ {0xCF1C, 0xCF1C, prLV}, // Lo HANGUL SYLLABLE KYEO
+ {0xCF1D, 0xCF37, prLVT}, // Lo [27] HANGUL SYLLABLE KYEOG..HANGUL SYLLABLE KYEOH
+ {0xCF38, 0xCF38, prLV}, // Lo HANGUL SYLLABLE KYE
+ {0xCF39, 0xCF53, prLVT}, // Lo [27] HANGUL SYLLABLE KYEG..HANGUL SYLLABLE KYEH
+ {0xCF54, 0xCF54, prLV}, // Lo HANGUL SYLLABLE KO
+ {0xCF55, 0xCF6F, prLVT}, // Lo [27] HANGUL SYLLABLE KOG..HANGUL SYLLABLE KOH
+ {0xCF70, 0xCF70, prLV}, // Lo HANGUL SYLLABLE KWA
+ {0xCF71, 0xCF8B, prLVT}, // Lo [27] HANGUL SYLLABLE KWAG..HANGUL SYLLABLE KWAH
+ {0xCF8C, 0xCF8C, prLV}, // Lo HANGUL SYLLABLE KWAE
+ {0xCF8D, 0xCFA7, prLVT}, // Lo [27] HANGUL SYLLABLE KWAEG..HANGUL SYLLABLE KWAEH
+ {0xCFA8, 0xCFA8, prLV}, // Lo HANGUL SYLLABLE KOE
+ {0xCFA9, 0xCFC3, prLVT}, // Lo [27] HANGUL SYLLABLE KOEG..HANGUL SYLLABLE KOEH
+ {0xCFC4, 0xCFC4, prLV}, // Lo HANGUL SYLLABLE KYO
+ {0xCFC5, 0xCFDF, prLVT}, // Lo [27] HANGUL SYLLABLE KYOG..HANGUL SYLLABLE KYOH
+ {0xCFE0, 0xCFE0, prLV}, // Lo HANGUL SYLLABLE KU
+ {0xCFE1, 0xCFFB, prLVT}, // Lo [27] HANGUL SYLLABLE KUG..HANGUL SYLLABLE KUH
+ {0xCFFC, 0xCFFC, prLV}, // Lo HANGUL SYLLABLE KWEO
+ {0xCFFD, 0xD017, prLVT}, // Lo [27] HANGUL SYLLABLE KWEOG..HANGUL SYLLABLE KWEOH
+ {0xD018, 0xD018, prLV}, // Lo HANGUL SYLLABLE KWE
+ {0xD019, 0xD033, prLVT}, // Lo [27] HANGUL SYLLABLE KWEG..HANGUL SYLLABLE KWEH
+ {0xD034, 0xD034, prLV}, // Lo HANGUL SYLLABLE KWI
+ {0xD035, 0xD04F, prLVT}, // Lo [27] HANGUL SYLLABLE KWIG..HANGUL SYLLABLE KWIH
+ {0xD050, 0xD050, prLV}, // Lo HANGUL SYLLABLE KYU
+ {0xD051, 0xD06B, prLVT}, // Lo [27] HANGUL SYLLABLE KYUG..HANGUL SYLLABLE KYUH
+ {0xD06C, 0xD06C, prLV}, // Lo HANGUL SYLLABLE KEU
+ {0xD06D, 0xD087, prLVT}, // Lo [27] HANGUL SYLLABLE KEUG..HANGUL SYLLABLE KEUH
+ {0xD088, 0xD088, prLV}, // Lo HANGUL SYLLABLE KYI
+ {0xD089, 0xD0A3, prLVT}, // Lo [27] HANGUL SYLLABLE KYIG..HANGUL SYLLABLE KYIH
+ {0xD0A4, 0xD0A4, prLV}, // Lo HANGUL SYLLABLE KI
+ {0xD0A5, 0xD0BF, prLVT}, // Lo [27] HANGUL SYLLABLE KIG..HANGUL SYLLABLE KIH
+ {0xD0C0, 0xD0C0, prLV}, // Lo HANGUL SYLLABLE TA
+ {0xD0C1, 0xD0DB, prLVT}, // Lo [27] HANGUL SYLLABLE TAG..HANGUL SYLLABLE TAH
+ {0xD0DC, 0xD0DC, prLV}, // Lo HANGUL SYLLABLE TAE
+ {0xD0DD, 0xD0F7, prLVT}, // Lo [27] HANGUL SYLLABLE TAEG..HANGUL SYLLABLE TAEH
+ {0xD0F8, 0xD0F8, prLV}, // Lo HANGUL SYLLABLE TYA
+ {0xD0F9, 0xD113, prLVT}, // Lo [27] HANGUL SYLLABLE TYAG..HANGUL SYLLABLE TYAH
+ {0xD114, 0xD114, prLV}, // Lo HANGUL SYLLABLE TYAE
+ {0xD115, 0xD12F, prLVT}, // Lo [27] HANGUL SYLLABLE TYAEG..HANGUL SYLLABLE TYAEH
+ {0xD130, 0xD130, prLV}, // Lo HANGUL SYLLABLE TEO
+ {0xD131, 0xD14B, prLVT}, // Lo [27] HANGUL SYLLABLE TEOG..HANGUL SYLLABLE TEOH
+ {0xD14C, 0xD14C, prLV}, // Lo HANGUL SYLLABLE TE
+ {0xD14D, 0xD167, prLVT}, // Lo [27] HANGUL SYLLABLE TEG..HANGUL SYLLABLE TEH
+ {0xD168, 0xD168, prLV}, // Lo HANGUL SYLLABLE TYEO
+ {0xD169, 0xD183, prLVT}, // Lo [27] HANGUL SYLLABLE TYEOG..HANGUL SYLLABLE TYEOH
+ {0xD184, 0xD184, prLV}, // Lo HANGUL SYLLABLE TYE
+ {0xD185, 0xD19F, prLVT}, // Lo [27] HANGUL SYLLABLE TYEG..HANGUL SYLLABLE TYEH
+ {0xD1A0, 0xD1A0, prLV}, // Lo HANGUL SYLLABLE TO
+ {0xD1A1, 0xD1BB, prLVT}, // Lo [27] HANGUL SYLLABLE TOG..HANGUL SYLLABLE TOH
+ {0xD1BC, 0xD1BC, prLV}, // Lo HANGUL SYLLABLE TWA
+ {0xD1BD, 0xD1D7, prLVT}, // Lo [27] HANGUL SYLLABLE TWAG..HANGUL SYLLABLE TWAH
+ {0xD1D8, 0xD1D8, prLV}, // Lo HANGUL SYLLABLE TWAE
+ {0xD1D9, 0xD1F3, prLVT}, // Lo [27] HANGUL SYLLABLE TWAEG..HANGUL SYLLABLE TWAEH
+ {0xD1F4, 0xD1F4, prLV}, // Lo HANGUL SYLLABLE TOE
+ {0xD1F5, 0xD20F, prLVT}, // Lo [27] HANGUL SYLLABLE TOEG..HANGUL SYLLABLE TOEH
+ {0xD210, 0xD210, prLV}, // Lo HANGUL SYLLABLE TYO
+ {0xD211, 0xD22B, prLVT}, // Lo [27] HANGUL SYLLABLE TYOG..HANGUL SYLLABLE TYOH
+ {0xD22C, 0xD22C, prLV}, // Lo HANGUL SYLLABLE TU
+ {0xD22D, 0xD247, prLVT}, // Lo [27] HANGUL SYLLABLE TUG..HANGUL SYLLABLE TUH
+ {0xD248, 0xD248, prLV}, // Lo HANGUL SYLLABLE TWEO
+ {0xD249, 0xD263, prLVT}, // Lo [27] HANGUL SYLLABLE TWEOG..HANGUL SYLLABLE TWEOH
+ {0xD264, 0xD264, prLV}, // Lo HANGUL SYLLABLE TWE
+ {0xD265, 0xD27F, prLVT}, // Lo [27] HANGUL SYLLABLE TWEG..HANGUL SYLLABLE TWEH
+ {0xD280, 0xD280, prLV}, // Lo HANGUL SYLLABLE TWI
+ {0xD281, 0xD29B, prLVT}, // Lo [27] HANGUL SYLLABLE TWIG..HANGUL SYLLABLE TWIH
+ {0xD29C, 0xD29C, prLV}, // Lo HANGUL SYLLABLE TYU
+ {0xD29D, 0xD2B7, prLVT}, // Lo [27] HANGUL SYLLABLE TYUG..HANGUL SYLLABLE TYUH
+ {0xD2B8, 0xD2B8, prLV}, // Lo HANGUL SYLLABLE TEU
+ {0xD2B9, 0xD2D3, prLVT}, // Lo [27] HANGUL SYLLABLE TEUG..HANGUL SYLLABLE TEUH
+ {0xD2D4, 0xD2D4, prLV}, // Lo HANGUL SYLLABLE TYI
+ {0xD2D5, 0xD2EF, prLVT}, // Lo [27] HANGUL SYLLABLE TYIG..HANGUL SYLLABLE TYIH
+ {0xD2F0, 0xD2F0, prLV}, // Lo HANGUL SYLLABLE TI
+ {0xD2F1, 0xD30B, prLVT}, // Lo [27] HANGUL SYLLABLE TIG..HANGUL SYLLABLE TIH
+ {0xD30C, 0xD30C, prLV}, // Lo HANGUL SYLLABLE PA
+ {0xD30D, 0xD327, prLVT}, // Lo [27] HANGUL SYLLABLE PAG..HANGUL SYLLABLE PAH
+ {0xD328, 0xD328, prLV}, // Lo HANGUL SYLLABLE PAE
+ {0xD329, 0xD343, prLVT}, // Lo [27] HANGUL SYLLABLE PAEG..HANGUL SYLLABLE PAEH
+ {0xD344, 0xD344, prLV}, // Lo HANGUL SYLLABLE PYA
+ {0xD345, 0xD35F, prLVT}, // Lo [27] HANGUL SYLLABLE PYAG..HANGUL SYLLABLE PYAH
+ {0xD360, 0xD360, prLV}, // Lo HANGUL SYLLABLE PYAE
+ {0xD361, 0xD37B, prLVT}, // Lo [27] HANGUL SYLLABLE PYAEG..HANGUL SYLLABLE PYAEH
+ {0xD37C, 0xD37C, prLV}, // Lo HANGUL SYLLABLE PEO
+ {0xD37D, 0xD397, prLVT}, // Lo [27] HANGUL SYLLABLE PEOG..HANGUL SYLLABLE PEOH
+ {0xD398, 0xD398, prLV}, // Lo HANGUL SYLLABLE PE
+ {0xD399, 0xD3B3, prLVT}, // Lo [27] HANGUL SYLLABLE PEG..HANGUL SYLLABLE PEH
+ {0xD3B4, 0xD3B4, prLV}, // Lo HANGUL SYLLABLE PYEO
+ {0xD3B5, 0xD3CF, prLVT}, // Lo [27] HANGUL SYLLABLE PYEOG..HANGUL SYLLABLE PYEOH
+ {0xD3D0, 0xD3D0, prLV}, // Lo HANGUL SYLLABLE PYE
+ {0xD3D1, 0xD3EB, prLVT}, // Lo [27] HANGUL SYLLABLE PYEG..HANGUL SYLLABLE PYEH
+ {0xD3EC, 0xD3EC, prLV}, // Lo HANGUL SYLLABLE PO
+ {0xD3ED, 0xD407, prLVT}, // Lo [27] HANGUL SYLLABLE POG..HANGUL SYLLABLE POH
+ {0xD408, 0xD408, prLV}, // Lo HANGUL SYLLABLE PWA
+ {0xD409, 0xD423, prLVT}, // Lo [27] HANGUL SYLLABLE PWAG..HANGUL SYLLABLE PWAH
+ {0xD424, 0xD424, prLV}, // Lo HANGUL SYLLABLE PWAE
+ {0xD425, 0xD43F, prLVT}, // Lo [27] HANGUL SYLLABLE PWAEG..HANGUL SYLLABLE PWAEH
+ {0xD440, 0xD440, prLV}, // Lo HANGUL SYLLABLE POE
+ {0xD441, 0xD45B, prLVT}, // Lo [27] HANGUL SYLLABLE POEG..HANGUL SYLLABLE POEH
+ {0xD45C, 0xD45C, prLV}, // Lo HANGUL SYLLABLE PYO
+ {0xD45D, 0xD477, prLVT}, // Lo [27] HANGUL SYLLABLE PYOG..HANGUL SYLLABLE PYOH
+ {0xD478, 0xD478, prLV}, // Lo HANGUL SYLLABLE PU
+ {0xD479, 0xD493, prLVT}, // Lo [27] HANGUL SYLLABLE PUG..HANGUL SYLLABLE PUH
+ {0xD494, 0xD494, prLV}, // Lo HANGUL SYLLABLE PWEO
+ {0xD495, 0xD4AF, prLVT}, // Lo [27] HANGUL SYLLABLE PWEOG..HANGUL SYLLABLE PWEOH
+ {0xD4B0, 0xD4B0, prLV}, // Lo HANGUL SYLLABLE PWE
+ {0xD4B1, 0xD4CB, prLVT}, // Lo [27] HANGUL SYLLABLE PWEG..HANGUL SYLLABLE PWEH
+ {0xD4CC, 0xD4CC, prLV}, // Lo HANGUL SYLLABLE PWI
+ {0xD4CD, 0xD4E7, prLVT}, // Lo [27] HANGUL SYLLABLE PWIG..HANGUL SYLLABLE PWIH
+ {0xD4E8, 0xD4E8, prLV}, // Lo HANGUL SYLLABLE PYU
+ {0xD4E9, 0xD503, prLVT}, // Lo [27] HANGUL SYLLABLE PYUG..HANGUL SYLLABLE PYUH
+ {0xD504, 0xD504, prLV}, // Lo HANGUL SYLLABLE PEU
+ {0xD505, 0xD51F, prLVT}, // Lo [27] HANGUL SYLLABLE PEUG..HANGUL SYLLABLE PEUH
+ {0xD520, 0xD520, prLV}, // Lo HANGUL SYLLABLE PYI
+ {0xD521, 0xD53B, prLVT}, // Lo [27] HANGUL SYLLABLE PYIG..HANGUL SYLLABLE PYIH
+ {0xD53C, 0xD53C, prLV}, // Lo HANGUL SYLLABLE PI
+ {0xD53D, 0xD557, prLVT}, // Lo [27] HANGUL SYLLABLE PIG..HANGUL SYLLABLE PIH
+ {0xD558, 0xD558, prLV}, // Lo HANGUL SYLLABLE HA
+ {0xD559, 0xD573, prLVT}, // Lo [27] HANGUL SYLLABLE HAG..HANGUL SYLLABLE HAH
+ {0xD574, 0xD574, prLV}, // Lo HANGUL SYLLABLE HAE
+ {0xD575, 0xD58F, prLVT}, // Lo [27] HANGUL SYLLABLE HAEG..HANGUL SYLLABLE HAEH
+ {0xD590, 0xD590, prLV}, // Lo HANGUL SYLLABLE HYA
+ {0xD591, 0xD5AB, prLVT}, // Lo [27] HANGUL SYLLABLE HYAG..HANGUL SYLLABLE HYAH
+ {0xD5AC, 0xD5AC, prLV}, // Lo HANGUL SYLLABLE HYAE
+ {0xD5AD, 0xD5C7, prLVT}, // Lo [27] HANGUL SYLLABLE HYAEG..HANGUL SYLLABLE HYAEH
+ {0xD5C8, 0xD5C8, prLV}, // Lo HANGUL SYLLABLE HEO
+ {0xD5C9, 0xD5E3, prLVT}, // Lo [27] HANGUL SYLLABLE HEOG..HANGUL SYLLABLE HEOH
+ {0xD5E4, 0xD5E4, prLV}, // Lo HANGUL SYLLABLE HE
+ {0xD5E5, 0xD5FF, prLVT}, // Lo [27] HANGUL SYLLABLE HEG..HANGUL SYLLABLE HEH
+ {0xD600, 0xD600, prLV}, // Lo HANGUL SYLLABLE HYEO
+ {0xD601, 0xD61B, prLVT}, // Lo [27] HANGUL SYLLABLE HYEOG..HANGUL SYLLABLE HYEOH
+ {0xD61C, 0xD61C, prLV}, // Lo HANGUL SYLLABLE HYE
+ {0xD61D, 0xD637, prLVT}, // Lo [27] HANGUL SYLLABLE HYEG..HANGUL SYLLABLE HYEH
+ {0xD638, 0xD638, prLV}, // Lo HANGUL SYLLABLE HO
+ {0xD639, 0xD653, prLVT}, // Lo [27] HANGUL SYLLABLE HOG..HANGUL SYLLABLE HOH
+ {0xD654, 0xD654, prLV}, // Lo HANGUL SYLLABLE HWA
+ {0xD655, 0xD66F, prLVT}, // Lo [27] HANGUL SYLLABLE HWAG..HANGUL SYLLABLE HWAH
+ {0xD670, 0xD670, prLV}, // Lo HANGUL SYLLABLE HWAE
+ {0xD671, 0xD68B, prLVT}, // Lo [27] HANGUL SYLLABLE HWAEG..HANGUL SYLLABLE HWAEH
+ {0xD68C, 0xD68C, prLV}, // Lo HANGUL SYLLABLE HOE
+ {0xD68D, 0xD6A7, prLVT}, // Lo [27] HANGUL SYLLABLE HOEG..HANGUL SYLLABLE HOEH
+ {0xD6A8, 0xD6A8, prLV}, // Lo HANGUL SYLLABLE HYO
+ {0xD6A9, 0xD6C3, prLVT}, // Lo [27] HANGUL SYLLABLE HYOG..HANGUL SYLLABLE HYOH
+ {0xD6C4, 0xD6C4, prLV}, // Lo HANGUL SYLLABLE HU
+ {0xD6C5, 0xD6DF, prLVT}, // Lo [27] HANGUL SYLLABLE HUG..HANGUL SYLLABLE HUH
+ {0xD6E0, 0xD6E0, prLV}, // Lo HANGUL SYLLABLE HWEO
+ {0xD6E1, 0xD6FB, prLVT}, // Lo [27] HANGUL SYLLABLE HWEOG..HANGUL SYLLABLE HWEOH
+ {0xD6FC, 0xD6FC, prLV}, // Lo HANGUL SYLLABLE HWE
+ {0xD6FD, 0xD717, prLVT}, // Lo [27] HANGUL SYLLABLE HWEG..HANGUL SYLLABLE HWEH
+ {0xD718, 0xD718, prLV}, // Lo HANGUL SYLLABLE HWI
+ {0xD719, 0xD733, prLVT}, // Lo [27] HANGUL SYLLABLE HWIG..HANGUL SYLLABLE HWIH
+ {0xD734, 0xD734, prLV}, // Lo HANGUL SYLLABLE HYU
+ {0xD735, 0xD74F, prLVT}, // Lo [27] HANGUL SYLLABLE HYUG..HANGUL SYLLABLE HYUH
+ {0xD750, 0xD750, prLV}, // Lo HANGUL SYLLABLE HEU
+ {0xD751, 0xD76B, prLVT}, // Lo [27] HANGUL SYLLABLE HEUG..HANGUL SYLLABLE HEUH
+ {0xD76C, 0xD76C, prLV}, // Lo HANGUL SYLLABLE HYI
+ {0xD76D, 0xD787, prLVT}, // Lo [27] HANGUL SYLLABLE HYIG..HANGUL SYLLABLE HYIH
+ {0xD788, 0xD788, prLV}, // Lo HANGUL SYLLABLE HI
+ {0xD789, 0xD7A3, prLVT}, // Lo [27] HANGUL SYLLABLE HIG..HANGUL SYLLABLE HIH
+ {0xD7B0, 0xD7C6, prV}, // Lo [23] HANGUL JUNGSEONG O-YEO..HANGUL JUNGSEONG ARAEA-E
+ {0xD7CB, 0xD7FB, prT}, // Lo [49] HANGUL JONGSEONG NIEUN-RIEUL..HANGUL JONGSEONG PHIEUPH-THIEUTH
+ {0xFB1E, 0xFB1E, prExtend}, // Mn HEBREW POINT JUDEO-SPANISH VARIKA
+ {0xFE00, 0xFE0F, prExtend}, // Mn [16] VARIATION SELECTOR-1..VARIATION SELECTOR-16
+ {0xFE20, 0xFE2F, prExtend}, // Mn [16] COMBINING LIGATURE LEFT HALF..COMBINING CYRILLIC TITLO RIGHT HALF
+ {0xFEFF, 0xFEFF, prControl}, // Cf ZERO WIDTH NO-BREAK SPACE
+ {0xFF9E, 0xFF9F, prExtend}, // Lm [2] HALFWIDTH KATAKANA VOICED SOUND MARK..HALFWIDTH KATAKANA SEMI-VOICED SOUND MARK
+ {0xFFF0, 0xFFF8, prControl}, // Cn [9] ..
+ {0xFFF9, 0xFFFB, prControl}, // Cf [3] INTERLINEAR ANNOTATION ANCHOR..INTERLINEAR ANNOTATION TERMINATOR
+ {0x101FD, 0x101FD, prExtend}, // Mn PHAISTOS DISC SIGN COMBINING OBLIQUE STROKE
+ {0x102E0, 0x102E0, prExtend}, // Mn COPTIC EPACT THOUSANDS MARK
+ {0x10376, 0x1037A, prExtend}, // Mn [5] COMBINING OLD PERMIC LETTER AN..COMBINING OLD PERMIC LETTER SII
+ {0x10A01, 0x10A03, prExtend}, // Mn [3] KHAROSHTHI VOWEL SIGN I..KHAROSHTHI VOWEL SIGN VOCALIC R
+ {0x10A05, 0x10A06, prExtend}, // Mn [2] KHAROSHTHI VOWEL SIGN E..KHAROSHTHI VOWEL SIGN O
+ {0x10A0C, 0x10A0F, prExtend}, // Mn [4] KHAROSHTHI VOWEL LENGTH MARK..KHAROSHTHI SIGN VISARGA
+ {0x10A38, 0x10A3A, prExtend}, // Mn [3] KHAROSHTHI SIGN BAR ABOVE..KHAROSHTHI SIGN DOT BELOW
+ {0x10A3F, 0x10A3F, prExtend}, // Mn KHAROSHTHI VIRAMA
+ {0x10AE5, 0x10AE6, prExtend}, // Mn [2] MANICHAEAN ABBREVIATION MARK ABOVE..MANICHAEAN ABBREVIATION MARK BELOW
+ {0x10D24, 0x10D27, prExtend}, // Mn [4] HANIFI ROHINGYA SIGN HARBAHAY..HANIFI ROHINGYA SIGN TASSI
+ {0x10EAB, 0x10EAC, prExtend}, // Mn [2] YEZIDI COMBINING HAMZA MARK..YEZIDI COMBINING MADDA MARK
+ {0x10F46, 0x10F50, prExtend}, // Mn [11] SOGDIAN COMBINING DOT BELOW..SOGDIAN COMBINING STROKE BELOW
+ {0x10F82, 0x10F85, prExtend}, // Mn [4] OLD UYGHUR COMBINING DOT ABOVE..OLD UYGHUR COMBINING TWO DOTS BELOW
+ {0x11000, 0x11000, prSpacingMark}, // Mc BRAHMI SIGN CANDRABINDU
+ {0x11001, 0x11001, prExtend}, // Mn BRAHMI SIGN ANUSVARA
+ {0x11002, 0x11002, prSpacingMark}, // Mc BRAHMI SIGN VISARGA
+ {0x11038, 0x11046, prExtend}, // Mn [15] BRAHMI VOWEL SIGN AA..BRAHMI VIRAMA
+ {0x11070, 0x11070, prExtend}, // Mn BRAHMI SIGN OLD TAMIL VIRAMA
+ {0x11073, 0x11074, prExtend}, // Mn [2] BRAHMI VOWEL SIGN OLD TAMIL SHORT E..BRAHMI VOWEL SIGN OLD TAMIL SHORT O
+ {0x1107F, 0x11081, prExtend}, // Mn [3] BRAHMI NUMBER JOINER..KAITHI SIGN ANUSVARA
+ {0x11082, 0x11082, prSpacingMark}, // Mc KAITHI SIGN VISARGA
+ {0x110B0, 0x110B2, prSpacingMark}, // Mc [3] KAITHI VOWEL SIGN AA..KAITHI VOWEL SIGN II
+ {0x110B3, 0x110B6, prExtend}, // Mn [4] KAITHI VOWEL SIGN U..KAITHI VOWEL SIGN AI
+ {0x110B7, 0x110B8, prSpacingMark}, // Mc [2] KAITHI VOWEL SIGN O..KAITHI VOWEL SIGN AU
+ {0x110B9, 0x110BA, prExtend}, // Mn [2] KAITHI SIGN VIRAMA..KAITHI SIGN NUKTA
+ {0x110BD, 0x110BD, prPrepend}, // Cf KAITHI NUMBER SIGN
+ {0x110C2, 0x110C2, prExtend}, // Mn KAITHI VOWEL SIGN VOCALIC R
+ {0x110CD, 0x110CD, prPrepend}, // Cf KAITHI NUMBER SIGN ABOVE
+ {0x11100, 0x11102, prExtend}, // Mn [3] CHAKMA SIGN CANDRABINDU..CHAKMA SIGN VISARGA
+ {0x11127, 0x1112B, prExtend}, // Mn [5] CHAKMA VOWEL SIGN A..CHAKMA VOWEL SIGN UU
+ {0x1112C, 0x1112C, prSpacingMark}, // Mc CHAKMA VOWEL SIGN E
+ {0x1112D, 0x11134, prExtend}, // Mn [8] CHAKMA VOWEL SIGN AI..CHAKMA MAAYYAA
+ {0x11145, 0x11146, prSpacingMark}, // Mc [2] CHAKMA VOWEL SIGN AA..CHAKMA VOWEL SIGN EI
+ {0x11173, 0x11173, prExtend}, // Mn MAHAJANI SIGN NUKTA
+ {0x11180, 0x11181, prExtend}, // Mn [2] SHARADA SIGN CANDRABINDU..SHARADA SIGN ANUSVARA
+ {0x11182, 0x11182, prSpacingMark}, // Mc SHARADA SIGN VISARGA
+ {0x111B3, 0x111B5, prSpacingMark}, // Mc [3] SHARADA VOWEL SIGN AA..SHARADA VOWEL SIGN II
+ {0x111B6, 0x111BE, prExtend}, // Mn [9] SHARADA VOWEL SIGN U..SHARADA VOWEL SIGN O
+ {0x111BF, 0x111C0, prSpacingMark}, // Mc [2] SHARADA VOWEL SIGN AU..SHARADA SIGN VIRAMA
+ {0x111C2, 0x111C3, prPrepend}, // Lo [2] SHARADA SIGN JIHVAMULIYA..SHARADA SIGN UPADHMANIYA
+ {0x111C9, 0x111CC, prExtend}, // Mn [4] SHARADA SANDHI MARK..SHARADA EXTRA SHORT VOWEL MARK
+ {0x111CE, 0x111CE, prSpacingMark}, // Mc SHARADA VOWEL SIGN PRISHTHAMATRA E
+ {0x111CF, 0x111CF, prExtend}, // Mn SHARADA SIGN INVERTED CANDRABINDU
+ {0x1122C, 0x1122E, prSpacingMark}, // Mc [3] KHOJKI VOWEL SIGN AA..KHOJKI VOWEL SIGN II
+ {0x1122F, 0x11231, prExtend}, // Mn [3] KHOJKI VOWEL SIGN U..KHOJKI VOWEL SIGN AI
+ {0x11232, 0x11233, prSpacingMark}, // Mc [2] KHOJKI VOWEL SIGN O..KHOJKI VOWEL SIGN AU
+ {0x11234, 0x11234, prExtend}, // Mn KHOJKI SIGN ANUSVARA
+ {0x11235, 0x11235, prSpacingMark}, // Mc KHOJKI SIGN VIRAMA
+ {0x11236, 0x11237, prExtend}, // Mn [2] KHOJKI SIGN NUKTA..KHOJKI SIGN SHADDA
+ {0x1123E, 0x1123E, prExtend}, // Mn KHOJKI SIGN SUKUN
+ {0x112DF, 0x112DF, prExtend}, // Mn KHUDAWADI SIGN ANUSVARA
+ {0x112E0, 0x112E2, prSpacingMark}, // Mc [3] KHUDAWADI VOWEL SIGN AA..KHUDAWADI VOWEL SIGN II
+ {0x112E3, 0x112EA, prExtend}, // Mn [8] KHUDAWADI VOWEL SIGN U..KHUDAWADI SIGN VIRAMA
+ {0x11300, 0x11301, prExtend}, // Mn [2] GRANTHA SIGN COMBINING ANUSVARA ABOVE..GRANTHA SIGN CANDRABINDU
+ {0x11302, 0x11303, prSpacingMark}, // Mc [2] GRANTHA SIGN ANUSVARA..GRANTHA SIGN VISARGA
+ {0x1133B, 0x1133C, prExtend}, // Mn [2] COMBINING BINDU BELOW..GRANTHA SIGN NUKTA
+ {0x1133E, 0x1133E, prExtend}, // Mc GRANTHA VOWEL SIGN AA
+ {0x1133F, 0x1133F, prSpacingMark}, // Mc GRANTHA VOWEL SIGN I
+ {0x11340, 0x11340, prExtend}, // Mn GRANTHA VOWEL SIGN II
+ {0x11341, 0x11344, prSpacingMark}, // Mc [4] GRANTHA VOWEL SIGN U..GRANTHA VOWEL SIGN VOCALIC RR
+ {0x11347, 0x11348, prSpacingMark}, // Mc [2] GRANTHA VOWEL SIGN EE..GRANTHA VOWEL SIGN AI
+ {0x1134B, 0x1134D, prSpacingMark}, // Mc [3] GRANTHA VOWEL SIGN OO..GRANTHA SIGN VIRAMA
+ {0x11357, 0x11357, prExtend}, // Mc GRANTHA AU LENGTH MARK
+ {0x11362, 0x11363, prSpacingMark}, // Mc [2] GRANTHA VOWEL SIGN VOCALIC L..GRANTHA VOWEL SIGN VOCALIC LL
+ {0x11366, 0x1136C, prExtend}, // Mn [7] COMBINING GRANTHA DIGIT ZERO..COMBINING GRANTHA DIGIT SIX
+ {0x11370, 0x11374, prExtend}, // Mn [5] COMBINING GRANTHA LETTER A..COMBINING GRANTHA LETTER PA
+ {0x11435, 0x11437, prSpacingMark}, // Mc [3] NEWA VOWEL SIGN AA..NEWA VOWEL SIGN II
+ {0x11438, 0x1143F, prExtend}, // Mn [8] NEWA VOWEL SIGN U..NEWA VOWEL SIGN AI
+ {0x11440, 0x11441, prSpacingMark}, // Mc [2] NEWA VOWEL SIGN O..NEWA VOWEL SIGN AU
+ {0x11442, 0x11444, prExtend}, // Mn [3] NEWA SIGN VIRAMA..NEWA SIGN ANUSVARA
+ {0x11445, 0x11445, prSpacingMark}, // Mc NEWA SIGN VISARGA
+ {0x11446, 0x11446, prExtend}, // Mn NEWA SIGN NUKTA
+ {0x1145E, 0x1145E, prExtend}, // Mn NEWA SANDHI MARK
+ {0x114B0, 0x114B0, prExtend}, // Mc TIRHUTA VOWEL SIGN AA
+ {0x114B1, 0x114B2, prSpacingMark}, // Mc [2] TIRHUTA VOWEL SIGN I..TIRHUTA VOWEL SIGN II
+ {0x114B3, 0x114B8, prExtend}, // Mn [6] TIRHUTA VOWEL SIGN U..TIRHUTA VOWEL SIGN VOCALIC LL
+ {0x114B9, 0x114B9, prSpacingMark}, // Mc TIRHUTA VOWEL SIGN E
+ {0x114BA, 0x114BA, prExtend}, // Mn TIRHUTA VOWEL SIGN SHORT E
+ {0x114BB, 0x114BC, prSpacingMark}, // Mc [2] TIRHUTA VOWEL SIGN AI..TIRHUTA VOWEL SIGN O
+ {0x114BD, 0x114BD, prExtend}, // Mc TIRHUTA VOWEL SIGN SHORT O
+ {0x114BE, 0x114BE, prSpacingMark}, // Mc TIRHUTA VOWEL SIGN AU
+ {0x114BF, 0x114C0, prExtend}, // Mn [2] TIRHUTA SIGN CANDRABINDU..TIRHUTA SIGN ANUSVARA
+ {0x114C1, 0x114C1, prSpacingMark}, // Mc TIRHUTA SIGN VISARGA
+ {0x114C2, 0x114C3, prExtend}, // Mn [2] TIRHUTA SIGN VIRAMA..TIRHUTA SIGN NUKTA
+ {0x115AF, 0x115AF, prExtend}, // Mc SIDDHAM VOWEL SIGN AA
+ {0x115B0, 0x115B1, prSpacingMark}, // Mc [2] SIDDHAM VOWEL SIGN I..SIDDHAM VOWEL SIGN II
+ {0x115B2, 0x115B5, prExtend}, // Mn [4] SIDDHAM VOWEL SIGN U..SIDDHAM VOWEL SIGN VOCALIC RR
+ {0x115B8, 0x115BB, prSpacingMark}, // Mc [4] SIDDHAM VOWEL SIGN E..SIDDHAM VOWEL SIGN AU
+ {0x115BC, 0x115BD, prExtend}, // Mn [2] SIDDHAM SIGN CANDRABINDU..SIDDHAM SIGN ANUSVARA
+ {0x115BE, 0x115BE, prSpacingMark}, // Mc SIDDHAM SIGN VISARGA
+ {0x115BF, 0x115C0, prExtend}, // Mn [2] SIDDHAM SIGN VIRAMA..SIDDHAM SIGN NUKTA
+ {0x115DC, 0x115DD, prExtend}, // Mn [2] SIDDHAM VOWEL SIGN ALTERNATE U..SIDDHAM VOWEL SIGN ALTERNATE UU
+ {0x11630, 0x11632, prSpacingMark}, // Mc [3] MODI VOWEL SIGN AA..MODI VOWEL SIGN II
+ {0x11633, 0x1163A, prExtend}, // Mn [8] MODI VOWEL SIGN U..MODI VOWEL SIGN AI
+ {0x1163B, 0x1163C, prSpacingMark}, // Mc [2] MODI VOWEL SIGN O..MODI VOWEL SIGN AU
+ {0x1163D, 0x1163D, prExtend}, // Mn MODI SIGN ANUSVARA
+ {0x1163E, 0x1163E, prSpacingMark}, // Mc MODI SIGN VISARGA
+ {0x1163F, 0x11640, prExtend}, // Mn [2] MODI SIGN VIRAMA..MODI SIGN ARDHACANDRA
+ {0x116AB, 0x116AB, prExtend}, // Mn TAKRI SIGN ANUSVARA
+ {0x116AC, 0x116AC, prSpacingMark}, // Mc TAKRI SIGN VISARGA
+ {0x116AD, 0x116AD, prExtend}, // Mn TAKRI VOWEL SIGN AA
+ {0x116AE, 0x116AF, prSpacingMark}, // Mc [2] TAKRI VOWEL SIGN I..TAKRI VOWEL SIGN II
+ {0x116B0, 0x116B5, prExtend}, // Mn [6] TAKRI VOWEL SIGN U..TAKRI VOWEL SIGN AU
+ {0x116B6, 0x116B6, prSpacingMark}, // Mc TAKRI SIGN VIRAMA
+ {0x116B7, 0x116B7, prExtend}, // Mn TAKRI SIGN NUKTA
+ {0x1171D, 0x1171F, prExtend}, // Mn [3] AHOM CONSONANT SIGN MEDIAL LA..AHOM CONSONANT SIGN MEDIAL LIGATING RA
+ {0x11722, 0x11725, prExtend}, // Mn [4] AHOM VOWEL SIGN I..AHOM VOWEL SIGN UU
+ {0x11726, 0x11726, prSpacingMark}, // Mc AHOM VOWEL SIGN E
+ {0x11727, 0x1172B, prExtend}, // Mn [5] AHOM VOWEL SIGN AW..AHOM SIGN KILLER
+ {0x1182C, 0x1182E, prSpacingMark}, // Mc [3] DOGRA VOWEL SIGN AA..DOGRA VOWEL SIGN II
+ {0x1182F, 0x11837, prExtend}, // Mn [9] DOGRA VOWEL SIGN U..DOGRA SIGN ANUSVARA
+ {0x11838, 0x11838, prSpacingMark}, // Mc DOGRA SIGN VISARGA
+ {0x11839, 0x1183A, prExtend}, // Mn [2] DOGRA SIGN VIRAMA..DOGRA SIGN NUKTA
+ {0x11930, 0x11930, prExtend}, // Mc DIVES AKURU VOWEL SIGN AA
+ {0x11931, 0x11935, prSpacingMark}, // Mc [5] DIVES AKURU VOWEL SIGN I..DIVES AKURU VOWEL SIGN E
+ {0x11937, 0x11938, prSpacingMark}, // Mc [2] DIVES AKURU VOWEL SIGN AI..DIVES AKURU VOWEL SIGN O
+ {0x1193B, 0x1193C, prExtend}, // Mn [2] DIVES AKURU SIGN ANUSVARA..DIVES AKURU SIGN CANDRABINDU
+ {0x1193D, 0x1193D, prSpacingMark}, // Mc DIVES AKURU SIGN HALANTA
+ {0x1193E, 0x1193E, prExtend}, // Mn DIVES AKURU VIRAMA
+ {0x1193F, 0x1193F, prPrepend}, // Lo DIVES AKURU PREFIXED NASAL SIGN
+ {0x11940, 0x11940, prSpacingMark}, // Mc DIVES AKURU MEDIAL YA
+ {0x11941, 0x11941, prPrepend}, // Lo DIVES AKURU INITIAL RA
+ {0x11942, 0x11942, prSpacingMark}, // Mc DIVES AKURU MEDIAL RA
+ {0x11943, 0x11943, prExtend}, // Mn DIVES AKURU SIGN NUKTA
+ {0x119D1, 0x119D3, prSpacingMark}, // Mc [3] NANDINAGARI VOWEL SIGN AA..NANDINAGARI VOWEL SIGN II
+ {0x119D4, 0x119D7, prExtend}, // Mn [4] NANDINAGARI VOWEL SIGN U..NANDINAGARI VOWEL SIGN VOCALIC RR
+ {0x119DA, 0x119DB, prExtend}, // Mn [2] NANDINAGARI VOWEL SIGN E..NANDINAGARI VOWEL SIGN AI
+ {0x119DC, 0x119DF, prSpacingMark}, // Mc [4] NANDINAGARI VOWEL SIGN O..NANDINAGARI SIGN VISARGA
+ {0x119E0, 0x119E0, prExtend}, // Mn NANDINAGARI SIGN VIRAMA
+ {0x119E4, 0x119E4, prSpacingMark}, // Mc NANDINAGARI VOWEL SIGN PRISHTHAMATRA E
+ {0x11A01, 0x11A0A, prExtend}, // Mn [10] ZANABAZAR SQUARE VOWEL SIGN I..ZANABAZAR SQUARE VOWEL LENGTH MARK
+ {0x11A33, 0x11A38, prExtend}, // Mn [6] ZANABAZAR SQUARE FINAL CONSONANT MARK..ZANABAZAR SQUARE SIGN ANUSVARA
+ {0x11A39, 0x11A39, prSpacingMark}, // Mc ZANABAZAR SQUARE SIGN VISARGA
+ {0x11A3A, 0x11A3A, prPrepend}, // Lo ZANABAZAR SQUARE CLUSTER-INITIAL LETTER RA
+ {0x11A3B, 0x11A3E, prExtend}, // Mn [4] ZANABAZAR SQUARE CLUSTER-FINAL LETTER YA..ZANABAZAR SQUARE CLUSTER-FINAL LETTER VA
+ {0x11A47, 0x11A47, prExtend}, // Mn ZANABAZAR SQUARE SUBJOINER
+ {0x11A51, 0x11A56, prExtend}, // Mn [6] SOYOMBO VOWEL SIGN I..SOYOMBO VOWEL SIGN OE
+ {0x11A57, 0x11A58, prSpacingMark}, // Mc [2] SOYOMBO VOWEL SIGN AI..SOYOMBO VOWEL SIGN AU
+ {0x11A59, 0x11A5B, prExtend}, // Mn [3] SOYOMBO VOWEL SIGN VOCALIC R..SOYOMBO VOWEL LENGTH MARK
+ {0x11A84, 0x11A89, prPrepend}, // Lo [6] SOYOMBO SIGN JIHVAMULIYA..SOYOMBO CLUSTER-INITIAL LETTER SA
+ {0x11A8A, 0x11A96, prExtend}, // Mn [13] SOYOMBO FINAL CONSONANT SIGN G..SOYOMBO SIGN ANUSVARA
+ {0x11A97, 0x11A97, prSpacingMark}, // Mc SOYOMBO SIGN VISARGA
+ {0x11A98, 0x11A99, prExtend}, // Mn [2] SOYOMBO GEMINATION MARK..SOYOMBO SUBJOINER
+ {0x11C2F, 0x11C2F, prSpacingMark}, // Mc BHAIKSUKI VOWEL SIGN AA
+ {0x11C30, 0x11C36, prExtend}, // Mn [7] BHAIKSUKI VOWEL SIGN I..BHAIKSUKI VOWEL SIGN VOCALIC L
+ {0x11C38, 0x11C3D, prExtend}, // Mn [6] BHAIKSUKI VOWEL SIGN E..BHAIKSUKI SIGN ANUSVARA
+ {0x11C3E, 0x11C3E, prSpacingMark}, // Mc BHAIKSUKI SIGN VISARGA
+ {0x11C3F, 0x11C3F, prExtend}, // Mn BHAIKSUKI SIGN VIRAMA
+ {0x11C92, 0x11CA7, prExtend}, // Mn [22] MARCHEN SUBJOINED LETTER KA..MARCHEN SUBJOINED LETTER ZA
+ {0x11CA9, 0x11CA9, prSpacingMark}, // Mc MARCHEN SUBJOINED LETTER YA
+ {0x11CAA, 0x11CB0, prExtend}, // Mn [7] MARCHEN SUBJOINED LETTER RA..MARCHEN VOWEL SIGN AA
+ {0x11CB1, 0x11CB1, prSpacingMark}, // Mc MARCHEN VOWEL SIGN I
+ {0x11CB2, 0x11CB3, prExtend}, // Mn [2] MARCHEN VOWEL SIGN U..MARCHEN VOWEL SIGN E
+ {0x11CB4, 0x11CB4, prSpacingMark}, // Mc MARCHEN VOWEL SIGN O
+ {0x11CB5, 0x11CB6, prExtend}, // Mn [2] MARCHEN SIGN ANUSVARA..MARCHEN SIGN CANDRABINDU
+ {0x11D31, 0x11D36, prExtend}, // Mn [6] MASARAM GONDI VOWEL SIGN AA..MASARAM GONDI VOWEL SIGN VOCALIC R
+ {0x11D3A, 0x11D3A, prExtend}, // Mn MASARAM GONDI VOWEL SIGN E
+ {0x11D3C, 0x11D3D, prExtend}, // Mn [2] MASARAM GONDI VOWEL SIGN AI..MASARAM GONDI VOWEL SIGN O
+ {0x11D3F, 0x11D45, prExtend}, // Mn [7] MASARAM GONDI VOWEL SIGN AU..MASARAM GONDI VIRAMA
+ {0x11D46, 0x11D46, prPrepend}, // Lo MASARAM GONDI REPHA
+ {0x11D47, 0x11D47, prExtend}, // Mn MASARAM GONDI RA-KARA
+ {0x11D8A, 0x11D8E, prSpacingMark}, // Mc [5] GUNJALA GONDI VOWEL SIGN AA..GUNJALA GONDI VOWEL SIGN UU
+ {0x11D90, 0x11D91, prExtend}, // Mn [2] GUNJALA GONDI VOWEL SIGN EE..GUNJALA GONDI VOWEL SIGN AI
+ {0x11D93, 0x11D94, prSpacingMark}, // Mc [2] GUNJALA GONDI VOWEL SIGN OO..GUNJALA GONDI VOWEL SIGN AU
+ {0x11D95, 0x11D95, prExtend}, // Mn GUNJALA GONDI SIGN ANUSVARA
+ {0x11D96, 0x11D96, prSpacingMark}, // Mc GUNJALA GONDI SIGN VISARGA
+ {0x11D97, 0x11D97, prExtend}, // Mn GUNJALA GONDI VIRAMA
+ {0x11EF3, 0x11EF4, prExtend}, // Mn [2] MAKASAR VOWEL SIGN I..MAKASAR VOWEL SIGN U
+ {0x11EF5, 0x11EF6, prSpacingMark}, // Mc [2] MAKASAR VOWEL SIGN E..MAKASAR VOWEL SIGN O
+ {0x13430, 0x13438, prControl}, // Cf [9] EGYPTIAN HIEROGLYPH VERTICAL JOINER..EGYPTIAN HIEROGLYPH END SEGMENT
+ {0x16AF0, 0x16AF4, prExtend}, // Mn [5] BASSA VAH COMBINING HIGH TONE..BASSA VAH COMBINING HIGH-LOW TONE
+ {0x16B30, 0x16B36, prExtend}, // Mn [7] PAHAWH HMONG MARK CIM TUB..PAHAWH HMONG MARK CIM TAUM
+ {0x16F4F, 0x16F4F, prExtend}, // Mn MIAO SIGN CONSONANT MODIFIER BAR
+ {0x16F51, 0x16F87, prSpacingMark}, // Mc [55] MIAO SIGN ASPIRATION..MIAO VOWEL SIGN UI
+ {0x16F8F, 0x16F92, prExtend}, // Mn [4] MIAO TONE RIGHT..MIAO TONE BELOW
+ {0x16FE4, 0x16FE4, prExtend}, // Mn KHITAN SMALL SCRIPT FILLER
+ {0x16FF0, 0x16FF1, prSpacingMark}, // Mc [2] VIETNAMESE ALTERNATE READING MARK CA..VIETNAMESE ALTERNATE READING MARK NHAY
+ {0x1BC9D, 0x1BC9E, prExtend}, // Mn [2] DUPLOYAN THICK LETTER SELECTOR..DUPLOYAN DOUBLE MARK
+ {0x1BCA0, 0x1BCA3, prControl}, // Cf [4] SHORTHAND FORMAT LETTER OVERLAP..SHORTHAND FORMAT UP STEP
+ {0x1CF00, 0x1CF2D, prExtend}, // Mn [46] ZNAMENNY COMBINING MARK GORAZDO NIZKO S KRYZHEM ON LEFT..ZNAMENNY COMBINING MARK KRYZH ON LEFT
+ {0x1CF30, 0x1CF46, prExtend}, // Mn [23] ZNAMENNY COMBINING TONAL RANGE MARK MRACHNO..ZNAMENNY PRIZNAK MODIFIER ROG
+ {0x1D165, 0x1D165, prExtend}, // Mc MUSICAL SYMBOL COMBINING STEM
+ {0x1D166, 0x1D166, prSpacingMark}, // Mc MUSICAL SYMBOL COMBINING SPRECHGESANG STEM
+ {0x1D167, 0x1D169, prExtend}, // Mn [3] MUSICAL SYMBOL COMBINING TREMOLO-1..MUSICAL SYMBOL COMBINING TREMOLO-3
+ {0x1D16D, 0x1D16D, prSpacingMark}, // Mc MUSICAL SYMBOL COMBINING AUGMENTATION DOT
+ {0x1D16E, 0x1D172, prExtend}, // Mc [5] MUSICAL SYMBOL COMBINING FLAG-1..MUSICAL SYMBOL COMBINING FLAG-5
+ {0x1D173, 0x1D17A, prControl}, // Cf [8] MUSICAL SYMBOL BEGIN BEAM..MUSICAL SYMBOL END PHRASE
+ {0x1D17B, 0x1D182, prExtend}, // Mn [8] MUSICAL SYMBOL COMBINING ACCENT..MUSICAL SYMBOL COMBINING LOURE
+ {0x1D185, 0x1D18B, prExtend}, // Mn [7] MUSICAL SYMBOL COMBINING DOIT..MUSICAL SYMBOL COMBINING TRIPLE TONGUE
+ {0x1D1AA, 0x1D1AD, prExtend}, // Mn [4] MUSICAL SYMBOL COMBINING DOWN BOW..MUSICAL SYMBOL COMBINING SNAP PIZZICATO
+ {0x1D242, 0x1D244, prExtend}, // Mn [3] COMBINING GREEK MUSICAL TRISEME..COMBINING GREEK MUSICAL PENTASEME
+ {0x1DA00, 0x1DA36, prExtend}, // Mn [55] SIGNWRITING HEAD RIM..SIGNWRITING AIR SUCKING IN
+ {0x1DA3B, 0x1DA6C, prExtend}, // Mn [50] SIGNWRITING MOUTH CLOSED NEUTRAL..SIGNWRITING EXCITEMENT
+ {0x1DA75, 0x1DA75, prExtend}, // Mn SIGNWRITING UPPER BODY TILTING FROM HIP JOINTS
+ {0x1DA84, 0x1DA84, prExtend}, // Mn SIGNWRITING LOCATION HEAD NECK
+ {0x1DA9B, 0x1DA9F, prExtend}, // Mn [5] SIGNWRITING FILL MODIFIER-2..SIGNWRITING FILL MODIFIER-6
+ {0x1DAA1, 0x1DAAF, prExtend}, // Mn [15] SIGNWRITING ROTATION MODIFIER-2..SIGNWRITING ROTATION MODIFIER-16
+ {0x1E000, 0x1E006, prExtend}, // Mn [7] COMBINING GLAGOLITIC LETTER AZU..COMBINING GLAGOLITIC LETTER ZHIVETE
+ {0x1E008, 0x1E018, prExtend}, // Mn [17] COMBINING GLAGOLITIC LETTER ZEMLJA..COMBINING GLAGOLITIC LETTER HERU
+ {0x1E01B, 0x1E021, prExtend}, // Mn [7] COMBINING GLAGOLITIC LETTER SHTA..COMBINING GLAGOLITIC LETTER YATI
+ {0x1E023, 0x1E024, prExtend}, // Mn [2] COMBINING GLAGOLITIC LETTER YU..COMBINING GLAGOLITIC LETTER SMALL YUS
+ {0x1E026, 0x1E02A, prExtend}, // Mn [5] COMBINING GLAGOLITIC LETTER YO..COMBINING GLAGOLITIC LETTER FITA
+ {0x1E130, 0x1E136, prExtend}, // Mn [7] NYIAKENG PUACHUE HMONG TONE-B..NYIAKENG PUACHUE HMONG TONE-D
+ {0x1E2AE, 0x1E2AE, prExtend}, // Mn TOTO SIGN RISING TONE
+ {0x1E2EC, 0x1E2EF, prExtend}, // Mn [4] WANCHO TONE TUP..WANCHO TONE KOINI
+ {0x1E8D0, 0x1E8D6, prExtend}, // Mn [7] MENDE KIKAKUI COMBINING NUMBER TEENS..MENDE KIKAKUI COMBINING NUMBER MILLIONS
+ {0x1E944, 0x1E94A, prExtend}, // Mn [7] ADLAM ALIF LENGTHENER..ADLAM NUKTA
+ {0x1F000, 0x1F003, prExtendedPictographic}, // E0.0 [4] (🀀..🀃) MAHJONG TILE EAST WIND..MAHJONG TILE NORTH WIND
+ {0x1F004, 0x1F004, prExtendedPictographic}, // E0.6 [1] (🀄) mahjong red dragon
+ {0x1F005, 0x1F0CE, prExtendedPictographic}, // E0.0 [202] (🀅..🃎) MAHJONG TILE GREEN DRAGON..PLAYING CARD KING OF DIAMONDS
+ {0x1F0CF, 0x1F0CF, prExtendedPictographic}, // E0.6 [1] (🃏) joker
+ {0x1F0D0, 0x1F0FF, prExtendedPictographic}, // E0.0 [48] (..) ..
+ {0x1F10D, 0x1F10F, prExtendedPictographic}, // E0.0 [3] (🄍..🄏) CIRCLED ZERO WITH SLASH..CIRCLED DOLLAR SIGN WITH OVERLAID BACKSLASH
+ {0x1F12F, 0x1F12F, prExtendedPictographic}, // E0.0 [1] (🄯) COPYLEFT SYMBOL
+ {0x1F16C, 0x1F16F, prExtendedPictographic}, // E0.0 [4] (🅬..🅯) RAISED MR SIGN..CIRCLED HUMAN FIGURE
+ {0x1F170, 0x1F171, prExtendedPictographic}, // E0.6 [2] (🅰️..🅱️) A button (blood type)..B button (blood type)
+ {0x1F17E, 0x1F17F, prExtendedPictographic}, // E0.6 [2] (🅾️..🅿️) O button (blood type)..P button
+ {0x1F18E, 0x1F18E, prExtendedPictographic}, // E0.6 [1] (🆎) AB button (blood type)
+ {0x1F191, 0x1F19A, prExtendedPictographic}, // E0.6 [10] (🆑..🆚) CL button..VS button
+ {0x1F1AD, 0x1F1E5, prExtendedPictographic}, // E0.0 [57] (🆭..) MASK WORK SYMBOL..
+ {0x1F1E6, 0x1F1FF, prRegionalIndicator}, // So [26] REGIONAL INDICATOR SYMBOL LETTER A..REGIONAL INDICATOR SYMBOL LETTER Z
+ {0x1F201, 0x1F202, prExtendedPictographic}, // E0.6 [2] (🈁..🈂️) Japanese “here” button..Japanese “service charge” button
+ {0x1F203, 0x1F20F, prExtendedPictographic}, // E0.0 [13] (..) ..
+ {0x1F21A, 0x1F21A, prExtendedPictographic}, // E0.6 [1] (🈚) Japanese “free of charge” button
+ {0x1F22F, 0x1F22F, prExtendedPictographic}, // E0.6 [1] (🈯) Japanese “reserved” button
+ {0x1F232, 0x1F23A, prExtendedPictographic}, // E0.6 [9] (🈲..🈺) Japanese “prohibited” button..Japanese “open for business” button
+ {0x1F23C, 0x1F23F, prExtendedPictographic}, // E0.0 [4] (..) ..
+ {0x1F249, 0x1F24F, prExtendedPictographic}, // E0.0 [7] (..) ..
+ {0x1F250, 0x1F251, prExtendedPictographic}, // E0.6 [2] (🉐..🉑) Japanese “bargain” button..Japanese “acceptable” button
+ {0x1F252, 0x1F2FF, prExtendedPictographic}, // E0.0 [174] (..)